Skip to content

Commit a7826c4

Browse files
author
DavidQ
committed
Consolidate frame state schema SSoT and add frame tile state dropdown - PR_26133_048-frame-state-schema-ssot-and-palette-filter-layout
1 parent 844e4ca commit a7826c4

12 files changed

Lines changed: 196 additions & 133 deletions

File tree

docs/dev/reports/playwright_v8_coverage_report.md

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

3-
Task: PR_26133_047-frame-state-help-and-frame-id-generation-fix
3+
Task: PR_26133_048-frame-state-schema-ssot-and-palette-filter-layout
44
Date: 2026-05-15
55

66
## Result
@@ -27,6 +27,7 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
2727
(80%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 97/97; executed functions 4/5
2828
(90%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 3187/3187; executed functions 331/369
2929
(95%) tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js - executed lines 409/409; executed functions 53/56
30+
(91%) tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js - executed lines 1598/1598; executed functions 145/159
3031
```
3132

3233
## Guardrail
@@ -37,4 +38,4 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
3738

3839
## PR-Specific Note
3940

40-
The Workspace V2 run exercised Object Vector Studio V2 state selection, contextual state help text, legacy state/frame manifest loading, generic frame duplication, playback, runtime preview, and export paths.
41+
The Workspace V2 run exercised Object Vector Studio V2 palette sort layout, frame-tile state dropdown/help controls, Object Vector state schema validation, Workspace Manager game-manifest validation with an external Object Vector schema reference, and Asteroids runtime object-vector rendering.
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_047 Workspace V2 Playwright Results
1+
# PR_26133_048 Workspace V2 Playwright Results
22

3-
Task: PR_26133_047-frame-state-help-and-frame-id-generation-fix
3+
Task: PR_26133_048-frame-state-schema-ssot-and-palette-filter-layout
44
Date: 2026-05-15
55

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

1515
## PR-Specific Coverage
1616

17-
- Verified the Object Vector Studio V2 frame state dropdown renders beside a `?` help button.
18-
- Verified the `idle` help text title is `Default stationary state.` and `No movement or action animation active.`
19-
- Verified the `move` help text title is `Movement/action state.` and `Used for thrusting, walking, flying, or active movement visuals.`
20-
- Verified new schema-valid state IDs include `move` while legacy `thrust` manifests still load.
21-
- Verified duplicate frame generation uses generic `frame-x` IDs and does not continue legacy `idle-frame-x` naming.
22-
- Verified a legacy `idle-frame-1` import duplicates into `frame-1`.
17+
- Verified Hue, Sat, Bri, and Name palette sort controls fit on one line with reduced text and icon sizing.
18+
- Verified `thrust` is removed from the Object Vector Studio V2 state enum.
19+
- Verified `game.manifest.schema.json` references the Object Vector Studio V2 `objectState` definition instead of duplicating a local state enum.
20+
- Verified Asteroids runtime state selection now uses `move` for thrust visuals.
21+
- Verified each frame tile renders a compact state dropdown directly under the frame tile label.
22+
- Verified each frame tile renders a `?` hover help button using the contextual state help text.
2323

2424
## Additional Validation
2525

26-
- Targeted Playwright check passed for `supports Object Vector Studio V2 animation states and frame timeline foundation`.
26+
- Targeted Playwright checks passed for Object Vector Studio V2 shell/schema/palette layout and animation frame controls.
2727
- `git diff --check` passed.

games/Asteroids/game.manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,8 @@
353353
]
354354
},
355355
{
356-
"id": "thrust",
357-
"name": "Thrust",
356+
"id": "move",
357+
"name": "Move",
358358
"frames": [
359359
{
360360
"id": "frame-1",

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ export default class AsteroidsGameScene extends Scene {
794794
elapsedMs: this.objectVectorPlaybackMs,
795795
fps: 12,
796796
rotation: this.world.ship.angle + Math.PI / 2,
797-
stateId: this.world.ship.thrusting && this.session.mode === 'playing' ? "thrust" : "idle",
797+
stateId: this.world.ship.thrusting && this.session.mode === 'playing' ? "move" : "idle",
798798
x: this.world.ship.x,
799799
y: this.world.ship.y,
800800
});

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,23 +1215,33 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12151215
const toolSchema = window.__objectVectorStudioV2App.schemaService.schema;
12161216
const gameSchema = await fetch("/tools/schemas/game.manifest.schema.json", { cache: "no-store" }).then((response) => response.json());
12171217
return {
1218+
gameHasLocalObjectStateDefinition: Object.prototype.hasOwnProperty.call(gameSchema.$defs, "objectVectorStudioState"),
12181219
gameObjectDefaultTags: gameSchema.$defs.objectVectorStudioObject.default.tags,
12191220
gamePolygonDefaultPointCount: gameSchema.$defs.objectVectorStudioPolygonGeometry.default.points.length,
12201221
gameShapeCommonDefaultTool: gameSchema.$defs.objectVectorStudioShapeCommon.default.tool,
1222+
gameStateSchemaRef: gameSchema.$defs.objectVectorStudioObject.properties.states.items.$ref,
1223+
gameStateThrustMentionRemoved: !JSON.stringify(gameSchema).includes('"thrust"'),
12211224
gameTransformDefaultOrigin: gameSchema.$defs.objectVectorStudioTransform.default.origin,
12221225
toolPolygonDefaultPointCount: toolSchema.$defs.polygonGeometry.default.points.length,
12231226
toolShapeCommonDefaultTool: toolSchema.$defs.shapeCommon.default.tool,
1227+
toolStateEnum: toolSchema.$defs.objectState.properties.id.enum,
1228+
toolStateThrustRemoved: !toolSchema.$defs.objectState.properties.id.enum.includes("thrust"),
12241229
toolStyleDefaultStrokeWidth: toolSchema.$defs.style.default.strokeWidth,
12251230
toolTransformDefaultOrigin: toolSchema.$defs.transform.default.origin
12261231
};
12271232
});
12281233
expect(objectVectorSchemaDefaults).toEqual({
1234+
gameHasLocalObjectStateDefinition: false,
12291235
gameObjectDefaultTags: [],
12301236
gamePolygonDefaultPointCount: 5,
12311237
gameShapeCommonDefaultTool: "polygon",
1238+
gameStateSchemaRef: "tools/object-vector-studio-v2.schema.json#/$defs/objectState",
1239+
gameStateThrustMentionRemoved: true,
12321240
gameTransformDefaultOrigin: { x: 0, y: 0 },
12331241
toolPolygonDefaultPointCount: 5,
12341242
toolShapeCommonDefaultTool: "polygon",
1243+
toolStateEnum: ["idle", "move", "active", "inactive", "damaged", "destroyed"],
1244+
toolStateThrustRemoved: true,
12351245
toolStyleDefaultStrokeWidth: 3,
12361246
toolTransformDefaultOrigin: { x: 0, y: 0 }
12371247
});
@@ -1317,6 +1327,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13171327
};
13181328
};
13191329
const title = (selector) => document.querySelector(selector).title;
1330+
const paletteSortButtons = Array.from(document.querySelectorAll(".object-vector-studio-v2__palette-sort button"));
1331+
const paletteSortRects = paletteSortButtons.map((button) => button.getBoundingClientRect());
1332+
const paletteSortButtonStyles = paletteSortButtons.map((button) => getComputedStyle(button));
13201333
return {
13211334
actionIcons: {
13221335
add: icon("#objectVectorStudioV2AddObjectButton"),
@@ -1332,6 +1345,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13321345
name: icon("[data-palette-sort='name']"),
13331346
sat: icon("[data-palette-sort='sat']")
13341347
},
1348+
paletteSortLayout: {
1349+
buttonFontSizes: paletteSortButtonStyles.map((style) => Number.parseFloat(style.fontSize)),
1350+
iconFontSizes: paletteSortButtons.map((button) => Number.parseFloat(getComputedStyle(button, "::before").fontSize)),
1351+
singleLine: paletteSortRects.every((rect) => Math.abs(rect.top - paletteSortRects[0].top) < 2)
1352+
},
13351353
gridIcons: {
13361354
angle: icon("#objectVectorStudioV2AngleSnapButton"),
13371355
render: icon("#objectVectorStudioV2GridRenderButton"),
@@ -1387,7 +1405,6 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13871405
[
13881406
...Object.values(iconStyleState.actionIcons),
13891407
...Object.values(iconStyleState.gridIcons),
1390-
...Object.values(iconStyleState.paletteSortIcons),
13911408
...Object.values(iconStyleState.shapeIcons),
13921409
...Object.values(iconStyleState.viewportIcons),
13931410
...Object.values(iconStyleState.zIcons)
@@ -1396,6 +1413,15 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13961413
expect(icon.fontFamily).toContain("0xProto Nerd Font");
13971414
expect(icon.fontSize).toBeGreaterThanOrEqual(21);
13981415
});
1416+
Object.values(iconStyleState.paletteSortIcons).forEach((icon) => {
1417+
expect(icon.glyphPresent).toBe(true);
1418+
expect(icon.fontFamily).toContain("0xProto Nerd Font");
1419+
expect(icon.fontSize).toBeGreaterThanOrEqual(10);
1420+
expect(icon.fontSize).toBeLessThanOrEqual(16);
1421+
});
1422+
expect(iconStyleState.paletteSortLayout.singleLine).toBe(true);
1423+
expect(Math.max(...iconStyleState.paletteSortLayout.buttonFontSizes)).toBeLessThanOrEqual(12);
1424+
expect(Math.max(...iconStyleState.paletteSortLayout.iconFontSizes)).toBeLessThanOrEqual(16);
13991425
expect(iconStyleState.actionIcons.paint.iconName).toBe("nf-fa-paint_brush");
14001426
expect(iconStyleState.actionIcons.stroke.iconName).toBe("nf-fa-pencil");
14011427
expect(iconStyleState.modeButtons).toEqual({
@@ -3997,8 +4023,8 @@ test.describe("Workspace Manager V2 bootstrap", () => {
39974023
await expect(page.locator('[data-object-id="object.animation.ship-template"]')).toHaveAttribute("aria-pressed", "true");
39984024
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='idle']")).toHaveCount(1);
39994025
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1']")).toHaveAttribute("aria-pressed", "true");
4000-
await expect(page.locator("#objectVectorStudioV2StateSelect")).toHaveValue("idle");
4001-
await expect(page.locator("#objectVectorStudioV2StateHelpButton")).toHaveAttribute("title", "Default stationary state.\nNo movement or action animation active.");
4026+
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1'] [data-frame-state-select='frame-1']")).toHaveValue("idle");
4027+
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1'] [data-frame-state-help='frame-1']")).toHaveAttribute("title", "Default stationary state.\nNo movement or action animation active.");
40024028
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"id": "idle"');
40034029
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Selected Shape");
40044030
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("ship-template-hull");
@@ -4044,12 +4070,12 @@ test.describe("Workspace Manager V2 bootstrap", () => {
40444070
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-onion-skin-frame='frame-2']")).toHaveCount(1);
40454071
await expect(page.locator("#statusLog")).toHaveValue(/OK Onion-skin preview enabled\./);
40464072

4047-
await page.evaluate(() => window.__objectVectorStudioV2App.selectState("move", "test state selection"));
4073+
await page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1'] [data-frame-state-select='frame-1']").selectOption("move");
40484074
await expect(page.locator('[data-object-id="object.animation.ship-template"]')).toHaveAttribute("aria-pressed", "true");
4049-
await expect(page.locator("#objectVectorStudioV2StateSelect")).toHaveValue("move");
4050-
await expect(page.locator("#objectVectorStudioV2StateHelpButton")).toHaveAttribute("title", "Movement/action state.\nUsed for thrusting, walking, flying, or active movement visuals.");
4075+
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1'] [data-frame-state-select='frame-1']")).toHaveValue("move");
4076+
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1'] [data-frame-state-help='frame-1']")).toHaveAttribute("title", "Movement/action state.\nUsed for thrusting, walking, flying, or active movement visuals.");
40514077
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='move']")).toHaveCount(1);
4052-
await expect(page.locator("#statusLog")).toHaveValue(/OK Selected state Move from test state selection; active object remains Ship Template\./);
4078+
await expect(page.locator("#statusLog")).toHaveValue(/OK Selected state Move from frame frame-1 state dropdown; active object remains Ship Template\./);
40534079
await page.evaluate(() => window.__objectVectorStudioV2App.selectState("idle", "test state selection"));
40544080
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='idle']")).toHaveCount(2);
40554081

@@ -4131,7 +4157,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
41314157
version: 1
41324158
}, null, 2), "utf8");
41334159
await page.locator("#objectVectorStudioV2ImportJsonInput").setInputFiles(invalidPayloadPath);
4134-
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-invalid-animation\.json: root\.objects\[0\]\.states\[0\]\.id must be one of idle, move, active, inactive, damaged, destroyed, thrust\./);
4160+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-invalid-animation\.json: root\.objects\[0\]\.states\[0\]\.id must be one of idle, move, active, inactive, damaged, destroyed\./);
41354161

41364162
expect(pageErrors).toEqual([]);
41374163
} finally {

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,6 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
165165
<div id="objectVectorStudioV2ObjectPreviewFooter" class="object-vector-studio-v2__preview-footer">Object ID: none</div>
166166
<hr class="object-vector-studio-v2__separator">
167167
<div class="object-vector-studio-v2__animation-controls" aria-label="Animation controls">
168-
<select id="objectVectorStudioV2StateSelect" class="object-vector-studio-v2__state-select" aria-label="Frame state" disabled>
169-
<option value="">No state</option>
170-
</select>
171-
<button id="objectVectorStudioV2StateHelpButton" class="object-vector-studio-v2__state-help" type="button" disabled title="Disabled until a state is selected" aria-label="State help">?</button>
172168
<button id="objectVectorStudioV2PlayButton" type="button" disabled title="Disabled until a state frame is selected">Play</button>
173169
<button id="objectVectorStudioV2PauseButton" type="button" disabled title="Disabled until playback starts">Pause</button>
174170
<button id="objectVectorStudioV2StopButton" type="button" disabled title="Disabled until a state frame is selected">Stop</button>

0 commit comments

Comments
 (0)