victor HF Staff commited on
Commit
50d6913
Β·
verified Β·
1 Parent(s): 41e5e13

Add subtitles toggle in settings

Browse files
Files changed (3) hide show
  1. index.html +4 -0
  2. src/app.js +9 -0
  3. src/style.css +13 -0
index.html CHANGED
@@ -61,6 +61,10 @@
61
  Voice
62
  <select id="voice"></select>
63
  </label>
 
 
 
 
64
  <label>
65
  Extra instructions
66
  <textarea
 
61
  Voice
62
  <select id="voice"></select>
63
  </label>
64
+ <label class="check-row">
65
+ <input type="checkbox" id="subtitles-toggle" />
66
+ Show subtitles while she speaks
67
+ </label>
68
  <label>
69
  Extra instructions
70
  <textarea
src/app.js CHANGED
@@ -39,6 +39,7 @@ const STORAGE_KEYS = {
39
  voice: "avatar.voice",
40
  instructions: "avatar.instructions",
41
  directUrl: "avatar.directUrl",
 
42
  };
43
 
44
  /** Function tools declared to the backend: the model plays the avatar. */
@@ -95,6 +96,7 @@ const settingsDialog = /** @type {HTMLDialogElement} */ ($("#settings"));
95
  const inputVoice = /** @type {HTMLSelectElement} */ ($("#voice"));
96
  const inputInstructions = /** @type {HTMLTextAreaElement} */ ($("#instructions"));
97
  const inputDirectUrl = /** @type {HTMLInputElement} */ ($("#direct-url"));
 
98
  const directUrlRow = $("#direct-url-row");
99
 
100
  // ── State ────────────────────────────────────────────────────────────────
@@ -111,6 +113,8 @@ function loadSettings() {
111
  voice: localStorage.getItem(STORAGE_KEYS.voice) || DEFAULT_VOICE,
112
  instructions: localStorage.getItem(STORAGE_KEYS.instructions) || "",
113
  directUrl: localStorage.getItem(STORAGE_KEYS.directUrl) || "",
 
 
114
  };
115
  }
116
  let settings = loadSettings();
@@ -119,6 +123,7 @@ function saveSettings() {
119
  localStorage.setItem(STORAGE_KEYS.voice, settings.voice);
120
  localStorage.setItem(STORAGE_KEYS.instructions, settings.instructions);
121
  localStorage.setItem(STORAGE_KEYS.directUrl, settings.directUrl);
 
122
  }
123
 
124
  /** Persona + whatever extra guidance the user typed in Settings. */
@@ -136,6 +141,7 @@ function setCaption(text, kind = "") {
136
 
137
  /** @param {string} text */
138
  function showSubtitles(text) {
 
139
  clearTimeout(subtitleTimer);
140
  subtitles.textContent = text;
141
  subtitles.classList.add("visible");
@@ -347,6 +353,7 @@ settingsBtn.addEventListener("click", () => {
347
  inputVoice.value = settings.voice;
348
  inputInstructions.value = settings.instructions;
349
  inputDirectUrl.value = settings.directUrl;
 
350
  settingsDialog.showModal();
351
  });
352
 
@@ -355,8 +362,10 @@ settingsDialog.addEventListener("close", () => {
355
  voice: inputVoice.value || DEFAULT_VOICE,
356
  instructions: inputInstructions.value,
357
  directUrl: inputDirectUrl.value.trim(),
 
358
  };
359
  saveSettings();
 
360
  // Voice/instructions apply live to an ongoing session.
361
  client?.updateSession({ voice: settings.voice, instructions: effectiveInstructions() });
362
  });
 
39
  voice: "avatar.voice",
40
  instructions: "avatar.instructions",
41
  directUrl: "avatar.directUrl",
42
+ subtitles: "avatar.subtitles",
43
  };
44
 
45
  /** Function tools declared to the backend: the model plays the avatar. */
 
96
  const inputVoice = /** @type {HTMLSelectElement} */ ($("#voice"));
97
  const inputInstructions = /** @type {HTMLTextAreaElement} */ ($("#instructions"));
98
  const inputDirectUrl = /** @type {HTMLInputElement} */ ($("#direct-url"));
99
+ const inputSubtitles = /** @type {HTMLInputElement} */ ($("#subtitles-toggle"));
100
  const directUrlRow = $("#direct-url-row");
101
 
102
  // ── State ────────────────────────────────────────────────────────────────
 
113
  voice: localStorage.getItem(STORAGE_KEYS.voice) || DEFAULT_VOICE,
114
  instructions: localStorage.getItem(STORAGE_KEYS.instructions) || "",
115
  directUrl: localStorage.getItem(STORAGE_KEYS.directUrl) || "",
116
+ // Off by default: the face already carries the conversation.
117
+ subtitles: localStorage.getItem(STORAGE_KEYS.subtitles) === "1",
118
  };
119
  }
120
  let settings = loadSettings();
 
123
  localStorage.setItem(STORAGE_KEYS.voice, settings.voice);
124
  localStorage.setItem(STORAGE_KEYS.instructions, settings.instructions);
125
  localStorage.setItem(STORAGE_KEYS.directUrl, settings.directUrl);
126
+ localStorage.setItem(STORAGE_KEYS.subtitles, settings.subtitles ? "1" : "0");
127
  }
128
 
129
  /** Persona + whatever extra guidance the user typed in Settings. */
 
141
 
142
  /** @param {string} text */
143
  function showSubtitles(text) {
144
+ if (!settings.subtitles) return;
145
  clearTimeout(subtitleTimer);
146
  subtitles.textContent = text;
147
  subtitles.classList.add("visible");
 
353
  inputVoice.value = settings.voice;
354
  inputInstructions.value = settings.instructions;
355
  inputDirectUrl.value = settings.directUrl;
356
+ inputSubtitles.checked = settings.subtitles;
357
  settingsDialog.showModal();
358
  });
359
 
 
362
  voice: inputVoice.value || DEFAULT_VOICE,
363
  instructions: inputInstructions.value,
364
  directUrl: inputDirectUrl.value.trim(),
365
+ subtitles: inputSubtitles.checked,
366
  };
367
  saveSettings();
368
+ if (!settings.subtitles) subtitles.classList.remove("visible");
369
  // Voice/instructions apply live to an ongoing session.
370
  client?.updateSession({ voice: settings.voice, instructions: effectiveInstructions() });
371
  });
src/style.css CHANGED
@@ -302,6 +302,19 @@ body {
302
  resize: vertical;
303
  }
304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  .dialog-actions {
306
  display: flex;
307
  justify-content: flex-end;
 
302
  resize: vertical;
303
  }
304
 
305
+ #settings .check-row {
306
+ display: flex;
307
+ align-items: center;
308
+ gap: 9px;
309
+ }
310
+
311
+ #settings .check-row input {
312
+ display: inline-block;
313
+ width: auto;
314
+ margin: 0;
315
+ accent-color: var(--text);
316
+ }
317
+
318
  .dialog-actions {
319
  display: flex;
320
  justify-content: flex-end;