Skip to content

Commit 97563a2

Browse files
committed
Sync Sprite Creator canvas and preview
1 parent 0a2eb22 commit 97563a2

5 files changed

Lines changed: 337 additions & 304 deletions

File tree

assets/toolbox/sprites/js/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ function applySnapshot(snapshot) {
152152
}
153153

154154
function updateDraftStatus() {
155+
applyPaintedPixelsToGrid();
155156
const status = document.querySelector("[data-sprites-draft-status]");
156157
if (status) {
157158
status.textContent = draftStatusText();
@@ -217,6 +218,8 @@ function setZoomLevel(zoomLevel) {
217218
if (status) {
218219
status.textContent = `Canvas zoom display: ${normalizedZoomLevel * 100}%.`;
219220
}
221+
applyPaintedPixelsToGrid();
222+
renderPreview();
220223
}
221224

222225
function pickCellColor(cell) {
@@ -232,6 +235,8 @@ function pickCellColor(cell) {
232235
if (status) {
233236
status.textContent = `Picker selected ${colorLabel(colorKey)} from the unsaved editor canvas.`;
234237
}
238+
applyPaintedPixelsToGrid();
239+
renderPreview();
235240
}
236241

237242
function setPixel(row, column, colorKey) {

dev/tests/playwright/tools/SpritesToolShell.spec.mjs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,38 @@ function collectPageFailures(page) {
201201
return { consoleErrors, failedRequests, pageErrors };
202202
}
203203

204+
function spriteCell(page, row, column) {
205+
return page.locator(`[data-sprite-pixel-row="${row}"][data-sprite-pixel-column="${column}"]`);
206+
}
207+
208+
async function previewCellHasPaint(page, row, column) {
209+
const gridSize = Number(await page.locator("[data-sprites-pixel-grid]").getAttribute("data-sprites-grid-size"));
210+
return page.locator("[data-sprites-preview-canvas]").evaluate((canvas, coordinates) => {
211+
const context = canvas.getContext("2d");
212+
if (!context || !Number.isFinite(coordinates.gridSize) || coordinates.gridSize <= 0) {
213+
return false;
214+
}
215+
const cellSize = canvas.width / coordinates.gridSize;
216+
const x = Math.max(0, Math.min(canvas.width - 1, Math.floor((coordinates.column - 0.5) * cellSize)));
217+
const y = Math.max(0, Math.min(canvas.height - 1, Math.floor((coordinates.row - 0.5) * cellSize)));
218+
return context.getImageData(x, y, 1, 1).data[3] > 0;
219+
}, { column, gridSize, row });
220+
}
221+
222+
async function expectCenterAndPreviewPainted(page, row, column, colorClass = null) {
223+
const cell = spriteCell(page, row, column);
224+
await expect(cell).toHaveClass(/is-painted/);
225+
if (colorClass) {
226+
await expect(cell).toHaveClass(new RegExp(colorClass));
227+
}
228+
expect(await previewCellHasPaint(page, row, column)).toBe(true);
229+
}
230+
231+
async function expectCenterAndPreviewEmpty(page, row, column) {
232+
await expect(spriteCell(page, row, column)).not.toHaveClass(/is-painted/);
233+
expect(await previewCellHasPaint(page, row, column)).toBe(false);
234+
}
235+
204236
test("Sprite Creator shell loads with visible tool, canvas, details, and status regions", async ({ page }) => {
205237
const server = await startSpriteShellTestServer();
206238
const failures = collectPageFailures(page);
@@ -366,3 +398,72 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
366398
await server.close();
367399
}
368400
});
401+
402+
test("Sprite Creator keeps center canvas and right preview in sync", async ({ page }) => {
403+
const server = await startSpriteShellTestServer();
404+
const failures = collectPageFailures(page);
405+
406+
try {
407+
await page.goto(`${server.baseUrl}/toolbox/sprites/index.html`, { waitUntil: "networkidle" });
408+
409+
await spriteCell(page, 1, 1).click();
410+
await expectCenterAndPreviewPainted(page, 1, 1, "sprite-canvas-cell--ink");
411+
412+
await page.getByRole("button", { name: "Eraser tool" }).click();
413+
await spriteCell(page, 1, 1).click();
414+
await expectCenterAndPreviewEmpty(page, 1, 1);
415+
416+
await page.getByRole("button", { name: "Blue editor color" }).click();
417+
await page.getByRole("button", { name: "Fill tool" }).click();
418+
await expectCenterAndPreviewPainted(page, 1, 1, "sprite-canvas-cell--blue");
419+
420+
await page.getByRole("button", { name: "Clear Canvas" }).click();
421+
await expectCenterAndPreviewEmpty(page, 1, 1);
422+
423+
await page.getByRole("button", { name: "Undo" }).click();
424+
await expectCenterAndPreviewPainted(page, 1, 1, "sprite-canvas-cell--blue");
425+
426+
await page.getByRole("button", { name: "Redo" }).click();
427+
await expectCenterAndPreviewEmpty(page, 1, 1);
428+
429+
await page.getByRole("button", { name: "Gold editor color" }).click();
430+
await page.getByRole("button", { name: "Pencil tool" }).click();
431+
await spriteCell(page, 2, 2).click();
432+
await expectCenterAndPreviewPainted(page, 2, 2, "sprite-canvas-cell--gold");
433+
await page.getByRole("button", { name: "Blue editor color" }).click();
434+
await page.getByRole("button", { name: "Picker tool" }).click();
435+
await spriteCell(page, 2, 2).click();
436+
await expect(page.locator("[data-sprites-palette-status]")).toContainText("Active editor color: Gold");
437+
await expectCenterAndPreviewPainted(page, 2, 2, "sprite-canvas-cell--gold");
438+
439+
await page.getByRole("button", { name: "Zoom tool" }).click();
440+
await page.getByRole("button", { name: "200%" }).click();
441+
await expect(page.locator("[data-sprites-grid-shell]")).toHaveAttribute("data-sprites-zoom-level", "2");
442+
await expectCenterAndPreviewPainted(page, 2, 2, "sprite-canvas-cell--gold");
443+
444+
await page.getByRole("button", { name: "Clear Canvas" }).click();
445+
await page.getByRole("button", { name: "Green editor color" }).click();
446+
await page.getByRole("button", { name: "Line tool" }).click();
447+
await spriteCell(page, 1, 1).click();
448+
await spriteCell(page, 1, 4).click();
449+
await expectCenterAndPreviewPainted(page, 1, 2, "sprite-canvas-cell--green");
450+
451+
await page.getByRole("button", { name: "Clear Canvas" }).click();
452+
await page.getByRole("button", { name: "Rectangle tool" }).click();
453+
await spriteCell(page, 2, 2).click();
454+
await spriteCell(page, 4, 4).click();
455+
await expectCenterAndPreviewPainted(page, 2, 3, "sprite-canvas-cell--green");
456+
457+
await page.getByRole("button", { name: "Clear Canvas" }).click();
458+
await page.getByRole("button", { name: "Circle tool" }).click();
459+
await spriteCell(page, 8, 8).click();
460+
await spriteCell(page, 8, 11).click();
461+
await expectCenterAndPreviewPainted(page, 8, 11, "sprite-canvas-cell--green");
462+
463+
expect(failures.failedRequests).toEqual([]);
464+
expect(failures.pageErrors).toEqual([]);
465+
expect(failures.consoleErrors).toEqual([]);
466+
} finally {
467+
await server.close();
468+
}
469+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# PR_26179_CHARLIE_033-sprites-canvas-preview-sync
2+
3+
Team: CHARLIE
4+
5+
Mode: Batch governance stacked feature workflow
6+
7+
Base branch: PR_26179_CHARLIE_032-sprites-shape-tools
8+
9+
Branch: PR_26179_CHARLIE_033-sprites-canvas-preview-sync
10+
11+
## Summary
12+
13+
Fixed the Sprite Creator center-canvas and right-preview sync path so both render from the same page-session editor state. The center grid is repainted from `editorState.paintedPixels` whenever draft status/preview refreshes, and picker/zoom paths also trigger a repaint even though they do not mutate pixels.
14+
15+
## Branch Validation
16+
17+
PASS
18+
19+
- Built from `PR_26179_CHARLIE_032-sprites-shape-tools`.
20+
- Scope stayed limited to canvas/preview synchronization and regression coverage.
21+
- No DB/API/schema changes.
22+
- No persistence or browser-owned authoritative product data added.
23+
- No start_of_day files changed.
24+
- ZIP package created at the requested path.
25+
26+
## Requirement Checklist
27+
28+
PASS - Right preview updates from page-session editor state.
29+
30+
PASS - Center editor canvas updates from the same page-session editor state.
31+
32+
PASS - Drawing repaints center canvas immediately.
33+
34+
PASS - Erase repaints center canvas immediately.
35+
36+
PASS - Fill repaints center canvas immediately.
37+
38+
PASS - Clear repaints center canvas immediately.
39+
40+
PASS - Undo repaints center canvas immediately.
41+
42+
PASS - Redo repaints center canvas immediately.
43+
44+
PASS - Picker repaints center canvas and preview without mutating pixels.
45+
46+
PASS - Zoom repaints center canvas and preview without mutating pixels.
47+
48+
PASS - Shape tools repaint center canvas immediately.
49+
50+
PASS - Playwright regression coverage added for center canvas and preview sync.
51+
52+
## Validation Lane
53+
54+
PASS - `node --check assets/toolbox/sprites/js/index.js`
55+
56+
PASS - `node --check dev/tests/playwright/tools/SpritesToolShell.spec.mjs`
57+
58+
PASS - `git diff --check -- assets/toolbox/sprites/js/index.js dev/tests/playwright/tools/SpritesToolShell.spec.mjs`
59+
60+
PASS - Runtime guard scan found no prohibited inline style/script/event-handler or stale placeholder text in touched runtime files.
61+
62+
PASS - `npx playwright test dev/tests/playwright/tools/SpritesToolShell.spec.mjs --workers=1 --reporter=list`
63+
64+
## Manual Validation Notes
65+
66+
1. Open `toolbox/sprites/index.html`.
67+
2. Draw a pixel and confirm the center grid and right preview both update.
68+
3. Erase the pixel and confirm both surfaces clear.
69+
4. Fill, clear, undo, and redo; confirm both surfaces stay matched.
70+
5. Use Picker and Zoom; confirm both surfaces remain matched.
71+
6. Draw Line, Rectangle, and Circle shapes; confirm center grid and right preview both show the shape.
72+
73+
## ZIP
74+
75+
`dev/workspace/zip/PR_26179_CHARLIE_033-sprites-canvas-preview-sync_delta.zip`
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
assets/toolbox/sprites/js/index.js
22
dev/tests/playwright/tools/SpritesToolShell.spec.mjs
3-
docs_build/dev/reports/PR_26179_CHARLIE_032-sprites-shape-tools.md
3+
docs_build/dev/reports/PR_26179_CHARLIE_033-sprites-canvas-preview-sync.md
44
docs_build/dev/reports/codex_changed_files.txt
55
docs_build/dev/reports/codex_review.diff
6-
toolbox/sprites/index.html

0 commit comments

Comments
 (0)