Skip to content

Commit c983edb

Browse files
committed
Fix TTS seed profiles and guest save routing
1 parent 675244b commit c983edb

13 files changed

Lines changed: 1152 additions & 352 deletions

assets/toolbox/text-to-speech/js/index.js

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
listTtsProfiles,
1717
updateTtsProfile,
1818
} from "../../../../toolbox/messages/messages-api-client.js";
19+
import { getSessionCurrent } from "../../../../src/api/session-api-client.js";
1920

2021
const TTS_OWNERSHIP = Object.freeze({
2122
DESIGN: "Design",
@@ -78,6 +79,7 @@ const TTS_PROFILE_CONTRACT_VERSION = "tts-profile-emotion-v1";
7879
const NEW_ROW_KEY = "__new__";
7980
const DEFAULT_TTS_PROFILE_ID = "default-balanced-profile";
8081
const DEFAULT_TTS_EMOTION_ID = "neutral";
82+
const SIGN_IN_ROUTE = "account/sign-in.html";
8183

8284
const TTS_PROFILE_GENDER_OPTIONS = Object.freeze([
8385
Object.freeze({ label: "Neutral", value: "neutral" }),
@@ -95,7 +97,6 @@ const TTS_PROFILE_EMOTION_OPTIONS = Object.freeze([
9597
Object.freeze({ label: "Urgent", value: "urgent" }),
9698
Object.freeze({ label: "Whisper", value: "whisper" }),
9799
Object.freeze({ label: "Excited", value: "excited" }),
98-
Object.freeze({ label: "Robot", value: "robot" })
99100
]);
100101

101102
function boundedNumber(value, { fallback, max, min, value: defaultValue }) {
@@ -306,16 +307,13 @@ function createDefaultEmotionSettings({ markNeutralInUse = false } = {}) {
306307
emotion: "neutral",
307308
messagePartsUsageCount: markNeutralInUse ? 1 : 0,
308309
}),
309-
createTextToSpeechProfileEmotion({ emotion: "happy", pitch: 1.08, rate: 1.04 }),
310-
createTextToSpeechProfileEmotion({ emotion: "angry", pitch: 0.96, rate: 1.08, volume: 1 }),
311-
createTextToSpeechProfileEmotion({ emotion: "scared", pitch: 1.12, rate: 1.12, volume: 0.9 }),
310+
createTextToSpeechProfileEmotion({ emotion: "calm" }),
311+
createTextToSpeechProfileEmotion({ emotion: "urgent", pitch: 1.08, rate: 1.15, volume: 1 }),
312312
];
313313
}
314314

315315
function createDefaultTextToSpeechProfiles(voiceOptions = []) {
316316
const balancedVoice = defaultVoiceForProfile(voiceOptions);
317-
const manVoice = defaultVoiceForProfile(voiceOptions, "male") || balancedVoice;
318-
const womanVoice = defaultVoiceForProfile(voiceOptions, "female") || voiceOptions[1] || balancedVoice;
319317
return [
320318
createTextToSpeechProfile({
321319
emotions: createDefaultEmotionSettings({ markNeutralInUse: true }),
@@ -326,27 +324,16 @@ function createDefaultTextToSpeechProfiles(voiceOptions = []) {
326324
voice: balancedVoice?.value || "",
327325
voiceName: balancedVoice?.name || balancedVoice?.label || "Default browser voice"
328326
}),
329-
createTextToSpeechProfile({
330-
emotions: createDefaultEmotionSettings(),
331-
gender: "male",
332-
id: "man-profile-1",
333-
language: manVoice?.language || TEXT_TO_SPEECH_DEFAULTS.language,
334-
name: "Man Profile 1",
335-
voice: manVoice?.value || "",
336-
voiceName: manVoice?.name || manVoice?.label || "Default browser voice"
337-
}),
338-
createTextToSpeechProfile({
339-
emotions: createDefaultEmotionSettings(),
340-
gender: "female",
341-
id: "woman-profile-2",
342-
language: womanVoice?.language || TEXT_TO_SPEECH_DEFAULTS.language,
343-
name: "Woman Profile 2",
344-
voice: womanVoice?.value || "",
345-
voiceName: womanVoice?.name || womanVoice?.label || "Default browser voice"
346-
})
347327
];
348328
}
349329

330+
function signInUrl() {
331+
if (typeof document === "undefined" || typeof window === "undefined") {
332+
return SIGN_IN_ROUTE;
333+
}
334+
return new URL(SIGN_IN_ROUTE, document.baseURI || window.location.href).href;
335+
}
336+
350337
function isDefaultBrowserVoice(value) {
351338
return ["browser default", "default", "default browser voice"].includes(String(value || "").trim().toLowerCase());
352339
}
@@ -851,6 +838,38 @@ function initializeTextToSpeechTool(root = document, { engine = new TextToSpeech
851838
return errors;
852839
}
853840

841+
function currentSessionState() {
842+
try {
843+
const session = getSessionCurrent();
844+
return {
845+
apiAvailable: true,
846+
authenticated: Boolean(session?.authenticated && session.userKey),
847+
session,
848+
};
849+
} catch (error) {
850+
console.warn("Text To Speech could not verify the current session.", error instanceof Error ? error.message : String(error || ""));
851+
return {
852+
apiAvailable: false,
853+
authenticated: false,
854+
session: null,
855+
};
856+
}
857+
}
858+
859+
function requireAuthenticatedWrite(action) {
860+
const sessionState = currentSessionState();
861+
if (!sessionState.apiAvailable) {
862+
writeStatus("Session status could not be verified. Try again shortly.", "FAIL");
863+
return false;
864+
}
865+
if (!sessionState.authenticated) {
866+
writeStatus(`Sign in before ${action}.`, "FAIL");
867+
window.location.href = signInUrl();
868+
return false;
869+
}
870+
return true;
871+
}
872+
854873
function profileApiPayload(profile) {
855874
return {
856875
active: profile.active !== false,
@@ -947,6 +966,9 @@ function initializeTextToSpeechTool(root = document, { engine = new TextToSpeech
947966
}
948967

949968
function commitProfile(key) {
969+
if (!requireAuthenticatedWrite("saving Text To Speech profiles")) {
970+
return;
971+
}
950972
const profile = profileValues(key);
951973
const errors = validateProfile(profile);
952974
if (errors.length) {
@@ -975,6 +997,9 @@ function initializeTextToSpeechTool(root = document, { engine = new TextToSpeech
975997
}
976998

977999
function deleteProfile(key) {
1000+
if (!requireAuthenticatedWrite("deleting Text To Speech profiles")) {
1001+
return;
1002+
}
9781003
const profile = state.profiles.find((candidate) => candidate.id === key);
9791004
if (!profile) return;
9801005
if (profileInUseByMessageStudio(profile)) {
@@ -1004,6 +1029,9 @@ function initializeTextToSpeechTool(root = document, { engine = new TextToSpeech
10041029
}
10051030

10061031
function commitEmotion(key) {
1032+
if (!requireAuthenticatedWrite("saving Text To Speech emotion settings")) {
1033+
return;
1034+
}
10071035
const emotion = emotionValues(key);
10081036
const errors = validateEmotion(emotion, key === NEW_ROW_KEY ? "" : key);
10091037
if (errors.length) {
@@ -1046,6 +1074,9 @@ function initializeTextToSpeechTool(root = document, { engine = new TextToSpeech
10461074
}
10471075

10481076
function deleteEmotion(key) {
1077+
if (!requireAuthenticatedWrite("deleting Text To Speech emotion settings")) {
1078+
return;
1079+
}
10491080
const profile = selectedProfile();
10501081
const emotion = profile?.emotions.find((candidate) => candidate.id === key);
10511082
if (!profile || !emotion) return;

docs_build/database/seed/messages.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"Notification"
1717
],
1818
"messages_emotion_profiles": [
19+
"Neutral",
1920
"Calm",
2021
"Urgent",
2122
"Whisper",
@@ -25,8 +26,7 @@
2526
"Mysterious"
2627
],
2728
"messages_tts_profiles": [
28-
"Browser Speech Default",
29-
"Narration Preview"
29+
"Default Balanced Profile"
3030
],
3131
"messages_records": [],
3232
"messages_segments": []
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# PR_26177_BRAVO_005 Fix TTS Seed Profiles And Guest Save Routing
2+
3+
## Branch Validation
4+
5+
| Check | Result | Notes |
6+
| --- | --- | --- |
7+
| Active branch | PASS | `bravo/26177-text-to-speech` |
8+
| Did not switch to main | PASS | Work stayed on the active Bravo branch. |
9+
| Scope | PASS | Changed only Team Bravo Text To Speech seed/profile usability, guest write routing, and targeted impacted tests. |
10+
| No governance changes | PASS | No governance files were edited. |
11+
| No `start_of_day` changes | PASS | `git status --short -- docs_build/dev/start_of_day start_of_day` returned no changed files. |
12+
13+
## Removed Invalid Profiles/Emotions
14+
15+
| Record | Result | Notes |
16+
| --- | --- | --- |
17+
| `Default Balanced Profile` | KEPT | Default-safe browser profile using `Default browser voice`, `en-US`, and Neutral/Calm/Urgent emotion settings. |
18+
| `Hero` | NOT PRESENT | No active runtime/static TTS seed row was present. No browser-owned cleanup was added. |
19+
| `Merchant` | NOT PRESENT | No active runtime/static TTS seed row was present. No browser-owned cleanup was added. |
20+
| `Neutral` | KEPT | Safe default emotion setting. |
21+
| `Robot` | REMOVED | Removed from runtime Emotion Profile seed, TTS profile emotion settings, and TTS emotion authoring options because it was sample/stylized seed data rather than default-safe TTS data. |
22+
| `Man Profile 1` | REMOVED | Removed unsupported starter profile seed; it did not persist a real gender/voice-filter distinction from Default Balanced. |
23+
| `Woman Profile 2` | REMOVED | Removed unsupported starter profile seed and its Robot setting. |
24+
| `Browser Speech Default` / `Narration Preview` | REMOVED | Removed stale static seed inventory names and aligned static seed inventory to the runtime `Default Balanced Profile`. |
25+
26+
## Guest Save Routing Checklist
27+
28+
| Requirement | Result | Notes |
29+
| --- | --- | --- |
30+
| Guest browsing allowed | PASS | GET/list routes for TTS profiles/emotions remain open. |
31+
| Guest saving blocked in UI | PASS | TTS Profile save/delete and Emotion setting save/delete now check the current session before mutating state. |
32+
| Guest write routes to sign-in | PASS | Unauthenticated TTS write actions redirect to `account/sign-in.html`. |
33+
| Guest saving blocked in API | PASS | Local API rejects unauthenticated POST writes to `tts-profiles` and `emotion-profiles`. |
34+
| No localStorage/product-data fallback | PASS | Text To Speech still uses Local API profile contracts; no browser-owned product data was added. |
35+
| No silent fallback profiles | PASS | Runtime and helper seeds now expose only the explicit safe default profile. |
36+
37+
## Validation Lane Report
38+
39+
| Command | Result | Notes |
40+
| --- | --- | --- |
41+
| `node --check assets/toolbox/text-to-speech/js/index.js` | PASS | Syntax check passed. |
42+
| `node --check src/dev-runtime/messages/messages-postgres-service.mjs` | PASS | Syntax check passed. |
43+
| `node --check src/dev-runtime/server/local-api-router.mjs` | PASS | Syntax check passed. |
44+
| `node --check tests/playwright/tools/TextToSpeechFunctional.spec.mjs` | PASS | Syntax check passed. |
45+
| `node --check tests/playwright/tools/MessagesTool.spec.mjs` | PASS | Syntax check passed after impacted seed-reference updates. |
46+
| `node --check tests/playwright/tools/EventsTool.spec.mjs` | PASS | Syntax check passed. |
47+
| `node --test tests/tools/Text2SpeechShell.test.mjs` | PASS | 6/6 tests passed. |
48+
| `node --test tests/dev-runtime/MessagesPublishValidation.test.mjs` | PASS | 6/6 tests passed, including guest TTS write rejection. |
49+
| `node --test --test-name-pattern "Messages Local API seeds" tests/dev-runtime/DbSeedIntegrity.test.mjs` | PASS | Targeted Messages seed-integrity case passed. |
50+
| `node --test tests/dev-runtime/DbSeedIntegrity.test.mjs` | PARTIAL | Targeted Messages seed test passed; two unrelated Local DB snapshot tests still fail on `/api/local-db/snapshot`. |
51+
| `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs tests/playwright/tools/MessagesTool.spec.mjs tests/playwright/tools/EventsTool.spec.mjs --project=playwright` | BLOCKED/FAIL | Browser launch failed before page runtime because Chromium is missing at `C:\Users\davidq\AppData\Local\ms-playwright\chromium-1217\chrome-win64\chrome.exe`. |
52+
| `git diff --check` | PASS | No whitespace errors. Git warned touched files may be normalized from LF to CRLF when Git writes them. |
53+
54+
## Manual Validation Notes
55+
56+
- Source inspection confirmed `Default browser voice` remains accepted by the Text To Speech preview resolver when a playable browser voice exists.
57+
- Source inspection confirmed TTS write actions call session verification before save/delete state mutation.
58+
- Source inspection confirmed no browser-owned product data, local storage SSoT, governance edits, or `start_of_day` changes were introduced.
59+
- Playwright browser validation could not complete locally because the configured Chromium binary is missing.
60+
61+
## Known Issues
62+
63+
- Local Playwright validation remains blocked until Chromium is installed for the configured Playwright version.
64+
- The broader `DbSeedIntegrity.test.mjs` file still has unrelated Local DB snapshot failures outside the Messages/TTS seed case.
65+
66+
## Output Files
67+
68+
- `docs_build/dev/reports/codex_review.diff`
69+
- `docs_build/dev/reports/codex_changed_files.txt`
70+
- `docs_build/dev/reports/PR_26177_BRAVO_005-fix-tts-seed-profiles-and-guest-save-routing.md`
71+
- `tmp/PR_26177_BRAVO_005-fix-tts-seed-profiles-and-guest-save-routing_delta.zip`
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
assets/toolbox/text-to-speech/js/index.js
2+
docs_build/database/seed/messages.json
23
docs_build/dev/reports/codex_changed_files.txt
34
docs_build/dev/reports/codex_review.diff
4-
docs_build/dev/reports/PR_26177_BRAVO_004-fix-emotion-preview-parent-voice.md
5+
docs_build/dev/reports/PR_26177_BRAVO_005-fix-tts-seed-profiles-and-guest-save-routing.md
6+
src/dev-runtime/messages/messages-postgres-service.mjs
7+
src/dev-runtime/server/local-api-router.mjs
8+
tests/dev-runtime/DbSeedIntegrity.test.mjs
9+
tests/dev-runtime/MessagesPublishValidation.test.mjs
10+
tests/playwright/tools/EventsTool.spec.mjs
11+
tests/playwright/tools/MessagesTool.spec.mjs
512
tests/playwright/tools/TextToSpeechFunctional.spec.mjs
613
tests/tools/Text2SpeechShell.test.mjs

0 commit comments

Comments
 (0)