Skip to content

fix: Fixed book title in home screen#1013

Merged
daveallie merged 6 commits intocrosspoint-reader:masterfrom
DestinySpeaker:fix/book-title-in-home-screen
Feb 22, 2026
Merged

fix: Fixed book title in home screen#1013
daveallie merged 6 commits intocrosspoint-reader:masterfrom
DestinySpeaker:fix/book-title-in-home-screen

Conversation

@DestinySpeaker
Copy link
Contributor

Summary

  • What is the goal of this PR? (e.g., Implements the new feature for file uploading.)
  • The goal is to fix the title of books in the Home Screen.

Before
IMG_8867

After:
IMG_8868

  • 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? YES, Cursor

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds multi-line, word-wrapped recent-book title rendering, limits metadata title capture to the first dc:title, switches wrapping width calculations to text-advance metrics, and bumps recent-books file version with load-time migration and metadata refresh logic.

Changes

Cohort / File(s) Summary
Metadata Parsing
lib/Epub/Epub/parsers/ContentOpfParser.cpp
When parsing IN_METADATA, only the first dc:title is recorded as the book title; subsequent dc:title elements are ignored for title capture.
Wrapping Logic
src/components/themes/BaseTheme.cpp
Reworked recent-book title wrapping to use text-advance metrics, avoid over-trimming very short lines (≤3 chars), guard against unfittable words, and explicitly handle starting new lines.
Title Rendering (Lyra)
src/components/themes/lyra/LyraTheme.cpp
Replace single-line title rendering with up to 3-line, word-wrapped title rendering (ellipsis handling); add UTF‑8/vector support; compute vertical centering for title block and author and render each title line.
Recent Books Storage / Migration
src/RecentBooksStore.cpp
Bump RECENT_BOOKS_FILE_VERSION 3→4; add migration path that re-parses entries via getDataFromBook(path) for older files; on load, refresh missing metadata by querying the book and save file when metadata was refreshed.

Sequence Diagram(s)

sequenceDiagram
    participant Loader as RecentBooksStore::loadFromFile
    participant Disk as Disk/File
    participant Parser as getDataFromBook
    participant Store as RecentBooksStore::saveToFile

    Loader->>Disk: open recent-books file
    Disk-->>Loader: return file header (version)
    alt file version < current
        Loader->>Disk: read legacy entries
        loop for each entry
            Loader->>Parser: getDataFromBook(path)
            Parser-->>Loader: metadata/enriched entry
            alt enriched non-empty
                Loader->>Loader: replace legacy entry with enriched data
            else
                Loader->>Loader: keep legacy entry
            end
        end
        Loader->>Disk: close file
        Loader->>Store: saveToFile(updated list)
        Store-->>Disk: write new file (v4)
    else version >= current
        loop for each entry
            Loader->>Disk: read entry
            alt title empty
                Loader->>Parser: getDataFromBook(path)
                Parser-->>Loader: metadata (maybe)
                Loader->>Loader: fill missing fields if available
            end
        end
        alt metadataRefreshed
            Loader->>Store: saveToFile(updated list)
            Store-->>Disk: write refreshed file
        end
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested Reviewers

  • daveallie
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Fixed book title in home screen' is directly related to the main objective of the PR, which is to fix how book titles are displayed in the home screen. The changeset includes modifications to title handling, wrapping logic, and metadata loading that collectively address this issue.
Description check ✅ Passed The PR description clearly explains the goal (fix book title display in home screen) and provides before/after screenshots demonstrating the improvement from a truncated title to the full title being displayed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/themes/lyra/LyraTheme.cpp (1)

502-510: Consider extracting max lines as a named constant.

The magic number 3 appears multiple times for the maximum line count. A named constant would improve readability and make future adjustments easier.

Suggested refactor

Add to namespace at top of file:

constexpr int maxTitleLines = 3;

Then replace occurrences:

-    if (titleLines.size() >= 3) {
+    if (titleLines.size() >= maxTitleLines) {
-    if (!currentLine.empty() && titleLines.size() < 3) titleLines.push_back(currentLine);
+    if (!currentLine.empty() && titleLines.size() < maxTitleLines) titleLines.push_back(currentLine);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/themes/lyra/LyraTheme.cpp` around lines 502 - 510, Extract the
hard-coded max line count (3) into a named constant (e.g. add constexpr int
maxTitleLines = 3; in the file's namespace) and replace all literal `3` usages
that govern title wrapping/ellipsis logic — specifically in the block handling
titleLines.size() checks, titleLines.back().resize(... - 3), and any loop that
relies on that count — ensuring you still call renderer.getTextWidth(...) and
utf8RemoveLastChar(...) as before but use maxTitleLines for readability and
future adjustments.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cabbfcf and 9b2589d.

📒 Files selected for processing (3)
  • lib/Epub/Epub/parsers/ContentOpfParser.cpp
  • src/components/themes/BaseTheme.cpp
  • src/components/themes/lyra/LyraTheme.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-19T12:17:00.962Z
Learnt from: Uri-Tauber
Repo: crosspoint-reader/crosspoint-reader PR: 988
File: lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp:649-661
Timestamp: 2026-02-19T12:17:00.962Z
Learning: In ChapterHtmlSlimParser.cpp, when computing footnote word indices in endElement() for footnote links, the wordIndex must be cumulative across the 750-word mid-paragraph flush boundary. The correct calculation is: `wordIndex = wordsExtractedInBlock + currentTextBlock->size()`, not just `currentTextBlock->size()`. This ensures footnotes attach to the page containing their actual anchor word, even after layoutAndExtractLines(false) has extracted and removed earlier words from the block.

Applied to files:

  • src/components/themes/BaseTheme.cpp
🧬 Code graph analysis (1)
src/components/themes/BaseTheme.cpp (1)
src/main.cpp (1)
  • renderer (39-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cppcheck
  • GitHub Check: build
🔇 Additional comments (4)
lib/Epub/Epub/parsers/ContentOpfParser.cpp (1)

104-110: LGTM!

The guard correctly ensures only the first dc:title is captured. The state machine remains consistent: subsequent dc:title elements stay in IN_METADATA, so characterData ignores them and endElement (line 367) is a no-op since state != IN_BOOK_TITLE.

Minor note: EPUB 3 allows multiple dc:title elements with refinements (title-type like "main", "subtitle"). Assuming the first is always the primary title is a reasonable convention and aligns with typical publisher behavior.

src/components/themes/lyra/LyraTheme.cpp (2)

6-10: LGTM!

The added includes for Utf8.h and <vector> are necessary for the new multi-line title wrapping implementation.


537-550: LGTM!

The vertical alignment logic correctly calculates the total block height (title lines + author) and centers it within the tile. The per-line rendering with explicit heights provides proper spacing.

src/components/themes/BaseTheme.cpp (1)

519-531: Metric consistency between word trimming and line wrapping should be verified

At line 506-513, word trimming uses getTextWidth (bounding box), but line 519-523 uses getTextAdvanceX (glyph advance sum) for wrapping. These metrics can differ—if advance width exceeds visual width, a trimmed word could still overflow maxLineWidth. Consider using getTextAdvanceX consistently for both trimming and wrapping, or document why this mixed approach is intentional. Note: LyraTheme.cpp uses the same pattern, so any change should be coordinated across themes.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 504-509: The truncation loop that modifies titleLines.back() can
underflow when calling resize(titleLines.back().size() - 3); update the loop in
LyraTheme.cpp to guard the subtract-by-3 by checking the current string size
(use titleLines.back().size() > 3) before calling resize or otherwise set the
string to empty if <= 3, then call utf8RemoveLastChar and append "..." as
before; reference the loop using titleLines.back(), renderer.getTextWidth(...,
UI_12_FONT_ID, ...), and utf8RemoveLastChar to locate and fix the code path.
- Around line 513-521: The truncation loop in LyraTheme.cpp that shrinks a word
(using utf8RemoveLastChar) can produce an empty w when even "..." won't fit;
after the while loop that updates wordW and w, add a guard that if w.empty()
then either set w to "..." only if that fits (check renderer.getTextWidth with
"..." and maxLineWidth) or skip appending this word to currentLine entirely
(avoid pushing an empty string into currentLine); ensure the check uses the same
variables (wordW, maxLineWidth, w, renderer) and update currentLine only when w
is non-empty or when "..." fits.

---

Nitpick comments:
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 502-510: Extract the hard-coded max line count (3) into a named
constant (e.g. add constexpr int maxTitleLines = 3; in the file's namespace) and
replace all literal `3` usages that govern title wrapping/ellipsis logic —
specifically in the block handling titleLines.size() checks,
titleLines.back().resize(... - 3), and any loop that relies on that count —
ensuring you still call renderer.getTextWidth(...) and utf8RemoveLastChar(...)
as before but use maxTitleLines for readability and future adjustments.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/themes/BaseTheme.cpp (1)

497-525: ⚠️ Potential issue | 🟡 Minor

Use consistent text metrics for truncation and line fitting.

Line wrapping (lines 521–525) uses getTextAdvanceX, but the truncation loop (lines 498, 507, 513) uses getTextWidth. These metrics can diverge: a word trimmed to fit getTextWidth may still exceed maxLineWidth when measured by getTextAdvanceX, causing overflow (especially when currentLine is empty and the overflow check is skipped). Use getTextAdvanceX consistently for both truncation and line-fit checks.

Suggested fix (consistent metric)
         while (!lines.back().empty() && lines.back().size() > 3 &&
-               renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) {
+               renderer.getTextAdvanceX(UI_12_FONT_ID, lines.back().c_str(), EpdFontFamily::REGULAR) >
+                   maxLineWidth) {
           // Remove "..." first, then remove one UTF-8 char, then add "..." back
           lines.back().resize(lines.back().size() - 3);  // Remove "..."
           utf8RemoveLastChar(lines.back());
           lines.back().append("...");
         }
         break;
       }
 
-      int wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str());
+      int wordWidth = renderer.getTextAdvanceX(UI_12_FONT_ID, i.c_str(), EpdFontFamily::REGULAR);
       while (wordWidth > maxLineWidth && !i.empty()) {
         // Word itself is too long, trim it (UTF-8 safe)
         utf8RemoveLastChar(i);
         // Check if we have room for ellipsis
         std::string withEllipsis = i + "...";
-        wordWidth = renderer.getTextWidth(UI_12_FONT_ID, withEllipsis.c_str());
+        wordWidth = renderer.getTextAdvanceX(UI_12_FONT_ID, withEllipsis.c_str(), EpdFontFamily::REGULAR);
         if (wordWidth <= maxLineWidth) {
           i = withEllipsis;
           break;
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/themes/BaseTheme.cpp` around lines 497 - 525, The truncation
loop uses renderer.getTextWidth while later fitting uses
renderer.getTextAdvanceX, causing inconsistent measurements; update the
truncation and ellipsis-check logic to use renderer.getTextAdvanceX (same font
id/UI_12_FONT_ID and EpdFontFamily where applicable) instead of getTextWidth so
trimmed words and the withEllipsis check use the same metric as the newLineWidth
calculation; adjust calls around utf8RemoveLastChar, the withEllipsis
construction, and any comparisons to maxLineWidth (and ensure the initial
empty-currentLine overflow check still applies) so lines, i, and currentLine are
measured consistently with getTextAdvanceX.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b2589d and ae5eba3.

📒 Files selected for processing (2)
  • src/components/themes/BaseTheme.cpp
  • src/components/themes/lyra/LyraTheme.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-19T12:17:00.962Z
Learnt from: Uri-Tauber
Repo: crosspoint-reader/crosspoint-reader PR: 988
File: lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp:649-661
Timestamp: 2026-02-19T12:17:00.962Z
Learning: In ChapterHtmlSlimParser.cpp, when computing footnote word indices in endElement() for footnote links, the wordIndex must be cumulative across the 750-word mid-paragraph flush boundary. The correct calculation is: `wordIndex = wordsExtractedInBlock + currentTextBlock->size()`, not just `currentTextBlock->size()`. This ensures footnotes attach to the page containing their actual anchor word, even after layoutAndExtractLines(false) has extracted and removed earlier words from the block.

Applied to files:

  • src/components/themes/lyra/LyraTheme.cpp
  • src/components/themes/BaseTheme.cpp
🧬 Code graph analysis (1)
src/components/themes/BaseTheme.cpp (1)
src/main.cpp (1)
  • renderer (39-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/components/themes/lyra/LyraTheme.cpp (3)

6-10: LGTM!

The new includes are correctly added for the multi-line title wrapping feature: <Utf8.h> provides utf8RemoveLastChar() for proper UTF-8 character removal, and <vector> is needed for std::vector<std::string> to store title lines.


485-536: Word-wrapping algorithm looks solid.

The logic correctly handles: word splitting, line building with up to 3 lines, ellipsis truncation for overflow, and oversized single-word truncation. The past review concerns about underflow in the ellipsis loop (line 504 guard) and empty truncated words (line 522 skip) are addressed.

Minor observation: initial word-width check at line 512 uses getTextWidth, while line-building (lines 523-525) uses getTextAdvanceX. These typically return similar values, but could theoretically differ. If any edge-case truncation issues appear, this would be worth investigating.


538-551: LGTM!

The vertical centering and rendering logic is well-structured. The block height calculation correctly accounts for title lines and optional author with appropriate spacing. The rendering loop properly iterates through title lines and conditionally renders the author with a half-line gap.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/components/themes/BaseTheme.cpp`:
- Around line 497-525: The truncation loop uses renderer.getTextWidth while
later fitting uses renderer.getTextAdvanceX, causing inconsistent measurements;
update the truncation and ellipsis-check logic to use renderer.getTextAdvanceX
(same font id/UI_12_FONT_ID and EpdFontFamily where applicable) instead of
getTextWidth so trimmed words and the withEllipsis check use the same metric as
the newLineWidth calculation; adjust calls around utf8RemoveLastChar, the
withEllipsis construction, and any comparisons to maxLineWidth (and ensure the
initial empty-currentLine overflow check still applies) so lines, i, and
currentLine are measured consistently with getTextAdvanceX.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/RecentBooksStore.cpp (1)

138-160: Migration logic is correct; consider checking saveToFile() result.

The migration path correctly reads version 3 format, re-parses book metadata via getDataFromBook(), and falls back to cached data when the book file cannot be loaded. One minor observation:

Line 158: saveToFile() return value is ignored. If the save fails, migration will appear successful but won't persist—causing a re-migration on every subsequent load. This is self-correcting but inefficient on constrained devices.

💡 Optional: Log a warning if save fails
       }
       inputFile.close();
-      saveToFile();
+      if (!saveToFile()) {
+        LOG_WRN("RBS", "Migration completed but failed to persist; will re-migrate on next load");
+      }
       LOG_DBG("RBS", "Migrated recent books from version %u, re-parsed %u entries", version, count);
       return true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/RecentBooksStore.cpp` around lines 138 - 160, The migration currently
calls saveToFile() and ignores its result; update the migration branch inside
RecentBooksStore (where you read count, fill recentBooks using
getDataFromBook(), then call saveToFile()) to capture the boolean return, and if
saveToFile() fails emit a warning (e.g., LOG_WRN) and return false so the caller
knows migration did not persist; keep the existing fallback push_back behavior
for RecentBook entries but ensure you check the saveToFile() return and handle
failure instead of always returning true.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae5eba3 and 29217cf.

📒 Files selected for processing (1)
  • src/RecentBooksStore.cpp
🔇 Additional comments (1)
src/RecentBooksStore.cpp (1)

14-14: LGTM!

Version bump to 4 correctly triggers the migration path for users with version 3 files, enabling the re-parsing of book metadata with the updated title logic.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/RecentBooksStore.cpp`:
- Around line 138-160: The migration currently calls saveToFile() and ignores
its result; update the migration branch inside RecentBooksStore (where you read
count, fill recentBooks using getDataFromBook(), then call saveToFile()) to
capture the boolean return, and if saveToFile() fails emit a warning (e.g.,
LOG_WRN) and return false so the caller knows migration did not persist; keep
the existing fallback push_back behavior for RecentBook entries but ensure you
check the saveToFile() return and handle failure instead of always returning
true.

Copy link
Member

@daveallie daveallie left a comment

Choose a reason for hiding this comment

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

Overall looks good, mostly just a little confused as to why the changes in src/RecentBooksStore.cpp are necessary.

const int spaceWidth = renderer.getSpaceWidth(UI_12_FONT_ID, EpdFontFamily::BOLD);
std::vector<std::string> titleLines;
std::string currentLine;
for (auto& w : words) {
Copy link
Contributor

@CaptainFrito CaptainFrito Feb 20, 2026

Choose a reason for hiding this comment

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

It would be great if the text wrapping function could be centralized elsewhere, like in https://github.com/crosspoint-reader/crosspoint-reader/pull/911/changes
That way we could also clean up the classic theme home screen implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it require us to refactor the entire Lyra theme?

Copy link
Member

Choose a reason for hiding this comment

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

Agree that following #911, we should amend these implementations to reduce code duplication and improve readability, but I am also keen to get this one into 1.1, so will merge as is and we can clean up all the implementations once #911 is merged.

const int spaceWidth = renderer.getSpaceWidth(UI_12_FONT_ID, EpdFontFamily::BOLD);
std::vector<std::string> titleLines;
std::string currentLine;
for (auto& w : words) {
Copy link
Member

Choose a reason for hiding this comment

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

Agree that following #911, we should amend these implementations to reduce code duplication and improve readability, but I am also keen to get this one into 1.1, so will merge as is and we can clean up all the implementations once #911 is merged.

@daveallie daveallie merged commit 10a2678 into crosspoint-reader:master Feb 22, 2026
6 checks passed
@DestinySpeaker DestinySpeaker deleted the fix/book-title-in-home-screen branch February 22, 2026 01:59
daveallie pushed a commit that referenced this pull request Feb 22, 2026
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
* The goal is to fix the title of books in the Home Screen. 

Before

![IMG_8867](https://github.com/user-attachments/assets/6cc9ca22-b95b-4863-872d-ef427c42f833)

After:

![IMG_8868](https://github.com/user-attachments/assets/585031b1-2348-444c-8f32-073fed3b6582)

* **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? YES, Cursor
ngxson pushed a commit that referenced this pull request Feb 25, 2026
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)


* **What changes are included?**
Conrgegate the changes of #1074 , #1013 , and extended upon #911 by
@lkristensen
New function implemented in GfxRenderer.cpp
```C++
std::vector<std::string> GfxRenderer::wrappedText(const int fontId, const char* text, const int maxWidth,
                                                  const int maxLines, const EpdFontFamily::Style style) const
```
Applied logic to all uses in Lyra, Lyra Extended, and base theme
(continue reading card as pointed out by @znelson


## Additional Context





![IMG_8604](https://github.com/user-attachments/assets/49da71c9-a44f-4cde-b3bf-6773d71601b6)

![IMG_8605](https://github.com/user-attachments/assets/5eab4293-65c1-47fb-b422-8ab53a6b50a2)

![IMG_8606](https://github.com/user-attachments/assets/e0f98d19-0e3f-4294-83a1-e49264378dca)


---

### 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: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
el pushed a commit to el/crosspoint-reader that referenced this pull request Feb 26, 2026
…oint-reader#1141)

## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)


* **What changes are included?**
Conrgegate the changes of crosspoint-reader#1074 , crosspoint-reader#1013 , and extended upon crosspoint-reader#911 by
@lkristensen
New function implemented in GfxRenderer.cpp
```C++
std::vector<std::string> GfxRenderer::wrappedText(const int fontId, const char* text, const int maxWidth,
                                                  const int maxLines, const EpdFontFamily::Style style) const
```
Applied logic to all uses in Lyra, Lyra Extended, and base theme
(continue reading card as pointed out by @znelson


## Additional Context





![IMG_8604](https://github.com/user-attachments/assets/49da71c9-a44f-4cde-b3bf-6773d71601b6)

![IMG_8605](https://github.com/user-attachments/assets/5eab4293-65c1-47fb-b422-8ab53a6b50a2)

![IMG_8606](https://github.com/user-attachments/assets/e0f98d19-0e3f-4294-83a1-e49264378dca)


---

### 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: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants