Skip to content

Commit 5021008

Browse files
author
DavidQ
committed
Source Text to Speech V2 payloads from JSON and align workspace manifest array data - PR_26130_020-text-to-speech-v2-sample-json-source
1 parent a2d098e commit 5021008

4 files changed

Lines changed: 216 additions & 10 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# PR_26130_023-text-to-speech-v2-layout-height-fix
2+
3+
## Purpose
4+
5+
Fix Text to Speech V2 layout height behavior only.
6+
7+
## Changes
8+
9+
- Removed normal-mode accordion fill behavior that created large empty space in Text to Speak and Named Sentences.
10+
- Added Text to Speech V2-specific layout classes for Output Summary and Status so fullscreen mode can constrain each panel independently.
11+
- Updated fullscreen sizing so left, center, and right columns fit inside the viewport height.
12+
- Constrained Output Summary and Status to scroll within their own panels so Status remains visible.
13+
- Kept Speak, Pause, Resume, and Stop at the bottom of Text to Speak.
14+
15+
## Files Changed
16+
17+
- `tools/text2speach-V2/index.html`
18+
- `tools/text2speach-V2/styles/text2speach-V2.css`
19+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
20+
21+
## Validation
22+
23+
Playwright impacted: Yes.
24+
25+
Command run:
26+
27+
```powershell
28+
npm run test:workspace-v2
29+
```
30+
31+
Result: PASS, 33 tests passed.
32+
33+
Additional checks:
34+
35+
```powershell
36+
npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "loads Text to Speech V2 from URL"
37+
git diff --check
38+
```
39+
40+
Result: focused Playwright test passed. `git diff --check` passed with line-ending normalization warnings only.
41+
42+
## Playwright Coverage
43+
44+
- Non-fullscreen Text to Speak content no longer has a large empty spacer.
45+
- Non-fullscreen Named Sentences content no longer has large unused bottom space.
46+
- Fullscreen Text to Speak remains visible and does not collapse.
47+
- Fullscreen center Named Sentences remains visible inside the center column.
48+
- Fullscreen Output Summary stays within the right column and does not push Status offscreen.
49+
- Fullscreen Status remains visible and scrolls internally.
50+
51+
Expected pass behavior: all layout assertions pass in the Text to Speech V2 URL JSON workflow.
52+
53+
Expected fail behavior: tests fail if normal mode reintroduces excess spacer height, if fullscreen columns exceed viewport height, or if Status is pushed out of the right panel.
54+
55+
## Manual Validation
56+
57+
1. Open Text to Speech V2 with sample 1903 loaded.
58+
2. Confirm Text to Speak wraps tightly around the textarea and speech buttons.
59+
3. Confirm Named Sentences has no large empty area below tiles.
60+
4. Click Hide Header and Details.
61+
5. Confirm the left, center, and right panels fit in the viewport; Text to Speak remains visible; Output Summary and Status scroll within their own panels.
62+
63+
## Full Samples Smoke Test
64+
65+
Skipped. The BUILD request explicitly said not to run the full samples smoke test, and this PR is scoped to Text to Speech V2 layout height behavior only.
66+
67+
## Scope Notes
68+
69+
- No schema changes.
70+
- No runtime JavaScript changes.
71+
- No unrelated files.
72+
- No `start_of_day` changes.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,21 +844,40 @@ async function expectTextToSpeechV2FullscreenShell(page) {
844844
const left = document.querySelector(".tool-shell-common__fullscreen-panel-left").getBoundingClientRect();
845845
const center = document.querySelector(".tool-shell-common__fullscreen-center-panel").getBoundingClientRect();
846846
const right = document.querySelector(".tool-shell-common__fullscreen-panel-right").getBoundingClientRect();
847+
const textContent = document.querySelector("#text2speach-V2TextContent").getBoundingClientRect();
848+
const queueContent = document.querySelector("#text2speach-V2QueueContent").getBoundingClientRect();
849+
const summaryAccordion = document.querySelector(".text2speach-V2__summary-accordion").getBoundingClientRect();
850+
const statusAccordion = document.querySelector(".text2speach-V2__status-accordion").getBoundingClientRect();
851+
const statusLog = document.querySelector("#text2speach-V2StatusLog").getBoundingClientRect();
847852
const rootStyle = getComputedStyle(document.querySelector(".tool-shell-common__fullscreen-root"));
848853
const gap = Number.parseFloat(rootStyle.columnGap || "0");
849854
const horizontalPadding = Number.parseFloat(rootStyle.paddingLeft || "0") + Number.parseFloat(rootStyle.paddingRight || "0");
855+
const statusStyle = getComputedStyle(document.querySelector("#text2speach-V2StatusLog"));
850856
return {
851857
centerAfterLeft: center.left > left.right,
852858
centerFillsRemaining: Math.abs(center.width - (root.width - horizontalPadding - left.width - right.width - (gap * 2))) <= 4,
859+
centerFitsViewport: center.bottom <= window.innerHeight + 1,
853860
centerWidth: Math.round(center.width),
854861
gridTemplateColumns: rootStyle.gridTemplateColumns,
855862
leftAtSide: left.left < center.left,
863+
leftFitsViewport: left.bottom <= window.innerHeight + 1,
856864
leftWidth: Math.round(left.width),
857865
layoutDisplay: rootStyle.display,
866+
queueContentBottomWithinCenter: queueContent.bottom <= center.bottom + 1,
867+
queueContentHeight: Math.round(queueContent.height),
858868
rightAtSide: right.left > center.right,
869+
rightFitsViewport: right.bottom <= window.innerHeight + 1,
859870
rightWithinRoot: right.right <= root.right + 1,
860871
rightWidth: Math.round(right.width),
861872
rootWidth: Math.round(root.width),
873+
statusBelowSummary: statusAccordion.top >= summaryAccordion.bottom - 1,
874+
statusBottomWithinRight: statusAccordion.bottom <= right.bottom + 1,
875+
statusLogHeight: Math.round(statusLog.height),
876+
statusLogOverflowY: statusStyle.overflowY,
877+
summaryBottomWithinRight: summaryAccordion.bottom <= right.bottom + 1,
878+
textContentBottomWithinCenter: textContent.bottom <= center.bottom + 1,
879+
textContentHeight: Math.round(textContent.height),
880+
textContentVisible: textContent.height >= 220,
862881
viewportWidth: window.innerWidth
863882
};
864883
});
@@ -870,10 +889,22 @@ async function expectTextToSpeechV2FullscreenShell(page) {
870889
expect(fullscreenLayout.rightWidth).toBe(360);
871890
expect(fullscreenLayout.leftAtSide).toBe(true);
872891
expect(fullscreenLayout.centerAfterLeft).toBe(true);
892+
expect(fullscreenLayout.leftFitsViewport).toBe(true);
893+
expect(fullscreenLayout.centerFitsViewport).toBe(true);
894+
expect(fullscreenLayout.rightFitsViewport).toBe(true);
873895
expect(fullscreenLayout.rightAtSide).toBe(true);
874896
expect(fullscreenLayout.rightWithinRoot).toBe(true);
875897
expect(fullscreenLayout.centerFillsRemaining).toBe(true);
876898
expect(fullscreenLayout.centerWidth).toBeGreaterThan(500);
899+
expect(fullscreenLayout.textContentVisible).toBe(true);
900+
expect(fullscreenLayout.textContentBottomWithinCenter).toBe(true);
901+
expect(fullscreenLayout.queueContentHeight).toBeGreaterThan(120);
902+
expect(fullscreenLayout.queueContentBottomWithinCenter).toBe(true);
903+
expect(fullscreenLayout.summaryBottomWithinRight).toBe(true);
904+
expect(fullscreenLayout.statusBelowSummary).toBe(true);
905+
expect(fullscreenLayout.statusBottomWithinRight).toBe(true);
906+
expect(fullscreenLayout.statusLogHeight).toBeGreaterThan(120);
907+
expect(fullscreenLayout.statusLogOverflowY).toBe("auto");
877908

878909
const namedSentencesHeader = page.locator('[aria-controls="text2speach-V2QueueContent"]');
879910
await namedSentencesHeader.click();
@@ -1174,20 +1205,26 @@ test.describe("Workspace Manager V2 bootstrap", () => {
11741205
const actions = content.querySelector("#text2speach-V2SpeechActions");
11751206
const contentRect = content.getBoundingClientRect();
11761207
const textareaRect = textarea.getBoundingClientRect();
1208+
const fieldRect = textarea.closest(".text2speach-V2__field").getBoundingClientRect();
11771209
const actionsRect = actions.getBoundingClientRect();
11781210
const contentStyle = getComputedStyle(content);
1211+
const rowGap = Number.parseFloat(contentStyle.rowGap || contentStyle.gap || "0");
1212+
const paddingTop = Number.parseFloat(contentStyle.paddingTop || "0");
1213+
const paddingBottom = Number.parseFloat(contentStyle.paddingBottom || "0");
1214+
const expectedContentHeight = fieldRect.height + actionsRect.height + rowGap + paddingTop + paddingBottom;
11791215
return {
11801216
actionsAreBottomControl: content.lastElementChild?.id === "text2speach-V2SpeechActions",
11811217
actionsBelowText: actionsRect.top >= textareaRect.bottom,
11821218
actionsBottomAligned: Math.abs(actionsRect.bottom - contentRect.bottom) <= 1,
1219+
contentExcessHeight: Math.round(contentRect.height - expectedContentHeight),
11831220
contentDisplay: contentStyle.display,
11841221
contentFlexDirection: contentStyle.flexDirection,
11851222
contentJustify: contentStyle.justifyContent,
11861223
fieldMarginBottom: getComputedStyle(textarea.closest(".text2speach-V2__field")).marginBottom,
11871224
textareaResize: getComputedStyle(textarea).resize
11881225
};
11891226
});
1190-
expect(textAccordionLayout).toEqual({
1227+
expect(textAccordionLayout).toMatchObject({
11911228
actionsAreBottomControl: true,
11921229
actionsBelowText: true,
11931230
actionsBottomAligned: true,
@@ -1197,11 +1234,27 @@ test.describe("Workspace Manager V2 bootstrap", () => {
11971234
fieldMarginBottom: "0px",
11981235
textareaResize: "none"
11991236
});
1237+
expect(textAccordionLayout.contentExcessHeight).toBeLessThanOrEqual(16);
1238+
expect(textAccordionLayout.contentExcessHeight).toBeGreaterThanOrEqual(-2);
12001239
const namedSentenceTopControls = await page.locator("#text2speach-V2QueueContent").evaluate((content) => {
12011240
const firstElement = Array.from(content.children).find((child) => child.nodeType === Node.ELEMENT_NODE);
12021241
return firstElement?.id || "";
12031242
});
12041243
expect(namedSentenceTopControls).toBe("text2speach-V2QueueTiles");
1244+
const namedSentenceLayout = await page.locator("#text2speach-V2QueueContent").evaluate((content) => {
1245+
const tiles = content.querySelector("#text2speach-V2QueueTiles");
1246+
const contentRect = content.getBoundingClientRect();
1247+
const tilesRect = tiles.getBoundingClientRect();
1248+
const contentStyle = getComputedStyle(content);
1249+
const paddingTop = Number.parseFloat(contentStyle.paddingTop || "0");
1250+
const paddingBottom = Number.parseFloat(contentStyle.paddingBottom || "0");
1251+
return {
1252+
contentExcessHeight: Math.round(contentRect.height - tilesRect.height - paddingTop - paddingBottom),
1253+
unusedBottomSpace: Math.round(contentRect.bottom - tilesRect.bottom - paddingBottom)
1254+
};
1255+
});
1256+
expect(namedSentenceLayout.contentExcessHeight).toBeLessThanOrEqual(16);
1257+
expect(namedSentenceLayout.unusedBottomSpace).toBeLessThanOrEqual(2);
12051258
expect(await page.locator("#text2speach-V2SpeechOptionsContent .text2speach-V2__item-actions button").evaluateAll((buttons) => buttons.map((button) => button.textContent.trim()))).toEqual(["Add", "Duplicate", "Delete"]);
12061259
const statusHeaderOrder = await page.locator(".text2speach-V2__status-accordion-header").evaluate((header) => Array.from(header.querySelectorAll(":scope > span, :scope > div > span, :scope > div > button"), (element) => element.textContent.trim()));
12071260
expect(statusHeaderOrder).toEqual(["Status", "+", "Clear"]);

tools/text2speach-V2/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ <h2 class="tools-platform-frame__eyebrow">Browser speech synthesis</h2>
145145
</section>
146146

147147
<aside class="text2speach-V2__panel text2speach-V2__panel--right tool-shell-common__fullscreen-panel tool-shell-common__fullscreen-side-panel tool-shell-common__fullscreen-panel-right" aria-label="Speech output">
148-
<section class="accordion-v2 text2speach-V2__accordion is-open" data-accordion-v2-open="true">
148+
<section class="accordion-v2 text2speach-V2__accordion text2speach-V2__summary-accordion is-open" data-accordion-v2-open="true">
149149
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="text2speach-V2SpeechSummaryContent">
150150
<span>Output Summary</span>
151151
<span class="accordion-v2__icon" aria-hidden="true">+</span>
@@ -155,7 +155,7 @@ <h2 class="tools-platform-frame__eyebrow">Browser speech synthesis</h2>
155155
</div>
156156
</section>
157157

158-
<section class="accordion-v2 text2speach-V2__accordion is-open" data-accordion-v2-open="true">
158+
<section class="accordion-v2 text2speach-V2__accordion text2speach-V2__status-accordion is-open" data-accordion-v2-open="true">
159159
<div class="accordion-v2__header text2speach-V2__status-accordion-header" aria-expanded="true" aria-controls="text2speach-V2StatusLogContent">
160160
<span>Status</span>
161161
<div class="text2speach-V2__status-header-actions">

tools/text2speach-V2/styles/text2speach-V2.css

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@
113113
min-width: 0;
114114
}
115115

116+
.text2speach-V2__panel--right {
117+
display: flex;
118+
flex-direction: column;
119+
min-height: 0;
120+
}
121+
116122
.text2speach-V2__panel--center {
117123
display: flex;
118124
flex-direction: column;
@@ -123,6 +129,10 @@
123129
margin-bottom: 12px;
124130
}
125131

132+
.text2speach-V2__accordion.is-open {
133+
flex: 0 1 auto;
134+
}
135+
126136
.text2speach-V2__text-accordion {
127137
flex: 0 0 auto;
128138
}
@@ -144,15 +154,15 @@
144154
margin: 0;
145155
}
146156

147-
.text2speach-V2__accordion--fill,
148-
.text2speach-V2__accordion--fill .accordion-v2__content {
149-
height: 100%;
150-
}
151-
152157
.text2speach-V2__accordion--fill {
158+
flex: 0 1 auto;
153159
min-height: 0;
154160
}
155161

162+
.text2speach-V2__accordion--fill .accordion-v2__content {
163+
height: auto;
164+
}
165+
156166
.text2speach-V2__field {
157167
display: grid;
158168
gap: 6px;
@@ -313,6 +323,8 @@
313323
html.tools-platform-fullscreen-active,
314324
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"],
315325
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] {
326+
height: 100vh;
327+
max-height: 100vh;
316328
min-height: 100vh;
317329
overflow: hidden;
318330
}
@@ -348,12 +360,13 @@ html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] > .te
348360

349361
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] > .text2speach-V2,
350362
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] > .text2speach-V2 {
351-
flex: 1 1 auto;
363+
flex: 1 1 0;
352364
display: grid;
353365
box-sizing: border-box;
354366
grid-template-columns: 340px minmax(0, 1fr) 360px;
355367
align-items: stretch;
356368
gap: 16px;
369+
height: 0;
357370
width: 100%;
358371
max-width: none;
359372
min-height: 0;
@@ -368,7 +381,16 @@ html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .tool
368381
height: 100%;
369382
max-height: none;
370383
min-height: 0;
371-
overflow: auto;
384+
overflow: hidden;
385+
}
386+
387+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__panel,
388+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__panel {
389+
display: flex;
390+
flex-direction: column;
391+
max-height: 100%;
392+
min-height: 0;
393+
overflow: hidden;
372394
}
373395

374396
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .tool-shell-common__fullscreen-panel-left,
@@ -395,6 +417,33 @@ html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .tool
395417
overflow: hidden;
396418
}
397419

420+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__panel--left .text2speach-V2__accordion,
421+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__panel--left .text2speach-V2__accordion {
422+
flex: 1 1 auto;
423+
min-height: 0;
424+
}
425+
426+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__panel--left .accordion-v2__content,
427+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__panel--left .accordion-v2__content {
428+
flex: 1 1 auto;
429+
min-height: 0;
430+
overflow: auto;
431+
}
432+
433+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__text-accordion,
434+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__text-accordion {
435+
flex: 0 0 auto;
436+
max-height: 42%;
437+
min-height: 0;
438+
}
439+
440+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] #text2speach-V2TextContent,
441+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] #text2speach-V2TextContent {
442+
min-height: 238px;
443+
max-height: 100%;
444+
overflow: auto;
445+
}
446+
398447
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__accordion--fill,
399448
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__accordion--fill {
400449
flex: 1 1 auto;
@@ -414,3 +463,35 @@ body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2
414463
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__queue-tiles {
415464
max-height: none;
416465
}
466+
467+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__summary-accordion,
468+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__summary-accordion {
469+
flex: 0 1 45%;
470+
max-height: 45%;
471+
min-height: 0;
472+
}
473+
474+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__status-accordion,
475+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__status-accordion {
476+
flex: 1 1 0;
477+
min-height: 160px;
478+
}
479+
480+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__summary-accordion .accordion-v2__content,
481+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__status-accordion .accordion-v2__content,
482+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__summary-accordion .accordion-v2__content,
483+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__status-accordion .accordion-v2__content {
484+
flex: 1 1 auto;
485+
min-height: 0;
486+
overflow: hidden;
487+
}
488+
489+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] .text2speach-V2__summary,
490+
body.tools-platform-surface.tools-platform-fullscreen-active[data-tool-id="text2speach-V2"] #text2speach-V2StatusLog,
491+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] .text2speach-V2__summary,
492+
html:fullscreen body.tools-platform-surface[data-tool-id="text2speach-V2"] #text2speach-V2StatusLog {
493+
height: 100%;
494+
min-height: 0;
495+
overflow: auto;
496+
resize: none;
497+
}

0 commit comments

Comments
 (0)