Skip to content

Commit 45311fb

Browse files
committed
Add Sprite Creator palette panel
1 parent 19abdfd commit 45311fb

7 files changed

Lines changed: 359 additions & 217 deletions

File tree

assets/theme-v2/css/gamefoundrystudio.css

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,46 @@ body.tool-focus-mode .tool-column:last-of-type {
11991199
background: var(--text)
12001200
}
12011201

1202+
.sprite-canvas-cell--ink,
1203+
.sprite-color-chip--ink {
1204+
background: var(--text)
1205+
}
1206+
1207+
.sprite-canvas-cell--orange,
1208+
.sprite-color-chip--orange {
1209+
background: var(--molten-orange)
1210+
}
1211+
1212+
.sprite-canvas-cell--gold,
1213+
.sprite-color-chip--gold {
1214+
background: var(--forge-gold)
1215+
}
1216+
1217+
.sprite-canvas-cell--green,
1218+
.sprite-color-chip--green {
1219+
background: var(--green)
1220+
}
1221+
1222+
.sprite-canvas-cell--blue,
1223+
.sprite-color-chip--blue {
1224+
background: var(--electric-blue)
1225+
}
1226+
1227+
.sprite-palette-panel {
1228+
display: flex;
1229+
flex-wrap: wrap;
1230+
gap: 8px
1231+
}
1232+
1233+
.sprite-color-chip {
1234+
display: inline-block;
1235+
width: 14px;
1236+
height: 14px;
1237+
border: 1px solid var(--swatch-border);
1238+
border-radius: 50%;
1239+
box-shadow: 0 1px 4px var(--swatch-shadow-color)
1240+
}
1241+
12021242
@media(max-width:980px) {
12031243

12041244
.grid.cols-4,

assets/toolbox/sprites/js/index.js

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
const DEFAULT_GRID_SIZE = 16;
22
const SUPPORTED_GRID_SIZES = Object.freeze([16, 32]);
33
const DRAWING_TOOLS = Object.freeze(["pencil", "eraser", "fill"]);
4+
const EDITOR_COLOR_KEYS = Object.freeze(["ink", "orange", "gold", "green", "blue"]);
45

56
const editorState = {
67
activeTool: "pencil",
8+
activeColor: "ink",
79
gridSize: DEFAULT_GRID_SIZE,
8-
paintedPixels: new Set(),
10+
paintedPixels: new Map(),
911
};
1012

1113
function gridLabel(size) {
@@ -28,13 +30,47 @@ function draftStatusText() {
2830
return `Unsaved editor state: ${count} draft pixel${count === 1 ? "" : "s"} painted.`;
2931
}
3032

33+
function normalizeColorKey(colorKey) {
34+
return EDITOR_COLOR_KEYS.includes(colorKey) ? colorKey : "ink";
35+
}
36+
37+
function clearCellColor(cell) {
38+
cell.classList.remove(...EDITOR_COLOR_KEYS.map((colorKey) => `sprite-canvas-cell--${colorKey}`));
39+
delete cell.dataset.spriteColorKey;
40+
}
41+
42+
function setCellColor(cell, colorKey) {
43+
const normalizedColorKey = normalizeColorKey(colorKey);
44+
clearCellColor(cell);
45+
cell.classList.add("is-painted", `sprite-canvas-cell--${normalizedColorKey}`);
46+
cell.dataset.spriteColorKey = normalizedColorKey;
47+
}
48+
3149
function updateDraftStatus() {
3250
const status = document.querySelector("[data-sprites-draft-status]");
3351
if (status) {
3452
status.textContent = draftStatusText();
3553
}
3654
}
3755

56+
function updatePaletteStatus() {
57+
const status = document.querySelector("[data-sprites-palette-status]");
58+
if (status) {
59+
status.textContent = `Active editor color: ${editorState.activeColor[0].toUpperCase()}${editorState.activeColor.slice(1)}. Palette/Colors remains the reusable color source for future saved sprite records.`;
60+
}
61+
}
62+
63+
function setActiveColor(colorKey) {
64+
const normalizedColorKey = normalizeColorKey(colorKey);
65+
editorState.activeColor = normalizedColorKey;
66+
document.querySelectorAll("[data-sprite-color-button]").forEach((button) => {
67+
const isActive = button.dataset.spriteColorButton === normalizedColorKey;
68+
button.classList.toggle("primary", isActive);
69+
button.setAttribute("aria-pressed", String(isActive));
70+
});
71+
updatePaletteStatus();
72+
}
73+
3874
function setActiveTool(toolName) {
3975
if (!DRAWING_TOOLS.includes(toolName)) {
4076
return;
@@ -56,9 +92,10 @@ function paintCell(cell) {
5692
if (editorState.activeTool === "eraser") {
5793
editorState.paintedPixels.delete(key);
5894
cell.classList.remove("is-painted");
95+
clearCellColor(cell);
5996
} else {
60-
editorState.paintedPixels.add(key);
61-
cell.classList.add("is-painted");
97+
editorState.paintedPixels.set(key, editorState.activeColor);
98+
setCellColor(cell, editorState.activeColor);
6299
}
63100
updateDraftStatus();
64101
}
@@ -71,8 +108,8 @@ function fillGrid() {
71108
editorState.paintedPixels.clear();
72109
grid.querySelectorAll("[data-sprite-pixel-row]").forEach((cell) => {
73110
const key = pixelKey(cell.dataset.spritePixelRow, cell.dataset.spritePixelColumn);
74-
editorState.paintedPixels.add(key);
75-
cell.classList.add("is-painted");
111+
editorState.paintedPixels.set(key, editorState.activeColor);
112+
setCellColor(cell, editorState.activeColor);
76113
});
77114
updateDraftStatus();
78115
}
@@ -141,7 +178,17 @@ function wireDrawingTools() {
141178
});
142179
}
143180

181+
function wirePaletteButtons() {
182+
document.querySelectorAll("[data-sprite-color-button]").forEach((button) => {
183+
button.addEventListener("click", () => {
184+
setActiveColor(button.dataset.spriteColorButton);
185+
});
186+
});
187+
}
188+
144189
wireGridControls();
145190
wireDrawingTools();
191+
wirePaletteButtons();
146192
setGridSize(DEFAULT_GRID_SIZE);
147193
setActiveTool(editorState.activeTool);
194+
setActiveColor(editorState.activeColor);

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
220220
await expect(page.locator("[data-sprites-details-panel]")).toBeVisible();
221221
await expect(page.locator("[data-sprites-footer-status]")).toBeVisible();
222222
await expect(page.locator("[data-sprites-tools-panel]")).toContainText("Sprite Tools");
223-
await expect(page.getByText("Drawing Tools")).toBeVisible();
224-
await expect(page.getByText("Canvas Setup")).toBeVisible();
223+
await expect(page.getByText("Drawing Tools", { exact: true })).toBeVisible();
224+
await expect(page.getByText("Canvas Setup", { exact: true })).toBeVisible();
225225
await expect(page.getByRole("heading", { level: 2, name: "Pixel Work Area" })).toBeVisible();
226226
await expect(page.getByRole("heading", { level: 2, name: "Sprite Details" })).toBeVisible();
227227
await expect(page.locator("[data-sprites-toolbar]")).toBeVisible();
@@ -232,6 +232,8 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
232232
await expect(page.getByRole("button", { name: `${toolName} tool placeholder` })).toBeDisabled();
233233
}
234234
await expect(page.locator("[data-sprites-tool-status]")).toContainText("Pencil is active");
235+
await expect(page.locator("[data-sprites-palette-panel]")).toBeVisible();
236+
await expect(page.locator("[data-sprites-palette-status]")).toContainText("Active editor color: Ink");
235237
await expect(page.locator("[data-sprites-pixel-grid]")).toBeVisible();
236238
await expect(page.locator("[data-sprites-pixel-grid] [role='gridcell']")).toHaveCount(256);
237239
await expect(page.locator("[data-sprites-grid-status]")).toContainText("Canvas display mode: 16x16");
@@ -248,11 +250,18 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
248250
await firstPixel.click();
249251
await expect(firstPixel).not.toHaveClass(/is-painted/);
250252
await expect(page.locator("[data-sprites-draft-status]")).toContainText("empty draft");
253+
await page.getByRole("button", { name: "Gold editor color" }).click();
254+
await expect(page.locator("[data-sprites-palette-status]")).toContainText("Active editor color: Gold");
255+
await page.getByRole("button", { name: "Pencil tool" }).click();
256+
await firstPixel.click();
257+
await expect(firstPixel).toHaveClass(/sprite-canvas-cell--gold/);
258+
await page.getByRole("button", { name: "Blue editor color" }).click();
251259
await page.getByRole("button", { name: "Fill tool" }).click();
252260
await expect(page.locator("[data-sprites-pixel-grid] .is-painted")).toHaveCount(1024);
261+
await expect(page.locator("[data-sprites-pixel-grid] .sprite-canvas-cell--blue")).toHaveCount(1024);
253262
await expect(page.locator("[data-sprites-draft-status]")).toContainText("1024 draft pixels painted");
254263
await expect(page.locator("[data-sprites-shell-status]")).toContainText("Editor ready");
255-
await expect(page.locator("main")).toContainText("Palette/Colors keys only");
264+
await expect(page.locator("main")).toContainText("Palette/Colors remains the reusable color source");
256265
await expect(page.locator("main")).not.toContainText(/Not implemented yet|future rebuild work|Static wireframe only|Plan sprite creation/i);
257266
await expect(page.locator("style, [style], script:not([src])")).toHaveCount(0);
258267

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# PR_26179_CHARLIE_026-sprites-palette-panel
2+
3+
Team: CHARLIE
4+
Workflow: stacked feature workflow
5+
Base branch: PR_26179_CHARLIE_025-sprites-basic-drawing
6+
Canonical ZIP path: dev/workspace/zip/PR_26179_CHARLIE_026-sprites-palette-panel_delta.zip
7+
8+
## Summary
9+
10+
Added an editor-only palette panel with active color selection. Selected editor colors apply to Pencil and Fill drawing in unsaved page-session state. The page still states that Palette/Colors remains the reusable color source for future saved sprite records.
11+
12+
## Branch Validation
13+
14+
PASS
15+
16+
- Current branch: PR_26179_CHARLIE_026-sprites-palette-panel
17+
- Based on: PR_26179_CHARLIE_025-sprites-basic-drawing
18+
- No start_of_day files changed
19+
- No DB/API/schema files changed
20+
- No stale PR #219-#228 code copied
21+
22+
## Requirement Checklist
23+
24+
| Requirement | Status | Notes |
25+
| --- | --- | --- |
26+
| Add active color selection | PASS | Palette buttons update active editor color. |
27+
| Add basic preset palette | PASS | Ink, Orange, Gold, Green, and Blue editor presets are visible. |
28+
| Connect selected color to drawing tools | PASS | Pencil and Fill apply selected color classes. |
29+
| Creator-facing language | PASS | Copy describes editor draft colors and future Palette/Colors source. |
30+
| No DB/API | PASS | No API/database changes. |
31+
| No browser-owned authoritative product data | PASS | No product save or browser storage. |
32+
33+
## Validation Lane Report
34+
35+
Commands:
36+
37+
```text
38+
node --check assets/toolbox/sprites/js/index.js
39+
node --check dev/tests/playwright/tools/SpritesToolShell.spec.mjs
40+
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
41+
rg --pcre2 -n -i "localStorage|sessionStorage|indexedDB|<style|style=|<script(?![^>]+src=)|on(click|change|submit|input|load|error)=|imageDataUrl|local-mem|fake-login|MEM DB" toolbox/sprites/index.html assets/toolbox/sprites/js/index.js dev/tests/playwright/tools/SpritesToolShell.spec.mjs
42+
npx playwright test dev/tests/playwright/tools/SpritesToolShell.spec.mjs --workers=1 --reporter=list --output=<temp>
43+
```
44+
45+
Results:
46+
47+
- Node syntax checks: PASS
48+
- `git diff --check`: PASS
49+
- Guard scan: PASS, no matches
50+
- Targeted Playwright: PASS, 1 test passed
51+
52+
## Manual Validation Notes
53+
54+
1. Open `/toolbox/sprites/index.html` from the stacked branch.
55+
2. Select Gold, use Pencil, and confirm the painted pixel uses Gold.
56+
3. Select Blue, use Fill, and confirm the grid uses Blue.
57+
4. Confirm the page states Palette/Colors remains the reusable color source for future saved records.
58+
59+
## ZIP Path
60+
61+
`dev/workspace/zip/PR_26179_CHARLIE_026-sprites-palette-panel_delta.zip`

docs_build/dev/reports/codex_changed_files.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
assets/toolbox/sprites/js/index.js
33
assets/theme-v2/css/gamefoundrystudio.css
44
dev/tests/playwright/tools/SpritesToolShell.spec.mjs
5-
docs_build/dev/reports/PR_26179_CHARLIE_025-sprites-basic-drawing.md
5+
docs_build/dev/reports/PR_26179_CHARLIE_026-sprites-palette-panel.md
66
docs_build/dev/reports/codex_changed_files.txt
77
docs_build/dev/reports/codex_review.diff

0 commit comments

Comments
 (0)