-
Notifications
You must be signed in to change notification settings - Fork 0
PR_26179_CHARLIE_027-sprites-preview-export #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,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 EDITOR_COLOR_CSS_VARIABLES = Object.freeze({ | ||
| blue: "--electric-blue", | ||
| gold: "--forge-gold", | ||
| green: "--green", | ||
| ink: "--text", | ||
| orange: "--molten-orange", | ||
| }); | ||
|
|
||
| const editorState = { | ||
| activeTool: "pencil", | ||
|
|
@@ -51,6 +58,7 @@ function updateDraftStatus() { | |
| if (status) { | ||
| status.textContent = draftStatusText(); | ||
| } | ||
| renderPreview(); | ||
| } | ||
|
|
||
| function updatePaletteStatus() { | ||
|
|
@@ -114,6 +122,64 @@ function fillGrid() { | |
| updateDraftStatus(); | ||
| } | ||
|
|
||
| function editorColorValue(colorKey) { | ||
| const variableName = EDITOR_COLOR_CSS_VARIABLES[normalizeColorKey(colorKey)]; | ||
| const value = getComputedStyle(document.documentElement).getPropertyValue(variableName).trim(); | ||
| return value || "#111111"; | ||
| } | ||
|
|
||
| function renderPreview() { | ||
| const canvas = document.querySelector("[data-sprites-preview-canvas]"); | ||
| if (!canvas) { | ||
| return; | ||
| } | ||
| const context = canvas.getContext("2d"); | ||
| if (!context) { | ||
| return; | ||
| } | ||
| const size = editorState.gridSize; | ||
| const cellSize = canvas.width / size; | ||
| context.clearRect(0, 0, canvas.width, canvas.height); | ||
| for (const [key, colorKey] of editorState.paintedPixels.entries()) { | ||
| const [rowText, columnText] = key.split(":"); | ||
| const row = Number(rowText); | ||
| const column = Number(columnText); | ||
| if (!Number.isFinite(row) || !Number.isFinite(column)) { | ||
| continue; | ||
| } | ||
| context.fillStyle = editorColorValue(colorKey); | ||
| context.fillRect((column - 1) * cellSize, (row - 1) * cellSize, cellSize, cellSize); | ||
| } | ||
| } | ||
|
|
||
| function exportPreviewPng() { | ||
| const canvas = document.querySelector("[data-sprites-preview-canvas]"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the user downloads after choosing either 16x16 or 32x32, this exports the fixed preview canvas, which is declared as Useful? React with 👍 / 👎. |
||
| const status = document.querySelector("[data-sprites-export-status]"); | ||
| if (!canvas) { | ||
| return; | ||
| } | ||
| canvas.toBlob((blob) => { | ||
| if (!blob) { | ||
| if (status) { | ||
| status.textContent = "PNG export is unavailable in this browser session."; | ||
| } | ||
| return; | ||
| } | ||
| const objectUrl = URL.createObjectURL(blob); | ||
| const link = document.createElement("a"); | ||
| link.href = objectUrl; | ||
| link.download = "sprite-creator-draft.png"; | ||
| link.rel = "noopener"; | ||
| document.body.append(link); | ||
| link.click(); | ||
| link.remove(); | ||
| URL.revokeObjectURL(objectUrl); | ||
| if (status) { | ||
| status.textContent = "PNG downloaded from unsaved editor draft."; | ||
| } | ||
| }, "image/png"); | ||
| } | ||
|
|
||
| function setGridSize(size) { | ||
| const grid = document.querySelector("[data-sprites-pixel-grid]"); | ||
| const status = document.querySelector("[data-sprites-grid-status]"); | ||
|
|
@@ -186,9 +252,18 @@ function wirePaletteButtons() { | |
| }); | ||
| } | ||
|
|
||
| function wireExportButton() { | ||
| const button = document.querySelector("[data-sprites-export-png]"); | ||
| if (button) { | ||
| button.addEventListener("click", exportPreviewPng); | ||
| } | ||
| } | ||
|
|
||
| wireGridControls(); | ||
| wireDrawingTools(); | ||
| wirePaletteButtons(); | ||
| wireExportButton(); | ||
| setGridSize(DEFAULT_GRID_SIZE); | ||
| setActiveTool(editorState.activeTool); | ||
| setActiveColor(editorState.activeColor); | ||
| renderPreview(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # PR_26179_CHARLIE_027-sprites-preview-export | ||
|
|
||
| Team: CHARLIE | ||
| Workflow: stacked feature workflow | ||
| Base branch: PR_26179_CHARLIE_026-sprites-palette-panel | ||
| Canonical ZIP path: dev/workspace/zip/PR_26179_CHARLIE_027-sprites-preview-export_delta.zip | ||
|
|
||
| ## Summary | ||
|
|
||
| Added live preview and PNG download/export for the unsaved Sprite Creator editor draft. Export uses canvas `toBlob` and browser download behavior. This PR does not save to library, publish, write browser storage, or add API/DB/schema changes. | ||
|
|
||
| ## Branch Validation | ||
|
|
||
| PASS | ||
|
|
||
| - Current branch: PR_26179_CHARLIE_027-sprites-preview-export | ||
| - Based on: PR_26179_CHARLIE_026-sprites-palette-panel | ||
| - 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 live preview | PASS | Preview canvas reflects unsaved draft pixels. | | ||
| | Add PNG export/download | PASS | Download button exports `sprite-creator-draft.png`. | | ||
| | No save-to-library | PASS | No saved sprite library or product persistence added. | | ||
| | No publishing | PASS | Export is local download only. | | ||
| | Avoid persisted data URLs | PASS | Uses `canvas.toBlob`; no `imageDataUrl` or `toDataURL`. | | ||
| | No DB/API/schema changes | PASS | Only UI/JS/CSS/test/report files changed. | | ||
|
|
||
| ## 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|imageDataUrl|toDataURL|<style|style=|<script(?![^>]+src=)|on(click|change|submit|input|load|error)=|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. Draw or fill pixels. | ||
| 3. Confirm the preview canvas shows the draft. | ||
| 4. Click Download PNG and confirm a `sprite-creator-draft.png` download starts. | ||
| 5. Confirm no save-to-library or publishing controls are present. | ||
|
|
||
| ## ZIP Path | ||
|
|
||
| `dev/workspace/zip/PR_26179_CHARLIE_027-sprites-preview-export_delta.zip` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These new
.sprite-preview-*rules are added togamefoundrystudio.css, but the sprites page only linksassets/theme-v2/css/theme.css, and that entrypoint does not importgamefoundrystudio.css. In the actual/toolbox/sprites/index.htmlpage the preview/export panel therefore loses the intended shell, border, sizing, and pixelated rendering; move these rules into an imported Theme V2 module or wire this stylesheet intentionally.Useful? React with 👍 / 👎.