Skip to content

Commit 2a362e0

Browse files
author
DavidQ
committed
Allow concurrent named speech and refine named item editing - PR_26130_016-text-to-speech-v2-named-speech-selection
1 parent d976cb1 commit 2a362e0

12 files changed

Lines changed: 436 additions & 191 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# PR_26130_016-text-to-speech-v2-named-speech-selection
2+
3+
## Purpose
4+
5+
Update Text to Speech V2 named speech selection so speech items can be started independently, repeat controls are removed, and item naming is editable and schema-valid.
6+
7+
## Scope
8+
9+
Changed only Text to Speech V2 runtime/UI/schema/defaults, the shared Text to Speech engine/default modules, and Workspace Manager V2 Playwright coverage for this tool.
10+
11+
No unrelated files were modified. No `start_of_day` files were changed.
12+
13+
## Implementation Summary
14+
15+
- Removed Repeat Count and Delay Between Repeats from UI, schema, defaults, item data, hydration, and tests.
16+
- Added safe migration for older queue items that still contain `repeatCount` or `delayBetweenRepeatsMs`; the fields are stripped before render and workspace launch data is marked dirty for migration.
17+
- Changed visible text labels from `Text` / `Speech text` to `Text to Speak`.
18+
- Added a Name input above Add/Duplicate/Delete.
19+
- Blocked Add when Name is empty with a visible status message.
20+
- Populated Name from the selected tile and wired Name edits to update the selected item name, tile label, summary, and dirty toolState.
21+
- Changed speech start behavior so starting another named speech item does not call global `speechSynthesis.cancel()`.
22+
- Added active speaker tracking by selected speech item id/name.
23+
- Added selected Stop handling that uses global cancel only when it will not stop other tracked speakers.
24+
- Added Stop All as the explicit global-cancel action for multiple active speakers.
25+
- Updated the `D&D Dungeon Master` preset to `ssmlLikePreset=normal`, `volume=1`, `rate=0.8`, and `pitch=0.5`.
26+
27+
## Playwright Impact
28+
29+
Playwright impacted: Yes.
30+
31+
The Workspace Manager V2 Playwright suite validates:
32+
33+
- Multiple named speech items can be started without canceling prior speakers.
34+
- Starting a new named speech item does not call global `speechSynthesis.cancel()`.
35+
- Stop refuses selected-speaker global cancel while multiple speakers are active, and Stop All is explicit.
36+
- Repeat Count and Delay Between Repeats controls are removed.
37+
- Schema/default queue items no longer require repeat or delay fields.
38+
- `Text to Speak` label appears and `Speech text` is not rendered.
39+
- Add is blocked when Name is empty.
40+
- Selected tiles populate Name.
41+
- Name edits update the selected tile/item and dirty toolState.
42+
- `D&D Dungeon Master` applies rate `0.8`, pitch `0.5`, volume `1`, and SSML-like preset `normal`.
43+
44+
Expected pass behavior: named speech starts append active speakers without canceling existing ones, removed fields stay out of rendered/current queue data, and item naming changes remain schema-valid.
45+
46+
Expected fail behavior: Playwright fails if removed controls reappear, schema still requires repeat/delay, starting another speech item calls cancel, or Name can create invalid empty queue items.
47+
48+
## Validation
49+
50+
Passed:
51+
52+
```text
53+
npm run test:workspace-v2
54+
```
55+
56+
Result:
57+
58+
```text
59+
28 passed
60+
```
61+
62+
Additional checks passed:
63+
64+
```text
65+
npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "Text to Speech V2"
66+
node --check src/engine/audio/TextToSpeechDefaults.js
67+
node --check src/engine/audio/TextToSpeechEngine.js
68+
node --check tools/text2speach-V2/js/TextToSpeechToolApp.js
69+
node --check tools/text2speach-V2/js/bootstrap.js
70+
node --check tools/text2speach-V2/js/controls/ActionNavControl.js
71+
node --check tools/text2speach-V2/js/controls/QueueControl.js
72+
node --check tools/text2speach-V2/js/controls/SpeechOptionsControl.js
73+
node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs
74+
node -e "JSON.parse(require('node:fs').readFileSync('tools/schemas/tools/text2speach-V2.schema.json','utf8')); console.log('schema ok')"
75+
git diff --check HEAD -- .
76+
rg -n -P "<script(?![^>]*\bsrc=)|<style|\son[a-zA-Z]+=" tools/text2speach-V2/index.html
77+
rg -n "tools/shared|imageDataUrl|start_of_day" src/engine/audio tools/text2speach-V2 tools/schemas/tools/text2speach-V2.schema.json tests/playwright/tools/WorkspaceManagerV2.spec.mjs
78+
```
79+
80+
The inline HTML restriction scan and forbidden-scope scan returned no matches. `git diff --check` reported only existing Windows line-ending warnings and no whitespace errors.
81+
82+
## V8 Coverage
83+
84+
The `npm run test:workspace-v2` run generated the Playwright V8 coverage report at:
85+
86+
```text
87+
docs/dev/reports/playwright_v8_coverage_report.txt
88+
```
89+
90+
Changed runtime JS coverage included:
91+
92+
- `src/engine/audio/TextToSpeechDefaults.js`
93+
- `src/engine/audio/TextToSpeechEngine.js`
94+
- `tools/text2speach-V2/js/TextToSpeechToolApp.js`
95+
- `tools/text2speach-V2/js/bootstrap.js`
96+
- `tools/text2speach-V2/js/controls/ActionNavControl.js`
97+
- `tools/text2speach-V2/js/controls/QueueControl.js`
98+
- `tools/text2speach-V2/js/controls/SpeechOptionsControl.js`
99+
100+
The coverage guard reported no low-coverage changed runtime JS files.
101+
102+
## Full Samples Smoke Test
103+
104+
Skipped. This PR is limited to Text to Speech V2 named speech runtime behavior, UI controls, schema/default queue data, and Workspace Manager V2 tool coverage. It does not modify the shared sample loader, sample JSON, or broad game launch behavior.
105+
106+
## ZIP Artifact
107+
108+
Repo-structured delta ZIP:
109+
110+
```text
111+
tmp/PR_26130_016-text-to-speech-v2-named-speech-selection_delta.zip
112+
```
113+
114+
## Manual Validation Steps
115+
116+
1. Open `tools/text2speach-V2/index.html`.
117+
2. Confirm the center accordion reads `Text to Speak` and no visible `Speech text` label appears.
118+
3. Confirm Repeat Count and Delay Between Repeats controls are absent.
119+
4. Select different named speech tiles and confirm the Name input updates.
120+
5. Edit Name and confirm the selected tile label updates.
121+
6. Clear Name and click Add; confirm the status log blocks Add with an actionable message.
122+
7. Start one named speech item, select another tile, and start it; confirm the log shows two active speakers and no global cancel.
123+
8. Click Stop with multiple active speakers; confirm it refuses selected stop without global cancel.
124+
9. Click Stop All; confirm all tracked speakers clear.
125+
10. Select `D&D Dungeon Master` and confirm Rate is `0.8`, Pitch is `0.5`, Volume is `1`, and SSML-like preset is `normal`.
126+
127+
Expected outcome: named speech selection and playback remain independent, removed repeat fields are not rendered or required, and Name edits keep queue data schema-valid.
128+
129+
## Changed Files
130+
131+
- `src/engine/audio/TextToSpeechDefaults.js`
132+
- `src/engine/audio/TextToSpeechEngine.js`
133+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
134+
- `tools/schemas/tools/text2speach-V2.schema.json`
135+
- `tools/text2speach-V2/index.html`
136+
- `tools/text2speach-V2/js/TextToSpeechToolApp.js`
137+
- `tools/text2speach-V2/js/bootstrap.js`
138+
- `tools/text2speach-V2/js/controls/ActionNavControl.js`
139+
- `tools/text2speach-V2/js/controls/QueueControl.js`
140+
- `tools/text2speach-V2/js/controls/SpeechOptionsControl.js`
141+
- `tools/text2speach-V2/styles/text2speach-V2.css`
142+
- `docs/dev/reports/PR_26130_016-text-to-speech-v2-named-speech-selection.md`
143+
- `docs/dev/reports/codex_review.diff`
144+
- `docs/dev/reports/codex_changed_files.txt`
145+
- `docs/dev/codex_commands.md`
146+
- `docs/dev/commit_comment.txt`

src/engine/audio/TextToSpeechDefaults.js

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ const TEXT_TO_SPEECH_QUEUE_MODE_OPTIONS = Object.freeze([
3838
Object.freeze({ label: "Replace current speech", value: "replace" })
3939
]);
4040

41-
const TEXT_TO_SPEECH_REPEAT_COUNT_OPTIONS = Object.freeze([
42-
Object.freeze({ label: "1", value: 1 }),
43-
Object.freeze({ label: "2", value: 2 }),
44-
Object.freeze({ label: "3", value: 3 }),
45-
Object.freeze({ label: "Loop", value: "loop" })
46-
]);
47-
4841
const TEXT_TO_SPEECH_CHARACTER_PRESET_OPTIONS = Object.freeze([
4942
Object.freeze({ label: "Manual", value: "manual" }),
5043
Object.freeze({ label: "Alert", value: "alert" }),
@@ -68,7 +61,6 @@ const TEXT_TO_SPEECH_SSML_LIKE_PRESET_DEFAULTS = Object.freeze({
6861
});
6962

7063
const TEXT_TO_SPEECH_RANGE_DEFAULTS = Object.freeze({
71-
delayBetweenRepeatsMs: Object.freeze({ max: 5000, min: 0, step: 100, value: 0 }),
7264
pitch: Object.freeze({ max: 2, min: 0.1, step: 0.1, value: 1 }),
7365
rate: Object.freeze({ max: 2, min: 0.1, step: 0.1, value: 1 }),
7466
volume: Object.freeze({ max: 1, min: 0, step: 0.01, value: 1 })
@@ -77,7 +69,7 @@ const TEXT_TO_SPEECH_RANGE_DEFAULTS = Object.freeze({
7769
const TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS = Object.freeze({
7870
alert: Object.freeze({ pitch: 0.9, rate: 1.3, ssmlLikePreset: "normal", volume: 1 }),
7971
calm: Object.freeze({ pitch: 1, rate: 0.8, ssmlLikePreset: "normal", volume: 1 }),
80-
"dnd-dungeon-master": Object.freeze({ pitch: 0.7, rate: 0.9, ssmlLikePreset: "normal", volume: 1 }),
72+
"dnd-dungeon-master": Object.freeze({ pitch: 0.5, rate: 0.8, ssmlLikePreset: "normal", volume: 1 }),
8173
dramatic: Object.freeze({ pitch: 1.2, rate: 1.1, ssmlLikePreset: "normal", volume: 1 }),
8274
manual: Object.freeze({ pitch: 1, rate: 1, ssmlLikePreset: "normal", volume: 1 }),
8375
narrator: Object.freeze({ pitch: 1, rate: 1, ssmlLikePreset: "normal", volume: 1 }),
@@ -97,22 +89,18 @@ const TEXT_TO_SPEECH_QUEUE_ITEM_REQUIRED_FIELDS = Object.freeze([
9789
"pitch",
9890
"queueMode",
9991
"autoSpeak",
100-
"repeatCount",
101-
"delayBetweenRepeatsMs",
10292
"characterPreset",
10393
"ssmlLikePreset"
10494
]);
10595

10696
const TEXT_TO_SPEECH_DEFAULT_OPTIONS = Object.freeze({
10797
autoSpeak: false,
10898
characterPreset: "manual",
109-
delayBetweenRepeatsMs: TEXT_TO_SPEECH_RANGE_DEFAULTS.delayBetweenRepeatsMs.value,
11099
gender: "any",
111100
language: "en-US",
112101
pitch: TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.manual.pitch,
113102
queueMode: "replace",
114103
rate: TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.manual.rate,
115-
repeatCount: 1,
116104
ssmlLikePreset: TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.manual.ssmlLikePreset,
117105
voice: "",
118106
voiceAge: "any",
@@ -137,15 +125,13 @@ const TEXT_TO_SPEECH_DEFAULT_QUEUE = Object.freeze([
137125
...TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.dramatic,
138126
autoSpeak: false,
139127
characterPreset: "dramatic",
140-
delayBetweenRepeatsMs: 500,
141128
gender: "male-preferred",
142129
id: "hero-ready",
143130
language: "en-GB",
144131
name: "Hero ready",
145132
pitch: 1.4,
146133
queueMode: "append",
147134
rate: 1.2,
148-
repeatCount: 2,
149135
text: "Systems ready. The hero prompt is queued for an upbeat menu confirmation.",
150136
voice: "mock-google-uk-english-male",
151137
voiceAge: "teen"
@@ -155,15 +141,13 @@ const TEXT_TO_SPEECH_DEFAULT_QUEUE = Object.freeze([
155141
...TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.alert,
156142
autoSpeak: false,
157143
characterPreset: "alert",
158-
delayBetweenRepeatsMs: 1000,
159144
gender: "female-preferred",
160145
id: "alert-warning",
161146
language: "en-US",
162147
name: "Alert warning",
163148
pitch: 0.9,
164149
queueMode: "replace",
165150
rate: 1.3,
166-
repeatCount: 3,
167151
text: "Warning. Incoming hazard detected. Please confirm the next action.",
168152
voice: "mock-microsoft-zira",
169153
voiceAge: "adult",
@@ -201,7 +185,6 @@ export {
201185
TEXT_TO_SPEECH_QUEUE_ITEM_REQUIRED_FIELDS,
202186
TEXT_TO_SPEECH_QUEUE_MODE_OPTIONS,
203187
TEXT_TO_SPEECH_RANGE_DEFAULTS,
204-
TEXT_TO_SPEECH_REPEAT_COUNT_OPTIONS,
205188
TEXT_TO_SPEECH_SAMPLE_TEXT,
206189
TEXT_TO_SPEECH_SCHEMA_ID,
207190
TEXT_TO_SPEECH_SSML_LIKE_PRESET_DEFAULTS,

0 commit comments

Comments
 (0)