From 97627be0da9ea414678de2c906a71bfcfb26f194 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Sat, 13 Jun 2026 12:38:38 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E9=98=85?= =?UTF-8?q?=E8=AF=BB=E6=90=9C=E7=B4=A2=E5=AE=9A=E4=BD=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/SearchContentRepository.kt | 43 ++++- .../app/ui/book/read/ReadBookContract.kt | 8 +- .../app/ui/book/read/ReadBookController.kt | 73 +++++--- .../app/ui/book/read/ReadBookRouteScreen.kt | 24 ++- .../app/ui/book/read/ReadBookSearchBar.kt | 18 +- .../app/ui/book/read/ReadBookViewModel.kt | 163 +++++++++--------- .../searchContent/SearchContentViewModel.kt | 7 +- 7 files changed, 195 insertions(+), 141 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/repository/SearchContentRepository.kt b/app/src/main/java/io/legado/app/data/repository/SearchContentRepository.kt index ef53d7528..6e3457826 100644 --- a/app/src/main/java/io/legado/app/data/repository/SearchContentRepository.kt +++ b/app/src/main/java/io/legado/app/data/repository/SearchContentRepository.kt @@ -7,6 +7,7 @@ import io.legado.app.help.book.BookHelp import io.legado.app.help.book.ContentProcessor import io.legado.app.help.book.isLocal import io.legado.app.help.config.AppConfig +import io.legado.app.help.config.ReadBookConfig import io.legado.app.ui.book.searchContent.SearchResult import io.legado.app.utils.ChineseUtils import kotlinx.coroutines.Dispatchers @@ -21,8 +22,13 @@ class SearchContentRepository { private var lastSearchResults: List? = null private var lastQueryKey: String? = null - fun getCache(bookUrl: String, query: String): List? { - val key = "$bookUrl-$query" + fun getCache( + bookUrl: String, + query: String, + replaceEnabled: Boolean, + regexReplace: Boolean + ): List? { + val key = searchKey(bookUrl, query, replaceEnabled, regexReplace) return if (lastQueryKey == key) lastSearchResults else null } @@ -61,10 +67,19 @@ class SearchContentRepository { } } lastSearchResults = allResults - lastQueryKey = "${book.bookUrl}-$query" + lastQueryKey = searchKey(book.bookUrl, query, replaceEnabled, regexReplace) emit(ArrayList(allResults)) }.flowOn(Dispatchers.Default) + private fun searchKey( + bookUrl: String, + query: String, + replaceEnabled: Boolean, + regexReplace: Boolean + ): String { + return "$bookUrl-$query-$replaceEnabled-$regexReplace-${ReadBookConfig.titleMode}" + } + private suspend fun searchChapter( query: String, book: Book, @@ -82,9 +97,25 @@ class SearchContentRepository { else -> chapter.title } - val mContent = contentProcessor.getContent( - book, chapter, chapterContent, useReplace = replaceEnabled - ).toString() + val bodyContent = contentProcessor.getContent( + book, + chapter, + chapterContent, + includeTitle = false, + useReplace = replaceEnabled + ) + val includeTitle = ReadBookConfig.titleMode != 2 || + chapter.isVolume || + bodyContent.textList.isEmpty() + val mContent = if (includeTitle) { + val title = chapter.getDisplayTitle( + contentProcessor.getTitleReplaceRules(), + useReplace = replaceEnabled && book.getUseReplaceRule() + ) + listOf(title).plus(bodyContent.textList).joinToString("\n") + } else { + bodyContent.toString() + } val matches = searchPosition(mContent, query, regexReplace) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt index 1663226e3..3df4ca09a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt @@ -258,6 +258,7 @@ sealed interface ReadBookIntent { data class SetSearchResults(val results: List, val index: Int, val query: String? = null) : ReadBookIntent data class SetSearchResultIndex(val index: Int) : ReadBookIntent data class SetShowingSearchResult(val value: Boolean) : ReadBookIntent + data class NavigateSearchResultByOffset(val offset: Int) : ReadBookIntent data class NavigateToSearchResult(val result: SearchResult, val index: Int) : ReadBookIntent data object RestoreLastBookProgress : ReadBookIntent data object KeepCurrentBookProgress : ReadBookIntent @@ -515,7 +516,11 @@ sealed interface ReadBookEffect { // ReadView operations (require Activity/View reference) data class UpdateReadViewConfig(val actions: Set) : ReadBookEffect - data class UpContent(val relativePosition: Int, val resetPageOffset: Boolean) : ReadBookEffect + data class UpContent( + val relativePosition: Int, + val resetPageOffset: Boolean, + val success: (() -> Unit)? = null, + ) : ReadBookEffect data class UpPageAnim(val upRecorder: Boolean) : ReadBookEffect data object UpTime : ReadBookEffect data class UpBattery(val level: Int) : ReadBookEffect @@ -547,6 +552,7 @@ sealed interface ReadBookEffect { val pageIndex: Int, val lineIndex: Int, val startCharIndex: Int, + val endRelativePage: Int, val endLineIndex: Int, val endCharIndex: Int, ) : ReadBookEffect diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt index aa948b042..72c02227a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt @@ -116,6 +116,7 @@ class ReadBookController( } private val popupAction by lazy { PopupAction(activity) } private var screenTimeOut: Long = 0 + private var pendingSearchResultMark: IntArray? = null // justInitData moved to ViewModel (set on InitData intent) val isAutoPage: Boolean get() = refs?.readView?.isAutoPage == true @@ -599,6 +600,10 @@ class ReadBookController( is ReadBookEffect.UpContent -> { refs?.readView?.upContent(effect.relativePosition, effect.resetPageOffset) + effect.success?.invoke() + refs?.readView?.post { + consumePendingSearchResultMark() + } if (effect.relativePosition == 0) onUnhandledEffect(ReadBookEffect.UpSeekBar) } @@ -611,7 +616,10 @@ class ReadBookController( ReadBook.loadContent(false) } - is ReadBookEffect.CancelSelect -> refs?.readView?.cancelSelect() + is ReadBookEffect.CancelSelect -> { + pendingSearchResultMark = null + refs?.readView?.cancelSelect() + } is ReadBookEffect.MenuImageStyleChanged -> refs?.readView?.upPageAnim() // ── Simple Activity-API effects ── @@ -707,37 +715,35 @@ class ReadBookController( is ReadBookEffect.NavigateToSearchResult -> { if (effect.pageIndex < 0) { // Chapter not loaded — open it, then mark in the success callback - ReadBook.openChapter(effect.chapterIndex) { + ReadBook.openChapter( + effect.chapterIndex, + effect.result.queryIndexInChapter + ) { val tc = ReadBook.curTextChapter ?: return@openChapter val query = effect.result.query val pos = viewModel.searchResultPositions(tc, effect.result, query) - val lineIndex = pos[1] - val charIndex = pos[2] - val endLineIndex = pos[3] - val endCharIndex = pos[4] + if (pos[0] < 0) return@openChapter activity.lifecycleScope.launch(Main) { - navigatePageByPos(tc, pos[0]) - markSearchResultOnPage( - intArrayOf( - pos[0], lineIndex, charIndex, endLineIndex, endCharIndex - ) + markSearchResultAfterNavigation( + intArrayOf(pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]) ) } } } else { // Same chapter — navigate to page, then mark val tc = ReadBook.curTextChapter ?: return - navigatePageByPos(tc, effect.pageIndex) - markSearchResultOnPage( + markSearchResultAfterNavigation( intArrayOf( effect.pageIndex, effect.lineIndex, - effect.startCharIndex, effect.endLineIndex, effect.endCharIndex + effect.startCharIndex, effect.endRelativePage, + effect.endLineIndex, effect.endCharIndex ) ) } } is ReadBookEffect.ExitSearch -> { + pendingSearchResultMark = null ReadBook.clearSearchResult() refs?.readView?.cancelSelect(clearSearchResult = true) } @@ -1105,33 +1111,46 @@ class ReadBookController( // ── Search result navigation helpers ───────────────────────────── - /** - * Navigate to the page at [pageIndex] within the current chapter. - */ - private fun navigatePageByPos(textChapter: TextChapter, pageIndex: Int) { - val pagePos = textChapter.getReadLength(pageIndex) - if (ReadBook.durChapterPos != pagePos) { - ReadBook.durChapterPos = pagePos - ReadBook.callBack?.upContent() - ReadBook.callBack?.cancelSelect() + private fun markSearchResultAfterNavigation(pos: IntArray) { + if (pos[0] < 0) return + val readView = refs?.readView ?: return + pendingSearchResultMark = pos + ReadBook.skipToPage(pos[0]) { + readView.post { + consumePendingSearchResultMark() + } } } + private fun consumePendingSearchResultMark(): Boolean { + val pos = pendingSearchResultMark ?: return false + val readView = refs?.readView ?: return false + if (ReadBook.durPageIndex != pos[0] || readView.curPage.textPage.index != pos[0]) { + return false + } + pendingSearchResultMark = null + markSearchResultOnPage(pos) + return true + } + /** * Mark search result columns on the current page for highlighting. - * @param pos array of [pageIndex, lineIndex, startCharIndex, endLineIndex, endCharIndex] + * @param pos array of [pageIndex, lineIndex, startCharIndex, endRelativePage, endLineIndex, endCharIndex] */ private fun markSearchResultOnPage(pos: IntArray) { val readView = refs?.readView ?: return val lineIndex = pos[1] val startCharIndex = pos[2] - val endLineIndex = pos[3] - val endCharIndex = pos[4] + val endRelativePage = pos[3] + val endLineIndex = pos[4] + val endCharIndex = pos[5] + ReadBook.clearSearchResult() readView.cancelSelect(clearSearchResult = true) isSelectingSearchResult = true try { readView.curPage.selectStartMoveIndex(0, lineIndex, startCharIndex) - readView.curPage.selectEndMoveIndex(0, endLineIndex, endCharIndex) + readView.curPage.selectEndMoveIndex(endRelativePage, endLineIndex, endCharIndex) + readView.isTextSelected = true } finally { isSelectingSearchResult = false } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt index a843108fc..e8a4aa9a1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt @@ -19,7 +19,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.HazeState @@ -99,6 +102,7 @@ fun ReadBookRouteScreen( val state by viewModel.uiState.collectAsStateWithLifecycle() val readPreferences by viewModel.readPreferences.collectAsStateWithLifecycle() val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current val effectsReady = remember(viewModel) { CompletableDeferred() } val menuBackdrop = rememberLayerBackdrop() val menuHazeState = remember { HazeState() } @@ -375,18 +379,20 @@ fun ReadBookRouteScreen( // ── Search result collection (from Navigation3 search route) ────── - LaunchedEffect(viewModel) { - SearchContentResult.results.collect { result -> - effectsReady.await() - viewModel.onIntent( - ReadBookIntent.SetSearchResults(result.searchResults, result.index, result.query) - ) - result.searchResults.getOrNull(result.index)?.let { searchResult -> + LaunchedEffect(viewModel, lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + SearchContentResult.results.collect { result -> + effectsReady.await() viewModel.onIntent( - ReadBookIntent.NavigateToSearchResult(searchResult, result.index) + ReadBookIntent.SetSearchResults(result.searchResults, result.index, result.query) ) + result.searchResults.getOrNull(result.index)?.let { searchResult -> + viewModel.onIntent( + ReadBookIntent.NavigateToSearchResult(searchResult, result.index) + ) + } + SearchContentResult.resetReplayCache() } - SearchContentResult.resetReplayCache() } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt index 6ccdb4af7..840930068 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt @@ -44,14 +44,7 @@ fun ReadBookSearchBar( ) { AppFloatingActionButton( onClick = { - val prevIndex = currentIndex - 1 - val result = state.searchResultList.getOrNull(prevIndex) - ?: return@AppFloatingActionButton - onIntent( - ReadBookIntent.NavigateToSearchResult( - result, prevIndex - ) - ) + onIntent(ReadBookIntent.NavigateSearchResultByOffset(-1)) }, tooltipText = "上一个结果", containerColor = LegadoTheme.colorScheme.surfaceContainerLow, @@ -76,14 +69,7 @@ fun ReadBookSearchBar( ) { AppFloatingActionButton( onClick = { - val nextIndex = currentIndex + 1 - val result = state.searchResultList.getOrNull(nextIndex) - ?: return@AppFloatingActionButton - onIntent( - ReadBookIntent.NavigateToSearchResult( - result, nextIndex - ) - ) + onIntent(ReadBookIntent.NavigateSearchResultByOffset(1)) }, tooltipText = "下一个结果", containerColor = LegadoTheme.colorScheme.surfaceContainerLow, diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index c6e943b61..796384175 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -258,6 +258,10 @@ class ReadBookViewModel( _uiState.update { it.copy(isShowingSearchResult = intent.value) } } + is ReadBookIntent.NavigateSearchResultByOffset -> { + navigateSearchResultByOffset(intent.offset) + } + is ReadBookIntent.NavigateToSearchResult -> { ReadBook.saveCurrentBookProgress() _uiState.update { @@ -1265,9 +1269,8 @@ class ReadBookViewModel( ) { _uiState.update { syncFromReadBook(it) } _effects.tryEmit( - ReadBookEffect.UpContent(relativePosition, resetPageOffset) + ReadBookEffect.UpContent(relativePosition, resetPageOffset, success) ) - success?.invoke() } override suspend fun upContentAwait( @@ -1278,9 +1281,8 @@ class ReadBookViewModel( withContext(Main.immediate) { _uiState.update { syncFromReadBook(it) } _effects.tryEmit( - ReadBookEffect.UpContent(relativePosition, resetPageOffset) + ReadBookEffect.UpContent(relativePosition, resetPageOffset, success) ) - success?.invoke() } } @@ -2216,102 +2218,86 @@ class ReadBookViewModel( ): Array { val pages = textChapter.pages val content = textChapter.getContent() - if (pages.isEmpty()) return arrayOf(0, 0, 0, 0, 0) + if (pages.isEmpty()) return arrayOf(-1, 0, 0, 0, 0, 0) - val contentPosition = searchResult.queryIndexInChapter - .takeIf { it in content.indices && searchResultMatchesAt(content, it, searchResult, query) } - ?: findSearchResultPosition(content, searchResult, query) - if (contentPosition < 0) return arrayOf(0, 0, 0, 0, 0) + val match = findSearchResultMatch(content, searchResult, query) + ?: return arrayOf(-1, 0, 0, 0, 0, 0) + val contentPosition = match.first + val queryLength = match.second + if (contentPosition < 0 || queryLength <= 0) { + return arrayOf(-1, 0, 0, 0, 0, 0) + } + + var pageIndex = 0 + var length = pages[pageIndex].text.length + while (length < contentPosition && pageIndex + 1 < pages.size) { + pageIndex += 1 + length += pages[pageIndex].text.length + } + if (length < contentPosition) return arrayOf(-1, 0, 0, 0, 0, 0) - val pageIndex = textChapter.getPageIndexByCharIndex(contentPosition) - .coerceIn(0, pages.lastIndex) val currentPage = pages[pageIndex] - val matchLength = searchResult.matchLength.takeIf { it > 0 } ?: query.length - val endPosition = (contentPosition + matchLength).coerceAtMost(content.length) - val start = linePositionFor(currentPage, contentPosition, preferPreviousAtBoundary = false) - val end = linePositionFor(currentPage, endPosition, preferPreviousAtBoundary = true) - return arrayOf(pageIndex, start.lineIndex, start.charIndex, end.lineIndex, end.charIndex) + val lines = currentPage.lines + if (lines.isEmpty()) return arrayOf(-1, 0, 0, 0, 0, 0) + + var lineIndex = 0 + var currentLine = lines[lineIndex] + length = length - currentPage.text.length + currentLine.text.length + if (currentLine.isParagraphEnd) length++ + while (length <= contentPosition && lineIndex + 1 < lines.size) { + lineIndex += 1 + currentLine = lines[lineIndex] + length += currentLine.text.length + if (currentLine.isParagraphEnd) length++ + } + + var currentLineLength = currentLine.text.length + if (currentLine.isParagraphEnd) currentLineLength++ + length -= currentLineLength + + val charIndex = contentPosition - length + var addLine = 0 + var charIndex2 = 0 + if ((charIndex + queryLength) > currentLineLength) { + addLine = 1 + charIndex2 = charIndex + queryLength - currentLineLength - 1 + } + if ((lineIndex + addLine + 1) > currentPage.lines.size) { + addLine = -1 + charIndex2 = charIndex + queryLength - currentLineLength - 1 + } + return when (addLine) { + 0 -> arrayOf(pageIndex, lineIndex, charIndex, 0, lineIndex, charIndex + queryLength - 1) + 1 -> arrayOf(pageIndex, lineIndex, charIndex, 0, lineIndex + 1, charIndex2) + -1 -> arrayOf(pageIndex, lineIndex, charIndex, 1, 0, charIndex2) + else -> arrayOf(pageIndex, lineIndex, charIndex, 0, lineIndex, charIndex) + } } - private fun findSearchResultPosition( + private fun findSearchResultMatch( content: String, searchResult: SearchResult, query: String, - ): Int { - if (query.isEmpty()) return -1 + ): Pair? { + if (query.isEmpty()) return null if (searchResult.isRegex) { return runCatching { Regex(query).findAll(content) .drop(searchResult.resultCountWithinChapter) .firstOrNull() - ?.range - ?.first - }.getOrNull() ?: -1 + ?.let { it.range.first to it.value.length } + }.getOrNull() } var count = 0 - var index = content.indexOf(query, ignoreCase = true) + var index = content.indexOf(query) while (count != searchResult.resultCountWithinChapter && index >= 0) { - index = content.indexOf(query, index + query.length, ignoreCase = true) + index = content.indexOf(query, index + query.length) count += 1 } - return index + return index.takeIf { it >= 0 }?.let { it to query.length } } - private fun searchResultMatchesAt( - content: String, - position: Int, - searchResult: SearchResult, - query: String, - ): Boolean { - if (query.isEmpty()) return false - if (searchResult.isRegex) { - return runCatching { - Regex(query).find(content, position)?.range?.first == position - }.getOrDefault(false) - } - return position + query.length <= content.length && - content.regionMatches(position, query, 0, query.length, ignoreCase = true) - } - - private fun linePositionFor( - page: TextPage, - chapterPosition: Int, - preferPreviousAtBoundary: Boolean, - ): SearchLinePosition { - if (page.lines.isEmpty()) return SearchLinePosition(0, 0) - var fallbackIndex = 0 - page.lines.forEachIndexed { index, line -> - val lineEnd = line.chapterPosition + line.text.length + - if (line.isParagraphEnd) 1 else 0 - if (chapterPosition < line.chapterPosition) { - return@forEachIndexed - } - if (preferPreviousAtBoundary && chapterPosition == line.chapterPosition && index > 0) { - val previousLine = page.lines[index - 1] - return SearchLinePosition(index - 1, previousLine.text.length) - } - if (chapterPosition <= lineEnd) { - return SearchLinePosition( - lineIndex = index, - charIndex = (chapterPosition - line.chapterPosition).coerceIn(0, line.text.length), - ) - } - fallbackIndex = index - } - val lineIndex = fallbackIndex - val line = page.lines[lineIndex] - return SearchLinePosition( - lineIndex = lineIndex, - charIndex = (chapterPosition - line.chapterPosition).coerceIn(0, line.text.length), - ) - } - - private data class SearchLinePosition( - val lineIndex: Int, - val charIndex: Int, - ) - /** * Compute the search result position and emit [ReadBookEffect.NavigateToSearchResult] * so the Controller can navigate and highlight. @@ -2325,8 +2311,9 @@ class ReadBookViewModel( val pos = searchResultPositions(textChapter, result, query) val lineIndex = pos[1] val charIndex = pos[2] - val endLineIndex = pos[3] - val endCharIndex = pos[4] + val endRelativePage = pos[3] + val endLineIndex = pos[4] + val endCharIndex = pos[5] _effects.tryEmit( ReadBookEffect.NavigateToSearchResult( result = result, @@ -2334,6 +2321,7 @@ class ReadBookViewModel( pageIndex = pos[0], lineIndex = lineIndex, startCharIndex = charIndex, + endRelativePage = endRelativePage, endLineIndex = endLineIndex, endCharIndex = endCharIndex, ) @@ -2347,6 +2335,7 @@ class ReadBookViewModel( pageIndex = -1, lineIndex = 0, startCharIndex = 0, + endRelativePage = 0, endLineIndex = 0, endCharIndex = 0, ) @@ -3450,6 +3439,18 @@ class ReadBookViewModel( _effects.tryEmit(ReadBookEffect.ExitSearch) } + private fun navigateSearchResultByOffset(offset: Int) { + val state = _uiState.value + val currentIndex = state.searchResultIndex.coerceSearchResultIndex( + state.searchResultList.size + ) + val targetIndex = currentIndex + offset + val result = state.searchResultList.getOrNull(targetIndex) ?: return + ReadBook.saveCurrentBookProgress() + _uiState.update { it.copy(searchResultIndex = targetIndex) } + navigateToSearchResult(result) + } + override fun onCleared() { super.onCleared() if (BaseReadAloudService.isRun && BaseReadAloudService.pause) { diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt index b7ebc2465..31b08a00e 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt @@ -81,7 +81,12 @@ class SearchContentViewModel( private fun initBook() { viewModelScope.launch { val book = bookRepository.getBook(bookUrl) - val cachedResults = searchContentRepository.getCache(bookUrl, _searchQuery.value) + val cachedResults = searchContentRepository.getCache( + bookUrl, + _searchQuery.value, + _replaceEnabled.value, + _regexReplace.value + ) _uiState.update { it.copy(