Skip to content

Commit 7d0c714

Browse files
author
DavidQ
committed
Implement Object Vector Studio V2 object system foundation with schema validation and selectable object tiles - PR_26132_005-object-vector-studio-v2-object-system
1 parent 9be065c commit 7d0c714

9 files changed

Lines changed: 795 additions & 24 deletions

File tree

docs/dev/reports/playwright_v8_coverage.txt

Lines changed: 305 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# PR_26132_005-object-vector-studio-v2-object-system
2+
3+
## Purpose
4+
5+
Add the Object Vector Studio V2 object management foundation without implementing full vector editing.
6+
7+
## Scope
8+
9+
- Added Object accordion controls for Add, Rename, Delete, Flatten, and Object Name.
10+
- Added selectable object tiles with visible selected state and synchronized object count/name/details.
11+
- Added object type metadata foundation for details rendering by object type.
12+
- Changed JSON Details to show the active selected object JSON only, read-only.
13+
- Added schema validation before every payload render and object mutation.
14+
- Invalid payloads and blocked object actions are rejected visibly through Status Log.
15+
- Added Shape/Tools icon buttons with icon/text and compact icon display mode.
16+
- Kept all CSS and JavaScript external.
17+
- Did not add hidden default payloads or fallback objects.
18+
19+
## Validation
20+
21+
Playwright impacted: Yes.
22+
23+
Command run:
24+
25+
```powershell
26+
npm run test:workspace-v2
27+
```
28+
29+
Result:
30+
31+
```text
32+
39 passed
33+
```
34+
35+
Additional checks:
36+
37+
```powershell
38+
node --check tools/object-vector-studio-v2/js/ToolStarterApp.js
39+
node --check tools/object-vector-studio-v2/js/bootstrap.js
40+
node --check tools/object-vector-studio-v2/js/controls/StatusLogControl.js
41+
node --check tools/object-vector-studio-v2/tests/playwright/FirstClassToolStarter.spec.mjs
42+
node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs
43+
git diff --check
44+
```
45+
46+
All checks passed.
47+
48+
## Playwright Coverage
49+
50+
The Workspace Manager V2 suite now validates:
51+
52+
- Object Add is blocked without a schema-valid palette-backed payload.
53+
- Invalid payloads are rejected before render and leave JSON Details at `{}`.
54+
- Valid payloads render object tiles, selected state, object count, details, and active object JSON.
55+
- Select, Rename, Add, Flatten, and Delete synchronize object tiles, details, selected state, and JSON Details.
56+
- Shape/Tools icon/text toggle and compact icon mode update correctly.
57+
- Status Log emits actionable `OK` and `FAIL` lines for object and schema actions.
58+
59+
Expected pass behavior:
60+
61+
- A valid palette-backed payload can add, select, rename, flatten, and delete objects while remaining schema-valid.
62+
- JSON Details shows only the selected object.
63+
- Object details use the selected object type metadata framework.
64+
65+
Expected fail behavior:
66+
67+
- Missing palette and object actions without a valid payload fail visibly and do not partially render.
68+
69+
## V8 Coverage
70+
71+
Required V8 coverage reports:
72+
73+
```text
74+
docs/dev/reports/playwright_v8_coverage_report.txt
75+
docs/dev/reports/playwright_v8_coverage.txt
76+
docs/dev/reports/coverage_changed_js_guardrail.txt
77+
```
78+
79+
Changed runtime JavaScript coverage includes:
80+
81+
```text
82+
(57%) tools/object-vector-studio-v2/js/controls/StatusLogControl.js
83+
(91%) tools/object-vector-studio-v2/js/controls/ActionNavControl.js
84+
(92%) tools/object-vector-studio-v2/js/controls/ToolStarterShellControl.js
85+
(93%) tools/object-vector-studio-v2/js/ToolStarterApp.js
86+
(100%) tools/object-vector-studio-v2/js/bootstrap.js
87+
(100%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js
88+
```
89+
90+
The coverage guardrail reported no changed-runtime-JS warnings.
91+
92+
## Full Samples Smoke Test
93+
94+
Skipped. This PR is limited to Object Vector Studio V2 object management foundation and targeted Workspace Manager V2 Playwright coverage. It does not change shared sample loading or broad sample runtime behavior.
95+
96+
## Manual Test Steps
97+
98+
1. Open `tools/object-vector-studio-v2/index.html`.
99+
2. Try Add with an object name before importing JSON and confirm Status Log shows a `FAIL` message.
100+
3. Import invalid JSON missing `palette` and confirm no object tiles render.
101+
4. Import valid JSON with `palette.swatches` and `objects[]`.
102+
5. Select an object tile and confirm Object Name, Object Details, selected state, and JSON Details update.
103+
6. Rename the selected object and confirm the tile and JSON Details update.
104+
7. Add a new object and confirm the object count increases and the new tile is selected.
105+
8. Flatten the selected object and confirm the active object JSON includes flatten metadata.
106+
9. Delete the selected object and confirm the object list/count/details stay synchronized.
107+
108+
## Out Of Scope
109+
110+
- Full object editing.
111+
- Shape/path editing.
112+
- Persisting changes into Workspace Manager V2 manifests.
113+
- Palette editing.
114+
- Full samples smoke test.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,17 +1183,29 @@ test.describe("Workspace Manager V2 bootstrap", () => {
11831183
await expect(page.locator(".tool-starter__panel--right > .accordion-v2 > .accordion-v2__header > span:first-child")).toHaveText(["Palette", "Object Details", "JSON Details", "Status Log"]);
11841184
await expect(page.locator("#objectVectorStudioV2PaletteGate")).toHaveValue("Required before render");
11851185
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("No objects loaded");
1186-
await expect(page.locator(".object-vector-studio-v2__tool-toggle")).toHaveText(["+ Select", "Sh Shape", "P Path", "M Move", "R Rotate", "G Group"]);
1186+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("0 objects");
1187+
await expect(page.locator("#objectVectorStudioV2RenameObjectButton")).toBeDisabled();
1188+
await expect(page.locator("#objectVectorStudioV2DeleteObjectButton")).toBeDisabled();
1189+
await expect(page.locator("#objectVectorStudioV2FlattenObjectButton")).toBeDisabled();
1190+
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Blocked Object");
1191+
await page.locator("#objectVectorStudioV2AddObjectButton").click();
1192+
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"]);
11871194

11881195
await page.locator('[data-shape-tool="path"]').click();
11891196
await expect(page.locator('[data-shape-tool="path"]')).toHaveAttribute("aria-pressed", "true");
1190-
await expect(page.locator("#statusLog")).toHaveValue(/INFO Shape\/Tools shell toggle selected: path/);
1197+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape\/Tools shell toggle selected: path/);
1198+
await page.locator("#objectVectorStudioV2ToolLabelModeButton").click();
1199+
await expect(page.locator("#objectVectorStudioV2ToolLabelModeButton")).toHaveText("Icons");
1200+
await expect(page.locator("#objectVectorStudioV2ToolToggleGrid")).toHaveClass(/is-icon-only/);
1201+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape\/Tools display mode set to compact icons\./);
11911202

11921203
const invalidPayloadPath = testInfo.outputPath("object-vector-invalid.json");
11931204
await writeFile(invalidPayloadPath, JSON.stringify({ objects: [] }, null, 2), "utf8");
11941205
await page.locator("#objectVectorStudioV2ImportJsonInput").setInputFiles(invalidPayloadPath);
11951206
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-invalid\.json: root\.palette is required\./);
11961207
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("No objects loaded");
1208+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toHaveText("{}");
11971209

11981210
const validPayload = {
11991211
palette: {
@@ -1215,16 +1227,46 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12151227
await expect(page.locator("#objectVectorStudioV2PaletteGate")).toHaveValue("Palette loaded");
12161228
await expect(page.locator("#objectVectorStudioV2PaletteSummary")).toContainText("Palette arcade-primary: 2 swatches.");
12171229
await expect(page.locator("#objectVectorStudioV2ObjectTiles .object-vector-studio-v2__object-tile")).toHaveCount(18);
1230+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("18 objects");
12181231
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Asteroids Ship");
1232+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Ship entity metadata framework");
12191233
await expect(page.locator("#objectVectorStudioV2SelectedItemVisibility")).toContainText("Selected item visible: Asteroids Ship");
1220-
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"palette"');
1234+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"name": "Asteroids Ship"');
1235+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"palette"');
12211236
await expect(page.locator("#objectVectorStudioV2CopyJsonButton")).toBeEnabled();
12221237
await expect(page.locator("#objectVectorStudioV2ExportJsonButton")).toBeEnabled();
12231238
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\./);
12241239

12251240
await page.locator('[data-object-id="object-2"]').click();
12261241
await expect(page.locator('[data-object-id="object-2"]')).toHaveAttribute("aria-pressed", "true");
12271242
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Object 2");
1243+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Enemy entity metadata framework");
1244+
await expect(page.locator("#objectVectorStudioV2ObjectNameInput")).toHaveValue("Object 2");
1245+
1246+
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Object 2 Renamed");
1247+
await page.locator("#objectVectorStudioV2RenameObjectButton").click();
1248+
await expect(page.locator('[data-object-id="object-2"]')).toContainText("Object 2 Renamed");
1249+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"name": "Object 2 Renamed"');
1250+
await expect(page.locator("#statusLog")).toHaveValue(/OK Renamed object object-2 to Object 2 Renamed\./);
1251+
1252+
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Shield Pickup");
1253+
await page.locator("#objectVectorStudioV2AddObjectButton").click();
1254+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("19 objects");
1255+
await expect(page.locator('[data-object-id="shield-pickup"]')).toHaveAttribute("aria-pressed", "true");
1256+
await expect(page.locator('[data-object-id="shield-pickup"]')).toContainText("Shield Pickup");
1257+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"type": "object"');
1258+
await expect(page.locator("#statusLog")).toHaveValue(/OK Added object Shield Pickup with id shield-pickup\./);
1259+
1260+
await page.locator("#objectVectorStudioV2FlattenObjectButton").click();
1261+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Flattened metadata foundation");
1262+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"flattened": true');
1263+
await expect(page.locator("#statusLog")).toHaveValue(/OK Flattened object metadata for Shield Pickup\./);
1264+
1265+
await page.locator("#objectVectorStudioV2DeleteObjectButton").click();
1266+
await expect(page.locator("#objectVectorStudioV2ObjectCount")).toHaveValue("18 objects");
1267+
await expect(page.locator('[data-object-id="shield-pickup"]')).toHaveCount(0);
1268+
await expect(page.locator('[data-object-id="object-1"]')).toHaveAttribute("aria-pressed", "true");
1269+
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted object Shield Pickup\./);
12281270

12291271
const leftOpenHeights = await page.locator(".tool-starter__panel--left .accordion-v2.is-open").evaluateAll((sections) => (
12301272
sections.map((section) => Math.round(section.getBoundingClientRect().height))

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

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
6363
<span>Palette</span>
6464
<input id="objectVectorStudioV2PaletteGate" type="text" autocomplete="off" value="Required before render" readonly>
6565
</label>
66+
<label class="tool-starter__field tool-starter__field--compact" for="objectVectorStudioV2ObjectCount">
67+
<span>Objects</span>
68+
<input id="objectVectorStudioV2ObjectCount" type="text" autocomplete="off" value="0 objects" readonly>
69+
</label>
70+
<label class="tool-starter__field tool-starter__field--stacked" for="objectVectorStudioV2ObjectNameInput">
71+
<span>Object Name</span>
72+
<input id="objectVectorStudioV2ObjectNameInput" type="text" autocomplete="off" placeholder="Object name">
73+
</label>
74+
<div class="object-vector-studio-v2__object-actions" aria-label="Object actions">
75+
<button id="objectVectorStudioV2AddObjectButton" type="button">Add</button>
76+
<button id="objectVectorStudioV2RenameObjectButton" type="button">Rename</button>
77+
<button id="objectVectorStudioV2DeleteObjectButton" type="button">Delete</button>
78+
<button id="objectVectorStudioV2FlattenObjectButton" type="button">Flatten</button>
79+
</div>
6680
<div id="objectVectorStudioV2LoadStatus" class="object-vector-studio-v2__callout" role="status">Schema-only loading is idle. Import JSON or launch with workspace toolState data that includes a palette.</div>
6781
</div>
6882
</section>
@@ -73,30 +87,37 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
7387
<span class="accordion-v2__icon" aria-hidden="true">+</span>
7488
</button>
7589
<div id="objectVectorStudioV2ShapeToolsContent" class="accordion-v2__content object-vector-studio-v2__scroll-content">
76-
<div class="object-vector-studio-v2__tool-toggle-grid" aria-label="Shape and tool toggles">
90+
<div class="object-vector-studio-v2__tool-display-actions">
91+
<button id="objectVectorStudioV2ToolLabelModeButton" type="button" aria-pressed="false">Icon/Text</button>
92+
</div>
93+
<div id="objectVectorStudioV2ToolToggleGrid" class="object-vector-studio-v2__tool-toggle-grid" aria-label="Shape and tool toggles">
7794
<button class="object-vector-studio-v2__tool-toggle is-active" type="button" aria-pressed="true" data-shape-tool="select">
7895
<span aria-hidden="true">+</span>
79-
<span>Select</span>
96+
<span class="object-vector-studio-v2__tool-label">Select</span>
97+
</button>
98+
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="rectangle">
99+
<span aria-hidden="true">Re</span>
100+
<span class="object-vector-studio-v2__tool-label">Rectangle</span>
80101
</button>
81-
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="shape">
82-
<span aria-hidden="true">Sh</span>
83-
<span>Shape</span>
102+
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="ellipse">
103+
<span aria-hidden="true">El</span>
104+
<span class="object-vector-studio-v2__tool-label">Ellipse</span>
84105
</button>
85106
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="path">
86107
<span aria-hidden="true">P</span>
87-
<span>Path</span>
108+
<span class="object-vector-studio-v2__tool-label">Path</span>
88109
</button>
89110
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="move">
90111
<span aria-hidden="true">M</span>
91-
<span>Move</span>
112+
<span class="object-vector-studio-v2__tool-label">Move</span>
92113
</button>
93114
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="rotate">
94115
<span aria-hidden="true">R</span>
95-
<span>Rotate</span>
116+
<span class="object-vector-studio-v2__tool-label">Rotate</span>
96117
</button>
97118
<button class="object-vector-studio-v2__tool-toggle" type="button" aria-pressed="false" data-shape-tool="group">
98119
<span aria-hidden="true">G</span>
99-
<span>Group</span>
120+
<span class="object-vector-studio-v2__tool-label">Group</span>
100121
</button>
101122
</div>
102123
<p class="tool-starter__hint">Tool toggles are shell-only placeholders until object editing is implemented.</p>

0 commit comments

Comments
 (0)