77 * Preserves subtitle structure (timestamps, indices). Only the dialogue
88 * text gets transliterated.
99 */
10- import { ref , computed , onMounted , onUnmounted } from " vue"
10+ import { ref , onMounted , onUnmounted } from " vue"
1111import { createWorkerClient , type WorkerClient } from " ../scripts/worker-client"
1212
1313interface Props {
@@ -38,7 +38,8 @@ async function ensureEngine() {
3838
3939// Regex: capture subtitle cues with timestamps.
4040// SRT/VTT pattern: index (optional), time range line, dialogue.
41- const CUE_RE = / (\d + \s * \n )? ((?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} \s * -->\s * (?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} )\s * \n ([\s\S ] *? )(?=\n\s * \n | \n\d + \s * \n | \n (?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} | $ )/ g
41+ const CUE_RE =
42+ / (\d + \s * \n )? ((?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} \s * -->\s * (?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} )\s * \n ([\s\S ] *? )(?=\n\s * \n | \n\d + \s * \n | \n (?:\d {2} :)? \d {2} :\d {2} [,. ] \d {3} | $ )/ g
4243
4344async function run() {
4445 if (! client ) return
@@ -49,7 +50,8 @@ async function run() {
4950 try {
5051 const out = inputText .value .replace (CUE_RE , async (_match , idx , time , dialogue : string ) => {
5152 // Strip HTML formatting tags before transliterating
52- const stripped = dialogue .replace (/ <[^ >] + >/ g , " " ).trim ()
53+ const stripped = dialogue .replace (/ <[^ >] * >/ g , " " ).replace (/ [<>] / g , " " ).trim ()
54+ // eslint-disable-next-line no-control-regex -- \x00-\x7F is the full ASCII range
5355 if (! / [^ \x00 -\x7F ] / .test (stripped )) {
5456 return ` ${idx ?? " " }${time }\n ${dialogue } ` .trim ()
5557 }
@@ -84,7 +86,8 @@ async function run() {
8486
8587 const outParts: string [] = []
8688 for (const cue of cues ) {
87- const stripped = cue .dialogue .replace (/ <[^ >] + >/ g , " " ).trim ()
89+ const stripped = cue .dialogue .replace (/ <[^ >] * >/ g , " " ).replace (/ [<>] / g , " " ).trim ()
90+ // eslint-disable-next-line no-control-regex -- \x00-\x7F is the full ASCII range
8891 if (! / [^ \x00 -\x7F ] / .test (stripped )) {
8992 outParts .push (` ${cue .idx }${cue .time }\n ${cue .dialogue } ` .trim ())
9093 continue
@@ -93,7 +96,7 @@ async function run() {
9396 const transliterated = await client .transliterate (system .value , stripped )
9497 count ++
9598 outParts .push (` ${cue .idx }${cue .time }\n ${transliterated } ` )
96- } catch ( e ) {
99+ } catch {
97100 outParts .push (` ${cue .idx }${cue .time }\n ${cue .dialogue } ` )
98101 }
99102 }
@@ -141,11 +144,13 @@ onUnmounted(() => client?.terminate())
141144 <span class =" pane-label" >Transliterated output</span >
142145 <button v-if =" output" class =" copy-btn" @click =" copyOutput" >Copy</button >
143146 </header >
144- <pre >{{ output || ' Output appears here.' }}</pre >
147+ <pre >{{ output || " Output appears here." }}</pre >
145148 </div >
146149 </div >
147150
148- <p v-if =" cueCount > 0" class =" cue-count tnum" >{{ cueCount }} cue{{ cueCount > 1 ? "s" : "" }} transliterated.</p >
151+ <p v-if =" cueCount > 0" class =" cue-count tnum" >
152+ {{ cueCount }} cue{{ cueCount > 1 ? "s" : "" }} transliterated.
153+ </p >
149154 <p v-if =" error" class =" error" >⚠ {{ error }}</p >
150155 <p class =" privacy" >Text never leaves your browser.</p >
151156 </div >
@@ -185,7 +190,9 @@ onUnmounted(() => client?.terminate())
185190 color : var (--color-ink );
186191 border-radius : 1px ;
187192}
188- .control-field select :focus { border-color : var (--color-brand ); }
193+ .control-field select :focus {
194+ border-color : var (--color-brand );
195+ }
189196
190197.run-btn {
191198 font-family : var (--font-mono );
@@ -203,15 +210,19 @@ onUnmounted(() => client?.terminate())
203210 background : var (--color-highlight-deep );
204211 border-color : var (--color-highlight-deep );
205212}
206- .run-btn :disabled { opacity : 0.5 ; }
213+ .run-btn :disabled {
214+ opacity : 0.5 ;
215+ }
207216
208217.subs-grid {
209218 display : grid ;
210219 grid-template-columns : 1fr ;
211220 gap : 1rem ;
212221}
213222@media (min-width : 900px ) {
214- .subs-grid { grid-template-columns : 1fr 1fr ; }
223+ .subs-grid {
224+ grid-template-columns : 1fr 1fr ;
225+ }
215226}
216227
217228.io-pane {
@@ -240,7 +251,8 @@ onUnmounted(() => client?.terminate())
240251 color : var (--color-stone-light );
241252}
242253
243- textarea , pre {
254+ textarea ,
255+ pre {
244256 flex : 1 ;
245257 min-height : 320px ;
246258 font-family : var (--font-mono );
0 commit comments