phonon-studio 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -2
- package/package.json +40 -4
- package/public/app.js +1 -0
- package/public/index.html +457 -0
- package/public/styles.css +1234 -0
- package/server.js +2 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="theme-color" content="#10131b">
|
|
7
|
+
<title>Phonon Studio | Local AI Speech</title>
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="https://mohan-chinnappan-n5.github.io/dfv/img/mc_favIcon.ico" />
|
|
9
|
+
<link rel="stylesheet" href="/styles.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
|
|
13
|
+
<!-- ── Splash Screen ── -->
|
|
14
|
+
<div id="splash" class="splash" role="dialog" aria-modal="true" aria-label="Welcome to Phonon Studio">
|
|
15
|
+
<div class="splash-inner">
|
|
16
|
+
<div class="splash-logo">P</div>
|
|
17
|
+
<h2 class="splash-title">Phonon Studio</h2>
|
|
18
|
+
<p class="splash-sub">Text ↔ speech, entirely on your Mac.</p>
|
|
19
|
+
<div class="splash-features">
|
|
20
|
+
<div class="splash-feat"><span>🎙</span><span>4 voices</span></div>
|
|
21
|
+
<div class="splash-feat"><span>📊</span><span>Waveform</span></div>
|
|
22
|
+
<div class="splash-feat"><span>🤖</span><span>LLM pipeline</span></div>
|
|
23
|
+
<div class="splash-feat"><span>⚡</span><span>WAV + MP3</span></div>
|
|
24
|
+
<div class="splash-feat"><span>🕐</span><span>History</span></div>
|
|
25
|
+
</div>
|
|
26
|
+
<button id="splash-start" class="primary splash-btn">Get Started →</button>
|
|
27
|
+
<button id="splash-tour" class="subtle splash-tour-link">Take a guided tour ↗</button>
|
|
28
|
+
<p class="splash-note">Powered by Ollama · Phonon model · 24 kHz · No cloud required</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- ── Help Modal ── -->
|
|
33
|
+
<div id="help-modal" class="modal-overlay" hidden role="dialog" aria-modal="true" aria-labelledby="help-title">
|
|
34
|
+
<div class="modal">
|
|
35
|
+
<div class="modal-header">
|
|
36
|
+
<h3 id="help-title">Quick Reference</h3>
|
|
37
|
+
<button id="help-close" class="subtle modal-close" aria-label="Close help">✕</button>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="modal-body">
|
|
40
|
+
<div class="help-section">
|
|
41
|
+
<h4>Keyboard shortcuts</h4>
|
|
42
|
+
<div class="shortcut-row"><kbd>Cmd</kbd>+<kbd>Enter</kbd><span>Generate speech</span></div>
|
|
43
|
+
<div class="shortcut-row"><kbd>?</kbd><span>Open this help</span></div>
|
|
44
|
+
<div class="shortcut-row"><kbd>Esc</kbd><span>Close help / tour</span></div>
|
|
45
|
+
<div class="shortcut-row"><kbd>←</kbd><kbd>→</kbd><span>Previous / next tour step</span></div>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="help-section">
|
|
48
|
+
<h4>Tabs</h4>
|
|
49
|
+
<dl class="help-dl">
|
|
50
|
+
<dt>Speech</dt>
|
|
51
|
+
<dd>Type, paste, or drop a .txt file. Text over 4000 chars is split at sentence boundaries and stitched automatically.</dd>
|
|
52
|
+
<dt>Voice Compare</dt>
|
|
53
|
+
<dd>Generates the same text with all 4 voices in parallel — hear them side-by-side before committing.</dd>
|
|
54
|
+
<dt>LLM → Speech</dt>
|
|
55
|
+
<dd>Pick any local Ollama chat model, ask a question, and send its reply straight to Phonon — a fully local AI chain.</dd>
|
|
56
|
+
<dt>History</dt>
|
|
57
|
+
<dd>Up to 20 past generations this session. Replay or download any of them.</dd>
|
|
58
|
+
</dl>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="help-section">
|
|
61
|
+
<h4>Tips</h4>
|
|
62
|
+
<ul class="help-tips">
|
|
63
|
+
<li>Text over 4000 characters is auto-chunked — no practical length limit.</li>
|
|
64
|
+
<li>The speed slider adjusts playback in real time — no re-generation needed.</li>
|
|
65
|
+
<li>MP3 export appears automatically when ffmpeg is installed (<code>brew install ffmpeg</code>).</li>
|
|
66
|
+
<li>Drag and drop a <code>.txt</code> file directly onto the editor.</li>
|
|
67
|
+
<li>The Paste button loads sample text if the clipboard is empty.</li>
|
|
68
|
+
<li>Press the ▷ Tour button in the header to replay the guided tour.</li>
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="help-section">
|
|
72
|
+
<h4>Status indicator</h4>
|
|
73
|
+
<p class="help-status"><span class="dot-green">●</span> Ollama connected & model installed</p>
|
|
74
|
+
<p class="help-status"><span class="dot-red">●</span> Ollama unavailable or model not pulled</p>
|
|
75
|
+
<p class="help-hint">Run <code>ollama pull phonon</code> to install the TTS model.</p>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- ── Pronunciations Modal ── -->
|
|
82
|
+
<div id="pron-modal" class="modal-overlay" hidden role="dialog" aria-modal="true" aria-labelledby="pron-title">
|
|
83
|
+
<div class="modal">
|
|
84
|
+
<div class="modal-header">
|
|
85
|
+
<h3 id="pron-title">Custom Pronunciations</h3>
|
|
86
|
+
<button id="pron-close" class="subtle modal-close" aria-label="Close pronunciations">✕</button>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="modal-body">
|
|
89
|
+
<p class="help-hint">Word → spoken form substitutions applied before every generation.</p>
|
|
90
|
+
<div id="pron-list"></div>
|
|
91
|
+
<div class="pron-add-row">
|
|
92
|
+
<input id="pron-word" type="text" placeholder="Word (e.g. SQL)" spellcheck="false" autocomplete="off">
|
|
93
|
+
<span class="pron-arrow-label">→</span>
|
|
94
|
+
<input id="pron-spoken" type="text" placeholder="Spoken form (e.g. sequel)" spellcheck="false" autocomplete="off">
|
|
95
|
+
<button id="pron-add" class="subtle">Add</button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- ── Templates Modal ── -->
|
|
102
|
+
<div id="templates-modal" class="modal-overlay" hidden role="dialog" aria-modal="true" aria-labelledby="templates-title">
|
|
103
|
+
<div class="modal">
|
|
104
|
+
<div class="modal-header">
|
|
105
|
+
<h3 id="templates-title">Text Templates</h3>
|
|
106
|
+
<button id="templates-close" class="subtle modal-close" aria-label="Close templates">✕</button>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="modal-body">
|
|
109
|
+
<p class="help-hint">Save named text snippets for quick reuse across sessions.</p>
|
|
110
|
+
<div id="templates-list"></div>
|
|
111
|
+
<div class="templates-save-row">
|
|
112
|
+
<input id="template-name" type="text" placeholder="Template name…" autocomplete="off">
|
|
113
|
+
<button id="template-save" class="subtle">Save current text</button>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- ── Karaoke / Focus Mode Overlay ── -->
|
|
120
|
+
<div id="karaoke-overlay" hidden aria-live="polite">
|
|
121
|
+
<div class="karaoke-inner">
|
|
122
|
+
<div class="karaoke-progress-wrap">
|
|
123
|
+
<div id="karaoke-progress-bar" class="karaoke-progress-bar"></div>
|
|
124
|
+
</div>
|
|
125
|
+
<div id="karaoke-line" class="karaoke-line">Ready</div>
|
|
126
|
+
<button id="karaoke-exit" class="subtle karaoke-exit-btn">Exit focus mode (Esc)</button>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<!-- ── Guided Tour Overlay ── -->
|
|
131
|
+
<div id="tour-overlay" hidden aria-live="polite">
|
|
132
|
+
<div id="tour-spot" class="tour-spot"></div>
|
|
133
|
+
<div id="tour-bubble" class="tour-bubble" role="dialog" aria-labelledby="tour-title-text">
|
|
134
|
+
<div class="tour-step-num" id="tour-step-num">1 / 6</div>
|
|
135
|
+
<h4 id="tour-title-text"></h4>
|
|
136
|
+
<p id="tour-text"></p>
|
|
137
|
+
<div class="tour-actions">
|
|
138
|
+
<button id="tour-skip" class="subtle tour-skip-btn">Skip tour</button>
|
|
139
|
+
<div class="tour-nav">
|
|
140
|
+
<button id="tour-prev" class="subtle tour-arrow" aria-label="Previous step">←</button>
|
|
141
|
+
<button id="tour-next" class="primary tour-next-btn">Next →</button>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- ── Sticky Header ── -->
|
|
148
|
+
<header id="topbar" class="topbar">
|
|
149
|
+
<div class="topbar-inner">
|
|
150
|
+
<div class="brand">
|
|
151
|
+
<div class="logo">P</div>
|
|
152
|
+
<div>
|
|
153
|
+
<h1>Phonon Studio</h1>
|
|
154
|
+
<p>Local AI text-to-speech</p>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="topbar-right">
|
|
158
|
+
<button id="tour-btn" class="theme-toggle" title="Start guided tour">▷ Tour</button>
|
|
159
|
+
<button id="help-btn" class="theme-toggle" title="Help & shortcuts (press ?)">?</button>
|
|
160
|
+
<button id="theme-toggle" class="theme-toggle" title="Switch to light mode">☀</button>
|
|
161
|
+
<span id="health" class="status">Checking Ollama…</span>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</header>
|
|
165
|
+
|
|
166
|
+
<!-- ── App Content ── -->
|
|
167
|
+
<div class="app">
|
|
168
|
+
|
|
169
|
+
<section class="hero">
|
|
170
|
+
<p class="eyebrow">SPEAK · LISTEN · TRANSCRIBE · YOUR MAC</p>
|
|
171
|
+
<h2>Give your words a voice.<br><span>Give your voice words.</span></h2>
|
|
172
|
+
<p>Turn notes, articles, and ideas into natural-sounding audio — or speak in any language and get an instant transcript. All entirely offline.</p>
|
|
173
|
+
</section>
|
|
174
|
+
|
|
175
|
+
<nav class="tabs" id="tabs-nav" role="tablist">
|
|
176
|
+
<button class="tab active" data-tab="speech" role="tab" aria-selected="true">Speech</button>
|
|
177
|
+
<button class="tab" data-tab="compare" role="tab" aria-selected="false">Voice Compare</button>
|
|
178
|
+
<button class="tab" data-tab="chat" role="tab" aria-selected="false">LLM → Speech</button>
|
|
179
|
+
<button class="tab" data-tab="history" role="tab" aria-selected="false">
|
|
180
|
+
History <span id="history-badge" class="badge" hidden></span>
|
|
181
|
+
</button>
|
|
182
|
+
<button class="tab" data-tab="transcribe" role="tab" aria-selected="false">Voice → Text</button>
|
|
183
|
+
</nav>
|
|
184
|
+
|
|
185
|
+
<!-- ── Speech Tab ── -->
|
|
186
|
+
<section id="tab-speech" class="panel tab-content" role="tabpanel">
|
|
187
|
+
<div class="panel-heading">
|
|
188
|
+
<div>
|
|
189
|
+
<h3>Speech editor</h3>
|
|
190
|
+
<p>Type, paste, or drop a .txt file. Long text is chunked automatically.</p>
|
|
191
|
+
</div>
|
|
192
|
+
<span class="tag">PHONON</span>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="drop-zone" id="drop-zone">
|
|
196
|
+
<textarea id="text" maxlength="16384"
|
|
197
|
+
placeholder="Hello! Welcome to my local AI speech studio. Tip: Drop a .txt file here, or press Cmd+Enter to generate."></textarea>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div class="url-fetch-row">
|
|
201
|
+
<input id="url-input" type="url" class="url-input" placeholder="Paste a URL or PDF link to extract text…">
|
|
202
|
+
<button id="url-fetch-btn" class="subtle" data-tooltip="Fetch and extract readable text — supports HTML pages and PDFs">Fetch</button>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div id="pdf-pagination" class="pdf-pagination" hidden>
|
|
206
|
+
<button id="pdf-prev" class="subtle pdf-nav-btn" disabled>← Prev</button>
|
|
207
|
+
<span id="pdf-page-info" class="pdf-page-info">Page 1 of 1</span>
|
|
208
|
+
<button id="pdf-next" class="subtle pdf-nav-btn">Next →</button>
|
|
209
|
+
<span class="pdf-page-hint">Each page is loaded separately — generate speech per page.</span>
|
|
210
|
+
<button id="queue-all-btn" class="subtle"
|
|
211
|
+
data-tooltip="Generate all pages sequentially and collect WAV downloads">▶▶ Queue all</button>
|
|
212
|
+
</div>
|
|
213
|
+
<div id="queue-panel" class="queue-panel" hidden>
|
|
214
|
+
<div class="queue-panel-header">
|
|
215
|
+
Batch queue — <span id="queue-status-label">0 items</span>
|
|
216
|
+
<button id="queue-clear" class="subtle queue-clear-btn">Clear</button>
|
|
217
|
+
</div>
|
|
218
|
+
<div id="queue-list"></div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div class="editor-footer">
|
|
222
|
+
<div class="editor-meta">
|
|
223
|
+
<span id="count">0 / 16384 characters</span>
|
|
224
|
+
<span id="duration" class="duration-hint"></span>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="btn-row">
|
|
227
|
+
<button id="paste-btn" class="subtle" data-tooltip="Paste clipboard — loads sample text if empty">Paste</button>
|
|
228
|
+
<button id="clear" class="subtle" data-tooltip="Clear the editor">Clear</button>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="controls">
|
|
233
|
+
<label>
|
|
234
|
+
Voice
|
|
235
|
+
<select id="voice">
|
|
236
|
+
<option value="default">Default</option>
|
|
237
|
+
<option value="Zoey">Zoey</option>
|
|
238
|
+
<option value="Easton">Easton</option>
|
|
239
|
+
<option value="Freya">Freya</option>
|
|
240
|
+
</select>
|
|
241
|
+
</label>
|
|
242
|
+
<div class="controls-opts">
|
|
243
|
+
<label class="cleanup-label" data-tooltip="Strip markdown and expand abbreviations before sending">
|
|
244
|
+
<input type="checkbox" id="cleanup-toggle" checked>
|
|
245
|
+
<span>Auto-clean</span>
|
|
246
|
+
</label>
|
|
247
|
+
<label class="cleanup-label" data-tooltip="Normalize peak loudness to −3 dBFS after generation">
|
|
248
|
+
<input type="checkbox" id="normalize-toggle" checked>
|
|
249
|
+
<span>Normalize</span>
|
|
250
|
+
</label>
|
|
251
|
+
<span class="controls-divider"></span>
|
|
252
|
+
<button id="pron-btn" class="subtle controls-action-btn" data-tooltip="Manage custom word → spoken form substitutions">⚙ Pron.</button>
|
|
253
|
+
<button id="templates-btn" class="subtle controls-action-btn" data-tooltip="Save and load named text snippets">Templates</button>
|
|
254
|
+
<button id="browser-speak-btn" class="subtle controls-action-btn" hidden data-tooltip="Speak using the browser's built-in speech synthesis (Ollama offline)">↗ Browser</button>
|
|
255
|
+
</div>
|
|
256
|
+
<button id="generate" class="primary" data-tooltip="Generate speech (Cmd+Enter)">▶ Generate speech</button>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<p id="message" role="status">Ready to generate speech.</p>
|
|
260
|
+
<button id="show-sent-btn" class="subtle sent-text-toggle" hidden
|
|
261
|
+
data-tooltip="Show exactly what was sent to TTS after auto-clean and pronunciation transforms">↗ Show sent text</button>
|
|
262
|
+
<div id="sent-text-row" class="sent-text-row" hidden>
|
|
263
|
+
<pre id="sent-text-content" class="sent-text-content"></pre>
|
|
264
|
+
<button id="copy-sent" class="subtle">Copy</button>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<section id="result" hidden>
|
|
268
|
+
<div class="result-header">
|
|
269
|
+
<h3>Generated audio</h3>
|
|
270
|
+
<div class="speed-control">
|
|
271
|
+
<label for="speed">Speed <span id="speed-label">1.0×</span></label>
|
|
272
|
+
<input type="range" id="speed" min="0.5" max="2" step="0.1" value="1"
|
|
273
|
+
data-tooltip="Adjust playback speed — no re-generation needed">
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
<canvas id="waveform" height="60"></canvas>
|
|
277
|
+
<div id="word-display" class="word-display" hidden></div>
|
|
278
|
+
<div class="sync-row" id="sync-row" hidden>
|
|
279
|
+
<label for="sync-offset">Line sync <span id="sync-label">1.5×</span></label>
|
|
280
|
+
<input type="range" id="sync-offset" min="0" max="4" step="0.1" value="1.5"
|
|
281
|
+
data-tooltip="Adjust if the line highlight leads or lags the voice">
|
|
282
|
+
<button id="karaoke-btn" class="subtle karaoke-launch-btn"
|
|
283
|
+
data-tooltip="Fullscreen focus mode — current sentence in large type">◫ Focus</button>
|
|
284
|
+
</div>
|
|
285
|
+
<div id="bookmark-row" class="bookmark-row" hidden>
|
|
286
|
+
<div class="bookmark-actions">
|
|
287
|
+
<span class="bookmark-hint">⚑ Bookmarks — press <kbd>B</kbd> while playing</span>
|
|
288
|
+
<button id="auto-chapters-btn" class="subtle"
|
|
289
|
+
data-tooltip="Auto-detect headings and paragraph breaks as chapter bookmarks">✦ Auto-chapters</button>
|
|
290
|
+
</div>
|
|
291
|
+
<div id="bookmark-list" class="bookmark-list"></div>
|
|
292
|
+
</div>
|
|
293
|
+
<div id="loop-row" class="loop-row" hidden>
|
|
294
|
+
<span id="loop-label" class="loop-label">⇄ Loop: — → —</span>
|
|
295
|
+
<span class="loop-hint"><kbd>[</kbd> set in • <kbd>]</kbd> set out</span>
|
|
296
|
+
<button id="loop-clear" class="subtle">✕ Clear loop</button>
|
|
297
|
+
</div>
|
|
298
|
+
<audio id="audio" controls></audio>
|
|
299
|
+
<div class="download-row">
|
|
300
|
+
<a id="download-wav" class="download-btn" download="phonon-speech.wav"
|
|
301
|
+
data-tooltip="Download as WAV audio">Download WAV ↓</a>
|
|
302
|
+
<button id="download-mp3" class="download-btn mp3-btn" hidden
|
|
303
|
+
data-tooltip="Download as MP3 (requires ffmpeg)">Download MP3 ↓</button>
|
|
304
|
+
<button id="download-srt" class="download-btn subtitle-btn" hidden
|
|
305
|
+
data-tooltip="Download subtitles for video editors (.srt)">Download SRT ↓</button>
|
|
306
|
+
<button id="download-vtt" class="download-btn subtitle-btn" hidden
|
|
307
|
+
data-tooltip="Download subtitles for web video (.vtt)">Download VTT ↓</button>
|
|
308
|
+
</div>
|
|
309
|
+
</section>
|
|
310
|
+
</section>
|
|
311
|
+
|
|
312
|
+
<!-- ── Voice Compare Tab ── -->
|
|
313
|
+
<section id="tab-compare" class="panel tab-content" role="tabpanel" hidden>
|
|
314
|
+
<div class="panel-heading">
|
|
315
|
+
<div>
|
|
316
|
+
<h3>Voice compare</h3>
|
|
317
|
+
<p>Enter text in the Speech tab, then compare all 4 voices side-by-side.</p>
|
|
318
|
+
</div>
|
|
319
|
+
<button id="compare-generate" class="primary"
|
|
320
|
+
data-tooltip="Generate all 4 voices in parallel">Compare all voices</button>
|
|
321
|
+
</div>
|
|
322
|
+
<p id="compare-message" role="status">Enter text in the Speech tab, then click Compare.</p>
|
|
323
|
+
<div id="compare-grid" class="voice-grid"></div>
|
|
324
|
+
</section>
|
|
325
|
+
|
|
326
|
+
<!-- ── LLM Chat Tab ── -->
|
|
327
|
+
<section id="tab-chat" class="panel tab-content" role="tabpanel" hidden>
|
|
328
|
+
<div class="panel-heading">
|
|
329
|
+
<div>
|
|
330
|
+
<h3>LLM → Speech pipeline</h3>
|
|
331
|
+
<p>Ask a local LLM, then send its answer straight to Phonon.</p>
|
|
332
|
+
</div>
|
|
333
|
+
<span class="tag">LOCAL LLM</span>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<label class="field-label">
|
|
337
|
+
Chat model
|
|
338
|
+
<select id="chat-model"><option value="">Loading…</option></select>
|
|
339
|
+
</label>
|
|
340
|
+
|
|
341
|
+
<label class="field-label" style="margin-top:16px">
|
|
342
|
+
Your message
|
|
343
|
+
<textarea id="chat-input" rows="3"
|
|
344
|
+
placeholder="Explain the theory of relativity in two sentences."></textarea>
|
|
345
|
+
</label>
|
|
346
|
+
|
|
347
|
+
<div class="chat-actions">
|
|
348
|
+
<button id="chat-send" class="primary" data-tooltip="Send your message to the selected Ollama model">Ask LLM</button>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<p id="chat-message" role="status"></p>
|
|
352
|
+
|
|
353
|
+
<div id="chat-response" hidden>
|
|
354
|
+
<div class="response-box">
|
|
355
|
+
<p id="chat-response-text"></p>
|
|
356
|
+
</div>
|
|
357
|
+
<div class="chat-response-actions">
|
|
358
|
+
<button id="chat-to-speech" class="primary"
|
|
359
|
+
data-tooltip="Load this response into the Speech editor">▶ Send to Phonon</button>
|
|
360
|
+
<button id="chat-copy" class="subtle" data-tooltip="Copy response to clipboard">Copy text</button>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</section>
|
|
364
|
+
|
|
365
|
+
<!-- ── History Tab ── -->
|
|
366
|
+
<section id="tab-history" class="panel tab-content" role="tabpanel" hidden>
|
|
367
|
+
<div class="panel-heading">
|
|
368
|
+
<div>
|
|
369
|
+
<h3>Generation history</h3>
|
|
370
|
+
<p>Your recent generations this session (up to 20). ★ to pin across sessions.</p>
|
|
371
|
+
</div>
|
|
372
|
+
<div class="history-controls">
|
|
373
|
+
<select id="history-filter">
|
|
374
|
+
<option value="">All voices</option>
|
|
375
|
+
<option value="default">Default</option>
|
|
376
|
+
<option value="Zoey">Zoey</option>
|
|
377
|
+
<option value="Easton">Easton</option>
|
|
378
|
+
<option value="Freya">Freya</option>
|
|
379
|
+
</select>
|
|
380
|
+
<button id="clear-history" class="subtle" data-tooltip="Remove all session history entries">Clear all</button>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
<div id="history-list">
|
|
384
|
+
<p class="empty-state">No generations yet. Generate some speech first!</p>
|
|
385
|
+
</div>
|
|
386
|
+
</section>
|
|
387
|
+
|
|
388
|
+
<!-- ── Voice → Text Tab ── -->
|
|
389
|
+
<section id="tab-transcribe" class="panel tab-content" role="tabpanel" hidden>
|
|
390
|
+
<div class="panel-heading">
|
|
391
|
+
<div>
|
|
392
|
+
<h3>Voice input</h3>
|
|
393
|
+
<p>Speak in any language — get a live transcript to copy or send to Phonon.</p>
|
|
394
|
+
</div>
|
|
395
|
+
<span class="tag">SPEECH→TEXT</span>
|
|
396
|
+
</div>
|
|
397
|
+
|
|
398
|
+
<div id="transcribe-unsupported" class="transcribe-unsupported" hidden>
|
|
399
|
+
<strong>Speech recognition not available.</strong>
|
|
400
|
+
Browser speech recognition requires Chrome, Edge, or Safari. Firefox does not support it.
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<div id="transcribe-ui">
|
|
404
|
+
<div class="transcribe-controls">
|
|
405
|
+
<label class="field-label transcribe-lang-label">
|
|
406
|
+
Language
|
|
407
|
+
<select id="transcribe-lang">
|
|
408
|
+
<option value="en-US">English (US)</option>
|
|
409
|
+
<option value="en-GB">English (UK)</option>
|
|
410
|
+
<option value="fr-FR">French</option>
|
|
411
|
+
<option value="de-DE">German</option>
|
|
412
|
+
<option value="es-ES">Spanish</option>
|
|
413
|
+
<option value="it-IT">Italian</option>
|
|
414
|
+
<option value="pt-BR">Portuguese (Brazil)</option>
|
|
415
|
+
<option value="zh-CN">Chinese (Simplified)</option>
|
|
416
|
+
<option value="zh-TW">Chinese (Traditional)</option>
|
|
417
|
+
<option value="ja-JP">Japanese</option>
|
|
418
|
+
<option value="ko-KR">Korean</option>
|
|
419
|
+
<option value="hi-IN">Hindi</option>
|
|
420
|
+
<option value="ta-IN">Tamil</option>
|
|
421
|
+
<option value="ar-SA">Arabic</option>
|
|
422
|
+
<option value="ru-RU">Russian</option>
|
|
423
|
+
<option value="nl-NL">Dutch</option>
|
|
424
|
+
<option value="pl-PL">Polish</option>
|
|
425
|
+
<option value="tr-TR">Turkish</option>
|
|
426
|
+
<option value="uk-UA">Ukrainian</option>
|
|
427
|
+
<option value="vi-VN">Vietnamese</option>
|
|
428
|
+
</select>
|
|
429
|
+
</label>
|
|
430
|
+
<button id="transcribe-toggle" class="primary transcribe-toggle-btn">● Start recording</button>
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
<p id="transcribe-status" class="transcribe-status" role="status"></p>
|
|
434
|
+
|
|
435
|
+
<div id="transcribe-output" hidden>
|
|
436
|
+
<div id="transcript-display" class="transcript-display" aria-live="polite">
|
|
437
|
+
<span id="transcript-final" class="transcript-final"></span><span id="transcript-interim" class="transcript-interim"></span>
|
|
438
|
+
</div>
|
|
439
|
+
<div class="transcribe-actions">
|
|
440
|
+
<button id="transcript-copy" class="subtle">Copy text</button>
|
|
441
|
+
<button id="transcript-to-speech" class="primary" data-tooltip="Load transcript into Speech editor">▶ Send to Phonon</button>
|
|
442
|
+
<button id="transcript-browser-speak" class="subtle" hidden data-tooltip="Speak transcript using the browser's built-in speech synthesis">↗ Speak via Browser</button>
|
|
443
|
+
<button id="transcript-clear" class="subtle">Clear</button>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
</section>
|
|
448
|
+
|
|
449
|
+
<footer>
|
|
450
|
+
<span>RUNNING LOCALLY</span>
|
|
451
|
+
<span>Ollama · Phonon · 24 kHz WAV · Mohan Chinnappan</span>
|
|
452
|
+
</footer>
|
|
453
|
+
</div><!-- /.app -->
|
|
454
|
+
|
|
455
|
+
<script src="/app.js"></script>
|
|
456
|
+
</body>
|
|
457
|
+
</html>
|