Skip to content

Commit 1162e85

Browse files
author
DavidQ
committed
Implement Object Vector Studio V2 primitive shape runtime and render foundation - PR_26132_006-object-vector-studio-v2-shape-runtime
1 parent 7d0c714 commit 1162e85

9 files changed

Lines changed: 918 additions & 66 deletions

File tree

docs/dev/reports/playwright_v8_coverage.txt

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ Exercised tool entry points detected:
2121

2222
Changed runtime JS files covered:
2323
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js - executed lines 24/24; executed functions 4/7
24-
(91%) tools/object-vector-studio-v2/js/controls/ActionNavControl.js - executed lines 75/75; executed functions 10/11
25-
(92%) tools/object-vector-studio-v2/js/controls/ToolStarterShellControl.js - executed lines 112/112; executed functions 11/12
26-
(93%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 403/403; executed functions 52/56
27-
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 56/56; executed functions 4/4
28-
(100%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 70/70; executed functions 7/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
2927

3028
Files with executed line/function counts where available:
3129
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -198,6 +196,7 @@ Files with executed line/function counts where available:
198196
(88%) tools/world-vector-studio-v2/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
199197
(89%) tools/asset-manager-v2/js/services/AssetSchemaValidator.js - executed lines 295/295; executed functions 25/28
200198
(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
201200
(90%) tools/palette-manager-v2/modules/PaletteValidationService.js - executed lines 88/88; executed functions 9/10
202201
(90%) tools/text2speech-V2/js/controls/ActionNavControl.js - executed lines 117/117; executed functions 19/21
203202
(90%) tools/text2speech-V2/js/TextToSpeechToolApp.js - executed lines 807/807; executed functions 62/69
@@ -207,7 +206,7 @@ Files with executed line/function counts where available:
207206
(91%) tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js - executed lines 1583/1583; executed functions 145/159
208207
(92%) tools/object-vector-studio-v2/js/controls/ToolStarterShellControl.js - executed lines 112/112; executed functions 11/12
209208
(93%) tools/asset-manager-v2/js/services/WorkspaceBridge.js - executed lines 305/305; executed functions 25/27
210-
(93%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 403/403; executed functions 52/56
209+
(93%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 178/178; executed functions 14/15
211210
(93%) tools/session-inspector-v2/js/SessionInspectorV2App.js - executed lines 337/337; executed functions 42/45
212211
(93%) tools/text2speech-V2/js/controls/QueueControl.js - executed lines 122/122; executed functions 26/28
213212
(93%) tools/workspace-manager-v2/js/controls/GameSelectorControl.js - executed lines 59/59; executed functions 13/14
@@ -255,9 +254,8 @@ Files with executed line/function counts where available:
255254
(100%) src/shared/utils/textWrapUtils.js - executed lines 34/34; executed functions 4/4
256255
(100%) tools/asset-manager-v2/js/bootstrap.js - executed lines 70/70; executed functions 4/4
257256
(100%) tools/asset-manager-v2/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
258-
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 56/56; executed functions 4/4
257+
(100%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 59/59; executed functions 4/4
259258
(100%) tools/object-vector-studio-v2/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
260-
(100%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 70/70; executed functions 7/7
261259
(100%) tools/preview-generator-v2/controls/AssetFolderControl.js - executed lines 20/20; executed functions 6/6
262260
(100%) tools/preview-generator-v2/controls/CaptureModeControl.js - executed lines 25/25; executed functions 9/9
263261
(100%) tools/preview-generator-v2/controls/GeneratePreviewControl.js - executed lines 20/20; executed functions 6/6
@@ -298,8 +296,6 @@ Changed JS files considered:
298296
(0%) tests/playwright/tools/WorkspaceManagerV2.spec.mjs - changed JS file not collected as browser runtime coverage
299297
(0%) tools/object-vector-studio-v2/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
300298
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js - changed JS file with browser V8 coverage
301-
(91%) tools/object-vector-studio-v2/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
302-
(92%) tools/object-vector-studio-v2/js/controls/ToolStarterShellControl.js - changed JS file with browser V8 coverage
303-
(93%) tools/object-vector-studio-v2/js/ToolStarterApp.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
304301
(100%) tools/object-vector-studio-v2/js/bootstrap.js - changed JS file with browser V8 coverage
305-
(100%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - changed JS file with browser V8 coverage
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# PR_26132_006-object-vector-studio-v2-shape-runtime
2+
3+
## Purpose
4+
5+
Add the Object Vector Studio V2 shape runtime foundation without implementing full vector editing.
6+
7+
## Scope
8+
9+
- Added shape/tool creation for rectangle, circle, ellipse, line, polygon, arc, and text primitives.
10+
- Added object-to-shape ownership with per-object shape ordering.
11+
- Added selectable rendered shapes with selected highlight state.
12+
- Added shape visibility, lock, ordering, and type metadata controls in Object Details.
13+
- Added center SVG work surface rendering with explicit render/capture mode logging.
14+
- Added palette swatches that apply color to the selected shape.
15+
- Updated JSON Details to show the selected object and selected shape payload hierarchy.
16+
- Added invalid shape payload validation before render with actionable Status Log failures.
17+
- Finalized Shape/Tools compact icon grid behavior and persisted Words/Icon mode during the session.
18+
- Kept all CSS and JavaScript external.
19+
- Did not add hidden defaults or fallback rendering.
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+
node --check tools/object-vector-studio-v2/tests/playwright/FirstClassToolStarter.spec.mjs
45+
git diff --check
46+
```
47+
48+
All checks passed.
49+
50+
## Playwright Coverage
51+
52+
The Workspace Manager V2 suite now validates:
53+
54+
- Primitive creation for rendered Object Vector Studio V2 shapes.
55+
- Shape selection from the SVG work surface.
56+
- Selected shape highlight state and selected shape JSON details.
57+
- Shape visibility toggle behavior.
58+
- Shape lock state logging and JSON synchronization.
59+
- Palette color application to a selected shape.
60+
- Invalid shape payload rejection before render.
61+
- Render/capture mode logging with visible shape counts.
62+
- Shape/Tools icon mode layout and session persistence.
63+
64+
Expected pass behavior:
65+
66+
- A valid palette-backed payload can create and select primitives while preserving object-to-shape ownership.
67+
- Palette swatches update the selected shape style and log the operation.
68+
- Hidden shapes are omitted from the render surface without deleting payload data.
69+
- JSON Details shows selected object and selected shape hierarchy.
70+
71+
Expected fail behavior:
72+
73+
- Missing palette, missing selected object, and invalid shape payloads fail visibly and do not partially render.
74+
- Render failures include the exact shape item and type in Status Log.
75+
76+
## V8 Coverage
77+
78+
Required V8 coverage reports:
79+
80+
```text
81+
docs/dev/reports/playwright_v8_coverage_report.txt
82+
docs/dev/reports/playwright_v8_coverage.txt
83+
docs/dev/reports/coverage_changed_js_guardrail.txt
84+
```
85+
86+
Changed runtime JavaScript coverage includes:
87+
88+
```text
89+
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js
90+
(90%) tools/object-vector-studio-v2/js/ToolStarterApp.js
91+
(93%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js
92+
(100%) tools/object-vector-studio-v2/js/bootstrap.js
93+
```
94+
95+
The coverage guardrail reported no changed-runtime-JS warnings.
96+
97+
## Full Samples Smoke Test
98+
99+
Skipped. This PR is limited to Object Vector Studio V2 shape runtime foundation and targeted Workspace Manager V2 Playwright coverage. It does not change shared sample loading or broad sample runtime behavior.
100+
101+
## Manual Test Steps
102+
103+
1. Open `tools/object-vector-studio-v2/index.html`.
104+
2. Import a valid payload with `palette.swatches` and `objects[]`.
105+
3. Select an object tile.
106+
4. Create rectangle, circle, ellipse, line, polygon, arc, and text primitives from Shape/Tools.
107+
5. Select a rendered shape and confirm Object Details and JSON Details update.
108+
6. Apply a palette color and confirm the selected shape style updates.
109+
7. Toggle shape visibility and confirm the rendered shape hides without deleting payload data.
110+
8. Toggle shape lock and confirm Status Log and JSON Details update.
111+
9. Import an invalid shape payload and confirm render is blocked with an actionable `FAIL` log.
112+
113+
## Out Of Scope
114+
115+
- Full vector path editing.
116+
- Drag handles and direct geometry editing on the work surface.
117+
- Persisting changes into Workspace Manager V2 manifests.
118+
- Palette editing.
119+
- World Vector Studio V2 runtime behavior.
120+
- Full samples smoke test.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,15 +1190,18 @@ test.describe("Workspace Manager V2 bootstrap", () => {
11901190
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Blocked Object");
11911191
await page.locator("#objectVectorStudioV2AddObjectButton").click();
11921192
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Add object blocked: load a schema-valid palette-backed payload before adding objects\./);
1193-
await expect(page.locator(".object-vector-studio-v2__tool-toggle")).toHaveText(["+ Select", "Re Rectangle", "El Ellipse", "P Path", "M Move", "R Rotate", "G Group"]);
1193+
await expect(page.locator(".object-vector-studio-v2__tool-toggle")).toHaveText(["+ Select", "Re Rectangle", "Ci Circle", "El Ellipse", "Li Line", "Po Polygon", "Ar Arc", "Tx Text"]);
11941194

1195-
await page.locator('[data-shape-tool="path"]').click();
1196-
await expect(page.locator('[data-shape-tool="path"]')).toHaveAttribute("aria-pressed", "true");
1197-
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape\/Tools shell toggle selected: path/);
1195+
await page.locator('[data-shape-tool="rectangle"]').click();
1196+
await expect(page.locator('[data-shape-tool="rectangle"]')).toHaveAttribute("aria-pressed", "true");
1197+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Create rectangle blocked: select a schema-valid object first\./);
11981198
await page.locator("#objectVectorStudioV2ToolLabelModeButton").click();
1199-
await expect(page.locator("#objectVectorStudioV2ToolLabelModeButton")).toHaveText("Icons");
1199+
await expect(page.locator("#objectVectorStudioV2ToolLabelModeButton")).toHaveText("Words");
12001200
await expect(page.locator("#objectVectorStudioV2ToolToggleGrid")).toHaveClass(/is-icon-only/);
12011201
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape\/Tools display mode set to compact icons\./);
1202+
await page.reload({ waitUntil: "networkidle" });
1203+
await expect(page.locator("#objectVectorStudioV2ToolToggleGrid")).toHaveClass(/is-icon-only/);
1204+
await expect(page.locator("#objectVectorStudioV2ToolLabelModeButton")).toHaveText("Words");
12021205

12031206
const invalidPayloadPath = testInfo.outputPath("object-vector-invalid.json");
12041207
await writeFile(invalidPayloadPath, JSON.stringify({ objects: [] }, null, 2), "utf8");
@@ -1207,6 +1210,38 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12071210
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("No objects loaded");
12081211
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toHaveText("{}");
12091212

1213+
const invalidShapePayloadPath = testInfo.outputPath("object-vector-invalid-shape.json");
1214+
await writeFile(invalidShapePayloadPath, JSON.stringify({
1215+
palette: {
1216+
id: "arcade-primary",
1217+
swatches: [
1218+
{ id: "white", value: "#ffffff" }
1219+
]
1220+
},
1221+
objects: [
1222+
{
1223+
id: "bad-object",
1224+
name: "Bad Object",
1225+
shapes: [
1226+
{
1227+
geometry: {},
1228+
id: "bad-shape",
1229+
locked: false,
1230+
order: 1,
1231+
style: { fill: "#ffffff", stroke: "#ffffff", strokeWidth: 3 },
1232+
type: "triangle",
1233+
visible: true
1234+
}
1235+
],
1236+
type: "object"
1237+
}
1238+
]
1239+
}, null, 2), "utf8");
1240+
await page.locator("#objectVectorStudioV2ImportJsonInput").setInputFiles(invalidShapePayloadPath);
1241+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-invalid-shape\.json: root\.objects\[0\]\.shapes\[0\]\.type must be one of rectangle, circle, ellipse, line, polygon, arc, text\./);
1242+
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("No objects loaded");
1243+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toHaveText("{}");
1244+
12101245
const validPayload = {
12111246
palette: {
12121247
id: "arcade-primary",
@@ -1218,6 +1253,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12181253
objects: Array.from({ length: 18 }, (_, index) => ({
12191254
id: `object-${index + 1}`,
12201255
name: index === 0 ? "Asteroids Ship" : `Object ${index + 1}`,
1256+
shapes: [],
12211257
type: index === 1 ? "enemy" : "ship"
12221258
}))
12231259
};
@@ -1232,10 +1268,43 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12321268
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Ship entity metadata framework");
12331269
await expect(page.locator("#objectVectorStudioV2SelectedItemVisibility")).toContainText("Selected item visible: Asteroids Ship");
12341270
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"name": "Asteroids Ship"');
1271+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShape": null');
12351272
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"palette"');
12361273
await expect(page.locator("#objectVectorStudioV2CopyJsonButton")).toBeEnabled();
12371274
await expect(page.locator("#objectVectorStudioV2ExportJsonButton")).toBeEnabled();
12381275
await expect(page.locator("#statusLog")).toHaveValue(/OK Loaded Object Vector Studio V2 schema payload from import:object-vector-valid\.json: 18 objects, 2 palette swatches\./);
1276+
await expect(page.locator("#statusLog")).toHaveValue(/OK Render mode svg-work-surface: rendered Asteroids Ship with 0 visible shapes; capture mode none\./);
1277+
1278+
await page.locator('[data-shape-tool="rectangle"]').click();
1279+
await expect(page.locator("#objectVectorStudioV2ShapeCount")).toHaveValue("1 shape");
1280+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveClass(/is-selected/);
1281+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShape"');
1282+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"type": "rectangle"');
1283+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Rectangle primitive metadata");
1284+
await expect(page.locator("#statusLog")).toHaveValue(/OK Created rectangle shape rectangle-1 on Asteroids Ship\./);
1285+
await expect(page.locator("#statusLog")).toHaveValue(/OK Render mode svg-work-surface: rendered Asteroids Ship with 1 visible shapes; capture mode none\./);
1286+
1287+
await page.locator('[data-shape-tool="circle"]').click();
1288+
await expect(page.locator("#objectVectorStudioV2ShapeCount")).toHaveValue("2 shapes");
1289+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']")).toHaveClass(/is-selected/);
1290+
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']").click();
1291+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveClass(/is-selected/);
1292+
await expect(page.locator("#statusLog")).toHaveValue(/OK Selected shape from render surface: rectangle-1 \(rectangle\)\./);
1293+
1294+
await page.locator("[data-palette-color='#6fd3ff']").click();
1295+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"fill": "#6fd3ff"');
1296+
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied palette color #6fd3ff from cyan to shape rectangle-1\./);
1297+
1298+
await page.locator("#objectVectorStudioV2ShapeVisibilityButton").click();
1299+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"visible": false');
1300+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveCount(0);
1301+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape rectangle-1 visibility set to hidden\./);
1302+
1303+
await page.locator("#objectVectorStudioV2ShapeVisibilityButton").click();
1304+
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveCount(1);
1305+
await page.locator("#objectVectorStudioV2ShapeLockButton").click();
1306+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"locked": true');
1307+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape rectangle-1 lock set to locked\./);
12391308

12401309
await page.locator('[data-object-id="object-2"]').click();
12411310
await expect(page.locator('[data-object-id="object-2"]')).toHaveAttribute("aria-pressed", "true");
@@ -1255,6 +1324,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12551324
await expect(page.locator('[data-object-id="shield-pickup"]')).toHaveAttribute("aria-pressed", "true");
12561325
await expect(page.locator('[data-object-id="shield-pickup"]')).toContainText("Shield Pickup");
12571326
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"type": "object"');
1327+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"shapes": []');
12581328
await expect(page.locator("#statusLog")).toHaveValue(/OK Added object Shield Pickup with id shield-pickup\./);
12591329

12601330
await page.locator("#objectVectorStudioV2FlattenObjectButton").click();

0 commit comments

Comments
 (0)