diff --git a/apps/readest-app/src/app/reader/components/sidebar/SearchBar.tsx b/apps/readest-app/src/app/reader/components/sidebar/SearchBar.tsx index 4cc6a391..776299d1 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/SearchBar.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/SearchBar.tsx @@ -11,6 +11,7 @@ import { BookSearchConfig, BookSearchResult } from '@/types/book'; import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import { debounce } from '@/utils/debounce'; import { isCJKStr } from '@/utils/lang'; +import { createRejecttFilter } from '@/utils/node'; import Dropdown from '@/components/Dropdown'; import SearchOptions from './SearchOptions'; @@ -117,19 +118,6 @@ const SearchBar: React.FC = ({ return searchTerm.length >= minLength; }; - const createAcceptNode = ({ withRT = true } = {}) => { - return (node: Node): number => { - if (node.nodeType === Node.ELEMENT_NODE) { - const name = (node as Element).tagName.toLowerCase(); - if (name === 'script' || name === 'style' || (!withRT && name === 'rt')) { - return NodeFilter.FILTER_REJECT; - } - return NodeFilter.FILTER_SKIP; - } - return NodeFilter.FILTER_ACCEPT; - }; - }; - const handleSearch = useCallback( async (term: string) => { console.log('searching for:', term); @@ -139,7 +127,9 @@ const SearchBar: React.FC = ({ ...searchConfig, index, query: term, - acceptNode: createAcceptNode({ withRT: !primaryLang.startsWith('ja') }), + acceptNode: createRejecttFilter({ + tags: primaryLang.startsWith('ja') ? ['rt'] : [], + }), }); const results: BookSearchResult[] = []; let lastProgressLogTime = 0; diff --git a/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx b/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx index d698f384..69a83569 100644 --- a/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx +++ b/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx @@ -212,7 +212,7 @@ const TTSControl: React.FC = ({ bookKey }) => { await ttsController.initViewTTS(); const ssml = view.tts?.from(ttsFromRange); if (ssml) { - let lang = parseSSMLLang(ssml, primaryLang) || 'en'; + const lang = parseSSMLLang(ssml, primaryLang) || 'en'; setIsPlaying(true); setTtsLang(lang); diff --git a/apps/readest-app/src/services/tts/TTSController.ts b/apps/readest-app/src/services/tts/TTSController.ts index ea0091a0..a911c8ab 100644 --- a/apps/readest-app/src/services/tts/TTSController.ts +++ b/apps/readest-app/src/services/tts/TTSController.ts @@ -3,6 +3,7 @@ import { AppService } from '@/types/system'; import { parseSSMLMarks } from '@/utils/ssml'; import { Overlayer } from 'foliate-js/overlayer.js'; import { TTSGranularity, TTSHighlightOptions, TTSMark, TTSVoice } from './types'; +import { createRejecttFilter } from '@/utils/node'; import { WebSpeechClient } from './WebSpeechClient'; import { NativeTTSClient } from './NativeTTSClient'; import { EdgeTTSClient } from './EdgeTTSClient'; @@ -106,7 +107,14 @@ export class TTSController extends EventTarget { granularity = supportedGranularities[0]!; } const highlightOptions: TTSHighlightOptions = { style: 'highlight', color: 'gray' }; - await this.view.initTTS(granularity, this.#getHighlighter(highlightOptions)); + await this.view.initTTS( + granularity, + createRejecttFilter({ + tags: ['rt', 'sup'], + contents: [{ tag: 'a', content: /^\d+$/ }], + }), + this.#getHighlighter(highlightOptions), + ); } async preloadSSML(ssml: string | undefined) { diff --git a/apps/readest-app/src/types/view.ts b/apps/readest-app/src/types/view.ts index 83f2cb7e..c4f4b133 100644 --- a/apps/readest-app/src/types/view.ts +++ b/apps/readest-app/src/types/view.ts @@ -20,7 +20,11 @@ export interface FoliateView extends HTMLElement { clearSearch: () => void; select: (target: string | number | { fraction: number }) => void; deselect: () => void; - initTTS: (granularity?: TTSGranularity, highlight?: (range: Range) => void) => Promise; + initTTS: ( + granularity?: TTSGranularity, + nodeFilter?: (node: Node) => number, + highlight?: (range: Range) => void, + ) => Promise; book: BookDoc; tts: TTS | null; language: { diff --git a/apps/readest-app/src/utils/node.ts b/apps/readest-app/src/utils/node.ts new file mode 100644 index 00000000..ca871d4e --- /dev/null +++ b/apps/readest-app/src/utils/node.ts @@ -0,0 +1,38 @@ +export const createRejecttFilter = ({ + tags = [], + classes = [], + attributes = [], + contents = [], +}: { + tags?: string[]; + classes?: string[]; + attributes?: string[]; + contents?: { tag: string; content: RegExp }[]; +}) => { + return (node: Node): number => { + if (node.nodeType === Node.ELEMENT_NODE) { + const name = (node as Element).tagName.toLowerCase(); + if (name === 'script' || name === 'style') { + return NodeFilter.FILTER_REJECT; + } + if (tags.includes(name)) { + return NodeFilter.FILTER_REJECT; + } + if (classes.some((cls) => (node as Element).classList.contains(cls))) { + return NodeFilter.FILTER_REJECT; + } + if (attributes.some((attr) => (node as Element).hasAttribute(attr))) { + return NodeFilter.FILTER_REJECT; + } + if ( + contents.some(({ tag, content }) => { + return name === tag && content.test((node as Element).textContent || ''); + }) + ) { + return NodeFilter.FILTER_REJECT; + } + return NodeFilter.FILTER_SKIP; + } + return NodeFilter.FILTER_ACCEPT; + }; +}; diff --git a/apps/readest-app/src/utils/sel.ts b/apps/readest-app/src/utils/sel.ts index 74f777d1..f8cc44ba 100644 --- a/apps/readest-app/src/utils/sel.ts +++ b/apps/readest-app/src/utils/sel.ts @@ -183,13 +183,13 @@ export const getPopupPosition = ( return { point: popupPoint, dir: position.dir } as Position; }; -export const getTextFromRange = (range: Range, rejects: string[] = []): string => { +export const getTextFromRange = (range: Range, rejectTags: string[] = []): string => { const clonedRange = range.cloneRange(); const fragment = clonedRange.cloneContents(); const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { const parent = node.parentElement; - if (rejects.includes(parent?.tagName.toLowerCase() || '')) { + if (rejectTags.includes(parent?.tagName.toLowerCase() || '')) { return NodeFilter.FILTER_REJECT; } return NodeFilter.FILTER_ACCEPT; diff --git a/packages/foliate-js b/packages/foliate-js index ff36292b..97b90607 160000 --- a/packages/foliate-js +++ b/packages/foliate-js @@ -1 +1 @@ -Subproject commit ff36292b7a4f866a87446d55b82d95669e9cac29 +Subproject commit 97b906075f29ea4a6524f813a8423f748f64949f