App themes (#217)

* Updated `.gitattributes` to correctly vendor all files within the `libmobi` and `woff2` directories.

* Improved TTS chapter transition handling

* Improved TTS stability and chapter advancement in the EPUB reader.

* Implemented PDF metadata extraction for titles and authors using PdfiumCore in `MainViewModel` and `MetadataExtractionWorker`.

* Implemented support for interactive footnotes in the EPUB vertical reader.

* Added support for footnotes in the paginated EPUB reader.

* Implemented a customizable app theming system.

* Added support for app-wide contrast and text brightness customization in the "App Theme" option.

* option to adjust pull-to-chapter-change drag in the EPUB vertical reader under visual options.

* Updated `PdfViewerScreen` to replace the standalone full-screen toggle with a more comprehensive "Visual Options" system UI management tool.
This commit is contained in:
Aryan 2026-04-21 18:11:55 +05:30 committed by GitHub
parent 4cb25e6abd
commit 71e3614ad6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1413 additions and 287 deletions

View file

@ -442,14 +442,70 @@
return false;
}
function getFootnoteContent(targetId) {
console.log("FootnoteDiag: Searching for footnote content with id: '" + targetId + "'");
var el = document.getElementById(targetId);
if (el) {
console.log("FootnoteDiag: Found element directly in DOM.");
return el.innerHTML;
}
console.log("FootnoteDiag: Element not in DOM, checking virtualized chunks.");
if (window.virtualization && window.virtualization.chunksData) {
for (var i = 0; i < window.virtualization.chunksData.length; i++) {
var chunkHtml = window.virtualization.chunksData[i];
if (chunkHtml && chunkHtml.indexOf('id="' + targetId + '"') !== -1) {
console.log("FootnoteDiag: Found potential id match in chunk " + i);
var tempDiv = document.createElement('div');
tempDiv.innerHTML = chunkHtml;
var found = tempDiv.querySelector('#' + targetId);
if (found) {
console.log("FootnoteDiag: Successfully extracted note from chunk " + i + ".");
return found.innerHTML;
}
}
}
}
console.log("FootnoteDiag: Footnote content not found anywhere.");
return null;
}
// 1. Handle Taps (Click)
document.addEventListener(
"click",
function (e) {
handleHighlightInteraction(e);
},
true
);
document.addEventListener("click", function (e) {
if (handleHighlightInteraction(e)) return;
var target = e.target;
var anchor = target.closest('a');
if (anchor) {
var href = anchor.getAttribute('href');
var epubType = anchor.getAttribute('epub:type');
console.log("FootnoteDiag: Link clicked. href: '" + href + "', epub:type: '" + epubType + "'");
if ((href && href.startsWith('#')) || epubType === 'noteref') {
var targetId = href ? href.substring(1) : null;
console.log("FootnoteDiag: Extracted targetId: '" + targetId + "'");
if (targetId) {
var content = getFootnoteContent(targetId);
if (content && window.FootnoteBridge) {
console.log("FootnoteDiag: Content extracted, sending to Kotlin Bridge.");
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
window.FootnoteBridge.onFootnoteRequested(content);
return;
} else if (!content) {
console.log("FootnoteDiag: Failed to get content. Link might just be a regular anchor.");
} else if (!window.FootnoteBridge) {
console.log("FootnoteDiag: window.FootnoteBridge is undefined!");
}
}
}
}
}, true);
window.updateReaderStyles = function (fontSizeEm, lineHeight, fontFamily, textAlign, paragraphGap) {
var logTag = "ReaderFontDiagnosis";
@ -1094,13 +1150,14 @@
};
window.extractTextWithCfiFromTop = function () {
console.log("TTS_CHAPTER_CHANGE_DIAG: Starting extractTextWithCfiFromTop");
try {
const ttsNodeSelector = "p, h1, h2, h3, h4, h5, h6, li, blockquote";
const allContentNodesRaw = Array.from(document.body.querySelectorAll(ttsNodeSelector));
// FIX: Filter out parents that contain matching children to prevent duplicates
const allContentNodes = allContentNodesRaw.filter(node => node.querySelector(ttsNodeSelector) === null);
console.log("TTS_CHAPTER_CHANGE_DIAG: Total nodes found: " + allContentNodes.length);
let startBlock = null;
let startIndex = -1;
@ -1116,9 +1173,12 @@
}
if (!startBlock) {
console.log("TTS_CHAPTER_CHANGE_DIAG: No visible start block found in viewport.");
return window.extractTextWithCfi();
}
console.log("TTS_CHAPTER_CHANGE_DIAG: Starting extraction from node index: " + startIndex);
const nodesToProcess = allContentNodes.slice(startIndex);
const results =[];
@ -1136,6 +1196,7 @@
return JSON.stringify(results);
} catch (e) {
console.log("TTS_CHAPTER_CHANGE_DIAG: Error in extractTextWithCfiFromTop: " + e.message);
return window.extractTextWithCfi();
}
};
@ -1240,13 +1301,20 @@
window.TtsBridgeHelper = {
extractAndRelayText: function () {
const traceId = Date.now();
console.log("TTS_CHAPTER_CHANGE_DIAG: [" + traceId + "] extractAndRelayText invoked.");
try {
const structuredTextJson = window.extractTextWithCfiFromTop();
const len = structuredTextJson ? structuredTextJson.length : 0;
console.log("TTS_CHAPTER_CHANGE_DIAG: [" + traceId + "] Relay text JSON length: " + len);
if (typeof TtsBridge !== "undefined" && TtsBridge.onStructuredTextExtracted) {
TtsBridge.onStructuredTextExtracted(structuredTextJson);
} else {
console.log("TTS_CHAPTER_CHANGE_DIAG: [" + traceId + "] Bridge missing!");
}
} catch (e) {
console.log("TTS_CHAPTER_CHANGE_DIAG: [" + traceId + "] Error: " + e.message);
if (typeof TtsBridge !== "undefined" && TtsBridge.onStructuredTextExtracted) {
TtsBridge.onStructuredTextExtracted("[]");
}