mirror of
https://github.com/litruv/picoGraph.git
synced 2026-07-24 02:36:04 +10:00
- Created test_cart.p8 with basic drawing functionality and a print statement. - Created test_unix.p8 with a simple initialization function.
206 lines
5.1 KiB
JavaScript
206 lines
5.1 KiB
JavaScript
import { SpriteEditor } from './SpriteEditor.js';
|
|
import { SpriteSheet } from '../core/SpriteSheet.js';
|
|
|
|
/**
|
|
* Manages the sprite editor modal integration.
|
|
*/
|
|
export class SpriteEditorManager {
|
|
/**
|
|
* @param {HTMLElement} modal Modal element.
|
|
* @param {HTMLElement} container Container for sprite editor.
|
|
*/
|
|
constructor(modal, container) {
|
|
this.modal = modal;
|
|
this.container = container;
|
|
this.spriteSheet = new SpriteSheet();
|
|
this.editor = null;
|
|
this.isOpen = false;
|
|
|
|
this.#setupModal();
|
|
}
|
|
|
|
/**
|
|
* Sets up modal event listeners.
|
|
*/
|
|
#setupModal() {
|
|
// Close modal on backdrop click
|
|
const backdrop = this.modal.querySelector('.modal__backdrop');
|
|
backdrop?.addEventListener('click', () => this.close());
|
|
|
|
// Close modal on close button click
|
|
const closeButtons = this.modal.querySelectorAll('[data-modal-close]');
|
|
closeButtons.forEach((btn) => {
|
|
btn.addEventListener('click', () => this.close());
|
|
});
|
|
|
|
// Export to .p8
|
|
const exportBtn = document.getElementById('exportP8Button');
|
|
exportBtn?.addEventListener('click', () => this.#exportToP8());
|
|
|
|
// Import from .p8
|
|
const importBtn = document.getElementById('importP8Button');
|
|
importBtn?.addEventListener('click', () => this.#importFromP8());
|
|
|
|
// Initialize editor on first open
|
|
this.modal.addEventListener('transitionend', () => {
|
|
if (this.isOpen && !this.editor) {
|
|
this.#initializeEditor();
|
|
}
|
|
}, { once: true });
|
|
}
|
|
|
|
/**
|
|
* Initializes the sprite editor.
|
|
*/
|
|
#initializeEditor() {
|
|
if (this.editor) return;
|
|
|
|
this.editor = new SpriteEditor(this.container, this.spriteSheet);
|
|
console.log('Sprite editor initialized');
|
|
}
|
|
|
|
/**
|
|
* Opens the sprite editor modal.
|
|
*/
|
|
open() {
|
|
if (this.isOpen) return;
|
|
|
|
this.isOpen = true;
|
|
this.modal.removeAttribute('hidden');
|
|
this.modal.setAttribute('aria-hidden', 'false');
|
|
document.body.classList.add('modal-open');
|
|
|
|
// Initialize editor immediately if not already done
|
|
if (!this.editor) {
|
|
this.#initializeEditor();
|
|
}
|
|
|
|
// Focus trap
|
|
requestAnimationFrame(() => {
|
|
const closeBtn = this.modal.querySelector('[data-modal-close]');
|
|
closeBtn?.focus();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Closes the sprite editor modal.
|
|
*/
|
|
close() {
|
|
if (!this.isOpen) return;
|
|
|
|
this.isOpen = false;
|
|
this.modal.setAttribute('hidden', '');
|
|
this.modal.setAttribute('aria-hidden', 'true');
|
|
document.body.classList.remove('modal-open');
|
|
}
|
|
|
|
/**
|
|
* Exports sprite data to .p8 format.
|
|
*/
|
|
#exportToP8() {
|
|
if (!this.editor) return;
|
|
|
|
const gfxData = this.editor.getSpriteSheet().toP8Gfx();
|
|
const flagData = this.editor.getSpriteSheet().toP8Flags();
|
|
|
|
const p8Content = `pico-8 cartridge // http://www.pico-8.com
|
|
version 41
|
|
__lua__
|
|
-- generated by picoGraph
|
|
-- paste your blueprinted code here
|
|
|
|
__gfx__
|
|
${gfxData}
|
|
__gff__
|
|
${flagData}
|
|
`;
|
|
|
|
const blob = new Blob([p8Content], { type: 'text/plain' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'sprites.p8';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
|
|
// Show success feedback
|
|
const exportBtn = document.getElementById('exportP8Button');
|
|
if (exportBtn) {
|
|
exportBtn.classList.add('is-success');
|
|
setTimeout(() => {
|
|
exportBtn.classList.remove('is-success');
|
|
}, 1500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Imports sprite data from .p8 format.
|
|
*/
|
|
#importFromP8() {
|
|
if (!this.editor) return;
|
|
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = '.p8';
|
|
|
|
input.addEventListener('change', async (e) => {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
const text = await file.text();
|
|
|
|
// Extract __gfx__ section
|
|
const gfxMatch = text.match(/__gfx__\s*\n([\s\S]*?)(?=\n__|$)/);
|
|
if (gfxMatch) {
|
|
this.editor.getSpriteSheet().fromP8Gfx(gfxMatch[1]);
|
|
}
|
|
|
|
// Extract __gff__ section
|
|
const gffMatch = text.match(/__gff__\s*\n([\s\S]*?)(?=\n__|$)/);
|
|
if (gffMatch) {
|
|
this.editor.getSpriteSheet().fromP8Flags(gffMatch[1]);
|
|
}
|
|
|
|
// Re-render
|
|
this.editor.setSpriteSheet(this.editor.getSpriteSheet());
|
|
|
|
// Show success feedback
|
|
const importBtn = document.getElementById('importP8Button');
|
|
if (importBtn) {
|
|
importBtn.classList.add('is-success');
|
|
setTimeout(() => {
|
|
importBtn.classList.remove('is-success');
|
|
}, 1500);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to import sprite data:', error);
|
|
alert('Failed to import sprite data. Please ensure the file is a valid .p8 file.');
|
|
}
|
|
});
|
|
|
|
input.click();
|
|
}
|
|
|
|
/**
|
|
* Gets the current sprite sheet data.
|
|
*
|
|
* @returns {SpriteSheet}
|
|
*/
|
|
getSpriteSheet() {
|
|
return this.spriteSheet;
|
|
}
|
|
|
|
/**
|
|
* Sets sprite sheet data.
|
|
*
|
|
* @param {SpriteSheet} spriteSheet New sprite sheet.
|
|
*/
|
|
setSpriteSheet(spriteSheet) {
|
|
this.spriteSheet = spriteSheet;
|
|
if (this.editor) {
|
|
this.editor.setSpriteSheet(spriteSheet);
|
|
}
|
|
}
|
|
}
|