Spaces:
Running
Running
deploy 2068570
Browse files- assets/{index-Cgnakw51.js → index-CaLiB7gc.js} +0 -0
- assets/{readall-DQKU58ac.js → readall-CBhGAHk6.js} +1 -1
- assets/tts-By_9DACa.js +0 -1
- assets/tts-D-8K-KMm.js +1 -0
- index.html +2 -2
- readall.html +2 -2
assets/{index-Cgnakw51.js → index-CaLiB7gc.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/{readall-DQKU58ac.js → readall-CBhGAHk6.js}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
import"./pins-DVBrHC3g.js";import{a as e,i as t,n,r,t as i}from"./tts-
|
|
|
|
| 1 |
+
import"./pins-DVBrHC3g.js";import{a as e,i as t,n,r,t as i}from"./tts-D-8K-KMm.js";var a=t(),o=e=>document.getElementById(e),s=o(`text`),c=o(`status`),l=o(`now`),u=o(`rate`),d=o(`voice`);o(`readall`).textContent=e(`dial.readall`),o(`stop`).textContent=e(`panel.stop`),o(`pause`).textContent=e(`panel.pause`),o(`resume`).textContent=e(`panel.resume`),o(`skip`).textContent=e(`panel.skip`),o(`rate-label`).textContent=e(`panel.rate`),o(`voice-label`).textContent=e(`panel.voice`),u.min=String(n),u.max=String(i),u.step=`0.1`,u.value=String(a.getRate());for(let e of r){let t=document.createElement(`option`);t.value=e,t.textContent=e,d.appendChild(t)}d.value=a.getVoice();function f(e){c.textContent=e}a.onProgress((t,n,r)=>f(`${e(`loading.voice`)} ${Math.round(n/1e6)} / ${Math.round(r/1e6)} MB ${t}`));async function p(t,n={}){f(e(`notice.reading_all`));let r=await a.readAll(t,{...n,onStart:e=>{l.textContent=t.slice(e.start,e.end),l.dataset.seq=String(e.seq)},onEnd:e=>{l.dataset.seq===String(e.seq)&&(l.textContent=``)}});return f(r.finished?e(`notice.done`):e(`notice.stopped`)),r}o(`readall`).addEventListener(`click`,()=>void p(s.value)),o(`stop`).addEventListener(`click`,()=>{a.stop(),f(e(`notice.stopped`))}),o(`pause`).addEventListener(`click`,()=>a.pause()),o(`resume`).addEventListener(`click`,()=>a.resume()),o(`skip`).addEventListener(`click`,()=>a.skip()),u.addEventListener(`input`,()=>u.value=String(a.setRate(Number(u.value)))),d.addEventListener(`change`,()=>a.setVoice(d.value)),document.addEventListener(`keydown`,t=>{t.key===`Escape`&&(a.stop(),f(e(`notice.stopped`)))}),window.__tts={load:()=>a.load(),plan:e=>a.plan(e),measure:(e,t,n)=>a.measure(e,t,n),readAll:p,speak:e=>a.speak(e),stop:()=>a.stop(),pause:()=>a.pause(),resume:()=>a.resume(),skip:()=>a.skip(),setRate:e=>a.setRate(e),getRate:()=>a.getRate(),setVoice:e=>a.setVoice(e),state:()=>a.state(),stats:()=>a.stats()},f(e(`loading.ready`));
|
assets/tts-By_9DACa.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{r as e}from"./pins-DVBrHC3g.js";var t={$comment:`Every user-facing string, both targets (docs/PLAN.md rule 8). S0-03 owns the KEYS; Values written in S1-L0 (Claude, Voice Bank protocol, 2026-09-24). Every message is spoken, so key names are spelled out (Control Option V, Command C), never symbols. Web and Mac load this file; no string literal lives in UI code (scripts/strings-check.mjs). Every notice/error is also spoken (rule 8, 'Speak messages' default on). Placeholders {n} {mb} {words} {level} are filled at runtime.`,version:1,locale:`en`,$plan_wording:{"notice.dial_shorter":`Making it shorter.`,"notice.dial_longer":`Adding detail.`,"notice.all_cut":`Couldn't make a summary I trust for this part.`,"notice.oneline_failed":`Couldn't boil this down to one line I trust. Here's the short version.`,"notice.input_limit":`This is over 120,000 words. Select a part of it.`,"mac.intake.press_cmd_c":`Press ⌘C to read it (spoken form: Press Command C to read it.)`,"mac.intake.no_copy_seen":`Didn't see a copy.`,"loading.summarizer_button":`Load summarizer ({mb} MB)`,"dial.readall":`Read all`,"dial.short":`Short`,"dial.caveman":`Caveman`,"dial.oneline":`One line`,$comment:`Wording the plan fixes (rules 2, 11, C3, C9, Architecture). S1-L0 keeps the meaning; it may tune words after the Voice Bank pass.`},strings:{"app.title":`Fruitbat`,"theme.toggle":`Dark mode`,"dial.readall":`Read all`,"dial.short":`Short`,"dial.caveman":`Caveman`,"dial.oneline":`One line`,"panel.title":`The short version`,"panel.empty":`Highlight some text or paste it below. Then pick how short you want it.`,"panel.read_chip":`Read`,"panel.summarize":`Read it`,"panel.stop":`Stop`,"panel.pause":`Pause`,"panel.resume":`Resume`,"panel.skip":`Skip`,"panel.read_this_part":`Read this part`,"panel.rate":`Speed`,"panel.rate_slower":`Slower`,"panel.rate_faster":`Faster`,"panel.voice":`Voice`,"panel.speak_messages":`Speak messages`,"panel.hide_pip":`Hide Pip`,"panel.show_pip":`Show Pip`,"panel.font_size":`Text size`,"panel.font_size_small":`Small`,"panel.font_size_medium":`Medium`,"panel.font_size_large":`Large`,"panel.spacing":`Spacing`,"panel.spacing_compact":`Compact`,"panel.spacing_cozy":`Normal`,"panel.spacing_roomy":`Roomy`,"panel.dark_mode":`Dark mode`,"panel.position":`Panel position`,"panel.bug_report":`Report a problem`,"panel.stats":`Stats`,"panel.close":`Close`,"panel.now_reading":`Now reading`,"panel.next_up":`Next`,"intake.paste_placeholder":`Paste text here`,"intake.word_count":`{n} words`,"intake.drop_here":`Drop a text or PDF file here`,"intake.empty":`Nothing to read yet. Highlight some text or paste it here.`,"intake.sample_label":`Sample article`,"demo.try_it_now":`Try it now`,"demo.recording_label":`This one is a recording. Your own text runs on your machine.`,"demo.switch_level":`Turn the dial to hear the same article shorter or longer.`,"demo.excerpt_note":`This is the first part of the article, not the whole thing.`,"loading.probing":`Checking what this browser can do.`,"loading.voice":`Getting the voice ready.`,"loading.summarizer_button":`Load summarizer ({mb} MB)`,"loading.summarizer_size":`The summarizer is a {mb} MB download. It happens once and stays on this device.`,"loading.progress":`{mb} MB so far.`,"loading.cached":`Already on this device. No download needed.`,"loading.first_time":`First time here. The models download once and stay on this device.`,"loading.save_data":`Your browser is set to save data, so nothing big downloads until you ask.`,"loading.mobile":`On a phone the voice works and the sample is a recording. Open this on a laptop for the full thing.`,"loading.almost":`Almost there.`,"loading.ready":`Ready.`,"notice.dial_shorter":`Making it shorter.`,"notice.dial_longer":`Adding detail.`,"notice.all_cut":`Couldn't make a summary I trust for this part.`,"notice.oneline_failed":`Couldn't boil this down to one line I trust. Here's the short version.`,"notice.input_limit":`This is over 120,000 words. Select a part of it.`,"notice.no_webgpu":`This browser can't run the summarizer. Read all still works, and the sample is a recording.`,"notice.probe_failed":`The summarizer didn't start on this machine. Read all still works.`,"notice.low_end_tier":`Using a smaller summarizer so it fits this machine.`,"notice.stopped":`Stopped.`,"notice.done":`That's the end.`,"notice.paused":`Paused.`,"notice.making_summary":`Making the short version.`,"notice.reading_all":`Reading all of it.`,"error.model_download":`The download didn't finish. Check your connection and try again.`,"error.voice":`The voice didn't load. Try again in a moment.`,"error.generation":`Something went wrong making the summary. Read all still works.`,"error.storage_full":`This browser is out of storage, so stats won't be saved.`,"error.empty_input":`There's no text to read yet.`,"error.offline":`You're offline. Anything already downloaded still works.`,"error.unknown":`Something went wrong. Try again.`,"report.title":`Report a problem`,"report.preview_intro":`Here is everything the report contains. Nothing you were reading is in it.`,"report.describe":`What happened?`,"report.open_github":`Open on GitHub`,"report.copy":`Copy report`,"report.copied":`Copied.`,"report.copy_failed":`Couldn't copy it. The report above is selected so you can copy it by hand.`,"report.privacy_note":`The report has numbers and settings only. Never your text, your audio or your file names.`,"panel.voice.af_heart":`Heart`,"panel.voice.af_bella":`Bella`,"stats.title":`Stats`,"stats.empty":`No runs yet.`,"stats.export":`Export CSV`,"stats.clear":`Clear stats`,"stats.col.date":`When`,"stats.col.level":`Level`,"stats.col.device":`Device`,"stats.col.dtype":`Precision`,"stats.col.cache_state":`Cache`,"stats.col.doc_id":`Text`,"stats.col.ttfa_ms":`First audio (ms)`,"stats.col.tok_s":`Tokens per second`,"stats.col.rtf":`Voice speed (x real time)`,"stats.col.bullets_total":`Bullets`,"stats.col.bullets_cut":`Bullets cut`,"a11y.panel_region":`Fruitbat panel`,"a11y.bullet_list":`Summary`,"a11y.dial":`How short`,"a11y.pip":`Pip the fruit bat`,"mac.menu.summarize_selection":`Read the selection`,"mac.menu.summarize_clipboard":`Read the clipboard`,"mac.menu.summarize_region":`Read part of the screen`,"mac.menu.settings":`Settings`,"mac.menu.stats":`Stats`,"mac.menu.check_updates":`Check for updates`,"mac.menu.quit":`Quit Fruitbat`,"mac.onboarding.try_it":`Copy any text, then press Control Option V.`,"mac.onboarding.first_success":`Worked. Now try it on anything you highlight.`,"mac.onboarding.offer_accessibility":`Want to skip the copy step? Give Fruitbat the Accessibility permission and it reads what you highlight.`,"mac.onboarding.updates_disclosure":`Fruitbat checks for updates once a day. You can turn that off in Settings.`,"mac.onboarding.done":`You're set. Fruitbat lives in the menu bar.`,"mac.permission.accessibility":`Fruitbat needs the Accessibility permission to read what you highlight. Without it, copy the text first and press Control Option V.`,"mac.permission.accessibility_denied":`No Accessibility permission. Copy the text and press Control Option V instead.`,"mac.permission.screen_recording":`Fruitbat needs Screen Recording to read part of the screen. Without it, the other two ways still work.`,"mac.permission.screen_recording_denied":`No Screen Recording permission. Highlight text or copy it instead.`,"mac.permission.open_settings":`Open System Settings`,"mac.intake.press_cmd_c":`Press Command C to read it.`,"mac.intake.no_copy_seen":`Didn't see a copy.`,"mac.intake.clipboard_empty":`The clipboard is empty.`,"mac.intake.clipboard_not_text":`The clipboard doesn't have text in it.`,"mac.ocr.first_run_warning":`Anything on screen becomes text on this Mac, passwords included. It stays here.`,"mac.ocr.nothing_found":`Couldn't find any text there.`,"mac.ocr.pick_region":`Drag over the part you want read.`,"mac.update.toggle":`Check for updates`,"mac.update.available":`A new version is ready.`,"mac.update.up_to_date":`You're up to date.`,"mac.license.trial_days_left":`{n} days left in the trial.`,"mac.license.enter_key":`Enter your license key`,"mac.license.licensed":`Licensed. Thank you.`,"mac.license.nag":`Couldn't check the license in a while. Fruitbat keeps working.`,"mac.license.sign_out":`Sign out of this Mac`,"mac.license.invalid":`That key didn't work. Check it and try again.`,"intake.read_button":`Read this`}}.strings;function n(e){return t[e]??e}var r=25,i=.005,a=class{maxAheadSeconds;maxInFlight;ctx;opts;runId=0;nextStart=0;scheduled=[];inFlight=0;analyser=null;maxGapMs=0;totalSeconds=0;enqueued=0;ended=0;closed=!1;ticker=null;constructor(e,t={}){this.ctx=e,this.opts=t,this.maxAheadSeconds=t.maxAheadSeconds??30,this.maxInFlight=t.maxInFlight??3}get currentRunId(){return this.runId}beginRun(e){this.stop(),this.runId=e,this.nextStart=this.ctx.currentTime,this.maxGapMs=0,this.totalSeconds=0,this.enqueued=0,this.ended=0,this.closed=!1}close(){this.closed=!0,this.maybeDrain()}requestSent(e=0){return this.inFlight++,this.reserved+=e,{run:this.runId,seconds:e}}requestSettled(e={run:this.runId,seconds:0}){e.run===this.runId&&(this.inFlight=Math.max(0,this.inFlight-1),this.reserved=Math.max(0,this.reserved-e.seconds))}reserved=0;get requestsInFlight(){return this.inFlight}aheadSeconds(){return Math.max(0,this.nextStart-this.ctx.currentTime)}canAccept(e=0){if(this.inFlight>=this.maxInFlight)return!1;let t=this.aheadSeconds()+this.reserved;return t+e<=this.maxAheadSeconds&&t<this.maxAheadSeconds||this.inFlight===0&&t<=Math.min(5,this.maxAheadSeconds/2)}getAnalyser(){return!this.analyser&&this.ctx.createAnalyser&&(this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=2048,this.analyser.connect(this.ctx.destination)),this.analyser}enqueue(e){if(e.runId!==this.runId||this.closed)return!1;let t=this.ctx.createBuffer(1,e.pcm.length,e.sampleRate);t.copyToChannel(e.pcm,0);let n=this.ctx.currentTime,r=Math.max(n,this.nextStart);if(this.enqueued>0){let e=(r-this.nextStart)*1e3;e>this.maxGapMs&&(this.maxGapMs=e)}let i=this.schedule(e,t,r);return this.nextStart=r+i.seconds,this.totalSeconds+=i.seconds,this.enqueued++,!0}schedule(e,t,n){let r=this.ctx.createBufferSource();r.buffer=t,r.connect(this.getAnalyser()??this.ctx.destination);let i={item:e,buffer:t,source:r,startAt:n,seconds:t.duration,started:!1,ended:!1};return r.onended=()=>{if(i.ended)return;i.ended=!0;let t=this.scheduled.indexOf(i);t>=0&&this.scheduled.splice(t,1),e.runId===this.runId&&(this.ended++,this.opts.onEnd?.({runId:e.runId,seq:e.seq,start:e.start,end:e.end,tag:e.tag,at:this.ctx.currentTime,seconds:i.seconds}),this.maybeDrain())},r.start(n),this.scheduled.push(i),this.ensureTicker(),this.tick(),i}ensureTicker(){this.ticker===null&&(this.ticker=setInterval(()=>this.tick(),r))}tick(){if(this.ctx.state===`suspended`)return;let e=this.ctx.currentTime;for(let t of this.scheduled)t.started||t.ended||t.item.runId!==this.runId||e+i>=t.startAt&&(t.started=!0,this.opts.onStart?.({runId:t.item.runId,seq:t.item.seq,start:t.item.start,end:t.item.end,tag:t.item.tag,at:t.startAt,seconds:t.seconds}));this.scheduled.length===0&&this.ticker!==null&&(clearInterval(this.ticker),this.ticker=null)}maybeDrain(){if(this.closed&&this.scheduled.length===0&&this.enqueued>0&&this.ended>=this.enqueued){let e=this.runId;this.closed=!1,this.opts.onDrain?.(e)}}playing(){let e=this.ctx.currentTime;for(let t of this.scheduled)if(!t.ended&&e+i>=t.startAt&&e<t.startAt+t.seconds)return t.item;return null}stop(){let e=typeof performance<`u`?performance.now():Date.now();for(let e of this.scheduled){e.ended=!0;try{e.source.stop()}catch{}}return this.scheduled=[],this.ticker!==null&&(clearInterval(this.ticker),this.ticker=null),this.nextStart=this.ctx.currentTime,this.runId++,this.closed=!1,this.inFlight=0,this.reserved=0,(typeof performance<`u`?performance.now():Date.now())-e}pause(){return this.ctx.suspend()}resume(){return this.ctx.resume()}skip(){let e=this.ctx.currentTime,t=this.scheduled.findIndex(t=>!t.ended&&e+i>=t.startAt&&e<t.startAt+t.seconds);if(t<0)return!1;let n=this.scheduled.slice(t+1),r=this.scheduled[t];for(let e of[r,...n]){e.ended=!0;try{e.source.stop()}catch{}}this.scheduled=this.scheduled.slice(0,t),this.ended++,this.opts.onEnd?.({runId:r.item.runId,seq:r.item.seq,start:r.item.start,end:r.item.end,tag:r.item.tag,at:e,seconds:r.seconds});let a=e;for(let e of n){let t=this.schedule(e.item,e.buffer,a);a+=t.seconds}return this.nextStart=a,this.maybeDrain(),!0}},o=.7,s=1.5,c=[`af_heart`,`af_bella`],l=`fruitbat.rate`,u=14,d=`fruitbat.voice`,f={target:e().web.voice.tts_phoneme_target,limit:e().web.voice.tts_phoneme_limit};function p(){let e=typeof navigator<`u`?navigator.userAgent:``,t=t=>{let n=t.exec(e);return n?Number(n[1]):null},n=`other`,r=null;/Edg\//.test(e)?(n=`edge`,r=t(/Edg\/(\d+)/)):/Firefox\//.test(e)?(n=`firefox`,r=t(/Firefox\/(\d+)/)):/Chrome\//.test(e)?(n=`chrome`,r=t(/Chrome\/(\d+)/)):/Safari\//.test(e)&&/Version\//.test(e)&&(n=`safari`,r=t(/Version\/(\d+)/));let i=`other`,a=null;return/iPhone|iPad/.test(e)?(i=`ios`,a=t(/OS (\d+)_/)):/Android/.test(e)?(i=`android`,a=t(/Android (\d+)/)):/Mac OS X/.test(e)?(i=`macos`,a=t(/Mac OS X (\d+)/)):/Windows NT/.test(e)?(i=`windows`,a=t(/Windows NT (\d+)/)):/Linux/.test(e)&&(i=`linux`,a=null),{browser:n,browser_major:r,os:i,os_major:a}}function m(e){try{return localStorage.getItem(e)}catch{return null}}function h(e,t){try{localStorage.setItem(e,t)}catch{}}var g=class{worker=null;ctx=null;noticeNextStart=0;queue=null;runId=0;messageSeq=0;waiters=new Map;loaded=null;messageSources=[];lastStats=null;progressListeners=new Set;phase=`idle`;error=null;current=null;next=null;rate;voice;downloadedBytes=0;coldPending=!1;runCold=!1;takeCold(){this.coldPending&&=(this.runCold=!0,!1)}cacheState(){return this.loaded?this.runCold?`cold`:`warm`:`unknown`}device;loadedInfo=null;loadedDevice=`wasm`;loadedDtype=`q8`;noticeCache=new Map;prewarmed=!1;lastNoticeLatencyMs=null;constructor(){let e=Number(m(l));this.rate=Number.isFinite(e)&&e>=.7&&e<=1.5?e:1;let t=m(d);this.voice=c.includes(t??``)?t:`af_heart`;let n=typeof location<`u`?new URLSearchParams(location.search).get(`tts`):null;this.device=n===`webgpu`?`webgpu`:`wasm`}get isLoaded(){return this.loadedInfo!==null}getRate(){return this.rate}setRate(e){let t=Math.min(s,Math.max(o,Number(e)||1));return this.rate=Math.round(t*100)/100,h(l,String(this.rate)),this.rate}getVoice(){return this.voice}setVoice(e){return c.includes(e)&&(this.voice=e,h(d,e)),this.voice}onProgress(e){return this.progressListeners.add(e),()=>this.progressListeners.delete(e)}ensureContext(){return this.ctx||(this.ctx=new AudioContext({sampleRate:24e3}),this.queue=new a(this.ctx,{maxAheadSeconds:30,maxInFlight:3,onStart:e=>this.handleStart(e),onEnd:e=>this.handleEnd(e),onDrain:e=>this.handleDrain(e)})),this.paused||this.ctx.resume(),this.ctx}get context(){return this.ctx}getAnalyser(){return this.queue?.getAnalyser()??null}getWorker(){return this.worker||(this.worker=new Worker(new URL(`/assets/tts.worker-DY38C-hY.js`,``+import.meta.url),{type:`module`}),this.worker.onmessage=e=>this.handleMessage(e.data),this.worker.onerror=e=>{this.error=`worker: ${e.message}`;for(let e of this.waiters.values())e.reject(Error(this.error));this.waiters.clear()}),this.worker}send(e){this.getWorker().postMessage(e)}wait(e){return new Promise((t,n)=>this.waiters.set(e,{resolve:t,reject:n}))}handleMessage(e){if(e.type===`progress`){for(let t of this.progressListeners)t(e.file,e.loaded,e.total);return}if(e.type===`loaded`){this.downloadedBytes=e.downloadedBytes,this.coldPending=e.downloadedBytes>1e6,this.loadedDevice=e.device,this.loadedDtype=e.dtype,this.loadedInfo=e,this.waiters.get(`loaded`)?.resolve(e),this.waiters.delete(`loaded`);return}if(e.type===`planned`){let t=`planned:${e.runId}`;this.waiters.get(t)?.resolve(e),this.waiters.delete(t);return}if((e.type===`error`||e.type===`overlimit`)&&e.seq===-1){let t=e.type===`error`?Error(`${e.name}: ${e.message}`):Error(`tts_overlimit: ${e.phonemes} > ${e.limit}`),n=e.runId===-1&&this.waiters.has(`loaded`)?`loaded`:`planned:${e.runId}`,r=this.waiters.get(n);if(!r)return;this.waiters.delete(n),r.reject(t);return}let t=`${e.runId}:${e.seq}`,n=this.waiters.get(t);n&&(this.waiters.delete(t),n.resolve(e))}settleWaiters(e,t){let n=Error(t);n.name=`AbortError`;for(let[t,r]of this.waiters)(t===`planned:${e}`||t.startsWith(`${e}:`))&&(this.waiters.delete(t),r.reject(n))}load(){if(!this.loaded){this.phase=`loading`;let e=this.wait(`loaded`).then(e=>(this.phase===`loading`&&(this.phase=`idle`),e));this.send({type:`load`,device:this.device}),this.loaded=e,e.catch(()=>{this.loaded=null,this.phase=`failed`})}return this.loaded}async plan(e){let t=-++this.messageSeq,n=this.wait(`planned:${t}`);this.send({type:`plan`,runId:t,text:e,limits:f});let r=await n;return{segments:r.segments,maxPhonemes:r.maxPhonemes,splitMs:r.splitMs}}pendingDone=null;readSeq=0;lastStop=null;runTtfa=null;runT0=0;runSynthMs=0;runOverlimit=0;secPerChar=1/u;estimateSeconds(e,t){return Math.max(0,e.end-e.start)*this.secPerChar/Math.max(t,.1)}async readAll(e,t={}){this.releasePause(),this.ensureContext();let n=this.runId,r=++this.readSeq;try{await this.load()}catch(e){return this.phase=`failed`,this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e),this.result(n,0,0,!1)}if(this.runId!==n||r!==this.readSeq)return this.result(n,0,0,!1);let i=++this.runId;this.runCold=!1,this.takeCold();let a=this.pendingDone;this.pendingDone=null,a&&(this.replacedMetrics=this.metricsNow(n)),a?.resolve(!1),this.send({type:`cancel`,runId:n}),this.settleWaiters(n,`stopped`);let o=this.queue;o.beginRun(i),this.phase=`planning`,this.error=null,this.current=null,this.next=null,this.runTtfa=null,this.runSynthMs=0,this.runOverlimit=0,this.runT0=performance.now();let s=t.voice??this.voice,c=t.onStart,l=t.onEnd;this.userOnStart=c??null,this.userOnEnd=l??null;let u=this.wait(`planned:${i}`);this.send({type:`plan`,runId:i,text:e,limits:f});let d;try{d=await u}catch(e){return i===this.runId&&(this.phase=`failed`,this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e)),this.result(i,0,0,!1)}if(i!==this.runId)return this.result(i,d.segments.length,0,!1);let p=d.segments;t.onPlanned?.(p);let m=_(e,p);this.phase=`reading`,this.runSegments=p,this.next=p[0]??null;let h,g=new Promise(e=>h=e);this.pendingDone={resolve:h},p.length===0&&(o.close(),this.phase=`done`,h(!0));let v=0,y=null,b=new Map;(async()=>{for(;v<p.length&&i===this.runId&&y===null;){let e=p[v],n=t.rate??this.rate,r=this.estimateSeconds(e,n);if(!o.canAccept(r)){await new Promise(e=>setTimeout(e,50));continue}v++;let a=o.requestSent(r),c=this.synthOne(i,e,s,n,o).catch(e=>{y===null&&(y=e)}).finally(()=>{o.requestSettled(a),b.delete(e.seq)});b.set(e.seq,c)}if(await Promise.all([...b.values()]),y!==null)throw y;i===this.runId&&o.close()})().catch(e=>{i===this.runId&&(this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e),this.phase=`failed`,o.stop(),this.send({type:`cancel`,runId:i}),this.settleWaiters(i,`failed`),this.current=null,this.next=null,this.pendingDone?.resolve(!1),this.pendingDone=null)});let x=await g;return this.result(i,p.length,m,x,o.totalSeconds)}userOnStart=null;userOnEnd=null;async synthOne(e,t,n,r,i,a){let o=`${e}:${t.seq}`,s=this.wait(o);this.send({type:`synth`,runId:e,seq:t.seq,phonemes:t.phonemes,start:t.start,end:t.end,voice:n,speed:r,limit:f.limit,tag:a});let c;try{c=await s}catch(t){if(e!==this.runId)return;throw t}if(e===this.runId){if(c.type===`pcm`){this.runSynthMs+=c.synthMs;let t=c.end-c.start;t>0&&(this.secPerChar=Math.max(this.secPerChar,c.pcm.length/c.sampleRate*r/t)),i.enqueue({runId:e,seq:c.seq,pcm:c.pcm,sampleRate:c.sampleRate,start:c.start,end:c.end,tag:c.tag});return}if(c.type===`overlimit`)throw this.runOverlimit++,Error(`tts_overlimit: ${c.phonemes} > ${c.limit}`);if(c.type===`error`)throw Error(`${c.name}: ${c.message}`)}}runSegments=[];handleStart(e){e.runId===this.runId&&(e.seq<0||(this.runTtfa===null&&(this.runTtfa=performance.now()-this.runT0),this.current={seq:e.seq,start:e.start,end:e.end,at:e.at,seconds:e.seconds,tag:e.tag},this.next=this.runSegments[e.seq+1]??null,this.userOnStart?.(this.current)))}handleEnd(e){e.runId===this.runId&&(e.seq<0||(this.userOnEnd?.({seq:e.seq,start:e.start,end:e.end,at:e.at,seconds:e.seconds,tag:e.tag}),this.current?.seq===e.seq&&(this.current=null)))}handleDrain(e){e===this.runId&&(this.phase=`done`,this.next=null,this.pendingDone?.resolve(!0),this.pendingDone=null)}metrics(e){let t=this.runSynthMs/1e3;return{cache_state:this.cacheState(),ttfa_ms:this.runTtfa===null?null:Math.round(this.runTtfa),rtf:t>0?Math.round(e/t*100)/100:null,gap_ms:Math.round(this.queue?.maxGapMs??0),seconds:Math.round(e*100)/100,synth_ms:Math.round(this.runSynthMs),tts_overlimit:this.runOverlimit,device:this.loadedDevice,dtype:this.loadedDtype}}metricsNow(e){return{runId:e,ttfa:this.runTtfa,synthMs:this.runSynthMs,overlimit:this.runOverlimit,gapMs:this.queue?.maxGapMs??0,seconds:this.queue?.totalSeconds??0}}replacedMetrics=null;result(e,t,n,r,i=0){let a=this.replacedMetrics?.runId===e?this.replacedMetrics:null,o=a??this.metricsNow(e),s=a?a.seconds:i,c=o.synthMs/1e3,l=this.buildRow({ttfa_ms:o.ttfa===null?null:Math.round(o.ttfa),rtf:c>0?Math.round(s/c*100)/100:null,gap_ms:Math.round(o.gapMs),stop_ms:this.lastStop?.runId===e?this.lastStop.ms:null,coverage:n,tts_overlimit:o.overlimit});return this.lastStats=l,{runId:e,segments:t,coverage:n,ttsOverlimit:o.overlimit,seconds:s,finished:r}}buildRow(t){let n=e().web.voice,r=p(),i=performance.memory;return{schema_version:1,target:`web`,device:this.loadedDevice,level:`readall`,browser:r.browser,browser_major:r.browser_major,os:r.os,os_major:r.os_major,cache_state:this.cacheState(),model_id:n.id,model_rev:n.revision,dtype:this.loadedDtype,doc_id:null,commit:null,date:new Date().toISOString(),machine_id:`web`,ttfa_ms:null,gen_ms:null,tok_s:null,rtf:null,gap_ms:null,stop_ms:null,coverage:null,bullets_total:null,bullets_cut:null,cut_rate:null,facts_token_hit:null,halluc_flags:null,forbidden_hits:null,oneline_keyword_hit:null,tts_overlimit:0,peak_mb:null,heap_mb:i?Math.round(i.usedJSHeapSize/1e6):null,...t}}streamChain=Promise.resolve();streamSeq=0;streaming=!1;beginStream(e={}){this.ensureContext();let t=++this.runId;return this.queue.beginRun(t),this.phase=`reading`,this.error=null,this.current=null,this.next=null,this.runTtfa=null,this.runSynthMs=0,this.runOverlimit=0,this.runT0=performance.now(),this.userOnStart=e.onStart??null,this.userOnEnd=e.onEnd??null,this.streamSeq=0,this.streamChain=Promise.resolve(),this.streaming=!0,this.runCold=!1,this.takeCold(),this.pendingDone=null,t}streamPending=0;pushText(e,t,n,r,i){this.streamPending++;let a=this.streamChain.then(async()=>{if(e!==this.runId||this.phase===`failed`||(await this.load(),e!==this.runId))return;this.takeCold();let a=-++this.messageSeq,o=this.wait(`planned:${a}`);this.send({type:`plan`,runId:a,text:t,limits:f,firstPieceTarget:0});let s;try{s=await o}catch(t){if(e!==this.runId)return;throw t}let c=this.queue;for(let t of s.segments){let a=this.rate,o=t.text.length*this.secPerChar/Math.max(a,.1);for(;e===this.runId&&!c.canAccept(o);)await new Promise(e=>setTimeout(e,50));if(e!==this.runId)return;let s=this.streamSeq++,l=c.requestSent(o);try{await this.synthOne(e,{seq:s,text:t.text,start:n,end:r,phonemes:t.phonemes},this.voice,a,c,i)}finally{c.requestSettled(l)}}});return a.then(()=>this.streamPending--,()=>this.streamPending--),this.streamChain=a.catch(t=>{e===this.runId&&(this.phase=`failed`,this.error=t instanceof Error?`${t.name}: ${t.message}`:String(t),this.queue?.stop(),this.send({type:`cancel`,runId:e}),this.settleWaiters(e,`failed`),this.current=null,this.next=null,this.pendingDone?.resolve(!1),this.pendingDone=null)}),a}async endStream(e){let t;do t=this.streamChain,await t;while(t!==this.streamChain);e===this.runId&&(this.streaming=!1);let n=this.queue;if(e!==this.runId||this.phase===`failed`)return{finished:!1,metrics:this.metrics(n.totalSeconds)};if(n.enqueued===0)return this.phase=`done`,{finished:!0,metrics:this.metrics(0)};let r=new Promise(e=>this.pendingDone={resolve:e});return n.close(),{finished:await r,metrics:this.metrics(n.totalSeconds)}}noticeKey(e){return`${e}|${this.voice}|${this.rate}`}async prewarm(e){await this.load();for(let t of e){let e=this.noticeKey(t);if(!this.noticeCache.has(e))try{await this.synthMessage(t,e)}catch{}}this.prewarmed=e.every(e=>this.noticeCache.has(this.noticeKey(e)))}async synthMessage(e,t){let r=n(e),i=++this.messageSeq,a=-i,o=this.messageEpoch,s=this.wait(`${a}:${i}`);this.pendingMessages.add(i),this.send({type:`speak`,runId:a,seq:i,text:r,voice:this.voice,speed:this.rate,limits:f});let c;try{c=await s}catch(e){if(o!==this.messageEpoch)return null;throw e}finally{this.pendingMessages.delete(i)}if(o!==this.messageEpoch)return null;if(c.type!==`pcm`)throw Error(c.type===`error`?`${c.name}: ${c.message}`:`tts_overlimit`);let l={pcm:c.pcm,sampleRate:c.sampleRate};return this.noticeCache.set(t,l),l}pendingMessages=new Set;messageEpoch=0;stop(){this.releasePause(),this.noticeNextStart=0,this.streaming=!1;let e=performance.now(),t=this.runId;this.runId++,this.send({type:`cancel`,runId:t}),this.queue?.stop(),this.settleWaiters(t,`stopped`),this.messageEpoch++;for(let e of this.pendingMessages)this.send({type:`cancel`,runId:-e}),this.settleWaiters(-e,`stopped`);this.pendingMessages.clear();for(let e of this.messageSources.splice(0))try{e.stop()}catch{}this.phase=`stopped`,this.current=null,this.next=null;let n=performance.now()-e;return this.lastStop={runId:t,ms:Math.round(n*100)/100},this.lastStats&&={...this.lastStats,stop_ms:Math.round(n*100)/100},this.pendingDone?.resolve(!1),this.pendingDone=null,n}paused=!1;resumeWaiters=[];releasePause(){this.paused=!1;for(let e of this.resumeWaiters.splice(0))e()}pause(){this.paused=!0,this.queue?.pause()}resume(){this.releasePause(),this.queue?.resume()}skip(){return this.queue?.skip()??!1}async speak(e,t){let n=t??performance.now(),r=this.ensureContext(),i=this.noticeKey(e),a=this.messageEpoch,o=this.noticeCache.get(i)??null;if(!o&&(await this.load(),a!==this.messageEpoch||(o=await this.synthMessage(e,i),!o))||a!==this.messageEpoch||this.paused&&(await new Promise(e=>this.resumeWaiters.push(e)),a!==this.messageEpoch))return{seconds:0};let s=this.queue;if(this.streaming&&s&&s.currentRunId===this.runId){let e=this.runId,{pcm:t,sampleRate:n}=o,r=!1;if(this.streamChain=this.streamChain.then(()=>{e===this.runId&&this.streaming&&(r=s.enqueue({runId:e,seq:-1,pcm:t,sampleRate:n,start:-1,end:-1}))}),await this.streamChain,r)return{seconds:t.length/n};if(a!==this.messageEpoch)return{seconds:0}}let c=r.createBuffer(1,o.pcm.length,o.sampleRate);c.copyToChannel(o.pcm,0);let l=r.createBufferSource();l.buffer=c,l.connect(this.getAnalyser()??r.destination),this.messageSources.push(l);let u=new Promise(e=>{l.onended=()=>{let t=this.messageSources.indexOf(l);t>=0&&this.messageSources.splice(t,1),e()};let t=r.currentTime;setTimeout(()=>{r.currentTime===t&&e()},Math.ceil(c.duration*1e3)+500)}),d=Math.max(r.currentTime,this.noticeNextStart);return l.start(d),this.noticeNextStart=d+c.duration,this.lastNoticeLatencyMs=Math.round((performance.now()-n)*100)/100,await u,{seconds:c.duration}}async measure(e,t,n){await this.load();let r=-++this.messageSeq,i=this.wait(`planned:${r}`);this.send({type:`plan`,runId:r,text:e,limits:{target:t,limit:f.limit},firstPieceTarget:0});let a=await i,o=0,s=0;for(let e of a.segments){let t=`${r}:${e.seq}`,i=this.wait(t);this.send({type:`synth`,runId:r,seq:e.seq,phonemes:e.phonemes,start:e.start,end:e.end,voice:this.voice,speed:n??this.rate,limit:f.limit});let a=await i;if(a.type!==`pcm`)throw Error(a.type===`error`?`${a.name}: ${a.message}`:`tts_overlimit`);o+=a.pcm.length/a.sampleRate,s+=a.synthMs}return{seconds:o,pieces:a.segments.length,synthMs:s,maxPhonemes:a.maxPhonemes}}state(){return{phase:this.phase,error:this.error,current:this.current,next:this.next,rate:this.rate,voice:this.voice,ctxState:this.ctx?.state??`none`,aheadSeconds:this.queue?.aheadSeconds()??0,inFlight:this.queue?.requestsInFlight??0,pending:this.streamPending,playing:this.queue?.playing()??null,enqueued:this.queue?.enqueued??0,ended:this.queue?.ended??0}}stats(){return this.lastStats}};function _(e,t){if(t.length===0)return+(e.trim().length===0);let n=0,r=!0;for(let i of t)i.start<n&&(r=!1),e.slice(n,i.start).trim().length>0&&(r=!1),n=i.end;if(e.slice(n).trim().length>0&&(r=!1),r)return 1;let i=new Uint8Array(e.length);for(let n of t)i.fill(1,Math.max(0,n.start),Math.min(e.length,n.end));let a=0,o=0;for(let t=0;t<e.length;t++)/\s/.test(e[t])||(a++,i[t]&&o++);return Math.min(a===0?1:o/a,.999)}var v=null;function y(){return v||=new g,v}export{n as a,y as i,o as n,c as r,s as t};
|
|
|
|
|
|
assets/tts-D-8K-KMm.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{r as e}from"./pins-DVBrHC3g.js";var t={$comment:`Every user-facing string, both targets (docs/PLAN.md rule 8). S0-03 owns the KEYS; Values written in S1-L0 (Claude, Voice Bank protocol, 2026-09-24). Every message is spoken, so key names are spelled out (Control Option V, Command C), never symbols. Web and Mac load this file; no string literal lives in UI code (scripts/strings-check.mjs). Every notice/error is also spoken (rule 8, 'Speak messages' default on). Placeholders {n} {mb} {words} {level} are filled at runtime.`,version:1,locale:`en`,$plan_wording:{"notice.dial_shorter":`Making it shorter.`,"notice.dial_longer":`Adding detail.`,"notice.all_cut":`Couldn't make a summary I trust for this part.`,"notice.oneline_failed":`Couldn't boil this down to one line I trust. Here's the short version.`,"notice.input_limit":`This is over 120,000 words. Select a part of it.`,"mac.intake.press_cmd_c":`Press ⌘C to read it (spoken form: Press Command C to read it.)`,"mac.intake.no_copy_seen":`Didn't see a copy.`,"loading.summarizer_button":`Load summarizer ({mb} MB)`,"dial.readall":`Read all`,"dial.short":`Short`,"dial.caveman":`Caveman`,"dial.oneline":`One line`,$comment:`Wording the plan fixes (rules 2, 11, C3, C9, Architecture). S1-L0 keeps the meaning; it may tune words after the Voice Bank pass.`},strings:{"app.title":`Fruitbat`,"theme.toggle":`Dark mode`,"dial.readall":`Read all`,"dial.short":`Short`,"dial.caveman":`Caveman`,"dial.oneline":`One line`,"panel.title":`The short version`,"panel.empty":`Highlight some text or paste it in the box at the top. Then pick how short you want it.`,"panel.read_chip":`Read`,"panel.summarize":`Read it`,"panel.stop":`Stop`,"panel.pause":`Pause`,"panel.resume":`Resume`,"panel.skip":`Skip`,"panel.read_this_part":`Read this part`,"panel.rate":`Speed`,"panel.rate_slower":`Slower`,"panel.rate_faster":`Faster`,"panel.voice":`Voice`,"panel.speak_messages":`Speak messages`,"panel.hide_pip":`Hide Pip`,"panel.show_pip":`Show Pip`,"panel.font_size":`Text size`,"panel.font_size_small":`Small`,"panel.font_size_medium":`Medium`,"panel.font_size_large":`Large`,"panel.spacing":`Spacing`,"panel.spacing_compact":`Compact`,"panel.spacing_cozy":`Normal`,"panel.spacing_roomy":`Roomy`,"panel.dark_mode":`Dark mode`,"panel.position":`Panel position`,"panel.bug_report":`Report a problem`,"panel.stats":`Stats`,"panel.close":`Close`,"panel.now_reading":`Now reading`,"panel.next_up":`Next`,"intake.paste_placeholder":`Paste text here`,"intake.word_count":`{n} words`,"intake.drop_here":`Drop a text or PDF file here`,"intake.empty":`Nothing to read yet. Highlight some text or paste it here.`,"intake.sample_label":`Sample article`,"demo.try_it_now":`Try it now`,"demo.recording_label":`This one is a recording. Your own text runs on your machine.`,"demo.switch_level":`Turn the dial to hear the same article shorter or longer.`,"demo.excerpt_note":`This is the first part of the article, not the whole thing.`,"loading.probing":`Checking what this browser can do.`,"loading.voice":`Getting the voice ready.`,"loading.summarizer_button":`Load summarizer ({mb} MB)`,"loading.summarizer_size":`The summarizer is a {mb} MB download. It happens once and stays on this device.`,"loading.progress":`{mb} MB so far.`,"loading.cached":`Already on this device. No download needed.`,"loading.first_time":`First time here. The models download once and stay on this device.`,"loading.save_data":`Your browser is set to save data, so nothing big downloads until you ask.`,"loading.mobile":`On a phone the voice works and the sample is a recording. Open this on a laptop for the full thing.`,"loading.almost":`Almost there.`,"loading.ready":`Ready.`,"notice.dial_shorter":`Making it shorter.`,"notice.dial_longer":`Adding detail.`,"notice.all_cut":`Couldn't make a summary I trust for this part.`,"notice.oneline_failed":`Couldn't boil this down to one line I trust. Here's the short version.`,"notice.input_limit":`This is over 120,000 words. Select a part of it.`,"notice.no_webgpu":`This browser can't run the summarizer. Read all still works, and the sample is a recording.`,"notice.probe_failed":`The summarizer didn't start on this machine. Read all still works.`,"notice.low_end_tier":`Using a smaller summarizer so it fits this machine.`,"notice.stopped":`Stopped.`,"notice.done":`That's the end.`,"notice.paused":`Paused.`,"notice.making_summary":`Making the short version.`,"notice.reading_all":`Reading all of it.`,"error.model_download":`The download didn't finish. Check your connection and try again.`,"error.voice":`The voice didn't load. Try again in a moment.`,"error.generation":`Something went wrong making the summary. Read all still works.`,"error.storage_full":`This browser is out of storage, so stats won't be saved.`,"error.empty_input":`There's no text to read yet.`,"error.offline":`You're offline. Anything already downloaded still works.`,"error.unknown":`Something went wrong. Try again.`,"report.title":`Report a problem`,"report.preview_intro":`Here is everything the report contains. Nothing you were reading is in it.`,"report.describe":`What happened?`,"report.open_github":`Open on GitHub`,"report.copy":`Copy report`,"report.copied":`Copied.`,"report.copy_failed":`Couldn't copy it. The report above is selected so you can copy it by hand.`,"report.privacy_note":`The report has numbers and settings only. Never your text, your audio or your file names.`,"panel.voice.af_heart":`Heart`,"panel.voice.af_bella":`Bella`,"stats.title":`Stats`,"stats.empty":`No runs yet.`,"stats.export":`Export CSV`,"stats.clear":`Clear stats`,"stats.col.date":`When`,"stats.col.level":`Level`,"stats.col.device":`Device`,"stats.col.dtype":`Precision`,"stats.col.cache_state":`Cache`,"stats.col.doc_id":`Text`,"stats.col.ttfa_ms":`First audio (ms)`,"stats.col.tok_s":`Tokens per second`,"stats.col.rtf":`Voice speed (x real time)`,"stats.col.bullets_total":`Bullets`,"stats.col.bullets_cut":`Bullets cut`,"a11y.panel_region":`Fruitbat panel`,"a11y.bullet_list":`Summary`,"a11y.dial":`How short`,"a11y.pip":`Pip the fruit bat`,"mac.menu.summarize_selection":`Read the selection`,"mac.menu.summarize_clipboard":`Read the clipboard`,"mac.menu.summarize_region":`Read part of the screen`,"mac.menu.settings":`Settings`,"mac.menu.stats":`Stats`,"mac.menu.check_updates":`Check for updates`,"mac.menu.quit":`Quit Fruitbat`,"mac.onboarding.try_it":`Copy any text, then press Control Option V.`,"mac.onboarding.first_success":`Worked. Now try it on anything you highlight.`,"mac.onboarding.offer_accessibility":`Want to skip the copy step? Give Fruitbat the Accessibility permission and it reads what you highlight.`,"mac.onboarding.updates_disclosure":`Fruitbat checks for updates once a day. You can turn that off in Settings.`,"mac.onboarding.done":`You're set. Fruitbat lives in the menu bar.`,"mac.permission.accessibility":`Fruitbat needs the Accessibility permission to read what you highlight. Without it, copy the text first and press Control Option V.`,"mac.permission.accessibility_denied":`No Accessibility permission. Copy the text and press Control Option V instead.`,"mac.permission.screen_recording":`Fruitbat needs Screen Recording to read part of the screen. Without it, the other two ways still work.`,"mac.permission.screen_recording_denied":`No Screen Recording permission. Highlight text or copy it instead.`,"mac.permission.open_settings":`Open System Settings`,"mac.intake.press_cmd_c":`Press Command C to read it.`,"mac.intake.no_copy_seen":`Didn't see a copy.`,"mac.intake.clipboard_empty":`The clipboard is empty.`,"mac.intake.clipboard_not_text":`The clipboard doesn't have text in it.`,"mac.ocr.first_run_warning":`Anything on screen becomes text on this Mac, passwords included. It stays here.`,"mac.ocr.nothing_found":`Couldn't find any text there.`,"mac.ocr.pick_region":`Drag over the part you want read.`,"mac.update.toggle":`Check for updates`,"mac.update.available":`A new version is ready.`,"mac.update.up_to_date":`You're up to date.`,"mac.license.trial_days_left":`{n} days left in the trial.`,"mac.license.enter_key":`Enter your license key`,"mac.license.licensed":`Licensed. Thank you.`,"mac.license.nag":`Couldn't check the license in a while. Fruitbat keeps working.`,"mac.license.sign_out":`Sign out of this Mac`,"mac.license.invalid":`That key didn't work. Check it and try again.`,"intake.read_button":`Read this`}}.strings;function n(e){return t[e]??e}var r=25,i=.005,a=class{maxAheadSeconds;maxInFlight;ctx;opts;runId=0;nextStart=0;scheduled=[];inFlight=0;analyser=null;maxGapMs=0;totalSeconds=0;enqueued=0;ended=0;closed=!1;ticker=null;constructor(e,t={}){this.ctx=e,this.opts=t,this.maxAheadSeconds=t.maxAheadSeconds??30,this.maxInFlight=t.maxInFlight??3}get currentRunId(){return this.runId}beginRun(e){this.stop(),this.runId=e,this.nextStart=this.ctx.currentTime,this.maxGapMs=0,this.totalSeconds=0,this.enqueued=0,this.ended=0,this.closed=!1}close(){this.closed=!0,this.maybeDrain()}requestSent(e=0){return this.inFlight++,this.reserved+=e,{run:this.runId,seconds:e}}requestSettled(e={run:this.runId,seconds:0}){e.run===this.runId&&(this.inFlight=Math.max(0,this.inFlight-1),this.reserved=Math.max(0,this.reserved-e.seconds))}reserved=0;get requestsInFlight(){return this.inFlight}aheadSeconds(){return Math.max(0,this.nextStart-this.ctx.currentTime)}canAccept(e=0){if(this.inFlight>=this.maxInFlight)return!1;let t=this.aheadSeconds()+this.reserved;return t+e<=this.maxAheadSeconds&&t<this.maxAheadSeconds||this.inFlight===0&&t<=Math.min(5,this.maxAheadSeconds/2)}getAnalyser(){return!this.analyser&&this.ctx.createAnalyser&&(this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=2048,this.analyser.connect(this.ctx.destination)),this.analyser}enqueue(e){if(e.runId!==this.runId||this.closed)return!1;let t=this.ctx.createBuffer(1,e.pcm.length,e.sampleRate);t.copyToChannel(e.pcm,0);let n=this.ctx.currentTime,r=Math.max(n,this.nextStart);if(this.enqueued>0){let e=(r-this.nextStart)*1e3;e>this.maxGapMs&&(this.maxGapMs=e)}let i=this.schedule(e,t,r);return this.nextStart=r+i.seconds,this.totalSeconds+=i.seconds,this.enqueued++,!0}schedule(e,t,n){let r=this.ctx.createBufferSource();r.buffer=t,r.connect(this.getAnalyser()??this.ctx.destination);let i={item:e,buffer:t,source:r,startAt:n,seconds:t.duration,started:!1,ended:!1};return r.onended=()=>{if(i.ended)return;i.ended=!0;let t=this.scheduled.indexOf(i);t>=0&&this.scheduled.splice(t,1),e.runId===this.runId&&(this.ended++,this.opts.onEnd?.({runId:e.runId,seq:e.seq,start:e.start,end:e.end,tag:e.tag,at:this.ctx.currentTime,seconds:i.seconds}),this.maybeDrain())},r.start(n),this.scheduled.push(i),this.ensureTicker(),this.tick(),i}ensureTicker(){this.ticker===null&&(this.ticker=setInterval(()=>this.tick(),r))}tick(){if(this.ctx.state===`suspended`)return;let e=this.ctx.currentTime;for(let t of this.scheduled)t.started||t.ended||t.item.runId!==this.runId||e+i>=t.startAt&&(t.started=!0,this.opts.onStart?.({runId:t.item.runId,seq:t.item.seq,start:t.item.start,end:t.item.end,tag:t.item.tag,at:t.startAt,seconds:t.seconds}));this.scheduled.length===0&&this.ticker!==null&&(clearInterval(this.ticker),this.ticker=null)}maybeDrain(){if(this.closed&&this.scheduled.length===0&&this.enqueued>0&&this.ended>=this.enqueued){let e=this.runId;this.closed=!1,this.opts.onDrain?.(e)}}playing(){let e=this.ctx.currentTime;for(let t of this.scheduled)if(!t.ended&&e+i>=t.startAt&&e<t.startAt+t.seconds)return t.item;return null}stop(){let e=typeof performance<`u`?performance.now():Date.now();for(let e of this.scheduled){e.ended=!0;try{e.source.stop()}catch{}}return this.scheduled=[],this.ticker!==null&&(clearInterval(this.ticker),this.ticker=null),this.nextStart=this.ctx.currentTime,this.runId++,this.closed=!1,this.inFlight=0,this.reserved=0,(typeof performance<`u`?performance.now():Date.now())-e}pause(){return this.ctx.suspend()}resume(){return this.ctx.resume()}skip(){let e=this.ctx.currentTime,t=this.scheduled.findIndex(t=>!t.ended&&e+i>=t.startAt&&e<t.startAt+t.seconds);if(t<0)return!1;let n=this.scheduled.slice(t+1),r=this.scheduled[t];for(let e of[r,...n]){e.ended=!0;try{e.source.stop()}catch{}}this.scheduled=this.scheduled.slice(0,t),this.ended++,this.opts.onEnd?.({runId:r.item.runId,seq:r.item.seq,start:r.item.start,end:r.item.end,tag:r.item.tag,at:e,seconds:r.seconds});let a=e;for(let e of n){let t=this.schedule(e.item,e.buffer,a);a+=t.seconds}return this.nextStart=a,this.maybeDrain(),!0}},o=.7,s=1.5,c=[`af_heart`,`af_bella`],l=`fruitbat.rate`,u=14,d=`fruitbat.voice`,f={target:e().web.voice.tts_phoneme_target,limit:e().web.voice.tts_phoneme_limit};function p(){let e=typeof navigator<`u`?navigator.userAgent:``,t=t=>{let n=t.exec(e);return n?Number(n[1]):null},n=`other`,r=null;/Edg\//.test(e)?(n=`edge`,r=t(/Edg\/(\d+)/)):/Firefox\//.test(e)?(n=`firefox`,r=t(/Firefox\/(\d+)/)):/Chrome\//.test(e)?(n=`chrome`,r=t(/Chrome\/(\d+)/)):/Safari\//.test(e)&&/Version\//.test(e)&&(n=`safari`,r=t(/Version\/(\d+)/));let i=`other`,a=null;return/iPhone|iPad/.test(e)?(i=`ios`,a=t(/OS (\d+)_/)):/Android/.test(e)?(i=`android`,a=t(/Android (\d+)/)):/Mac OS X/.test(e)?(i=`macos`,a=t(/Mac OS X (\d+)/)):/Windows NT/.test(e)?(i=`windows`,a=t(/Windows NT (\d+)/)):/Linux/.test(e)&&(i=`linux`,a=null),{browser:n,browser_major:r,os:i,os_major:a}}function m(e){try{return localStorage.getItem(e)}catch{return null}}function h(e,t){try{localStorage.setItem(e,t)}catch{}}var g=class{worker=null;ctx=null;noticeNextStart=0;queue=null;runId=0;messageSeq=0;waiters=new Map;loaded=null;messageSources=[];lastStats=null;progressListeners=new Set;phase=`idle`;error=null;current=null;next=null;rate;voice;downloadedBytes=0;coldPending=!1;runCold=!1;takeCold(){this.coldPending&&=(this.runCold=!0,!1)}cacheState(){return this.loaded?this.runCold?`cold`:`warm`:`unknown`}device;loadedInfo=null;loadedDevice=`wasm`;loadedDtype=`q8`;noticeCache=new Map;prewarmed=!1;lastNoticeLatencyMs=null;constructor(){let e=Number(m(l));this.rate=Number.isFinite(e)&&e>=.7&&e<=1.5?e:1;let t=m(d);this.voice=c.includes(t??``)?t:`af_heart`;let n=typeof location<`u`?new URLSearchParams(location.search).get(`tts`):null;this.device=n===`webgpu`?`webgpu`:`wasm`}get isLoaded(){return this.loadedInfo!==null}getRate(){return this.rate}setRate(e){let t=Math.min(s,Math.max(o,Number(e)||1));return this.rate=Math.round(t*100)/100,h(l,String(this.rate)),this.rate}getVoice(){return this.voice}setVoice(e){return c.includes(e)&&(this.voice=e,h(d,e)),this.voice}onProgress(e){return this.progressListeners.add(e),()=>this.progressListeners.delete(e)}ensureContext(){return this.ctx||(this.ctx=new AudioContext({sampleRate:24e3}),this.queue=new a(this.ctx,{maxAheadSeconds:30,maxInFlight:3,onStart:e=>this.handleStart(e),onEnd:e=>this.handleEnd(e),onDrain:e=>this.handleDrain(e)})),this.paused||this.ctx.resume(),this.ctx}get context(){return this.ctx}getAnalyser(){return this.queue?.getAnalyser()??null}getWorker(){return this.worker||(this.worker=new Worker(new URL(`/assets/tts.worker-DY38C-hY.js`,``+import.meta.url),{type:`module`}),this.worker.onmessage=e=>this.handleMessage(e.data),this.worker.onerror=e=>{this.error=`worker: ${e.message}`;for(let e of this.waiters.values())e.reject(Error(this.error));this.waiters.clear()}),this.worker}send(e){this.getWorker().postMessage(e)}wait(e){return new Promise((t,n)=>this.waiters.set(e,{resolve:t,reject:n}))}handleMessage(e){if(e.type===`progress`){for(let t of this.progressListeners)t(e.file,e.loaded,e.total);return}if(e.type===`loaded`){this.downloadedBytes=e.downloadedBytes,this.coldPending=e.downloadedBytes>1e6,this.loadedDevice=e.device,this.loadedDtype=e.dtype,this.loadedInfo=e,this.waiters.get(`loaded`)?.resolve(e),this.waiters.delete(`loaded`);return}if(e.type===`planned`){let t=`planned:${e.runId}`;this.waiters.get(t)?.resolve(e),this.waiters.delete(t);return}if((e.type===`error`||e.type===`overlimit`)&&e.seq===-1){let t=e.type===`error`?Error(`${e.name}: ${e.message}`):Error(`tts_overlimit: ${e.phonemes} > ${e.limit}`),n=e.runId===-1&&this.waiters.has(`loaded`)?`loaded`:`planned:${e.runId}`,r=this.waiters.get(n);if(!r)return;this.waiters.delete(n),r.reject(t);return}let t=`${e.runId}:${e.seq}`,n=this.waiters.get(t);n&&(this.waiters.delete(t),n.resolve(e))}settleWaiters(e,t){let n=Error(t);n.name=`AbortError`;for(let[t,r]of this.waiters)(t===`planned:${e}`||t.startsWith(`${e}:`))&&(this.waiters.delete(t),r.reject(n))}load(){if(!this.loaded){this.phase=`loading`;let e=this.wait(`loaded`).then(e=>(this.phase===`loading`&&(this.phase=`idle`),e));this.send({type:`load`,device:this.device}),this.loaded=e,e.catch(()=>{this.loaded=null,this.phase=`failed`})}return this.loaded}async plan(e){let t=-++this.messageSeq,n=this.wait(`planned:${t}`);this.send({type:`plan`,runId:t,text:e,limits:f});let r=await n;return{segments:r.segments,maxPhonemes:r.maxPhonemes,splitMs:r.splitMs}}pendingDone=null;readSeq=0;lastStop=null;runTtfa=null;runT0=0;runSynthMs=0;runOverlimit=0;secPerChar=1/u;estimateSeconds(e,t){return Math.max(0,e.end-e.start)*this.secPerChar/Math.max(t,.1)}async readAll(e,t={}){this.releasePause(),this.ensureContext();let n=this.runId,r=++this.readSeq;try{await this.load()}catch(e){return this.phase=`failed`,this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e),this.result(n,0,0,!1)}if(this.runId!==n||r!==this.readSeq)return this.result(n,0,0,!1);let i=++this.runId;this.runCold=!1,this.takeCold();let a=this.pendingDone;this.pendingDone=null,a&&(this.replacedMetrics=this.metricsNow(n)),a?.resolve(!1),this.send({type:`cancel`,runId:n}),this.settleWaiters(n,`stopped`);let o=this.queue;o.beginRun(i),this.phase=`planning`,this.error=null,this.current=null,this.next=null,this.runTtfa=null,this.runSynthMs=0,this.runOverlimit=0,this.runT0=performance.now();let s=t.voice??this.voice,c=t.onStart,l=t.onEnd;this.userOnStart=c??null,this.userOnEnd=l??null;let u=this.wait(`planned:${i}`);this.send({type:`plan`,runId:i,text:e,limits:f});let d;try{d=await u}catch(e){return i===this.runId&&(this.phase=`failed`,this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e)),this.result(i,0,0,!1)}if(i!==this.runId)return this.result(i,d.segments.length,0,!1);let p=d.segments;t.onPlanned?.(p);let m=_(e,p);this.phase=`reading`,this.runSegments=p,this.next=p[0]??null;let h,g=new Promise(e=>h=e);this.pendingDone={resolve:h},p.length===0&&(o.close(),this.phase=`done`,h(!0));let v=0,y=null,b=new Map;(async()=>{for(;v<p.length&&i===this.runId&&y===null;){let e=p[v],n=t.rate??this.rate,r=this.estimateSeconds(e,n);if(!o.canAccept(r)){await new Promise(e=>setTimeout(e,50));continue}v++;let a=o.requestSent(r),c=this.synthOne(i,e,s,n,o).catch(e=>{y===null&&(y=e)}).finally(()=>{o.requestSettled(a),b.delete(e.seq)});b.set(e.seq,c)}if(await Promise.all([...b.values()]),y!==null)throw y;i===this.runId&&o.close()})().catch(e=>{i===this.runId&&(this.error=e instanceof Error?`${e.name}: ${e.message}`:String(e),this.phase=`failed`,o.stop(),this.send({type:`cancel`,runId:i}),this.settleWaiters(i,`failed`),this.current=null,this.next=null,this.pendingDone?.resolve(!1),this.pendingDone=null)});let x=await g;return this.result(i,p.length,m,x,o.totalSeconds)}userOnStart=null;userOnEnd=null;async synthOne(e,t,n,r,i,a){let o=`${e}:${t.seq}`,s=this.wait(o);this.send({type:`synth`,runId:e,seq:t.seq,phonemes:t.phonemes,start:t.start,end:t.end,voice:n,speed:r,limit:f.limit,tag:a});let c;try{c=await s}catch(t){if(e!==this.runId)return;throw t}if(e===this.runId){if(c.type===`pcm`){this.runSynthMs+=c.synthMs;let t=c.end-c.start;t>0&&(this.secPerChar=Math.max(this.secPerChar,c.pcm.length/c.sampleRate*r/t)),i.enqueue({runId:e,seq:c.seq,pcm:c.pcm,sampleRate:c.sampleRate,start:c.start,end:c.end,tag:c.tag});return}if(c.type===`overlimit`)throw this.runOverlimit++,Error(`tts_overlimit: ${c.phonemes} > ${c.limit}`);if(c.type===`error`)throw Error(`${c.name}: ${c.message}`)}}runSegments=[];handleStart(e){e.runId===this.runId&&(e.seq<0||(this.runTtfa===null&&(this.runTtfa=performance.now()-this.runT0),this.current={seq:e.seq,start:e.start,end:e.end,at:e.at,seconds:e.seconds,tag:e.tag},this.next=this.runSegments[e.seq+1]??null,this.userOnStart?.(this.current)))}handleEnd(e){e.runId===this.runId&&(e.seq<0||(this.userOnEnd?.({seq:e.seq,start:e.start,end:e.end,at:e.at,seconds:e.seconds,tag:e.tag}),this.current?.seq===e.seq&&(this.current=null)))}handleDrain(e){e===this.runId&&(this.phase=`done`,this.next=null,this.pendingDone?.resolve(!0),this.pendingDone=null)}metrics(e){let t=this.runSynthMs/1e3;return{cache_state:this.cacheState(),ttfa_ms:this.runTtfa===null?null:Math.round(this.runTtfa),rtf:t>0?Math.round(e/t*100)/100:null,gap_ms:Math.round(this.queue?.maxGapMs??0),seconds:Math.round(e*100)/100,synth_ms:Math.round(this.runSynthMs),tts_overlimit:this.runOverlimit,device:this.loadedDevice,dtype:this.loadedDtype}}metricsNow(e){return{runId:e,ttfa:this.runTtfa,synthMs:this.runSynthMs,overlimit:this.runOverlimit,gapMs:this.queue?.maxGapMs??0,seconds:this.queue?.totalSeconds??0}}replacedMetrics=null;result(e,t,n,r,i=0){let a=this.replacedMetrics?.runId===e?this.replacedMetrics:null,o=a??this.metricsNow(e),s=a?a.seconds:i,c=o.synthMs/1e3,l=this.buildRow({ttfa_ms:o.ttfa===null?null:Math.round(o.ttfa),rtf:c>0?Math.round(s/c*100)/100:null,gap_ms:Math.round(o.gapMs),stop_ms:this.lastStop?.runId===e?this.lastStop.ms:null,coverage:n,tts_overlimit:o.overlimit});return this.lastStats=l,{runId:e,segments:t,coverage:n,ttsOverlimit:o.overlimit,seconds:s,finished:r}}buildRow(t){let n=e().web.voice,r=p(),i=performance.memory;return{schema_version:1,target:`web`,device:this.loadedDevice,level:`readall`,browser:r.browser,browser_major:r.browser_major,os:r.os,os_major:r.os_major,cache_state:this.cacheState(),model_id:n.id,model_rev:n.revision,dtype:this.loadedDtype,doc_id:null,commit:null,date:new Date().toISOString(),machine_id:`web`,ttfa_ms:null,gen_ms:null,tok_s:null,rtf:null,gap_ms:null,stop_ms:null,coverage:null,bullets_total:null,bullets_cut:null,cut_rate:null,facts_token_hit:null,halluc_flags:null,forbidden_hits:null,oneline_keyword_hit:null,tts_overlimit:0,peak_mb:null,heap_mb:i?Math.round(i.usedJSHeapSize/1e6):null,...t}}streamChain=Promise.resolve();streamSeq=0;streaming=!1;beginStream(e={}){this.ensureContext();let t=++this.runId;return this.queue.beginRun(t),this.phase=`reading`,this.error=null,this.current=null,this.next=null,this.runTtfa=null,this.runSynthMs=0,this.runOverlimit=0,this.runT0=performance.now(),this.userOnStart=e.onStart??null,this.userOnEnd=e.onEnd??null,this.streamSeq=0,this.streamChain=Promise.resolve(),this.streaming=!0,this.runCold=!1,this.takeCold(),this.pendingDone=null,t}streamPending=0;pushText(e,t,n,r,i){this.streamPending++;let a=this.streamChain.then(async()=>{if(e!==this.runId||this.phase===`failed`||(await this.load(),e!==this.runId))return;this.takeCold();let a=-++this.messageSeq,o=this.wait(`planned:${a}`);this.send({type:`plan`,runId:a,text:t,limits:f,firstPieceTarget:0});let s;try{s=await o}catch(t){if(e!==this.runId)return;throw t}let c=this.queue;for(let t of s.segments){let a=this.rate,o=t.text.length*this.secPerChar/Math.max(a,.1);for(;e===this.runId&&!c.canAccept(o);)await new Promise(e=>setTimeout(e,50));if(e!==this.runId)return;let s=this.streamSeq++,l=c.requestSent(o);try{await this.synthOne(e,{seq:s,text:t.text,start:n,end:r,phonemes:t.phonemes},this.voice,a,c,i)}finally{c.requestSettled(l)}}});return a.then(()=>this.streamPending--,()=>this.streamPending--),this.streamChain=a.catch(t=>{e===this.runId&&(this.phase=`failed`,this.error=t instanceof Error?`${t.name}: ${t.message}`:String(t),this.queue?.stop(),this.send({type:`cancel`,runId:e}),this.settleWaiters(e,`failed`),this.current=null,this.next=null,this.pendingDone?.resolve(!1),this.pendingDone=null)}),a}async endStream(e){let t;do t=this.streamChain,await t;while(t!==this.streamChain);e===this.runId&&(this.streaming=!1);let n=this.queue;if(e!==this.runId||this.phase===`failed`)return{finished:!1,metrics:this.metrics(n.totalSeconds)};if(n.enqueued===0)return this.phase=`done`,{finished:!0,metrics:this.metrics(0)};let r=new Promise(e=>this.pendingDone={resolve:e});return n.close(),{finished:await r,metrics:this.metrics(n.totalSeconds)}}noticeKey(e){return`${e}|${this.voice}|${this.rate}`}async prewarm(e){await this.load();for(let t of e){let e=this.noticeKey(t);if(!this.noticeCache.has(e))try{await this.synthMessage(t,e)}catch{}}this.prewarmed=e.every(e=>this.noticeCache.has(this.noticeKey(e)))}async synthMessage(e,t){let r=n(e),i=++this.messageSeq,a=-i,o=this.messageEpoch,s=this.wait(`${a}:${i}`);this.pendingMessages.add(i),this.send({type:`speak`,runId:a,seq:i,text:r,voice:this.voice,speed:this.rate,limits:f});let c;try{c=await s}catch(e){if(o!==this.messageEpoch)return null;throw e}finally{this.pendingMessages.delete(i)}if(o!==this.messageEpoch)return null;if(c.type!==`pcm`)throw Error(c.type===`error`?`${c.name}: ${c.message}`:`tts_overlimit`);let l={pcm:c.pcm,sampleRate:c.sampleRate};return this.noticeCache.set(t,l),l}pendingMessages=new Set;messageEpoch=0;stop(){this.releasePause(),this.noticeNextStart=0,this.streaming=!1;let e=performance.now(),t=this.runId;this.runId++,this.send({type:`cancel`,runId:t}),this.queue?.stop(),this.settleWaiters(t,`stopped`),this.messageEpoch++;for(let e of this.pendingMessages)this.send({type:`cancel`,runId:-e}),this.settleWaiters(-e,`stopped`);this.pendingMessages.clear();for(let e of this.messageSources.splice(0))try{e.stop()}catch{}this.phase=`stopped`,this.current=null,this.next=null;let n=performance.now()-e;return this.lastStop={runId:t,ms:Math.round(n*100)/100},this.lastStats&&={...this.lastStats,stop_ms:Math.round(n*100)/100},this.pendingDone?.resolve(!1),this.pendingDone=null,n}paused=!1;resumeWaiters=[];releasePause(){this.paused=!1;for(let e of this.resumeWaiters.splice(0))e()}pause(){this.paused=!0,this.queue?.pause()}resume(){this.releasePause(),this.queue?.resume()}skip(){return this.queue?.skip()??!1}async speak(e,t){let n=t??performance.now(),r=this.ensureContext(),i=this.noticeKey(e),a=this.messageEpoch,o=this.noticeCache.get(i)??null;if(!o&&(await this.load(),a!==this.messageEpoch||(o=await this.synthMessage(e,i),!o))||a!==this.messageEpoch||this.paused&&(await new Promise(e=>this.resumeWaiters.push(e)),a!==this.messageEpoch))return{seconds:0};let s=this.queue;if(this.streaming&&s&&s.currentRunId===this.runId){let e=this.runId,{pcm:t,sampleRate:n}=o,r=!1;if(this.streamChain=this.streamChain.then(()=>{e===this.runId&&this.streaming&&(r=s.enqueue({runId:e,seq:-1,pcm:t,sampleRate:n,start:-1,end:-1}))}),await this.streamChain,r)return{seconds:t.length/n};if(a!==this.messageEpoch)return{seconds:0}}let c=r.createBuffer(1,o.pcm.length,o.sampleRate);c.copyToChannel(o.pcm,0);let l=r.createBufferSource();l.buffer=c,l.connect(this.getAnalyser()??r.destination),this.messageSources.push(l);let u=new Promise(e=>{l.onended=()=>{let t=this.messageSources.indexOf(l);t>=0&&this.messageSources.splice(t,1),e()};let t=r.currentTime;setTimeout(()=>{r.currentTime===t&&e()},Math.ceil(c.duration*1e3)+500)}),d=Math.max(r.currentTime,this.noticeNextStart);return l.start(d),this.noticeNextStart=d+c.duration,this.lastNoticeLatencyMs=Math.round((performance.now()-n)*100)/100,await u,{seconds:c.duration}}async measure(e,t,n){await this.load();let r=-++this.messageSeq,i=this.wait(`planned:${r}`);this.send({type:`plan`,runId:r,text:e,limits:{target:t,limit:f.limit},firstPieceTarget:0});let a=await i,o=0,s=0;for(let e of a.segments){let t=`${r}:${e.seq}`,i=this.wait(t);this.send({type:`synth`,runId:r,seq:e.seq,phonemes:e.phonemes,start:e.start,end:e.end,voice:this.voice,speed:n??this.rate,limit:f.limit});let a=await i;if(a.type!==`pcm`)throw Error(a.type===`error`?`${a.name}: ${a.message}`:`tts_overlimit`);o+=a.pcm.length/a.sampleRate,s+=a.synthMs}return{seconds:o,pieces:a.segments.length,synthMs:s,maxPhonemes:a.maxPhonemes}}state(){return{phase:this.phase,error:this.error,current:this.current,next:this.next,rate:this.rate,voice:this.voice,ctxState:this.ctx?.state??`none`,aheadSeconds:this.queue?.aheadSeconds()??0,inFlight:this.queue?.requestsInFlight??0,pending:this.streamPending,playing:this.queue?.playing()??null,enqueued:this.queue?.enqueued??0,ended:this.queue?.ended??0}}stats(){return this.lastStats}};function _(e,t){if(t.length===0)return+(e.trim().length===0);let n=0,r=!0;for(let i of t)i.start<n&&(r=!1),e.slice(n,i.start).trim().length>0&&(r=!1),n=i.end;if(e.slice(n).trim().length>0&&(r=!1),r)return 1;let i=new Uint8Array(e.length);for(let n of t)i.fill(1,Math.max(0,n.start),Math.min(e.length,n.end));let a=0,o=0;for(let t=0;t<e.length;t++)/\s/.test(e[t])||(a++,i[t]&&o++);return Math.min(a===0?1:o/a,.999)}var v=null;function y(){return v||=new g,v}export{n as a,y as i,o as n,c as r,s as t};
|
index.html
CHANGED
|
@@ -9,11 +9,11 @@
|
|
| 9 |
<!-- csp:end -->
|
| 10 |
<!-- Cross-origin isolation on a static host: the service worker adds COOP/COEP (S1-00a). -->
|
| 11 |
<script src="/coi-serviceworker.js"></script>
|
| 12 |
-
<script type="module" crossorigin src="/assets/index-
|
| 13 |
<link rel="modulepreload" crossorigin href="/assets/pins-DVBrHC3g.js">
|
| 14 |
<link rel="modulepreload" crossorigin href="/assets/loader-BcwYWrA-.js">
|
| 15 |
<link rel="modulepreload" crossorigin href="/assets/llm-BwHcI5dl.js">
|
| 16 |
-
<link rel="modulepreload" crossorigin href="/assets/tts-
|
| 17 |
<link rel="stylesheet" crossorigin href="/assets/index-DTUQ8gca.css">
|
| 18 |
</head>
|
| 19 |
<body>
|
|
|
|
| 9 |
<!-- csp:end -->
|
| 10 |
<!-- Cross-origin isolation on a static host: the service worker adds COOP/COEP (S1-00a). -->
|
| 11 |
<script src="/coi-serviceworker.js"></script>
|
| 12 |
+
<script type="module" crossorigin src="/assets/index-CaLiB7gc.js"></script>
|
| 13 |
<link rel="modulepreload" crossorigin href="/assets/pins-DVBrHC3g.js">
|
| 14 |
<link rel="modulepreload" crossorigin href="/assets/loader-BcwYWrA-.js">
|
| 15 |
<link rel="modulepreload" crossorigin href="/assets/llm-BwHcI5dl.js">
|
| 16 |
+
<link rel="modulepreload" crossorigin href="/assets/tts-D-8K-KMm.js">
|
| 17 |
<link rel="stylesheet" crossorigin href="/assets/index-DTUQ8gca.css">
|
| 18 |
</head>
|
| 19 |
<body>
|
readall.html
CHANGED
|
@@ -8,9 +8,9 @@
|
|
| 8 |
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; connect-src 'self' https://huggingface.co https://us.aws.cdn.hf.co https://*.cdn.hf.co; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self'; media-src 'self' blob:; object-src 'none'; base-uri 'self'; form-action 'none'">
|
| 9 |
<!-- csp:end -->
|
| 10 |
<script src="/coi-serviceworker.js"></script>
|
| 11 |
-
<script type="module" crossorigin src="/assets/readall-
|
| 12 |
<link rel="modulepreload" crossorigin href="/assets/pins-DVBrHC3g.js">
|
| 13 |
-
<link rel="modulepreload" crossorigin href="/assets/tts-
|
| 14 |
</head>
|
| 15 |
<body>
|
| 16 |
<h1>Read all (S1-04 harness)</h1>
|
|
|
|
| 8 |
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; connect-src 'self' https://huggingface.co https://us.aws.cdn.hf.co https://*.cdn.hf.co; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self'; media-src 'self' blob:; object-src 'none'; base-uri 'self'; form-action 'none'">
|
| 9 |
<!-- csp:end -->
|
| 10 |
<script src="/coi-serviceworker.js"></script>
|
| 11 |
+
<script type="module" crossorigin src="/assets/readall-CBhGAHk6.js"></script>
|
| 12 |
<link rel="modulepreload" crossorigin href="/assets/pins-DVBrHC3g.js">
|
| 13 |
+
<link rel="modulepreload" crossorigin href="/assets/tts-D-8K-KMm.js">
|
| 14 |
</head>
|
| 15 |
<body>
|
| 16 |
<h1>Read all (S1-04 harness)</h1>
|