deckhearth/migrations/1787700511000_add-custom-frames-symbols.js
Randall Stillwell fe1695f7ad feat(designer): custom frame editor and cost symbols
- custom_frames: per-user frames with full 9-slot palette (JSONB),
  unique names; designer frame picker lists them alongside starters,
  click to use, edit/delete via inline editor with live preview
- custom_symbols: upload cost icons (PNG/WebP/SVG, 2MB) keyed by short
  code; re-uploading a code replaces the old icon; ManaPips renders
  icon pips for {CODE} tokens with graceful text fallback
- custom_cards.custom_frame_id links designs to custom frames; API GETs
  join and nest the palette; deleting a frame falls back to starter
- migration 1787700511000
2026-08-24 21:13:12 -05:00

56 lines
1.8 KiB
JavaScript

/**
* Phase 3 — user-created frames and cost symbols.
*
* custom_frames: private per-user frames. `palette` holds the full slot
* map (outer, border, titleBar, typeBar, textBox, artBacking, text,
* titleText, accent) as JSONB so new slots stay additive.
*
* custom_symbols: user-uploaded cost icons keyed by a short code used in
* mana cost strings ({F}, {2}{F}...).
*
* custom_cards.custom_frame_id: set when a design uses a custom frame;
* NULL means the starter frame in `frame_id` applies.
*/
export const up = (pgm) => {
pgm.sql(`
CREATE TABLE IF NOT EXISTS custom_frames (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
palette JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_custom_frames_user_name UNIQUE (user_id, name)
)
`);
pgm.sql(`
CREATE TABLE IF NOT EXISTS custom_symbols (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
code VARCHAR(10) NOT NULL,
image_url TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_custom_symbols_user_code UNIQUE (user_id, code)
)
`);
pgm.sql(`
ALTER TABLE custom_cards
ADD COLUMN IF NOT EXISTS custom_frame_id INTEGER REFERENCES custom_frames(id) ON DELETE SET NULL
`);
pgm.sql(`
CREATE INDEX IF NOT EXISTS idx_custom_cards_custom_frame_id
ON custom_cards(custom_frame_id)
`);
};
export const down = (pgm) => {
pgm.sql(`
ALTER TABLE custom_cards DROP COLUMN IF EXISTS custom_frame_id
`);
pgm.sql(`DROP TABLE IF EXISTS custom_symbols CASCADE`);
pgm.sql(`DROP TABLE IF EXISTS custom_frames CASCADE`);
};