Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions assets/theme-v2/css/gamefoundrystudio.css
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,10 @@ body.tool-focus-mode .tool-column:last-of-type {
grid-template-rows: repeat(32, minmax(0, 1fr))
}

.sprite-canvas-shell[data-sprites-zoom-level="0.5"] .sprite-canvas-grid {
width: min(260px, 50%)
}

.sprite-canvas-shell[data-sprites-zoom-level="2"] .sprite-canvas-grid {
width: min(720px, 160%)
}
Expand Down Expand Up @@ -1212,26 +1216,31 @@ body.tool-focus-mode .tool-column:last-of-type {
background: var(--text)
}

.sprite-canvas-cell.is-painted.sprite-canvas-cell--ink,
.sprite-canvas-cell--ink,
.sprite-color-chip--ink {
background: var(--text)
}

.sprite-canvas-cell.is-painted.sprite-canvas-cell--orange,
.sprite-canvas-cell--orange,
.sprite-color-chip--orange {
background: var(--molten-orange)
}

.sprite-canvas-cell.is-painted.sprite-canvas-cell--gold,
.sprite-canvas-cell--gold,
.sprite-color-chip--gold {
background: var(--forge-gold)
}

.sprite-canvas-cell.is-painted.sprite-canvas-cell--green,
.sprite-canvas-cell--green,
.sprite-color-chip--green {
background: var(--green)
}

.sprite-canvas-cell.is-painted.sprite-canvas-cell--blue,
.sprite-canvas-cell--blue,
.sprite-color-chip--blue {
background: var(--electric-blue)
Expand Down
2 changes: 1 addition & 1 deletion assets/toolbox/sprites/js/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const DEFAULT_GRID_SIZE = 16;
const DEFAULT_FRAME_DURATION_MS = 120;
const SUPPORTED_GRID_SIZES = Object.freeze([16, 32]);
const SUPPORTED_ZOOM_LEVELS = Object.freeze([1, 2, 4]);
const SUPPORTED_ZOOM_LEVELS = Object.freeze([0.5, 1, 2, 4]);
const SHAPE_TOOLS = Object.freeze(["line", "rectangle", "circle"]);
const EDITOR_TOOLS = Object.freeze(["pencil", "eraser", "fill", "picker", "zoom", ...SHAPE_TOOLS]);
const EDITOR_COLOR_KEYS = Object.freeze(["ink", "orange", "gold", "green", "blue"]);
Expand Down
31 changes: 29 additions & 2 deletions dev/tests/playwright/tools/SpritesToolShell.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,25 @@ async function previewCellHasPaint(page, row, column) {
}, { column, gridSize, row });
}

async function previewCellColor(page, row, column) {
const gridSize = Number(await page.locator("[data-sprites-pixel-grid]").getAttribute("data-sprites-grid-size"));
return page.locator("[data-sprites-preview-canvas]").evaluate((canvas, coordinates) => {
const context = canvas.getContext("2d");
if (!context || !Number.isFinite(coordinates.gridSize) || coordinates.gridSize <= 0) {
return "";
}
const cellSize = canvas.width / coordinates.gridSize;
const x = Math.max(0, Math.min(canvas.width - 1, Math.floor((coordinates.column - 0.5) * cellSize)));
const y = Math.max(0, Math.min(canvas.height - 1, Math.floor((coordinates.row - 0.5) * cellSize)));
const pixel = context.getImageData(x, y, 1, 1).data;
return `rgb(${pixel[0]}, ${pixel[1]}, ${pixel[2]})`;
}, { column, gridSize, row });
}

async function centerCellColor(page, row, column) {
return spriteCell(page, row, column).evaluate((cell) => getComputedStyle(cell).backgroundColor);
}

async function canvasHasAnyPaint(locator) {
return locator.evaluate((canvas) => {
const context = canvas.getContext("2d");
Expand All @@ -240,6 +259,9 @@ async function expectCenterAndPreviewPainted(page, row, column, colorClass = nul
await expect(cell).toHaveClass(/is-painted/);
if (colorClass) {
await expect(cell).toHaveClass(new RegExp(colorClass));
const centerColor = await centerCellColor(page, row, column);
expect(centerColor).not.toBe("rgb(255, 255, 255)");
expect(await previewCellColor(page, row, column)).toBe(centerColor);
}
expect(await previewCellHasPaint(page, row, column)).toBe(true);
}
Expand Down Expand Up @@ -337,6 +359,7 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
await expect(page.locator("[data-sprites-pixel-grid] [role='gridcell']")).toHaveCount(256);
await expect(page.locator("[data-sprites-grid-status]")).toContainText("Canvas display mode: 16x16");
await expect(page.locator("[data-sprites-zoom-status]")).toContainText("100%");
await expect(page.getByRole("button", { name: "50%" })).toBeVisible();
await page.getByRole("button", { name: "32x32" }).click();
await expect(page.locator("[data-sprites-pixel-grid]")).toHaveAttribute("aria-label", "Sprite Creator 32 by 32 pixel canvas");
await expect(page.locator("[data-sprites-pixel-grid] [role='gridcell']")).toHaveCount(1024);
Expand Down Expand Up @@ -412,6 +435,10 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
await expect(page.locator("[data-sprites-zoom-status]")).toContainText("400%");
await page.getByRole("button", { name: "100%" }).click();
await expect(page.locator("[data-sprites-grid-shell]")).toHaveAttribute("data-sprites-zoom-level", "1");
await page.getByRole("button", { name: "50%" }).click();
await expect(page.locator("[data-sprites-grid-shell]")).toHaveAttribute("data-sprites-zoom-level", "0.5");
await expect(page.locator("[data-sprites-zoom-status]")).toContainText("50%");
await expect(page.locator("[data-sprites-pixel-grid] [role='gridcell']")).toHaveCount(256);
const gridCells = page.locator("[data-sprites-pixel-grid] [role='gridcell']");
await page.getByRole("button", { name: "Clear Canvas" }).click();
await page.getByRole("button", { name: "Green editor color" }).click();
Expand Down Expand Up @@ -472,14 +499,14 @@ test("Sprite Creator canvas grid dimensions stay exact across modes and zoom", a
await page.goto(`${server.baseUrl}/toolbox/sprites/index.html`, { waitUntil: "networkidle" });

await expectExactGridDimensions(page, 16);
for (const zoomLabel of ["200%", "400%", "100%"]) {
for (const zoomLabel of ["50%", "200%", "400%", "100%"]) {
await page.getByRole("button", { name: zoomLabel }).click();
await expectExactGridDimensions(page, 16);
}

await page.getByRole("button", { name: "32x32" }).click();
await expectExactGridDimensions(page, 32);
for (const zoomLabel of ["200%", "400%", "100%"]) {
for (const zoomLabel of ["50%", "200%", "400%", "100%"]) {
await page.getByRole("button", { name: zoomLabel }).click();
await expectExactGridDimensions(page, 32);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# PR_26179_CHARLIE_039-sprites-color-and-zoom-fix

Team: CHARLIE

Mode: Batch governance stacked bug-fix workflow

Base branch: PR_26179_CHARLIE_038-sprites-grid-dimension-fix

Branch: PR_26179_CHARLIE_039-sprites-color-and-zoom-fix

## Summary

Fixed Sprite Creator center canvas selected-color rendering and added a 50% zoom option. The center grid now uses color-specific painted-cell selectors with enough specificity to override the generic painted fallback, so Pencil, Fill, and Shape tools display the selected palette color instead of defaulting visually to the fallback color. The Playwright coverage now compares the center cell computed color with the right preview canvas pixel color to prove both surfaces use the same selected-color page-session state.

## Branch Validation

PASS

- Built from `PR_26179_CHARLIE_038-sprites-grid-dimension-fix`.
- Scope stayed limited to selected color rendering, 50% zoom, and regression coverage.
- No DB/API/schema changes.
- No browser-owned authoritative product data added.
- No start_of_day files changed.
- ZIP package created at the requested path.

## Requirement Checklist

PASS - Center canvas drawing color follows selected palette color.

PASS - Pencil applies selected color to center grid.

PASS - Fill applies selected color to center grid.

PASS - Shape tools apply selected color to center grid.

PASS - Center grid, right preview, frame data, animation preview, and exports use the same selected-color pixel state.

PASS - Painted pixels do not default to white unless that is the selected palette color.

PASS - 50% zoom option added.

PASS - Existing 100%, 200%, and 400% zoom options retained.

PASS - Zoom does not change grid dimensions or pixel state.

PASS - Export path still works.

PASS - No DB/API/schema changes.

PASS - No browser-owned authoritative product data.

PASS - No start_of_day changes.

## Validation Lane

PASS - `node --check assets/toolbox/sprites/js/index.js`

PASS - `node --check dev/tests/playwright/tools/SpritesToolShell.spec.mjs`

PASS - `git diff --check -- toolbox/sprites/index.html assets/toolbox/sprites/js/index.js assets/theme-v2/css/gamefoundrystudio.css dev/tests/playwright/tools/SpritesToolShell.spec.mjs`

PASS - Runtime guard scan found no prohibited inline style/script/event-handler or stale placeholder text in touched runtime files.

PASS - `npx playwright test dev/tests/playwright/tools/SpritesToolShell.spec.mjs --workers=1 --reporter=list`

## Manual Validation Notes

1. Open `toolbox/sprites/index.html`.
2. Select a non-white palette color such as Gold or Blue.
3. Draw with Pencil and confirm the center grid cell uses the selected color.
4. Confirm the right preview shows the same selected color.
5. Use Fill and Shape tools with a selected color and confirm the center grid matches.
6. Use 50%, 100%, 200%, and 400% zoom and confirm grid dimensions and pixels do not change.
7. Download PNG and Animation Strip and confirm export status still completes.

## ZIP

`dev/workspace/zip/PR_26179_CHARLIE_039-sprites-color-and-zoom-fix_delta.zip`
3 changes: 2 additions & 1 deletion docs_build/dev/reports/codex_changed_files.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
assets/theme-v2/css/gamefoundrystudio.css
assets/toolbox/sprites/js/index.js
dev/tests/playwright/tools/SpritesToolShell.spec.mjs
docs_build/dev/reports/PR_26179_CHARLIE_038-sprites-grid-dimension-fix.md
docs_build/dev/reports/PR_26179_CHARLIE_039-sprites-color-and-zoom-fix.md
docs_build/dev/reports/codex_changed_files.txt
docs_build/dev/reports/codex_review.diff
toolbox/sprites/index.html
Loading
Loading