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
40 changes: 40 additions & 0 deletions assets/theme-v2/css/gamefoundrystudio.css
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,46 @@ body.tool-focus-mode .tool-column:last-of-type {
background: var(--text)
}

.sprite-canvas-cell--ink,
.sprite-color-chip--ink {
background: var(--text)
Comment on lines +1202 to +1204

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move palette styles into the loaded theme bundle

The sprites page only links assets/theme-v2/css/theme.css, and that bundle does not import gamefoundrystudio.css, so these new palette/cell color rules are never loaded at runtime. In this context, selecting Gold/Blue toggles classes in JS, but the chips and painted pixels get no palette-specific styling, leaving the new color-selection feature visually ineffective.

Useful? React with 👍 / 👎.

}

.sprite-canvas-cell--orange,
.sprite-color-chip--orange {
background: var(--molten-orange)
Comment on lines +1207 to +1209

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Increase color rule specificity

When these palette styles are loaded, painted cells still keep the old ink background because each cell gets both is-painted and a color class, and .sprite-canvas-cell.is-painted has higher specificity than these one-class color selectors. In that scenario Pencil/Fill with Orange/Gold/etc. still render as var(--text) instead of the selected palette color, so the color rules need to match or exceed the painted-cell selector specificity.

Useful? React with 👍 / 👎.

}

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

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

.sprite-canvas-cell--blue,
.sprite-color-chip--blue {
background: var(--electric-blue)
}

.sprite-palette-panel {
display: flex;
flex-wrap: wrap;
gap: 8px
}

.sprite-color-chip {
display: inline-block;
width: 14px;
height: 14px;
border: 1px solid var(--swatch-border);
border-radius: 50%;
box-shadow: 0 1px 4px var(--swatch-shadow-color)
}

@media(max-width:980px) {

.grid.cols-4,
Expand Down
57 changes: 52 additions & 5 deletions assets/toolbox/sprites/js/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const DEFAULT_GRID_SIZE = 16;
const SUPPORTED_GRID_SIZES = Object.freeze([16, 32]);
const DRAWING_TOOLS = Object.freeze(["pencil", "eraser", "fill"]);
const EDITOR_COLOR_KEYS = Object.freeze(["ink", "orange", "gold", "green", "blue"]);

const editorState = {
activeTool: "pencil",
activeColor: "ink",
gridSize: DEFAULT_GRID_SIZE,
paintedPixels: new Set(),
paintedPixels: new Map(),
};

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

function normalizeColorKey(colorKey) {
return EDITOR_COLOR_KEYS.includes(colorKey) ? colorKey : "ink";
}

function clearCellColor(cell) {
cell.classList.remove(...EDITOR_COLOR_KEYS.map((colorKey) => `sprite-canvas-cell--${colorKey}`));
delete cell.dataset.spriteColorKey;
}

function setCellColor(cell, colorKey) {
const normalizedColorKey = normalizeColorKey(colorKey);
clearCellColor(cell);
cell.classList.add("is-painted", `sprite-canvas-cell--${normalizedColorKey}`);
cell.dataset.spriteColorKey = normalizedColorKey;
}

function updateDraftStatus() {
const status = document.querySelector("[data-sprites-draft-status]");
if (status) {
status.textContent = draftStatusText();
}
}

function updatePaletteStatus() {
const status = document.querySelector("[data-sprites-palette-status]");
if (status) {
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.`;
}
}

function setActiveColor(colorKey) {
const normalizedColorKey = normalizeColorKey(colorKey);
editorState.activeColor = normalizedColorKey;
document.querySelectorAll("[data-sprite-color-button]").forEach((button) => {
const isActive = button.dataset.spriteColorButton === normalizedColorKey;
button.classList.toggle("primary", isActive);
button.setAttribute("aria-pressed", String(isActive));
});
updatePaletteStatus();
}

function setActiveTool(toolName) {
if (!DRAWING_TOOLS.includes(toolName)) {
return;
Expand All @@ -56,9 +92,10 @@ function paintCell(cell) {
if (editorState.activeTool === "eraser") {
editorState.paintedPixels.delete(key);
cell.classList.remove("is-painted");
clearCellColor(cell);
} else {
editorState.paintedPixels.add(key);
cell.classList.add("is-painted");
editorState.paintedPixels.set(key, editorState.activeColor);
setCellColor(cell, editorState.activeColor);
}
updateDraftStatus();
}
Expand All @@ -71,8 +108,8 @@ function fillGrid() {
editorState.paintedPixels.clear();
grid.querySelectorAll("[data-sprite-pixel-row]").forEach((cell) => {
const key = pixelKey(cell.dataset.spritePixelRow, cell.dataset.spritePixelColumn);
editorState.paintedPixels.add(key);
cell.classList.add("is-painted");
editorState.paintedPixels.set(key, editorState.activeColor);
setCellColor(cell, editorState.activeColor);
});
updateDraftStatus();
}
Expand Down Expand Up @@ -141,7 +178,17 @@ function wireDrawingTools() {
});
}

function wirePaletteButtons() {
document.querySelectorAll("[data-sprite-color-button]").forEach((button) => {
button.addEventListener("click", () => {
setActiveColor(button.dataset.spriteColorButton);
});
});
}

wireGridControls();
wireDrawingTools();
wirePaletteButtons();
setGridSize(DEFAULT_GRID_SIZE);
setActiveTool(editorState.activeTool);
setActiveColor(editorState.activeColor);
15 changes: 12 additions & 3 deletions dev/tests/playwright/tools/SpritesToolShell.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
await expect(page.locator("[data-sprites-details-panel]")).toBeVisible();
await expect(page.locator("[data-sprites-footer-status]")).toBeVisible();
await expect(page.locator("[data-sprites-tools-panel]")).toContainText("Sprite Tools");
await expect(page.getByText("Drawing Tools")).toBeVisible();
await expect(page.getByText("Canvas Setup")).toBeVisible();
await expect(page.getByText("Drawing Tools", { exact: true })).toBeVisible();
await expect(page.getByText("Canvas Setup", { exact: true })).toBeVisible();
await expect(page.getByRole("heading", { level: 2, name: "Pixel Work Area" })).toBeVisible();
await expect(page.getByRole("heading", { level: 2, name: "Sprite Details" })).toBeVisible();
await expect(page.locator("[data-sprites-toolbar]")).toBeVisible();
Expand All @@ -232,6 +232,8 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
await expect(page.getByRole("button", { name: `${toolName} tool placeholder` })).toBeDisabled();
}
await expect(page.locator("[data-sprites-tool-status]")).toContainText("Pencil is active");
await expect(page.locator("[data-sprites-palette-panel]")).toBeVisible();
await expect(page.locator("[data-sprites-palette-status]")).toContainText("Active editor color: Ink");
await expect(page.locator("[data-sprites-pixel-grid]")).toBeVisible();
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");
Expand All @@ -248,11 +250,18 @@ test("Sprite Creator shell loads with visible tool, canvas, details, and status
await firstPixel.click();
await expect(firstPixel).not.toHaveClass(/is-painted/);
await expect(page.locator("[data-sprites-draft-status]")).toContainText("empty draft");
await page.getByRole("button", { name: "Gold editor color" }).click();
await expect(page.locator("[data-sprites-palette-status]")).toContainText("Active editor color: Gold");
await page.getByRole("button", { name: "Pencil tool" }).click();
await firstPixel.click();
await expect(firstPixel).toHaveClass(/sprite-canvas-cell--gold/);
await page.getByRole("button", { name: "Blue editor color" }).click();
await page.getByRole("button", { name: "Fill tool" }).click();
await expect(page.locator("[data-sprites-pixel-grid] .is-painted")).toHaveCount(1024);
await expect(page.locator("[data-sprites-pixel-grid] .sprite-canvas-cell--blue")).toHaveCount(1024);
await expect(page.locator("[data-sprites-draft-status]")).toContainText("1024 draft pixels painted");
await expect(page.locator("[data-sprites-shell-status]")).toContainText("Editor ready");
await expect(page.locator("main")).toContainText("Palette/Colors keys only");
await expect(page.locator("main")).toContainText("Palette/Colors remains the reusable color source");
await expect(page.locator("main")).not.toContainText(/Not implemented yet|future rebuild work|Static wireframe only|Plan sprite creation/i);
await expect(page.locator("style, [style], script:not([src])")).toHaveCount(0);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# PR_26179_CHARLIE_026-sprites-palette-panel

Team: CHARLIE
Workflow: stacked feature workflow
Base branch: PR_26179_CHARLIE_025-sprites-basic-drawing
Canonical ZIP path: dev/workspace/zip/PR_26179_CHARLIE_026-sprites-palette-panel_delta.zip

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the required tmp delta ZIP path

The repo packaging instructions require every BUILD artifact at <project folder>/tmp/<TASK_NAME>_delta.zip, but this report declares dev/workspace/zip/... as canonical. In the build/evaluation context that consumes these reports, the delta ZIP will be looked for under tmp, so documenting and producing the alternate path can leave the required artifact undiscoverable.

Useful? React with 👍 / 👎.


## Summary

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.

## Branch Validation

PASS

- Current branch: PR_26179_CHARLIE_026-sprites-palette-panel
- Based on: PR_26179_CHARLIE_025-sprites-basic-drawing
- No start_of_day files changed
- No DB/API/schema files changed
- No stale PR #219-#228 code copied

## Requirement Checklist

| Requirement | Status | Notes |
| --- | --- | --- |
| Add active color selection | PASS | Palette buttons update active editor color. |
| Add basic preset palette | PASS | Ink, Orange, Gold, Green, and Blue editor presets are visible. |
| Connect selected color to drawing tools | PASS | Pencil and Fill apply selected color classes. |
| Creator-facing language | PASS | Copy describes editor draft colors and future Palette/Colors source. |
| No DB/API | PASS | No API/database changes. |
| No browser-owned authoritative product data | PASS | No product save or browser storage. |

## Validation Lane Report

Commands:

```text
node --check assets/toolbox/sprites/js/index.js
node --check dev/tests/playwright/tools/SpritesToolShell.spec.mjs
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
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
npx playwright test dev/tests/playwright/tools/SpritesToolShell.spec.mjs --workers=1 --reporter=list --output=<temp>
```

Results:

- Node syntax checks: PASS
- `git diff --check`: PASS
- Guard scan: PASS, no matches
- Targeted Playwright: PASS, 1 test passed

## Manual Validation Notes

1. Open `/toolbox/sprites/index.html` from the stacked branch.
2. Select Gold, use Pencil, and confirm the painted pixel uses Gold.
3. Select Blue, use Fill, and confirm the grid uses Blue.
4. Confirm the page states Palette/Colors remains the reusable color source for future saved records.

## ZIP Path

`dev/workspace/zip/PR_26179_CHARLIE_026-sprites-palette-panel_delta.zip`
2 changes: 1 addition & 1 deletion docs_build/dev/reports/codex_changed_files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
assets/toolbox/sprites/js/index.js
assets/theme-v2/css/gamefoundrystudio.css
dev/tests/playwright/tools/SpritesToolShell.spec.mjs
docs_build/dev/reports/PR_26179_CHARLIE_025-sprites-basic-drawing.md
docs_build/dev/reports/PR_26179_CHARLIE_026-sprites-palette-panel.md
docs_build/dev/reports/codex_changed_files.txt
docs_build/dev/reports/codex_review.diff
Loading
Loading