Skip to content

Commit c70f0d9

Browse files
author
DavidQ
committed
Fix Workspace Manager V2 return context and hydrate launched tool state - PR_26127_004-workspace-manager-v2-return-context-and-tool-launch-fixes
1 parent 2af1c70 commit c70f0d9

18 files changed

Lines changed: 295 additions & 38 deletions
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# PR_26127_004-workspace-manager-v2-return-context-and-tool-launch-fixes
2+
3+
## Scope
4+
- Updated Workspace Manager V2, workspace-launched V2 tool handoff, and related Playwright coverage.
5+
- Did not modify deprecated `tools/workspace-v2`.
6+
- Did not modify sample JSON.
7+
- Did not add fallback behavior.
8+
9+
## Workspace Manager V2 Notes
10+
- Increased Workspace Context tile minimum height and removed internal tile scrolling.
11+
- Aligned Workspace Context tile content to the top.
12+
- Removed the self-launching Workspace Manager V2 tool tile from the Workspace Manager V2 tool launcher.
13+
- Preserved temporary UAT mode on Return to Workspace by passing a launch-only `workspaceMode=uat` marker and returning to `?workspace=uat`.
14+
15+
## Tool Launch Notes
16+
- Palette Manager V2 now reads the Workspace Manager V2 session manifest on workspace launch and imports `tools.palette-manager-v2.swatches` into the active user palette.
17+
- Preview Generator V2 now hydrates workspace launch context by setting Repo selected display, Target Source `games`, asset folder from `assetsPath`, the active game path, and Status log hydration events.
18+
- Preview Generator V2 loads Last Generated Image from `/games/<game>/assets/preview.svg` when that preview file is present.
19+
- Asset Manager V2 launch guard now includes a Return to Tools button for unavailable/direct-launch states.
20+
21+
## Validation
22+
- PASS: JS syntax checks for changed runtime and Playwright files.
23+
- PASS: `npx playwright test tests/playwright/tools/AssetManagerV2.spec.mjs tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list` completed with 17 passed tests.
24+
- PASS: `npm run test:workspace-v2` completed with 24 passed tests.
25+
- PASS: `git diff --check`.
26+
- PASS: Scope check found no diffs under deprecated `tools/workspace-v2` or sample schema paths.
27+
- SKIPPED: Full samples smoke test, per PR instructions. This PR is Workspace Manager V2 context/tool launch scoped.
28+
29+
## Manual Validation Notes
30+
- Open `tools/workspace-manager-v2/index.html?workspace=uat`, seed UAT, launch Asset Manager V2, then Return to Workspace; the Workspace Manager URL should preserve `workspace=uat`.
31+
- Load Asteroids in Workspace Manager V2; Tool tiles should omit Workspace Manager V2 and include Templates V2, Asset Manager V2, Palette Manager V2, and Preview Generator V2.
32+
- Launch Palette Manager V2 from Workspace Manager V2; active workspace palette swatches should display in the user palette.
33+
- Launch Preview Generator V2 from Workspace Manager V2; Repo selected, Target Source, asset folder, paths, Status log hydration, and existing preview image behavior should reflect the active workspace context.
34+
- Direct-launch Asset Manager V2 unavailable state should show Return to Tools.

tests/playwright/tools/AssetManagerV2.spec.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ test.describe("Asset Manager V2", () => {
143143
await expect(page.locator("#assetLaunchGuard")).toBeVisible();
144144
await expect(page.locator("#assetLaunchGuardMessage")).toHaveText("Asset Manager V2 is only available through Workspace Manager with a game workspace and palette.");
145145
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Launch context is missing.");
146+
await expect(page.locator("#assetLaunchGuardReturnToToolsButton")).toHaveText("Return to Tools");
146147
await expect(page.locator("body")).toHaveClass(/asset-manager-v2--launch-blocked/);
148+
await page.locator("#assetLaunchGuardReturnToToolsButton").click();
149+
await expect(page).toHaveURL(/\/tools\/index\.html$/);
147150
expect(pageErrors).toEqual([]);
148151
} finally {
149152
await coverageReporter.stop(page);
@@ -1243,7 +1246,8 @@ test.describe("Asset Manager V2", () => {
12431246
});
12441247

12451248
try {
1246-
await expect(page.locator("#workspaceToolTiles [data-workspace-tool-id]")).toHaveCount(5);
1249+
await expect(page.locator("#workspaceToolTiles [data-workspace-tool-id]")).toHaveCount(4);
1250+
await expect(page.locator('[data-workspace-tool-id="workspace-manager-v2"]')).toHaveCount(0);
12471251
await page.locator("#activeGameSelect").selectOption("Asteroids");
12481252
await expect(page.locator("#workspaceContextOutput")).toContainText('"gameRoot": "games/Asteroids/"');
12491253
await expect(page.locator("#workspaceContextOutput")).toContainText('"assetsPath": "games/Asteroids/assets"');

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ test.describe("Workspace Manager V2 bootstrap", () => {
175175
const centerControlLabels = await page.locator(".workspace-manager-v2__panel--center > .accordion-v2 > .accordion-v2__header span:first-child")
176176
.evaluateAll((labels) => labels.map((label) => label.textContent.trim()));
177177
expect(centerControlLabels.slice(0, 3)).toEqual(["Tools", "Workspace Context", "Workspace JSON"]);
178-
await expect(page.locator("#workspaceToolTiles [data-workspace-tool-id]")).toHaveCount(5);
178+
await expect(page.locator("#workspaceToolTiles [data-workspace-tool-id]")).toHaveCount(4);
179+
await expect(page.locator('[data-workspace-tool-id="workspace-manager-v2"]')).toHaveCount(0);
179180
expect(await page.locator("#workspaceToolTiles [data-workspace-tool-id]").evaluateAll((tiles) => tiles.every((tile) => tile.disabled))).toBe(true);
180181
const compactCenterLayout = await page.evaluate(() => {
181182
const getRect = (selector) => {
@@ -214,7 +215,16 @@ test.describe("Workspace Manager V2 bootstrap", () => {
214215
await expect(page.locator("#launchContextSummary")).toHaveText("Schema-valid manifest is ready for Asteroids.");
215216
const workspaceContextTileHeights = await page.locator(".workspace-manager-v2__summary-card").evaluateAll((tiles) => tiles.map((tile) => Math.round(tile.getBoundingClientRect().height)));
216217
expect(new Set(workspaceContextTileHeights).size).toBe(1);
217-
expect(workspaceContextTileHeights.every((height) => height >= 118)).toBe(true);
218+
expect(workspaceContextTileHeights.every((height) => height >= 148)).toBe(true);
219+
const workspaceContextTileScrollState = await page.locator(".workspace-manager-v2__summary-card").evaluateAll((tiles) => tiles.map((tile) => ({
220+
alignItems: getComputedStyle(tile).alignItems,
221+
justifyContent: getComputedStyle(tile).justifyContent,
222+
overflowY: getComputedStyle(tile).overflowY,
223+
scrolls: tile.scrollHeight > tile.clientHeight
224+
})));
225+
expect(workspaceContextTileScrollState.every((tile) => tile.justifyContent === "flex-start")).toBe(true);
226+
expect(workspaceContextTileScrollState.every((tile) => tile.overflowY === "visible")).toBe(true);
227+
expect(workspaceContextTileScrollState.every((tile) => tile.scrolls === false)).toBe(true);
218228
await expect(page.locator("#workspaceContextOutput")).toContainText('"gameRoot": "games/Asteroids/"');
219229
await expect(page.locator("#workspaceContextOutput")).toContainText('"assetsPath": "games/Asteroids/assets"');
220230
await expect(page.locator("#workspaceContextOutput")).toContainText('"source": "manifest"');
@@ -232,7 +242,6 @@ test.describe("Workspace Manager V2 bootstrap", () => {
232242
await expect(page.locator("#exportManifestButton")).toBeEnabled();
233243
const templateTile = page.locator('[data-workspace-tool-id="templates-v2"]');
234244
const assetTile = page.locator('[data-workspace-tool-id="asset-manager-v2"]');
235-
const workspaceTile = page.locator('[data-workspace-tool-id="workspace-manager-v2"]');
236245
const paletteTile = page.locator('[data-workspace-tool-id="palette-manager-v2"]');
237246
const previewTile = page.locator('[data-workspace-tool-id="preview-generator-v2"]');
238247
await expect(templateTile).toBeEnabled();
@@ -242,8 +251,6 @@ test.describe("Workspace Manager V2 bootstrap", () => {
242251
await expect(assetTile).toContainText("Asset Manager V2");
243252
await expect(assetTile).toContainText("Ready to launch");
244253
await expect(assetTile).toContainText("13 managed assets");
245-
await expect(workspaceTile).toContainText("Workspace Manager V2");
246-
await expect(workspaceTile).toContainText("Active workspace surface");
247254
await expect(paletteTile).toContainText("11 palette swatches");
248255
await expect(previewTile).toContainText("Schema-valid manifest");
249256
const tileLayout = await page.locator("#workspaceToolTiles [data-workspace-tool-id]").evaluateAll((tiles) => tiles.map((tile) => ({
@@ -254,7 +261,6 @@ test.describe("Workspace Manager V2 bootstrap", () => {
254261
{ height: 118, width: 180 },
255262
{ height: 118, width: 180 },
256263
{ height: 118, width: 180 },
257-
{ height: 118, width: 180 },
258264
{ height: 118, width: 180 }
259265
]);
260266
await expect(page.locator("#statusLog")).toHaveValue(/OK Loaded Asteroids from \/games\/Asteroids\/game\.manifest\.json with 11 active palette colors and 13 managed assets\./);
@@ -400,21 +406,37 @@ test.describe("Workspace Manager V2 bootstrap", () => {
400406
await expect(page.locator('[data-launch-mode-nav="workspace"]').getByRole("button")).toHaveText(["Return to Workspace"]);
401407
await page.locator("#returnToWorkspaceButton").click();
402408
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
403-
await page.locator('[data-workspace-tool-id="workspace-manager-v2"]').click();
404-
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
405-
await expect(page.locator("#activeGameSelect")).toHaveValue("Asteroids");
406409
await page.locator('[data-workspace-tool-id="palette-manager-v2"]').click();
407410
await expect(page).toHaveURL(/palette-manager-v2\/index\.html.*launch=workspace/);
408411
await expect(page.locator('[data-launch-mode-nav="tool"]')).toBeHidden();
409412
await expect(page.locator('[data-launch-mode-nav="workspace"]')).toBeVisible();
410413
await expect(page.locator('[data-launch-mode-nav="workspace"] button')).toHaveText(["Return to Workspace"]);
414+
await expect(page.locator("#userPaletteCount")).toHaveText("11 user swatches");
415+
await expect(page.locator('#userSwatchList [aria-label="Edit Space Black"]')).toBeVisible();
416+
await expect(page.locator('#userSwatchList [aria-label="Edit Space Black"]')).toHaveAttribute("title", /Name: Space Black/);
417+
await expect(page.locator("#paletteStatus")).toHaveText("Loaded active workspace palette Asteroids Palette.");
411418
await page.locator("#returnToWorkspaceButton").click();
412419
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
420+
await page.route("**/games/Asteroids/assets/preview.svg", async (route) => {
421+
await route.fulfill({
422+
body: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><rect width="8" height="8"/></svg>',
423+
contentType: "image/svg+xml",
424+
status: 200
425+
});
426+
});
413427
await page.locator('[data-workspace-tool-id="preview-generator-v2"]').click();
414428
await expect(page).toHaveURL(/preview-generator-v2\/index\.html.*launch=workspace/);
415429
await expect(page.locator('[data-launch-mode-nav="tool"]')).toBeHidden();
416430
await expect(page.locator('[data-launch-mode-nav="workspace"]')).toBeVisible();
417431
await expect(page.locator('[data-launch-mode-nav="workspace"] button')).toHaveText(["Return to Workspace"]);
432+
await expect(page.locator("#repoSelectedValue")).toHaveText("Asteroids workspace (games/Asteroids)");
433+
await expect(page.locator("#targetTypeGames")).toBeChecked();
434+
await expect(page.locator("#assetFolder")).toHaveValue("assets");
435+
await expect(page.locator("#sampleList")).toHaveValue("Asteroids");
436+
await expect(page.locator("#lastGeneratedImagePreview")).toBeVisible();
437+
await expect(page.locator("#lastGeneratedImageMeta")).toHaveText("Last generated: Asteroids preview.svg");
438+
await expect(page.locator("#log")).toContainText("OK Workspace launch context hydrated for Asteroids.");
439+
await expect(page.locator("#log")).toContainText("OK Loaded existing preview image from /games/Asteroids/assets/preview.svg.");
418440
await page.locator("#returnToWorkspaceButton").click();
419441
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
420442
expect(pageErrors).toEqual([]);
@@ -541,7 +563,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
541563
await expect(page.locator("#activeAssetRegistrySummary")).toHaveText("Schema-ready Asset Manager V2 manifest payload contains 0 managed assets.");
542564
await expect(page.locator('[data-workspace-tool-id="templates-v2"]')).toContainText("Canonical V2 template");
543565
await expect(page.locator('[data-workspace-tool-id="asset-manager-v2"]')).toContainText("0 managed assets");
544-
await expect(page.locator('[data-workspace-tool-id="workspace-manager-v2"]')).toContainText("Active workspace surface");
566+
await expect(page.locator('[data-workspace-tool-id="workspace-manager-v2"]')).toHaveCount(0);
545567
await expect(page.locator('[data-workspace-tool-id="palette-manager-v2"]')).toContainText("3 palette swatches");
546568
await expect(page.locator('[data-workspace-tool-id="preview-generator-v2"]')).toContainText("Schema-valid manifest");
547569
await expect(page.locator("#workspaceContextOutput")).toContainText('"id": "workspace-manager-v2-UAT-Asteroids"');
@@ -556,7 +578,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
556578
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 0 validated assets from tools\.asset-manager-v2\.assets/);
557579
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 3 palette colors from active palette context/);
558580
await page.locator("#returnToWorkspaceButton").click();
559-
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
581+
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html.*hostContextId=workspace-manager-v2-/);
582+
await expect(page).toHaveURL(/workspace=uat/);
583+
await expect(page.locator("#seedUatManifestButton")).toBeVisible();
560584
await expect(page.locator("#activeAssetRegistrySummary")).toHaveText("Schema-ready Asset Manager V2 manifest payload contains 0 managed assets.");
561585
expect(pageErrors).toEqual([]);
562586
} finally {
@@ -577,6 +601,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
577601
await expect(page.locator("#assetLaunchGuard")).toBeVisible();
578602
await expect(page.locator("#assetLaunchGuardMessage")).toHaveText("Asset Manager V2 is only available through Workspace Manager with a game workspace and palette.");
579603
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Temporary workspace query launches are no longer supported; launch through Workspace Manager V2.");
604+
await expect(page.locator("#assetLaunchGuardReturnToToolsButton")).toHaveText("Return to Tools");
580605
await expect(page.locator("body")).toHaveClass(/asset-manager-v2--launch-blocked/);
581606
expect(pageErrors).toEqual([]);
582607
} finally {

tools/asset-manager-v2/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<h1 id="assetLaunchGuardTitle">Asset Manager V2 unavailable</h1>
1717
<p id="assetLaunchGuardMessage">Asset Manager V2 is only available through Workspace Manager with a game workspace and palette.</p>
1818
<p id="assetLaunchGuardReason"></p>
19+
<button id="assetLaunchGuardReturnToToolsButton" class="asset-manager-v2__launch-guard-return" type="button">Return to Tools</button>
1920
</div>
2021
</div>
2122

tools/asset-manager-v2/js/AssetManagerV2App.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ export class AssetManagerV2App {
137137
if (reasonNode) {
138138
reasonNode.textContent = reason ? `Reason: ${reason}` : "";
139139
}
140+
const returnToToolsButton = this.launchGuard.querySelector("#assetLaunchGuardReturnToToolsButton");
141+
if (returnToToolsButton && !returnToToolsButton.dataset.assetManagerV2Bound) {
142+
returnToToolsButton.dataset.assetManagerV2Bound = "true";
143+
returnToToolsButton.addEventListener("click", () => {
144+
this.window.location.href = new URL("../index.html", this.window.location.href).href;
145+
});
146+
}
140147
this.launchGuard.hidden = false;
141148
this.window.document.body.classList.add("asset-manager-v2--launch-blocked");
142149
}

tools/asset-manager-v2/js/services/WorkspaceBridge.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@ export class WorkspaceBridge {
5050

5151
workspaceManagerUrl() {
5252
const url = new URL("../workspace-manager-v2/index.html", this.window.location.href);
53+
const params = new URLSearchParams(this.window.location.search);
5354
const hostContextId = this.hostContextId();
5455
if (hostContextId) {
5556
url.searchParams.set("hostContextId", hostContextId);
5657
}
58+
if (params.get("workspaceMode")?.toLowerCase() === "uat") {
59+
url.searchParams.set("workspace", "uat");
60+
}
5761
return url.href;
5862
}
5963

tools/asset-manager-v2/styles/assetManager.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ textarea:disabled {
118118
line-height: 1.45;
119119
}
120120

121+
.asset-manager-v2__launch-guard-return {
122+
margin-top: 16px;
123+
}
124+
121125
body.asset-manager-v2--launch-blocked > :not(.asset-manager-v2__launch-guard) {
122126
pointer-events: none;
123127
}

0 commit comments

Comments
 (0)