Repository navigation
Sync uxjulia/crossink development 965b4bd - #62
Merged
Merged
Conversation
ActivityManager already holds RenderLock when destroying activities. Taking another RenderLock in the destructor causes self-deadlock since renderingMutex is non-recursive.
…Sleep, add Home key option (crosspoint-reader#3089)
…reader#3657) Co-authored-by: Uri Tauber <uritaube@gmail.com>
Update the selection indicator style from Card to CoverFrame for book cards in the cover grid home UI.
Co-authored-by: Julia Nguyen <julia@uxj.io> Co-authored-by: julian naydichev <489233+naydichev@users.noreply.github.com>
…sspoint-reader#3670) Cover grid home now uses directional button navigation (Up/Down for books, Left/Right for tabs) instead of flat next/previous cycling. Added visual selection indicators: vertical accent bar for featured card on button boards, thick dithered rings for grid thumbnails. Classic list home retains original flat navigation.
## Summary The OTA completion screen draws the power-on instruction on one line. The Catalan translation is 575 pixels wide with the firmware UI font, so it extends beyond the X4 portrait screen. Use `UITheme::drawCenteredWrappedText` with theme side padding and top alignment below the completion heading, matching the existing SD-update success screen. Allow up to three lines, bounded by the remaining screen height. ## Scope Check - [x] I have read SCOPE.md and ROADMAP.md. - [x] This PR is not a new built-in theme. - [x] This PR is not a new external network connector. - [x] This PR is not an interactive app, writing tool, RSS/news/browser, media playback, or PDF feature. - [ ] Stock firmware / popular fork comparison: not investigated; this corrects an existing CrossPoint UI regression/defect. - [ ] Maintainer coordination: pending. This touches the OTA activity's completion-screen rendering only; update installation and boot handling are unchanged. ## Additional Context Only `src/activities/settings/OtaUpdateActivity.cpp` changes. Translation strings and the shared wrapping implementation are unchanged. The new local state is an integer and a Rect. Long hints use the existing helper's temporary string/vector allocations for at most three output lines; hints that fit use its single-line fast path. No persistent buffers are added. Heap and flash deltas have not been measured against a baseline. Validation: - Formatting wrapper and `git diff --check`: passed. - Host font/layout probe using the actual Ubuntu 10 UI font, EpdFont measurement code, and extracted firmware wrapping implementation: English, Catalan, Valencian, and Spanish preserved all text within 280, 360, 440, and 760 pixel content widths. At 440 pixels, Catalan lines measure 411 and 159 pixels, Valencian 404 and 159 pixels. English and Spanish remain single-line. - `pio run -e default`: passed (ESP32-C3/X4); no compiler warnings or errors reported. - Simulator screenshots are attached below; no physical-device validation performed. Device verification: select Catalan or Valencian, complete an OTA update, and confirm the full power-on instruction wraps below the completion heading without clipping. Repeat in the supported orientations; English/Spanish should remain single-line where they fit. ### AI Usage YES — AI assisted with implementation and local validation. ## Simulator screenshots The X4 portrait simulator was held in the OTA completion state for these captures; no OTA download or device flashing was performed. The before image uses the parent of this PR (`8c84ef3268fd56147ae1625e04f773f0f50b7720`); the after images use this PR head (`880cb12389f6aff82332c797b99f6988604efbab`). A simulator-only fixture held the completion state and is not part of the firmware change. <table> <tr><th>Before: Catalan, one-line hint clipped at both edges</th><th>After: Catalan, centered two-line hint</th></tr> <tr> <td><img src="https://raw.githubusercontent.com/lpla/crosspoint-reader/221ea8f55a09c4276c4d47d391a9a6e4b9335728/docs/qa/ota-completion-screenshots-2026-09-23/before-catalan.png" width="320" alt="Before fix: Catalan OTA completion hint is clipped on one line"></td> <td><img src="https://raw.githubusercontent.com/lpla/crosspoint-reader/221ea8f55a09c4276c4d47d391a9a6e4b9335728/docs/qa/ota-completion-screenshots-2026-09-23/after-catalan.png" width="320" alt="After fix: Catalan OTA completion hint wraps to two centered lines"></td> </tr> </table> Valencian also wraps cleanly in the same layout: <img src="https://raw.githubusercontent.com/lpla/crosspoint-reader/221ea8f55a09c4276c4d47d391a9a6e4b9335728/docs/qa/ota-completion-screenshots-2026-09-23/after-valencian.png" width="320" alt="After fix: Valencian OTA completion hint wraps to two centered lines">
Co-authored-by: Uri Tauber <uritaube@gmail.com>
Portuguese was the only major Romance language missing from the hyphenation registry. Books tagged `pt` / `pt-BR` resolve to `nullptr` in `getLanguageHyphenatorForPrimaryTag`, so they render with no hyphenation at all — very visible on a 3.7" X3 with the default justified alignment. Related: uxjulia#709 (someone mentions working on a Portuguese dictionary there; this uses the trie already published upstream instead). Closes crosspoint-reader#3656 ## What this does - Adds `hyph-pt.trie.h`, generated with the existing `scripts/generate_hyphenation_trie.py` from `typst/hypher/tries/pt.bin` (1065 bytes payload — Portuguese syllabification is very regular, comparable to Italian at 1555 bytes). - Registers `portuguese` / `pt` in `LanguageRegistry.cpp` as a Latin-script hyphenator. - Adds `process pt` to `scripts/update_hyphenation.sh` so it stays in sync on future refreshes. - Adds an eval case with test data generated by the repo's own `generate_hyphenation_test_data.py`, using 5000 words from Machado de Assis' *Dom Casmurro* — following the existing convention of one canonical work per language (Quijote for Spanish, Manzoni for Italian). ## Evaluation ``` portuguese: F1=99.2143% (threshold 98.21%, 5000 cases) ``` In line with the other Romance languages already supported: | Language | F1 | |---|---| | French | 100% | | Italian | 99.9956% | | **Portuguese** | **99.2143%** | | Spanish | 99.0236% | All 9 tests in `HyphenationEvaluationTest` pass. ## Notes The same patterns serve both pt-BR and pt-PT — the two variants differ in orthography, not in syllabification rules — so the single `pt` primary-tag lookup is correct for both. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: type0labs-dev <jubahhh@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
* **What is the goal of this PR?** Address the actionable Python Sonar
findings in helper scripts without changing firmware runtime behavior.
* **What changes are included?**
* Clean up duplicate literals, needless f-strings, and regex style
issues in the HTML and i18n helper scripts.
* Replace bare font-loading exceptions with explicit `OSError` handling
in the EPUB test generator.
* Centralize repeated test image filenames in the EPUB test generator to
reduce duplication.
* Fix the fallback `ImageFont.load_default()` call in the
kerning/ligature EPUB generator.
* Add a clarifying no-op stub implementation in the PlatformIO git
version helper.
* Fix the two valid review findings: keep the icon header preamble
outside the pixel loop and interpolate all generated EPUB image paths.
* Keep the requested exclusions unchanged: Cognitive Complexity findings
were intentionally not addressed, and environment-only import
diagnostics were left alone.
## Additional Context
* Validation performed:
* `python3 -m py_compile scripts/build_html.py scripts/convert_icon.py
scripts/gen_i18n.py scripts/generate_kerning_ligature_epub.py
scripts/generate_test_bmps.py scripts/generate_test_epub.py
scripts/git_branch.py`
* Generated the JPEG, PNG, and mixed-format test EPUBs in a temporary
directory; verified no literal `{image_src(...)}` placeholders remain
and every image reference resolves to an archived asset.
* Validated in-memory icon conversion output.
* `pio run -t clean && pio run`
* `pio check`
* GitHub build, unit tests, cppcheck, clang-format, title, and
CodeRabbit checks all pass.
---
### AI Usage
While CrossPoint does not 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**_
## Summary Replace the flat list of footnotes/links with intuitive navigation. Similar to how dictionary lookup works. --- ### 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 >**_
## Summary * **What is the goal of this PR?** Justified Korean text currently widens the gaps between syllables inside a word as well as the spaces between words, so every word looks letter-spaced. English only widens the spaces. This PR makes Korean behave like English: only word spaces stretch. * **Root cause** crosspoint-reader#2288 (Korean line breaking) split every Hangul word into one token per syllable, joined by zero-width break opportunities (`continues=false, noSpaceBefore=true`). `TokenBoundary::isJustifiableGap()` treats those boundaries as stretchable, so `extractLine` divides the spare width across every syllable boundary as well as the real spaces. That fits Chinese and Japanese, which don't use spaces between words, but Korean does. crosspoint-reader#2768 later restored the real spaces between Hangul words but left the syllable boundaries stretchable. * **What changes are included?** - **Hangul words are no longer split into syllables.** `hasCjkBreakOpportunityBetween()` returns false when either side is Hangul, so a Korean word is one token like a Latin word. The same function covers words glued across inline tags (`<b>한국</b>어`). Chinese and Japanese are unchanged. - **Korean wraps at spaces by default.** With hyphenation off (the default), a Korean word moves to the next line whole, like CSS `word-break: keep-all`. - **Hyphenation ON can split Korean words at a line end.** The existing CJK boundary rule (the body of `hasCjkBreakOpportunityBetween()` before this PR, now `cjkBoundaryAllowsBreak()`) still defines where a Hangul word may break. `hyphenateWordAtIndex()` offers those points as line-end split candidates, ahead of the Hyphenator's own. `Hyphenator` itself is unchanged, and no Korean language entry is needed. - Allowed: between syllables, and where Hangul meets digits, Latin letters, or brackets (`12|월`, `소신|(conviction)|이`). - Never before closing punctuation or after opening punctuation, so `했|다.` keeps its period. - No hyphen is drawn. - **Cache:** `SECTION_FILE_VERSION` 47 → 48. Line breaks and word positions change; the binary layout does not. | Setting | Chinese / Japanese | Korean before | Korean after | |---|---|---|---| | Hyphenation OFF | unchanged | breaks between any syllables; syllable gaps stretch | wraps at spaces only; only spaces stretch | | Hyphenation ON | unchanged | same as OFF | mostly the same line breaks (a whole word can measure a few px narrower than its syllables did); only spaces stretch | ### Screenshots (X3, Pretendard 16 pt, justified) Chapter 1 of a Korean translation of *Demian*. The next line starts with `해?”`, which cannot be split, so the line above has a lot of spare width to distribute. That makes the difference easy to see. <img width="1056" height="900" alt="pr-visible-letterspacing-zoom" src="https://github.com/user-attachments/assets/d844b7cd-1491-4430-84fa-f01082b16303" /> - **develop:** the spare width goes into every syllable gap, so words look letter-spaced (`무 슨 일 이 야?`). - **This PR, hyphenation ON:** the same line breaks in this passage, but syllables keep their natural spacing and only the word spaces stretch. <img width="1632" height="848" alt="pr-visible-letterspacing-pages" src="https://github.com/user-attachments/assets/875ec34e-9a3b-49f0-b998-661c397c726c" /> Full pages. develop is identical with hyphenation OFF or ON. With hyphenation OFF, this PR wraps only at spaces, so the same dialogue moves to the next page and some lines get wide word gaps. ## Tradeoffs for review - **Korean line breaking is now a reader choice.** With hyphenation OFF, Korean wraps only at spaces, so words are never split. This reads well when a line holds many words (small fonts, landscape). With few words per line (large fonts, portrait), justified lines can show wide word spaces, and hyphenation ON gives tighter lines by splitting words at a line end. The setting already exists, so no new option is added. Hyphenation is a single global setting, so it also switches English hyphenation on or off. A Korean-specific option (or choosing the behaviour from the book's language) would need a new setting and is out of scope here. - **ASCII quotes and hyphens are not punctuation-guarded.** The CJK lists have no entries for `"`, `'`, or `-`, so a line-end split can put one at the start or end of a line (`이라구|"라고`, `대한민국|-서울`), exactly as develop does. Adding them would also change Chinese and Japanese breaking, so that is left for a follow-up. - **Hanja inside a Korean word can still break.** A boundary between two Han characters (e.g. inside `韓國語는`) still counts as a break opportunity, because treating it differently would change Chinese and Japanese layout. This is rare in Korean body text. - **This changes Korean line-breaking policy, not only justification.** Korean lines used to break between any two syllables (browser `word-break: normal`). With this PR they break only at spaces when hyphenation is off, and still between syllables when it is on. ## Testing - **Host tests:** `ctest` passes 371/371. - `KoreanLayout.*` (chapter parser suite): whole-word tokens, spaces-only justification, glued inline styles stay unbreakable. - New `KoreanLineBreakingTest` target: runs `ParsedText` with the real `Hyphenator`. The existing parser suite stubs the Hyphenator out, so it can't test this path. Covers hyphenation OFF/ON, trailing punctuation staying with its syllable, splits at digit and bracket boundaries, and short words splitting. - Three existing letter-spacing tests used Korean as their sample CJK script. They now use Han characters so they keep testing per-character tracking. - **Build:** `pio run` (default) succeeds. - **Device (X3):** develop and this PR were built with the same serial-control instrumentation, and every capture came from a fresh section build. - Korean: see the screenshots above. The final revision renders this page pixel-identically to the capture. - English regression: a justified *Aesop's Fables* page with hyphenation ON is pixel-identical to develop in the text body. The only differing pixels are the status-bar battery percentage. - Not tested on a Chinese or Japanese book. The host tests cover per-character breaking for Han text. ## Additional Context - **Size:** Flash +682 B, static RAM unchanged (X3 `default` build, compared with develop). - **Performance:** no measured change, from single samples of first-page render time (measured on an earlier revision of this PR; later changes only affect which line-end split points a Korean word offers): 3500 → 3500 ms opening the Korean page cold, 1342 → 1334 ms re-laying it out after a settings change, 1198 → 1203 ms for the English page. Korean paragraphs now create fewer tokens, and a word is scanned for split points only when it overflows a line end. - **Memory:** no new persistent allocation. Korean paragraphs create fewer tokens (one per word instead of one per syllable), so the per-token vectors and word arena shrink. Line-end split points are collected only when a word overflows a line end, the same as the existing hyphenation path. ## Scope Check - [x] I have read SCOPE.md and ROADMAP.md. - [x] This PR is **not** a new built-in theme. - [x] This PR is **not** a new external network connector. - [x] This PR is **not** an interactive app, writing tool, RSS/news/browser, media playback, or PDF feature. - [x] The stock firmware does not already handle this well, **and** no other popular CrossPoint fork already does. - [x] This PR does not touch `freeink-sdk/`, `lib/hal/`, the bootloader, OTA, or recovery code. --- ### AI Usage Did you use AI tools to help write this code? _**YES**_ — root-cause investigation, implementation, and tests were done with Claude Code, and the design was iterated on with the author. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01J3qbDbhkJZXFEGRDgRbd8n
…sspoint-reader#3689) Implements a header back button that can be tapped to navigate back. The button is rendered in the header's leading area on applicable screens and routes to the existing Back button handling. Excludes the back button from root screens (Home, Crash) where navigation back is not applicable. Also prevents header back button taps from being interpreted as status bar taps.
Quick Resume now keeps the current frame's inversion state, allowing night-mode pages to sleep in night polarity. The moon icon will invert with the frame during transfer. Other sleep screens continue to draw fresh content in normal polarity.
In night mode, the display is inverted and a HALF refresh would cause a full white flash. Since only the moon icon differs from the previous frame, use FAST refresh to add it without flashing.
## Summary Returning Home after reading with an SD font leaves rebuildable font caches owned by the global font system. Those allocations can split large free heap regions after the reader's temporary buffers have been freed. Release those caches from `ReaderActivity::onExit()`, preserving loaded font metadata and glyph-miss callbacks so the font data can be rebuilt when needed. The final diff contains only this reader-exit change; `SdCardFont.cpp` matches the baseline. The existing 40 KiB mini-cache retention policy between pages remains in place, and the change adds no heap allocations. ## Scope Check - [x] Read SCOPE.md and ROADMAP.md; this addresses the Phase 1 heap-fragmentation focus. - [x] Changes are limited to existing reader/font-cache behavior; no new theme, connector, app, media or PDF feature. - Stock/fork feature comparison: not applicable to this fix for CrossPoint's existing cache lifetime. - SDK/HAL/bootloader/OTA/recovery coordination: not applicable; those paths are untouched. ## Additional Context Compared with `origin/develop` at `0f01106a` on an Xteink X3 using `default` / LOG_LEVEL=2 builds with identical serial-control instrumentation and SDK. Three interleaved cold baseline/candidate pairs used `/CP-Test/scheduling-demian.epub`, Pretendard 16 pt, anti-aliasing enabled, and **hyphenation ON**, starting at spine 4 page 1. Each boot had six warmup and six measured turns, then post-page and Home heap samples after 13-second waits, followed by reopening the book. These are 18 measured turns per build. **Known cost:** median total page rendering increased from 1,507.5 to 1,514.5 ms, **+7 ms (+0.46%)**. Per-pair median increases were **+3.5 / +11.5 / +8 ms**. This remains an unexplained cost; timing parity is not established. No extra kerning/ligature-table reloads were observed during reading: both builds logged zero full-table reloads during warmup and measured turns, with matching mini-matrix rebuild and advance-table fetch logs. The captures do not provide a complete SD-read timing breakdown. | Median measurement | Baseline | Candidate | Difference | |---|---:|---:|---:| | Free heap on Home | 82,680 B | 110,536 B | +27,856 B (+27.9 KB) | | Largest free block on Home | 36,852 B | 94,196 B | +57,344 B (+57.3 KB) | | Largest free block after page turns | 53,236 B | 59,380 B | +6,144 B | | Free heap after page turns | 68,464 B | 68,468 B | +4 B | | Since-boot Min Free at post-page sample | 20,276 B | 20,288 B | +12 B | | Drawing including grayscale | 358 ms | 361 ms | +3 ms | | Total page render | 1,507.5 ms | 1,514.5 ms | +7 ms | | Cold preparation including first render | 4,398 ms | 4,408 ms | +10 ms | | First render after reopening | 4,019 ms | 4,033 ms | +14 ms | | Static RAM | 58,064 B | 58,064 B | unchanged | | Flash | 5,610,791 B | 5,610,803 B | +12 B | Releasing the resident caches allows adjacent free heap regions to coalesce. The recovery includes mini buffers and persistent advance caches as well as full kerning/ligature tables. The observed post-page largest-block gain does not establish that during-reading kerning-table pinning is fixed. Advance-table allocation/merge code is outside this diff. Hyphenation was OFF in the earlier comparison and ON in this round, following the current reader setting. Both builds used the same settings within this round, but the changed setting prevents a clean causal comparison with the earlier timing result. Cold-preparation differences also varied by pair (+14 / -9 / +1 ms). Both builds reloaded tables on reopening because resuming a section build already releases caches, so the full reopen difference cannot be attributed to newly required table reads. Validation: - `pio run -e default` passed for the revised candidate; reused the unchanged baseline build. Formatting and `git diff --check` passed, as did all 12 existing SdCardFont / FontCacheManager host tests. - All nine paired page-content captures were pixel-identical. Seven full frames were identical; two differed only in the footer. - Zero advance-cache bitmap retries across all six boots. No errors, power transitions, crashes or unexpected resets during measured turns. - Both builds logged one thumbnail-cache error per boot during deliberate cache deletion and one CSS low-heap warning per boot outside measured turns. - One read-only STATE timeout before the third candidate boot's first warmup recovered without input replay, reconnect or reset. All six warmups and six measured turns then completed; no paired boot was discarded. The 150-turn heaptrace workload has not been repeated with this revision, so the longer-run bitmap retries remain unverified. Test code, instrumentation and local evidence are excluded from the PR. ### AI Usage YES — AI tools assisted with implementation, analysis, device testing, and this description.
…ader#3652) ## Summary Reduce main-loop contention during EPUB rendering. While background pagination is active, the main loop can keep running even though rendering holds the section lock and pagination cannot advance. The main loop now tries the render lock without waiting and sleeps for 10 ms if it is busy, giving rendering more CPU time. The reader's background-work checks also acquire the same lock before accessing section state, without waiting behind a render. No allocations, tasks, mutexes or task-priority changes are added. On the tested X3 workloads, median total page-render time decreases **12.8% for Korean** and **1.0% for English** against the updated `develop`. The baseline already includes the glyph optimizations in crosspoint-reader#3633; these results measure the additional effect of this PR. ## Scope Check - [x] I have read SCOPE.md and ROADMAP.md. - [x] This PR is not a new built-in theme. - [x] This PR is not a new external network connector. - [x] This PR is not an interactive app, writing tool, RSS/news/browser, media playback, or PDF feature. - [ ] Stock firmware / popular-fork comparison has not been established. This fixes contention in CrossPoint's existing core EPUB reader; its value is demonstrated against `develop` below. - [x] SDK/HAL/bootloader/OTA/recovery coordination is not applicable: none of those components are changed. This is an optimization of the existing reading engine, consistent with the reading-performance and code-cleanup priorities in SCOPE.md and ROADMAP.md. Changes are limited to the main-loop scheduling decision, the reader's background-work gates and scheduling hint, and the existing render-lock wrapper. ## Additional Context ### Locking and background work The existing background-work checks read the current section before taking the render lock, even though the render task can replace or delete that section. They also check whether the lock is free before acquiring it with a blocking wait; rendering can take the lock between those steps and stall the main loop until the render finishes. This PR addresses those problems in the paths that prepare the next page, extend a partially built section, and continue background pagination: - **Acquire the lock before reading section state.** Each path makes one nonblocking lock attempt. If rendering owns the lock, the path skips its work for that iteration without touching the section or waiting for rendering to finish. The main loop also reads the activity's scheduling hint under the lock. - **Share the decision about whether pagination has more work.** The scheduling hint and pagination loop use the same condition. The pagination loop still checks available heap before doing more work, so it can resume after a low-memory pause. - **Keep lock acquisition consistent.** Both blocking constructors use the same implementation, with an assertion for an unexpected acquisition failure. Most of the added diff moves existing conditions inside lock-protected scopes. The fixes and small refactors are separate commits so they can be reviewed individually. The section-access race and check-then-wait gap were identified by code inspection; no crash from them was reproduced in device testing. ### Performance These measurements compare `develop` at `038d615f` with the complete rebased PR at `0f1b86ee`. The rebase preserved all eight commit patches. Both builds use SDK `deb62ab7`, the `default` environment, and identical serial-control instrumentation. Each workload used three interleaved baseline/PR pairs on X3 in portrait with antialiasing enabled. Each boot included six warmup turns followed by six measured turns: **18 measured turns per firmware per workload**. Dedicated, byte-identical book copies had their render caches reset before each boot, with settings and font identity verified. Background pagination was active on both sides. **Korean: Demian, Pretendard 16 pt from SD** | Median time | Baseline | Rebased PR | Time reduction | |---|---:|---:|---:| | Font preparation | 205 ms | 136.5 ms | 33.4% | | Black-and-white drawing | 44 ms | 30 ms | 31.8% | | Black-and-white display refresh | 604.5 ms | 586 ms | 3.1% | | Grayscale LSB drawing | 138.5 ms | 102.5 ms | 26.0% | | Grayscale MSB drawing | 139 ms | 103 ms | 25.9% | | Grayscale display refresh | 229 ms | 229 ms | 0.0% | | Cleanup | 118.5 ms | 100 ms | 15.6% | | **All drawing subtotal** | **321.5 ms** | **235.5 ms** | **26.7%** | | **Total page rendering** | **1,476.5 ms** | **1,287 ms** | **12.8%** | | Cold section preparation + first render | 4,599 ms | 4,323 ms | 6.0% | **English: Aesop's Fables, built-in Noto Serif 16 pt** | Median time | Baseline | Rebased PR | Time reduction | |---|---:|---:|---:| | Font preparation | 32.5 ms | 25.5 ms | 21.5% | | Black-and-white drawing | 26 ms | 23 ms | 11.5% | | Black-and-white display refresh | 586 ms | 586 ms | 0.0% | | Grayscale LSB drawing | 94 ms | 89 ms | 5.3% | | Grayscale MSB drawing | 94 ms | 88.5 ms | 5.9% | | Grayscale display refresh | 229 ms | 229 ms | 0.0% | | Cleanup | 99 ms | 99 ms | 0.0% | | **All drawing subtotal** | **213.5 ms** | **200.5 ms** | **6.1%** | | **Total page rendering** | **1,160 ms** | **1,148.5 ms** | **1.0%** | | Cold section preparation + first render | 4,005 ms | 3,945 ms | 1.5% | Time reduction is `(Baseline - PR) / Baseline`. The drawing subtotal is calculated for each page as black-and-white drawing plus both grayscale passes, then summarized by its median. Each row is calculated independently, so stage medians need not sum to the total median. Grayscale drawing stages include strip uploads. These are elapsed firmware timings, not button-to-visible-page measurements. Cold preparation excludes earlier book metadata preparation. Per-pair total-render median reductions were **11.7-12.9% for Korean** and **1.0-1.1% for English**. The English benefit is concentrated on two of the six measured page positions: summed rendering time across all 18 samples decreases 7.0%, while the median matched per-page difference is 0 ms. The 1.0% headline compares the two firmware medians; these statistics describe different aspects of the same samples. <details> <summary>Comparison with the original measurement series</summary> The original series compared `develop` at `8c84ef32` with the initial scheduling commit, `79b2d773`. It predates the glyph optimizations in crosspoint-reader#3633. The rerun above covers the complete PR on the updated baseline. | Metric | Original baseline → PR | Rebased baseline → PR | |---|---:|---:| | Korean total-render median | 1,623 → 1,416 ms (12.8%) | 1,476.5 → 1,287 ms (12.8%) | | Korean drawing subtotal median | 487 → 367.5 ms (24.5%) | 321.5 → 235.5 ms (26.7%) | | English total-render median | 1,238 → 1,214 ms (1.9%) | 1,160 → 1,148.5 ms (1.0%) | | English drawing subtotal median | 289 → 267 ms (7.6%) | 213.5 → 200.5 ms (6.1%) | The Korean relative improvement is reproduced; the English median improvement is smaller. Both the baseline and candidate now render faster in absolute terms. Cross-revision changes cannot be attributed solely to this PR, and percentage gains from separate PRs should not be added together. </details> ### Resource impact - No new heap allocation, task, mutex, framebuffer or task-priority change. - Instrumented static RAM is unchanged at **57,424 bytes**. Application flash is **5,600,815 → 5,600,987 bytes (+172)**, including differing build identifiers. - Median post-page free heap is equal between baseline and PR: **45,512 bytes for Korean**, **111,200 bytes for English**. - Median largest free block is also equal: **25,588 bytes for Korean**, **102,388 bytes for English**. Korean baseline samples include both 21,492 and 25,588 bytes; candidate samples are 25,588. The cause of that variation is not established, and no fragmentation improvement is claimed. - Lowest since-boot free heap is **15,212 → 15,216 bytes for Korean** and **67,128 bytes on both builds for English**. These minima include startup and cold preparation. No memory-saving claim is made. ### Validation and limitations - Fresh instrumented baseline and candidate builds passed without compiler warnings. The repository formatting wrapper, diff whitespace check, frozen-image checks, and 26 host helper tests passed. Instrumentation and test helpers are outside the production diff. - All 12 boots completed the expected page sequences. All 12 paired checkpoint comparisons have identical black-and-white reading-area pixels. Full-frame differences, where present, are confined to the battery/charging footer. These captures do not establish grayscale-plane or physical-display equality. - No unexpected reset, panic, assertion or watchdog failure occurred during reading. Background pagination was observed on both builds; no logged power-mode transition overlapped the measured renders. - Every Korean boot logged the same low-heap CSS fallback during the measured window. Returning Home also logged inflate-window/JPEG thumbnail allocation failures on both builds, resulting in a placeholder cover for the test copy. The affected samples are retained in the results. - Both workloads logged a thumbnail-cache error before opening after the cache reset. Both builds also emitted the same early I²C probe errors. English cold-open-through-restoration windows had no error or low-heap CSS entries. - Two English screenshot transfers required a read-only retry outside the measured turns. Neither retry reset the device or discarded a measured boot; incomplete captures and timeout records were retained. The transfer stall remains unresolved. - Earlier testing found a settings-read failure that selected the wrong font and malformed entries in the original Demian cache. Those failures remain unresolved; the accepted comparisons use dedicated test copies with verified settings and font identity. - Coverage is these two workloads on X3 with background pagination active. Fully cached-only reading, physical-button latency, other devices/orientations, and release builds without instrumentation were not measured. The author's manual smoke test covered the initial scheduling commit; the rebased candidate was tested through serial control. - Original device firmware and saved SD metadata were restored and verified after testing. --- ### AI Usage **YES** — AI tools assisted with implementation, tests, performance analysis, and this PR description. The author also smoke-tested the initial scheduling firmware on hardware.
…er#3748) ## Summary Two fixes in one PR: 1. Toolbar menu in landscape mode Before:  After:  2. TTF font sizes. Extend the four sizes to fifteen.  --- ### 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 >**_
## Summary Fix crosspoint-reader#3703. Suppress crosspoint-reader#3707 <img width="483" height="806" alt="image" src="https://github.com/user-attachments/assets/2364041f-cf6b-42e3-b715-3b312c05d9d3" /> Test epubs [crosspoint-3703-test-books.zip](https://github.com/user-attachments/files/32689451/crosspoint-3703-test-books.zip) --- ### 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 >**_
Ported from crosspoint-reader/crosspoint-reader@93e98bb. Adapted to the CrossInk release environment matrix.
Ported from crosspoint-reader/crosspoint-reader@5bfaf7b. Preserves CrossInk dark-mode icon inversion.
Ported from crosspoint-reader/crosspoint-reader@f7eedba. CrossInk keeps X3 frame restoration and moves X4 ghost cleanup into the first Home paint. Co-authored-by: Julia Nguyen <julia@uxj.io>
Ported from crosspoint-reader/crosspoint-reader@6743b68. Keeps CrossInk filesystem helpers and adds the upstream regression tests.
Ported from crosspoint-reader/crosspoint-reader@0914aa1. Uses CrossInk's existing centered text wrapper.
Ported from crosspoint-reader/crosspoint-reader@3f70f5d. CrossInk already logs internal heap and PSRAM together; only the Serial gate needed changing.
Ported from crosspoint-reader/crosspoint-reader@637ad4a. Inverted Tap remains an explicit reversal, and TXT/XTC keep their current directions.
Ported from crosspoint-reader/crosspoint-reader@1d61100. CrossInk transfers from a short-lived ReaderActivity into separate EPUB/TXT activities, so their exits own the cleanup. Co-authored-by: Julia Nguyen <julia@uxj.io>
Ported from crosspoint-reader/crosspoint-reader@794ac97f4. Adapted to CrossInk layout APIs and bumped both full and suspended section cache versions (79 and 0xF4). Eight focused Korean layout tests pass. Co-authored-by: Julia Nguyen <julia@uxj.io>
Adapted from crosspoint-reader/crosspoint-reader@b88b653a7. CrossInk stores link IDs in page words instead of PageLink rectangles, so this port shares the existing touch geometry. The old list remains available for pages without complete link targets. Co-authored-by: Julia Nguyen <julia@uxj.io>
Adapted from crosspoint-reader/crosspoint-reader@362c88112. CrossInk reuses its EPUB cover cache, Library index, and Lyra theme while limiting the grid to PSRAM devices. Home checks index freshness and shows the Library scan message before rebuilding. Co-authored-by: Uri Tauber <uritaube@gmail.com> Co-authored-by: Julia Nguyen <julia@uxj.io>
…s the device while asleep.
Conflicts resolved to keep CrossInk changes on top of upstream: - Side buttons: took upstream's per-button SideButtonShortcuts; kept our first-page no-op for previous chapter (EPUB, XTC) and re-clear the EPUB background-build input yield after a consumed side release. - EndOfBookOptions: kept our readMenuKey/applyMenuKey split with upstream's release-only side navigation. - Library: kept our latched input, row cache and background index; added upstream's portrait rotation and cover-grid paging (incl. horizontal swipes). - main: periodic memory log keeps our 2 s onlyIfChanged cadence plus upstream's ROM transport; stats backup honours the tracking toggle. - Section cache v79 / partial 0xF4 per upstream; release.yml, CHANGELOG and docs merged; freeink-sdk stays on the prokrypt fork pin. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgLRarbAmBVepTPKCK7mgd
- SectionPersistenceTest: expect cache version 79 / partial 0xF4, and treat our old 78 / 0xF2 files as stale. - clang-format LibraryActivity after the conflict resolution. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgLRarbAmBVepTPKCK7mgd
…s code Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgLRarbAmBVepTPKCK7mgd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before: development was at upstream a6b2e8e.
After: development includes 60 newer upstream commits, up to uxjulia/crossink 965b4bd. Highlights:
How: a merge commit, with conflicts resolved to keep CrossInk's changes. The merge commit message lists every file and how its conflict was resolved. The SDK stays on the prokrypt fork pin. The EPUB section cache moves to v79 (partial 0xF4), so cached sections rebuild once.
Sizes vs development e0c27ab:
All 838 unit tests pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_01XgLRarbAmBVepTPKCK7mgd
Generated by Claude Code