Skip to content

Commit f0d860b

Browse files
author
DavidQ
committed
Fix fullscreen-only bezel behavior and Object Vector V2 canvas drag/frame controls - PR_26133_109-object-vector-fullscreen-pan-and-bezel-fix
1 parent b0825d9 commit f0d860b

6 files changed

Lines changed: 300 additions & 30 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# PR_26133_109-object-vector-fullscreen-pan-and-bezel-fix Report
2+
3+
## Summary
4+
- Read `docs/dev/PROJECT_INSTRUCTIONS.md` before changes.
5+
- Used the PR_26133_108 report/current baseline as the prior reference; `tmp/PR_26133_108-shared-vector-collision-and-bezel-fix_delta.zip` was not present in this workspace.
6+
- Updated `fullscreenBezel` so the bezel asset remains attached/resolved but is visible only while fullscreen is active; normal page-window mode now reports `visible: false` and keeps the canvas in normal layout.
7+
- Added Object Vector Studio V2 empty-canvas object drag: after shape selection is cleared, the render surface uses grab/grabbing cursors and blank-surface drag moves all selected-object shape transforms through the existing validated preview edit/history path.
8+
- Tightened Object Vector Studio V2 fullscreen center-panel layout so frame controls and the frame timeline remain visible and usable in fullscreen.
9+
10+
## Changed Files
11+
- `src/engine/runtime/fullscreenBezel.js`
12+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`
13+
- `tools/object-vector-studio-v2/styles/toolStarter.css`
14+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
15+
16+
## Validation
17+
- PASS: `node --check src/engine/runtime/fullscreenBezel.js`
18+
- PASS: `node --check tools/object-vector-studio-v2/js/ToolStarterApp.js`
19+
- PASS: `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
20+
- PASS: `git diff --check` (CRLF warnings only for existing line-ending behavior in the Playwright spec/CSS)
21+
- PASS: `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "supports Object Vector Studio V2 animation states and frame timeline foundation|drags selected Object Vector Studio V2 shapes from preview selection bounds|fits the game canvas inside the fullscreen play area|loads Object Vector Studio V2 runtime assets into Asteroids gameplay rendering"` (4 passed)
22+
- PASS: `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "creates Object Vector Studio V2 shapes with canvas drawing and snap modes|saves empty Text to Speech V2 arrays through workspace return and manifest write-back"` (2 passed after the first full-suite run exposed transient failures in those existing tests)
23+
- PASS: `npm run test:workspace-v2` (final run: 56 passed)
24+
25+
## Skipped
26+
- Full samples smoke test skipped by request.

games/Asteroids/game.manifest.json

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,6 @@
166166
"role": "ui",
167167
"source": "manifest"
168168
},
169-
"assets.image.background.deluxe": {
170-
"path": "assets/images/deluxe.png",
171-
"type": "image",
172-
"kind": "png",
173-
"role": "background",
174-
"source": "manifest",
175-
"stretchOverride": {
176-
"uniformEdgeStretchPx": 0
177-
}
178-
},
179169
"assets.image.bezel.bezel": {
180170
"path": "assets/images/bezel.png",
181171
"type": "image",
@@ -280,8 +270,12 @@
280270
"y": 24
281271
},
282272
{
283-
"x": 0,
284-
"y": 12
273+
"x": 7,
274+
"y": 16
275+
},
276+
{
277+
"x": -7,
278+
"y": 16
285279
},
286280
{
287281
"x": -21,
@@ -298,7 +292,8 @@
298292
"pointRounding": [
299293
false,
300294
false,
301-
true,
295+
false,
296+
false,
302297
false
303298
],
304299
"startPointStyle": "square",

src/engine/runtime/fullscreenBezel.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,10 +1155,11 @@ export default class fullscreenBezel {
11551155
}
11561156

11571157
const shouldShow = this.ready && !this.missing;
1158-
this.element.style.display = toDisplayValue(shouldShow);
1159-
this.element.style.visibility = toVisibilityValue(shouldShow);
1160-
this.element.style.opacity = toOpacityValue(shouldShow);
1161-
if (shouldShow && fullscreenActive) {
1158+
const visible = shouldShow && fullscreenActive;
1159+
this.element.style.display = toDisplayValue(visible);
1160+
this.element.style.visibility = toVisibilityValue(visible);
1161+
this.element.style.opacity = toOpacityValue(visible);
1162+
if (visible) {
11621163
const fitted = this.applyCanvasWindowFitLayout();
11631164
if (!fitted) {
11641165
this.applyCanvasFullscreenFitLayout() || this.applyCanvasFallbackLayout();
@@ -1169,8 +1170,8 @@ export default class fullscreenBezel {
11691170
this.applyCanvasNormalLayout();
11701171
}
11711172
return {
1172-
visible: shouldShow,
1173-
reason: shouldShow ? "visible" : (fullscreenActive ? "asset-unavailable" : "fullscreen-inactive"),
1173+
visible,
1174+
reason: visible ? "visible" : (fullscreenActive ? "asset-unavailable" : "fullscreen-inactive"),
11741175
path: this.path,
11751176
hostTagName: this.host?.tagName || "",
11761177
canvasLayoutMode: this.canvasLayoutMode

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6019,10 +6019,51 @@ test.describe("Workspace Manager V2 bootstrap", () => {
60196019
await expect(page.locator("[aria-label='Frame controls'] button")).toHaveText(["Left", "Frame Earlier", "Duplicate Frame", "Frame Later", "Right", "Delete Frame"]);
60206020
await expect(page.locator("#objectVectorStudioV2DeleteFrameButton")).toBeDisabled();
60216021

6022+
await page.setViewportSize({ width: 1440, height: 900 });
6023+
await page.evaluate(() => {
6024+
document.querySelector(".is-collapsible").open = false;
6025+
window.__objectVectorStudioV2App.shell.applyFullscreenState(true);
6026+
window.__objectVectorStudioV2App.shell.updateSummary();
6027+
});
6028+
await expect(page.locator("body")).toHaveClass(/tools-platform-fullscreen-active/);
6029+
const fullscreenFrameControls = await page.evaluate(() => {
6030+
const center = document.querySelector(".tool-starter__panel--center");
6031+
const frameControls = document.querySelector('[aria-label="Frame controls"]');
6032+
const timeline = document.querySelector("#objectVectorStudioV2FrameTimeline");
6033+
const duplicateButton = document.querySelector("#objectVectorStudioV2DuplicateFrameButton");
6034+
const centerRect = center.getBoundingClientRect();
6035+
const controlsRect = frameControls.getBoundingClientRect();
6036+
const timelineRect = timeline.getBoundingClientRect();
6037+
const duplicateRect = duplicateButton.getBoundingClientRect();
6038+
return {
6039+
centerOverflowY: getComputedStyle(center).overflowY,
6040+
controlsVisible: controlsRect.width > 0 && controlsRect.height > 0,
6041+
controlsWithinViewport: controlsRect.top >= 0 && controlsRect.bottom <= window.innerHeight + 1,
6042+
duplicateButtonEnabled: !duplicateButton.disabled,
6043+
duplicateButtonVisible: duplicateRect.width > 0 && duplicateRect.height > 0,
6044+
timelineVisible: timelineRect.width > 0 && timelineRect.height > 0,
6045+
timelineWithinCenter: timelineRect.top >= centerRect.top - 1 && timelineRect.bottom <= centerRect.bottom + 1
6046+
};
6047+
});
6048+
expect(fullscreenFrameControls).toEqual({
6049+
centerOverflowY: "auto",
6050+
controlsVisible: true,
6051+
controlsWithinViewport: true,
6052+
duplicateButtonEnabled: true,
6053+
duplicateButtonVisible: true,
6054+
timelineVisible: true,
6055+
timelineWithinCenter: true
6056+
});
60226057
await page.locator("#objectVectorStudioV2DuplicateFrameButton").click();
60236058
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='idle']")).toHaveCount(2);
60246059
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-2']")).toHaveAttribute("aria-pressed", "true");
60256060
await expect(page.locator("#objectVectorStudioV2DeleteFrameButton")).toBeEnabled();
6061+
await page.evaluate(() => {
6062+
window.__objectVectorStudioV2App.shell.applyFullscreenState(false);
6063+
document.querySelector(".is-collapsible").open = true;
6064+
window.__objectVectorStudioV2App.shell.updateSummary();
6065+
});
6066+
await expect(page.locator("body")).not.toHaveClass(/tools-platform-fullscreen-active/);
60266067
await page.locator("#objectVectorStudioV2DeleteFrameButton").click();
60276068
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id]")).toHaveCount(1);
60286069
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='frame-1']")).toHaveAttribute("aria-pressed", "true");
@@ -6713,6 +6754,35 @@ test.describe("Workspace Manager V2 bootstrap", () => {
67136754
expect(outsideDragResult.selectedCount).toBe(0);
67146755
expect(outsideDragResult.selectedShapeIndex).toBe(-1);
67156756

6757+
const emptyCanvasCursor = await page.locator("#objectVectorStudioV2RenderSurface").evaluate((surface) => ({
6758+
classList: Array.from(surface.classList),
6759+
cursor: getComputedStyle(surface).cursor
6760+
}));
6761+
expect(emptyCanvasCursor.classList).toContain("is-empty-canvas-object-drag-ready");
6762+
expect(emptyCanvasCursor.cursor).toBe("grab");
6763+
await mouseDragObjectVectorLogicalPoints(page, { x: -80, y: 60 }, { x: -60, y: 70 });
6764+
await expect(page.locator("#statusLog")).toHaveValue(/OK Dragged object Preview Drag Selection by 20, 10\./);
6765+
const objectDragResult = await page.evaluate(() => {
6766+
const app = window.__objectVectorStudioV2App;
6767+
const frame = app.activeFrame();
6768+
return {
6769+
cursorReady: app.elements.renderSurface.classList.contains("is-empty-canvas-object-drag-ready"),
6770+
selectedCount: app.selectedShapeIndexes.size,
6771+
selectedShapeIndex: app.selectedShapeIndex,
6772+
transforms: app.selectedObject().shapes.map((shape, shapeIndex) => {
6773+
const transform = app.effectiveShapeForFrame(shape, frame, shapeIndex).transform;
6774+
return { x: transform.x, y: transform.y };
6775+
})
6776+
};
6777+
});
6778+
expect(objectDragResult.cursorReady).toBe(true);
6779+
expect(objectDragResult.selectedCount).toBe(0);
6780+
expect(objectDragResult.selectedShapeIndex).toBe(-1);
6781+
expect(objectDragResult.transforms).toEqual(transformsBeforeOutsideDrag.map((transform) => ({
6782+
x: Number((transform.x + 20).toFixed(3)),
6783+
y: Number((transform.y + 10).toFixed(3))
6784+
})));
6785+
67166786
await shapePanel.locator("[data-object-tile-shape-index='1']").click();
67176787
const liveDragStart = await page.evaluate(() => {
67186788
const app = window.__objectVectorStudioV2App;
@@ -7196,7 +7266,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
71967266
const engine = window.__asteroidsNewEngine;
71977267
const bezelState = engine?.fullscreenBezelLayer?.getState?.();
71987268
return !document.fullscreenElement
7199-
&& bezelState?.visible === true
7269+
&& bezelState?.visible === false
72007270
&& bezelState?.canvasLayoutMode === "normal";
72017271
});
72027272
const normalLayout = await page.evaluate(() => {
@@ -7226,12 +7296,12 @@ test.describe("Workspace Manager V2 bootstrap", () => {
72267296
expect(normalLayout.bezelState).toMatchObject({
72277297
canvasLayoutMode: "normal",
72287298
path: "/games/Asteroids/assets/images/bezel.png",
7229-
visible: true
7299+
visible: false
72307300
});
7231-
expect(normalLayout.bezelDisplay).toBe("block");
7301+
expect(normalLayout.bezelDisplay).toBe("none");
72327302
expect(normalLayout.bezelInHost).toBe(true);
7233-
expect(normalLayout.bezelRectWidth).toBeGreaterThan(900);
7234-
expect(normalLayout.bezelRectHeight).toBeGreaterThan(600);
7303+
expect(normalLayout.bezelRectWidth).toBe(0);
7304+
expect(normalLayout.bezelRectHeight).toBe(0);
72357305
expect(normalLayout.hostKind).toBe("canvas");
72367306
expect(normalLayout.canvasInlineHeight).toBe("");
72377307
expect(normalLayout.canvasInlinePosition).toBe("");
@@ -7359,7 +7429,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
73597429
const engine = window.__asteroidsNewEngine;
73607430
return engine?.backgroundImageLayer?.getState?.().manifestResolved === true
73617431
&& engine?.fullscreenBezelLayer?.getState?.().manifestResolved === true
7362-
&& engine?.fullscreenBezelLayer?.getState?.().visible === true;
7432+
&& engine?.fullscreenBezelLayer?.getState?.().canvasLayoutMode === "normal";
73637433
});
73647434
const chromeAssetState = await page.evaluate(() => {
73657435
const engine = window.__asteroidsNewEngine;
@@ -7375,7 +7445,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
73757445
expect(chromeAssetState.bezel).toMatchObject({
73767446
canvasLayoutMode: "normal",
73777447
path: "/games/Asteroids/assets/images/bezel.png",
7378-
visible: true,
7448+
visible: false,
73797449
uniformEdgeStretchPx: 10
73807450
});
73817451
expect(chromeAssetState.bezel.stretchConfigPath).toContain("games/Asteroids/game.manifest.json");

0 commit comments

Comments
 (0)