/** * 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`); };