Skip to content

Commit bb618a9

Browse files
author
DavidQ
committed
Separate Object Transform into its own accordion section in Object Vector Studio V2 - PR_26133_010-object-transform-accordion-separation
1 parent 6a68cda commit bb618a9

7 files changed

Lines changed: 100 additions & 35 deletions

File tree

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
# PR_26133_009 Playwright V8 Coverage Report
1+
# PR_26133_010 Playwright V8 Coverage Report
22

33
Coverage source: `docs/dev/reports/playwright_v8_coverage_report.txt`, refreshed by the final `npm run test:workspace-v2` run.
44

55
## Summary
66

77
- Coverage is advisory only; no thresholds are enforced.
88
- Workspace Manager V2 entry point: 91%.
9-
- Object Vector Studio V2 runtime coverage entries from the generated report:
10-
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 91%, executed lines 3313/3313, executed functions 348/384.
11-
- `tools/object-vector-studio-v2/js/bootstrap.js`: 80%, executed lines 99/99, executed functions 4/5.
12-
- The generated report lists `tools/object-vector-studio-v2/js/ToolStarterApp.js` as a changed JS file with browser V8 coverage.
9+
- Changed Object Vector Studio V2 runtime coverage entries:
10+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 91%, executed lines 3335/3335, executed functions 349/385.
11+
- `tools/object-vector-studio-v2/js/bootstrap.js`: 80%, executed lines 100/100, executed functions 4/5.
12+
- The generated report lists `tests/playwright/tools/WorkspaceManagerV2.spec.mjs` as changed JS not collected by browser runtime coverage.
1313

1414
## Validation Context
1515

1616
- Main command: `npm run test:workspace-v2`.
1717
- Result: 46 passed.
18-
- Focused Object Vector Studio V2 layout and preview-coordinate scenarios also passed.
19-
- Manual Object Preview probe confirmed the applied zoom hack values, confirmed default view, max zoom cap, preserved logical coordinates, and no console/runtime errors.
18+
- Focused Object Vector Studio V2 layout scenario passed.
19+
- Manual Object Transform probe confirmed accordion separation, transform controls, collapse/reopen behavior, a move action, compact layout, and no console/runtime errors.
Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
1-
# PR_26133_009 Workspace V2 Results
1+
# PR_26133_010 Workspace V2 Results
22

33
## Command Results
44

55
- `node --check tools/object-vector-studio-v2/js/ToolStarterApp.js`: passed.
6+
- `node --check tools/object-vector-studio-v2/js/bootstrap.js`: passed.
67
- `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`: passed.
7-
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "Object Vector Studio V2 (layout shell|preview coordinates)"`: 2 passed.
8+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "shows Object Vector Studio V2 layout shell and schema-only palette gate"`: 1 passed.
89
- `npm run test:workspace-v2`: 46 passed.
9-
- `git diff --check`: passed with the existing LF-to-CRLF working-copy warning for `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`.
10+
- `git diff --check`: passed with LF-to-CRLF working-copy warnings for touched files.
1011

11-
## Targeted Object Preview Verification
12+
## Targeted Object Vector Studio V2 Verification
1213

13-
- Standalone Playwright manual probe reported `consoleErrors: []` and `pageErrors: []`.
14-
- Confirmed values are applied: `DEFAULT_VIEWPORT.zoom = 0.1`, `MAX_ZOOM = 0.5`, and zoom text/status use `formatZoomPercentage() * 10`.
15-
- Startup/default Object Preview display is `Origin: 0, 0 | Canvas 0,0 centered | Zoom 100%` with viewBox `-1600 -1100 3200 2200`.
16-
- Zoom in from startup displays `Zoom 110%` with viewBox `-1454.545 -1000 2909.091 2000`.
17-
- Max zoom clamps at the confirmed cap: `Zoom 500%` with viewBox `-320 -220 640 440`.
18-
- Internal `0.25` displays `Zoom 250%` with viewBox `-640 -440 1280 880`.
19-
- Visual scale remains aligned to the confirmed grid/object contract: grid step `10`, Asteroids ship drawn points `0,-180`, `140,160`, `0,80`, `-140,160`.
20-
- Pointer/origin coordinate math remains logical: panned origin displays `Origin: 2, 0`; grid-space pointer `-140,-160` displays `Pointer -14, -16`.
14+
- Confirmed left accordion order: `Object`, `Object Details`, `Object Transform`, `Objects`.
15+
- Confirmed `Object Transform` renders directly under `Object Details`.
16+
- Confirmed `Object Details` no longer contains Transform text or transform action buttons.
17+
- Confirmed `Object Transform` contains the selected-shape transform section and the existing move/rotate/scale/resize/origin controls.
18+
- Confirmed `Object Transform` collapses and reopens through the existing accordion behavior.
19+
- Confirmed transform functionality by moving the selected rectangle and verifying JSON updated with transform `x: 7`.
20+
- Confirmed compact left sections use non-growing layout and their content reaches the section bottom.
21+
- Confirmed manual probe reported `consoleErrors: []` and `pageErrors: []`.
2122

22-
## Contract Checks
23+
## Scope Checks
2324

24-
- Canvas size was not changed.
25-
- Grid size was not changed.
26-
- Object drawing scale was not changed.
27-
- Pointer/origin coordinate normalization was preserved.
25+
- Transform controls were moved without changing their handlers.
26+
- Existing Object Vector Studio V2 JSON contracts were preserved.
2827
- No sample JSON files were changed.
2928
- No unrelated tool/runtime files were changed.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
11931193
await expect(page.locator("#objectVectorStudioV2ExportJsonButton")).toBeDisabled();
11941194
await expect(page.locator("#objectVectorStudioV2ExportSvgButton")).toBeDisabled();
11951195

1196-
await expect(page.locator(".tool-starter__panel--left > .accordion-v2 > .accordion-v2__header > span:first-child")).toHaveText(["Object", "Object Details (0 obj, 0 shapes)", "Objects"]);
1196+
await expect(page.locator(".tool-starter__panel--left > .accordion-v2 > .accordion-v2__header > span:first-child")).toHaveText(["Object", "Object Details (0 obj, 0 shapes)", "Object Transform", "Objects"]);
11971197
await expect(page.locator(".tool-starter__panel--right > .accordion-v2 > .accordion-v2__header > span:first-child")).toHaveText(["Shape/Tools", "Palette (0 swatches)", "JSON Details", "Dependency Graph", "Status Log"]);
11981198
await expect(page.locator(".tool-starter__panel--right > .accordion-v2").first().locator(".accordion-v2__header > span:first-child")).toHaveText("Shape/Tools");
11991199
await expect(page.locator("#objectVectorStudioV2JsonDetailsContent")).toBeHidden();
@@ -1202,6 +1202,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12021202
await expect(page.locator("#objectVectorStudioV2PaletteSwatchCount")).toHaveText("(0 swatches)");
12031203
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("No objects loaded");
12041204
await expect(page.locator("#objectVectorStudioV2ObjectDetailsCount")).toHaveText("(0 obj, 0 shapes)");
1205+
await expect(page.locator("#objectVectorStudioV2ObjectTransform")).toHaveText("No shape selected.");
12051206
await expect(page.locator("#objectVectorStudioV2RenameObjectButton")).toBeDisabled();
12061207
await expect(page.locator("#objectVectorStudioV2RenameObjectButton")).toHaveAttribute("data-disabled-reason", "Disabled until a schema-valid object is selected.");
12071208
await expect(page.locator("#objectVectorStudioV2DeleteObjectButton")).toBeDisabled();
@@ -1544,6 +1545,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
15441545
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"type": "rectangle"');
15451546
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Editable fields below are limited to schema-valid geometry");
15461547
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Selected Shaperectangle-1 (rectangle)");
1548+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Transform");
1549+
await expect(page.locator("#objectVectorStudioV2ObjectDetails #objectVectorStudioV2MoveShapeButton")).toHaveCount(0);
1550+
await expect(page.locator("#objectVectorStudioV2ObjectTransform")).toContainText("Selected Shape: rectangle-1");
1551+
await expect(page.locator("#objectVectorStudioV2ObjectTransform")).toContainText("Transform");
1552+
await expect(page.locator("#objectVectorStudioV2ObjectTransform #objectVectorStudioV2MoveShapeButton")).toHaveCount(1);
15471553
await expect(page.locator("#objectVectorStudioV2ObjectDetailsActions")).toHaveCount(0);
15481554
await expect(page.locator("#objectVectorStudioV2ShapeVisibilityButton")).toHaveCount(0);
15491555
await expect(page.locator("#objectVectorStudioV2ShapeLockButton")).toHaveCount(0);
@@ -1729,6 +1735,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17291735
await clickPreviewShape("rectangle-1");
17301736
await expect(page.locator("[data-palette-color='#6fd3ff']")).toHaveClass(/is-selected/);
17311737
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Rectangle Geometry");
1738+
await expect(page.locator("#objectVectorStudioV2ObjectTransform")).toContainText("Transform");
1739+
await page.locator('button[aria-controls="objectVectorStudioV2ObjectTransformContent"]').click();
1740+
await expect(page.locator("#objectVectorStudioV2ObjectTransformContent")).toBeHidden();
1741+
await page.locator('button[aria-controls="objectVectorStudioV2ObjectTransformContent"]').click();
1742+
await expect(page.locator("#objectVectorStudioV2ObjectTransformContent")).toBeVisible();
17321743
const reviewLayoutState = await page.evaluate(() => {
17331744
const jsonContent = document.querySelector("#objectVectorStudioV2JsonDetailsContent");
17341745
const statusSection = document.querySelector("#statusLogContent").closest(".accordion-v2");
@@ -1980,10 +1991,22 @@ test.describe("Workspace Manager V2 bootstrap", () => {
19801991
await expect(page.locator('[data-object-id="object.asteroids.object-1"]')).toHaveAttribute("aria-pressed", "true");
19811992
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted object Shield Pickup\./);
19821993

1983-
const leftOpenHeights = await page.locator(".tool-starter__panel--left .accordion-v2.is-open").evaluateAll((sections) => (
1984-
sections.map((section) => Math.round(section.getBoundingClientRect().height))
1994+
const leftAccordionLayout = await page.locator(".tool-starter__panel--left > .accordion-v2").evaluateAll((sections) => (
1995+
sections.map((section) => {
1996+
const content = section.querySelector(".accordion-v2__content");
1997+
const sectionRect = section.getBoundingClientRect();
1998+
const contentRect = content?.getBoundingClientRect();
1999+
const style = getComputedStyle(section);
2000+
return {
2001+
contentReachesSectionBottom: !content || content.hidden || Math.abs(contentRect.bottom - sectionRect.bottom) <= 2,
2002+
flexGrow: style.flexGrow,
2003+
isOpen: section.classList.contains("is-open"),
2004+
title: section.querySelector(".accordion-v2__header > span:first-child")?.textContent.trim() || ""
2005+
};
2006+
})
19852007
));
1986-
expect(Math.max(...leftOpenHeights) - Math.min(...leftOpenHeights)).toBeLessThanOrEqual(18);
2008+
expect(leftAccordionLayout.map((entry) => entry.title)).toEqual(["Object", "Object Details (18 obj, 2 shapes)", "Object Transform", "Objects"]);
2009+
expect(leftAccordionLayout.slice(0, 3).every((entry) => entry.isOpen && entry.contentReachesSectionBottom && entry.flexGrow === "0")).toBe(true);
19872010

19882011
const tileScrollState = await page.locator("#objectVectorStudioV2ObjectsContent").evaluate((element) => ({
19892012
actionsInsideObjects: element.querySelector(":scope > .object-vector-studio-v2__objects-actions") !== null,

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
5050

5151
<main class="tool-starter object-vector-studio-v2 app-shell" data-tool-id="object-vector-studio-v2">
5252
<aside class="tool-starter__panel tool-starter__panel--left" aria-label="Tool inputs">
53-
<section class="accordion-v2 tool-starter__accordion is-open" data-accordion-v2-open="true">
53+
<section class="accordion-v2 tool-starter__accordion tool-starter__accordion--compact is-open" data-accordion-v2-open="true">
5454
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="objectVectorStudioV2ObjectContent">
5555
<span>Object</span>
5656
<span class="accordion-v2__icon" aria-hidden="true">+</span>
@@ -77,7 +77,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
7777
</div>
7878
</section>
7979

80-
<section class="accordion-v2 tool-starter__accordion is-open" data-accordion-v2-open="true">
80+
<section class="accordion-v2 tool-starter__accordion tool-starter__accordion--compact is-open" data-accordion-v2-open="true">
8181
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="objectVectorStudioV2ObjectDetailsContent">
8282
<span>Object Details <span id="objectVectorStudioV2ObjectDetailsCount" class="object-vector-studio-v2__header-count">(0 obj, 0 shapes)</span></span>
8383
<span class="accordion-v2__icon" aria-hidden="true">+</span>
@@ -87,6 +87,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
8787
</div>
8888
</section>
8989

90+
<section class="accordion-v2 tool-starter__accordion tool-starter__accordion--compact is-open" data-accordion-v2-open="true">
91+
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="objectVectorStudioV2ObjectTransformContent">
92+
<span>Object Transform</span>
93+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
94+
</button>
95+
<div id="objectVectorStudioV2ObjectTransformContent" class="accordion-v2__content object-vector-studio-v2__scroll-content">
96+
<div id="objectVectorStudioV2ObjectTransform" class="object-vector-studio-v2__details">No shape selected.</div>
97+
</div>
98+
</section>
99+
90100
<section class="accordion-v2 tool-starter__accordion is-open" data-accordion-v2-open="true">
91101
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="objectVectorStudioV2ObjectsContent">
92102
<span>Objects</span>

tools/object-vector-studio-v2/js/ToolStarterApp.js

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ export class ToolStarterApp {
657657
this.renderObjectTagList(null);
658658
this.elements.paletteSummary.textContent = this.runtimePalette ? "" : "Palette required before render.";
659659
this.elements.objectDetails.textContent = "No object selected.";
660+
this.elements.objectTransform.textContent = "No shape selected.";
660661
this.elements.objectPreviewFooter.textContent = "Object ID: none";
661662
this.elements.jsonDetails.textContent = "{}";
662663
this.renderFrameTimeline();
@@ -815,6 +816,7 @@ export class ToolStarterApp {
815816
this.elements.objectTagInput.value = "";
816817
this.renderObjectTagList(null);
817818
this.elements.objectDetails.textContent = "Runtime palette required before object render.";
819+
this.elements.objectTransform.textContent = "Runtime palette required before object transform.";
818820
this.elements.objectPreviewFooter.textContent = "Object ID: none";
819821
this.elements.jsonDetails.textContent = "{}";
820822
this.renderFrameTimeline();
@@ -1171,6 +1173,7 @@ export class ToolStarterApp {
11711173
this.renderObjectTagList(null);
11721174
this.updateObjectDetailsHeader(this.currentPayload?.objects.length || 0, 0);
11731175
this.elements.objectDetails.textContent = "No object selected.";
1176+
this.elements.objectTransform.textContent = "No shape selected.";
11741177
this.elements.objectPreviewFooter.textContent = "Object ID: none";
11751178
this.elements.jsonDetails.textContent = "{}";
11761179
this.renderFrameTimeline();
@@ -1182,6 +1185,7 @@ export class ToolStarterApp {
11821185
this.renderObjectTagList(selected);
11831186
this.updateObjectDetailsHeader(this.currentPayload.objects.length, selected.shapes.length);
11841187
this.elements.objectDetails.replaceChildren(this.createObjectDetails(selected, shape));
1188+
this.elements.objectTransform.replaceChildren(this.createObjectTransformDetails(shape));
11851189
this.elements.objectPreviewFooter.textContent = `Object ID: ${selected.id}`;
11861190
this.elements.jsonDetails.textContent = JSON.stringify({
11871191
object: selected,
@@ -1290,23 +1294,43 @@ export class ToolStarterApp {
12901294
return wrapper;
12911295
}
12921296

1293-
const transform = this.shapeTransform(shape);
12941297
const editableHint = document.createElement("p");
12951298
editableHint.className = "tool-starter__hint";
1296-
editableHint.textContent = "Editable fields below are limited to schema-valid geometry, transform, visibility, lock, order, and grouping fields for the selected shape.";
1299+
editableHint.textContent = "Editable fields below are limited to schema-valid geometry fields for the selected shape.";
12971300
shapePanel.append(this.createDetailGrid([
12981301
["Selected Shape", `${shape.id} (${shape.type})`],
12991302
["Group", shape.groupId || "None"],
1300-
["Color", shape.style.fill === "none" ? shape.style.stroke : shape.style.fill],
1301-
["Transform", `x ${transform.x}, y ${transform.y}, rot ${transform.rotation}, scale ${transform.scaleX} x ${transform.scaleY}`]
1303+
["Color", shape.style.fill === "none" ? shape.style.stroke : shape.style.fill]
13021304
]));
13031305
shapePanel.append(editableHint);
13041306
shapePanel.append(this.createShapeGeometryControls(shape));
1305-
shapePanel.append(this.createShapeTransformControls(shape));
13061307
wrapper.append(shapePanel);
13071308
return wrapper;
13081309
}
13091310

1311+
createObjectTransformDetails(shape) {
1312+
const wrapper = document.createElement("div");
1313+
wrapper.className = "object-vector-studio-v2__object-detail-stack";
1314+
if (!shape) {
1315+
const empty = document.createElement("p");
1316+
empty.textContent = "No shape selected. Create a primitive from Shape/Tools.";
1317+
wrapper.append(empty);
1318+
return wrapper;
1319+
}
1320+
1321+
const transformPanel = document.createElement("section");
1322+
transformPanel.className = "object-vector-studio-v2__shape-panel";
1323+
const heading = document.createElement("h3");
1324+
heading.textContent = `Selected Shape: ${shape.id}`;
1325+
const transform = this.shapeTransform(shape);
1326+
transformPanel.append(heading, this.createDetailGrid([
1327+
["Transform", `x ${transform.x}, y ${transform.y}, rot ${transform.rotation}, scale ${transform.scaleX} x ${transform.scaleY}`]
1328+
]));
1329+
transformPanel.append(this.createShapeTransformControls(shape));
1330+
wrapper.append(transformPanel);
1331+
return wrapper;
1332+
}
1333+
13101334
createDetailGrid(entries) {
13111335
const grid = document.createElement("div");
13121336
grid.className = "object-vector-studio-v2__object-detail-grid";

tools/object-vector-studio-v2/js/bootstrap.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ window.addEventListener("DOMContentLoaded", () => {
5959
objectPreviewFooter: requireElement("#objectVectorStudioV2ObjectPreviewFooter"),
6060
objectTagInput: requireElement("#objectVectorStudioV2ObjectTagInput"),
6161
objectTagList: requireElement("#objectVectorStudioV2ObjectTagList"),
62+
objectTransform: requireElement("#objectVectorStudioV2ObjectTransform"),
6263
objectsContent: requireElement("#objectVectorStudioV2ObjectsContent"),
6364
objectTiles: requireElement("#objectVectorStudioV2ObjectTiles"),
6465
paintModeButton: requireElement("#objectVectorStudioV2PaintModeButton"),

tools/object-vector-studio-v2/styles/toolStarter.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,14 @@ textarea:hover {
206206
flex: 1 0 220px;
207207
}
208208

209+
.tool-starter__panel--left > .accordion-v2.tool-starter__accordion--compact.is-open {
210+
flex: 0 0 auto;
211+
}
212+
213+
.tool-starter__panel--left > .accordion-v2.tool-starter__accordion--compact.is-open > .accordion-v2__content {
214+
flex: 0 0 auto;
215+
}
216+
209217
.accordion-v2 {
210218
--accordion-v2-accent: var(--tool-starter-accent);
211219
--accordion-v2-line: var(--tool-starter-line);

0 commit comments

Comments
 (0)