Skip to content

Commit b7292c7

Browse files
committed
Add guest view and polish Toolbox tile layout - PR_26155_022-024-toolbox-guest-and-tile-polish
1 parent 70cb87b commit b7292c7

10 files changed

Lines changed: 211 additions & 33 deletions

File tree

assets/theme-v2/css/panels.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,23 @@
8282
object-fit: cover
8383
}
8484

85+
.card-media-link {
86+
display: block;
87+
width: 100%;
88+
height: 100%;
89+
overflow: hidden;
90+
border-radius: inherit
91+
}
92+
93+
.card-media-link img {
94+
transition: transform var(--transition-duration-fast) ease
95+
}
96+
97+
.card-media-link:hover img,
98+
.card-media-link:focus-visible img {
99+
transform: scale(1.04) rotate(2deg)
100+
}
101+
85102
.card img {
86103
width: 100%;
87104
display: block

assets/theme-v2/partials/header-nav.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
</div>
2424
</div>
2525
<div class="nav-item nav-popout-item" data-toolbox-menu-group>
26-
<a data-toolbox-menu-group-label data-route="toolbox" href="toolbox/index.html" aria-haspopup="true">Assets &#9656;</a>
27-
<div class="sub-menu sub-menu--nested" data-toolbox-submenu aria-label="Assets tools">
26+
<a data-toolbox-menu-group-label data-route="toolbox" href="toolbox/index.html" aria-haspopup="true">Content &#9656;</a>
27+
<div class="sub-menu sub-menu--nested" data-toolbox-submenu aria-label="Content tools">
2828
<a data-nav-link data-toolbox-menu-item data-route="tool-assets" href="toolbox/assets/index.html">Assets</a>
2929
<a data-nav-link data-toolbox-menu-item data-route="colors" href="toolbox/colors/index.html">Colors</a>
3030
<a data-nav-link data-toolbox-menu-item data-route="fonts" href="toolbox/fonts/index.html">Fonts</a>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Guest Role View
2+
3+
Stack item: `PR_26155_022-guest-role-view`
4+
5+
## Summary
6+
7+
- Added Guest as the default public Toolbox role.
8+
- Supported URL roles are `?role=guest`, `?role=user`, and `?role=admin`.
9+
- Guest banner text is `GUEST VIEW • Preview only • Sign in to create`.
10+
- Creator banner text is `CREATOR VIEW • Project tools enabled • Switch to Admin View`.
11+
- Admin banner text is `ADMIN VIEW • Planned tools visible • Switch to Creator View`.
12+
- Banner links continue to switch Guest -> Creator, Creator -> Admin, and Admin -> Creator.
13+
14+
## Behavior Notes
15+
16+
- Guest and Creator views show the same current public preview-safe tool set because the Toolbox remains a static wireframe surface with no login, database, save, or create runtime behavior.
17+
- Admin view continues to reveal planned, hidden, and admin-only tool tiles.
18+
- No login, auth, DB, persistence, or real save/create behavior was added.
19+
20+
## Validation Notes
21+
22+
- Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` before changes.
23+
- Ran `node scripts/validate-active-tools-surface.mjs`: passed.
24+
- Ran `node scripts/validate-tool-registry.mjs`: passed.
25+
- Ran `npm run test:workspace-v2`: passed. This is a legacy command name for the Project Workspace validation lane.
26+
27+
## Manual Test Notes
28+
29+
- `/toolbox/index.html` defaults to Guest view.
30+
- `/toolbox/index.html?role=guest` shows the Guest banner and no admin-only tiles.
31+
- `/toolbox/index.html?role=user` shows the Creator banner and no admin-only tiles.
32+
- `/toolbox/index.html?role=admin` shows the Admin banner and planned/admin tiles.
33+
- No console errors were reported by the targeted Playwright coverage.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Toolbox Tile Layout Polish
2+
3+
Stack item: `PR_26155_023-toolbox-tile-layout-polish`
4+
5+
## Summary
6+
7+
- Moved the tool category/content cluster under the Open Tool button on Toolbox tiles.
8+
- Replaced duplicate visible category text with the existing content cluster text.
9+
- Removed the separate card-level `.kicker` category row from generated tool cards.
10+
- Made preview images clickable through the same route as the Open Tool button.
11+
- Added reusable Theme V2 `.card-media-link` styling for preview-image hover.
12+
13+
## Theme V2 Gap Finding
14+
15+
- Existing Theme V2 card media styling supported static media but did not include a reusable linked preview-media hover affordance.
16+
- Added the reusable pattern to `assets/theme-v2/css/panels.css`.
17+
- No page-local CSS, tool-local CSS, inline styles, or style blocks were added.
18+
19+
## Validation Notes
20+
21+
- Targeted Playwright checks verify that card preview links match Open Tool routes.
22+
- Targeted Playwright checks verify that preview hover changes the image transform.
23+
- Targeted Playwright checks verify that generated Toolbox cards no longer contain duplicate `.kicker` category rows.
24+
- `npm run test:workspace-v2` passed.
25+
26+
## Manual Test Notes
27+
28+
- Toolbox tiles keep the existing card structure.
29+
- The preview image for the Assets tile routes to `/toolbox/assets/index.html`, matching Open Tool.
30+
- Category display appears once per tile through the content cluster under the Open Tool button.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Toolbox Tool Count And Group Name
2+
3+
Stack item: `PR_26155_024-toolbox-tool-count-and-group-name`
4+
5+
## Summary
6+
7+
- Added `Tool Count: <visible>/<total>` to the right of the Toolbox view controls.
8+
- Tool count reflects the active role filter:
9+
- Guest: `30/42`
10+
- Creator: `30/42`
11+
- Admin: `42/42`
12+
- Renamed the Toolbox group from `Assets` to `Content`.
13+
- Kept the tool label `Assets`.
14+
15+
## Validation Notes
16+
17+
- Targeted static validation confirms the Tool Count slot exists.
18+
- Targeted Playwright validation confirms Guest, Creator, and Admin counts.
19+
- Targeted Playwright validation confirms the grouped view uses `Content` and no longer exposes an `Assets` group heading.
20+
- Targeted Playwright validation confirms Arcade is absent from the Toolbox page.
21+
- Targeted Playwright validation confirms no forbidden tool label suffix appears in active Toolbox tile labels.
22+
23+
## Manual Test Notes
24+
25+
- `/toolbox/index.html` shows Order A-Z, Group, Progress, Build Path, and Tool Count.
26+
- Group view displays `Content` as the group heading.
27+
- The `Assets` tool remains present as a tile inside the Content group.

scripts/validate-active-tools-surface.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,21 @@ async function main() {
119119
if (!/<button[^>]+data-tools-view="build-path"[^>]*>\s*Build Path\s*<\/button>/i.test(toolboxIndex)) {
120120
issues.push("Toolbox index is missing the Build Path view mode control.");
121121
}
122+
if (!/data-tools-count/.test(toolboxIndex) || !/Tool Count:/.test(toolboxIndex + "\n" + toolsAccordions)) {
123+
issues.push("Toolbox index must expose a visible Tool Count control tied to current role filtering.");
124+
}
125+
if (!/GUEST VIEW/.test(toolboxIndex + "\n" + toolsAccordions) || !/"guest"/.test(toolsAccordions)) {
126+
issues.push("Toolbox role simulation must support an explicit Guest view.");
127+
}
128+
if (!/"group": "Content"/.test(toolsAccordions) || /"group": "Assets"/.test(toolsAccordions)) {
129+
issues.push("Toolbox Content group must replace the old Assets group label while keeping the Assets tool label.");
130+
}
131+
if (!/card-media-link/.test(toolsAccordions)) {
132+
issues.push("Toolbox preview images must be linked through the shared card media link pattern.");
133+
}
134+
if (/group\.className = "kicker"/.test(toolsAccordions)) {
135+
issues.push("Toolbox tool cards must not render a duplicate category kicker.");
136+
}
122137
if (/data-tools-view="(?:progress|build-path)"[^>]*aria-disabled="true"/.test(toolboxIndex)) {
123138
issues.push("Progress and Build Path view mode controls must render as normal reviewable controls, not disabled controls.");
124139
}

tests/playwright/tools/RootToolsFutureState.spec.mjs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ test("root tools surface links current tool pages without old_* routes", async (
4949
await expect(page.getByRole("button", { name: "Build Path" })).toBeVisible();
5050
await expect(page.getByRole("button", { name: "Progress" })).not.toHaveAttribute("aria-disabled", "true");
5151
await expect(page.getByRole("button", { name: "Build Path" })).not.toHaveAttribute("aria-disabled", "true");
52-
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("CREATOR VIEW • Planned tools hidden • Switch to Admin View");
53-
await expect(page.locator("[data-toolbox-role-banner]")).toHaveAttribute("href", /role=admin/);
52+
await expect(page.locator("[data-tools-count]")).toHaveText("Tool Count: 30/42");
53+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("GUEST VIEW • Preview only • Sign in to create");
54+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveAttribute("href", /role=user/);
5455
await expect(page.getByText("Progress Wireframe")).toHaveCount(0);
5556
await expect(page.getByText("Build Path Wireframe")).toHaveCount(0);
5657
await expect(page.locator("[data-toolbox-wireframe]")).toHaveCount(0);
58+
await expect(page.locator("style, [style], script:not([src])")).toHaveCount(0);
5759
const languagesCard = page.locator("main .control-card").filter({
5860
has: page.locator("h3", { hasText: "Languages" })
5961
});
@@ -93,6 +95,32 @@ test("root tools surface links current tool pages without old_* routes", async (
9395
"Cloud",
9496
"Custom Extensions"
9597
]));
98+
await expect(page.locator("main .control-card .kicker")).toHaveCount(0);
99+
const assetsCard = page.locator("main .control-card").filter({
100+
has: page.locator("h3", { hasText: /^Assets$/ })
101+
}).first();
102+
await expect(assetsCard.locator(".card-body > .content-cluster")).toContainText("Content");
103+
await expect(assetsCard.locator(".card-body > .content-cluster")).toHaveCount(1);
104+
const assetsBodyOrder = await assetsCard.locator(".card-body").evaluate((body) => (
105+
Array.from(body.children).map((child) => child.classList.contains("content-cluster") ? "content-cluster" : child.tagName.toLowerCase())
106+
));
107+
expect(assetsBodyOrder).toEqual(["h3", "p", "a", "content-cluster"]);
108+
await expect(assetsCard.locator(".card-media-link")).toHaveAttribute("href", "../toolbox/assets/index.html");
109+
await expect(assetsCard.locator("a.btn")).toHaveAttribute("href", "../toolbox/assets/index.html");
110+
const previewImage = assetsCard.locator(".card-media-link img");
111+
const transformBeforeHover = await previewImage.evaluate((image) => getComputedStyle(image).transform);
112+
await assetsCard.locator(".card-media-link").hover();
113+
await page.waitForTimeout(100);
114+
const transformAfterHover = await previewImage.evaluate((image) => getComputedStyle(image).transform);
115+
expect(transformAfterHover).not.toBe(transformBeforeHover);
116+
expect(transformAfterHover).not.toBe("none");
117+
await assetsCard.locator(".card-media-link").click();
118+
await page.waitForURL(/\/toolbox\/assets\/index\.html$/);
119+
await page.goBack({ waitUntil: "networkidle" });
120+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("GUEST VIEW • Preview only • Sign in to create");
121+
await page.getByRole("button", { name: "Group" }).click();
122+
await expect(page.locator("[data-tools-accordion='Content']")).toBeVisible();
123+
await expect(page.locator("[data-tools-accordion='Assets']")).toHaveCount(0);
96124
await page.getByRole("button", { name: "Progress" }).click();
97125
await expect(page.locator("[data-tools-accordion-list] [data-toolbox-readiness='locked']").first()).toBeVisible();
98126
await expect(page.locator("[data-tools-accordion-list] [data-toolbox-readiness='ready']").first()).toBeVisible();
@@ -124,11 +152,16 @@ test("root tools surface links current tool pages without old_* routes", async (
124152
const hrefs = await page.locator("a[href]").evaluateAll((links) => links.map((link) => link.getAttribute("href")));
125153
expect(hrefs.filter((href) => href && /(^|\/|\.\.\/)tools\/old_/.test(href))).toEqual([]);
126154
expect(failedRequests.filter((request) => request.includes("/toolbox/old_"))).toEqual([]);
127-
expect(pageErrors).toEqual([]);
128155

156+
await page.locator("[data-toolbox-role-banner]").click();
157+
await page.waitForURL(/role=user/);
158+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("CREATOR VIEW • Project tools enabled • Switch to Admin View");
159+
await expect(page.locator("[data-tools-count]")).toHaveText("Tool Count: 30/42");
160+
await expect(page.locator("main").getByText("Users", { exact: true })).toHaveCount(0);
129161
await page.locator("[data-toolbox-role-banner]").click();
130162
await page.waitForURL(/role=admin/);
131163
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("ADMIN VIEW • Planned tools visible • Switch to Creator View");
164+
await expect(page.locator("[data-tools-count]")).toHaveText("Tool Count: 42/42");
132165
const adminLabels = await page.locator("main [data-tools-accordion-list] .control-card h3").evaluateAll((labels) => labels.map((label) => label.textContent.trim()));
133166
expect(adminLabels).toEqual(expect.arrayContaining([
134167
"Users",
@@ -140,8 +173,13 @@ test("root tools surface links current tool pages without old_* routes", async (
140173
]));
141174
await page.locator("[data-toolbox-role-banner]").click();
142175
await page.waitForURL(/role=user/);
143-
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("CREATOR VIEW • Planned tools hidden • Switch to Admin View");
176+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("CREATOR VIEW • Project tools enabled • Switch to Admin View");
177+
await expect(page.locator("main").getByText("Users", { exact: true })).toHaveCount(0);
178+
await page.goto(`${server.baseUrl}/toolbox/index.html?role=guest`, { waitUntil: "networkidle" });
179+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText("GUEST VIEW • Preview only • Sign in to create");
180+
await expect(page.locator("[data-tools-count]")).toHaveText("Tool Count: 30/42");
144181
await expect(page.locator("main").getByText("Users", { exact: true })).toHaveCount(0);
182+
expect(pageErrors).toEqual([]);
145183
} finally {
146184
await workspaceV2CoverageReporter.stop(page);
147185
await server.close();

toolbox/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<body>
1414
<div data-partial="header-nav"></div>
1515
<div class="container" aria-label="Toolbox role simulation">
16-
<a class="status" href="toolbox/index.html?role=admin" data-toolbox-role-banner>CREATOR VIEW • Planned tools hidden • Switch to Admin View</a>
16+
<a class="status" href="toolbox/index.html?role=user" data-toolbox-role-banner>GUEST VIEW • Preview only • Sign in to create</a>
1717
</div>
1818
<main>
1919
<section class="page-title">
@@ -29,6 +29,7 @@ <h1>Creator tools organized by build surface.</h1>
2929
<button class="btn" type="button" data-tools-sort="grouped" aria-pressed="false">Group</button>
3030
<button class="btn" type="button" data-tools-view="progress" aria-pressed="false">Progress</button>
3131
<button class="btn" type="button" data-tools-view="build-path" aria-pressed="false">Build Path</button>
32+
<span class="pill" data-tools-count>Tool Count: 0/0</span>
3233
</div>
3334
<div class="accordion-group" data-tools-accordion-list></div>
3435
</div>

toolbox/toolRegistry.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export const TOOL_REGISTRY = Object.freeze([
119119
"folderName": "assets",
120120
"entryPoint": "assets/index.html",
121121
"description": "Create sprites, animations, vectors, and palettes.",
122-
"category": "Assets",
122+
"category": "Content",
123123
"colorGroup": "tool-group-content-assets",
124124
"requiredForPlayable": true,
125125
"requiredForTestable": true,
@@ -146,7 +146,7 @@ export const TOOL_REGISTRY = Object.freeze([
146146
"folderName": "colors",
147147
"entryPoint": "colors/index.html",
148148
"description": "Craft and manage color palettes for your games.",
149-
"category": "Assets",
149+
"category": "Content",
150150
"colorGroup": "tool-group-platform-cloud",
151151
"requiredForPlayable": true,
152152
"requiredForTestable": true,
@@ -173,7 +173,7 @@ export const TOOL_REGISTRY = Object.freeze([
173173
"folderName": "fonts",
174174
"entryPoint": "fonts/index.html",
175175
"description": "Plan game typography, font loading, and readable text choices.",
176-
"category": "Assets",
176+
"category": "Content",
177177
"colorGroup": "tool-group-content-assets",
178178
"requiredForPlayable": false,
179179
"requiredForTestable": false,
@@ -200,7 +200,7 @@ export const TOOL_REGISTRY = Object.freeze([
200200
"folderName": "sprites",
201201
"entryPoint": "sprites/index.html",
202202
"description": "Plan sprite creation, review, and game-ready export workflows.",
203-
"category": "Assets",
203+
"category": "Content",
204204
"colorGroup": "tool-group-build-create",
205205
"requiredForPlayable": true,
206206
"requiredForTestable": true,
@@ -227,7 +227,7 @@ export const TOOL_REGISTRY = Object.freeze([
227227
"folderName": "characters",
228228
"entryPoint": "characters/index.html",
229229
"description": "Plan player, NPC, and character asset workflows.",
230-
"category": "Assets",
230+
"category": "Content",
231231
"colorGroup": "tool-group-build-create",
232232
"requiredForPlayable": true,
233233
"requiredForTestable": true,
@@ -254,7 +254,7 @@ export const TOOL_REGISTRY = Object.freeze([
254254
"folderName": "objects",
255255
"entryPoint": "objects/index.html",
256256
"description": "Design reusable game objects and object-ready assets.",
257-
"category": "Assets",
257+
"category": "Content",
258258
"colorGroup": "tool-group-build-create",
259259
"requiredForPlayable": true,
260260
"requiredForTestable": true,
@@ -281,7 +281,7 @@ export const TOOL_REGISTRY = Object.freeze([
281281
"folderName": "worlds",
282282
"entryPoint": "worlds/index.html",
283283
"description": "Shape world layouts, maps, terrain, and scene geometry.",
284-
"category": "Assets",
284+
"category": "Content",
285285
"colorGroup": "tool-group-content-assets",
286286
"requiredForPlayable": true,
287287
"requiredForTestable": true,
@@ -308,7 +308,7 @@ export const TOOL_REGISTRY = Object.freeze([
308308
"folderName": "animations",
309309
"entryPoint": "animations/index.html",
310310
"description": "Plan character and object animation states.",
311-
"category": "Assets",
311+
"category": "Content",
312312
"colorGroup": "tool-group-build-create",
313313
"requiredForPlayable": true,
314314
"requiredForTestable": true,

0 commit comments

Comments
 (0)