Spaces:
Running
Running
release: webgpu demo + source
Browse files- 6 voicepacks (added Priya + Arjun from SPRINGLab)
- voicepacks.json: single source of truth for voice list + per-gender chips
- dynamic voice dropdown + chips (replaces hardcoded HTML)
- ship full source (src/, package.json, tsconfig.json, vite.config.ts) so
forks can rebuild without de-minifying
- LFS: add glob assets/index-*.js + espeakng worker
- .gitattributes +5 -0
- README.md +49 -12
- assets/index-BZq4ShBi.js +3 -0
- index.html +6 -76
- index.template.html +224 -0
- package-lock.json +2006 -0
- package.json +21 -0
- public/config.json +177 -0
- public/voice_speeds.json +20 -0
- public/voicepacks.json +92 -0
- public/voices/mf_asha.bin +3 -0
- public/voices/mf_mukta.bin +3 -0
- public/voices/mf_priya.bin +3 -0
- public/voices/mm_arjun.bin +3 -0
- public/voices/mm_dnyanesh.bin +3 -0
- public/voices/mm_vivek.bin +3 -0
- public/warli-strip.svg +1117 -0
- src/espeakng/espeakng.worker.js +3 -0
- src/espeakng/phonemizer.ts +238 -0
- src/main.ts +789 -0
- src/model.ts +85 -0
- src/phonemize.ts +11 -0
- src/styles.css +478 -0
- src/timestamps.ts +82 -0
- src/tokenize.ts +61 -0
- tsconfig.json +20 -0
- vite.config.ts +24 -0
- voicepacks.json +92 -0
- voices/mf_priya.bin +3 -0
- voices/mm_arjun.bin +3 -0
.gitattributes
CHANGED
|
@@ -49,3 +49,8 @@ assets/index-BDXCV8Ud.js filter=lfs diff=lfs merge=lfs -text
|
|
| 49 |
assets/index-HaFA3KMR.js filter=lfs diff=lfs merge=lfs -text
|
| 50 |
assets/index-B0gBa3_t.js filter=lfs diff=lfs merge=lfs -text
|
| 51 |
assets/index-C6vp9g9z.js filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
assets/index-HaFA3KMR.js filter=lfs diff=lfs merge=lfs -text
|
| 50 |
assets/index-B0gBa3_t.js filter=lfs diff=lfs merge=lfs -text
|
| 51 |
assets/index-C6vp9g9z.js filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
|
| 53 |
+
# Vite-built hashed bundles (replaces per-file rules — covers all current and future builds)
|
| 54 |
+
assets/index-*.js filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
# espeak-ng-wasm worker (~18 MB; bundled engine + JS shim)
|
| 56 |
+
src/espeakng/espeakng.worker.js filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -14,21 +14,58 @@ tags:
|
|
| 14 |
- marathi
|
| 15 |
- kokoro
|
| 16 |
- webgpu
|
| 17 |
-
-
|
| 18 |
---
|
| 19 |
|
| 20 |
-
#
|
| 21 |
|
| 22 |
-
Marathi (मराठी) text-to-speech running
|
| 23 |
|
| 24 |
-
|
| 25 |
-
- Four voices: Asha (आशा), Vivek (विवेक), Mukta (मुक्ता), Dnyanesh (ज्ञानेश)
|
| 26 |
-
- In-browser Marathi G2P via espeak-ng WASM (from hexgrad/kokoro's patched worker)
|
| 27 |
-
- Per-word timestamps + live highlighting synchronized to audio
|
| 28 |
-
- Click any word to seek
|
| 29 |
-
- Indic-English-style phoneme remap toggle for Minglish (Marathi + English)
|
| 30 |
-
- Warli-inspired UI (tribal art from Maharashtra)
|
| 31 |
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
- marathi
|
| 15 |
- kokoro
|
| 16 |
- webgpu
|
| 17 |
+
- on-device
|
| 18 |
---
|
| 19 |
|
| 20 |
+
# bol-tts-marathi — Marathi TTS via WebGPU
|
| 21 |
|
| 22 |
+
In-browser Marathi (मराठी) text-to-speech, running real-time on WebGPU. Six voices, code-switch (Minglish) support, no server round-trip.
|
| 23 |
|
| 24 |
+
This Space ships **both the built demo** (`index.html` + `assets/` served directly) **and the full source** (`src/`, `package.json`, `tsconfig.json`, `vite.config.ts`) so anyone can fork the Space and rebuild without de-minifying the bundle.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
+
## What's in this Space
|
| 27 |
|
| 28 |
+
| Path | Role |
|
| 29 |
+
|---|---|
|
| 30 |
+
| `index.html`, `assets/*` | built demo, served via static SDK |
|
| 31 |
+
| `voices/*.bin` | 6 voicepacks (style vectors, 522 KB each) |
|
| 32 |
+
| `voicepacks.json` | source-of-truth for voice list + per-gender test phrases |
|
| 33 |
+
| `src/` | TypeScript source (phonemizer, tokenizer, model wrapper, UI wiring) |
|
| 34 |
+
| `public/` | static assets fed to the build |
|
| 35 |
+
| `package.json`, `tsconfig.json`, `vite.config.ts` | reproducible build config |
|
| 36 |
+
| `index.template.html` | the source-side index.html that Vite injects modules into |
|
| 37 |
+
|
| 38 |
+
## Reproduce locally
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
git clone https://huggingface.co/spaces/<USER>/<THIS-SPACE>
|
| 42 |
+
cd <THIS-SPACE>
|
| 43 |
+
npm install
|
| 44 |
+
npm run dev # local dev server at http://localhost:5173
|
| 45 |
+
npm run build # produces dist/ — same artifacts shipped here
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
## How it works
|
| 49 |
+
|
| 50 |
+
- 82M-parameter Kokoro fine-tune for Marathi (model: [`shreyask/bol-tts-marathi-onnx`](https://huggingface.co/shreyask/bol-tts-marathi-onnx))
|
| 51 |
+
- `@huggingface/transformers` loads the ONNX model into WebGPU (or falls back to WASM)
|
| 52 |
+
- `espeak-ng-wasm` phonemizes Marathi + Indian English; we parse `(en)/(mr)` tags for per-segment Indian-accent rules on English chunks
|
| 53 |
+
- 6 voices via single shared model + per-voice 256-D style vectors (`voices/*.bin`, [510, 1, 256] float32)
|
| 54 |
+
- Test phrases + voice metadata in `voicepacks.json` — edit to add voices, no code changes needed
|
| 55 |
+
|
| 56 |
+
Recipe + training scripts: [github.com/shreyaskarnik/bol-tts-marathi](https://github.com/shreyaskarnik/bol-tts-marathi)
|
| 57 |
+
|
| 58 |
+
## Voices
|
| 59 |
+
|
| 60 |
+
| ID | Name | Gender | Source | Status |
|
| 61 |
+
|---|---|---|---|---|
|
| 62 |
+
| `mf_asha` | Asha (आशा) | female | Rasa Marathi | stable |
|
| 63 |
+
| `mm_vivek` | Vivek (विवेक) | male | Rasa Marathi | stable |
|
| 64 |
+
| `mf_mukta` | Mukta (मुक्ता) | female | IndicVoices-R | stable |
|
| 65 |
+
| `mm_dnyanesh` | Dnyanesh (ज्ञानेश) | male | IndicVoices-R | stable |
|
| 66 |
+
| `mf_priya` | Priya (प्रिया) | female | SPRINGLab IndicTTS | v0.2 preview |
|
| 67 |
+
| `mm_arjun` | Arjun (अर्जुन) | male | SPRINGLab IndicTTS | v0.2 preview |
|
| 68 |
+
|
| 69 |
+
## License
|
| 70 |
+
|
| 71 |
+
Apache 2.0. See LICENSE.
|
assets/index-BZq4ShBi.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bf6bfe9f328713c187f76ce5514e829945d5c9337022587cdca68885f6040469
|
| 3 |
+
size 19064726
|
index.html
CHANGED
|
@@ -17,7 +17,7 @@
|
|
| 17 |
rel="stylesheet"
|
| 18 |
/>
|
| 19 |
|
| 20 |
-
<script type="module" crossorigin src="/assets/index-
|
| 21 |
<link rel="stylesheet" crossorigin href="/assets/index-BdhRcMoO.css">
|
| 22 |
</head>
|
| 23 |
<body>
|
|
@@ -76,89 +76,19 @@
|
|
| 76 |
पावसाच्या संध्याकाळी शनिवारवाड्याजवळ गरम कटिंग चहा घेत मी मित्रांना म्हणालो, “आज आकाशातला प्रत्येक ढग एखादी नवी गोष्ट सांगतोय.”</textarea
|
| 77 |
>
|
| 78 |
|
| 79 |
-
<
|
|
|
|
| 80 |
<span class="examples-label">मराठी:</span>
|
| 81 |
-
<button
|
| 82 |
-
class="chip"
|
| 83 |
-
data-text="आज हवामान खूप छान आहे, पाऊस पडतो आहे."
|
| 84 |
-
>
|
| 85 |
-
हवामान
|
| 86 |
-
</button>
|
| 87 |
-
<button
|
| 88 |
-
class="chip"
|
| 89 |
-
data-text="महाराष्ट्र हे भारतातील एक महत्त्वाचे राज्य आहे."
|
| 90 |
-
>
|
| 91 |
-
महाराष्ट्र
|
| 92 |
-
</button>
|
| 93 |
-
<button
|
| 94 |
-
class="chip"
|
| 95 |
-
data-text="मुंबई ही महाराष्ट्राची राजधानी आणि भारताची आर्थिक राजधानी आहे."
|
| 96 |
-
>
|
| 97 |
-
मुंबई
|
| 98 |
-
</button>
|
| 99 |
-
<button
|
| 100 |
-
class="chip"
|
| 101 |
-
data-text="पुणे ही महाराष्ट्राची सांस्कृतिक राजधानी मानली जाते."
|
| 102 |
-
>
|
| 103 |
-
पुणे
|
| 104 |
-
</button>
|
| 105 |
-
<button class="chip" data-text="एक दोन तीन चार पाच सहा सात आठ.">
|
| 106 |
-
आकडे
|
| 107 |
-
</button>
|
| 108 |
-
<button
|
| 109 |
-
class="chip"
|
| 110 |
-
data-text="पूर्वी एका छोट्या गावात एक शहाणा शेतकरी राहत होता."
|
| 111 |
-
>
|
| 112 |
-
कथा
|
| 113 |
-
</button>
|
| 114 |
-
<button
|
| 115 |
-
class="chip"
|
| 116 |
-
data-text="गुढी पाडवा हा मराठी नववर्षाचा सण आहे."
|
| 117 |
-
>
|
| 118 |
-
गुढी पाडवा
|
| 119 |
-
</button>
|
| 120 |
-
<button
|
| 121 |
-
class="chip"
|
| 122 |
-
data-text="पावसाळी संध्याकाळी गरम भजी आणि कटिंग चहा म्हणजे स्वर्गच!"
|
| 123 |
-
>
|
| 124 |
-
भजी
|
| 125 |
-
</button>
|
| 126 |
-
<button
|
| 127 |
-
class="chip"
|
| 128 |
-
data-text="मुंबईचा वडापाव आणि पुण्याचा चहा — आपली खरी ओळख."
|
| 129 |
-
>
|
| 130 |
-
वडापाव
|
| 131 |
-
</button>
|
| 132 |
</div>
|
| 133 |
-
<div class="examples">
|
| 134 |
<span class="examples-label">Minglish:</span>
|
| 135 |
-
<button class="chip" data-text="मी Google मध्ये काम करतो.">
|
| 136 |
-
Google job
|
| 137 |
-
</button>
|
| 138 |
-
<button class="chip" data-text="आज मीटिंग ला late झालो.">
|
| 139 |
-
late meeting
|
| 140 |
-
</button>
|
| 141 |
-
<button class="chip" data-text="Coffee पिऊया का?">coffee?</button>
|
| 142 |
-
<button
|
| 143 |
-
class="chip"
|
| 144 |
-
data-text="Artificial intelligence ही technology खूप amazing आहे."
|
| 145 |
-
>
|
| 146 |
-
AI tech
|
| 147 |
-
</button>
|
| 148 |
-
<button class="chip" data-text="Weekend ला movie बघायचा plan आहे का?">
|
| 149 |
-
weekend plan
|
| 150 |
-
</button>
|
| 151 |
</div>
|
| 152 |
|
| 153 |
<div class="controls">
|
| 154 |
<label
|
| 155 |
>आवाज · Voice
|
| 156 |
-
<
|
| 157 |
-
|
| 158 |
-
<option value="mm_vivek">Vivek (विवेक) — male</option>
|
| 159 |
-
<option value="mf_mukta">Mukta (मुक्ता) — female</option>
|
| 160 |
-
<option value="mm_dnyanesh">Dnyanesh (ज्ञानेश) — male</option>
|
| 161 |
-
</select>
|
| 162 |
</label>
|
| 163 |
<label class="speed-control">
|
| 164 |
<span class="speed-control-row">
|
|
|
|
| 17 |
rel="stylesheet"
|
| 18 |
/>
|
| 19 |
|
| 20 |
+
<script type="module" crossorigin src="/assets/index-BZq4ShBi.js"></script>
|
| 21 |
<link rel="stylesheet" crossorigin href="/assets/index-BdhRcMoO.css">
|
| 22 |
</head>
|
| 23 |
<body>
|
|
|
|
| 76 |
पावसाच्या संध्याकाळी शनिवारवाड्याजवळ गरम कटिंग चहा घेत मी मित्रांना म्हणालो, “आज आकाशातला प्रत्येक ढग एखादी नवी गोष्ट सांगतोय.”</textarea
|
| 77 |
>
|
| 78 |
|
| 79 |
+
<!-- chips populated dynamically from public/voicepacks.json (test_phrases.*) -->
|
| 80 |
+
<div class="examples" id="chips-marathi">
|
| 81 |
<span class="examples-label">मराठी:</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
</div>
|
| 83 |
+
<div class="examples" id="chips-minglish">
|
| 84 |
<span class="examples-label">Minglish:</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
</div>
|
| 86 |
|
| 87 |
<div class="controls">
|
| 88 |
<label
|
| 89 |
>आवाज · Voice
|
| 90 |
+
<!-- options populated dynamically from public/voicepacks.json (voices[]) -->
|
| 91 |
+
<select id="voice"></select>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
</label>
|
| 93 |
<label class="speed-control">
|
| 94 |
<span class="speed-control-row">
|
index.template.html
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="mr">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<link
|
| 7 |
+
rel="icon"
|
| 8 |
+
type="image/svg+xml"
|
| 9 |
+
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Ctext y='48' font-size='48'%3E%F0%9F%AA%94%3C/text%3E%3C/svg%3E"
|
| 10 |
+
/>
|
| 11 |
+
<title>बोल — Marathi TTS · WebGPU</title>
|
| 12 |
+
|
| 13 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 14 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 15 |
+
<link
|
| 16 |
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Tiro+Devanagari+Marathi&family=Yatra+One&display=swap"
|
| 17 |
+
rel="stylesheet"
|
| 18 |
+
/>
|
| 19 |
+
|
| 20 |
+
<link rel="stylesheet" href="/src/styles.css" />
|
| 21 |
+
</head>
|
| 22 |
+
<body>
|
| 23 |
+
<!-- Warli tribal art strip: dancers, hut, deer, microphones, sun motifs.
|
| 24 |
+
Custom-illustrated band in the style of traditional Warli wall paintings
|
| 25 |
+
from Maharashtra. Microphones are a contemporary addition reflecting
|
| 26 |
+
the TTS theme. -->
|
| 27 |
+
<div class="warli-strip" aria-hidden="true">
|
| 28 |
+
<img src="/warli-strip.svg" alt="" />
|
| 29 |
+
</div>
|
| 30 |
+
|
| 31 |
+
<main>
|
| 32 |
+
<header>
|
| 33 |
+
<h1>बोल</h1>
|
| 34 |
+
<span class="namaste">Marathi Text-to-Speech</span>
|
| 35 |
+
<p>
|
| 36 |
+
Kokoro-82M fine-tuned for
|
| 37 |
+
<a
|
| 38 |
+
href="https://en.wikipedia.org/wiki/Marathi_language"
|
| 39 |
+
target="_blank"
|
| 40 |
+
rel="noopener"
|
| 41 |
+
>मराठी</a
|
| 42 |
+
>
|
| 43 |
+
· runs in your browser via WebGPU
|
| 44 |
+
</p>
|
| 45 |
+
</header>
|
| 46 |
+
|
| 47 |
+
<section id="status" class="card status">initializing…</section>
|
| 48 |
+
|
| 49 |
+
<section class="card model-gate" id="model-gate" hidden>
|
| 50 |
+
<div class="model-gate-text">
|
| 51 |
+
<strong>Tap to load the TTS model</strong> (~325 MB fp32 ONNX ·
|
| 52 |
+
one-time download, browser-cached)<br />
|
| 53 |
+
<span class="model-gate-sub">
|
| 54 |
+
fp32 is the recommended precision for Kokoro on WebGPU (matches
|
| 55 |
+
kokoro.js + HeadTTS defaults; int8 breaks ISTFTNet decoder audio
|
| 56 |
+
quality). Plus ~40 MB for onnxruntime-web WASM + espeak-ng worker.
|
| 57 |
+
</span>
|
| 58 |
+
</div>
|
| 59 |
+
<button id="load-model" class="primary">Load model</button>
|
| 60 |
+
</section>
|
| 61 |
+
|
| 62 |
+
<section class="card">
|
| 63 |
+
<div class="text-label-row">
|
| 64 |
+
<label for="text">मराठी मजकूर — Marathi text (Devanagari)</label>
|
| 65 |
+
<button
|
| 66 |
+
id="transliterate"
|
| 67 |
+
class="text-tool"
|
| 68 |
+
title="Transliterate Latin → Devanagari using ITRANS scheme. e.g. 'namaskaar majhe naav amit aahe' → 'नमस्कार माझे नाव अमित आहे'. Edit the result before synthesizing if needed."
|
| 69 |
+
type="button"
|
| 70 |
+
>
|
| 71 |
+
A·a → अ
|
| 72 |
+
</button>
|
| 73 |
+
</div>
|
| 74 |
+
<textarea id="text" rows="3">
|
| 75 |
+
पावसाच्या संध्याकाळी शनिवारवाड्याजवळ गरम कटिंग चहा घेत मी मित्रांना म्हणालो, “आज आकाशातला प्रत्येक ढग एखादी नवी गोष्ट सांगतोय.”</textarea
|
| 76 |
+
>
|
| 77 |
+
|
| 78 |
+
<!-- chips populated dynamically from public/voicepacks.json (test_phrases.*) -->
|
| 79 |
+
<div class="examples" id="chips-marathi">
|
| 80 |
+
<span class="examples-label">मराठी:</span>
|
| 81 |
+
</div>
|
| 82 |
+
<div class="examples" id="chips-minglish">
|
| 83 |
+
<span class="examples-label">Minglish:</span>
|
| 84 |
+
</div>
|
| 85 |
+
|
| 86 |
+
<div class="controls">
|
| 87 |
+
<label
|
| 88 |
+
>आवाज · Voice
|
| 89 |
+
<!-- options populated dynamically from public/voicepacks.json (voices[]) -->
|
| 90 |
+
<select id="voice"></select>
|
| 91 |
+
</label>
|
| 92 |
+
<label class="speed-control">
|
| 93 |
+
<span class="speed-control-row">
|
| 94 |
+
<span>गती · Speed</span>
|
| 95 |
+
<span id="speed-val">1.00×</span>
|
| 96 |
+
</span>
|
| 97 |
+
<input
|
| 98 |
+
id="speed"
|
| 99 |
+
type="range"
|
| 100 |
+
min="0.5"
|
| 101 |
+
max="1.5"
|
| 102 |
+
step="0.05"
|
| 103 |
+
value="1.0"
|
| 104 |
+
/>
|
| 105 |
+
</label>
|
| 106 |
+
<button id="synth" class="primary">Synthesize</button>
|
| 107 |
+
</div>
|
| 108 |
+
</section>
|
| 109 |
+
|
| 110 |
+
<section class="card" id="output-section" hidden>
|
| 111 |
+
<audio id="audio" hidden></audio>
|
| 112 |
+
<div class="player">
|
| 113 |
+
<button
|
| 114 |
+
id="playpause"
|
| 115 |
+
class="playpause"
|
| 116 |
+
title="Play / Pause (space)"
|
| 117 |
+
aria-label="Play / Pause"
|
| 118 |
+
>
|
| 119 |
+
▶
|
| 120 |
+
</button>
|
| 121 |
+
<div class="player-main">
|
| 122 |
+
<canvas
|
| 123 |
+
id="waveform"
|
| 124 |
+
class="waveform"
|
| 125 |
+
tabindex="0"
|
| 126 |
+
aria-label="Click to seek"
|
| 127 |
+
></canvas>
|
| 128 |
+
<div class="player-time">
|
| 129 |
+
<span id="time-cur">0:00</span>
|
| 130 |
+
<span id="time-total">0:00</span>
|
| 131 |
+
</div>
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
|
| 135 |
+
<!-- Primary transcript: each word highlighted as audio plays.
|
| 136 |
+
Click a word to seek there. Expand the summary below for IPA + timing. -->
|
| 137 |
+
<div
|
| 138 |
+
class="transcript-compact"
|
| 139 |
+
id="transcript-compact"
|
| 140 |
+
title="Click any word to seek playback to it"
|
| 141 |
+
></div>
|
| 142 |
+
|
| 143 |
+
<details class="transcript-details" id="transcript-details">
|
| 144 |
+
<summary>Per-word IPA + timing</summary>
|
| 145 |
+
<div class="sidebyside" id="transcript">
|
| 146 |
+
<div
|
| 147 |
+
class="col-head"
|
| 148 |
+
title="Devanagari script — Marathi text as written. Each row is one word from your input."
|
| 149 |
+
>
|
| 150 |
+
देवनागरी
|
| 151 |
+
</div>
|
| 152 |
+
<div
|
| 153 |
+
class="col-head"
|
| 154 |
+
title="International Phonetic Alphabet — how each word is pronounced. These are the phonemes fed to the model (via the espeak-ng WASM worker running in your browser)."
|
| 155 |
+
>
|
| 156 |
+
IPA
|
| 157 |
+
</div>
|
| 158 |
+
<div
|
| 159 |
+
class="col-head"
|
| 160 |
+
title="Start and end time of each word in the synthesized audio, predicted by Kokoro's prosody model (pred_dur). The bar fills progressively as the word is playing."
|
| 161 |
+
>
|
| 162 |
+
timing
|
| 163 |
+
</div>
|
| 164 |
+
<!-- rows injected by main.ts -->
|
| 165 |
+
</div>
|
| 166 |
+
</details>
|
| 167 |
+
|
| 168 |
+
<aside class="facts">
|
| 169 |
+
🪔 <strong>मराठी phoneme fact:</strong> Marathi's retroflex lateral
|
| 170 |
+
<span class="ipa-inline">ɭ</span> (ळ) is unusual among Indic languages
|
| 171 |
+
— Hindi doesn't have it. We added <span class="ipa-inline">ɭ</span> to
|
| 172 |
+
Kokoro's vocabulary at slot 144 (replacing an unused placeholder) so
|
| 173 |
+
the model could learn ळ cleanly during fine-tuning. Listen for it in
|
| 174 |
+
"केळी" (banana) or "सकाळ" (morning).
|
| 175 |
+
</aside>
|
| 176 |
+
|
| 177 |
+
<details class="details">
|
| 178 |
+
<summary>All phonemes + raw timings (debug)</summary>
|
| 179 |
+
<pre id="phonemes" class="phonemes"></pre>
|
| 180 |
+
<pre id="word-timings" class="word-timings"></pre>
|
| 181 |
+
</details>
|
| 182 |
+
</section>
|
| 183 |
+
|
| 184 |
+
<footer>
|
| 185 |
+
<p>
|
| 186 |
+
Model
|
| 187 |
+
<a
|
| 188 |
+
href="https://huggingface.co/shreyask/bol-tts-marathi-onnx"
|
| 189 |
+
target="_blank"
|
| 190 |
+
rel="noopener"
|
| 191 |
+
>shreyask/bol-tts-marathi-onnx</a
|
| 192 |
+
>
|
| 193 |
+
· recipe
|
| 194 |
+
<a
|
| 195 |
+
href="https://github.com/shreyaskarnik/bol-tts-marathi"
|
| 196 |
+
target="_blank"
|
| 197 |
+
rel="noopener"
|
| 198 |
+
>github.com/shreyaskarnik/bol-tts-marathi</a
|
| 199 |
+
>
|
| 200 |
+
· built on
|
| 201 |
+
<a
|
| 202 |
+
href="https://github.com/hexgrad/kokoro"
|
| 203 |
+
target="_blank"
|
| 204 |
+
rel="noopener"
|
| 205 |
+
>Kokoro-82M</a
|
| 206 |
+
>
|
| 207 |
+
via
|
| 208 |
+
<a
|
| 209 |
+
href="https://github.com/semidark/kokoro-deutsch"
|
| 210 |
+
target="_blank"
|
| 211 |
+
rel="noopener"
|
| 212 |
+
>kokoro-deutsch</a
|
| 213 |
+
>
|
| 214 |
+
</p>
|
| 215 |
+
</footer>
|
| 216 |
+
</main>
|
| 217 |
+
|
| 218 |
+
<div class="warli-strip warli-strip--bottom" aria-hidden="true">
|
| 219 |
+
<img src="/warli-strip.svg" alt="" />
|
| 220 |
+
</div>
|
| 221 |
+
|
| 222 |
+
<script type="module" src="/src/main.ts"></script>
|
| 223 |
+
</body>
|
| 224 |
+
</html>
|
package-lock.json
ADDED
|
@@ -0,0 +1,2006 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "bol-tts-marathi-webgpu-demo",
|
| 3 |
+
"version": "0.1.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "bol-tts-marathi-webgpu-demo",
|
| 9 |
+
"version": "0.1.0",
|
| 10 |
+
"license": "Apache-2.0",
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"@huggingface/transformers": "4.2.0",
|
| 13 |
+
"@indic-transliteration/sanscript": "^1.3.3"
|
| 14 |
+
},
|
| 15 |
+
"devDependencies": {
|
| 16 |
+
"typescript": "^5.6.3",
|
| 17 |
+
"vite": "^5.4.11"
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
"node_modules/@emnapi/runtime": {
|
| 21 |
+
"version": "1.10.0",
|
| 22 |
+
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
| 23 |
+
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
| 24 |
+
"license": "MIT",
|
| 25 |
+
"optional": true,
|
| 26 |
+
"dependencies": {
|
| 27 |
+
"tslib": "^2.4.0"
|
| 28 |
+
}
|
| 29 |
+
},
|
| 30 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 31 |
+
"version": "0.21.5",
|
| 32 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
| 33 |
+
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
| 34 |
+
"cpu": [
|
| 35 |
+
"ppc64"
|
| 36 |
+
],
|
| 37 |
+
"dev": true,
|
| 38 |
+
"license": "MIT",
|
| 39 |
+
"optional": true,
|
| 40 |
+
"os": [
|
| 41 |
+
"aix"
|
| 42 |
+
],
|
| 43 |
+
"engines": {
|
| 44 |
+
"node": ">=12"
|
| 45 |
+
}
|
| 46 |
+
},
|
| 47 |
+
"node_modules/@esbuild/android-arm": {
|
| 48 |
+
"version": "0.21.5",
|
| 49 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
| 50 |
+
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
| 51 |
+
"cpu": [
|
| 52 |
+
"arm"
|
| 53 |
+
],
|
| 54 |
+
"dev": true,
|
| 55 |
+
"license": "MIT",
|
| 56 |
+
"optional": true,
|
| 57 |
+
"os": [
|
| 58 |
+
"android"
|
| 59 |
+
],
|
| 60 |
+
"engines": {
|
| 61 |
+
"node": ">=12"
|
| 62 |
+
}
|
| 63 |
+
},
|
| 64 |
+
"node_modules/@esbuild/android-arm64": {
|
| 65 |
+
"version": "0.21.5",
|
| 66 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
| 67 |
+
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
| 68 |
+
"cpu": [
|
| 69 |
+
"arm64"
|
| 70 |
+
],
|
| 71 |
+
"dev": true,
|
| 72 |
+
"license": "MIT",
|
| 73 |
+
"optional": true,
|
| 74 |
+
"os": [
|
| 75 |
+
"android"
|
| 76 |
+
],
|
| 77 |
+
"engines": {
|
| 78 |
+
"node": ">=12"
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
"node_modules/@esbuild/android-x64": {
|
| 82 |
+
"version": "0.21.5",
|
| 83 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
| 84 |
+
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
| 85 |
+
"cpu": [
|
| 86 |
+
"x64"
|
| 87 |
+
],
|
| 88 |
+
"dev": true,
|
| 89 |
+
"license": "MIT",
|
| 90 |
+
"optional": true,
|
| 91 |
+
"os": [
|
| 92 |
+
"android"
|
| 93 |
+
],
|
| 94 |
+
"engines": {
|
| 95 |
+
"node": ">=12"
|
| 96 |
+
}
|
| 97 |
+
},
|
| 98 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 99 |
+
"version": "0.21.5",
|
| 100 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
| 101 |
+
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
| 102 |
+
"cpu": [
|
| 103 |
+
"arm64"
|
| 104 |
+
],
|
| 105 |
+
"dev": true,
|
| 106 |
+
"license": "MIT",
|
| 107 |
+
"optional": true,
|
| 108 |
+
"os": [
|
| 109 |
+
"darwin"
|
| 110 |
+
],
|
| 111 |
+
"engines": {
|
| 112 |
+
"node": ">=12"
|
| 113 |
+
}
|
| 114 |
+
},
|
| 115 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 116 |
+
"version": "0.21.5",
|
| 117 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
| 118 |
+
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
| 119 |
+
"cpu": [
|
| 120 |
+
"x64"
|
| 121 |
+
],
|
| 122 |
+
"dev": true,
|
| 123 |
+
"license": "MIT",
|
| 124 |
+
"optional": true,
|
| 125 |
+
"os": [
|
| 126 |
+
"darwin"
|
| 127 |
+
],
|
| 128 |
+
"engines": {
|
| 129 |
+
"node": ">=12"
|
| 130 |
+
}
|
| 131 |
+
},
|
| 132 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 133 |
+
"version": "0.21.5",
|
| 134 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
| 135 |
+
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
| 136 |
+
"cpu": [
|
| 137 |
+
"arm64"
|
| 138 |
+
],
|
| 139 |
+
"dev": true,
|
| 140 |
+
"license": "MIT",
|
| 141 |
+
"optional": true,
|
| 142 |
+
"os": [
|
| 143 |
+
"freebsd"
|
| 144 |
+
],
|
| 145 |
+
"engines": {
|
| 146 |
+
"node": ">=12"
|
| 147 |
+
}
|
| 148 |
+
},
|
| 149 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 150 |
+
"version": "0.21.5",
|
| 151 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
| 152 |
+
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
| 153 |
+
"cpu": [
|
| 154 |
+
"x64"
|
| 155 |
+
],
|
| 156 |
+
"dev": true,
|
| 157 |
+
"license": "MIT",
|
| 158 |
+
"optional": true,
|
| 159 |
+
"os": [
|
| 160 |
+
"freebsd"
|
| 161 |
+
],
|
| 162 |
+
"engines": {
|
| 163 |
+
"node": ">=12"
|
| 164 |
+
}
|
| 165 |
+
},
|
| 166 |
+
"node_modules/@esbuild/linux-arm": {
|
| 167 |
+
"version": "0.21.5",
|
| 168 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
| 169 |
+
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
| 170 |
+
"cpu": [
|
| 171 |
+
"arm"
|
| 172 |
+
],
|
| 173 |
+
"dev": true,
|
| 174 |
+
"license": "MIT",
|
| 175 |
+
"optional": true,
|
| 176 |
+
"os": [
|
| 177 |
+
"linux"
|
| 178 |
+
],
|
| 179 |
+
"engines": {
|
| 180 |
+
"node": ">=12"
|
| 181 |
+
}
|
| 182 |
+
},
|
| 183 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 184 |
+
"version": "0.21.5",
|
| 185 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
| 186 |
+
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
| 187 |
+
"cpu": [
|
| 188 |
+
"arm64"
|
| 189 |
+
],
|
| 190 |
+
"dev": true,
|
| 191 |
+
"license": "MIT",
|
| 192 |
+
"optional": true,
|
| 193 |
+
"os": [
|
| 194 |
+
"linux"
|
| 195 |
+
],
|
| 196 |
+
"engines": {
|
| 197 |
+
"node": ">=12"
|
| 198 |
+
}
|
| 199 |
+
},
|
| 200 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 201 |
+
"version": "0.21.5",
|
| 202 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
| 203 |
+
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
| 204 |
+
"cpu": [
|
| 205 |
+
"ia32"
|
| 206 |
+
],
|
| 207 |
+
"dev": true,
|
| 208 |
+
"license": "MIT",
|
| 209 |
+
"optional": true,
|
| 210 |
+
"os": [
|
| 211 |
+
"linux"
|
| 212 |
+
],
|
| 213 |
+
"engines": {
|
| 214 |
+
"node": ">=12"
|
| 215 |
+
}
|
| 216 |
+
},
|
| 217 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 218 |
+
"version": "0.21.5",
|
| 219 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
| 220 |
+
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
| 221 |
+
"cpu": [
|
| 222 |
+
"loong64"
|
| 223 |
+
],
|
| 224 |
+
"dev": true,
|
| 225 |
+
"license": "MIT",
|
| 226 |
+
"optional": true,
|
| 227 |
+
"os": [
|
| 228 |
+
"linux"
|
| 229 |
+
],
|
| 230 |
+
"engines": {
|
| 231 |
+
"node": ">=12"
|
| 232 |
+
}
|
| 233 |
+
},
|
| 234 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 235 |
+
"version": "0.21.5",
|
| 236 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
| 237 |
+
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
| 238 |
+
"cpu": [
|
| 239 |
+
"mips64el"
|
| 240 |
+
],
|
| 241 |
+
"dev": true,
|
| 242 |
+
"license": "MIT",
|
| 243 |
+
"optional": true,
|
| 244 |
+
"os": [
|
| 245 |
+
"linux"
|
| 246 |
+
],
|
| 247 |
+
"engines": {
|
| 248 |
+
"node": ">=12"
|
| 249 |
+
}
|
| 250 |
+
},
|
| 251 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 252 |
+
"version": "0.21.5",
|
| 253 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
| 254 |
+
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
| 255 |
+
"cpu": [
|
| 256 |
+
"ppc64"
|
| 257 |
+
],
|
| 258 |
+
"dev": true,
|
| 259 |
+
"license": "MIT",
|
| 260 |
+
"optional": true,
|
| 261 |
+
"os": [
|
| 262 |
+
"linux"
|
| 263 |
+
],
|
| 264 |
+
"engines": {
|
| 265 |
+
"node": ">=12"
|
| 266 |
+
}
|
| 267 |
+
},
|
| 268 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 269 |
+
"version": "0.21.5",
|
| 270 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
| 271 |
+
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
| 272 |
+
"cpu": [
|
| 273 |
+
"riscv64"
|
| 274 |
+
],
|
| 275 |
+
"dev": true,
|
| 276 |
+
"license": "MIT",
|
| 277 |
+
"optional": true,
|
| 278 |
+
"os": [
|
| 279 |
+
"linux"
|
| 280 |
+
],
|
| 281 |
+
"engines": {
|
| 282 |
+
"node": ">=12"
|
| 283 |
+
}
|
| 284 |
+
},
|
| 285 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 286 |
+
"version": "0.21.5",
|
| 287 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
| 288 |
+
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
| 289 |
+
"cpu": [
|
| 290 |
+
"s390x"
|
| 291 |
+
],
|
| 292 |
+
"dev": true,
|
| 293 |
+
"license": "MIT",
|
| 294 |
+
"optional": true,
|
| 295 |
+
"os": [
|
| 296 |
+
"linux"
|
| 297 |
+
],
|
| 298 |
+
"engines": {
|
| 299 |
+
"node": ">=12"
|
| 300 |
+
}
|
| 301 |
+
},
|
| 302 |
+
"node_modules/@esbuild/linux-x64": {
|
| 303 |
+
"version": "0.21.5",
|
| 304 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
| 305 |
+
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
| 306 |
+
"cpu": [
|
| 307 |
+
"x64"
|
| 308 |
+
],
|
| 309 |
+
"dev": true,
|
| 310 |
+
"license": "MIT",
|
| 311 |
+
"optional": true,
|
| 312 |
+
"os": [
|
| 313 |
+
"linux"
|
| 314 |
+
],
|
| 315 |
+
"engines": {
|
| 316 |
+
"node": ">=12"
|
| 317 |
+
}
|
| 318 |
+
},
|
| 319 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 320 |
+
"version": "0.21.5",
|
| 321 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
| 322 |
+
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
| 323 |
+
"cpu": [
|
| 324 |
+
"x64"
|
| 325 |
+
],
|
| 326 |
+
"dev": true,
|
| 327 |
+
"license": "MIT",
|
| 328 |
+
"optional": true,
|
| 329 |
+
"os": [
|
| 330 |
+
"netbsd"
|
| 331 |
+
],
|
| 332 |
+
"engines": {
|
| 333 |
+
"node": ">=12"
|
| 334 |
+
}
|
| 335 |
+
},
|
| 336 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 337 |
+
"version": "0.21.5",
|
| 338 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
| 339 |
+
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
| 340 |
+
"cpu": [
|
| 341 |
+
"x64"
|
| 342 |
+
],
|
| 343 |
+
"dev": true,
|
| 344 |
+
"license": "MIT",
|
| 345 |
+
"optional": true,
|
| 346 |
+
"os": [
|
| 347 |
+
"openbsd"
|
| 348 |
+
],
|
| 349 |
+
"engines": {
|
| 350 |
+
"node": ">=12"
|
| 351 |
+
}
|
| 352 |
+
},
|
| 353 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 354 |
+
"version": "0.21.5",
|
| 355 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
| 356 |
+
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
| 357 |
+
"cpu": [
|
| 358 |
+
"x64"
|
| 359 |
+
],
|
| 360 |
+
"dev": true,
|
| 361 |
+
"license": "MIT",
|
| 362 |
+
"optional": true,
|
| 363 |
+
"os": [
|
| 364 |
+
"sunos"
|
| 365 |
+
],
|
| 366 |
+
"engines": {
|
| 367 |
+
"node": ">=12"
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 371 |
+
"version": "0.21.5",
|
| 372 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
| 373 |
+
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
| 374 |
+
"cpu": [
|
| 375 |
+
"arm64"
|
| 376 |
+
],
|
| 377 |
+
"dev": true,
|
| 378 |
+
"license": "MIT",
|
| 379 |
+
"optional": true,
|
| 380 |
+
"os": [
|
| 381 |
+
"win32"
|
| 382 |
+
],
|
| 383 |
+
"engines": {
|
| 384 |
+
"node": ">=12"
|
| 385 |
+
}
|
| 386 |
+
},
|
| 387 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 388 |
+
"version": "0.21.5",
|
| 389 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
| 390 |
+
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
| 391 |
+
"cpu": [
|
| 392 |
+
"ia32"
|
| 393 |
+
],
|
| 394 |
+
"dev": true,
|
| 395 |
+
"license": "MIT",
|
| 396 |
+
"optional": true,
|
| 397 |
+
"os": [
|
| 398 |
+
"win32"
|
| 399 |
+
],
|
| 400 |
+
"engines": {
|
| 401 |
+
"node": ">=12"
|
| 402 |
+
}
|
| 403 |
+
},
|
| 404 |
+
"node_modules/@esbuild/win32-x64": {
|
| 405 |
+
"version": "0.21.5",
|
| 406 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
| 407 |
+
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
| 408 |
+
"cpu": [
|
| 409 |
+
"x64"
|
| 410 |
+
],
|
| 411 |
+
"dev": true,
|
| 412 |
+
"license": "MIT",
|
| 413 |
+
"optional": true,
|
| 414 |
+
"os": [
|
| 415 |
+
"win32"
|
| 416 |
+
],
|
| 417 |
+
"engines": {
|
| 418 |
+
"node": ">=12"
|
| 419 |
+
}
|
| 420 |
+
},
|
| 421 |
+
"node_modules/@huggingface/jinja": {
|
| 422 |
+
"version": "0.5.8",
|
| 423 |
+
"resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.8.tgz",
|
| 424 |
+
"integrity": "sha512-ZdElB7DPS7QQS8ZnFc5RPPtkg+eN11z8AmIZWAyes6pSbwXqiFB/POVevvm01begdSX1ho9Gxln/F6qlQMsuaA==",
|
| 425 |
+
"license": "MIT",
|
| 426 |
+
"engines": {
|
| 427 |
+
"node": ">=18"
|
| 428 |
+
}
|
| 429 |
+
},
|
| 430 |
+
"node_modules/@huggingface/tokenizers": {
|
| 431 |
+
"version": "0.1.3",
|
| 432 |
+
"resolved": "https://registry.npmjs.org/@huggingface/tokenizers/-/tokenizers-0.1.3.tgz",
|
| 433 |
+
"integrity": "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA==",
|
| 434 |
+
"license": "Apache-2.0"
|
| 435 |
+
},
|
| 436 |
+
"node_modules/@huggingface/transformers": {
|
| 437 |
+
"version": "4.2.0",
|
| 438 |
+
"resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-4.2.0.tgz",
|
| 439 |
+
"integrity": "sha512-8BRCoBMH0XsWaEIamuR0LrJGAfftgHAfb2Vrffy0VKlSAE/MnUJ5/h/zTfEP3fDIft+nk7TqB8xXEyABGitBjQ==",
|
| 440 |
+
"license": "Apache-2.0",
|
| 441 |
+
"dependencies": {
|
| 442 |
+
"@huggingface/jinja": "^0.5.6",
|
| 443 |
+
"@huggingface/tokenizers": "^0.1.3",
|
| 444 |
+
"onnxruntime-node": "1.24.3",
|
| 445 |
+
"onnxruntime-web": "1.26.0-dev.20260416-b7804b056c",
|
| 446 |
+
"sharp": "^0.34.5"
|
| 447 |
+
}
|
| 448 |
+
},
|
| 449 |
+
"node_modules/@img/colour": {
|
| 450 |
+
"version": "1.1.0",
|
| 451 |
+
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
| 452 |
+
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
| 453 |
+
"license": "MIT",
|
| 454 |
+
"engines": {
|
| 455 |
+
"node": ">=18"
|
| 456 |
+
}
|
| 457 |
+
},
|
| 458 |
+
"node_modules/@img/sharp-darwin-arm64": {
|
| 459 |
+
"version": "0.34.5",
|
| 460 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
| 461 |
+
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
| 462 |
+
"cpu": [
|
| 463 |
+
"arm64"
|
| 464 |
+
],
|
| 465 |
+
"license": "Apache-2.0",
|
| 466 |
+
"optional": true,
|
| 467 |
+
"os": [
|
| 468 |
+
"darwin"
|
| 469 |
+
],
|
| 470 |
+
"engines": {
|
| 471 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 472 |
+
},
|
| 473 |
+
"funding": {
|
| 474 |
+
"url": "https://opencollective.com/libvips"
|
| 475 |
+
},
|
| 476 |
+
"optionalDependencies": {
|
| 477 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
| 478 |
+
}
|
| 479 |
+
},
|
| 480 |
+
"node_modules/@img/sharp-darwin-x64": {
|
| 481 |
+
"version": "0.34.5",
|
| 482 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
| 483 |
+
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
| 484 |
+
"cpu": [
|
| 485 |
+
"x64"
|
| 486 |
+
],
|
| 487 |
+
"license": "Apache-2.0",
|
| 488 |
+
"optional": true,
|
| 489 |
+
"os": [
|
| 490 |
+
"darwin"
|
| 491 |
+
],
|
| 492 |
+
"engines": {
|
| 493 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 494 |
+
},
|
| 495 |
+
"funding": {
|
| 496 |
+
"url": "https://opencollective.com/libvips"
|
| 497 |
+
},
|
| 498 |
+
"optionalDependencies": {
|
| 499 |
+
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
| 500 |
+
}
|
| 501 |
+
},
|
| 502 |
+
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
| 503 |
+
"version": "1.2.4",
|
| 504 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
| 505 |
+
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
| 506 |
+
"cpu": [
|
| 507 |
+
"arm64"
|
| 508 |
+
],
|
| 509 |
+
"license": "LGPL-3.0-or-later",
|
| 510 |
+
"optional": true,
|
| 511 |
+
"os": [
|
| 512 |
+
"darwin"
|
| 513 |
+
],
|
| 514 |
+
"funding": {
|
| 515 |
+
"url": "https://opencollective.com/libvips"
|
| 516 |
+
}
|
| 517 |
+
},
|
| 518 |
+
"node_modules/@img/sharp-libvips-darwin-x64": {
|
| 519 |
+
"version": "1.2.4",
|
| 520 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
| 521 |
+
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
| 522 |
+
"cpu": [
|
| 523 |
+
"x64"
|
| 524 |
+
],
|
| 525 |
+
"license": "LGPL-3.0-or-later",
|
| 526 |
+
"optional": true,
|
| 527 |
+
"os": [
|
| 528 |
+
"darwin"
|
| 529 |
+
],
|
| 530 |
+
"funding": {
|
| 531 |
+
"url": "https://opencollective.com/libvips"
|
| 532 |
+
}
|
| 533 |
+
},
|
| 534 |
+
"node_modules/@img/sharp-libvips-linux-arm": {
|
| 535 |
+
"version": "1.2.4",
|
| 536 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
| 537 |
+
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
| 538 |
+
"cpu": [
|
| 539 |
+
"arm"
|
| 540 |
+
],
|
| 541 |
+
"license": "LGPL-3.0-or-later",
|
| 542 |
+
"optional": true,
|
| 543 |
+
"os": [
|
| 544 |
+
"linux"
|
| 545 |
+
],
|
| 546 |
+
"funding": {
|
| 547 |
+
"url": "https://opencollective.com/libvips"
|
| 548 |
+
}
|
| 549 |
+
},
|
| 550 |
+
"node_modules/@img/sharp-libvips-linux-arm64": {
|
| 551 |
+
"version": "1.2.4",
|
| 552 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
| 553 |
+
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
| 554 |
+
"cpu": [
|
| 555 |
+
"arm64"
|
| 556 |
+
],
|
| 557 |
+
"license": "LGPL-3.0-or-later",
|
| 558 |
+
"optional": true,
|
| 559 |
+
"os": [
|
| 560 |
+
"linux"
|
| 561 |
+
],
|
| 562 |
+
"funding": {
|
| 563 |
+
"url": "https://opencollective.com/libvips"
|
| 564 |
+
}
|
| 565 |
+
},
|
| 566 |
+
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
| 567 |
+
"version": "1.2.4",
|
| 568 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
| 569 |
+
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
| 570 |
+
"cpu": [
|
| 571 |
+
"ppc64"
|
| 572 |
+
],
|
| 573 |
+
"license": "LGPL-3.0-or-later",
|
| 574 |
+
"optional": true,
|
| 575 |
+
"os": [
|
| 576 |
+
"linux"
|
| 577 |
+
],
|
| 578 |
+
"funding": {
|
| 579 |
+
"url": "https://opencollective.com/libvips"
|
| 580 |
+
}
|
| 581 |
+
},
|
| 582 |
+
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
| 583 |
+
"version": "1.2.4",
|
| 584 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
| 585 |
+
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
| 586 |
+
"cpu": [
|
| 587 |
+
"riscv64"
|
| 588 |
+
],
|
| 589 |
+
"license": "LGPL-3.0-or-later",
|
| 590 |
+
"optional": true,
|
| 591 |
+
"os": [
|
| 592 |
+
"linux"
|
| 593 |
+
],
|
| 594 |
+
"funding": {
|
| 595 |
+
"url": "https://opencollective.com/libvips"
|
| 596 |
+
}
|
| 597 |
+
},
|
| 598 |
+
"node_modules/@img/sharp-libvips-linux-s390x": {
|
| 599 |
+
"version": "1.2.4",
|
| 600 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
| 601 |
+
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
| 602 |
+
"cpu": [
|
| 603 |
+
"s390x"
|
| 604 |
+
],
|
| 605 |
+
"license": "LGPL-3.0-or-later",
|
| 606 |
+
"optional": true,
|
| 607 |
+
"os": [
|
| 608 |
+
"linux"
|
| 609 |
+
],
|
| 610 |
+
"funding": {
|
| 611 |
+
"url": "https://opencollective.com/libvips"
|
| 612 |
+
}
|
| 613 |
+
},
|
| 614 |
+
"node_modules/@img/sharp-libvips-linux-x64": {
|
| 615 |
+
"version": "1.2.4",
|
| 616 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
| 617 |
+
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
| 618 |
+
"cpu": [
|
| 619 |
+
"x64"
|
| 620 |
+
],
|
| 621 |
+
"license": "LGPL-3.0-or-later",
|
| 622 |
+
"optional": true,
|
| 623 |
+
"os": [
|
| 624 |
+
"linux"
|
| 625 |
+
],
|
| 626 |
+
"funding": {
|
| 627 |
+
"url": "https://opencollective.com/libvips"
|
| 628 |
+
}
|
| 629 |
+
},
|
| 630 |
+
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
| 631 |
+
"version": "1.2.4",
|
| 632 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
| 633 |
+
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
| 634 |
+
"cpu": [
|
| 635 |
+
"arm64"
|
| 636 |
+
],
|
| 637 |
+
"license": "LGPL-3.0-or-later",
|
| 638 |
+
"optional": true,
|
| 639 |
+
"os": [
|
| 640 |
+
"linux"
|
| 641 |
+
],
|
| 642 |
+
"funding": {
|
| 643 |
+
"url": "https://opencollective.com/libvips"
|
| 644 |
+
}
|
| 645 |
+
},
|
| 646 |
+
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
| 647 |
+
"version": "1.2.4",
|
| 648 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
| 649 |
+
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
| 650 |
+
"cpu": [
|
| 651 |
+
"x64"
|
| 652 |
+
],
|
| 653 |
+
"license": "LGPL-3.0-or-later",
|
| 654 |
+
"optional": true,
|
| 655 |
+
"os": [
|
| 656 |
+
"linux"
|
| 657 |
+
],
|
| 658 |
+
"funding": {
|
| 659 |
+
"url": "https://opencollective.com/libvips"
|
| 660 |
+
}
|
| 661 |
+
},
|
| 662 |
+
"node_modules/@img/sharp-linux-arm": {
|
| 663 |
+
"version": "0.34.5",
|
| 664 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
| 665 |
+
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
| 666 |
+
"cpu": [
|
| 667 |
+
"arm"
|
| 668 |
+
],
|
| 669 |
+
"license": "Apache-2.0",
|
| 670 |
+
"optional": true,
|
| 671 |
+
"os": [
|
| 672 |
+
"linux"
|
| 673 |
+
],
|
| 674 |
+
"engines": {
|
| 675 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 676 |
+
},
|
| 677 |
+
"funding": {
|
| 678 |
+
"url": "https://opencollective.com/libvips"
|
| 679 |
+
},
|
| 680 |
+
"optionalDependencies": {
|
| 681 |
+
"@img/sharp-libvips-linux-arm": "1.2.4"
|
| 682 |
+
}
|
| 683 |
+
},
|
| 684 |
+
"node_modules/@img/sharp-linux-arm64": {
|
| 685 |
+
"version": "0.34.5",
|
| 686 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
| 687 |
+
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
| 688 |
+
"cpu": [
|
| 689 |
+
"arm64"
|
| 690 |
+
],
|
| 691 |
+
"license": "Apache-2.0",
|
| 692 |
+
"optional": true,
|
| 693 |
+
"os": [
|
| 694 |
+
"linux"
|
| 695 |
+
],
|
| 696 |
+
"engines": {
|
| 697 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 698 |
+
},
|
| 699 |
+
"funding": {
|
| 700 |
+
"url": "https://opencollective.com/libvips"
|
| 701 |
+
},
|
| 702 |
+
"optionalDependencies": {
|
| 703 |
+
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
| 704 |
+
}
|
| 705 |
+
},
|
| 706 |
+
"node_modules/@img/sharp-linux-ppc64": {
|
| 707 |
+
"version": "0.34.5",
|
| 708 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
| 709 |
+
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
| 710 |
+
"cpu": [
|
| 711 |
+
"ppc64"
|
| 712 |
+
],
|
| 713 |
+
"license": "Apache-2.0",
|
| 714 |
+
"optional": true,
|
| 715 |
+
"os": [
|
| 716 |
+
"linux"
|
| 717 |
+
],
|
| 718 |
+
"engines": {
|
| 719 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 720 |
+
},
|
| 721 |
+
"funding": {
|
| 722 |
+
"url": "https://opencollective.com/libvips"
|
| 723 |
+
},
|
| 724 |
+
"optionalDependencies": {
|
| 725 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
| 726 |
+
}
|
| 727 |
+
},
|
| 728 |
+
"node_modules/@img/sharp-linux-riscv64": {
|
| 729 |
+
"version": "0.34.5",
|
| 730 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
| 731 |
+
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
| 732 |
+
"cpu": [
|
| 733 |
+
"riscv64"
|
| 734 |
+
],
|
| 735 |
+
"license": "Apache-2.0",
|
| 736 |
+
"optional": true,
|
| 737 |
+
"os": [
|
| 738 |
+
"linux"
|
| 739 |
+
],
|
| 740 |
+
"engines": {
|
| 741 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 742 |
+
},
|
| 743 |
+
"funding": {
|
| 744 |
+
"url": "https://opencollective.com/libvips"
|
| 745 |
+
},
|
| 746 |
+
"optionalDependencies": {
|
| 747 |
+
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
| 748 |
+
}
|
| 749 |
+
},
|
| 750 |
+
"node_modules/@img/sharp-linux-s390x": {
|
| 751 |
+
"version": "0.34.5",
|
| 752 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
| 753 |
+
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
| 754 |
+
"cpu": [
|
| 755 |
+
"s390x"
|
| 756 |
+
],
|
| 757 |
+
"license": "Apache-2.0",
|
| 758 |
+
"optional": true,
|
| 759 |
+
"os": [
|
| 760 |
+
"linux"
|
| 761 |
+
],
|
| 762 |
+
"engines": {
|
| 763 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 764 |
+
},
|
| 765 |
+
"funding": {
|
| 766 |
+
"url": "https://opencollective.com/libvips"
|
| 767 |
+
},
|
| 768 |
+
"optionalDependencies": {
|
| 769 |
+
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
| 770 |
+
}
|
| 771 |
+
},
|
| 772 |
+
"node_modules/@img/sharp-linux-x64": {
|
| 773 |
+
"version": "0.34.5",
|
| 774 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
| 775 |
+
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
| 776 |
+
"cpu": [
|
| 777 |
+
"x64"
|
| 778 |
+
],
|
| 779 |
+
"license": "Apache-2.0",
|
| 780 |
+
"optional": true,
|
| 781 |
+
"os": [
|
| 782 |
+
"linux"
|
| 783 |
+
],
|
| 784 |
+
"engines": {
|
| 785 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 786 |
+
},
|
| 787 |
+
"funding": {
|
| 788 |
+
"url": "https://opencollective.com/libvips"
|
| 789 |
+
},
|
| 790 |
+
"optionalDependencies": {
|
| 791 |
+
"@img/sharp-libvips-linux-x64": "1.2.4"
|
| 792 |
+
}
|
| 793 |
+
},
|
| 794 |
+
"node_modules/@img/sharp-linuxmusl-arm64": {
|
| 795 |
+
"version": "0.34.5",
|
| 796 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
| 797 |
+
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
| 798 |
+
"cpu": [
|
| 799 |
+
"arm64"
|
| 800 |
+
],
|
| 801 |
+
"license": "Apache-2.0",
|
| 802 |
+
"optional": true,
|
| 803 |
+
"os": [
|
| 804 |
+
"linux"
|
| 805 |
+
],
|
| 806 |
+
"engines": {
|
| 807 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 808 |
+
},
|
| 809 |
+
"funding": {
|
| 810 |
+
"url": "https://opencollective.com/libvips"
|
| 811 |
+
},
|
| 812 |
+
"optionalDependencies": {
|
| 813 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
| 814 |
+
}
|
| 815 |
+
},
|
| 816 |
+
"node_modules/@img/sharp-linuxmusl-x64": {
|
| 817 |
+
"version": "0.34.5",
|
| 818 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
| 819 |
+
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
| 820 |
+
"cpu": [
|
| 821 |
+
"x64"
|
| 822 |
+
],
|
| 823 |
+
"license": "Apache-2.0",
|
| 824 |
+
"optional": true,
|
| 825 |
+
"os": [
|
| 826 |
+
"linux"
|
| 827 |
+
],
|
| 828 |
+
"engines": {
|
| 829 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 830 |
+
},
|
| 831 |
+
"funding": {
|
| 832 |
+
"url": "https://opencollective.com/libvips"
|
| 833 |
+
},
|
| 834 |
+
"optionalDependencies": {
|
| 835 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
| 836 |
+
}
|
| 837 |
+
},
|
| 838 |
+
"node_modules/@img/sharp-wasm32": {
|
| 839 |
+
"version": "0.34.5",
|
| 840 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
| 841 |
+
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
| 842 |
+
"cpu": [
|
| 843 |
+
"wasm32"
|
| 844 |
+
],
|
| 845 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
| 846 |
+
"optional": true,
|
| 847 |
+
"dependencies": {
|
| 848 |
+
"@emnapi/runtime": "^1.7.0"
|
| 849 |
+
},
|
| 850 |
+
"engines": {
|
| 851 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 852 |
+
},
|
| 853 |
+
"funding": {
|
| 854 |
+
"url": "https://opencollective.com/libvips"
|
| 855 |
+
}
|
| 856 |
+
},
|
| 857 |
+
"node_modules/@img/sharp-win32-arm64": {
|
| 858 |
+
"version": "0.34.5",
|
| 859 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
| 860 |
+
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
| 861 |
+
"cpu": [
|
| 862 |
+
"arm64"
|
| 863 |
+
],
|
| 864 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 865 |
+
"optional": true,
|
| 866 |
+
"os": [
|
| 867 |
+
"win32"
|
| 868 |
+
],
|
| 869 |
+
"engines": {
|
| 870 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 871 |
+
},
|
| 872 |
+
"funding": {
|
| 873 |
+
"url": "https://opencollective.com/libvips"
|
| 874 |
+
}
|
| 875 |
+
},
|
| 876 |
+
"node_modules/@img/sharp-win32-ia32": {
|
| 877 |
+
"version": "0.34.5",
|
| 878 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
| 879 |
+
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
| 880 |
+
"cpu": [
|
| 881 |
+
"ia32"
|
| 882 |
+
],
|
| 883 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 884 |
+
"optional": true,
|
| 885 |
+
"os": [
|
| 886 |
+
"win32"
|
| 887 |
+
],
|
| 888 |
+
"engines": {
|
| 889 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 890 |
+
},
|
| 891 |
+
"funding": {
|
| 892 |
+
"url": "https://opencollective.com/libvips"
|
| 893 |
+
}
|
| 894 |
+
},
|
| 895 |
+
"node_modules/@img/sharp-win32-x64": {
|
| 896 |
+
"version": "0.34.5",
|
| 897 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
| 898 |
+
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
| 899 |
+
"cpu": [
|
| 900 |
+
"x64"
|
| 901 |
+
],
|
| 902 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 903 |
+
"optional": true,
|
| 904 |
+
"os": [
|
| 905 |
+
"win32"
|
| 906 |
+
],
|
| 907 |
+
"engines": {
|
| 908 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 909 |
+
},
|
| 910 |
+
"funding": {
|
| 911 |
+
"url": "https://opencollective.com/libvips"
|
| 912 |
+
}
|
| 913 |
+
},
|
| 914 |
+
"node_modules/@indic-transliteration/common_maps": {
|
| 915 |
+
"version": "1.0.6",
|
| 916 |
+
"resolved": "https://registry.npmjs.org/@indic-transliteration/common_maps/-/common_maps-1.0.6.tgz",
|
| 917 |
+
"integrity": "sha512-OYX+SrpK26FjVZNphOdsQc9Ph0yFDtoWsU7INjhEhhWvGqzxF3Mqi0fF0buUsB4jpRWJe+7U+u9PO4fsTP/lfg==",
|
| 918 |
+
"license": "MIT"
|
| 919 |
+
},
|
| 920 |
+
"node_modules/@indic-transliteration/sanscript": {
|
| 921 |
+
"version": "1.3.3",
|
| 922 |
+
"resolved": "https://registry.npmjs.org/@indic-transliteration/sanscript/-/sanscript-1.3.3.tgz",
|
| 923 |
+
"integrity": "sha512-zNGeARmQTPIlubwgEhl/JumpwTPHrdT/cNsQeCL+G67SQmjJe3qRnMIYghXiVt7+KDso/pU1Ky2ZfD/RBISfJQ==",
|
| 924 |
+
"license": "MIT",
|
| 925 |
+
"dependencies": {
|
| 926 |
+
"@indic-transliteration/common_maps": "^1.0.2",
|
| 927 |
+
"toml": "^2.3.6"
|
| 928 |
+
}
|
| 929 |
+
},
|
| 930 |
+
"node_modules/@protobufjs/aspromise": {
|
| 931 |
+
"version": "1.1.2",
|
| 932 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
| 933 |
+
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
| 934 |
+
"license": "BSD-3-Clause"
|
| 935 |
+
},
|
| 936 |
+
"node_modules/@protobufjs/base64": {
|
| 937 |
+
"version": "1.1.2",
|
| 938 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
| 939 |
+
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
| 940 |
+
"license": "BSD-3-Clause"
|
| 941 |
+
},
|
| 942 |
+
"node_modules/@protobufjs/codegen": {
|
| 943 |
+
"version": "2.0.4",
|
| 944 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
| 945 |
+
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
| 946 |
+
"license": "BSD-3-Clause"
|
| 947 |
+
},
|
| 948 |
+
"node_modules/@protobufjs/eventemitter": {
|
| 949 |
+
"version": "1.1.0",
|
| 950 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
| 951 |
+
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
| 952 |
+
"license": "BSD-3-Clause"
|
| 953 |
+
},
|
| 954 |
+
"node_modules/@protobufjs/fetch": {
|
| 955 |
+
"version": "1.1.0",
|
| 956 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
| 957 |
+
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
| 958 |
+
"license": "BSD-3-Clause",
|
| 959 |
+
"dependencies": {
|
| 960 |
+
"@protobufjs/aspromise": "^1.1.1",
|
| 961 |
+
"@protobufjs/inquire": "^1.1.0"
|
| 962 |
+
}
|
| 963 |
+
},
|
| 964 |
+
"node_modules/@protobufjs/float": {
|
| 965 |
+
"version": "1.0.2",
|
| 966 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
| 967 |
+
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
| 968 |
+
"license": "BSD-3-Clause"
|
| 969 |
+
},
|
| 970 |
+
"node_modules/@protobufjs/inquire": {
|
| 971 |
+
"version": "1.1.0",
|
| 972 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
| 973 |
+
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
| 974 |
+
"license": "BSD-3-Clause"
|
| 975 |
+
},
|
| 976 |
+
"node_modules/@protobufjs/path": {
|
| 977 |
+
"version": "1.1.2",
|
| 978 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
| 979 |
+
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
| 980 |
+
"license": "BSD-3-Clause"
|
| 981 |
+
},
|
| 982 |
+
"node_modules/@protobufjs/pool": {
|
| 983 |
+
"version": "1.1.0",
|
| 984 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
| 985 |
+
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
| 986 |
+
"license": "BSD-3-Clause"
|
| 987 |
+
},
|
| 988 |
+
"node_modules/@protobufjs/utf8": {
|
| 989 |
+
"version": "1.1.0",
|
| 990 |
+
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
| 991 |
+
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
| 992 |
+
"license": "BSD-3-Clause"
|
| 993 |
+
},
|
| 994 |
+
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 995 |
+
"version": "4.60.2",
|
| 996 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz",
|
| 997 |
+
"integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==",
|
| 998 |
+
"cpu": [
|
| 999 |
+
"arm"
|
| 1000 |
+
],
|
| 1001 |
+
"dev": true,
|
| 1002 |
+
"license": "MIT",
|
| 1003 |
+
"optional": true,
|
| 1004 |
+
"os": [
|
| 1005 |
+
"android"
|
| 1006 |
+
]
|
| 1007 |
+
},
|
| 1008 |
+
"node_modules/@rollup/rollup-android-arm64": {
|
| 1009 |
+
"version": "4.60.2",
|
| 1010 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz",
|
| 1011 |
+
"integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==",
|
| 1012 |
+
"cpu": [
|
| 1013 |
+
"arm64"
|
| 1014 |
+
],
|
| 1015 |
+
"dev": true,
|
| 1016 |
+
"license": "MIT",
|
| 1017 |
+
"optional": true,
|
| 1018 |
+
"os": [
|
| 1019 |
+
"android"
|
| 1020 |
+
]
|
| 1021 |
+
},
|
| 1022 |
+
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 1023 |
+
"version": "4.60.2",
|
| 1024 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz",
|
| 1025 |
+
"integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==",
|
| 1026 |
+
"cpu": [
|
| 1027 |
+
"arm64"
|
| 1028 |
+
],
|
| 1029 |
+
"dev": true,
|
| 1030 |
+
"license": "MIT",
|
| 1031 |
+
"optional": true,
|
| 1032 |
+
"os": [
|
| 1033 |
+
"darwin"
|
| 1034 |
+
]
|
| 1035 |
+
},
|
| 1036 |
+
"node_modules/@rollup/rollup-darwin-x64": {
|
| 1037 |
+
"version": "4.60.2",
|
| 1038 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz",
|
| 1039 |
+
"integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==",
|
| 1040 |
+
"cpu": [
|
| 1041 |
+
"x64"
|
| 1042 |
+
],
|
| 1043 |
+
"dev": true,
|
| 1044 |
+
"license": "MIT",
|
| 1045 |
+
"optional": true,
|
| 1046 |
+
"os": [
|
| 1047 |
+
"darwin"
|
| 1048 |
+
]
|
| 1049 |
+
},
|
| 1050 |
+
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 1051 |
+
"version": "4.60.2",
|
| 1052 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz",
|
| 1053 |
+
"integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==",
|
| 1054 |
+
"cpu": [
|
| 1055 |
+
"arm64"
|
| 1056 |
+
],
|
| 1057 |
+
"dev": true,
|
| 1058 |
+
"license": "MIT",
|
| 1059 |
+
"optional": true,
|
| 1060 |
+
"os": [
|
| 1061 |
+
"freebsd"
|
| 1062 |
+
]
|
| 1063 |
+
},
|
| 1064 |
+
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 1065 |
+
"version": "4.60.2",
|
| 1066 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz",
|
| 1067 |
+
"integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==",
|
| 1068 |
+
"cpu": [
|
| 1069 |
+
"x64"
|
| 1070 |
+
],
|
| 1071 |
+
"dev": true,
|
| 1072 |
+
"license": "MIT",
|
| 1073 |
+
"optional": true,
|
| 1074 |
+
"os": [
|
| 1075 |
+
"freebsd"
|
| 1076 |
+
]
|
| 1077 |
+
},
|
| 1078 |
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 1079 |
+
"version": "4.60.2",
|
| 1080 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz",
|
| 1081 |
+
"integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==",
|
| 1082 |
+
"cpu": [
|
| 1083 |
+
"arm"
|
| 1084 |
+
],
|
| 1085 |
+
"dev": true,
|
| 1086 |
+
"license": "MIT",
|
| 1087 |
+
"optional": true,
|
| 1088 |
+
"os": [
|
| 1089 |
+
"linux"
|
| 1090 |
+
]
|
| 1091 |
+
},
|
| 1092 |
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 1093 |
+
"version": "4.60.2",
|
| 1094 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz",
|
| 1095 |
+
"integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==",
|
| 1096 |
+
"cpu": [
|
| 1097 |
+
"arm"
|
| 1098 |
+
],
|
| 1099 |
+
"dev": true,
|
| 1100 |
+
"license": "MIT",
|
| 1101 |
+
"optional": true,
|
| 1102 |
+
"os": [
|
| 1103 |
+
"linux"
|
| 1104 |
+
]
|
| 1105 |
+
},
|
| 1106 |
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 1107 |
+
"version": "4.60.2",
|
| 1108 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz",
|
| 1109 |
+
"integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==",
|
| 1110 |
+
"cpu": [
|
| 1111 |
+
"arm64"
|
| 1112 |
+
],
|
| 1113 |
+
"dev": true,
|
| 1114 |
+
"license": "MIT",
|
| 1115 |
+
"optional": true,
|
| 1116 |
+
"os": [
|
| 1117 |
+
"linux"
|
| 1118 |
+
]
|
| 1119 |
+
},
|
| 1120 |
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 1121 |
+
"version": "4.60.2",
|
| 1122 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz",
|
| 1123 |
+
"integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==",
|
| 1124 |
+
"cpu": [
|
| 1125 |
+
"arm64"
|
| 1126 |
+
],
|
| 1127 |
+
"dev": true,
|
| 1128 |
+
"license": "MIT",
|
| 1129 |
+
"optional": true,
|
| 1130 |
+
"os": [
|
| 1131 |
+
"linux"
|
| 1132 |
+
]
|
| 1133 |
+
},
|
| 1134 |
+
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 1135 |
+
"version": "4.60.2",
|
| 1136 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz",
|
| 1137 |
+
"integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==",
|
| 1138 |
+
"cpu": [
|
| 1139 |
+
"loong64"
|
| 1140 |
+
],
|
| 1141 |
+
"dev": true,
|
| 1142 |
+
"license": "MIT",
|
| 1143 |
+
"optional": true,
|
| 1144 |
+
"os": [
|
| 1145 |
+
"linux"
|
| 1146 |
+
]
|
| 1147 |
+
},
|
| 1148 |
+
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 1149 |
+
"version": "4.60.2",
|
| 1150 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz",
|
| 1151 |
+
"integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==",
|
| 1152 |
+
"cpu": [
|
| 1153 |
+
"loong64"
|
| 1154 |
+
],
|
| 1155 |
+
"dev": true,
|
| 1156 |
+
"license": "MIT",
|
| 1157 |
+
"optional": true,
|
| 1158 |
+
"os": [
|
| 1159 |
+
"linux"
|
| 1160 |
+
]
|
| 1161 |
+
},
|
| 1162 |
+
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 1163 |
+
"version": "4.60.2",
|
| 1164 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz",
|
| 1165 |
+
"integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==",
|
| 1166 |
+
"cpu": [
|
| 1167 |
+
"ppc64"
|
| 1168 |
+
],
|
| 1169 |
+
"dev": true,
|
| 1170 |
+
"license": "MIT",
|
| 1171 |
+
"optional": true,
|
| 1172 |
+
"os": [
|
| 1173 |
+
"linux"
|
| 1174 |
+
]
|
| 1175 |
+
},
|
| 1176 |
+
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 1177 |
+
"version": "4.60.2",
|
| 1178 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz",
|
| 1179 |
+
"integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==",
|
| 1180 |
+
"cpu": [
|
| 1181 |
+
"ppc64"
|
| 1182 |
+
],
|
| 1183 |
+
"dev": true,
|
| 1184 |
+
"license": "MIT",
|
| 1185 |
+
"optional": true,
|
| 1186 |
+
"os": [
|
| 1187 |
+
"linux"
|
| 1188 |
+
]
|
| 1189 |
+
},
|
| 1190 |
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 1191 |
+
"version": "4.60.2",
|
| 1192 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz",
|
| 1193 |
+
"integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==",
|
| 1194 |
+
"cpu": [
|
| 1195 |
+
"riscv64"
|
| 1196 |
+
],
|
| 1197 |
+
"dev": true,
|
| 1198 |
+
"license": "MIT",
|
| 1199 |
+
"optional": true,
|
| 1200 |
+
"os": [
|
| 1201 |
+
"linux"
|
| 1202 |
+
]
|
| 1203 |
+
},
|
| 1204 |
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 1205 |
+
"version": "4.60.2",
|
| 1206 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz",
|
| 1207 |
+
"integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==",
|
| 1208 |
+
"cpu": [
|
| 1209 |
+
"riscv64"
|
| 1210 |
+
],
|
| 1211 |
+
"dev": true,
|
| 1212 |
+
"license": "MIT",
|
| 1213 |
+
"optional": true,
|
| 1214 |
+
"os": [
|
| 1215 |
+
"linux"
|
| 1216 |
+
]
|
| 1217 |
+
},
|
| 1218 |
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 1219 |
+
"version": "4.60.2",
|
| 1220 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz",
|
| 1221 |
+
"integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==",
|
| 1222 |
+
"cpu": [
|
| 1223 |
+
"s390x"
|
| 1224 |
+
],
|
| 1225 |
+
"dev": true,
|
| 1226 |
+
"license": "MIT",
|
| 1227 |
+
"optional": true,
|
| 1228 |
+
"os": [
|
| 1229 |
+
"linux"
|
| 1230 |
+
]
|
| 1231 |
+
},
|
| 1232 |
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 1233 |
+
"version": "4.60.2",
|
| 1234 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz",
|
| 1235 |
+
"integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==",
|
| 1236 |
+
"cpu": [
|
| 1237 |
+
"x64"
|
| 1238 |
+
],
|
| 1239 |
+
"dev": true,
|
| 1240 |
+
"license": "MIT",
|
| 1241 |
+
"optional": true,
|
| 1242 |
+
"os": [
|
| 1243 |
+
"linux"
|
| 1244 |
+
]
|
| 1245 |
+
},
|
| 1246 |
+
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 1247 |
+
"version": "4.60.2",
|
| 1248 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz",
|
| 1249 |
+
"integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==",
|
| 1250 |
+
"cpu": [
|
| 1251 |
+
"x64"
|
| 1252 |
+
],
|
| 1253 |
+
"dev": true,
|
| 1254 |
+
"license": "MIT",
|
| 1255 |
+
"optional": true,
|
| 1256 |
+
"os": [
|
| 1257 |
+
"linux"
|
| 1258 |
+
]
|
| 1259 |
+
},
|
| 1260 |
+
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 1261 |
+
"version": "4.60.2",
|
| 1262 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz",
|
| 1263 |
+
"integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==",
|
| 1264 |
+
"cpu": [
|
| 1265 |
+
"x64"
|
| 1266 |
+
],
|
| 1267 |
+
"dev": true,
|
| 1268 |
+
"license": "MIT",
|
| 1269 |
+
"optional": true,
|
| 1270 |
+
"os": [
|
| 1271 |
+
"openbsd"
|
| 1272 |
+
]
|
| 1273 |
+
},
|
| 1274 |
+
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 1275 |
+
"version": "4.60.2",
|
| 1276 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz",
|
| 1277 |
+
"integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==",
|
| 1278 |
+
"cpu": [
|
| 1279 |
+
"arm64"
|
| 1280 |
+
],
|
| 1281 |
+
"dev": true,
|
| 1282 |
+
"license": "MIT",
|
| 1283 |
+
"optional": true,
|
| 1284 |
+
"os": [
|
| 1285 |
+
"openharmony"
|
| 1286 |
+
]
|
| 1287 |
+
},
|
| 1288 |
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 1289 |
+
"version": "4.60.2",
|
| 1290 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz",
|
| 1291 |
+
"integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==",
|
| 1292 |
+
"cpu": [
|
| 1293 |
+
"arm64"
|
| 1294 |
+
],
|
| 1295 |
+
"dev": true,
|
| 1296 |
+
"license": "MIT",
|
| 1297 |
+
"optional": true,
|
| 1298 |
+
"os": [
|
| 1299 |
+
"win32"
|
| 1300 |
+
]
|
| 1301 |
+
},
|
| 1302 |
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 1303 |
+
"version": "4.60.2",
|
| 1304 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz",
|
| 1305 |
+
"integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==",
|
| 1306 |
+
"cpu": [
|
| 1307 |
+
"ia32"
|
| 1308 |
+
],
|
| 1309 |
+
"dev": true,
|
| 1310 |
+
"license": "MIT",
|
| 1311 |
+
"optional": true,
|
| 1312 |
+
"os": [
|
| 1313 |
+
"win32"
|
| 1314 |
+
]
|
| 1315 |
+
},
|
| 1316 |
+
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 1317 |
+
"version": "4.60.2",
|
| 1318 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz",
|
| 1319 |
+
"integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==",
|
| 1320 |
+
"cpu": [
|
| 1321 |
+
"x64"
|
| 1322 |
+
],
|
| 1323 |
+
"dev": true,
|
| 1324 |
+
"license": "MIT",
|
| 1325 |
+
"optional": true,
|
| 1326 |
+
"os": [
|
| 1327 |
+
"win32"
|
| 1328 |
+
]
|
| 1329 |
+
},
|
| 1330 |
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 1331 |
+
"version": "4.60.2",
|
| 1332 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz",
|
| 1333 |
+
"integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==",
|
| 1334 |
+
"cpu": [
|
| 1335 |
+
"x64"
|
| 1336 |
+
],
|
| 1337 |
+
"dev": true,
|
| 1338 |
+
"license": "MIT",
|
| 1339 |
+
"optional": true,
|
| 1340 |
+
"os": [
|
| 1341 |
+
"win32"
|
| 1342 |
+
]
|
| 1343 |
+
},
|
| 1344 |
+
"node_modules/@types/estree": {
|
| 1345 |
+
"version": "1.0.8",
|
| 1346 |
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
| 1347 |
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
| 1348 |
+
"dev": true,
|
| 1349 |
+
"license": "MIT"
|
| 1350 |
+
},
|
| 1351 |
+
"node_modules/@types/node": {
|
| 1352 |
+
"version": "25.6.0",
|
| 1353 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
|
| 1354 |
+
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
|
| 1355 |
+
"license": "MIT",
|
| 1356 |
+
"dependencies": {
|
| 1357 |
+
"undici-types": "~7.19.0"
|
| 1358 |
+
}
|
| 1359 |
+
},
|
| 1360 |
+
"node_modules/adm-zip": {
|
| 1361 |
+
"version": "0.5.17",
|
| 1362 |
+
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.17.tgz",
|
| 1363 |
+
"integrity": "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==",
|
| 1364 |
+
"license": "MIT",
|
| 1365 |
+
"engines": {
|
| 1366 |
+
"node": ">=12.0"
|
| 1367 |
+
}
|
| 1368 |
+
},
|
| 1369 |
+
"node_modules/boolean": {
|
| 1370 |
+
"version": "3.2.0",
|
| 1371 |
+
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
|
| 1372 |
+
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
|
| 1373 |
+
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
| 1374 |
+
"license": "MIT"
|
| 1375 |
+
},
|
| 1376 |
+
"node_modules/define-data-property": {
|
| 1377 |
+
"version": "1.1.4",
|
| 1378 |
+
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
| 1379 |
+
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
| 1380 |
+
"license": "MIT",
|
| 1381 |
+
"dependencies": {
|
| 1382 |
+
"es-define-property": "^1.0.0",
|
| 1383 |
+
"es-errors": "^1.3.0",
|
| 1384 |
+
"gopd": "^1.0.1"
|
| 1385 |
+
},
|
| 1386 |
+
"engines": {
|
| 1387 |
+
"node": ">= 0.4"
|
| 1388 |
+
},
|
| 1389 |
+
"funding": {
|
| 1390 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1391 |
+
}
|
| 1392 |
+
},
|
| 1393 |
+
"node_modules/define-properties": {
|
| 1394 |
+
"version": "1.2.1",
|
| 1395 |
+
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
| 1396 |
+
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
| 1397 |
+
"license": "MIT",
|
| 1398 |
+
"dependencies": {
|
| 1399 |
+
"define-data-property": "^1.0.1",
|
| 1400 |
+
"has-property-descriptors": "^1.0.0",
|
| 1401 |
+
"object-keys": "^1.1.1"
|
| 1402 |
+
},
|
| 1403 |
+
"engines": {
|
| 1404 |
+
"node": ">= 0.4"
|
| 1405 |
+
},
|
| 1406 |
+
"funding": {
|
| 1407 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1408 |
+
}
|
| 1409 |
+
},
|
| 1410 |
+
"node_modules/detect-libc": {
|
| 1411 |
+
"version": "2.1.2",
|
| 1412 |
+
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
| 1413 |
+
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
| 1414 |
+
"license": "Apache-2.0",
|
| 1415 |
+
"engines": {
|
| 1416 |
+
"node": ">=8"
|
| 1417 |
+
}
|
| 1418 |
+
},
|
| 1419 |
+
"node_modules/detect-node": {
|
| 1420 |
+
"version": "2.1.0",
|
| 1421 |
+
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
| 1422 |
+
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
| 1423 |
+
"license": "MIT"
|
| 1424 |
+
},
|
| 1425 |
+
"node_modules/es-define-property": {
|
| 1426 |
+
"version": "1.0.1",
|
| 1427 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 1428 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 1429 |
+
"license": "MIT",
|
| 1430 |
+
"engines": {
|
| 1431 |
+
"node": ">= 0.4"
|
| 1432 |
+
}
|
| 1433 |
+
},
|
| 1434 |
+
"node_modules/es-errors": {
|
| 1435 |
+
"version": "1.3.0",
|
| 1436 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 1437 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 1438 |
+
"license": "MIT",
|
| 1439 |
+
"engines": {
|
| 1440 |
+
"node": ">= 0.4"
|
| 1441 |
+
}
|
| 1442 |
+
},
|
| 1443 |
+
"node_modules/es6-error": {
|
| 1444 |
+
"version": "4.1.1",
|
| 1445 |
+
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
|
| 1446 |
+
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
|
| 1447 |
+
"license": "MIT"
|
| 1448 |
+
},
|
| 1449 |
+
"node_modules/esbuild": {
|
| 1450 |
+
"version": "0.21.5",
|
| 1451 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
| 1452 |
+
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
| 1453 |
+
"dev": true,
|
| 1454 |
+
"hasInstallScript": true,
|
| 1455 |
+
"license": "MIT",
|
| 1456 |
+
"bin": {
|
| 1457 |
+
"esbuild": "bin/esbuild"
|
| 1458 |
+
},
|
| 1459 |
+
"engines": {
|
| 1460 |
+
"node": ">=12"
|
| 1461 |
+
},
|
| 1462 |
+
"optionalDependencies": {
|
| 1463 |
+
"@esbuild/aix-ppc64": "0.21.5",
|
| 1464 |
+
"@esbuild/android-arm": "0.21.5",
|
| 1465 |
+
"@esbuild/android-arm64": "0.21.5",
|
| 1466 |
+
"@esbuild/android-x64": "0.21.5",
|
| 1467 |
+
"@esbuild/darwin-arm64": "0.21.5",
|
| 1468 |
+
"@esbuild/darwin-x64": "0.21.5",
|
| 1469 |
+
"@esbuild/freebsd-arm64": "0.21.5",
|
| 1470 |
+
"@esbuild/freebsd-x64": "0.21.5",
|
| 1471 |
+
"@esbuild/linux-arm": "0.21.5",
|
| 1472 |
+
"@esbuild/linux-arm64": "0.21.5",
|
| 1473 |
+
"@esbuild/linux-ia32": "0.21.5",
|
| 1474 |
+
"@esbuild/linux-loong64": "0.21.5",
|
| 1475 |
+
"@esbuild/linux-mips64el": "0.21.5",
|
| 1476 |
+
"@esbuild/linux-ppc64": "0.21.5",
|
| 1477 |
+
"@esbuild/linux-riscv64": "0.21.5",
|
| 1478 |
+
"@esbuild/linux-s390x": "0.21.5",
|
| 1479 |
+
"@esbuild/linux-x64": "0.21.5",
|
| 1480 |
+
"@esbuild/netbsd-x64": "0.21.5",
|
| 1481 |
+
"@esbuild/openbsd-x64": "0.21.5",
|
| 1482 |
+
"@esbuild/sunos-x64": "0.21.5",
|
| 1483 |
+
"@esbuild/win32-arm64": "0.21.5",
|
| 1484 |
+
"@esbuild/win32-ia32": "0.21.5",
|
| 1485 |
+
"@esbuild/win32-x64": "0.21.5"
|
| 1486 |
+
}
|
| 1487 |
+
},
|
| 1488 |
+
"node_modules/escape-string-regexp": {
|
| 1489 |
+
"version": "4.0.0",
|
| 1490 |
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
| 1491 |
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
| 1492 |
+
"license": "MIT",
|
| 1493 |
+
"engines": {
|
| 1494 |
+
"node": ">=10"
|
| 1495 |
+
},
|
| 1496 |
+
"funding": {
|
| 1497 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1498 |
+
}
|
| 1499 |
+
},
|
| 1500 |
+
"node_modules/flatbuffers": {
|
| 1501 |
+
"version": "25.9.23",
|
| 1502 |
+
"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz",
|
| 1503 |
+
"integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==",
|
| 1504 |
+
"license": "Apache-2.0"
|
| 1505 |
+
},
|
| 1506 |
+
"node_modules/fsevents": {
|
| 1507 |
+
"version": "2.3.3",
|
| 1508 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1509 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1510 |
+
"dev": true,
|
| 1511 |
+
"hasInstallScript": true,
|
| 1512 |
+
"license": "MIT",
|
| 1513 |
+
"optional": true,
|
| 1514 |
+
"os": [
|
| 1515 |
+
"darwin"
|
| 1516 |
+
],
|
| 1517 |
+
"engines": {
|
| 1518 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1519 |
+
}
|
| 1520 |
+
},
|
| 1521 |
+
"node_modules/global-agent": {
|
| 1522 |
+
"version": "3.0.0",
|
| 1523 |
+
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
|
| 1524 |
+
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
|
| 1525 |
+
"license": "BSD-3-Clause",
|
| 1526 |
+
"dependencies": {
|
| 1527 |
+
"boolean": "^3.0.1",
|
| 1528 |
+
"es6-error": "^4.1.1",
|
| 1529 |
+
"matcher": "^3.0.0",
|
| 1530 |
+
"roarr": "^2.15.3",
|
| 1531 |
+
"semver": "^7.3.2",
|
| 1532 |
+
"serialize-error": "^7.0.1"
|
| 1533 |
+
},
|
| 1534 |
+
"engines": {
|
| 1535 |
+
"node": ">=10.0"
|
| 1536 |
+
}
|
| 1537 |
+
},
|
| 1538 |
+
"node_modules/globalthis": {
|
| 1539 |
+
"version": "1.0.4",
|
| 1540 |
+
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
|
| 1541 |
+
"integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
|
| 1542 |
+
"license": "MIT",
|
| 1543 |
+
"dependencies": {
|
| 1544 |
+
"define-properties": "^1.2.1",
|
| 1545 |
+
"gopd": "^1.0.1"
|
| 1546 |
+
},
|
| 1547 |
+
"engines": {
|
| 1548 |
+
"node": ">= 0.4"
|
| 1549 |
+
},
|
| 1550 |
+
"funding": {
|
| 1551 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1552 |
+
}
|
| 1553 |
+
},
|
| 1554 |
+
"node_modules/gopd": {
|
| 1555 |
+
"version": "1.2.0",
|
| 1556 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 1557 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 1558 |
+
"license": "MIT",
|
| 1559 |
+
"engines": {
|
| 1560 |
+
"node": ">= 0.4"
|
| 1561 |
+
},
|
| 1562 |
+
"funding": {
|
| 1563 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1564 |
+
}
|
| 1565 |
+
},
|
| 1566 |
+
"node_modules/guid-typescript": {
|
| 1567 |
+
"version": "1.0.9",
|
| 1568 |
+
"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
|
| 1569 |
+
"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==",
|
| 1570 |
+
"license": "ISC"
|
| 1571 |
+
},
|
| 1572 |
+
"node_modules/has-property-descriptors": {
|
| 1573 |
+
"version": "1.0.2",
|
| 1574 |
+
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
| 1575 |
+
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
| 1576 |
+
"license": "MIT",
|
| 1577 |
+
"dependencies": {
|
| 1578 |
+
"es-define-property": "^1.0.0"
|
| 1579 |
+
},
|
| 1580 |
+
"funding": {
|
| 1581 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1582 |
+
}
|
| 1583 |
+
},
|
| 1584 |
+
"node_modules/json-stringify-safe": {
|
| 1585 |
+
"version": "5.0.1",
|
| 1586 |
+
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
| 1587 |
+
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
| 1588 |
+
"license": "ISC"
|
| 1589 |
+
},
|
| 1590 |
+
"node_modules/long": {
|
| 1591 |
+
"version": "5.3.2",
|
| 1592 |
+
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
| 1593 |
+
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
| 1594 |
+
"license": "Apache-2.0"
|
| 1595 |
+
},
|
| 1596 |
+
"node_modules/matcher": {
|
| 1597 |
+
"version": "3.0.0",
|
| 1598 |
+
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
| 1599 |
+
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
|
| 1600 |
+
"license": "MIT",
|
| 1601 |
+
"dependencies": {
|
| 1602 |
+
"escape-string-regexp": "^4.0.0"
|
| 1603 |
+
},
|
| 1604 |
+
"engines": {
|
| 1605 |
+
"node": ">=10"
|
| 1606 |
+
}
|
| 1607 |
+
},
|
| 1608 |
+
"node_modules/nanoid": {
|
| 1609 |
+
"version": "3.3.11",
|
| 1610 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
| 1611 |
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
| 1612 |
+
"dev": true,
|
| 1613 |
+
"funding": [
|
| 1614 |
+
{
|
| 1615 |
+
"type": "github",
|
| 1616 |
+
"url": "https://github.com/sponsors/ai"
|
| 1617 |
+
}
|
| 1618 |
+
],
|
| 1619 |
+
"license": "MIT",
|
| 1620 |
+
"bin": {
|
| 1621 |
+
"nanoid": "bin/nanoid.cjs"
|
| 1622 |
+
},
|
| 1623 |
+
"engines": {
|
| 1624 |
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 1625 |
+
}
|
| 1626 |
+
},
|
| 1627 |
+
"node_modules/object-keys": {
|
| 1628 |
+
"version": "1.1.1",
|
| 1629 |
+
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
| 1630 |
+
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
| 1631 |
+
"license": "MIT",
|
| 1632 |
+
"engines": {
|
| 1633 |
+
"node": ">= 0.4"
|
| 1634 |
+
}
|
| 1635 |
+
},
|
| 1636 |
+
"node_modules/onnxruntime-common": {
|
| 1637 |
+
"version": "1.24.3",
|
| 1638 |
+
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz",
|
| 1639 |
+
"integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==",
|
| 1640 |
+
"license": "MIT"
|
| 1641 |
+
},
|
| 1642 |
+
"node_modules/onnxruntime-node": {
|
| 1643 |
+
"version": "1.24.3",
|
| 1644 |
+
"resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz",
|
| 1645 |
+
"integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==",
|
| 1646 |
+
"hasInstallScript": true,
|
| 1647 |
+
"license": "MIT",
|
| 1648 |
+
"os": [
|
| 1649 |
+
"win32",
|
| 1650 |
+
"darwin",
|
| 1651 |
+
"linux"
|
| 1652 |
+
],
|
| 1653 |
+
"dependencies": {
|
| 1654 |
+
"adm-zip": "^0.5.16",
|
| 1655 |
+
"global-agent": "^3.0.0",
|
| 1656 |
+
"onnxruntime-common": "1.24.3"
|
| 1657 |
+
}
|
| 1658 |
+
},
|
| 1659 |
+
"node_modules/onnxruntime-web": {
|
| 1660 |
+
"version": "1.26.0-dev.20260416-b7804b056c",
|
| 1661 |
+
"resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.26.0-dev.20260416-b7804b056c.tgz",
|
| 1662 |
+
"integrity": "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==",
|
| 1663 |
+
"license": "MIT",
|
| 1664 |
+
"dependencies": {
|
| 1665 |
+
"flatbuffers": "^25.1.24",
|
| 1666 |
+
"guid-typescript": "^1.0.9",
|
| 1667 |
+
"long": "^5.2.3",
|
| 1668 |
+
"onnxruntime-common": "1.24.0-dev.20251116-b39e144322",
|
| 1669 |
+
"platform": "^1.3.6",
|
| 1670 |
+
"protobufjs": "^7.2.4"
|
| 1671 |
+
}
|
| 1672 |
+
},
|
| 1673 |
+
"node_modules/onnxruntime-web/node_modules/onnxruntime-common": {
|
| 1674 |
+
"version": "1.24.0-dev.20251116-b39e144322",
|
| 1675 |
+
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.0-dev.20251116-b39e144322.tgz",
|
| 1676 |
+
"integrity": "sha512-BOoomdHYmNRL5r4iQ4bMvsl2t0/hzVQ3OM3PHD0gxeXu1PmggqBv3puZicEUVOA3AtHHYmqZtjMj9FOfGrATTw==",
|
| 1677 |
+
"license": "MIT"
|
| 1678 |
+
},
|
| 1679 |
+
"node_modules/picocolors": {
|
| 1680 |
+
"version": "1.1.1",
|
| 1681 |
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 1682 |
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 1683 |
+
"dev": true,
|
| 1684 |
+
"license": "ISC"
|
| 1685 |
+
},
|
| 1686 |
+
"node_modules/platform": {
|
| 1687 |
+
"version": "1.3.6",
|
| 1688 |
+
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
| 1689 |
+
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
|
| 1690 |
+
"license": "MIT"
|
| 1691 |
+
},
|
| 1692 |
+
"node_modules/postcss": {
|
| 1693 |
+
"version": "8.5.10",
|
| 1694 |
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
|
| 1695 |
+
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
|
| 1696 |
+
"dev": true,
|
| 1697 |
+
"funding": [
|
| 1698 |
+
{
|
| 1699 |
+
"type": "opencollective",
|
| 1700 |
+
"url": "https://opencollective.com/postcss/"
|
| 1701 |
+
},
|
| 1702 |
+
{
|
| 1703 |
+
"type": "tidelift",
|
| 1704 |
+
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 1705 |
+
},
|
| 1706 |
+
{
|
| 1707 |
+
"type": "github",
|
| 1708 |
+
"url": "https://github.com/sponsors/ai"
|
| 1709 |
+
}
|
| 1710 |
+
],
|
| 1711 |
+
"license": "MIT",
|
| 1712 |
+
"dependencies": {
|
| 1713 |
+
"nanoid": "^3.3.11",
|
| 1714 |
+
"picocolors": "^1.1.1",
|
| 1715 |
+
"source-map-js": "^1.2.1"
|
| 1716 |
+
},
|
| 1717 |
+
"engines": {
|
| 1718 |
+
"node": "^10 || ^12 || >=14"
|
| 1719 |
+
}
|
| 1720 |
+
},
|
| 1721 |
+
"node_modules/protobufjs": {
|
| 1722 |
+
"version": "7.5.5",
|
| 1723 |
+
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz",
|
| 1724 |
+
"integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==",
|
| 1725 |
+
"hasInstallScript": true,
|
| 1726 |
+
"license": "BSD-3-Clause",
|
| 1727 |
+
"dependencies": {
|
| 1728 |
+
"@protobufjs/aspromise": "^1.1.2",
|
| 1729 |
+
"@protobufjs/base64": "^1.1.2",
|
| 1730 |
+
"@protobufjs/codegen": "^2.0.4",
|
| 1731 |
+
"@protobufjs/eventemitter": "^1.1.0",
|
| 1732 |
+
"@protobufjs/fetch": "^1.1.0",
|
| 1733 |
+
"@protobufjs/float": "^1.0.2",
|
| 1734 |
+
"@protobufjs/inquire": "^1.1.0",
|
| 1735 |
+
"@protobufjs/path": "^1.1.2",
|
| 1736 |
+
"@protobufjs/pool": "^1.1.0",
|
| 1737 |
+
"@protobufjs/utf8": "^1.1.0",
|
| 1738 |
+
"@types/node": ">=13.7.0",
|
| 1739 |
+
"long": "^5.0.0"
|
| 1740 |
+
},
|
| 1741 |
+
"engines": {
|
| 1742 |
+
"node": ">=12.0.0"
|
| 1743 |
+
}
|
| 1744 |
+
},
|
| 1745 |
+
"node_modules/roarr": {
|
| 1746 |
+
"version": "2.15.4",
|
| 1747 |
+
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
|
| 1748 |
+
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
|
| 1749 |
+
"license": "BSD-3-Clause",
|
| 1750 |
+
"dependencies": {
|
| 1751 |
+
"boolean": "^3.0.1",
|
| 1752 |
+
"detect-node": "^2.0.4",
|
| 1753 |
+
"globalthis": "^1.0.1",
|
| 1754 |
+
"json-stringify-safe": "^5.0.1",
|
| 1755 |
+
"semver-compare": "^1.0.0",
|
| 1756 |
+
"sprintf-js": "^1.1.2"
|
| 1757 |
+
},
|
| 1758 |
+
"engines": {
|
| 1759 |
+
"node": ">=8.0"
|
| 1760 |
+
}
|
| 1761 |
+
},
|
| 1762 |
+
"node_modules/rollup": {
|
| 1763 |
+
"version": "4.60.2",
|
| 1764 |
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz",
|
| 1765 |
+
"integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==",
|
| 1766 |
+
"dev": true,
|
| 1767 |
+
"license": "MIT",
|
| 1768 |
+
"dependencies": {
|
| 1769 |
+
"@types/estree": "1.0.8"
|
| 1770 |
+
},
|
| 1771 |
+
"bin": {
|
| 1772 |
+
"rollup": "dist/bin/rollup"
|
| 1773 |
+
},
|
| 1774 |
+
"engines": {
|
| 1775 |
+
"node": ">=18.0.0",
|
| 1776 |
+
"npm": ">=8.0.0"
|
| 1777 |
+
},
|
| 1778 |
+
"optionalDependencies": {
|
| 1779 |
+
"@rollup/rollup-android-arm-eabi": "4.60.2",
|
| 1780 |
+
"@rollup/rollup-android-arm64": "4.60.2",
|
| 1781 |
+
"@rollup/rollup-darwin-arm64": "4.60.2",
|
| 1782 |
+
"@rollup/rollup-darwin-x64": "4.60.2",
|
| 1783 |
+
"@rollup/rollup-freebsd-arm64": "4.60.2",
|
| 1784 |
+
"@rollup/rollup-freebsd-x64": "4.60.2",
|
| 1785 |
+
"@rollup/rollup-linux-arm-gnueabihf": "4.60.2",
|
| 1786 |
+
"@rollup/rollup-linux-arm-musleabihf": "4.60.2",
|
| 1787 |
+
"@rollup/rollup-linux-arm64-gnu": "4.60.2",
|
| 1788 |
+
"@rollup/rollup-linux-arm64-musl": "4.60.2",
|
| 1789 |
+
"@rollup/rollup-linux-loong64-gnu": "4.60.2",
|
| 1790 |
+
"@rollup/rollup-linux-loong64-musl": "4.60.2",
|
| 1791 |
+
"@rollup/rollup-linux-ppc64-gnu": "4.60.2",
|
| 1792 |
+
"@rollup/rollup-linux-ppc64-musl": "4.60.2",
|
| 1793 |
+
"@rollup/rollup-linux-riscv64-gnu": "4.60.2",
|
| 1794 |
+
"@rollup/rollup-linux-riscv64-musl": "4.60.2",
|
| 1795 |
+
"@rollup/rollup-linux-s390x-gnu": "4.60.2",
|
| 1796 |
+
"@rollup/rollup-linux-x64-gnu": "4.60.2",
|
| 1797 |
+
"@rollup/rollup-linux-x64-musl": "4.60.2",
|
| 1798 |
+
"@rollup/rollup-openbsd-x64": "4.60.2",
|
| 1799 |
+
"@rollup/rollup-openharmony-arm64": "4.60.2",
|
| 1800 |
+
"@rollup/rollup-win32-arm64-msvc": "4.60.2",
|
| 1801 |
+
"@rollup/rollup-win32-ia32-msvc": "4.60.2",
|
| 1802 |
+
"@rollup/rollup-win32-x64-gnu": "4.60.2",
|
| 1803 |
+
"@rollup/rollup-win32-x64-msvc": "4.60.2",
|
| 1804 |
+
"fsevents": "~2.3.2"
|
| 1805 |
+
}
|
| 1806 |
+
},
|
| 1807 |
+
"node_modules/semver": {
|
| 1808 |
+
"version": "7.7.4",
|
| 1809 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
| 1810 |
+
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
| 1811 |
+
"license": "ISC",
|
| 1812 |
+
"bin": {
|
| 1813 |
+
"semver": "bin/semver.js"
|
| 1814 |
+
},
|
| 1815 |
+
"engines": {
|
| 1816 |
+
"node": ">=10"
|
| 1817 |
+
}
|
| 1818 |
+
},
|
| 1819 |
+
"node_modules/semver-compare": {
|
| 1820 |
+
"version": "1.0.0",
|
| 1821 |
+
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
|
| 1822 |
+
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
|
| 1823 |
+
"license": "MIT"
|
| 1824 |
+
},
|
| 1825 |
+
"node_modules/serialize-error": {
|
| 1826 |
+
"version": "7.0.1",
|
| 1827 |
+
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
|
| 1828 |
+
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
|
| 1829 |
+
"license": "MIT",
|
| 1830 |
+
"dependencies": {
|
| 1831 |
+
"type-fest": "^0.13.1"
|
| 1832 |
+
},
|
| 1833 |
+
"engines": {
|
| 1834 |
+
"node": ">=10"
|
| 1835 |
+
},
|
| 1836 |
+
"funding": {
|
| 1837 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1838 |
+
}
|
| 1839 |
+
},
|
| 1840 |
+
"node_modules/sharp": {
|
| 1841 |
+
"version": "0.34.5",
|
| 1842 |
+
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
| 1843 |
+
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
| 1844 |
+
"hasInstallScript": true,
|
| 1845 |
+
"license": "Apache-2.0",
|
| 1846 |
+
"dependencies": {
|
| 1847 |
+
"@img/colour": "^1.0.0",
|
| 1848 |
+
"detect-libc": "^2.1.2",
|
| 1849 |
+
"semver": "^7.7.3"
|
| 1850 |
+
},
|
| 1851 |
+
"engines": {
|
| 1852 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1853 |
+
},
|
| 1854 |
+
"funding": {
|
| 1855 |
+
"url": "https://opencollective.com/libvips"
|
| 1856 |
+
},
|
| 1857 |
+
"optionalDependencies": {
|
| 1858 |
+
"@img/sharp-darwin-arm64": "0.34.5",
|
| 1859 |
+
"@img/sharp-darwin-x64": "0.34.5",
|
| 1860 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
| 1861 |
+
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
| 1862 |
+
"@img/sharp-libvips-linux-arm": "1.2.4",
|
| 1863 |
+
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
| 1864 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
| 1865 |
+
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
| 1866 |
+
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
| 1867 |
+
"@img/sharp-libvips-linux-x64": "1.2.4",
|
| 1868 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
| 1869 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
| 1870 |
+
"@img/sharp-linux-arm": "0.34.5",
|
| 1871 |
+
"@img/sharp-linux-arm64": "0.34.5",
|
| 1872 |
+
"@img/sharp-linux-ppc64": "0.34.5",
|
| 1873 |
+
"@img/sharp-linux-riscv64": "0.34.5",
|
| 1874 |
+
"@img/sharp-linux-s390x": "0.34.5",
|
| 1875 |
+
"@img/sharp-linux-x64": "0.34.5",
|
| 1876 |
+
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
| 1877 |
+
"@img/sharp-linuxmusl-x64": "0.34.5",
|
| 1878 |
+
"@img/sharp-wasm32": "0.34.5",
|
| 1879 |
+
"@img/sharp-win32-arm64": "0.34.5",
|
| 1880 |
+
"@img/sharp-win32-ia32": "0.34.5",
|
| 1881 |
+
"@img/sharp-win32-x64": "0.34.5"
|
| 1882 |
+
}
|
| 1883 |
+
},
|
| 1884 |
+
"node_modules/source-map-js": {
|
| 1885 |
+
"version": "1.2.1",
|
| 1886 |
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 1887 |
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 1888 |
+
"dev": true,
|
| 1889 |
+
"license": "BSD-3-Clause",
|
| 1890 |
+
"engines": {
|
| 1891 |
+
"node": ">=0.10.0"
|
| 1892 |
+
}
|
| 1893 |
+
},
|
| 1894 |
+
"node_modules/sprintf-js": {
|
| 1895 |
+
"version": "1.1.3",
|
| 1896 |
+
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
| 1897 |
+
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
| 1898 |
+
"license": "BSD-3-Clause"
|
| 1899 |
+
},
|
| 1900 |
+
"node_modules/toml": {
|
| 1901 |
+
"version": "2.3.6",
|
| 1902 |
+
"resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz",
|
| 1903 |
+
"integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==",
|
| 1904 |
+
"license": "MIT"
|
| 1905 |
+
},
|
| 1906 |
+
"node_modules/tslib": {
|
| 1907 |
+
"version": "2.8.1",
|
| 1908 |
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
| 1909 |
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
| 1910 |
+
"license": "0BSD",
|
| 1911 |
+
"optional": true
|
| 1912 |
+
},
|
| 1913 |
+
"node_modules/type-fest": {
|
| 1914 |
+
"version": "0.13.1",
|
| 1915 |
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
|
| 1916 |
+
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
|
| 1917 |
+
"license": "(MIT OR CC0-1.0)",
|
| 1918 |
+
"engines": {
|
| 1919 |
+
"node": ">=10"
|
| 1920 |
+
},
|
| 1921 |
+
"funding": {
|
| 1922 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1923 |
+
}
|
| 1924 |
+
},
|
| 1925 |
+
"node_modules/typescript": {
|
| 1926 |
+
"version": "5.9.3",
|
| 1927 |
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
| 1928 |
+
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 1929 |
+
"dev": true,
|
| 1930 |
+
"license": "Apache-2.0",
|
| 1931 |
+
"bin": {
|
| 1932 |
+
"tsc": "bin/tsc",
|
| 1933 |
+
"tsserver": "bin/tsserver"
|
| 1934 |
+
},
|
| 1935 |
+
"engines": {
|
| 1936 |
+
"node": ">=14.17"
|
| 1937 |
+
}
|
| 1938 |
+
},
|
| 1939 |
+
"node_modules/undici-types": {
|
| 1940 |
+
"version": "7.19.2",
|
| 1941 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
|
| 1942 |
+
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
| 1943 |
+
"license": "MIT"
|
| 1944 |
+
},
|
| 1945 |
+
"node_modules/vite": {
|
| 1946 |
+
"version": "5.4.21",
|
| 1947 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
| 1948 |
+
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
| 1949 |
+
"dev": true,
|
| 1950 |
+
"license": "MIT",
|
| 1951 |
+
"dependencies": {
|
| 1952 |
+
"esbuild": "^0.21.3",
|
| 1953 |
+
"postcss": "^8.4.43",
|
| 1954 |
+
"rollup": "^4.20.0"
|
| 1955 |
+
},
|
| 1956 |
+
"bin": {
|
| 1957 |
+
"vite": "bin/vite.js"
|
| 1958 |
+
},
|
| 1959 |
+
"engines": {
|
| 1960 |
+
"node": "^18.0.0 || >=20.0.0"
|
| 1961 |
+
},
|
| 1962 |
+
"funding": {
|
| 1963 |
+
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 1964 |
+
},
|
| 1965 |
+
"optionalDependencies": {
|
| 1966 |
+
"fsevents": "~2.3.3"
|
| 1967 |
+
},
|
| 1968 |
+
"peerDependencies": {
|
| 1969 |
+
"@types/node": "^18.0.0 || >=20.0.0",
|
| 1970 |
+
"less": "*",
|
| 1971 |
+
"lightningcss": "^1.21.0",
|
| 1972 |
+
"sass": "*",
|
| 1973 |
+
"sass-embedded": "*",
|
| 1974 |
+
"stylus": "*",
|
| 1975 |
+
"sugarss": "*",
|
| 1976 |
+
"terser": "^5.4.0"
|
| 1977 |
+
},
|
| 1978 |
+
"peerDependenciesMeta": {
|
| 1979 |
+
"@types/node": {
|
| 1980 |
+
"optional": true
|
| 1981 |
+
},
|
| 1982 |
+
"less": {
|
| 1983 |
+
"optional": true
|
| 1984 |
+
},
|
| 1985 |
+
"lightningcss": {
|
| 1986 |
+
"optional": true
|
| 1987 |
+
},
|
| 1988 |
+
"sass": {
|
| 1989 |
+
"optional": true
|
| 1990 |
+
},
|
| 1991 |
+
"sass-embedded": {
|
| 1992 |
+
"optional": true
|
| 1993 |
+
},
|
| 1994 |
+
"stylus": {
|
| 1995 |
+
"optional": true
|
| 1996 |
+
},
|
| 1997 |
+
"sugarss": {
|
| 1998 |
+
"optional": true
|
| 1999 |
+
},
|
| 2000 |
+
"terser": {
|
| 2001 |
+
"optional": true
|
| 2002 |
+
}
|
| 2003 |
+
}
|
| 2004 |
+
}
|
| 2005 |
+
}
|
| 2006 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "bol-tts-marathi-webgpu-demo",
|
| 3 |
+
"version": "0.1.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"type": "module",
|
| 6 |
+
"description": "In-browser Marathi TTS demo for shreyask/bol-tts-marathi-onnx",
|
| 7 |
+
"license": "Apache-2.0",
|
| 8 |
+
"scripts": {
|
| 9 |
+
"dev": "vite",
|
| 10 |
+
"build": "tsc && vite build",
|
| 11 |
+
"preview": "vite preview"
|
| 12 |
+
},
|
| 13 |
+
"dependencies": {
|
| 14 |
+
"@huggingface/transformers": "4.2.0",
|
| 15 |
+
"@indic-transliteration/sanscript": "^1.3.3"
|
| 16 |
+
},
|
| 17 |
+
"devDependencies": {
|
| 18 |
+
"typescript": "^5.6.3",
|
| 19 |
+
"vite": "^5.4.11"
|
| 20 |
+
}
|
| 21 |
+
}
|
public/config.json
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"istftnet": {
|
| 3 |
+
"upsample_kernel_sizes": [
|
| 4 |
+
20,
|
| 5 |
+
12
|
| 6 |
+
],
|
| 7 |
+
"upsample_rates": [
|
| 8 |
+
10,
|
| 9 |
+
6
|
| 10 |
+
],
|
| 11 |
+
"gen_istft_hop_size": 5,
|
| 12 |
+
"gen_istft_n_fft": 20,
|
| 13 |
+
"resblock_dilation_sizes": [
|
| 14 |
+
[
|
| 15 |
+
1,
|
| 16 |
+
3,
|
| 17 |
+
5
|
| 18 |
+
],
|
| 19 |
+
[
|
| 20 |
+
1,
|
| 21 |
+
3,
|
| 22 |
+
5
|
| 23 |
+
],
|
| 24 |
+
[
|
| 25 |
+
1,
|
| 26 |
+
3,
|
| 27 |
+
5
|
| 28 |
+
]
|
| 29 |
+
],
|
| 30 |
+
"resblock_kernel_sizes": [
|
| 31 |
+
3,
|
| 32 |
+
7,
|
| 33 |
+
11
|
| 34 |
+
],
|
| 35 |
+
"upsample_initial_channel": 512
|
| 36 |
+
},
|
| 37 |
+
"dim_in": 64,
|
| 38 |
+
"dropout": 0.2,
|
| 39 |
+
"hidden_dim": 512,
|
| 40 |
+
"max_conv_dim": 512,
|
| 41 |
+
"max_dur": 50,
|
| 42 |
+
"multispeaker": true,
|
| 43 |
+
"n_layer": 3,
|
| 44 |
+
"n_mels": 80,
|
| 45 |
+
"n_token": 178,
|
| 46 |
+
"style_dim": 128,
|
| 47 |
+
"text_encoder_kernel_size": 5,
|
| 48 |
+
"plbert": {
|
| 49 |
+
"hidden_size": 768,
|
| 50 |
+
"num_attention_heads": 12,
|
| 51 |
+
"intermediate_size": 2048,
|
| 52 |
+
"max_position_embeddings": 512,
|
| 53 |
+
"num_hidden_layers": 12,
|
| 54 |
+
"dropout": 0.1
|
| 55 |
+
},
|
| 56 |
+
"vocab": {
|
| 57 |
+
";": 1,
|
| 58 |
+
":": 2,
|
| 59 |
+
",": 3,
|
| 60 |
+
".": 4,
|
| 61 |
+
"!": 5,
|
| 62 |
+
"?": 6,
|
| 63 |
+
"—": 9,
|
| 64 |
+
"…": 10,
|
| 65 |
+
"\"": 11,
|
| 66 |
+
"(": 12,
|
| 67 |
+
")": 13,
|
| 68 |
+
"“": 14,
|
| 69 |
+
"”": 15,
|
| 70 |
+
" ": 16,
|
| 71 |
+
"̃": 17,
|
| 72 |
+
"ʣ": 18,
|
| 73 |
+
"ʥ": 19,
|
| 74 |
+
"ʦ": 20,
|
| 75 |
+
"ʨ": 21,
|
| 76 |
+
"ᵝ": 22,
|
| 77 |
+
"ꭧ": 23,
|
| 78 |
+
"A": 24,
|
| 79 |
+
"I": 25,
|
| 80 |
+
"O": 31,
|
| 81 |
+
"Q": 33,
|
| 82 |
+
"S": 35,
|
| 83 |
+
"T": 36,
|
| 84 |
+
"W": 39,
|
| 85 |
+
"Y": 41,
|
| 86 |
+
"ᵊ": 42,
|
| 87 |
+
"a": 43,
|
| 88 |
+
"b": 44,
|
| 89 |
+
"c": 45,
|
| 90 |
+
"d": 46,
|
| 91 |
+
"e": 47,
|
| 92 |
+
"f": 48,
|
| 93 |
+
"h": 50,
|
| 94 |
+
"i": 51,
|
| 95 |
+
"j": 52,
|
| 96 |
+
"k": 53,
|
| 97 |
+
"l": 54,
|
| 98 |
+
"m": 55,
|
| 99 |
+
"n": 56,
|
| 100 |
+
"o": 57,
|
| 101 |
+
"p": 58,
|
| 102 |
+
"q": 59,
|
| 103 |
+
"r": 60,
|
| 104 |
+
"s": 61,
|
| 105 |
+
"t": 62,
|
| 106 |
+
"u": 63,
|
| 107 |
+
"v": 64,
|
| 108 |
+
"w": 65,
|
| 109 |
+
"x": 66,
|
| 110 |
+
"y": 67,
|
| 111 |
+
"z": 68,
|
| 112 |
+
"ɑ": 69,
|
| 113 |
+
"ɐ": 70,
|
| 114 |
+
"ɒ": 71,
|
| 115 |
+
"æ": 72,
|
| 116 |
+
"β": 75,
|
| 117 |
+
"ɔ": 76,
|
| 118 |
+
"ɕ": 77,
|
| 119 |
+
"ç": 78,
|
| 120 |
+
"ɖ": 80,
|
| 121 |
+
"ð": 81,
|
| 122 |
+
"ʤ": 82,
|
| 123 |
+
"ə": 83,
|
| 124 |
+
"ɚ": 85,
|
| 125 |
+
"ɛ": 86,
|
| 126 |
+
"ɜ": 87,
|
| 127 |
+
"ɟ": 90,
|
| 128 |
+
"ɡ": 92,
|
| 129 |
+
"ɥ": 99,
|
| 130 |
+
"ɨ": 101,
|
| 131 |
+
"ɪ": 102,
|
| 132 |
+
"ʝ": 103,
|
| 133 |
+
"ɯ": 110,
|
| 134 |
+
"ɰ": 111,
|
| 135 |
+
"ŋ": 112,
|
| 136 |
+
"ɳ": 113,
|
| 137 |
+
"ɲ": 114,
|
| 138 |
+
"ɴ": 115,
|
| 139 |
+
"ø": 116,
|
| 140 |
+
"ɸ": 118,
|
| 141 |
+
"θ": 119,
|
| 142 |
+
"œ": 120,
|
| 143 |
+
"ɹ": 123,
|
| 144 |
+
"ɾ": 125,
|
| 145 |
+
"ɻ": 126,
|
| 146 |
+
"ʁ": 128,
|
| 147 |
+
"ɽ": 129,
|
| 148 |
+
"ʂ": 130,
|
| 149 |
+
"ʃ": 131,
|
| 150 |
+
"ʈ": 132,
|
| 151 |
+
"ʧ": 133,
|
| 152 |
+
"ʊ": 135,
|
| 153 |
+
"ʋ": 136,
|
| 154 |
+
"ʌ": 138,
|
| 155 |
+
"ɣ": 139,
|
| 156 |
+
"ɤ": 140,
|
| 157 |
+
"χ": 142,
|
| 158 |
+
"ʎ": 143,
|
| 159 |
+
"ʒ": 147,
|
| 160 |
+
"ʔ": 148,
|
| 161 |
+
"ˈ": 156,
|
| 162 |
+
"ˌ": 157,
|
| 163 |
+
"ː": 158,
|
| 164 |
+
"ʰ": 162,
|
| 165 |
+
"ʲ": 164,
|
| 166 |
+
"↓": 169,
|
| 167 |
+
"→": 171,
|
| 168 |
+
"↗": 172,
|
| 169 |
+
"↘": 173,
|
| 170 |
+
"ᵻ": 177,
|
| 171 |
+
"ɭ": 144
|
| 172 |
+
},
|
| 173 |
+
"model_type": "style_text_to_speech_2",
|
| 174 |
+
"architectures": [
|
| 175 |
+
"StyleTextToSpeech2Model"
|
| 176 |
+
]
|
| 177 |
+
}
|
public/voice_speeds.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_description": "Per-voice optimal speed for Kokoro Marathi. Tuned on Stage 2 epoch 2 checkpoint; may shift after full Stage 2 run.",
|
| 3 |
+
"_reference_sentence": "\u0928\u092e\u0938\u094d\u0915\u093e\u0930! \u092e\u0932\u093e \u0915\u0947\u0933\u0940 \u0906\u0923\u093f \u0906\u0902\u092c\u093e \u0906\u0935\u0921\u0924\u094b.",
|
| 4 |
+
"mf_asha": {
|
| 5 |
+
"speed": 0.85,
|
| 6 |
+
"notes": "User-validated"
|
| 7 |
+
},
|
| 8 |
+
"mm_vivek": {
|
| 9 |
+
"speed": null,
|
| 10 |
+
"notes": "TBD \u2014 likely similar to Asha (0.85-1.0) since both Rasa"
|
| 11 |
+
},
|
| 12 |
+
"mf_mukta": {
|
| 13 |
+
"speed": 1.0,
|
| 14 |
+
"notes": "IndicTTS female"
|
| 15 |
+
},
|
| 16 |
+
"mm_dnyanesh": {
|
| 17 |
+
"speed": 1.0,
|
| 18 |
+
"notes": "IndicTTS male"
|
| 19 |
+
}
|
| 20 |
+
}
|
public/voicepacks.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema_version": 1,
|
| 3 |
+
"_comment": "Source of truth for the bol-tts-marathi WebGPU demo's voice list + per-voice metadata + test phrases. Edit this to add/remove voices or update sample chips — main.ts consumes it dynamically on page load.",
|
| 4 |
+
"voices": [
|
| 5 |
+
{
|
| 6 |
+
"id": "mf_asha",
|
| 7 |
+
"name": "Asha",
|
| 8 |
+
"name_native": "आशा",
|
| 9 |
+
"gender": "female",
|
| 10 |
+
"source": "rasa",
|
| 11 |
+
"default_speed": 1.0,
|
| 12 |
+
"ship_status": "stable"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": "mm_vivek",
|
| 16 |
+
"name": "Vivek",
|
| 17 |
+
"name_native": "विवेक",
|
| 18 |
+
"gender": "male",
|
| 19 |
+
"source": "rasa",
|
| 20 |
+
"default_speed": 1.0,
|
| 21 |
+
"ship_status": "stable"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"id": "mf_mukta",
|
| 25 |
+
"name": "Mukta",
|
| 26 |
+
"name_native": "मुक्ता",
|
| 27 |
+
"gender": "female",
|
| 28 |
+
"source": "indicvoices_r",
|
| 29 |
+
"default_speed": 1.0,
|
| 30 |
+
"ship_status": "stable"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": "mm_dnyanesh",
|
| 34 |
+
"name": "Dnyanesh",
|
| 35 |
+
"name_native": "ज्ञानेश",
|
| 36 |
+
"gender": "male",
|
| 37 |
+
"source": "indicvoices_r",
|
| 38 |
+
"default_speed": 1.0,
|
| 39 |
+
"ship_status": "stable"
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"id": "mf_priya",
|
| 43 |
+
"name": "Priya",
|
| 44 |
+
"name_native": "प्रिया",
|
| 45 |
+
"gender": "female",
|
| 46 |
+
"source": "springlab",
|
| 47 |
+
"default_speed": 1.0,
|
| 48 |
+
"ship_status": "v0.2_preview"
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"id": "mm_arjun",
|
| 52 |
+
"name": "Arjun",
|
| 53 |
+
"name_native": "अर्जुन",
|
| 54 |
+
"gender": "male",
|
| 55 |
+
"source": "springlab",
|
| 56 |
+
"default_speed": 1.0,
|
| 57 |
+
"ship_status": "v0.2_preview"
|
| 58 |
+
}
|
| 59 |
+
],
|
| 60 |
+
"test_phrases": {
|
| 61 |
+
"marathi_neutral": [
|
| 62 |
+
{ "label": "हवामान", "text": "आज हवामान खूप छान आहे, पाऊस पडतो आहे." },
|
| 63 |
+
{ "label": "महाराष्ट्र", "text": "महाराष्ट्र हे भारतातील एक महत्त्वाचे राज्य आहे." },
|
| 64 |
+
{ "label": "मुंबई", "text": "मुंबई ही महाराष्ट्राची राजधानी आणि भारताची आर्थिक राजधानी आहे." },
|
| 65 |
+
{ "label": "पुणे", "text": "पुणे ही महाराष्ट्राची सांस्कृतिक राजधानी मानली जाते." },
|
| 66 |
+
{ "label": "आकडे", "text": "एक दोन तीन चार पाच सहा सात आठ." },
|
| 67 |
+
{ "label": "कथा", "text": "पूर्वी एका छोट्या गावात एक शहाणा शेतकरी राहत होता." },
|
| 68 |
+
{ "label": "गुढी पाडवा", "text": "गुढी पाडवा हा मराठी नववर्षाचा सण आहे." },
|
| 69 |
+
{ "label": "भजी", "text": "पावसाळी संध्याकाळी गरम भजी आणि कटिंग चहा म्हणजे स्वर्गच!" },
|
| 70 |
+
{ "label": "वडापाव", "text": "मुंबईचा वडापाव आणि पुण्याचा चहा — आपली खरी ओळख." }
|
| 71 |
+
],
|
| 72 |
+
"marathi_female": [
|
| 73 |
+
{ "label": "थकले", "text": "आज खूप काम केले, मी थकले आहे." }
|
| 74 |
+
],
|
| 75 |
+
"marathi_male": [
|
| 76 |
+
{ "label": "थकलो", "text": "आज खूप काम केले, मी थकलो आहे." }
|
| 77 |
+
],
|
| 78 |
+
"minglish_neutral": [
|
| 79 |
+
{ "label": "coffee?", "text": "Coffee पिऊया का?" },
|
| 80 |
+
{ "label": "AI tech", "text": "Artificial intelligence ही technology खूप amazing आहे." },
|
| 81 |
+
{ "label": "weekend plan", "text": "Weekend ला movie बघायचा plan आहे का?" }
|
| 82 |
+
],
|
| 83 |
+
"minglish_female": [
|
| 84 |
+
{ "label": "Google job", "text": "मी Google मध्ये काम करते." },
|
| 85 |
+
{ "label": "late meeting", "text": "आज meeting ला late झाले." }
|
| 86 |
+
],
|
| 87 |
+
"minglish_male": [
|
| 88 |
+
{ "label": "Google job", "text": "मी Google मध्ये काम करतो." },
|
| 89 |
+
{ "label": "late meeting", "text": "आज meeting ला late झालो." }
|
| 90 |
+
]
|
| 91 |
+
}
|
| 92 |
+
}
|
public/voices/mf_asha.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:57784e5f6853a66605c897d9d4489e509e5c686f5cd95027f6d4ef9119115bea
|
| 3 |
+
size 522240
|
public/voices/mf_mukta.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4fbc32a8b5a067a828a1a43c360d8d0576873f69019b4c60cd9efa45c226412e
|
| 3 |
+
size 522240
|
public/voices/mf_priya.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c09561d3fda1dfddb88c461a79ff5d518198035a8906feece9fe38d6973225a1
|
| 3 |
+
size 522240
|
public/voices/mm_arjun.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d2eb6136af364a684e0504a77817e54ce6a96771284dee629c0d938e76b334b0
|
| 3 |
+
size 522240
|
public/voices/mm_dnyanesh.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5582a30e89860d4999ac1e1a82bc410c5e8401ea2cea0d377bdfeeaeba72ddff
|
| 3 |
+
size 522240
|
public/voices/mm_vivek.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31668f284533bc72637781214b143fbfcef8519964067d40438df8d9cb66821e
|
| 3 |
+
size 522240
|
public/warli-strip.svg
ADDED
|
|
src/espeakng/espeakng.worker.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ab1eaf96aa5d5b5fb76e264d440e9cb7672b7a2becead3fc0b1f75956d607398
|
| 3 |
+
size 18528868
|
src/espeakng/phonemizer.ts
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* In-browser Marathi G2P via espeak-ng WASM.
|
| 3 |
+
*
|
| 4 |
+
* Uses the patched espeakng.worker.js from hexgrad/kokoro (same Emscripten
|
| 5 |
+
* build as @echogarden/espeak-ng-emscripten, but the kokoro PR #262 worker
|
| 6 |
+
* ships cleanly for direct ESM import). ~18.5 MB; loaded lazily on first call.
|
| 7 |
+
*
|
| 8 |
+
* Produces IPA identical to the training-side misaki+espeak-ng pipeline.
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
| 12 |
+
// @ts-ignore — the worker.js doesn't ship types
|
| 13 |
+
import Module from "./espeakng.worker.js";
|
| 14 |
+
|
| 15 |
+
const SUPPORTED_LANGUAGES = ["en", "mr", "hi"];
|
| 16 |
+
|
| 17 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 18 |
+
let workerPromise: Promise<any> | null = null;
|
| 19 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 20 |
+
let voicesCache: Promise<{ identifiers: Set<string> }> | null = null;
|
| 21 |
+
|
| 22 |
+
function getWorker() {
|
| 23 |
+
if (!workerPromise) {
|
| 24 |
+
workerPromise = new Promise((resolve) => {
|
| 25 |
+
if (Module.calledRun) resolve(new Module.eSpeakNGWorker());
|
| 26 |
+
else Module.onRuntimeInitialized = () => resolve(new Module.eSpeakNGWorker());
|
| 27 |
+
});
|
| 28 |
+
}
|
| 29 |
+
return workerPromise;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
function initVoices() {
|
| 33 |
+
if (!voicesCache) {
|
| 34 |
+
voicesCache = getWorker().then((worker) => {
|
| 35 |
+
const voices = worker.list_voices()
|
| 36 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 37 |
+
.map(({ name, identifier, languages }: any) => ({
|
| 38 |
+
name,
|
| 39 |
+
identifier,
|
| 40 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 41 |
+
languages: languages.filter((l: any) =>
|
| 42 |
+
SUPPORTED_LANGUAGES.includes(l.name.split("-")[0]),
|
| 43 |
+
),
|
| 44 |
+
}))
|
| 45 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 46 |
+
.filter((v: any) => v.languages.length > 0);
|
| 47 |
+
const identifiers = new Set<string>();
|
| 48 |
+
for (const v of voices) {
|
| 49 |
+
identifiers.add(v.identifier);
|
| 50 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 51 |
+
for (const l of v.languages) identifiers.add(l.name);
|
| 52 |
+
}
|
| 53 |
+
return { identifiers };
|
| 54 |
+
});
|
| 55 |
+
}
|
| 56 |
+
return voicesCache;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
// espeak-ng marks language-switched runs with `(xx)` tags — e.g. an English
|
| 60 |
+
// word inside a Marathi sentence comes out as `mˈi (en)ɡˈuːɡəl(mr) mˈədʰeː`.
|
| 61 |
+
// Tags reaching the tokenizer would corrupt it, but the language info itself
|
| 62 |
+
// is gold: it tells us per-phoneme which voice produced what. We use it to
|
| 63 |
+
// apply per-segment remap rules (English code-switches get Indic-English
|
| 64 |
+
// treatment; Marathi gets only espeak-mr bug fixes), then drop the tags.
|
| 65 |
+
const LANG_TAG_SPLIT = /\(([a-z]{2,3}(?:-[a-z0-9]+)?)\)/g;
|
| 66 |
+
|
| 67 |
+
function parseTaggedIpa(
|
| 68 |
+
raw: string,
|
| 69 |
+
defaultLang: string,
|
| 70 |
+
): Array<{ lang: string; ipa: string }> {
|
| 71 |
+
// Split with a capture group: result alternates [text, lang, text, lang, ...].
|
| 72 |
+
const parts = raw.split(LANG_TAG_SPLIT);
|
| 73 |
+
const segments: Array<{ lang: string; ipa: string }> = [];
|
| 74 |
+
let lang = defaultLang;
|
| 75 |
+
for (let i = 0; i < parts.length; i++) {
|
| 76 |
+
if (i % 2 === 0) {
|
| 77 |
+
if (parts[i]) segments.push({ lang, ipa: parts[i] });
|
| 78 |
+
} else {
|
| 79 |
+
lang = parts[i];
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
return segments;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
// espeak-ng emits some affricates as two-character digraphs (d + ʒ, t + ʃ, t + s)
|
| 86 |
+
// while Kokoro's vocab uses precomposed single characters (ʤ, ʧ, ʦ). Without
|
| 87 |
+
// normalization, the tokenizer drops the bare `ʒ`/`ʃ` and we lose the affricate
|
| 88 |
+
// ("technology" → "technolod(ʒ)i" → "technolodi"). Always applied.
|
| 89 |
+
const DIGRAPH_NORMALIZE: Array<[RegExp, string]> = [
|
| 90 |
+
[/dʒ/g, "ʤ"],
|
| 91 |
+
[/tʃ/g, "ʧ"],
|
| 92 |
+
[/ts/g, "ʦ"],
|
| 93 |
+
];
|
| 94 |
+
|
| 95 |
+
// espeak-ng's Marathi voice has IPA bugs that aren't dialectal — they're flat
|
| 96 |
+
// wrong (no Marathi dialect uses these sounds). We patch them on `mr` segments:
|
| 97 |
+
// - `ɹ` (English alveolar approximant) — Marathi र is a tap /ɾ/, never /ɹ/.
|
| 98 |
+
// - `ɪ` (English lax i) — Marathi has only short /i/ and long /iː/.
|
| 99 |
+
// - Anusvara before a labial (ब, भ, प, फ, व): Marathi phonotactics says the
|
| 100 |
+
// anusvara assimilates to /m/ (the homorganic nasal). espeak sometimes
|
| 101 |
+
// collapses this to vowel-nasalization only (मुंबई → mũbiː instead of
|
| 102 |
+
// mumbiː) so Mumbai sounds like "mubi". Re-insert the missing /m/.
|
| 103 |
+
// NOTE: `z` is NOT in this list. Marathi ज has two valid pronunciations —
|
| 104 |
+
// /ʤ/ (जा) and /z/ (आज, झाडू, loanwords) — and espeak-mr correctly emits
|
| 105 |
+
// whichever fits. Remapping z→ʤ here would corrupt words like आज.
|
| 106 |
+
const MARATHI_FIXES: Array<[RegExp, string]> = [
|
| 107 |
+
[/ɹ/g, "ɾ"],
|
| 108 |
+
[/ɪ/g, "i"],
|
| 109 |
+
// Anusvara-to-labial: मुं → mum (not mũ).
|
| 110 |
+
[/ũ(?=[bpmfv])/g, "um"],
|
| 111 |
+
[/ã(?=[bpmfv])/g, "am"],
|
| 112 |
+
[/ĩ(?=[bpmfv])/g, "im"],
|
| 113 |
+
[/õ(?=[bpmfv])/g, "om"],
|
| 114 |
+
[/ẽ(?=[bpmfv])/g, "em"],
|
| 115 |
+
// Anusvara-to-velar: कटिं + ग → kəʈiŋ (not kəʈĩ). Same bug pattern in a
|
| 116 |
+
// different place of articulation — espeak collapses the ŋ into vowel
|
| 117 |
+
// nasalization when followed by क/ख/ग/घ (IPA: k, kʰ, ɡ, ɡʰ).
|
| 118 |
+
[/ũ(?=[kɡ])/g, "uŋ"],
|
| 119 |
+
[/ã(?=[kɡ])/g, "aŋ"],
|
| 120 |
+
[/ĩ(?=[kɡ])/g, "iŋ"],
|
| 121 |
+
[/õ(?=[kɡ])/g, "oŋ"],
|
| 122 |
+
[/ẽ(?=[kɡ])/g, "eŋ"],
|
| 123 |
+
// Stress placement: Marathi favors first-syllable or first-heavy-syllable
|
| 124 |
+
// stress, but espeak-mr often puts primary stress `ˈ` on later long vowels
|
| 125 |
+
// and demotes the first syllable to secondary `ˌ` (Mumbai → mˌumbˈiː should
|
| 126 |
+
// be mˈumbˌiː). Swap the first secondary-before-primary pair per word.
|
| 127 |
+
// NOTE: We *only* swap when both ˈ and ˌ exist — this is a high-confidence
|
| 128 |
+
// signal that espeak had the right stress position but chose the wrong
|
| 129 |
+
// level. A broader "move lone ˈ to start" rule was tried and regressed
|
| 130 |
+
// legitimately middle-stressed words (मित्रांना → audio dropout), so we
|
| 131 |
+
// trust espeak when it emits a bare primary stress.
|
| 132 |
+
[/ˌ([^ˈˌ]*)ˈ/, "ˈ$1ˌ"],
|
| 133 |
+
];
|
| 134 |
+
|
| 135 |
+
// Additional remaps for English-only phonemes with no native Marathi slot.
|
| 136 |
+
// Applied to `en` segments (in addition to MARATHI_FIXES) so code-switch words
|
| 137 |
+
// route through densely-trained Indic slots and sound naturally Indic-accented
|
| 138 |
+
// — which matches how Marathi speakers actually pronounce these words. The
|
| 139 |
+
// alternative (raw English IPA) would hit undertrained slots in the fine-tune.
|
| 140 |
+
const ENGLISH_INDIC_EXTRAS: Array<[RegExp, string]> = [
|
| 141 |
+
[/z/g, "ʤ"], // "amazing" → "amaJing", "zoo" → "joo" (Indic-English only;
|
| 142 |
+
// Marathi keeps native /z/ in words like आज)
|
| 143 |
+
[/θ/g, "t"], // "thing" → "ting"
|
| 144 |
+
[/ð/g, "d"], // "this" → "dis"
|
| 145 |
+
[/ɛ/g, "e"], // "bet" → ए
|
| 146 |
+
[/ɒ/g, "ɔ"], // "cot" → ऑ
|
| 147 |
+
[/æ/g, "a"], // "cat" → अ
|
| 148 |
+
[/ɐ/g, "ə"], // schwa-ish
|
| 149 |
+
[/ʊ/g, "u"], // "put" → u
|
| 150 |
+
];
|
| 151 |
+
|
| 152 |
+
// eSpeak's Marathi voice gets some proper nouns wrong only in their inflected
|
| 153 |
+
// forms. "मुंबई" alone sounds correct as `mˈumbˌi` in the current model, but
|
| 154 |
+
// "मुंबईचा" is emitted with a spurious long `i:` before the suffix, which
|
| 155 |
+
// makes the synthesis sound off. Keep these as narrow lexical overrides rather
|
| 156 |
+
// than a global phoneme rewrite so we don't perturb native Marathi `c` tokens
|
| 157 |
+
// elsewhere in the lexicon.
|
| 158 |
+
const MARATHI_WORD_OVERRIDES: Record<string, string> = {
|
| 159 |
+
"मुंबई": "mˈumbˌi",
|
| 160 |
+
"मुंबईचा": "mˈumbˌica:",
|
| 161 |
+
"मुंबईची": "mˈumbˌici",
|
| 162 |
+
"मुंबईचे": "mˈumbˌice",
|
| 163 |
+
};
|
| 164 |
+
|
| 165 |
+
function applyRules(ipa: string, rules: Array<[RegExp, string]>): string {
|
| 166 |
+
let out = ipa;
|
| 167 |
+
for (const [re, to] of rules) out = out.replace(re, to);
|
| 168 |
+
return out;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/**
|
| 172 |
+
* Phonemize a chunk of text and return raw IPA lines WITH `(xx)` language-switch
|
| 173 |
+
* tags preserved. Callers are expected to consume the tags via `parseTaggedIpa`
|
| 174 |
+
* before sending IPA downstream — the tokenizer doesn't understand parens.
|
| 175 |
+
*/
|
| 176 |
+
export async function phonemize(text: string, language = "mr"): Promise<string[]> {
|
| 177 |
+
const worker = await getWorker();
|
| 178 |
+
const { identifiers } = await initVoices();
|
| 179 |
+
if (!identifiers.has(language)) {
|
| 180 |
+
throw new Error(
|
| 181 |
+
`Invalid language: "${language}". Supported: ${[...identifiers].sort().join(", ")}.`,
|
| 182 |
+
);
|
| 183 |
+
}
|
| 184 |
+
worker.set_voice(language);
|
| 185 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 186 |
+
const out = (worker as any).synthesize_ipa(text);
|
| 187 |
+
return (out.ipa ?? "")
|
| 188 |
+
.split("\n")
|
| 189 |
+
.map((line: string) => line.replace(/\s+/g, " ").trim())
|
| 190 |
+
.filter((x: string) => x.length > 0);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
/**
|
| 194 |
+
* Phonemize word-by-word so we can align pred_dur → words for timestamps.
|
| 195 |
+
* Each word is a single phonemize() call. For mixed-script input, espeak
|
| 196 |
+
* inserts `(en)`/`(mr)` tags around language-switched runs; we use those to
|
| 197 |
+
* apply per-segment remaps:
|
| 198 |
+
* - `mr` segments get espeak-mr bug fixes only (z→ʤ, ɪ→i, ɹ→ɾ).
|
| 199 |
+
* - `en` segments get bug fixes PLUS Indic-English vowel/fricative remaps
|
| 200 |
+
* (θ→t, ð→d, ɛ→e, …) so code-switch words route through densely-trained
|
| 201 |
+
* Marathi slots and sound naturally Indic-accented.
|
| 202 |
+
*/
|
| 203 |
+
export async function phonemizeByWord(
|
| 204 |
+
text: string,
|
| 205 |
+
language = "mr",
|
| 206 |
+
): Promise<Array<{ word: string; ipa: string }>> {
|
| 207 |
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
| 208 |
+
const out: Array<{ word: string; ipa: string }> = [];
|
| 209 |
+
for (const w of words) {
|
| 210 |
+
const override = language === "mr" ? MARATHI_WORD_OVERRIDES[w] : undefined;
|
| 211 |
+
if (override) {
|
| 212 |
+
out.push({ word: w, ipa: override });
|
| 213 |
+
continue;
|
| 214 |
+
}
|
| 215 |
+
// NFC-normalize so `u + combining tilde` collapses to precomposed `ũ` —
|
| 216 |
+
// our MARATHI_FIXES regexes match the precomposed forms.
|
| 217 |
+
const raw = (await phonemize(w, language)).join(" ").trim().normalize("NFC");
|
| 218 |
+
let ipa = "";
|
| 219 |
+
for (const seg of parseTaggedIpa(raw, language)) {
|
| 220 |
+
let chunk = applyRules(seg.ipa, DIGRAPH_NORMALIZE);
|
| 221 |
+
chunk = applyRules(chunk, MARATHI_FIXES);
|
| 222 |
+
if (seg.lang.startsWith("en")) chunk = applyRules(chunk, ENGLISH_INDIC_EXTRAS);
|
| 223 |
+
ipa += chunk;
|
| 224 |
+
}
|
| 225 |
+
// Decompose to NFD for the tokenizer. Kokoro vocab stores the combining
|
| 226 |
+
// tilde (U+0303) as a standalone token at slot 17 — not the precomposed
|
| 227 |
+
// nasalized vowels (ã/ũ/ĩ/õ/ẽ are NOT in vocab). Any precomposed form
|
| 228 |
+
// surviving our MARATHI_FIXES (e.g., anusvara before a dental, where no
|
| 229 |
+
// rule fires) would be silently dropped by tokenize.ts and take nearby
|
| 230 |
+
// consonants' alignment down with it. NFD keeps the base vowel and lets
|
| 231 |
+
// the tilde ride as its own vocab slot.
|
| 232 |
+
out.push({
|
| 233 |
+
word: w,
|
| 234 |
+
ipa: ipa.replace(/\s+/g, " ").trim().normalize("NFD"),
|
| 235 |
+
});
|
| 236 |
+
}
|
| 237 |
+
return out;
|
| 238 |
+
}
|
src/main.ts
ADDED
|
@@ -0,0 +1,789 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { KokoroSession, loadConfig, loadVoicepack, type KokoroConfig, type KokoroVocab } from "./model";
|
| 2 |
+
import { phonemizeByWord } from "./phonemize";
|
| 3 |
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
| 4 |
+
// @ts-ignore — sanscript ships JS with no types
|
| 5 |
+
import Sanscript from "@indic-transliteration/sanscript";
|
| 6 |
+
import { tokenizeByWord } from "./tokenize";
|
| 7 |
+
import { buildTimings, type WordTiming, type PhonemeTiming } from "./timestamps";
|
| 8 |
+
import type { PhonemizedWord } from "./phonemize";
|
| 9 |
+
|
| 10 |
+
// Voice list, per-voice default speeds, and gender-aware test phrases all
|
| 11 |
+
// come from public/voicepacks.json — single source of truth, edited there
|
| 12 |
+
// to add voices or update sample chips without touching code.
|
| 13 |
+
interface VoicepackEntry {
|
| 14 |
+
id: string;
|
| 15 |
+
name: string;
|
| 16 |
+
name_native: string;
|
| 17 |
+
gender: "male" | "female";
|
| 18 |
+
source: string;
|
| 19 |
+
default_speed: number;
|
| 20 |
+
ship_status: string;
|
| 21 |
+
}
|
| 22 |
+
interface ChipPhrase {
|
| 23 |
+
label: string;
|
| 24 |
+
text: string;
|
| 25 |
+
}
|
| 26 |
+
interface VoicepackManifest {
|
| 27 |
+
voices: VoicepackEntry[];
|
| 28 |
+
test_phrases: Record<string, ChipPhrase[]>;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
// ── DOM refs ────────────────────────────────────────────────────────────────
|
| 32 |
+
const $ = <T extends HTMLElement>(id: string) => document.getElementById(id) as T;
|
| 33 |
+
|
| 34 |
+
const statusEl = $<HTMLElement>("status");
|
| 35 |
+
const textEl = $<HTMLTextAreaElement>("text");
|
| 36 |
+
const voiceEl = $<HTMLSelectElement>("voice");
|
| 37 |
+
const speedEl = $<HTMLInputElement>("speed");
|
| 38 |
+
const speedValEl = $<HTMLElement>("speed-val");
|
| 39 |
+
const synthBtn = $<HTMLButtonElement>("synth");
|
| 40 |
+
const transliterateBtn = $<HTMLButtonElement>("transliterate");
|
| 41 |
+
const outputSection = $<HTMLElement>("output-section");
|
| 42 |
+
const audioEl = $<HTMLAudioElement>("audio");
|
| 43 |
+
const transcriptEl = $<HTMLElement>("transcript");
|
| 44 |
+
const compactEl = $<HTMLElement>("transcript-compact");
|
| 45 |
+
const phonemesEl = $<HTMLElement>("phonemes");
|
| 46 |
+
const wordTimingsEl = $<HTMLElement>("word-timings");
|
| 47 |
+
const waveformCanvas = $<HTMLCanvasElement>("waveform");
|
| 48 |
+
const playPauseBtn = $<HTMLButtonElement>("playpause");
|
| 49 |
+
const timeCurEl = $<HTMLElement>("time-cur");
|
| 50 |
+
const timeTotalEl = $<HTMLElement>("time-total");
|
| 51 |
+
const loadModelBtn = $<HTMLButtonElement>("load-model");
|
| 52 |
+
const modelGateEl = $<HTMLElement>("model-gate");
|
| 53 |
+
|
| 54 |
+
// ── State ───────────────────────────────────────────────────────────────────
|
| 55 |
+
let session: KokoroSession | null = null;
|
| 56 |
+
let config: KokoroConfig | null = null;
|
| 57 |
+
let voicepackManifest: VoicepackManifest | null = null;
|
| 58 |
+
const voicepackCache: Record<string, Float32Array> = {};
|
| 59 |
+
let currentWords: WordTiming[] = [];
|
| 60 |
+
let currentAudio: Float32Array | null = null;
|
| 61 |
+
let currentAudioURL: string | null = null; // blob URL we must revoke before replacing
|
| 62 |
+
let highlightRaf: number | null = null;
|
| 63 |
+
|
| 64 |
+
async function loadVoicepackManifest(): Promise<VoicepackManifest> {
|
| 65 |
+
const res = await fetch("voicepacks.json");
|
| 66 |
+
if (!res.ok) throw new Error(`voicepacks.json: ${res.status}`);
|
| 67 |
+
return (await res.json()) as VoicepackManifest;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
function populateVoiceSelector(manifest: VoicepackManifest): void {
|
| 71 |
+
voiceEl.innerHTML = "";
|
| 72 |
+
for (const v of manifest.voices) {
|
| 73 |
+
const opt = document.createElement("option");
|
| 74 |
+
opt.value = v.id;
|
| 75 |
+
const tag = v.ship_status === "stable" ? "" : ` [${v.ship_status.replace(/_/g, " ")}]`;
|
| 76 |
+
opt.textContent = `${v.name} (${v.name_native}) — ${v.gender}${tag}`;
|
| 77 |
+
voiceEl.appendChild(opt);
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// Build chip <button>s under #chips-marathi and #chips-minglish, filtered by
|
| 82 |
+
// the active voice's gender. Called on initial load + every voice change.
|
| 83 |
+
function populateChips(manifest: VoicepackManifest, gender: "male" | "female"): void {
|
| 84 |
+
const marathiContainer = document.getElementById("chips-marathi") as HTMLDivElement | null;
|
| 85 |
+
const minglishContainer = document.getElementById("chips-minglish") as HTMLDivElement | null;
|
| 86 |
+
if (!marathiContainer || !minglishContainer) return;
|
| 87 |
+
|
| 88 |
+
// Strip out previously-injected chips (preserve the static label span).
|
| 89 |
+
marathiContainer.querySelectorAll(".chip").forEach((c) => c.remove());
|
| 90 |
+
minglishContainer.querySelectorAll(".chip").forEach((c) => c.remove());
|
| 91 |
+
|
| 92 |
+
const buildChip = (p: ChipPhrase): HTMLButtonElement => {
|
| 93 |
+
const b = document.createElement("button");
|
| 94 |
+
b.className = "chip";
|
| 95 |
+
b.type = "button";
|
| 96 |
+
b.setAttribute("data-text", p.text);
|
| 97 |
+
b.textContent = p.label;
|
| 98 |
+
b.addEventListener("click", () => {
|
| 99 |
+
textEl.value = p.text;
|
| 100 |
+
});
|
| 101 |
+
return b;
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
const marathi: ChipPhrase[] = [
|
| 105 |
+
...(manifest.test_phrases.marathi_neutral ?? []),
|
| 106 |
+
...(manifest.test_phrases[`marathi_${gender}`] ?? []),
|
| 107 |
+
];
|
| 108 |
+
const minglish: ChipPhrase[] = [
|
| 109 |
+
...(manifest.test_phrases.minglish_neutral ?? []),
|
| 110 |
+
...(manifest.test_phrases[`minglish_${gender}`] ?? []),
|
| 111 |
+
];
|
| 112 |
+
|
| 113 |
+
for (const p of marathi) marathiContainer.appendChild(buildChip(p));
|
| 114 |
+
for (const p of minglish) minglishContainer.appendChild(buildChip(p));
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
// Kokoro's PLBERT caps at 510 input tokens. Going over crashes with a shape
|
| 118 |
+
// assertion; we surface a clear user error before calling the model.
|
| 119 |
+
const PLBERT_MAX_TOKENS = 510;
|
| 120 |
+
|
| 121 |
+
// ── Boot ────────────────────────────────────────────────────────────────────
|
| 122 |
+
// Two-step boot: fetch small metadata on page load (config + one voicepack,
|
| 123 |
+
// together ~530 KB), then gate the big 325 MB model download behind a button
|
| 124 |
+
// so users don't accidentally consume data.
|
| 125 |
+
async function bootLight() {
|
| 126 |
+
setStatus("loading config…");
|
| 127 |
+
config = await loadConfig();
|
| 128 |
+
voicepackManifest = await loadVoicepackManifest();
|
| 129 |
+
populateVoiceSelector(voicepackManifest);
|
| 130 |
+
const initialVoice = voicepackManifest.voices[0];
|
| 131 |
+
voiceEl.value = initialVoice.id;
|
| 132 |
+
populateChips(voicepackManifest, initialVoice.gender);
|
| 133 |
+
speedEl.value = String(initialVoice.default_speed);
|
| 134 |
+
speedValEl.textContent = fmtSpeed(speedEl.value);
|
| 135 |
+
voicepackCache[initialVoice.id] = await loadVoicepack(initialVoice.id);
|
| 136 |
+
setStatus(
|
| 137 |
+
`ready to load model · vocab ${Object.keys(config.vocab).length} — tap "Load model" to proceed`,
|
| 138 |
+
);
|
| 139 |
+
modelGateEl.hidden = false;
|
| 140 |
+
loadModelBtn.disabled = false;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
async function loadModel() {
|
| 144 |
+
if (!config) return;
|
| 145 |
+
loadModelBtn.disabled = true;
|
| 146 |
+
loadModelBtn.textContent = "Loading…";
|
| 147 |
+
try {
|
| 148 |
+
session = await KokoroSession.create((msg) => setStatus(msg));
|
| 149 |
+
// Belt-and-suspenders: explicitly tear down the gate card and re-enable
|
| 150 |
+
// synthesize. `.hidden` sometimes fights stylesheet rules in certain
|
| 151 |
+
// browsers; setting display:none as a backup guarantees it disappears.
|
| 152 |
+
modelGateEl.hidden = true;
|
| 153 |
+
modelGateEl.style.display = "none";
|
| 154 |
+
loadModelBtn.textContent = "Load model";
|
| 155 |
+
synthBtn.disabled = false;
|
| 156 |
+
setStatus(
|
| 157 |
+
`ready · vocab ${Object.keys(config.vocab).length} · ${session.backend}`,
|
| 158 |
+
"ready",
|
| 159 |
+
);
|
| 160 |
+
} catch (err) {
|
| 161 |
+
console.error("model load failed", err);
|
| 162 |
+
setStatus(`model load failed: ${(err as Error).message}`, "error");
|
| 163 |
+
loadModelBtn.disabled = false;
|
| 164 |
+
loadModelBtn.textContent = "Retry load";
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
loadModelBtn.addEventListener("click", loadModel);
|
| 169 |
+
|
| 170 |
+
function setStatus(msg: string, cls: "ready" | "error" | "" = "") {
|
| 171 |
+
statusEl.textContent = msg;
|
| 172 |
+
statusEl.className = "card status" + (cls ? ` ${cls}` : "");
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
// ── UI wiring ───────────────────────────────────────────────────────────────
|
| 176 |
+
const fmtSpeed = (v: string | number) => `${parseFloat(String(v)).toFixed(2)}×`;
|
| 177 |
+
voiceEl.addEventListener("change", () => {
|
| 178 |
+
if (!voicepackManifest) return;
|
| 179 |
+
const v = voicepackManifest.voices.find((x) => x.id === voiceEl.value);
|
| 180 |
+
if (!v) return;
|
| 181 |
+
speedEl.value = String(v.default_speed);
|
| 182 |
+
speedValEl.textContent = fmtSpeed(speedEl.value);
|
| 183 |
+
populateChips(voicepackManifest, v.gender);
|
| 184 |
+
});
|
| 185 |
+
speedEl.addEventListener("input", () => {
|
| 186 |
+
speedValEl.textContent = fmtSpeed(speedEl.value);
|
| 187 |
+
});
|
| 188 |
+
|
| 189 |
+
// Transliteration: Latin (ITRANS) → Devanagari, in-place. User can edit the
|
| 190 |
+
// result before synthesizing, so any quirks of the scheme (intuitive mapping
|
| 191 |
+
// for vowels, anusvara via "M", proper-noun handling) can be fixed manually
|
| 192 |
+
// rather than by us second-guessing them.
|
| 193 |
+
//
|
| 194 |
+
// `syncope: true` is sanscript's built-in Hindi/Marathi schwa-deletion mode —
|
| 195 |
+
// it drops the inherent /a/ and the corresponding `्` (halant) in positions
|
| 196 |
+
// where Indo-Aryan languages don't pronounce it (especially word-final
|
| 197 |
+
// consonants). Without it, ITRANS produces Sanskrit-style "नमस्कार्" instead
|
| 198 |
+
// of the form a Marathi reader expects, "नमस्कार".
|
| 199 |
+
function transliterateLatinToDevanagari(input: string): string {
|
| 200 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 201 |
+
return (Sanscript as any).t(input, "itrans", "devanagari", { syncope: true }) as string;
|
| 202 |
+
}
|
| 203 |
+
transliterateBtn.addEventListener("click", () => {
|
| 204 |
+
const before = textEl.value;
|
| 205 |
+
const after = transliterateLatinToDevanagari(before);
|
| 206 |
+
if (after !== before) {
|
| 207 |
+
textEl.value = after;
|
| 208 |
+
textEl.focus();
|
| 209 |
+
}
|
| 210 |
+
});
|
| 211 |
+
|
| 212 |
+
synthBtn.disabled = true;
|
| 213 |
+
synthBtn.addEventListener("click", onSynth);
|
| 214 |
+
|
| 215 |
+
// ── fused player wiring ──────────────────────────────────────────────────
|
| 216 |
+
playPauseBtn.addEventListener("click", () => {
|
| 217 |
+
if (audioEl.paused) audioEl.play().catch(() => {});
|
| 218 |
+
else audioEl.pause();
|
| 219 |
+
});
|
| 220 |
+
audioEl.addEventListener("play", () => { playPauseBtn.textContent = "⏸"; playPauseBtn.classList.add("playing"); });
|
| 221 |
+
audioEl.addEventListener("pause", () => { playPauseBtn.textContent = "▶"; playPauseBtn.classList.remove("playing"); });
|
| 222 |
+
audioEl.addEventListener("ended", () => { playPauseBtn.textContent = "▶"; playPauseBtn.classList.remove("playing"); });
|
| 223 |
+
// HTMLMediaElement.currentTime/duration are MEDIA-time seconds (independent of
|
| 224 |
+
// playbackRate) — same units as our pred_dur-derived word timings. Don't
|
| 225 |
+
// multiply by playbackRate; doing so made the clock drift proportional to the
|
| 226 |
+
// speed slider and broke seek/highlight alignment on non-1.0 speeds.
|
| 227 |
+
audioEl.addEventListener("loadedmetadata", () => {
|
| 228 |
+
timeTotalEl.textContent = fmtTime(audioEl.duration);
|
| 229 |
+
});
|
| 230 |
+
audioEl.addEventListener("timeupdate", () => {
|
| 231 |
+
timeCurEl.textContent = fmtTime(audioEl.currentTime);
|
| 232 |
+
});
|
| 233 |
+
|
| 234 |
+
// Click + drag (scrub) on the waveform. Pointer events cover mouse + touch.
|
| 235 |
+
let scrubbing = false;
|
| 236 |
+
let wasPlayingBeforeScrub = false;
|
| 237 |
+
function seekToPointer(e: PointerEvent | MouseEvent) {
|
| 238 |
+
if (!currentAudio) return;
|
| 239 |
+
const rect = waveformCanvas.getBoundingClientRect();
|
| 240 |
+
const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
| 241 |
+
const totalNat = currentAudio.length / 24000;
|
| 242 |
+
// audioEl.currentTime is media-time; our natural-second target matches directly.
|
| 243 |
+
audioEl.currentTime = pct * totalNat;
|
| 244 |
+
// Keep the waveform + timing rows visually in sync even while paused / dragging
|
| 245 |
+
tickHighlight();
|
| 246 |
+
}
|
| 247 |
+
waveformCanvas.addEventListener("pointerdown", (e) => {
|
| 248 |
+
if (!currentAudio) return;
|
| 249 |
+
scrubbing = true;
|
| 250 |
+
wasPlayingBeforeScrub = !audioEl.paused;
|
| 251 |
+
if (wasPlayingBeforeScrub) audioEl.pause();
|
| 252 |
+
waveformCanvas.setPointerCapture(e.pointerId);
|
| 253 |
+
seekToPointer(e);
|
| 254 |
+
});
|
| 255 |
+
waveformCanvas.addEventListener("pointermove", (e) => {
|
| 256 |
+
if (scrubbing) seekToPointer(e);
|
| 257 |
+
});
|
| 258 |
+
function endScrub(e: PointerEvent) {
|
| 259 |
+
if (!scrubbing) return;
|
| 260 |
+
scrubbing = false;
|
| 261 |
+
try { waveformCanvas.releasePointerCapture(e.pointerId); } catch {/* ignore */}
|
| 262 |
+
if (wasPlayingBeforeScrub) audioEl.play().catch(() => {});
|
| 263 |
+
}
|
| 264 |
+
waveformCanvas.addEventListener("pointerup", endScrub);
|
| 265 |
+
waveformCanvas.addEventListener("pointercancel", endScrub);
|
| 266 |
+
|
| 267 |
+
// Space bar toggles play/pause when focus isn't on text input
|
| 268 |
+
document.addEventListener("keydown", (e) => {
|
| 269 |
+
if (e.code !== "Space") return;
|
| 270 |
+
const t = e.target as HTMLElement;
|
| 271 |
+
if (t.tagName === "TEXTAREA" || t.tagName === "INPUT") return;
|
| 272 |
+
e.preventDefault();
|
| 273 |
+
playPauseBtn.click();
|
| 274 |
+
});
|
| 275 |
+
|
| 276 |
+
function fmtTime(sec: number): string {
|
| 277 |
+
if (!isFinite(sec) || sec < 0) sec = 0;
|
| 278 |
+
const m = Math.floor(sec / 60);
|
| 279 |
+
const s = Math.floor(sec % 60);
|
| 280 |
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
// ── Chunk planning for long text ────────────────────────────────────────────
|
| 284 |
+
// Kokoro's PLBERT caps input at 510 phoneme tokens. For longer text we split
|
| 285 |
+
// on sentence boundaries (via Intl.Segmenter — locale-aware, understands
|
| 286 |
+
// Devanagari danda/double-danda as well as Latin .!?), phonemize each sentence,
|
| 287 |
+
// then greedy-pack sentences into chunks under the cap. If a single sentence
|
| 288 |
+
// still exceeds cap, we fall back to per-word greedy packing within that
|
| 289 |
+
// sentence — prosody at the sub-split boundary will be less clean but at
|
| 290 |
+
// least the synthesis runs.
|
| 291 |
+
async function planChunks(
|
| 292 |
+
text: string,
|
| 293 |
+
vocab: KokoroVocab,
|
| 294 |
+
cap: number,
|
| 295 |
+
): Promise<PhonemizedWord[][]> {
|
| 296 |
+
const segmenter = new Intl.Segmenter("mr", { granularity: "sentence" });
|
| 297 |
+
const sentences = [...segmenter.segment(text)]
|
| 298 |
+
.map(s => s.segment.trim())
|
| 299 |
+
.filter(s => s.length > 0);
|
| 300 |
+
|
| 301 |
+
const tokCount = (ws: PhonemizedWord[]) => tokenizeByWord(ws, vocab).inputIds.length;
|
| 302 |
+
|
| 303 |
+
const chunks: PhonemizedWord[][] = [];
|
| 304 |
+
let current: PhonemizedWord[] = [];
|
| 305 |
+
let currentTokens = 0;
|
| 306 |
+
|
| 307 |
+
// tokenizeByWord inserts one boundary-space token BETWEEN each word (not
|
| 308 |
+
// before the first). So `tokCount(sA ++ sB) = tokCount(sA) + tokCount(sB) + 1`
|
| 309 |
+
// when we merge sentences A and B into the same chunk — that +1 is easy to
|
| 310 |
+
// miss and was previously causing near-cap inputs to overflow PLBERT. Account
|
| 311 |
+
// for it explicitly in the packing math.
|
| 312 |
+
const BOUNDARY_TOKEN_COST = 1;
|
| 313 |
+
|
| 314 |
+
for (const s of sentences) {
|
| 315 |
+
const sWords = await phonemizeByWord(s);
|
| 316 |
+
const sTokens = tokCount(sWords);
|
| 317 |
+
|
| 318 |
+
// Skip sentences that phonemize to nothing (whitespace, orphan punctuation,
|
| 319 |
+
// quote marks that espeak drops). Passing 0 tokens to PLBERT crashes at the
|
| 320 |
+
// Expand node with "invalid expand shape".
|
| 321 |
+
if (sTokens === 0) continue;
|
| 322 |
+
|
| 323 |
+
if (sTokens > cap) {
|
| 324 |
+
// Sentence alone exceeds cap — flush current, sub-split by greedy word packing.
|
| 325 |
+
if (current.length) { chunks.push(current); current = []; currentTokens = 0; }
|
| 326 |
+
let sub: PhonemizedWord[] = [];
|
| 327 |
+
for (const w of sWords) {
|
| 328 |
+
const probe = [...sub, w];
|
| 329 |
+
const probeTokens = tokCount(probe);
|
| 330 |
+
if (probeTokens > cap) {
|
| 331 |
+
if (sub.length > 0) {
|
| 332 |
+
chunks.push(sub);
|
| 333 |
+
sub = [w];
|
| 334 |
+
// If this single word is STILL > cap by itself, we can't split further
|
| 335 |
+
// without corrupting phoneme-to-grapheme alignment. Skip with a warning.
|
| 336 |
+
if (tokCount(sub) > cap) {
|
| 337 |
+
console.warn(`dropping oversized word (${tokCount(sub)} tokens > cap ${cap})`, w);
|
| 338 |
+
sub = [];
|
| 339 |
+
}
|
| 340 |
+
} else {
|
| 341 |
+
// sub was already empty; this lone word exceeds cap. Skip.
|
| 342 |
+
console.warn(`dropping oversized word (${probeTokens} tokens > cap ${cap})`, w);
|
| 343 |
+
}
|
| 344 |
+
} else {
|
| 345 |
+
sub = probe;
|
| 346 |
+
}
|
| 347 |
+
}
|
| 348 |
+
if (sub.length) chunks.push(sub);
|
| 349 |
+
continue;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
const boundary = current.length > 0 ? BOUNDARY_TOKEN_COST : 0;
|
| 353 |
+
if (currentTokens + boundary + sTokens > cap) {
|
| 354 |
+
chunks.push(current);
|
| 355 |
+
current = [...sWords];
|
| 356 |
+
currentTokens = sTokens;
|
| 357 |
+
} else {
|
| 358 |
+
current.push(...sWords);
|
| 359 |
+
currentTokens += boundary + sTokens;
|
| 360 |
+
}
|
| 361 |
+
}
|
| 362 |
+
if (current.length) chunks.push(current);
|
| 363 |
+
// Final safety: drop any chunk that ended up with 0 tokens or, defensively,
|
| 364 |
+
// still over cap (belt-and-suspenders against any miscount above).
|
| 365 |
+
return chunks.filter(c => {
|
| 366 |
+
const t = tokCount(c);
|
| 367 |
+
if (t === 0) return false;
|
| 368 |
+
if (t > cap) {
|
| 369 |
+
console.warn(`dropping over-cap chunk (${t} tokens > ${cap}) after packing`, c);
|
| 370 |
+
return false;
|
| 371 |
+
}
|
| 372 |
+
return true;
|
| 373 |
+
});
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
// ── Main action ─────────────────────────────────────────────────────────────
|
| 377 |
+
async function onSynth() {
|
| 378 |
+
if (!session || !config) return;
|
| 379 |
+
const text = textEl.value.trim();
|
| 380 |
+
if (!text) return;
|
| 381 |
+
|
| 382 |
+
try {
|
| 383 |
+
synthBtn.disabled = true;
|
| 384 |
+
setStatus("phonemizing…");
|
| 385 |
+
|
| 386 |
+
const voiceId = voiceEl.value;
|
| 387 |
+
if (!voicepackCache[voiceId]) {
|
| 388 |
+
voicepackCache[voiceId] = await loadVoicepack(voiceId);
|
| 389 |
+
}
|
| 390 |
+
const voicepack = voicepackCache[voiceId];
|
| 391 |
+
|
| 392 |
+
const chunks = await planChunks(text, config.vocab, PLBERT_MAX_TOKENS);
|
| 393 |
+
if (chunks.length === 0) {
|
| 394 |
+
setStatus("no phonemes produced — check input text", "error");
|
| 395 |
+
return;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
const speed = parseFloat(speedEl.value);
|
| 399 |
+
// 100 ms silence between chunks — keeps sentence boundaries audibly
|
| 400 |
+
// separated without introducing a long gap.
|
| 401 |
+
const INTER_CHUNK_PAD_SEC = 0.1;
|
| 402 |
+
|
| 403 |
+
const allAudio: Float32Array[] = [];
|
| 404 |
+
const allWords: WordTiming[] = [];
|
| 405 |
+
const allPhonemes: PhonemeTiming[] = [];
|
| 406 |
+
let totalTokens = 0;
|
| 407 |
+
let audioOffsetSamples = 0;
|
| 408 |
+
let sampleRate = 24000;
|
| 409 |
+
|
| 410 |
+
const t0 = performance.now();
|
| 411 |
+
for (let ci = 0; ci < chunks.length; ci++) {
|
| 412 |
+
const chunk = chunks[ci];
|
| 413 |
+
const tok = tokenizeByWord(chunk, config.vocab);
|
| 414 |
+
if (tok.inputIds.length === 0) {
|
| 415 |
+
console.warn(`skipping empty chunk ${ci + 1}/${chunks.length}`, chunk);
|
| 416 |
+
continue;
|
| 417 |
+
}
|
| 418 |
+
totalTokens += tok.inputIds.length;
|
| 419 |
+
|
| 420 |
+
if (chunks.length > 1) {
|
| 421 |
+
setStatus(`synthesizing chunk ${ci + 1}/${chunks.length} (${tok.inputIds.length} tokens)…`);
|
| 422 |
+
} else {
|
| 423 |
+
setStatus(`synthesizing (${tok.inputIds.length} tokens)…`);
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
// KPipeline convention: ref_s = voicepack[n_phonemes - 1] (voicepack is
|
| 427 |
+
// [510, 1, 256] flat). Per chunk, not per whole text — shorter chunks
|
| 428 |
+
// want the ref_s slice for their own length.
|
| 429 |
+
const idx = Math.min(tok.inputIds.length - 1, 509);
|
| 430 |
+
const refS = voicepack.slice(idx * 256, (idx + 1) * 256);
|
| 431 |
+
|
| 432 |
+
const result = await session.synthesize(tok.inputIds, refS);
|
| 433 |
+
sampleRate = result.sampleRate;
|
| 434 |
+
const PAD_SAMPLES = Math.floor(sampleRate * INTER_CHUNK_PAD_SEC);
|
| 435 |
+
const { phonemes, words } = buildTimings(tok, result.predDur, sampleRate);
|
| 436 |
+
|
| 437 |
+
// Tail-trim NON-final chunks only: ISTFTNet's residual frames would
|
| 438 |
+
// otherwise bleed into the next chunk's silence pad and onset, which
|
| 439 |
+
// sounds gross. The final chunk has nothing after it, so we keep its
|
| 440 |
+
// full decoder output — letting word-final semi-vowels (/j/ in सांगतोय,
|
| 441 |
+
// /w/ in हवा) and aspirated stops complete their natural release. Trim
|
| 442 |
+
// pad on internal chunks is 80 ms, which preserves codas while still
|
| 443 |
+
// dropping the worst of the ghost-vowel "uhh" tail.
|
| 444 |
+
let audio = result.audio;
|
| 445 |
+
const isFinalChunk = ci === chunks.length - 1;
|
| 446 |
+
if (!isFinalChunk && words.length > 0) {
|
| 447 |
+
const cutoff = Math.min(
|
| 448 |
+
audio.length,
|
| 449 |
+
Math.floor((words[words.length - 1].end + 0.08) * sampleRate),
|
| 450 |
+
);
|
| 451 |
+
if (cutoff < audio.length) audio = audio.slice(0, cutoff);
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
// Offset this chunk's timings by cumulative audio start.
|
| 455 |
+
const offsetSec = audioOffsetSamples / sampleRate;
|
| 456 |
+
for (const p of phonemes) { p.start += offsetSec; p.end += offsetSec; }
|
| 457 |
+
for (const w of words) {
|
| 458 |
+
w.start += offsetSec; w.end += offsetSec;
|
| 459 |
+
for (const pp of w.phonemes) { pp.start += offsetSec; pp.end += offsetSec; }
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
allAudio.push(audio);
|
| 463 |
+
audioOffsetSamples += audio.length;
|
| 464 |
+
allPhonemes.push(...phonemes);
|
| 465 |
+
allWords.push(...words);
|
| 466 |
+
|
| 467 |
+
// Silence pad between chunks (not after the last).
|
| 468 |
+
if (ci < chunks.length - 1) {
|
| 469 |
+
allAudio.push(new Float32Array(PAD_SAMPLES));
|
| 470 |
+
audioOffsetSamples += PAD_SAMPLES;
|
| 471 |
+
}
|
| 472 |
+
}
|
| 473 |
+
const t1 = performance.now();
|
| 474 |
+
|
| 475 |
+
// Concat audio into one Float32Array for the WAV encoder + waveform.
|
| 476 |
+
const audio = new Float32Array(audioOffsetSamples);
|
| 477 |
+
{
|
| 478 |
+
let off = 0;
|
| 479 |
+
for (const a of allAudio) { audio.set(a, off); off += a.length; }
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
const wavBlob = float32ToWav(audio, sampleRate);
|
| 483 |
+
// Revoke the previous blob URL before replacing — else each synth leaks
|
| 484 |
+
// ~N bytes of WAV data in the browser's URL table until the tab closes.
|
| 485 |
+
if (currentAudioURL) URL.revokeObjectURL(currentAudioURL);
|
| 486 |
+
currentAudioURL = URL.createObjectURL(wavBlob);
|
| 487 |
+
audioEl.src = currentAudioURL;
|
| 488 |
+
audioEl.preservesPitch = true;
|
| 489 |
+
audioEl.playbackRate = speed;
|
| 490 |
+
|
| 491 |
+
const phonemes = allPhonemes;
|
| 492 |
+
const words = allWords;
|
| 493 |
+
currentWords = words;
|
| 494 |
+
|
| 495 |
+
renderSideBySide(words);
|
| 496 |
+
currentAudio = audio;
|
| 497 |
+
drawWaveform(audio, words, sampleRate);
|
| 498 |
+
|
| 499 |
+
phonemesEl.textContent = phonemes
|
| 500 |
+
.map(p => `${p.phone} ${fmtSec(p.start)}–${fmtSec(p.end)} (${p.durFrames}f)`)
|
| 501 |
+
.join("\n");
|
| 502 |
+
wordTimingsEl.textContent = words
|
| 503 |
+
.map(w => `${w.word.padEnd(14)} ${fmtSec(w.start)}–${fmtSec(w.end)} [${w.ipa}]`)
|
| 504 |
+
.join("\n");
|
| 505 |
+
|
| 506 |
+
outputSection.hidden = false;
|
| 507 |
+
const chunkTag = chunks.length > 1 ? ` · ${chunks.length} chunks` : "";
|
| 508 |
+
setStatus(
|
| 509 |
+
`synth ${(t1 - t0).toFixed(0)}ms · ${(audio.length / sampleRate).toFixed(2)}s audio · ${totalTokens} tokens${chunkTag}`,
|
| 510 |
+
"ready",
|
| 511 |
+
);
|
| 512 |
+
|
| 513 |
+
audioEl.play().catch(() => {/* user may block autoplay; harmless */});
|
| 514 |
+
|
| 515 |
+
} catch (err) {
|
| 516 |
+
console.error(err);
|
| 517 |
+
setStatus(`error: ${(err as Error).message}`, "error");
|
| 518 |
+
} finally {
|
| 519 |
+
synthBtn.disabled = false;
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
// ── Transcript rendering ────────────────────────────────────────────────
|
| 524 |
+
// Two views:
|
| 525 |
+
// 1. Compact (default): one inline Devanagari sentence, per-word spans that
|
| 526 |
+
// highlight during playback. Click/Enter opens the details panel.
|
| 527 |
+
// 2. Details: 3-column Devanagari / IPA / timing table — one row per word.
|
| 528 |
+
// Both get updated live by tickHighlight().
|
| 529 |
+
function renderSideBySide(words: WordTiming[]) {
|
| 530 |
+
// Compact transcript — one span per word for live highlight
|
| 531 |
+
while (compactEl.firstChild) compactEl.removeChild(compactEl.firstChild);
|
| 532 |
+
words.forEach((w, i) => {
|
| 533 |
+
const span = document.createElement("span");
|
| 534 |
+
span.className = "word";
|
| 535 |
+
span.dataset.idx = String(i);
|
| 536 |
+
span.tabIndex = 0;
|
| 537 |
+
span.role = "button";
|
| 538 |
+
span.title = `Seek to ${w.start.toFixed(2)}s (${w.ipa})`;
|
| 539 |
+
span.textContent = w.word;
|
| 540 |
+
compactEl.appendChild(span);
|
| 541 |
+
if (i < words.length - 1) compactEl.appendChild(document.createTextNode(" "));
|
| 542 |
+
});
|
| 543 |
+
|
| 544 |
+
// Details table — clear old rows, keep the 3 col-heads, add one row per word
|
| 545 |
+
const heads = Array.from(transcriptEl.querySelectorAll(".col-head"));
|
| 546 |
+
while (transcriptEl.firstChild) transcriptEl.removeChild(transcriptEl.firstChild);
|
| 547 |
+
heads.forEach((h) => transcriptEl.appendChild(h));
|
| 548 |
+
|
| 549 |
+
words.forEach((w, i) => {
|
| 550 |
+
const row = document.createElement("div");
|
| 551 |
+
row.className = "row";
|
| 552 |
+
row.dataset.idx = String(i);
|
| 553 |
+
|
| 554 |
+
const devanagari = document.createElement("div");
|
| 555 |
+
devanagari.className = "cell devanagari";
|
| 556 |
+
devanagari.textContent = w.word;
|
| 557 |
+
|
| 558 |
+
const ipa = document.createElement("div");
|
| 559 |
+
ipa.className = "cell ipa";
|
| 560 |
+
ipa.textContent = w.ipa;
|
| 561 |
+
|
| 562 |
+
const timing = document.createElement("div");
|
| 563 |
+
timing.className = "cell timing";
|
| 564 |
+
const range = document.createElement("span");
|
| 565 |
+
range.className = "range";
|
| 566 |
+
range.textContent = `${w.start.toFixed(2)}–${w.end.toFixed(2)}s`;
|
| 567 |
+
const bar = document.createElement("div");
|
| 568 |
+
bar.className = "bar";
|
| 569 |
+
const barFill = document.createElement("div");
|
| 570 |
+
barFill.className = "bar-fill";
|
| 571 |
+
barFill.style.transform = "scaleX(0)";
|
| 572 |
+
bar.appendChild(barFill);
|
| 573 |
+
timing.appendChild(range);
|
| 574 |
+
timing.appendChild(bar);
|
| 575 |
+
|
| 576 |
+
row.appendChild(devanagari);
|
| 577 |
+
row.appendChild(ipa);
|
| 578 |
+
row.appendChild(timing);
|
| 579 |
+
transcriptEl.appendChild(row);
|
| 580 |
+
});
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
// ── Waveform drawing ───────────────────────────────────────────────────────
|
| 584 |
+
// Static silhouette of the audio colored by word segments. A playhead is
|
| 585 |
+
// drawn on top in requestAnimationFrame tick during playback.
|
| 586 |
+
function drawWaveform(audio: Float32Array, words: WordTiming[], sampleRate: number) {
|
| 587 |
+
const canvas = waveformCanvas;
|
| 588 |
+
const dpr = window.devicePixelRatio || 1;
|
| 589 |
+
const cssW = canvas.clientWidth || 600;
|
| 590 |
+
const cssH = canvas.clientHeight || 56;
|
| 591 |
+
canvas.width = cssW * dpr;
|
| 592 |
+
canvas.height = cssH * dpr;
|
| 593 |
+
const ctx = canvas.getContext("2d")!;
|
| 594 |
+
ctx.scale(dpr, dpr);
|
| 595 |
+
ctx.clearRect(0, 0, cssW, cssH);
|
| 596 |
+
|
| 597 |
+
const totalDur = audio.length / sampleRate;
|
| 598 |
+
if (totalDur <= 0) return;
|
| 599 |
+
|
| 600 |
+
// Downsample audio into one bin per pixel column
|
| 601 |
+
const cols = Math.floor(cssW);
|
| 602 |
+
const samplesPerCol = Math.max(1, Math.floor(audio.length / cols));
|
| 603 |
+
|
| 604 |
+
// Per-pixel colour based on which word's time range the column belongs to
|
| 605 |
+
const colorForT = (t: number): string => {
|
| 606 |
+
for (const w of words) {
|
| 607 |
+
if (t >= w.start && t <= w.end) {
|
| 608 |
+
// alternate two warm tones for adjacent words so boundaries are visible
|
| 609 |
+
const idx = words.indexOf(w);
|
| 610 |
+
return idx % 2 === 0 ? "#E07A1F" : "#B65F14";
|
| 611 |
+
}
|
| 612 |
+
}
|
| 613 |
+
return "#C4A97A";
|
| 614 |
+
};
|
| 615 |
+
|
| 616 |
+
const mid = cssH / 2;
|
| 617 |
+
ctx.lineWidth = 1;
|
| 618 |
+
for (let x = 0; x < cols; x++) {
|
| 619 |
+
const t = (x / cols) * totalDur;
|
| 620 |
+
let peak = 0;
|
| 621 |
+
const from = x * samplesPerCol;
|
| 622 |
+
const to = Math.min(audio.length, from + samplesPerCol);
|
| 623 |
+
for (let i = from; i < to; i++) {
|
| 624 |
+
const v = Math.abs(audio[i]);
|
| 625 |
+
if (v > peak) peak = v;
|
| 626 |
+
}
|
| 627 |
+
const h = Math.max(1, peak * (cssH * 0.9));
|
| 628 |
+
ctx.strokeStyle = colorForT(t);
|
| 629 |
+
ctx.beginPath();
|
| 630 |
+
ctx.moveTo(x + 0.5, mid - h / 2);
|
| 631 |
+
ctx.lineTo(x + 0.5, mid + h / 2);
|
| 632 |
+
ctx.stroke();
|
| 633 |
+
}
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
// Called each frame during playback to overlay a moving playhead + a dimmed
|
| 637 |
+
// "past" region covering the played portion of the waveform.
|
| 638 |
+
function drawPlayhead(currentSec: number, totalSec: number) {
|
| 639 |
+
if (!currentAudio) return;
|
| 640 |
+
const canvas = waveformCanvas;
|
| 641 |
+
const cssW = canvas.clientWidth || 600;
|
| 642 |
+
const cssH = canvas.clientHeight || 56;
|
| 643 |
+
const ctx = canvas.getContext("2d")!;
|
| 644 |
+
// redraw underlying (cheap — ~600 stroked lines is trivial)
|
| 645 |
+
drawWaveform(currentAudio, currentWords, 24000);
|
| 646 |
+
const x = (currentSec / totalSec) * cssW;
|
| 647 |
+
// Dimmed overlay on the played portion (shows progress visually)
|
| 648 |
+
ctx.save();
|
| 649 |
+
ctx.globalCompositeOperation = "multiply";
|
| 650 |
+
ctx.fillStyle = "#D9C9A7"; // muted tan dims the bright saffron
|
| 651 |
+
ctx.fillRect(0, 0, x, cssH);
|
| 652 |
+
ctx.restore();
|
| 653 |
+
// Thin playhead line at the current position
|
| 654 |
+
ctx.save();
|
| 655 |
+
ctx.strokeStyle = "#8E2E2E";
|
| 656 |
+
ctx.lineWidth = 1.5;
|
| 657 |
+
ctx.beginPath();
|
| 658 |
+
ctx.moveTo(x, 0);
|
| 659 |
+
ctx.lineTo(x, cssH);
|
| 660 |
+
ctx.stroke();
|
| 661 |
+
ctx.restore();
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
// ── Live word highlighting + timing bar fill + waveform playhead ──────────
|
| 665 |
+
// Listeners attached ONCE at module load. The tick function always reads
|
| 666 |
+
// current state from DOM + module state — no stale closure captures when
|
| 667 |
+
// the user re-synthesizes with a different voice.
|
| 668 |
+
function tickHighlight(): void {
|
| 669 |
+
// audioEl.currentTime is media-time (independent of playbackRate), which
|
| 670 |
+
// matches our pred_dur-derived word timings directly.
|
| 671 |
+
const natSec = audioEl.currentTime;
|
| 672 |
+
const totalNatSec = currentAudio ? currentAudio.length / 24000 : 1;
|
| 673 |
+
|
| 674 |
+
let activeIdx = -1;
|
| 675 |
+
for (let i = 0; i < currentWords.length; i++) {
|
| 676 |
+
if (natSec >= currentWords[i].start && natSec <= currentWords[i].end) {
|
| 677 |
+
activeIdx = i;
|
| 678 |
+
break;
|
| 679 |
+
}
|
| 680 |
+
if (natSec > currentWords[i].end) activeIdx = i;
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
// Fresh DOM read every tick — always targets the currently-rendered nodes.
|
| 684 |
+
// Update compact transcript spans (the default-visible highlight).
|
| 685 |
+
compactEl.querySelectorAll<HTMLElement>(".word").forEach((span, i) => {
|
| 686 |
+
span.classList.toggle("active", i === activeIdx);
|
| 687 |
+
span.classList.toggle("past", i < activeIdx);
|
| 688 |
+
});
|
| 689 |
+
// Update detail rows + their timing-bar fills (only visible when details expanded).
|
| 690 |
+
const rows = transcriptEl.querySelectorAll<HTMLElement>(".row");
|
| 691 |
+
rows.forEach((row, i) => {
|
| 692 |
+
row.classList.toggle("active", i === activeIdx);
|
| 693 |
+
row.classList.toggle("past", i < activeIdx);
|
| 694 |
+
const fill = row.querySelector<HTMLElement>(".bar-fill");
|
| 695 |
+
if (!fill) return;
|
| 696 |
+
if (i < activeIdx) {
|
| 697 |
+
fill.style.transform = "scaleX(1)";
|
| 698 |
+
} else if (i === activeIdx) {
|
| 699 |
+
const w = currentWords[i];
|
| 700 |
+
const dur = Math.max(0.001, w.end - w.start);
|
| 701 |
+
const pct = Math.min(1, Math.max(0, (natSec - w.start) / dur));
|
| 702 |
+
fill.style.transform = `scaleX(${pct.toFixed(3)})`;
|
| 703 |
+
} else {
|
| 704 |
+
fill.style.transform = "scaleX(0)";
|
| 705 |
+
}
|
| 706 |
+
});
|
| 707 |
+
|
| 708 |
+
drawPlayhead(natSec, totalNatSec);
|
| 709 |
+
|
| 710 |
+
if (!audioEl.paused && !audioEl.ended) {
|
| 711 |
+
highlightRaf = requestAnimationFrame(tickHighlight);
|
| 712 |
+
} else {
|
| 713 |
+
highlightRaf = null;
|
| 714 |
+
}
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
audioEl.addEventListener("play", () => {
|
| 718 |
+
if (highlightRaf === null) highlightRaf = requestAnimationFrame(tickHighlight);
|
| 719 |
+
});
|
| 720 |
+
audioEl.addEventListener("ended", () => {
|
| 721 |
+
transcriptEl.querySelectorAll<HTMLElement>(".row").forEach((r) => r.classList.remove("active"));
|
| 722 |
+
compactEl.querySelectorAll<HTMLElement>(".word").forEach((s) => s.classList.remove("active"));
|
| 723 |
+
});
|
| 724 |
+
|
| 725 |
+
// Click a word in the compact transcript → seek audio playback to that word's
|
| 726 |
+
// start time. Also clickable in the per-word detail table.
|
| 727 |
+
function seekToWord(idx: number) {
|
| 728 |
+
const w = currentWords[idx];
|
| 729 |
+
if (!w || !currentAudio) return;
|
| 730 |
+
const wasPlaying = !audioEl.paused;
|
| 731 |
+
// Word timings are media-time seconds — assign directly (see loadedmetadata
|
| 732 |
+
// handler comment re: why `/ playbackRate` was removed).
|
| 733 |
+
audioEl.currentTime = w.start;
|
| 734 |
+
tickHighlight(); // update highlight instantly even if paused
|
| 735 |
+
if (!wasPlaying) audioEl.play().catch(() => {});
|
| 736 |
+
}
|
| 737 |
+
compactEl.addEventListener("click", (e) => {
|
| 738 |
+
const t = e.target as HTMLElement;
|
| 739 |
+
if (!t.classList.contains("word")) return;
|
| 740 |
+
const idx = parseInt(t.dataset.idx ?? "-1", 10);
|
| 741 |
+
if (idx >= 0) seekToWord(idx);
|
| 742 |
+
});
|
| 743 |
+
compactEl.addEventListener("keydown", (e) => {
|
| 744 |
+
if (e.key !== "Enter" && e.key !== " ") return;
|
| 745 |
+
const t = e.target as HTMLElement;
|
| 746 |
+
if (!t.classList.contains("word")) return;
|
| 747 |
+
e.preventDefault();
|
| 748 |
+
const idx = parseInt(t.dataset.idx ?? "-1", 10);
|
| 749 |
+
if (idx >= 0) seekToWord(idx);
|
| 750 |
+
});
|
| 751 |
+
// Same seek-on-click for Devanagari cell in the detail table rows
|
| 752 |
+
transcriptEl.addEventListener("click", (e) => {
|
| 753 |
+
const row = (e.target as HTMLElement).closest<HTMLElement>(".row");
|
| 754 |
+
if (!row) return;
|
| 755 |
+
const idx = parseInt(row.dataset.idx ?? "-1", 10);
|
| 756 |
+
if (idx >= 0) seekToWord(idx);
|
| 757 |
+
});
|
| 758 |
+
|
| 759 |
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
| 760 |
+
function fmtSec(s: number): string { return s.toFixed(2) + "s"; }
|
| 761 |
+
|
| 762 |
+
function float32ToWav(samples: Float32Array, sr: number): Blob {
|
| 763 |
+
const buf = new ArrayBuffer(44 + samples.length * 2);
|
| 764 |
+
const view = new DataView(buf);
|
| 765 |
+
writeStr(view, 0, "RIFF");
|
| 766 |
+
view.setUint32(4, 36 + samples.length * 2, true);
|
| 767 |
+
writeStr(view, 8, "WAVE");
|
| 768 |
+
writeStr(view, 12, "fmt ");
|
| 769 |
+
view.setUint32(16, 16, true);
|
| 770 |
+
view.setUint16(20, 1, true);
|
| 771 |
+
view.setUint16(22, 1, true);
|
| 772 |
+
view.setUint32(24, sr, true);
|
| 773 |
+
view.setUint32(28, sr * 2, true);
|
| 774 |
+
view.setUint16(32, 2, true);
|
| 775 |
+
view.setUint16(34, 16, true);
|
| 776 |
+
writeStr(view, 36, "data");
|
| 777 |
+
view.setUint32(40, samples.length * 2, true);
|
| 778 |
+
let off = 44;
|
| 779 |
+
for (let i = 0; i < samples.length; i++, off += 2) {
|
| 780 |
+
const v = Math.max(-1, Math.min(1, samples[i]));
|
| 781 |
+
view.setInt16(off, v < 0 ? v * 0x8000 : v * 0x7fff, true);
|
| 782 |
+
}
|
| 783 |
+
return new Blob([buf], { type: "audio/wav" });
|
| 784 |
+
}
|
| 785 |
+
function writeStr(view: DataView, off: number, s: string) {
|
| 786 |
+
for (let i = 0; i < s.length; i++) view.setUint8(off + i, s.charCodeAt(i));
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
bootLight().catch(err => setStatus(`boot failed: ${err.message}`, "error"));
|
src/model.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { AutoModel, Tensor, env } from "@huggingface/transformers";
|
| 2 |
+
|
| 3 |
+
// Where the ONNX model lives. The AutoModel loader will fetch:
|
| 4 |
+
// onnx/model.onnx + config.json from this repo on HF Hub.
|
| 5 |
+
const MODEL_REPO = "shreyask/bol-tts-marathi-onnx";
|
| 6 |
+
|
| 7 |
+
export interface KokoroVocab { [phone: string]: number; }
|
| 8 |
+
export interface KokoroConfig { vocab: KokoroVocab; n_token: number; }
|
| 9 |
+
|
| 10 |
+
export async function loadConfig(): Promise<KokoroConfig> {
|
| 11 |
+
const res = await fetch("/config.json");
|
| 12 |
+
if (!res.ok) throw new Error(`config.json fetch failed: ${res.status}`);
|
| 13 |
+
return res.json();
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
export async function loadVoicepack(voiceId: string): Promise<Float32Array> {
|
| 17 |
+
const res = await fetch(`/voices/${voiceId}.bin`);
|
| 18 |
+
if (!res.ok) throw new Error(`${voiceId}.bin fetch failed: ${res.status}`);
|
| 19 |
+
const buf = await res.arrayBuffer();
|
| 20 |
+
const f32 = new Float32Array(buf);
|
| 21 |
+
if (f32.length !== 510 * 1 * 256) {
|
| 22 |
+
throw new Error(`voicepack ${voiceId}: expected ${510 * 1 * 256} floats, got ${f32.length}`);
|
| 23 |
+
}
|
| 24 |
+
return f32;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
export interface SynthesizeResult {
|
| 28 |
+
audio: Float32Array;
|
| 29 |
+
predDur: Int32Array;
|
| 30 |
+
sampleRate: number;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
export class KokoroSession {
|
| 34 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 35 |
+
private model: any;
|
| 36 |
+
private isWebGpu: boolean;
|
| 37 |
+
private constructor(model: any, isWebGpu: boolean) { this.model = model; this.isWebGpu = isWebGpu; }
|
| 38 |
+
|
| 39 |
+
get backend(): string { return this.isWebGpu ? "webgpu" : "wasm"; }
|
| 40 |
+
|
| 41 |
+
static async create(onProgress?: (msg: string) => void): Promise<KokoroSession> {
|
| 42 |
+
env.allowRemoteModels = true;
|
| 43 |
+
env.allowLocalModels = false;
|
| 44 |
+
|
| 45 |
+
const hasWebGpu = typeof (navigator as any).gpu !== "undefined";
|
| 46 |
+
const device = hasWebGpu ? "webgpu" : "wasm";
|
| 47 |
+
|
| 48 |
+
// fp32 only — Kokoro's ISTFTNet decoder is sensitive to int8/int4 weight
|
| 49 |
+
// quantization (produces NaN or clipped audio). kokoro.js and HeadTTS both
|
| 50 |
+
// default to fp32 on WebGPU for this reason. Size is the cost of quality.
|
| 51 |
+
onProgress?.(`loading model from HF (${device}, ~325 MB one-time)…`);
|
| 52 |
+
const model = await AutoModel.from_pretrained(MODEL_REPO, {
|
| 53 |
+
dtype: "fp32",
|
| 54 |
+
device,
|
| 55 |
+
progress_callback: (p: any) => {
|
| 56 |
+
if (p.status === "progress" && p.file?.endsWith(".onnx")) {
|
| 57 |
+
const mb = (p.loaded / 1e6).toFixed(1);
|
| 58 |
+
const pct = p.progress ? `${p.progress.toFixed(1)}%` : "";
|
| 59 |
+
onProgress?.(`downloading ${p.file}: ${mb} MB ${pct}`);
|
| 60 |
+
} else if (p.status === "done") {
|
| 61 |
+
onProgress?.(`loaded ${p.file}`);
|
| 62 |
+
}
|
| 63 |
+
},
|
| 64 |
+
} as any);
|
| 65 |
+
return new KokoroSession(model, hasWebGpu);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
async synthesize(inputIds: number[], refS: Float32Array): Promise<SynthesizeResult> {
|
| 69 |
+
const ids = BigInt64Array.from(inputIds.map(v => BigInt(v)));
|
| 70 |
+
const idTensor = new Tensor("int64", ids, [1, ids.length]);
|
| 71 |
+
const refTensor = new Tensor("float32", refS, [1, 256]);
|
| 72 |
+
|
| 73 |
+
const out = await this.model({ input_ids: idTensor, ref_s: refTensor });
|
| 74 |
+
|
| 75 |
+
// transformers.js wraps ort outputs as Tensor objects; unwrap .data
|
| 76 |
+
const audio = out.audio.data as Float32Array;
|
| 77 |
+
|
| 78 |
+
// pred_dur comes back as BigInt64Array from int64 ONNX output — downcast
|
| 79 |
+
const raw = out.pred_dur.data as BigInt64Array;
|
| 80 |
+
const predDur = new Int32Array(raw.length);
|
| 81 |
+
for (let i = 0; i < raw.length; i++) predDur[i] = Number(raw[i]);
|
| 82 |
+
|
| 83 |
+
return { audio, predDur, sampleRate: 24000 };
|
| 84 |
+
}
|
| 85 |
+
}
|
src/phonemize.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Public phonemize API — delegates to the espeak-ng WASM worker.
|
| 3 |
+
*/
|
| 4 |
+
|
| 5 |
+
import { phonemizeByWord as _phonemizeByWord } from "./espeakng/phonemizer";
|
| 6 |
+
|
| 7 |
+
export interface PhonemizedWord { word: string; ipa: string; }
|
| 8 |
+
|
| 9 |
+
export async function phonemizeByWord(text: string): Promise<PhonemizedWord[]> {
|
| 10 |
+
return _phonemizeByWord(text, "mr");
|
| 11 |
+
}
|
src/styles.css
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ─── palette ─────────────────────────────────────────────────────────────
|
| 2 |
+
* Maharashtra Warli-art palette: gerua (geru / गेरू, ochre-red earth pigment
|
| 3 |
+
* traditional to Warli wall paintings) + warm cream + kumkum highlight.
|
| 4 |
+
* Aligned to the SVG header (`warli-strip.svg`) which uses #c34a19 → #9f320f
|
| 5 |
+
* gradient on #fff8ec for the dancers/sun/microphones.
|
| 6 |
+
*
|
| 7 |
+
* Cream paper #FAF3E7
|
| 8 |
+
* Ivory card #F2E8D3
|
| 9 |
+
* Gerua (Warli red-ochre) #C34A19 ← was #E07A1F (too saffron-yellow)
|
| 10 |
+
* Gerua dark #9F320F ← matches SVG gradient endpoint
|
| 11 |
+
* Kumkum (deep red) #8E2E2E
|
| 12 |
+
* Ink brown #2E1D10
|
| 13 |
+
* Muted cocoa #8C6E56
|
| 14 |
+
* Warli white #FFF8EC ← matches SVG #fff8ec
|
| 15 |
+
* Sage (success) #6B8B4E
|
| 16 |
+
*/
|
| 17 |
+
:root {
|
| 18 |
+
--bg: #FAF3E7;
|
| 19 |
+
--card: #F2E8D3;
|
| 20 |
+
--card-border: #D9C9A7;
|
| 21 |
+
--accent: #C34A19; /* gerua — earth-pigment red, authentic Warli */
|
| 22 |
+
--accent-dark: #9F320F; /* deep gerua, matches SVG gradient bottom */
|
| 23 |
+
--accent-soft: #C34A1922;
|
| 24 |
+
--red: #8E2E2E;
|
| 25 |
+
--ink: #2E1D10;
|
| 26 |
+
--muted: #8C6E56;
|
| 27 |
+
--good: #6B8B4E;
|
| 28 |
+
--bad: #A63A3A;
|
| 29 |
+
--warli: #FFF8EC;
|
| 30 |
+
font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
* { box-sizing: border-box; }
|
| 34 |
+
|
| 35 |
+
html, body {
|
| 36 |
+
margin: 0;
|
| 37 |
+
background: var(--bg);
|
| 38 |
+
color: var(--ink);
|
| 39 |
+
min-height: 100vh;
|
| 40 |
+
line-height: 1.5;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
/* ─── typography ──────────────────────────────────────────────────────────
|
| 44 |
+
* Yatra One: festival/display feel for title + ornamentals
|
| 45 |
+
* Tiro Devanagari Marathi: elegant serif for Devanagari body text
|
| 46 |
+
*/
|
| 47 |
+
.font-display { font-family: "Yatra One", "Tiro Devanagari Marathi", serif; }
|
| 48 |
+
.font-devanagari {
|
| 49 |
+
font-family: "Inter", "Tiro Devanagari Marathi", "Noto Sans Devanagari", serif;
|
| 50 |
+
font-weight: 400;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/* ─── warli border strip ─────────────────────────────────────────────────
|
| 54 |
+
* Tribal art band — dancers, hut, deer, microphones, sun motifs in white on
|
| 55 |
+
* saffron. Originals are 2172×154 PNG; we display at fixed pixel height and
|
| 56 |
+
* stretch horizontally to the viewport (object-fit: cover).
|
| 57 |
+
*/
|
| 58 |
+
.warli-strip {
|
| 59 |
+
width: 100%;
|
| 60 |
+
margin-top: 24px;
|
| 61 |
+
display: flex;
|
| 62 |
+
justify-content: center;
|
| 63 |
+
overflow: hidden;
|
| 64 |
+
}
|
| 65 |
+
.warli-strip img {
|
| 66 |
+
/* Natural aspect (2172×154 ≈ 14:1). width:100% fills the viewport up to
|
| 67 |
+
* max-width; past that the strip caps and cream gutters appear on either
|
| 68 |
+
* side. No object-fit cropping, no distortion, triangle borders always
|
| 69 |
+
* visible. */
|
| 70 |
+
width: 100%;
|
| 71 |
+
height: auto;
|
| 72 |
+
max-width: 1600px;
|
| 73 |
+
display: block;
|
| 74 |
+
}
|
| 75 |
+
.warli-strip--bottom {
|
| 76 |
+
margin-top: 24px;
|
| 77 |
+
/* No rotation — the SVG already has triangle borders on BOTH the top and
|
| 78 |
+
* bottom edges of the strip, so the same image works for both header and
|
| 79 |
+
* footer. Rotating 180° would have flipped figures upside-down. */
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/* ─── layout ──────────────────────────────────────────────────────────── */
|
| 83 |
+
main {
|
| 84 |
+
max-width: 860px;
|
| 85 |
+
margin: 0 auto;
|
| 86 |
+
padding: 40px 24px 80px;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
header {
|
| 90 |
+
text-align: center;
|
| 91 |
+
margin-bottom: 32px;
|
| 92 |
+
}
|
| 93 |
+
header h1 {
|
| 94 |
+
font-family: "Yatra One", "Tiro Devanagari Marathi", serif;
|
| 95 |
+
font-size: 38px;
|
| 96 |
+
margin: 0 0 6px;
|
| 97 |
+
color: var(--red);
|
| 98 |
+
letter-spacing: 0;
|
| 99 |
+
}
|
| 100 |
+
header .sub {
|
| 101 |
+
margin: 0;
|
| 102 |
+
color: var(--muted);
|
| 103 |
+
font-size: 14px;
|
| 104 |
+
}
|
| 105 |
+
header .namaste {
|
| 106 |
+
font-family: "Tiro Devanagari Marathi", serif;
|
| 107 |
+
font-size: 18px;
|
| 108 |
+
color: var(--accent-dark);
|
| 109 |
+
margin-bottom: 8px;
|
| 110 |
+
display: block;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.card {
|
| 114 |
+
background: var(--card);
|
| 115 |
+
border: 1px solid var(--card-border);
|
| 116 |
+
border-radius: 14px;
|
| 117 |
+
padding: 18px 20px;
|
| 118 |
+
margin-bottom: 16px;
|
| 119 |
+
box-shadow: 0 1px 2px rgba(46, 29, 16, 0.04);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.status {
|
| 123 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 124 |
+
font-size: 13px;
|
| 125 |
+
color: var(--muted);
|
| 126 |
+
padding: 10px 14px;
|
| 127 |
+
}
|
| 128 |
+
.status.ready { color: var(--good); }
|
| 129 |
+
.status.error { color: var(--bad); }
|
| 130 |
+
|
| 131 |
+
.model-gate {
|
| 132 |
+
display: flex;
|
| 133 |
+
align-items: center;
|
| 134 |
+
justify-content: space-between;
|
| 135 |
+
gap: 16px;
|
| 136 |
+
border-left: 3px solid var(--accent);
|
| 137 |
+
}
|
| 138 |
+
.model-gate-text { font-size: 14px; color: var(--ink); line-height: 1.7; }
|
| 139 |
+
.model-gate-text strong { color: var(--red); }
|
| 140 |
+
.model-gate-sub { color: var(--muted); font-size: 12px; display: block; margin-top: 4px; }
|
| 141 |
+
.model-gate-variant {
|
| 142 |
+
display: block;
|
| 143 |
+
font-size: 13px;
|
| 144 |
+
cursor: pointer;
|
| 145 |
+
padding: 2px 0;
|
| 146 |
+
}
|
| 147 |
+
.model-gate-variant input[type=radio] { accent-color: var(--accent); margin-right: 6px; }
|
| 148 |
+
.model-gate button { flex-shrink: 0; }
|
| 149 |
+
|
| 150 |
+
label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
|
| 151 |
+
|
| 152 |
+
/* Row that puts the textarea label and the Transliterate utility button on
|
| 153 |
+
the same line, label flush left and button flush right. */
|
| 154 |
+
.text-label-row {
|
| 155 |
+
display: flex;
|
| 156 |
+
align-items: baseline;
|
| 157 |
+
justify-content: space-between;
|
| 158 |
+
gap: 12px;
|
| 159 |
+
margin-bottom: 6px;
|
| 160 |
+
}
|
| 161 |
+
.text-label-row label { margin: 0; }
|
| 162 |
+
.text-tool {
|
| 163 |
+
background: transparent;
|
| 164 |
+
color: var(--accent-dark);
|
| 165 |
+
border: 1px solid var(--card-border);
|
| 166 |
+
border-radius: 999px;
|
| 167 |
+
padding: 3px 10px;
|
| 168 |
+
font-size: 12px;
|
| 169 |
+
font-family: "Inter", "Tiro Devanagari Marathi", sans-serif;
|
| 170 |
+
cursor: pointer;
|
| 171 |
+
transition: background 120ms, color 120ms, border-color 120ms;
|
| 172 |
+
}
|
| 173 |
+
.text-tool:hover {
|
| 174 |
+
background: var(--accent-soft);
|
| 175 |
+
border-color: var(--accent);
|
| 176 |
+
color: var(--accent-dark);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
textarea {
|
| 180 |
+
width: 100%;
|
| 181 |
+
background: var(--warli);
|
| 182 |
+
color: var(--ink);
|
| 183 |
+
border: 1px solid var(--card-border);
|
| 184 |
+
border-radius: 10px;
|
| 185 |
+
padding: 12px 14px;
|
| 186 |
+
font-size: 19px;
|
| 187 |
+
font-family: "Inter", "Tiro Devanagari Marathi", "Noto Sans Devanagari", serif;
|
| 188 |
+
line-height: 1.55;
|
| 189 |
+
resize: vertical;
|
| 190 |
+
}
|
| 191 |
+
textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
| 192 |
+
|
| 193 |
+
.examples { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 6px; align-items: center; }
|
| 194 |
+
.examples-label { color: var(--muted); font-size: 12px; margin-right: 4px; }
|
| 195 |
+
.chip {
|
| 196 |
+
background: transparent;
|
| 197 |
+
color: var(--ink);
|
| 198 |
+
border: 1px solid var(--card-border);
|
| 199 |
+
border-radius: 999px;
|
| 200 |
+
padding: 4px 10px;
|
| 201 |
+
font-size: 13px;
|
| 202 |
+
font-family: "Inter", "Tiro Devanagari Marathi", "Noto Sans Devanagari", serif;
|
| 203 |
+
cursor: pointer;
|
| 204 |
+
transition: background 120ms, color 120ms, border-color 120ms;
|
| 205 |
+
}
|
| 206 |
+
.chip:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-dark); }
|
| 207 |
+
|
| 208 |
+
.controls {
|
| 209 |
+
display: grid;
|
| 210 |
+
grid-template-columns: 1fr 1fr auto;
|
| 211 |
+
gap: 14px;
|
| 212 |
+
align-items: end;
|
| 213 |
+
margin-top: 14px;
|
| 214 |
+
}
|
| 215 |
+
.controls select, .controls input[type=range] {
|
| 216 |
+
width: 100%;
|
| 217 |
+
background: var(--warli);
|
| 218 |
+
color: var(--ink);
|
| 219 |
+
border: 1px solid var(--card-border);
|
| 220 |
+
border-radius: 8px;
|
| 221 |
+
padding: 7px 10px;
|
| 222 |
+
}
|
| 223 |
+
.controls input[type=range] {
|
| 224 |
+
accent-color: var(--accent);
|
| 225 |
+
padding: 4px 0;
|
| 226 |
+
}
|
| 227 |
+
.speed-control { margin-bottom: 0; }
|
| 228 |
+
.speed-control-row {
|
| 229 |
+
display: flex;
|
| 230 |
+
justify-content: space-between;
|
| 231 |
+
align-items: baseline;
|
| 232 |
+
margin-bottom: 6px;
|
| 233 |
+
font-size: 13px;
|
| 234 |
+
color: var(--muted);
|
| 235 |
+
}
|
| 236 |
+
.speed-control-row #speed-val {
|
| 237 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 238 |
+
font-size: 12px;
|
| 239 |
+
color: var(--accent-dark);
|
| 240 |
+
font-variant-numeric: tabular-nums;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
button.primary {
|
| 244 |
+
background: var(--accent);
|
| 245 |
+
color: #fff;
|
| 246 |
+
border: none;
|
| 247 |
+
border-radius: 8px;
|
| 248 |
+
padding: 9px 20px;
|
| 249 |
+
font-weight: 600;
|
| 250 |
+
cursor: pointer;
|
| 251 |
+
font-size: 14px;
|
| 252 |
+
font-family: "Inter", "Tiro Devanagari Marathi", sans-serif;
|
| 253 |
+
}
|
| 254 |
+
button.primary:hover { background: var(--accent-dark); }
|
| 255 |
+
button.primary:disabled { opacity: 0.45; cursor: default; }
|
| 256 |
+
|
| 257 |
+
.ab-test { margin-top: 10px; }
|
| 258 |
+
|
| 259 |
+
/* ─── audio output area ──────────────────────────────────────────────── */
|
| 260 |
+
/* ─── fused player: play/pause button + seekable waveform + time ─────── */
|
| 261 |
+
.player {
|
| 262 |
+
display: grid;
|
| 263 |
+
grid-template-columns: 48px 1fr;
|
| 264 |
+
gap: 12px;
|
| 265 |
+
align-items: center;
|
| 266 |
+
background: var(--warli);
|
| 267 |
+
border: 1px solid var(--card-border);
|
| 268 |
+
border-radius: 10px;
|
| 269 |
+
padding: 10px 12px;
|
| 270 |
+
margin-bottom: 14px;
|
| 271 |
+
}
|
| 272 |
+
.playpause {
|
| 273 |
+
width: 44px;
|
| 274 |
+
height: 44px;
|
| 275 |
+
border-radius: 50%;
|
| 276 |
+
background: var(--accent);
|
| 277 |
+
color: #fff;
|
| 278 |
+
border: none;
|
| 279 |
+
font-size: 18px;
|
| 280 |
+
cursor: pointer;
|
| 281 |
+
display: flex;
|
| 282 |
+
align-items: center;
|
| 283 |
+
justify-content: center;
|
| 284 |
+
line-height: 1;
|
| 285 |
+
padding: 0;
|
| 286 |
+
transition: background 120ms;
|
| 287 |
+
}
|
| 288 |
+
.playpause:hover { background: var(--accent-dark); }
|
| 289 |
+
.playpause.playing { font-size: 16px; }
|
| 290 |
+
|
| 291 |
+
.player-main {
|
| 292 |
+
position: relative;
|
| 293 |
+
display: flex;
|
| 294 |
+
flex-direction: column;
|
| 295 |
+
gap: 2px;
|
| 296 |
+
}
|
| 297 |
+
.waveform {
|
| 298 |
+
width: 100%;
|
| 299 |
+
height: 56px;
|
| 300 |
+
background: transparent;
|
| 301 |
+
cursor: pointer;
|
| 302 |
+
display: block;
|
| 303 |
+
}
|
| 304 |
+
.waveform:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-radius: 4px; }
|
| 305 |
+
.player-time {
|
| 306 |
+
display: flex;
|
| 307 |
+
justify-content: space-between;
|
| 308 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 309 |
+
font-size: 11px;
|
| 310 |
+
color: var(--muted);
|
| 311 |
+
font-feature-settings: "tnum";
|
| 312 |
+
padding: 0 2px;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
/* ─── compact transcript (default view) — Marathi text with per-word hl ── */
|
| 316 |
+
.transcript-compact {
|
| 317 |
+
font-family: "Inter", "Tiro Devanagari Marathi", "Noto Sans Devanagari", serif;
|
| 318 |
+
font-size: 24px;
|
| 319 |
+
line-height: 1.7;
|
| 320 |
+
padding: 14px 16px;
|
| 321 |
+
border: 1px solid var(--card-border);
|
| 322 |
+
border-radius: 10px;
|
| 323 |
+
background: var(--warli);
|
| 324 |
+
color: var(--ink);
|
| 325 |
+
min-height: 60px;
|
| 326 |
+
}
|
| 327 |
+
.transcript-compact .word {
|
| 328 |
+
display: inline-block;
|
| 329 |
+
padding: 1px 4px;
|
| 330 |
+
border-radius: 4px;
|
| 331 |
+
cursor: pointer;
|
| 332 |
+
transition: background-color 150ms ease, color 150ms ease;
|
| 333 |
+
}
|
| 334 |
+
.transcript-compact .word:hover { background: var(--accent-soft); }
|
| 335 |
+
.transcript-compact .word:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
| 336 |
+
.transcript-compact .word.active {
|
| 337 |
+
background: var(--accent);
|
| 338 |
+
color: #fff;
|
| 339 |
+
}
|
| 340 |
+
.transcript-compact .word.past { color: var(--muted); }
|
| 341 |
+
|
| 342 |
+
/* ─── expandable per-word detail (IPA + timing) ───────────────────────── */
|
| 343 |
+
.transcript-details {
|
| 344 |
+
margin-top: 10px;
|
| 345 |
+
}
|
| 346 |
+
.transcript-details > summary {
|
| 347 |
+
cursor: pointer;
|
| 348 |
+
color: var(--muted);
|
| 349 |
+
font-size: 12px;
|
| 350 |
+
padding: 4px 6px;
|
| 351 |
+
user-select: none;
|
| 352 |
+
}
|
| 353 |
+
.transcript-details > summary:hover { color: var(--accent-dark); }
|
| 354 |
+
.transcript-details[open] > summary { margin-bottom: 8px; }
|
| 355 |
+
|
| 356 |
+
/* ─── side-by-side Devanagari / IPA / timing columns ─────────────────── */
|
| 357 |
+
.sidebyside {
|
| 358 |
+
display: grid;
|
| 359 |
+
grid-template-columns: minmax(140px, 1.2fr) minmax(160px, 1.8fr) 2fr;
|
| 360 |
+
gap: 0;
|
| 361 |
+
border: 1px solid var(--card-border);
|
| 362 |
+
border-radius: 10px;
|
| 363 |
+
overflow: hidden;
|
| 364 |
+
background: var(--warli);
|
| 365 |
+
}
|
| 366 |
+
.sidebyside .col-head {
|
| 367 |
+
font-family: "Yatra One", serif;
|
| 368 |
+
font-size: 13px;
|
| 369 |
+
color: var(--red);
|
| 370 |
+
padding: 8px 12px;
|
| 371 |
+
border-bottom: 1px solid var(--card-border);
|
| 372 |
+
background: #EFE2C5;
|
| 373 |
+
letter-spacing: 0.02em;
|
| 374 |
+
}
|
| 375 |
+
.sidebyside .row {
|
| 376 |
+
display: contents;
|
| 377 |
+
}
|
| 378 |
+
.sidebyside .cell {
|
| 379 |
+
padding: 10px 12px;
|
| 380 |
+
border-bottom: 1px solid #E8DBBF;
|
| 381 |
+
display: flex;
|
| 382 |
+
align-items: center;
|
| 383 |
+
transition: background-color 180ms ease;
|
| 384 |
+
}
|
| 385 |
+
.sidebyside .row.active .cell { background: var(--accent-soft); }
|
| 386 |
+
.sidebyside .row.past .cell { color: var(--muted); }
|
| 387 |
+
.sidebyside .row:last-child .cell { border-bottom: none; }
|
| 388 |
+
|
| 389 |
+
.cell.devanagari {
|
| 390 |
+
font-family: "Inter", "Tiro Devanagari Marathi", "Noto Sans Devanagari", serif;
|
| 391 |
+
font-size: 22px;
|
| 392 |
+
font-weight: 500;
|
| 393 |
+
color: var(--ink);
|
| 394 |
+
}
|
| 395 |
+
.sidebyside .row.active .cell.devanagari { color: var(--red); }
|
| 396 |
+
|
| 397 |
+
.cell.ipa {
|
| 398 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 399 |
+
font-size: 14px;
|
| 400 |
+
color: var(--accent-dark);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
.cell.timing {
|
| 404 |
+
position: relative;
|
| 405 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 406 |
+
font-size: 12px;
|
| 407 |
+
color: var(--muted);
|
| 408 |
+
padding: 10px 12px;
|
| 409 |
+
}
|
| 410 |
+
.cell.timing .bar {
|
| 411 |
+
position: absolute;
|
| 412 |
+
left: 10px;
|
| 413 |
+
right: 10px;
|
| 414 |
+
bottom: 6px;
|
| 415 |
+
height: 4px;
|
| 416 |
+
background: #E8DBBF;
|
| 417 |
+
border-radius: 2px;
|
| 418 |
+
overflow: hidden;
|
| 419 |
+
}
|
| 420 |
+
.cell.timing .bar-fill {
|
| 421 |
+
height: 100%;
|
| 422 |
+
background: var(--accent);
|
| 423 |
+
border-radius: 2px;
|
| 424 |
+
transform-origin: left center;
|
| 425 |
+
transition: transform 80ms linear;
|
| 426 |
+
}
|
| 427 |
+
.sidebyside .row.past .cell.timing .bar-fill {
|
| 428 |
+
background: var(--muted);
|
| 429 |
+
transform: scaleX(1);
|
| 430 |
+
}
|
| 431 |
+
.cell.timing .range {
|
| 432 |
+
font-feature-settings: "tnum";
|
| 433 |
+
margin-right: 10px;
|
| 434 |
+
}
|
| 435 |
+
|
| 436 |
+
/* ─── Marathi facts sidebar ──────────────────────────────────────────── */
|
| 437 |
+
.facts {
|
| 438 |
+
background: #EFE2C5;
|
| 439 |
+
border-left: 3px solid var(--accent);
|
| 440 |
+
padding: 10px 14px;
|
| 441 |
+
border-radius: 6px;
|
| 442 |
+
font-size: 13px;
|
| 443 |
+
color: var(--ink);
|
| 444 |
+
margin-top: 14px;
|
| 445 |
+
line-height: 1.55;
|
| 446 |
+
}
|
| 447 |
+
.facts strong { color: var(--red); font-weight: 600; }
|
| 448 |
+
.facts .ipa-inline {
|
| 449 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 450 |
+
color: var(--accent-dark);
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
/* ─── misc ───────────────────────────────────────────────────────────── */
|
| 454 |
+
.details { margin-top: 12px; }
|
| 455 |
+
.details summary { cursor: pointer; color: var(--muted); font-size: 12px; }
|
| 456 |
+
.phonemes, .word-timings {
|
| 457 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 458 |
+
font-size: 12px;
|
| 459 |
+
color: var(--muted);
|
| 460 |
+
white-space: pre-wrap;
|
| 461 |
+
margin: 8px 0 0;
|
| 462 |
+
padding: 10px;
|
| 463 |
+
background: #EAE0C5;
|
| 464 |
+
border-radius: 6px;
|
| 465 |
+
max-height: 220px;
|
| 466 |
+
overflow: auto;
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
footer {
|
| 470 |
+
margin-top: 40px;
|
| 471 |
+
color: var(--muted);
|
| 472 |
+
font-size: 12px;
|
| 473 |
+
text-align: center;
|
| 474 |
+
}
|
| 475 |
+
footer a,
|
| 476 |
+
header p a { color: var(--accent-dark); text-decoration: underline; }
|
| 477 |
+
footer a:hover,
|
| 478 |
+
header p a:hover { color: var(--red); }
|
src/timestamps.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { TokenizeResult } from "./tokenize";
|
| 2 |
+
|
| 3 |
+
// pred_dur is in PREDICTOR frames, not mel frames. Empirically:
|
| 4 |
+
// audio.length / pred_dur.sum === 600 (not 300 as you'd guess from mel hop).
|
| 5 |
+
// The prosody predictor runs at half the mel-frame rate; the decoder
|
| 6 |
+
// upsamples 2× internally before iSTFT. So frame-to-seconds uses hop=600.
|
| 7 |
+
const HOP = 600;
|
| 8 |
+
const DEFAULT_SR = 24000;
|
| 9 |
+
|
| 10 |
+
export interface PhonemeTiming {
|
| 11 |
+
phone: string;
|
| 12 |
+
start: number; // seconds
|
| 13 |
+
end: number;
|
| 14 |
+
durFrames: number;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export interface WordTiming {
|
| 18 |
+
word: string;
|
| 19 |
+
ipa: string;
|
| 20 |
+
start: number; // seconds
|
| 21 |
+
end: number;
|
| 22 |
+
phonemes: PhonemeTiming[];
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Turn pred_dur (per-phoneme duration in frames) + the per-word index ranges
|
| 27 |
+
* from tokenize.ts into phoneme + word level timestamps in seconds.
|
| 28 |
+
*
|
| 29 |
+
* Kokoro's prosody predictor outputs durations in *predictor frames* where
|
| 30 |
+
* 1 frame = 600 audio samples at 24 kHz (25 ms/frame). We accumulate and
|
| 31 |
+
* attribute to the originating word via the tokenStart/tokenEnd ranges that
|
| 32 |
+
* tokenizeByWord wrote down.
|
| 33 |
+
*/
|
| 34 |
+
export function buildTimings(
|
| 35 |
+
tokenizeResult: TokenizeResult,
|
| 36 |
+
predDur: Int32Array | number[],
|
| 37 |
+
sampleRate: number = DEFAULT_SR,
|
| 38 |
+
): { phonemes: PhonemeTiming[]; words: WordTiming[] } {
|
| 39 |
+
const durArr: number[] = Array.from(predDur as Iterable<number>);
|
| 40 |
+
|
| 41 |
+
if (durArr.length !== tokenizeResult.inputIds.length) {
|
| 42 |
+
// Kokoro sometimes returns pred_dur aligned to internal sequence length
|
| 43 |
+
// (post-padding, post-space-injection). We expect them to match; if they
|
| 44 |
+
// don't, warn and continue with the shorter of the two so we don't crash.
|
| 45 |
+
console.warn(
|
| 46 |
+
`pred_dur length (${durArr.length}) != input_ids length (${tokenizeResult.inputIds.length}) — truncating`,
|
| 47 |
+
);
|
| 48 |
+
}
|
| 49 |
+
const n = Math.min(durArr.length, tokenizeResult.inputIds.length);
|
| 50 |
+
|
| 51 |
+
// Phoneme-level
|
| 52 |
+
const phonemes: PhonemeTiming[] = [];
|
| 53 |
+
let cumFrames = 0;
|
| 54 |
+
for (let i = 0; i < n; i++) {
|
| 55 |
+
const df = durArr[i];
|
| 56 |
+
const start = (cumFrames * HOP) / sampleRate;
|
| 57 |
+
cumFrames += df;
|
| 58 |
+
const end = (cumFrames * HOP) / sampleRate;
|
| 59 |
+
phonemes.push({
|
| 60 |
+
phone: tokenizeResult.phonemes[i] ?? "?",
|
| 61 |
+
start,
|
| 62 |
+
end,
|
| 63 |
+
durFrames: df,
|
| 64 |
+
});
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
// Word-level: for each word, take start of first in-range phoneme and end of last
|
| 68 |
+
const words: WordTiming[] = [];
|
| 69 |
+
for (const w of tokenizeResult.words) {
|
| 70 |
+
const slice = phonemes.slice(w.tokenStart, Math.min(w.tokenEnd, phonemes.length));
|
| 71 |
+
if (slice.length === 0) continue;
|
| 72 |
+
words.push({
|
| 73 |
+
word: w.word,
|
| 74 |
+
ipa: w.ipa,
|
| 75 |
+
start: slice[0].start,
|
| 76 |
+
end: slice[slice.length - 1].end,
|
| 77 |
+
phonemes: slice,
|
| 78 |
+
});
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
return { phonemes, words };
|
| 82 |
+
}
|
src/tokenize.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { KokoroVocab } from "./model";
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Result of turning text → model input, with word-level bookkeeping so we can
|
| 5 |
+
* align per-phoneme `pred_dur` back to words for timestamps.
|
| 6 |
+
*/
|
| 7 |
+
export interface TokenizeResult {
|
| 8 |
+
inputIds: number[]; // token IDs for KModel
|
| 9 |
+
phonemes: string[]; // flat phoneme sequence (each ID's char)
|
| 10 |
+
words: Array<{
|
| 11 |
+
word: string; // Devanagari source
|
| 12 |
+
ipa: string; // raw espeak-ng output for this word
|
| 13 |
+
tokenStart: number; // index into inputIds for first kept phoneme
|
| 14 |
+
tokenEnd: number; // exclusive
|
| 15 |
+
}>;
|
| 16 |
+
droppedChars: Record<string, number>; // chars not in vocab, counts for diagnostics
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
/**
|
| 20 |
+
* Convert per-word IPA (from phonemize.ts) into model-ready input_ids, keeping
|
| 21 |
+
* track of which input_ids came from which word for downstream timestamping.
|
| 22 |
+
*
|
| 23 |
+
* Characters not in `vocab` are silently dropped — this mirrors KModel.forward's
|
| 24 |
+
* own tokenization (`input_ids = filter(lambda i: i is not None, map(vocab.get, phonemes))`).
|
| 25 |
+
*/
|
| 26 |
+
export function tokenizeByWord(
|
| 27 |
+
wordIpas: Array<{ word: string; ipa: string }>,
|
| 28 |
+
vocab: KokoroVocab,
|
| 29 |
+
): TokenizeResult {
|
| 30 |
+
const inputIds: number[] = [];
|
| 31 |
+
const phonemes: string[] = [];
|
| 32 |
+
const words: TokenizeResult["words"] = [];
|
| 33 |
+
const droppedChars: Record<string, number> = {};
|
| 34 |
+
|
| 35 |
+
const spaceId = vocab[" "];
|
| 36 |
+
for (let wi = 0; wi < wordIpas.length; wi++) {
|
| 37 |
+
const { word, ipa } = wordIpas[wi];
|
| 38 |
+
// Insert word-boundary space BEFORE every word except the first.
|
| 39 |
+
// (Trailing-space-after-last-word tends to get stripped by the predictor,
|
| 40 |
+
// which throws off pred_dur ↔ input_ids alignment and drops the last
|
| 41 |
+
// word from our timestamps. Leading-space avoids the issue.)
|
| 42 |
+
if (wi > 0 && spaceId !== undefined) {
|
| 43 |
+
inputIds.push(spaceId);
|
| 44 |
+
phonemes.push(" ");
|
| 45 |
+
}
|
| 46 |
+
const tokenStart = inputIds.length;
|
| 47 |
+
for (const c of ipa) {
|
| 48 |
+
const id = vocab[c];
|
| 49 |
+
if (id === undefined) {
|
| 50 |
+
droppedChars[c] = (droppedChars[c] ?? 0) + 1;
|
| 51 |
+
} else {
|
| 52 |
+
inputIds.push(id);
|
| 53 |
+
phonemes.push(c);
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
const tokenEnd = inputIds.length;
|
| 57 |
+
words.push({ word, ipa, tokenStart, tokenEnd });
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
return { inputIds, phonemes, words, droppedChars };
|
| 61 |
+
}
|
tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"module": "ESNext",
|
| 5 |
+
"moduleResolution": "Bundler",
|
| 6 |
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
| 7 |
+
"strict": true,
|
| 8 |
+
"noImplicitAny": true,
|
| 9 |
+
"noUnusedLocals": true,
|
| 10 |
+
"esModuleInterop": true,
|
| 11 |
+
"allowSyntheticDefaultImports": true,
|
| 12 |
+
"forceConsistentCasingInFileNames": true,
|
| 13 |
+
"isolatedModules": true,
|
| 14 |
+
"skipLibCheck": true,
|
| 15 |
+
"noEmit": true,
|
| 16 |
+
"resolveJsonModule": true,
|
| 17 |
+
"types": ["vite/client"]
|
| 18 |
+
},
|
| 19 |
+
"include": ["src"]
|
| 20 |
+
}
|
vite.config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
|
| 3 |
+
export default defineConfig({
|
| 4 |
+
// onnxruntime-web ships .wasm files that must be served as static assets.
|
| 5 |
+
// Vite's default public/ dir handles them, but the package's internal wasm
|
| 6 |
+
// paths need to be resolved correctly when bundling.
|
| 7 |
+
optimizeDeps: {
|
| 8 |
+
exclude: ["onnxruntime-web"],
|
| 9 |
+
},
|
| 10 |
+
server: {
|
| 11 |
+
port: 5173,
|
| 12 |
+
// Required for SharedArrayBuffer (onnxruntime-web multithreading uses it)
|
| 13 |
+
headers: {
|
| 14 |
+
"Cross-Origin-Opener-Policy": "same-origin",
|
| 15 |
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
| 16 |
+
},
|
| 17 |
+
},
|
| 18 |
+
preview: {
|
| 19 |
+
headers: {
|
| 20 |
+
"Cross-Origin-Opener-Policy": "same-origin",
|
| 21 |
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
| 22 |
+
},
|
| 23 |
+
},
|
| 24 |
+
});
|
voicepacks.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema_version": 1,
|
| 3 |
+
"_comment": "Source of truth for the bol-tts-marathi WebGPU demo's voice list + per-voice metadata + test phrases. Edit this to add/remove voices or update sample chips — main.ts consumes it dynamically on page load.",
|
| 4 |
+
"voices": [
|
| 5 |
+
{
|
| 6 |
+
"id": "mf_asha",
|
| 7 |
+
"name": "Asha",
|
| 8 |
+
"name_native": "आशा",
|
| 9 |
+
"gender": "female",
|
| 10 |
+
"source": "rasa",
|
| 11 |
+
"default_speed": 1.0,
|
| 12 |
+
"ship_status": "stable"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": "mm_vivek",
|
| 16 |
+
"name": "Vivek",
|
| 17 |
+
"name_native": "विवेक",
|
| 18 |
+
"gender": "male",
|
| 19 |
+
"source": "rasa",
|
| 20 |
+
"default_speed": 1.0,
|
| 21 |
+
"ship_status": "stable"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"id": "mf_mukta",
|
| 25 |
+
"name": "Mukta",
|
| 26 |
+
"name_native": "मुक्ता",
|
| 27 |
+
"gender": "female",
|
| 28 |
+
"source": "indicvoices_r",
|
| 29 |
+
"default_speed": 1.0,
|
| 30 |
+
"ship_status": "stable"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": "mm_dnyanesh",
|
| 34 |
+
"name": "Dnyanesh",
|
| 35 |
+
"name_native": "ज्ञानेश",
|
| 36 |
+
"gender": "male",
|
| 37 |
+
"source": "indicvoices_r",
|
| 38 |
+
"default_speed": 1.0,
|
| 39 |
+
"ship_status": "stable"
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"id": "mf_priya",
|
| 43 |
+
"name": "Priya",
|
| 44 |
+
"name_native": "प्रिया",
|
| 45 |
+
"gender": "female",
|
| 46 |
+
"source": "springlab",
|
| 47 |
+
"default_speed": 1.0,
|
| 48 |
+
"ship_status": "v0.2_preview"
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"id": "mm_arjun",
|
| 52 |
+
"name": "Arjun",
|
| 53 |
+
"name_native": "अर्जुन",
|
| 54 |
+
"gender": "male",
|
| 55 |
+
"source": "springlab",
|
| 56 |
+
"default_speed": 1.0,
|
| 57 |
+
"ship_status": "v0.2_preview"
|
| 58 |
+
}
|
| 59 |
+
],
|
| 60 |
+
"test_phrases": {
|
| 61 |
+
"marathi_neutral": [
|
| 62 |
+
{ "label": "हवामान", "text": "आज हवामान खूप छान आहे, पाऊस पडतो आहे." },
|
| 63 |
+
{ "label": "महाराष्ट्र", "text": "महाराष्ट्र हे भारतातील एक महत्त्वाचे राज्य आहे." },
|
| 64 |
+
{ "label": "मुंबई", "text": "मुंबई ही महाराष्ट्राची राजधानी आणि भारताची आर्थिक राजधानी आहे." },
|
| 65 |
+
{ "label": "पुणे", "text": "पुणे ही महाराष्ट्राची सांस्कृतिक राजधानी मानली जाते." },
|
| 66 |
+
{ "label": "आकडे", "text": "एक दोन तीन चार पाच सहा सात आठ." },
|
| 67 |
+
{ "label": "कथा", "text": "पूर्वी एका छोट्या गावात एक शहाणा शेतकरी राहत होता." },
|
| 68 |
+
{ "label": "गुढी पाडवा", "text": "गुढी पाडवा हा मराठी नववर्षाचा सण आहे." },
|
| 69 |
+
{ "label": "भजी", "text": "पावसाळी संध्याकाळी गरम भजी आणि कटिंग चहा म्हणजे स्वर्गच!" },
|
| 70 |
+
{ "label": "वडापाव", "text": "मुंबईचा वडापाव आणि पुण्याचा चहा — आपली खरी ओळख." }
|
| 71 |
+
],
|
| 72 |
+
"marathi_female": [
|
| 73 |
+
{ "label": "थकले", "text": "आज खूप काम केले, मी थकले आहे." }
|
| 74 |
+
],
|
| 75 |
+
"marathi_male": [
|
| 76 |
+
{ "label": "थकलो", "text": "आज खूप काम केले, मी थकलो आहे." }
|
| 77 |
+
],
|
| 78 |
+
"minglish_neutral": [
|
| 79 |
+
{ "label": "coffee?", "text": "Coffee पिऊया का?" },
|
| 80 |
+
{ "label": "AI tech", "text": "Artificial intelligence ही technology खूप amazing आहे." },
|
| 81 |
+
{ "label": "weekend plan", "text": "Weekend ला movie बघायचा plan आहे का?" }
|
| 82 |
+
],
|
| 83 |
+
"minglish_female": [
|
| 84 |
+
{ "label": "Google job", "text": "मी Google मध्ये काम करते." },
|
| 85 |
+
{ "label": "late meeting", "text": "आज meeting ला late झाले." }
|
| 86 |
+
],
|
| 87 |
+
"minglish_male": [
|
| 88 |
+
{ "label": "Google job", "text": "मी Google मध्ये काम करतो." },
|
| 89 |
+
{ "label": "late meeting", "text": "आज meeting ला late झालो." }
|
| 90 |
+
]
|
| 91 |
+
}
|
| 92 |
+
}
|
voices/mf_priya.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c09561d3fda1dfddb88c461a79ff5d518198035a8906feece9fe38d6973225a1
|
| 3 |
+
size 522240
|
voices/mm_arjun.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d2eb6136af364a684e0504a77817e54ce6a96771284dee629c0d938e76b334b0
|
| 3 |
+
size 522240
|