Skip to content

Commit 20e0a23

Browse files
author
DavidQ
committed
Add full schema-driven browser TTS options to text2speach-V2 - PR_26130_009-text2speach-v2-full-tts-options
1 parent 55e1ce6 commit 20e0a23

14 files changed

Lines changed: 1278 additions & 128 deletions
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# PR_26130_009-text2speach-v2-full-tts-options
2+
3+
## Purpose
4+
5+
Expand `text2speach-V2` from a baseline speech sample into a schema-backed, workspace-embeddable full TTS options tool while keeping the exact tool-facing name `text2speach-V2` and preserving `/src/engine/audio/` structure.
6+
7+
## Scope
8+
9+
Changed only Workspace Manager V2/text2speach-V2 lifecycle, queue, schema, defaults, UI, and Playwright coverage surfaces.
10+
11+
No `start_of_day` files were changed.
12+
13+
`docs/dev/codex_commands.md` and `docs/dev/commit_comment.txt` were updated locally as required and remain ignored so they cannot be committed.
14+
15+
## Implementation Summary
16+
17+
- Added full enum/default coverage in `src/engine/audio/TextToSpeechDefaults.js` for language, queue mode, repeat count, character preset, ssml-like preset, numeric ranges, required queue fields, and schema-complete default queue data.
18+
- Expanded `TextToSpeechEngine` to use selected `SpeechSynthesis` voices and to support speak, pause, resume, stop, replace/append queue mode, repeat count, loop mode, and delay between repeats.
19+
- Added SpeechSynthesis `voiceschanged` handling so browsers that load voices after page startup repopulate the Voice dropdown and re-enable Speak when a real voice becomes available.
20+
- Rebuilt `text2speach-V2` UI controls around textarea text input, voice/language dropdowns, range sliders, queue mode, auto speak, repeat/delay, character preset, ssml-like preset, and direct/workspace action buttons.
21+
- Added `tools/schemas/tools/text2speach-V2.schema.json`; every queue item requires all defined speech options.
22+
- Registered the payload schema in `tools/schemas/workspace.manifest.schema.json`.
23+
- Updated Workspace Manager V2 toolState hydration so selected-game text2speach-V2 launches receive schema-complete default queue data when the game manifest has no text2speach-V2 payload.
24+
- Updated save/log item details to report `text2speach-V2 queue=3`.
25+
26+
## Tool Completion Status
27+
28+
Failing tool before: `text2speach-V2` did not expose the full TTS option set, did not have a workspace payload schema requiring all speech item options, and did not hydrate schema-complete queue data for Workspace Manager V2 launch/save flows.
29+
30+
Tool fixed: `text2speach-V2`.
31+
32+
Remaining failures after targeted validation: none found in `npm run test:workspace-v2`.
33+
34+
## Playwright Impact
35+
36+
Playwright impacted: Yes.
37+
38+
Coverage added/updated for:
39+
40+
- full option rendering
41+
- schema-valid default queue
42+
- required queue item option keys
43+
- voice dropdown population from `speechSynthesis.getVoices()`
44+
- voice dropdown recovery when `getVoices()` is empty at startup and `voiceschanged` fires later
45+
- volume/rate/pitch/delay numeric sliders
46+
- queue mode
47+
- auto speak
48+
- repeat count and delay
49+
- character preset
50+
- ssml-like preset
51+
- speak/pause/resume/stop actions
52+
- Workspace Manager V2 launch of text2speach-V2
53+
- Workspace Manager V2 toolState hydration/save context containing schema-complete text2speach-V2 queue data
54+
55+
Expected pass behavior: text2speach-V2 renders all full TTS options, enables Speak only when text and a live voice are available, calls the expected SpeechSynthesis APIs, and persists schema-valid queue data through Workspace Manager V2 toolState context.
56+
57+
Expected fail behavior: tests fail if any required queue option is missing, controls are not populated from enum/default/runtime voice sources, numeric controls are not sliders, speech action buttons do not call the expected APIs, or Workspace Manager V2 omits the text2speach-V2 schema-complete queue payload.
58+
59+
## Validation
60+
61+
Passed:
62+
63+
```text
64+
npm run test:workspace-v2
65+
```
66+
67+
Result:
68+
69+
```text
70+
26 passed
71+
```
72+
73+
Additional checks:
74+
75+
```text
76+
node --check src/engine/audio/TextToSpeechDefaults.js
77+
node --check src/engine/audio/TextToSpeechEngine.js
78+
node --check tools/text2speach-V2/js/TextToSpeechToolApp.js
79+
node --check tools/text2speach-V2/js/bootstrap.js
80+
node --check tools/text2speach-V2/js/controls/SpeechOptionsControl.js
81+
node --check tools/text2speach-V2/js/controls/QueueControl.js
82+
node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs
83+
node -e "JSON.parse(require('node:fs').readFileSync('tools/schemas/tools/text2speach-V2.schema.json','utf8'));"
84+
node -e "JSON.parse(require('node:fs').readFileSync('tools/schemas/workspace.manifest.schema.json','utf8'));"
85+
git diff --check
86+
```
87+
88+
The workspace-v2 Playwright run also generated advisory V8 coverage reports:
89+
90+
- `docs/dev/reports/playwright_v8_coverage_report.txt`
91+
- `docs/dev/reports/coverage_changed_js_guardrail.txt`
92+
93+
## Full Samples Smoke Test
94+
95+
Skipped. The full samples smoke test is intentionally out of scope because this PR is limited to text2speach-V2 full TTS options/schema/toolState behavior and targeted Workspace Manager V2 coverage, not broad sample runtime behavior.
96+
97+
## ZIP Artifact
98+
99+
Repo-structured delta ZIP:
100+
101+
```text
102+
tmp/PR_26130_009-text2speach-v2-full-tts-options_delta.zip
103+
```
104+
105+
## Manual Validation Steps
106+
107+
1. Open `tools/text2speach-V2/index.html`.
108+
2. Confirm the Speech Queue has named default sentences and the Speech Options section shows voice, language, volume, rate/speed, pitch, queue mode, auto speak, repeat count, delay, character preset, and ssml-like preset controls.
109+
3. Select a voice, edit the text, and use Speak, Pause, Resume, and Stop.
110+
4. Open Workspace Manager V2, pick a repo folder, select a game, launch `text2speach-V2`, and confirm Repo Destination is not shown and the workspace action buttons are available.
111+
5. Return to Workspace Manager V2 and confirm the selected game/toolState remains active.
112+
113+
Expected outcome: all controls remain enabled according to voice/text availability, speech actions update the status log, and Workspace Manager V2 retains schema-complete text2speach-V2 queue data.
114+
115+
## Changed Files
116+
117+
- `src/engine/audio/TextToSpeechDefaults.js`
118+
- `src/engine/audio/TextToSpeechEngine.js`
119+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
120+
- `tools/schemas/tools/text2speach-V2.schema.json`
121+
- `tools/schemas/workspace.manifest.schema.json`
122+
- `tools/text2speach-V2/index.html`
123+
- `tools/text2speach-V2/js/TextToSpeechToolApp.js`
124+
- `tools/text2speach-V2/js/bootstrap.js`
125+
- `tools/text2speach-V2/js/controls/ActionNavControl.js`
126+
- `tools/text2speach-V2/js/controls/QueueControl.js`
127+
- `tools/text2speach-V2/js/controls/SpeechOptionsControl.js`
128+
- `tools/text2speach-V2/styles/text2speach-V2.css`
129+
- `tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js`
130+
- `docs/dev/reports/PR_26130_009-text2speach-v2-full-tts-options.md`
131+
- `docs/dev/reports/codex_review.diff`
132+
- `docs/dev/reports/codex_changed_files.txt`
133+
- `docs/dev/reports/playwright_v8_coverage_report.txt`
134+
- `docs/dev/reports/coverage_changed_js_guardrail.txt`
135+
- `docs/dev/codex_commands.md`
136+
- `docs/dev/commit_comment.txt`
Lines changed: 112 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const TEXT_TO_SPEECH_TOOL_ID = "text2speach-V2";
2-
3-
const TEXT_TO_SPEECH_SAMPLE_TEXT = "Welcome to Toolbox Aid. This is the default text2speach-V2 sample line for previewing narration, prompts, and menu feedback.";
2+
const TEXT_TO_SPEECH_SCHEMA_ID = "tools/schemas/tools/text2speach-V2.schema.json";
3+
const TEXT_TO_SPEECH_PAYLOAD_SCHEMA = "html-js-gaming.text2speach-V2";
44

55
const TEXT_TO_SPEECH_LANGUAGE_OPTIONS = Object.freeze([
66
Object.freeze({ label: "English (US)", value: "en-US" }),
@@ -10,38 +10,127 @@ const TEXT_TO_SPEECH_LANGUAGE_OPTIONS = Object.freeze([
1010
Object.freeze({ label: "Japanese", value: "ja-JP" })
1111
]);
1212

13-
const TEXT_TO_SPEECH_RATE_OPTIONS = Object.freeze([
14-
Object.freeze({ label: "Slow", value: 0.75 }),
15-
Object.freeze({ label: "Normal", value: 1 }),
16-
Object.freeze({ label: "Fast", value: 1.25 })
13+
const TEXT_TO_SPEECH_QUEUE_MODE_OPTIONS = Object.freeze([
14+
Object.freeze({ label: "Replace current speech", value: "replace" }),
15+
Object.freeze({ label: "Append to queue", value: "append" })
1716
]);
1817

19-
const TEXT_TO_SPEECH_PITCH_OPTIONS = Object.freeze([
20-
Object.freeze({ label: "Low", value: 0.8 }),
21-
Object.freeze({ label: "Natural", value: 1 }),
22-
Object.freeze({ label: "Bright", value: 1.2 })
18+
const TEXT_TO_SPEECH_REPEAT_COUNT_OPTIONS = Object.freeze([
19+
Object.freeze({ label: "1", value: 1 }),
20+
Object.freeze({ label: "2", value: 2 }),
21+
Object.freeze({ label: "3", value: 3 }),
22+
Object.freeze({ label: "Loop", value: "loop" })
2323
]);
2424

25-
const TEXT_TO_SPEECH_VOLUME_OPTIONS = Object.freeze([
26-
Object.freeze({ label: "Quiet", value: 0.5 }),
27-
Object.freeze({ label: "Medium", value: 0.8 }),
28-
Object.freeze({ label: "Full", value: 1 })
25+
const TEXT_TO_SPEECH_CHARACTER_PRESET_OPTIONS = Object.freeze([
26+
Object.freeze({ label: "Narrator", value: "narrator" }),
27+
Object.freeze({ label: "Hero", value: "hero" }),
28+
Object.freeze({ label: "Villain", value: "villain" }),
29+
Object.freeze({ label: "Alert", value: "alert" })
2930
]);
3031

31-
const TEXT_TO_SPEECH_DEFAULTS = Object.freeze({
32+
const TEXT_TO_SPEECH_SSML_LIKE_PRESET_OPTIONS = Object.freeze([
33+
Object.freeze({ label: "Normal", value: "normal" }),
34+
Object.freeze({ label: "Whisper-ish", value: "whisper-ish" }),
35+
Object.freeze({ label: "Announcement", value: "announcement" }),
36+
Object.freeze({ label: "Slow", value: "slow" })
37+
]);
38+
39+
const TEXT_TO_SPEECH_RANGE_DEFAULTS = Object.freeze({
40+
delayBetweenRepeatsMs: Object.freeze({ max: 5000, min: 0, step: 100, value: 0 }),
41+
pitch: Object.freeze({ max: 2, min: 0, step: 0.1, value: 1 }),
42+
rate: Object.freeze({ max: 10, min: 0.1, step: 0.1, value: 1 }),
43+
volume: Object.freeze({ max: 1, min: 0, step: 0.01, value: 1 })
44+
});
45+
46+
const TEXT_TO_SPEECH_QUEUE_ITEM_REQUIRED_FIELDS = Object.freeze([
47+
"id",
48+
"name",
49+
"text",
50+
"voice",
51+
"language",
52+
"volume",
53+
"rate",
54+
"pitch",
55+
"queueMode",
56+
"autoSpeak",
57+
"repeatCount",
58+
"delayBetweenRepeatsMs",
59+
"characterPreset",
60+
"ssmlLikePreset"
61+
]);
62+
63+
const TEXT_TO_SPEECH_DEFAULT_OPTIONS = Object.freeze({
64+
autoSpeak: false,
65+
characterPreset: "narrator",
66+
delayBetweenRepeatsMs: TEXT_TO_SPEECH_RANGE_DEFAULTS.delayBetweenRepeatsMs.value,
3267
language: "en-US",
33-
pitch: 1,
34-
rate: 1,
35-
sampleText: TEXT_TO_SPEECH_SAMPLE_TEXT,
36-
volume: 1
68+
pitch: TEXT_TO_SPEECH_RANGE_DEFAULTS.pitch.value,
69+
queueMode: "replace",
70+
rate: TEXT_TO_SPEECH_RANGE_DEFAULTS.rate.value,
71+
repeatCount: 1,
72+
ssmlLikePreset: "normal",
73+
voice: "",
74+
volume: TEXT_TO_SPEECH_RANGE_DEFAULTS.volume.value
3775
});
3876

77+
const TEXT_TO_SPEECH_DEFAULT_QUEUE = Object.freeze([
78+
Object.freeze({
79+
...TEXT_TO_SPEECH_DEFAULT_OPTIONS,
80+
id: "narrator-welcome",
81+
name: "Narrator welcome",
82+
text: "Welcome to Toolbox Aid. This is the default text2speach-V2 sample line for previewing narration, prompts, and menu feedback."
83+
}),
84+
Object.freeze({
85+
...TEXT_TO_SPEECH_DEFAULT_OPTIONS,
86+
characterPreset: "hero",
87+
id: "hero-ready",
88+
name: "Hero ready",
89+
pitch: 1.15,
90+
rate: 1.1,
91+
text: "Systems ready. The hero prompt is queued for an upbeat menu confirmation."
92+
}),
93+
Object.freeze({
94+
...TEXT_TO_SPEECH_DEFAULT_OPTIONS,
95+
characterPreset: "alert",
96+
id: "alert-warning",
97+
name: "Alert warning",
98+
pitch: 0.9,
99+
rate: 1.25,
100+
ssmlLikePreset: "announcement",
101+
text: "Warning. Incoming hazard detected. Please confirm the next action."
102+
})
103+
]);
104+
105+
const TEXT_TO_SPEECH_DEFAULT_QUEUE_DATA = Object.freeze({
106+
$schema: TEXT_TO_SPEECH_SCHEMA_ID,
107+
schema: TEXT_TO_SPEECH_PAYLOAD_SCHEMA,
108+
version: 1,
109+
name: "text2speach-V2 default queue",
110+
queue: TEXT_TO_SPEECH_DEFAULT_QUEUE
111+
});
112+
113+
const TEXT_TO_SPEECH_DEFAULTS = Object.freeze({
114+
...TEXT_TO_SPEECH_DEFAULT_OPTIONS,
115+
sampleText: TEXT_TO_SPEECH_DEFAULT_QUEUE[0].text
116+
});
117+
118+
const TEXT_TO_SPEECH_SAMPLE_TEXT = TEXT_TO_SPEECH_DEFAULTS.sampleText;
119+
39120
export {
121+
TEXT_TO_SPEECH_CHARACTER_PRESET_OPTIONS,
122+
TEXT_TO_SPEECH_DEFAULT_OPTIONS,
123+
TEXT_TO_SPEECH_DEFAULT_QUEUE,
124+
TEXT_TO_SPEECH_DEFAULT_QUEUE_DATA,
40125
TEXT_TO_SPEECH_DEFAULTS,
41126
TEXT_TO_SPEECH_LANGUAGE_OPTIONS,
42-
TEXT_TO_SPEECH_PITCH_OPTIONS,
43-
TEXT_TO_SPEECH_RATE_OPTIONS,
127+
TEXT_TO_SPEECH_PAYLOAD_SCHEMA,
128+
TEXT_TO_SPEECH_QUEUE_ITEM_REQUIRED_FIELDS,
129+
TEXT_TO_SPEECH_QUEUE_MODE_OPTIONS,
130+
TEXT_TO_SPEECH_RANGE_DEFAULTS,
131+
TEXT_TO_SPEECH_REPEAT_COUNT_OPTIONS,
44132
TEXT_TO_SPEECH_SAMPLE_TEXT,
45-
TEXT_TO_SPEECH_TOOL_ID,
46-
TEXT_TO_SPEECH_VOLUME_OPTIONS
133+
TEXT_TO_SPEECH_SCHEMA_ID,
134+
TEXT_TO_SPEECH_SSML_LIKE_PRESET_OPTIONS,
135+
TEXT_TO_SPEECH_TOOL_ID
47136
};

0 commit comments

Comments
 (0)