Skip to content

Commit 0287320

Browse files
author
DavidQ
committed
Rename Center control and tune opacity input sizing and validation - PR_26133_043-object-transform-center-and-opacity-input-tuning
1 parent 87df2dc commit 0287320

6 files changed

Lines changed: 99 additions & 49 deletions

File tree

docs/dev/reports/playwright_v8_coverage_report.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_042 Playwright V8 Coverage Report
1+
# PR_26133_043 Playwright V8 Coverage Report
22

3-
Task: PR_26133_042-object-transform-tags-and-palette-layout-tuning
3+
Task: PR_26133_043-object-transform-center-and-opacity-input-tuning
44
Date: 2026-05-15
55

66
## Result
@@ -26,7 +26,7 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
2626
## Changed Runtime JS Coverage
2727

2828
```text
29-
(94%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 4729/4729; executed functions 488/517
29+
(94%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 4751/4751; executed functions 493/522
3030
```
3131

3232
## Changed Markup/Style/Test Coverage Note
@@ -45,4 +45,4 @@ Markup and CSS changes in `tools/object-vector-studio-v2/index.html` and `tools/
4545

4646
## PR-Specific Note
4747

48-
The Workspace V2 run exercised Object Vector Studio V2 tag button/chip sizing, Rotate range/input-preservation and wrapped summary behavior, singular scale summary formatting, and Palette Paint/Stroke/Width plus Fill Op/Stroke Op row layout. Coverage remains advisory only.
48+
The Workspace V2 run exercised Object Vector Studio V2 Center button labeling and recentering behavior, 0-255 opacity input validation, byte-to-normalized opacity conversion for SVG/schema rendering, and opacity input sizing/layout assertions. Coverage remains advisory only.
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_042 Workspace V2 Playwright Results
1+
# PR_26133_043 Workspace V2 Playwright Results
22

3-
Task: PR_26133_042-object-transform-tags-and-palette-layout-tuning
3+
Task: PR_26133_043-object-transform-center-and-opacity-input-tuning
44
Date: 2026-05-15
55

66
## Result
@@ -14,13 +14,13 @@ PASS - `npm run test:workspace-v2` completed successfully.
1414

1515
## PR-Specific Coverage
1616

17-
- Verified Object tag Add button width is `77px`.
18-
- Verified selected object tag chips for `bubba` and `player` render as `77px` buttons below the Add tag input row.
19-
- Verified Rotate input exposes `min=-359` and `max=359`.
20-
- Verified Rotate keeps the user-entered input value while applying the normalized/wrapped transform rotation.
21-
- Verified transform summary wraps large/current rotation values into `0..359` and keeps singular same-axis scale text, for example `x 0, y 0, rot 73, scale 0.77`.
22-
- Verified Palette controls render Paint, Stroke, and Width on the first row, with compact Fill Op and Stroke Op controls on the row below.
17+
- Verified Object Preview viewport controls now render `Center` instead of `Dot`.
18+
- Verified Center recenters the preview viewport to origin `0,0`, preserves current zoom, refreshes stale pointer text back to the centered origin display, and keeps the center marker visible.
19+
- Verified Fill Op and Stroke Op inputs use `min=0`, `max=255`, `step=1`, and width sufficient for 4 visible digits.
20+
- Verified opacity inputs visibly reject out-of-range values such as `-1` and `256`.
21+
- Verified valid 0-255 opacity inputs convert to normalized style opacity values for schema/SVG rendering.
22+
- Verified Fill Op and Stroke Op remain in the compact opacity row below Paint, Stroke, and Width.
2323

2424
## Manual Verification Equivalent
2525

26-
Targeted Object Vector Studio V2 browser automation covered the requested tag widths/layout, Rotate range and input preservation, wrapped transform summary rotation, compact Palette row split, and no-console-error checks.
26+
Targeted Object Vector Studio V2 browser automation covered the requested Center label and centering behavior, opacity input sizing/range validation, normalized opacity rendering behavior, Palette opacity row layout, and no-console-error checks.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,11 +1730,13 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17301730
const widthInput = content.querySelector("#objectVectorStudioV2StrokeWidth").getBoundingClientRect();
17311731
const fillOpacityLabel = content.querySelector("label[for='objectVectorStudioV2FillOpacity']").getBoundingClientRect();
17321732
const strokeOpacityLabel = content.querySelector("label[for='objectVectorStudioV2StrokeOpacity']").getBoundingClientRect();
1733-
const opacityInputs = Array.from(opacityRow.querySelectorAll("input")).map((input) => input.getBoundingClientRect());
1733+
const opacityInputs = Array.from(opacityRow.querySelectorAll("input"));
1734+
const opacityInputRects = opacityInputs.map((input) => input.getBoundingClientRect());
17341735
return {
17351736
opacityBelowPrimary: fillOpacityLabel.top >= Math.max(paintButton.bottom, strokeButton.bottom, widthLabel.bottom),
17361737
opacityInline: Math.abs((fillOpacityLabel.top + fillOpacityLabel.height / 2) - (strokeOpacityLabel.top + strokeOpacityLabel.height / 2)) < 4,
1737-
opacityInputCompact: opacityInputs.every((rect) => Math.round(rect.width) <= 42),
1738+
opacityInputFitsFourDigits: opacityInputRects.every((rect) => Math.round(rect.width) >= 54),
1739+
opacityInputRanges: opacityInputs.map((input) => ({ max: input.max, min: input.min, step: input.step, value: input.value })),
17381740
opacityLabels: Array.from(opacityRow.querySelectorAll("label > span")).map((label) => label.textContent.trim()),
17391741
primaryInline: [strokeButton, widthLabel].every((rect) => Math.abs((paintButton.top + paintButton.height / 2) - (rect.top + rect.height / 2)) < 4),
17401742
primaryOrder: Array.from(primaryRow.children).map((element) => element.textContent.trim()),
@@ -1745,7 +1747,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17451747
expect(paletteControlLayout).toEqual({
17461748
opacityBelowPrimary: true,
17471749
opacityInline: true,
1748-
opacityInputCompact: true,
1750+
opacityInputFitsFourDigits: true,
1751+
opacityInputRanges: [
1752+
{ max: "255", min: "0", step: "1", value: "255" },
1753+
{ max: "255", min: "0", step: "1", value: "255" }
1754+
],
17491755
opacityLabels: ["Fill Op", "Stroke Op"],
17501756
primaryInline: true,
17511757
primaryOrder: ["Paint", "Stroke", "Width"],
@@ -1816,16 +1822,19 @@ test.describe("Workspace Manager V2 bootstrap", () => {
18161822
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
18171823
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(1);
18181824
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveAttribute("r", "9");
1819-
await expect(page.locator("#objectVectorStudioV2ViewportControls button")).toHaveText(["Out", "In", "Up", "Down", "Left", "Right", "View", "Dot"]);
1820-
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).toHaveAttribute("aria-pressed", "true");
1821-
await page.locator("#objectVectorStudioV2CenterDotButton").click();
1822-
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).toHaveAttribute("aria-pressed", "false");
1823-
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(0);
1824-
await expect(page.locator("#statusLog")).toHaveValue(/OK Center dot hidden\./);
1825+
await expect(page.locator("#objectVectorStudioV2ViewportControls button")).toHaveText(["Out", "In", "Up", "Down", "Left", "Right", "View", "Center"]);
1826+
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).not.toHaveAttribute("aria-pressed", /.*/);
1827+
await page.locator("#objectVectorStudioV2PanRightButton").click();
1828+
await page.locator("#objectVectorStudioV2PanDownButton").click();
1829+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 2, 2 | Canvas 0,0 centered | Zoom 100%");
1830+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1580 -1080 3200 2200");
1831+
await page.locator("#objectVectorStudioV2RenderSurface").hover({ position: { x: 12, y: 12 } });
1832+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Pointer");
18251833
await page.locator("#objectVectorStudioV2CenterDotButton").click();
1826-
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).toHaveAttribute("aria-pressed", "true");
1834+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
1835+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas 0,0 centered | Zoom 100%");
18271836
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(1);
1828-
await expect(page.locator("#statusLog")).toHaveValue(/OK Center dot shown\./);
1837+
await expect(page.locator("#statusLog")).toHaveValue(/OK Viewport centered at origin 0,0; zoom 100% preserved\./);
18291838
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"name": "Asteroids Ship"');
18301839
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShape": null');
18311840
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"palette"');
@@ -2451,18 +2460,34 @@ test.describe("Workspace Manager V2 bootstrap", () => {
24512460
await clickPreviewShape(1);
24522461
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"fill": "#6fd3ff"');
24532462
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied palette color #6fd3ff from cyan to shape row 1 by render surface click\. Target: paint opacity 1\./);
2454-
await page.locator("#objectVectorStudioV2FillOpacity").fill("0.5");
2463+
await page.locator("#objectVectorStudioV2FillOpacity").fill("-1");
24552464
await page.locator("#objectVectorStudioV2FillOpacity").dispatchEvent("change");
2456-
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"fillOpacity": 0.5');
2457-
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied fill opacity 0\.5 to shape row 1\./);
2465+
await expect(page.locator("#objectVectorStudioV2FillOpacity")).toHaveAttribute("aria-invalid", "true");
2466+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"fillOpacity": 0.502');
2467+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Palette fill opacity rejected: Fill opacity must be a whole number between 0 and 255\./);
2468+
await page.locator("#objectVectorStudioV2FillOpacity").fill("128");
2469+
await page.locator("#objectVectorStudioV2FillOpacity").dispatchEvent("change");
2470+
await expect(page.locator("#objectVectorStudioV2FillOpacity")).not.toHaveAttribute("aria-invalid", "true");
2471+
await expect(page.locator("#objectVectorStudioV2FillOpacity")).toHaveValue("128");
2472+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"fillOpacity": 0.502');
2473+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-index='1']")).toHaveAttribute("fill-opacity", "0.502");
2474+
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied fill opacity 0\.502 to shape row 1\./);
24582475
await page.locator("#objectVectorStudioV2StrokeModeButton").click();
24592476
await clickPreviewShape(1);
24602477
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"stroke": "#ffffff"');
24612478
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied palette color #ffffff from white to shape row 1 by render surface click\. Target: stroke width 2, opacity 1\./);
2462-
await page.locator("#objectVectorStudioV2StrokeOpacity").fill("0.65");
2479+
await page.locator("#objectVectorStudioV2StrokeOpacity").fill("256");
2480+
await page.locator("#objectVectorStudioV2StrokeOpacity").dispatchEvent("change");
2481+
await expect(page.locator("#objectVectorStudioV2StrokeOpacity")).toHaveAttribute("aria-invalid", "true");
2482+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"strokeOpacity": 0.651');
2483+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Palette stroke opacity rejected: Stroke opacity must be a whole number between 0 and 255\./);
2484+
await page.locator("#objectVectorStudioV2StrokeOpacity").fill("166");
24632485
await page.locator("#objectVectorStudioV2StrokeOpacity").dispatchEvent("change");
2464-
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"strokeOpacity": 0.65');
2465-
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied stroke opacity 0\.65 to shape row 1\./);
2486+
await expect(page.locator("#objectVectorStudioV2StrokeOpacity")).not.toHaveAttribute("aria-invalid", "true");
2487+
await expect(page.locator("#objectVectorStudioV2StrokeOpacity")).toHaveValue("166");
2488+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"strokeOpacity": 0.651');
2489+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-index='1']")).toHaveAttribute("stroke-opacity", "0.651");
2490+
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied stroke opacity 0\.651 to shape row 1\./);
24662491
await page.evaluate(() => {
24672492
window.__objectVectorStudioV2App.selectedStrokeColor = "#123456";
24682493
window.__objectVectorStudioV2App.selectedStrokeLabel = "manual rogue";

tools/object-vector-studio-v2/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
133133
<button id="objectVectorStudioV2PanLeftButton" type="button" title="Pan the work surface left">Left</button>
134134
<button id="objectVectorStudioV2PanRightButton" type="button" title="Pan the work surface right">Right</button>
135135
<button id="objectVectorStudioV2ResetViewButton" type="button" title="Reset zoom and origin to 0,0">View</button>
136-
<button id="objectVectorStudioV2CenterDotButton" type="button" aria-pressed="true" title="Hide or show the canvas center dot">Dot</button>
136+
<button id="objectVectorStudioV2CenterDotButton" type="button" title="Center canvas origin in the current viewport">Center</button>
137137
</div>
138138
<svg id="objectVectorStudioV2RenderSurface" class="object-vector-studio-v2__render-surface" viewBox="0 0 320 220" tabindex="0" role="img" aria-label="Object shape render surface"></svg>
139139
<div id="objectVectorStudioV2ObjectPreviewFooter" class="object-vector-studio-v2__preview-footer">Object ID: none</div>
@@ -273,11 +273,11 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
273273
<div class="object-vector-studio-v2__palette-opacity-row">
274274
<label class="object-vector-studio-v2__inline-field" for="objectVectorStudioV2FillOpacity">
275275
<span>Fill Op</span>
276-
<input id="objectVectorStudioV2FillOpacity" type="number" min="0" max="1" step="0.05" value="1">
276+
<input id="objectVectorStudioV2FillOpacity" type="number" min="0" max="255" step="1" value="255">
277277
</label>
278278
<label class="object-vector-studio-v2__inline-field" for="objectVectorStudioV2StrokeOpacity">
279279
<span>Stroke Op</span>
280-
<input id="objectVectorStudioV2StrokeOpacity" type="number" min="0" max="1" step="0.05" value="1">
280+
<input id="objectVectorStudioV2StrokeOpacity" type="number" min="0" max="255" step="1" value="255">
281281
</label>
282282
</div>
283283
</div>

0 commit comments

Comments
 (0)