Skip to content

Commit 55e1ce6

Browse files
author
DavidQ
committed
Normalize tool naming to text2speach-V2 across runtime and schema surfaces - PR_26130_008-text2speach-v2-schema-queue
1 parent 25df225 commit 55e1ce6

25 files changed

Lines changed: 1103 additions & 39 deletions
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# PR_26130_008-text2speach-v2-schema-queue
2+
3+
## Summary
4+
5+
- Renamed the prior tool-facing speech surface to the exact name `text2speach-V2`.
6+
- Renamed the tool folder, stylesheet, registry entry, Workspace Manager V2 launch entry, labels, logs, CSS classes, DOM ids, Playwright selectors, and toolState ids.
7+
- Preserved the existing text-to-speech defaults, speaking queue action, Workspace Manager V2 launch behavior, Preview Generator V2 Repo Destination hiding, and shared audio engine location under `src/engine/audio/`.
8+
9+
## Scope
10+
11+
Changed only the text2speach-V2 naming pass and the existing Workspace Manager V2 / Preview Generator V2 coverage surface from the prior speech baseline.
12+
13+
No `start_of_day` files were modified.
14+
15+
No `tools/shared` dependency was added.
16+
17+
No inline script/style/event handlers were added.
18+
19+
`src/engine/audio/` was not renamed or restructured. Its existing modules remain in place; only tool-facing default text/id strings were updated.
20+
21+
## Naming Coverage
22+
23+
Updated tool-facing naming in:
24+
25+
- Tool registry id, display name, folder path, entry point, and README path.
26+
- Tools index utility grouping.
27+
- Workspace Manager V2 launchable tool id/name/path.
28+
- Workspace Manager V2 tile details.
29+
- Tool folder and stylesheet path: `tools/text2speach-V2/`.
30+
- HTML title, `data-tool-id`, header label, CSS classes, DOM ids, and status log id.
31+
- Browser status logs: `text2speach-V2 ready` / `text2speach-V2 unavailable`.
32+
- Playwright helper names, route URLs, selectors, expected labels, status logs, and sessionStorage toolState key assertions.
33+
- ToolState id/session key: `workspace.tools.text2speach-V2`.
34+
35+
Schema/session metadata now carries `toolId: "text2speach-V2"` for Workspace Manager V2 launch-context schema sessions. No new tool payload schema was introduced in this naming-only update.
36+
37+
## Validation
38+
39+
- `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
40+
- `node --check tools/text2speach-V2/js/bootstrap.js`
41+
- `node --check tools/text2speach-V2/js/TextToSpeechToolApp.js`
42+
- `node --check src/engine/audio/TextToSpeechDefaults.js`
43+
- `node --check src/engine/audio/TextToSpeechEngine.js`
44+
- `node --check tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js`
45+
- `npm run test:workspace-v2`: 25 passed
46+
- Inline/dependency guard grep: no matches for inline scripts/styles/event handlers, `tools/shared`, `imageDataUrl`, or `start_of_day` in touched implementation/test paths
47+
- `git diff --check`: passed
48+
49+
Full samples smoke test was skipped because this PR is scoped to text2speach-V2 naming, Workspace Manager V2 launch coverage, and existing speech queue behavior, not broad sample runtime behavior.
50+
51+
## Playwright Coverage
52+
53+
Playwright impacted: Yes.
54+
55+
Validated behavior:
56+
57+
- Direct `text2speach-V2` launch uses `/tools/text2speach-V2/index.html`.
58+
- The first-class tool surface exposes `data-tool-id="text2speach-V2"` and heading text `text2speach-V2`.
59+
- Sample text uses the exact `text2speach-V2` name.
60+
- Enum-populated language, rate, pitch, and volume controls remain available.
61+
- Speak remains disabled for empty text and enabled for non-empty text.
62+
- Speak queues browser speech synthesis and logs the existing queued status.
63+
- Workspace Manager V2 exposes and launches the `text2speach-V2` tile.
64+
- Session Inspector sees the toolState key as `sessionStorage:workspace.tools.text2speach-V2`.
65+
- Returning from the tool keeps Workspace Manager V2 tools enabled.
66+
67+
Expected pass behavior: all tool-facing names use `text2speach-V2`, existing queue/status behavior still works, and Workspace Manager V2 preserves the renamed toolState id.
68+
69+
Expected fail behavior: tests fail if the registry, tools index, Workspace Manager tile, DOM selectors, logs, route, or toolState id regress to a legacy speech-tool name.
70+
71+
## Manual Test
72+
73+
1. Open `/tools/text2speach-V2/index.html`.
74+
2. Confirm the header shows `text2speach-V2`.
75+
3. Confirm the default sample text mentions `text2speach-V2`.
76+
4. Click `Speak`.
77+
5. Expected: status logs `OK Speak queued...` and the output summary shows `speak-queued`.
78+
6. Open Workspace Manager V2, pick the repo folder, open Asteroids, and launch `text2speach-V2`.
79+
7. Expected: the tool opens under `/tools/text2speach-V2/index.html?launch=workspace...`, workspace nav is visible, and returning to Workspace Manager keeps tools enabled.
80+
81+
Out of scope: full samples smoke validation and sample JSON alignment.
82+
83+
## Changed Files
84+
85+
- `docs/dev/codex_commands.md`
86+
- `docs/dev/commit_comment.txt`
87+
- `docs/dev/reports/PR_26130_008-text2speach-v2-schema-queue.md`
88+
- `src/engine/audio/TextToSpeechDefaults.js`
89+
- `src/engine/audio/TextToSpeechEngine.js`
90+
- `src/engine/audio/index.js`
91+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
92+
- `tools/preview-generator-v2/PreviewGeneratorV2App.js`
93+
- `tools/preview-generator-v2/PreviewGeneratorV2Ui.js`
94+
- `tools/preview-generator-v2/controls/RepoDestinationControl.js`
95+
- `tools/preview-generator-v2/index.html`
96+
- `tools/preview-generator-v2/previewGeneratorV2.css`
97+
- `tools/renderToolsIndex.js`
98+
- `tools/text2speach-V2/README.md`
99+
- `tools/text2speach-V2/how_to_use.html`
100+
- `tools/text2speach-V2/index.html`
101+
- `tools/text2speach-V2/js/TextToSpeechToolApp.js`
102+
- `tools/text2speach-V2/js/bootstrap.js`
103+
- `tools/text2speach-V2/js/controls/ActionNavControl.js`
104+
- `tools/text2speach-V2/js/controls/OutputSummaryControl.js`
105+
- `tools/text2speach-V2/js/controls/SpeechOptionsControl.js`
106+
- `tools/text2speach-V2/js/controls/StatusLogControl.js`
107+
- `tools/text2speach-V2/js/controls/TextInputControl.js`
108+
- `tools/text2speach-V2/styles/text2speach-V2.css`
109+
- `tools/toolRegistry.js`
110+
- `tools/workspace-manager-v2/js/controls/ToolTilesControl.js`
111+
- `tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js`
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
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.";
4+
5+
const TEXT_TO_SPEECH_LANGUAGE_OPTIONS = Object.freeze([
6+
Object.freeze({ label: "English (US)", value: "en-US" }),
7+
Object.freeze({ label: "English (UK)", value: "en-GB" }),
8+
Object.freeze({ label: "Spanish (Spain)", value: "es-ES" }),
9+
Object.freeze({ label: "French (France)", value: "fr-FR" }),
10+
Object.freeze({ label: "Japanese", value: "ja-JP" })
11+
]);
12+
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 })
17+
]);
18+
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 })
23+
]);
24+
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 })
29+
]);
30+
31+
const TEXT_TO_SPEECH_DEFAULTS = Object.freeze({
32+
language: "en-US",
33+
pitch: 1,
34+
rate: 1,
35+
sampleText: TEXT_TO_SPEECH_SAMPLE_TEXT,
36+
volume: 1
37+
});
38+
39+
export {
40+
TEXT_TO_SPEECH_DEFAULTS,
41+
TEXT_TO_SPEECH_LANGUAGE_OPTIONS,
42+
TEXT_TO_SPEECH_PITCH_OPTIONS,
43+
TEXT_TO_SPEECH_RATE_OPTIONS,
44+
TEXT_TO_SPEECH_SAMPLE_TEXT,
45+
TEXT_TO_SPEECH_TOOL_ID,
46+
TEXT_TO_SPEECH_VOLUME_OPTIONS
47+
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { TEXT_TO_SPEECH_DEFAULTS } from "./TextToSpeechDefaults.js";
2+
3+
class TextToSpeechEngine {
4+
constructor({
5+
speechSynthesisRef = globalThis.speechSynthesis,
6+
utteranceCtor = globalThis.SpeechSynthesisUtterance
7+
} = {}) {
8+
this.speechSynthesis = speechSynthesisRef;
9+
this.Utterance = utteranceCtor;
10+
}
11+
12+
isSupported() {
13+
return Boolean(this.speechSynthesis
14+
&& typeof this.speechSynthesis.speak === "function"
15+
&& typeof this.Utterance === "function");
16+
}
17+
18+
voices() {
19+
if (!this.speechSynthesis || typeof this.speechSynthesis.getVoices !== "function") {
20+
return [];
21+
}
22+
return this.speechSynthesis.getVoices();
23+
}
24+
25+
voiceForLanguage(language) {
26+
const normalizedLanguage = String(language || "").trim();
27+
return this.voices().find((voice) => voice?.lang === normalizedLanguage) || null;
28+
}
29+
30+
speak({
31+
language = TEXT_TO_SPEECH_DEFAULTS.language,
32+
pitch = TEXT_TO_SPEECH_DEFAULTS.pitch,
33+
rate = TEXT_TO_SPEECH_DEFAULTS.rate,
34+
text = TEXT_TO_SPEECH_DEFAULTS.sampleText,
35+
volume = TEXT_TO_SPEECH_DEFAULTS.volume
36+
} = {}) {
37+
if (!this.isSupported()) {
38+
return { message: "SpeechSynthesis is unavailable in this browser.", ok: false };
39+
}
40+
41+
const normalizedText = String(text || "").trim();
42+
if (!normalizedText) {
43+
return { message: "text2speach-V2 text is required before speaking.", ok: false };
44+
}
45+
46+
const utterance = new this.Utterance(normalizedText);
47+
utterance.lang = String(language || TEXT_TO_SPEECH_DEFAULTS.language);
48+
utterance.pitch = Number(pitch);
49+
utterance.rate = Number(rate);
50+
utterance.volume = Number(volume);
51+
const voice = this.voiceForLanguage(utterance.lang);
52+
if (voice) {
53+
utterance.voice = voice;
54+
}
55+
this.speechSynthesis.cancel();
56+
this.speechSynthesis.speak(utterance);
57+
return {
58+
language: utterance.lang,
59+
ok: true,
60+
pitch: utterance.pitch,
61+
rate: utterance.rate,
62+
text: normalizedText,
63+
voiceName: voice?.name || "browser default",
64+
volume: utterance.volume
65+
};
66+
}
67+
68+
stop() {
69+
if (!this.isSupported()) {
70+
return { message: "SpeechSynthesis is unavailable in this browser.", ok: false };
71+
}
72+
this.speechSynthesis.cancel();
73+
return { ok: true };
74+
}
75+
}
76+
77+
export { TextToSpeechEngine };
78+
export default TextToSpeechEngine;

src/engine/audio/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ export { default as MidiPlayer } from './MidiPlayer.js';
1313
export { default as Synthesizer } from './Synthesizer.js';
1414
export { default as FrequencyPlayer } from './FrequencyPlayer.js';
1515
export { default as PlaylistManager } from './PlaylistManager.js';
16+
export { default as TextToSpeechEngine } from './TextToSpeechEngine.js';
17+
export * from './TextToSpeechDefaults.js';

0 commit comments

Comments
 (0)