Skip to content

Commit 7090569

Browse files
author
DavidQ
committed
Implement Object Vector Studio V2 transform editing, viewport controls, and selection synchronization - PR_26132_007-object-vector-studio-v2-transform-editing
1 parent 1162e85 commit 7090569

8 files changed

Lines changed: 1029 additions & 40 deletions

File tree

docs/dev/reports/playwright_v8_coverage.txt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ Exercised tool entry points detected:
2020
(0%) Workspace Manager - not exercised by this Playwright run
2121

2222
Changed runtime JS files covered:
23-
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js - executed lines 24/24; executed functions 4/7
24-
(90%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 820/820; executed functions 91/101
25-
(93%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 178/178; executed functions 14/15
26-
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 59/59; executed functions 4/4
23+
(88%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 1476/1476; executed functions 159/181
24+
(94%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 196/196; executed functions 15/16
25+
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 68/68; executed functions 4/4
2726

2827
Files with executed line/function counts where available:
2928
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -190,13 +189,13 @@ Files with executed line/function counts where available:
190189
(86%) tools/workspace-manager-v2/js/controls/RepoDestinationControl.js - executed lines 25/25; executed functions 6/7
191190
(86%) tools/workspace-manager-v2/js/WorkspaceManagerV2App.js - executed lines 945/945; executed functions 42/49
192191
(88%) games/Pong/game/PongInputController.js - executed lines 77/77; executed functions 7/8
192+
(88%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 1476/1476; executed functions 159/181
193193
(88%) tools/preview-generator-v2/PreviewGeneratorV2App.js - executed lines 1498/1498; executed functions 107/121
194194
(88%) tools/shared/toolLaunchSSoTData.js - executed lines 112/112; executed functions 14/16
195195
(88%) tools/text2speech-V2/js/controls/TextInputControl.js - executed lines 24/24; executed functions 7/8
196196
(88%) tools/world-vector-studio-v2/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
197197
(89%) tools/asset-manager-v2/js/services/AssetSchemaValidator.js - executed lines 295/295; executed functions 25/28
198198
(89%) tools/preview-generator-v2/controls/StatusLogControl.js - executed lines 32/32; executed functions 8/9
199-
(90%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 820/820; executed functions 91/101
200199
(90%) tools/palette-manager-v2/modules/PaletteValidationService.js - executed lines 88/88; executed functions 9/10
201200
(90%) tools/text2speech-V2/js/controls/ActionNavControl.js - executed lines 117/117; executed functions 19/21
202201
(90%) tools/text2speech-V2/js/TextToSpeechToolApp.js - executed lines 807/807; executed functions 62/69
@@ -206,13 +205,13 @@ Files with executed line/function counts where available:
206205
(91%) tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js - executed lines 1583/1583; executed functions 145/159
207206
(92%) tools/object-vector-studio-v2/js/controls/ToolStarterShellControl.js - executed lines 112/112; executed functions 11/12
208207
(93%) tools/asset-manager-v2/js/services/WorkspaceBridge.js - executed lines 305/305; executed functions 25/27
209-
(93%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 178/178; executed functions 14/15
210208
(93%) tools/session-inspector-v2/js/SessionInspectorV2App.js - executed lines 337/337; executed functions 42/45
211209
(93%) tools/text2speech-V2/js/controls/QueueControl.js - executed lines 122/122; executed functions 26/28
212210
(93%) tools/workspace-manager-v2/js/controls/GameSelectorControl.js - executed lines 59/59; executed functions 13/14
213211
(93%) tools/workspace-manager-v2/js/controls/ToolTilesControl.js - executed lines 130/130; executed functions 14/15
214212
(94%) games/shared/workspaceGameMetadataHydrator.js - executed lines 106/106; executed functions 16/17
215213
(94%) tools/common/PaletteSortService.js - executed lines 103/103; executed functions 17/18
214+
(94%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 196/196; executed functions 15/16
216215
(95%) tools/session-inspector-v2/js/services/SessionInspectorV2StorageService.js - executed lines 142/142; executed functions 18/19
217216
(100%) games/Asteroids/flow/attract.js - executed lines 17/17; executed functions 1/1
218217
(100%) games/Asteroids/flow/highscore.js - executed lines 16/16; executed functions 1/1
@@ -254,7 +253,7 @@ Files with executed line/function counts where available:
254253
(100%) src/shared/utils/textWrapUtils.js - executed lines 34/34; executed functions 4/4
255254
(100%) tools/asset-manager-v2/js/bootstrap.js - executed lines 70/70; executed functions 4/4
256255
(100%) tools/asset-manager-v2/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
257-
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 59/59; executed functions 4/4
256+
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 68/68; executed functions 4/4
258257
(100%) tools/object-vector-studio-v2/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
259258
(100%) tools/preview-generator-v2/controls/AssetFolderControl.js - executed lines 20/20; executed functions 6/6
260259
(100%) tools/preview-generator-v2/controls/CaptureModeControl.js - executed lines 25/25; executed functions 9/9
@@ -295,7 +294,6 @@ Uncovered or low-coverage changed JS files:
295294
Changed JS files considered:
296295
(0%) tests/playwright/tools/WorkspaceManagerV2.spec.mjs - changed JS file not collected as browser runtime coverage
297296
(0%) tools/object-vector-studio-v2/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
298-
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js - changed JS file with browser V8 coverage
299-
(90%) tools/object-vector-studio-v2/js/ToolStarterApp.js - changed JS file with browser V8 coverage
300-
(93%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - changed JS file with browser V8 coverage
297+
(88%) tools/object-vector-studio-v2/js/ToolStarterApp.js - changed JS file with browser V8 coverage
298+
(94%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - changed JS file with browser V8 coverage
301299
(100%) tools/object-vector-studio-v2/js/bootstrap.js - changed JS file with browser V8 coverage
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# PR_26132_007-object-vector-studio-v2-transform-editing
2+
3+
## Purpose
4+
5+
Add the Object Vector Studio V2 transform and editing foundation on top of the existing shape runtime.
6+
7+
## Scope
8+
9+
- Added selected-shape move, rotate, scale, resize, duplicate, delete, and z-order controls.
10+
- Added optional shape transform validation with explicit rejection for invalid transform values.
11+
- Added selection bounding box, resize handle markers, and pivot/origin visualization on the SVG work surface.
12+
- Added multi-select foundation using shift-click canvas selection.
13+
- Added keyboard Delete/Backspace support for selected shapes.
14+
- Added grid snap and angle snap controls with session persistence.
15+
- Added viewport zoom, pan, reset, and coordinate display controls.
16+
- Added dynamic shape geometry edit controls in Object Details by selected shape type.
17+
- Kept selection synchronized across canvas, object tiles, JSON Details, and Object Details.
18+
- Preserved schema-gated render behavior with no partial mutation on invalid operations.
19+
- Kept the change scoped to Object Vector Studio V2 runtime, layout, CSS, and targeted Playwright coverage.
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:
32+
33+
```text
34+
39 passed
35+
```
36+
37+
Additional checks:
38+
39+
```powershell
40+
node --check tools/object-vector-studio-v2/js/ToolStarterApp.js
41+
node --check tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js
42+
node --check tools/object-vector-studio-v2/js/bootstrap.js
43+
node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs
44+
```
45+
46+
All checks passed.
47+
48+
## Playwright Coverage
49+
50+
The Workspace Manager V2 suite now validates:
51+
52+
- Selection synchronization from canvas to Object Details and JSON Details.
53+
- Multi-select shift-click foundation.
54+
- Move with grid snap, rotate with angle snap, scale, and resize operations.
55+
- Invalid transform rejection without partial mutation.
56+
- Z-order changes through forward/back/front/back controls.
57+
- Duplicate and keyboard delete for shapes.
58+
- Duplicate object behavior.
59+
- Viewport zoom, pan, and reset.
60+
- Selection bounding box, resize handles, and pivot/origin visualization.
61+
- Existing invalid shape payload rejection before render.
62+
63+
Expected pass behavior:
64+
65+
- Valid transform and edit operations mutate only the selected shape/object and log `OK` entries.
66+
- Canvas selection updates Object Details and JSON Details.
67+
- Viewport controls update the SVG viewBox and coordinate/zoom display.
68+
69+
Expected fail behavior:
70+
71+
- Invalid transform values log `FAIL` and leave the prior shape payload unchanged.
72+
- Locked or missing selections log `WARN` and do not mutate payloads.
73+
74+
## V8 Coverage
75+
76+
Required V8 coverage reports:
77+
78+
```text
79+
docs/dev/reports/playwright_v8_coverage_report.txt
80+
docs/dev/reports/playwright_v8_coverage.txt
81+
docs/dev/reports/coverage_changed_js_guardrail.txt
82+
```
83+
84+
Changed runtime JavaScript coverage includes:
85+
86+
```text
87+
(88%) tools/object-vector-studio-v2/js/ToolStarterApp.js
88+
(94%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js
89+
(100%) tools/object-vector-studio-v2/js/bootstrap.js
90+
```
91+
92+
The coverage guardrail reported no changed-runtime-JS warnings.
93+
94+
## Full Samples Smoke Test
95+
96+
Skipped. This PR is limited to Object Vector Studio V2 transform/editing runtime behavior and targeted Workspace Manager V2 Playwright coverage. It does not change shared sample loading or broad sample runtime behavior.
97+
98+
## Manual Test Steps
99+
100+
1. Open `tools/object-vector-studio-v2/index.html`.
101+
2. Import a valid payload with `palette.swatches` and `objects[]`.
102+
3. Select an object, create rectangle and circle shapes, and confirm the canvas selection box/handles/pivot appear.
103+
4. Shift-click another shape and confirm multi-select count appears.
104+
5. Move, rotate, scale, resize, and z-order the selected shape and confirm JSON Details updates.
105+
6. Try scale `0` and confirm Status Log shows `FAIL` and the previous transform remains.
106+
7. Duplicate and delete a selected shape, including Delete/Backspace from the canvas.
107+
8. Use zoom, pan, and reset view and confirm the coordinate/zoom display updates.
108+
109+
## Out Of Scope
110+
111+
- Drag-based direct manipulation.
112+
- Full path editing.
113+
- Persistent workspace manifest write-back for Object Vector Studio V2.
114+
- Palette editing.
115+
- World Vector Studio V2 runtime behavior.
116+
- Full samples smoke test.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,74 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13051305
await page.locator("#objectVectorStudioV2ShapeLockButton").click();
13061306
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"locked": true');
13071307
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape rectangle-1 lock set to locked\./);
1308+
await page.locator("#objectVectorStudioV2ShapeLockButton").click();
1309+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"locked": false');
1310+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-selection-bounds='rectangle-1']")).toHaveCount(1);
1311+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-resize-handle]")).toHaveCount(4);
1312+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-pivot-origin='rectangle-1']")).toHaveCount(1);
1313+
1314+
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click({ modifiers: ["Shift"] });
1315+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveClass(/is-selected/);
1316+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']")).toHaveClass(/is-selected/);
1317+
await expect(page.locator("#objectVectorStudioV2SelectedItemVisibility")).toContainText("2 shape selection");
1318+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShapeIds"');
1319+
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']").click();
1320+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Rectangle Geometry");
1321+
1322+
await page.locator("#objectVectorStudioV2GridSnapButton").click();
1323+
await expect(page.locator("#objectVectorStudioV2GridSnapButton")).toHaveAttribute("aria-pressed", "true");
1324+
await page.locator("#objectVectorStudioV2MoveXInput").fill("13");
1325+
await page.locator("#objectVectorStudioV2MoveYInput").fill("7");
1326+
await page.locator("#objectVectorStudioV2MoveShapeButton").click();
1327+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"x": 10');
1328+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"y": 10');
1329+
await expect(page.locator("#statusLog")).toHaveValue(/OK Moved shape rectangle-1 by 10, 10\./);
1330+
1331+
await page.locator("#objectVectorStudioV2AngleSnapButton").click();
1332+
await page.locator("#objectVectorStudioV2RotateInput").fill("22");
1333+
await page.locator("#objectVectorStudioV2RotateShapeButton").click();
1334+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"rotation": 15');
1335+
await expect(page.locator("#statusLog")).toHaveValue(/OK Rotated shape rectangle-1 by 15 degrees\./);
1336+
1337+
await page.locator("#objectVectorStudioV2ScaleInput").fill("0");
1338+
await page.locator("#objectVectorStudioV2ScaleShapeButton").click();
1339+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Invalid transform rejected for shape rectangle-1: scale must be greater than 0\./);
1340+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"scaleX": 0');
1341+
await page.locator("#objectVectorStudioV2ScaleInput").fill("1.2");
1342+
await page.locator("#objectVectorStudioV2ScaleShapeButton").click();
1343+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"scaleX": 1.2');
1344+
await expect(page.locator("#statusLog")).toHaveValue(/OK Scaled shape rectangle-1 by 1\.2\./);
1345+
1346+
await page.locator("#objectVectorStudioV2ResizeInput").fill("5");
1347+
await page.locator("#objectVectorStudioV2ResizeShapeButton").click();
1348+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"width": 91');
1349+
await expect(page.locator("#statusLog")).toHaveValue(/OK Resized shape rectangle-1 by 5\./);
1350+
1351+
await page.locator("#objectVectorStudioV2BringForwardButton").click();
1352+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"order": 2');
1353+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape rectangle-1 z-order forward\./);
1354+
await page.locator("#objectVectorStudioV2SendToBackButton").click();
1355+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"order": 1');
1356+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape rectangle-1 z-order back\./);
1357+
1358+
await page.locator("#objectVectorStudioV2DuplicateShapeButton").click();
1359+
await expect(page.locator("#objectVectorStudioV2ShapeCount")).toHaveValue("3 shapes");
1360+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-3']")).toHaveClass(/is-selected/);
1361+
await expect(page.locator("#statusLog")).toHaveValue(/OK Duplicated shape rectangle-1 as rectangle-3\./);
1362+
await page.locator("#objectVectorStudioV2RenderSurface").focus();
1363+
await page.keyboard.press("Delete");
1364+
await expect(page.locator("#objectVectorStudioV2ShapeCount")).toHaveValue("2 shapes");
1365+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-3']")).toHaveCount(0);
1366+
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted shape rectangle-3 from keyboard\./);
1367+
1368+
await page.locator("#objectVectorStudioV2ZoomInButton").click();
1369+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 125%");
1370+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "0 0 256 176");
1371+
await page.locator("#objectVectorStudioV2PanRightButton").click();
1372+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "20 0 256 176");
1373+
await page.locator("#objectVectorStudioV2ResetViewButton").click();
1374+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "0 0 320 220");
1375+
await expect(page.locator("#statusLog")).toHaveValue(/OK Viewport reset to 100% at origin\./);
13081376

13091377
await page.locator('[data-object-id="object-2"]').click();
13101378
await expect(page.locator('[data-object-id="object-2"]')).toHaveAttribute("aria-pressed", "true");
@@ -1318,6 +1386,14 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13181386
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"name": "Object 2 Renamed"');
13191387
await expect(page.locator("#statusLog")).toHaveValue(/OK Renamed object object-2 to Object 2 Renamed\./);
13201388

1389+
await page.locator("#objectVectorStudioV2DuplicateObjectButton").click();
1390+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("19 objects");
1391+
await expect(page.locator('[data-object-id="object-2-renamed-copy"]')).toHaveAttribute("aria-pressed", "true");
1392+
await expect(page.locator("#statusLog")).toHaveValue(/OK Duplicated object Object 2 Renamed as Object 2 Renamed Copy\./);
1393+
await page.locator("#objectVectorStudioV2DeleteObjectButton").click();
1394+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("18 objects");
1395+
await expect(page.locator('[data-object-id="object-2-renamed-copy"]')).toHaveCount(0);
1396+
13211397
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Shield Pickup");
13221398
await page.locator("#objectVectorStudioV2AddObjectButton").click();
13231399
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("19 objects");

0 commit comments

Comments
 (0)