Update index.html
This commit is contained in:
887
index.html
887
index.html
@@ -1,448 +1,453 @@
|
|||||||
import { useState, useRef, useCallback } from "react";
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MTG Proxy Maker</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #0e0b07;
|
||||||
|
font-family: 'Palatino Linotype', Palatino, 'Book Antiqua', serif;
|
||||||
|
color: #e8dfc8;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
background: linear-gradient(90deg,#1a0f00,#2e1a00,#1a0f00);
|
||||||
|
border-bottom: 2px solid #5a3a10;
|
||||||
|
padding: 16px 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
header .logo { font-size: 28px; }
|
||||||
|
header h1 { font-size: 20px; font-weight: 700; color: #d4af37; letter-spacing: 1px; }
|
||||||
|
header p { font-size: 12px; color: #8a7060; }
|
||||||
|
#printBtn {
|
||||||
|
margin-left: auto;
|
||||||
|
background: linear-gradient(135deg,#8b6010,#d4af37,#8b6010);
|
||||||
|
color: #1a0f00;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 9px 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
box-shadow: 0 2px 8px #d4af3744;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 32px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
.editor {
|
||||||
|
flex: 1 1 360px;
|
||||||
|
max-width: 480px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
border-bottom: 1px solid #3a3020;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: #8a7060;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: inherit;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
.tab.active { background: #3a2a10; border-color: #d4af37; color: #d4af37; font-weight: 700; }
|
||||||
|
.tab-panel { display: none; flex-direction: column; gap: 12px; }
|
||||||
|
.tab-panel.active { display: flex; }
|
||||||
|
.field { display: flex; flex-direction: column; gap: 4px; }
|
||||||
|
.field label { font-size: 11px; font-weight: 600; color: #8a7a6a; letter-spacing: 0.5px; text-transform: uppercase; }
|
||||||
|
input, textarea, select {
|
||||||
|
background: #1e1a14; border: 1px solid #3a3020; border-radius: 6px;
|
||||||
|
color: #e8dfc8; padding: 7px 10px; font-size: 13px; font-family: inherit;
|
||||||
|
outline: none; width: 100%;
|
||||||
|
}
|
||||||
|
input:focus, textarea:focus, select:focus { border-color: #d4af37; box-shadow: 0 0 0 2px #d4af3733; }
|
||||||
|
select option { background: #1e1a14; color: #e8dfc8; }
|
||||||
|
textarea { resize: vertical; }
|
||||||
|
.row { display: flex; gap: 10px; }
|
||||||
|
.row .field { flex: 1; }
|
||||||
|
.color-btns { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||||
|
.color-btn {
|
||||||
|
width: 36px; height: 36px; border-radius: 50%; border: 2px solid transparent;
|
||||||
|
cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center;
|
||||||
|
}
|
||||||
|
.color-btn.active { border: 3px solid #d4af37 !important; box-shadow: 0 0 8px #d4af37; }
|
||||||
|
.upload-btn {
|
||||||
|
background: #2a1e0e; border: 2px dashed #5a3a10; border-radius: 6px;
|
||||||
|
color: #e8dfc8; padding: 20px; font-size: 14px; font-family: inherit;
|
||||||
|
cursor: pointer; width: 100%; text-align: center;
|
||||||
|
}
|
||||||
|
.url-row { display: flex; gap: 6px; }
|
||||||
|
.url-row input { flex: 1; }
|
||||||
|
.set-btn {
|
||||||
|
background: #3a2a10; border: 1px solid #d4af37; color: #d4af37;
|
||||||
|
border-radius: 6px; padding: 0 12px; cursor: pointer; font-family: inherit; font-size: 13px;
|
||||||
|
}
|
||||||
|
#artPreview { display: none; flex-direction: column; gap: 6px; }
|
||||||
|
#artPreview img { width: 100%; border-radius: 8px; border: 1px solid #3a3020; max-height: 180px; object-fit: cover; }
|
||||||
|
.remove-btn {
|
||||||
|
background: transparent; border: 1px solid #5a3020; color: #c06040;
|
||||||
|
border-radius: 6px; padding: 4px 12px; cursor: pointer; font-size: 12px; font-family: inherit; width: 100%;
|
||||||
|
}
|
||||||
|
.reset-btn {
|
||||||
|
background: #2a1e0e; border: 1px solid #5a3a10; color: #c8a060;
|
||||||
|
border-radius: 6px; padding: 7px 16px; cursor: pointer; font-family: inherit; font-size: 13px;
|
||||||
|
}
|
||||||
|
.divider { border-top: 1px solid #3a3020; padding-top: 12px; }
|
||||||
|
.divider-label { font-size: 12px; color: #8a7060; margin-bottom: 8px; }
|
||||||
|
.no-stats { font-size: 13px; color: #8a7060; font-style: italic; padding: 12px 0; }
|
||||||
|
.or-divider { color: #8a7060; font-size: 12px; text-align: center; }
|
||||||
|
.preview-col { flex: 1 1 300px; display: flex; flex-direction: column; align-items: center; gap: 20px; }
|
||||||
|
.preview-label { font-size: 12px; color: #8a7060; letter-spacing: 1px; text-transform: uppercase; }
|
||||||
|
.preview-hint { font-size: 11px; color: #5a4a30; text-align: center; max-width: 280px; line-height: 1.6; }
|
||||||
|
|
||||||
const CARD_TYPES = ["Creature", "Instant", "Sorcery", "Enchantment", "Artifact", "Land", "Planeswalker", "Battle"];
|
/* Card styles */
|
||||||
const COLORS = [
|
#card {
|
||||||
{ id: "W", label: "White", symbol: "☀", bg: "#f9f6d5", border: "#c8b87a", text: "#333" },
|
width: 280px; min-height: 390px; border-radius: 16px; border: 4px solid #c8b87a;
|
||||||
{ id: "U", label: "Blue", symbol: "💧", bg: "#c6d9e8", border: "#3a6b9c", text: "#fff" },
|
background: #f9f6d5; box-shadow: 0 0 0 2px #111, 0 8px 32px #0008;
|
||||||
{ id: "B", label: "Black", symbol: "💀", bg: "#1a1a1a", border: "#5a4a6e", text: "#d0c8e0" },
|
display: flex; flex-direction: column; overflow: hidden; position: relative;
|
||||||
{ id: "R", label: "Red", symbol: "🔥", bg: "#e87040", border: "#8b2500", text: "#fff" },
|
color: #333;
|
||||||
{ id: "G", label: "Green", symbol: "🌲", bg: "#3a7a48", border: "#1a4025", text: "#d4f5d0" },
|
}
|
||||||
{ id: "C", label: "Colorless", symbol: "◇", bg: "#c0bdb5", border: "#7a7068", text: "#333" },
|
#cardHeader {
|
||||||
{ id: "M", label: "Multi", symbol: "★", bg: "linear-gradient(135deg,#f9f6d5,#c6d9e8,#1a1a1a,#e87040,#3a7a48)", border: "#b8960c", text: "#fff" },
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
];
|
padding: 7px 10px 4px; border-bottom: 2px solid #c8b87a; background: #c8b87acc;
|
||||||
|
}
|
||||||
|
#cardName { font-weight: 700; font-size: 15px; letter-spacing: 0.3px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
#cardMana { font-size: 13px; font-family: monospace; margin-left: 8px; white-space: nowrap; letter-spacing: 1px; }
|
||||||
|
#artFrame {
|
||||||
|
margin: 6px 8px; height: 140px; border-radius: 8px; border: 2px solid #c8b87a;
|
||||||
|
overflow: hidden; display: flex; align-items: center; justify-content: center;
|
||||||
|
position: relative; background: linear-gradient(135deg,#c8b87a44,#c8b87a99);
|
||||||
|
}
|
||||||
|
#artFrame img { width: 100%; height: 100%; object-fit: cover; }
|
||||||
|
#artPlaceholder { opacity: 0.4; font-size: 48px; }
|
||||||
|
#setRarity { position: absolute; bottom: 4px; right: 6px; font-size: 11px; font-weight: 700; text-shadow: 0 1px 3px #000a; }
|
||||||
|
#typeLine {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 3px 10px; font-size: 12px; font-style: italic;
|
||||||
|
border-top: 1px solid #c8b87a66; border-bottom: 1px solid #c8b87a66; background: #c8b87a99;
|
||||||
|
}
|
||||||
|
#textBox {
|
||||||
|
flex: 1; margin: 5px 8px; background: #f5f0e044; border-radius: 6px;
|
||||||
|
border: 1px solid #c8b87a55; padding: 7px 9px; min-height: 80px;
|
||||||
|
display: flex; flex-direction: column; gap: 6px;
|
||||||
|
}
|
||||||
|
#rulesDisplay { font-size: 12px; line-height: 1.45; white-space: pre-wrap; }
|
||||||
|
#flavorDisplay {
|
||||||
|
font-size: 11px; font-style: italic; opacity: 0.75;
|
||||||
|
border-top: 1px solid #c8b87a44; padding-top: 5px; line-height: 1.35; display: none;
|
||||||
|
}
|
||||||
|
#cardFooter {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 4px 10px 6px; font-size: 10px; opacity: 0.7; border-top: 1px solid #c8b87a44;
|
||||||
|
}
|
||||||
|
#ptBox {
|
||||||
|
background: #c8b87a; color: #fff; border-radius: 4px; padding: 1px 7px;
|
||||||
|
font-weight: 700; font-size: 13px; box-shadow: 0 1px 4px #0006; display: none;
|
||||||
|
}
|
||||||
|
#loyaltyBox {
|
||||||
|
background: #1a5ab8; color: #fff; border-radius: 50%; width: 26px; height: 26px;
|
||||||
|
display: none; align-items: center; justify-content: center;
|
||||||
|
font-weight: 700; font-size: 14px; box-shadow: 0 1px 4px #0006;
|
||||||
|
}
|
||||||
|
|
||||||
const RARITY_COLORS = { Common: "#aaa", Uncommon: "#a0c0d0", Rare: "#d4af37", "Mythic Rare": "#e07020" };
|
|
||||||
|
|
||||||
const DEFAULT_CARD = {
|
|
||||||
name: "Blazing Champion",
|
|
||||||
manaCost: "{2}{R}{R}",
|
|
||||||
type: "Creature",
|
|
||||||
subtype: "Human Warrior",
|
|
||||||
color: "R",
|
|
||||||
rarity: "Rare",
|
|
||||||
power: "4",
|
|
||||||
toughness: "3",
|
|
||||||
rulesText: "Haste\nWhen Blazing Champion enters the battlefield, it deals 2 damage to any target.",
|
|
||||||
flavorText: "\"No retreat. No mercy. Only fire.\"",
|
|
||||||
artist: "Proxy Artist",
|
|
||||||
setSymbol: "★",
|
|
||||||
loyalty: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
function CardPreview({ card, imageUrl }) {
|
|
||||||
const colorObj = COLORS.find(c => c.id === card.color) || COLORS[5];
|
|
||||||
const isMulti = card.color === "M";
|
|
||||||
const isLand = card.type === "Land";
|
|
||||||
const isPW = card.type === "Planeswalker";
|
|
||||||
|
|
||||||
const frameBg = isMulti
|
|
||||||
? "linear-gradient(160deg,#f9f6d5 0%,#c6d9e8 25%,#1a1a1a 50%,#e87040 75%,#3a7a48 100%)"
|
|
||||||
: colorObj.bg;
|
|
||||||
|
|
||||||
const rarityColor = RARITY_COLORS[card.rarity] || "#aaa";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
width: 280,
|
|
||||||
minHeight: 390,
|
|
||||||
borderRadius: 16,
|
|
||||||
border: `4px solid ${colorObj.border}`,
|
|
||||||
background: frameBg,
|
|
||||||
boxShadow: `0 0 0 2px #111, 0 0 30px ${colorObj.border}55, 0 8px 32px #0008`,
|
|
||||||
fontFamily: "'Palatino Linotype', Palatino, 'Book Antiqua', serif",
|
|
||||||
color: colorObj.text,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
overflow: "hidden",
|
|
||||||
position: "relative",
|
|
||||||
userSelect: "none",
|
|
||||||
}}>
|
|
||||||
{/* Header */}
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "7px 10px 4px",
|
|
||||||
background: `${colorObj.border}cc`,
|
|
||||||
borderBottom: `2px solid ${colorObj.border}`,
|
|
||||||
}}>
|
|
||||||
<div style={{ fontWeight: 700, fontSize: 15, letterSpacing: 0.3, textShadow: "0 1px 2px #0005", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
|
||||||
{card.name || "Card Name"}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 13, fontFamily: "monospace", marginLeft: 8, whiteSpace: "nowrap", opacity: 0.95, letterSpacing: 1 }}>
|
|
||||||
{card.manaCost || ""}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Art Frame */}
|
|
||||||
<div style={{
|
|
||||||
margin: "6px 8px",
|
|
||||||
height: 140,
|
|
||||||
borderRadius: 8,
|
|
||||||
border: `2px solid ${colorObj.border}`,
|
|
||||||
overflow: "hidden",
|
|
||||||
background: imageUrl ? "transparent" : `linear-gradient(135deg, ${colorObj.border}44, ${colorObj.border}99)`,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
position: "relative",
|
|
||||||
}}>
|
|
||||||
{imageUrl ? (
|
|
||||||
<img src={imageUrl} alt="card art" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
|
|
||||||
) : (
|
|
||||||
<div style={{ opacity: 0.4, fontSize: 48, textAlign: "center" }}>
|
|
||||||
{colorObj.symbol}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* Set & rarity */}
|
|
||||||
<div style={{
|
|
||||||
position: "absolute", bottom: 4, right: 6,
|
|
||||||
fontSize: 11, color: rarityColor, textShadow: "0 1px 3px #000a",
|
|
||||||
fontWeight: 700,
|
|
||||||
}}>
|
|
||||||
{card.setSymbol || "★"} {card.rarity ? card.rarity[0] : ""}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Type Line */}
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "3px 10px",
|
|
||||||
background: `${colorObj.border}99`,
|
|
||||||
fontSize: 12,
|
|
||||||
fontStyle: "italic",
|
|
||||||
borderTop: `1px solid ${colorObj.border}66`,
|
|
||||||
borderBottom: `1px solid ${colorObj.border}66`,
|
|
||||||
}}>
|
|
||||||
<span>{[card.type, card.subtype].filter(Boolean).join(" — ") || "Type"}</span>
|
|
||||||
<span style={{ fontSize: 14 }}>{card.setSymbol || "★"}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Text Box */}
|
|
||||||
<div style={{
|
|
||||||
flex: 1,
|
|
||||||
margin: "5px 8px 5px",
|
|
||||||
background: "#f5f0e044",
|
|
||||||
borderRadius: 6,
|
|
||||||
border: `1px solid ${colorObj.border}55`,
|
|
||||||
padding: "7px 9px",
|
|
||||||
minHeight: 80,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: 6,
|
|
||||||
}}>
|
|
||||||
<div style={{ fontSize: 12, lineHeight: 1.45, whiteSpace: "pre-wrap" }}>
|
|
||||||
{card.rulesText || ""}
|
|
||||||
</div>
|
|
||||||
{card.flavorText && (
|
|
||||||
<div style={{ fontSize: 11, fontStyle: "italic", opacity: 0.75, borderTop: `1px solid ${colorObj.border}44`, paddingTop: 5, lineHeight: 1.35 }}>
|
|
||||||
{card.flavorText}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "4px 10px 6px",
|
|
||||||
fontSize: 10,
|
|
||||||
opacity: 0.7,
|
|
||||||
borderTop: `1px solid ${colorObj.border}44`,
|
|
||||||
}}>
|
|
||||||
<span>✦ {card.artist || "Unknown"}</span>
|
|
||||||
{(card.type === "Creature" || card.type === "Battle") && (
|
|
||||||
<div style={{
|
|
||||||
background: colorObj.border,
|
|
||||||
color: "#fff",
|
|
||||||
borderRadius: 4,
|
|
||||||
padding: "1px 7px",
|
|
||||||
fontWeight: 700,
|
|
||||||
fontSize: 13,
|
|
||||||
boxShadow: "0 1px 4px #0006",
|
|
||||||
}}>
|
|
||||||
{card.power}/{card.toughness}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isPW && card.loyalty && (
|
|
||||||
<div style={{
|
|
||||||
background: "#1a5ab8",
|
|
||||||
color: "#fff",
|
|
||||||
borderRadius: "50%",
|
|
||||||
width: 26, height: 26,
|
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
|
||||||
fontWeight: 700, fontSize: 14,
|
|
||||||
boxShadow: "0 1px 4px #0006",
|
|
||||||
}}>
|
|
||||||
{card.loyalty}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Field({ label, children }) {
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
|
||||||
<label style={{ fontSize: 11, fontWeight: 600, color: "#8a7a6a", letterSpacing: 0.5, textTransform: "uppercase" }}>{label}</label>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputStyle = {
|
|
||||||
background: "#1e1a14",
|
|
||||||
border: "1px solid #3a3020",
|
|
||||||
borderRadius: 6,
|
|
||||||
color: "#e8dfc8",
|
|
||||||
padding: "7px 10px",
|
|
||||||
fontSize: 13,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
outline: "none",
|
|
||||||
width: "100%",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
transition: "border-color 0.2s",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [card, setCard] = useState(DEFAULT_CARD);
|
|
||||||
const [imageUrl, setImageUrl] = useState("");
|
|
||||||
const [imageInput, setImageInput] = useState("");
|
|
||||||
const [tab, setTab] = useState("basic");
|
|
||||||
const previewRef = useRef();
|
|
||||||
const fileInputRef = useRef();
|
|
||||||
|
|
||||||
const update = useCallback((field, val) => setCard(c => ({ ...c, [field]: val })), []);
|
|
||||||
|
|
||||||
const handleImageFile = (e) => {
|
|
||||||
const file = e.target.files?.[0];
|
|
||||||
if (!file) return;
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = (ev) => setImageUrl(ev.target.result);
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleImageUrl = () => {
|
|
||||||
setImageUrl(imageInput.trim());
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePrint = () => window.print();
|
|
||||||
|
|
||||||
const isPW = card.type === "Planeswalker";
|
|
||||||
const isCreature = card.type === "Creature" || card.type === "Battle";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
minHeight: "100vh",
|
|
||||||
background: "#0e0b07",
|
|
||||||
fontFamily: "'Palatino Linotype', Palatino, 'Book Antiqua', serif",
|
|
||||||
color: "#e8dfc8",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}>
|
|
||||||
{/* Header */}
|
|
||||||
<div style={{
|
|
||||||
background: "linear-gradient(90deg,#1a0f00,#2e1a00,#1a0f00)",
|
|
||||||
borderBottom: "2px solid #5a3a10",
|
|
||||||
padding: "18px 28px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 14,
|
|
||||||
}}>
|
|
||||||
<div style={{ fontSize: 30 }}>🃏</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 22, fontWeight: 700, letterSpacing: 1, color: "#d4af37" }}>MTG Proxy Maker</div>
|
|
||||||
<div style={{ fontSize: 12, color: "#8a7060", letterSpacing: 0.3 }}>Design · Print · Play</div>
|
|
||||||
</div>
|
|
||||||
<button onClick={handlePrint} style={{
|
|
||||||
marginLeft: "auto",
|
|
||||||
background: "linear-gradient(135deg,#8b6010,#d4af37,#8b6010)",
|
|
||||||
color: "#1a0f00",
|
|
||||||
border: "none",
|
|
||||||
borderRadius: 8,
|
|
||||||
padding: "9px 22px",
|
|
||||||
fontWeight: 700,
|
|
||||||
fontSize: 14,
|
|
||||||
cursor: "pointer",
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
boxShadow: "0 2px 8px #d4af3744",
|
|
||||||
}}>
|
|
||||||
🖨 Print
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", flex: 1, padding: "24px 28px", gap: 32, flexWrap: "wrap" }}>
|
|
||||||
{/* Editor Panel */}
|
|
||||||
<div style={{ flex: "1 1 360px", minWidth: 300, maxWidth: 480, display: "flex", flexDirection: "column", gap: 16 }}>
|
|
||||||
{/* Tabs */}
|
|
||||||
<div style={{ display: "flex", gap: 4, borderBottom: "1px solid #3a3020", paddingBottom: 4 }}>
|
|
||||||
{["basic", "art", "stats"].map(t => (
|
|
||||||
<button key={t} onClick={() => setTab(t)} style={{
|
|
||||||
background: tab === t ? "#3a2a10" : "transparent",
|
|
||||||
border: tab === t ? "1px solid #d4af37" : "1px solid transparent",
|
|
||||||
color: tab === t ? "#d4af37" : "#8a7060",
|
|
||||||
borderRadius: 6,
|
|
||||||
padding: "5px 14px",
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: 13,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
fontWeight: tab === t ? 700 : 400,
|
|
||||||
textTransform: "capitalize",
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
}}>{t}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
||||||
{tab === "basic" && <>
|
|
||||||
<Field label="Card Name">
|
|
||||||
<input style={inputStyle} value={card.name} onChange={e => update("name", e.target.value)} placeholder="e.g. Blazing Champion" />
|
|
||||||
</Field>
|
|
||||||
<Field label="Mana Cost">
|
|
||||||
<input style={inputStyle} value={card.manaCost} onChange={e => update("manaCost", e.target.value)} placeholder="{2}{R}{R}" />
|
|
||||||
</Field>
|
|
||||||
<div style={{ display: "flex", gap: 10 }}>
|
|
||||||
<Field label="Type">
|
|
||||||
<select style={{ ...inputStyle }} value={card.type} onChange={e => update("type", e.target.value)}>
|
|
||||||
{CARD_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
|
|
||||||
</select>
|
|
||||||
</Field>
|
|
||||||
<Field label="Subtype">
|
|
||||||
<input style={inputStyle} value={card.subtype} onChange={e => update("subtype", e.target.value)} placeholder="e.g. Wizard" />
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
<Field label="Color">
|
|
||||||
<div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
|
|
||||||
{COLORS.map(c => (
|
|
||||||
<button key={c.id} onClick={() => update("color", c.id)} style={{
|
|
||||||
width: 36, height: 36, borderRadius: "50%",
|
|
||||||
background: c.id === "M" ? "linear-gradient(135deg,gold,blue,black)" : c.bg,
|
|
||||||
border: card.color === c.id ? "3px solid #d4af37" : `2px solid ${c.border}`,
|
|
||||||
cursor: "pointer",
|
|
||||||
fontWeight: 700, fontSize: 15,
|
|
||||||
boxShadow: card.color === c.id ? "0 0 8px #d4af37" : "none",
|
|
||||||
transition: "all 0.15s",
|
|
||||||
}} title={c.label}>{c.symbol}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Field>
|
|
||||||
<Field label="Rarity">
|
|
||||||
<select style={{ ...inputStyle }} value={card.rarity} onChange={e => update("rarity", e.target.value)}>
|
|
||||||
{Object.keys(RARITY_COLORS).map(r => <option key={r} value={r}>{r}</option>)}
|
|
||||||
</select>
|
|
||||||
</Field>
|
|
||||||
<Field label="Rules Text">
|
|
||||||
<textarea style={{ ...inputStyle, minHeight: 80, resize: "vertical" }} value={card.rulesText} onChange={e => update("rulesText", e.target.value)} placeholder="Card abilities..." />
|
|
||||||
</Field>
|
|
||||||
<Field label="Flavor Text">
|
|
||||||
<textarea style={{ ...inputStyle, minHeight: 50, resize: "vertical", fontStyle: "italic" }} value={card.flavorText} onChange={e => update("flavorText", e.target.value)} placeholder='"A whisper in the dark..."' />
|
|
||||||
</Field>
|
|
||||||
<Field label="Artist">
|
|
||||||
<input style={inputStyle} value={card.artist} onChange={e => update("artist", e.target.value)} placeholder="Artist name" />
|
|
||||||
</Field>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
{tab === "art" && <>
|
|
||||||
<Field label="Upload Card Art">
|
|
||||||
<input ref={fileInputRef} type="file" accept="image/*" onChange={handleImageFile} style={{ display: "none" }} />
|
|
||||||
<button onClick={() => fileInputRef.current?.click()} style={{
|
|
||||||
...inputStyle, cursor: "pointer", textAlign: "center", background: "#2a1e0e",
|
|
||||||
border: "2px dashed #5a3a10", padding: "20px", fontSize: 14,
|
|
||||||
}}>
|
|
||||||
📁 Click to upload image
|
|
||||||
</button>
|
|
||||||
</Field>
|
|
||||||
<div style={{ color: "#8a7060", fontSize: 12, textAlign: "center" }}>— or paste an image URL —</div>
|
|
||||||
<Field label="Image URL">
|
|
||||||
<div style={{ display: "flex", gap: 6 }}>
|
|
||||||
<input style={{ ...inputStyle, flex: 1 }} value={imageInput} onChange={e => setImageInput(e.target.value)} placeholder="https://..." />
|
|
||||||
<button onClick={handleImageUrl} style={{
|
|
||||||
background: "#3a2a10", border: "1px solid #d4af37", color: "#d4af37",
|
|
||||||
borderRadius: 6, padding: "0 12px", cursor: "pointer", fontFamily: "inherit", fontSize: 13,
|
|
||||||
}}>Set</button>
|
|
||||||
</div>
|
|
||||||
</Field>
|
|
||||||
{imageUrl && (
|
|
||||||
<div>
|
|
||||||
<img src={imageUrl} alt="preview" style={{ width: "100%", borderRadius: 8, border: "1px solid #3a3020", maxHeight: 180, objectFit: "cover" }} />
|
|
||||||
<button onClick={() => { setImageUrl(""); setImageInput(""); }} style={{
|
|
||||||
marginTop: 6, background: "transparent", border: "1px solid #5a3020", color: "#c06040",
|
|
||||||
borderRadius: 6, padding: "4px 12px", cursor: "pointer", fontSize: 12, fontFamily: "inherit", width: "100%",
|
|
||||||
}}>Remove Image</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Field label="Set Symbol">
|
|
||||||
<input style={inputStyle} value={card.setSymbol} onChange={e => update("setSymbol", e.target.value)} placeholder="★ or set code" maxLength={4} />
|
|
||||||
</Field>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
{tab === "stats" && <>
|
|
||||||
{isCreature && <>
|
|
||||||
<div style={{ display: "flex", gap: 10 }}>
|
|
||||||
<Field label="Power">
|
|
||||||
<input style={inputStyle} value={card.power} onChange={e => update("power", e.target.value)} placeholder="4" />
|
|
||||||
</Field>
|
|
||||||
<Field label="Toughness">
|
|
||||||
<input style={inputStyle} value={card.toughness} onChange={e => update("toughness", e.target.value)} placeholder="3" />
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
{isPW && (
|
|
||||||
<Field label="Starting Loyalty">
|
|
||||||
<input style={inputStyle} value={card.loyalty} onChange={e => update("loyalty", e.target.value)} placeholder="4" />
|
|
||||||
</Field>
|
|
||||||
)}
|
|
||||||
{!isCreature && !isPW && (
|
|
||||||
<div style={{ color: "#8a7060", fontSize: 13, fontStyle: "italic", padding: "12px 0" }}>
|
|
||||||
No stats for this card type. Switch to Creature or Planeswalker to edit stats.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div style={{ borderTop: "1px solid #3a3020", paddingTop: 12 }}>
|
|
||||||
<div style={{ fontSize: 12, color: "#8a7060", marginBottom: 8 }}>Quick Reset</div>
|
|
||||||
<button onClick={() => setCard(DEFAULT_CARD)} style={{
|
|
||||||
background: "#2a1e0e", border: "1px solid #5a3a10", color: "#c8a060",
|
|
||||||
borderRadius: 6, padding: "7px 16px", cursor: "pointer", fontFamily: "inherit", fontSize: 13,
|
|
||||||
}}>↩ Reset to Default</button>
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Preview */}
|
|
||||||
<div style={{ flex: "1 1 300px", display: "flex", flexDirection: "column", alignItems: "center", gap: 20 }}>
|
|
||||||
<div style={{ fontSize: 12, color: "#8a7060", letterSpacing: 1, textTransform: "uppercase" }}>Preview</div>
|
|
||||||
<div ref={previewRef} id="card-preview" style={{ transform: "scale(1)", transformOrigin: "top center" }}>
|
|
||||||
<CardPreview card={card} imageUrl={imageUrl} />
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 11, color: "#5a4a30", textAlign: "center", maxWidth: 280 }}>
|
|
||||||
Use the Print button to print on card stock.<br />
|
|
||||||
Cut to 2.5" × 3.5" for standard size.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>{`
|
|
||||||
@media print {
|
@media print {
|
||||||
body * { visibility: hidden; }
|
body * { visibility: hidden; }
|
||||||
#card-preview, #card-preview * { visibility: visible; }
|
#card, #card * { visibility: visible; }
|
||||||
#card-preview {
|
#card { position: fixed; left: 50%; top: 50%; transform: translate(-50%,-50%) scale(2.5); }
|
||||||
position: fixed; left: 50%; top: 50%;
|
|
||||||
transform: translate(-50%,-50%) scale(2.5);
|
|
||||||
transform-origin: center center;
|
|
||||||
}
|
}
|
||||||
}
|
</style>
|
||||||
input:focus, textarea:focus, select:focus {
|
</head>
|
||||||
border-color: #d4af37 !important;
|
<body>
|
||||||
box-shadow: 0 0 0 2px #d4af3733;
|
|
||||||
}
|
<header>
|
||||||
select option { background: #1e1a14; color: #e8dfc8; }
|
<div class="logo">🃏</div>
|
||||||
* { box-sizing: border-box; }
|
<div>
|
||||||
`}</style>
|
<h1>MTG Proxy Maker</h1>
|
||||||
|
<p>Design · Print · Play</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
<button id="printBtn" onclick="window.print()">🖨 Print</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="main">
|
||||||
|
<div class="editor">
|
||||||
|
<div class="tabs">
|
||||||
|
<button class="tab active" onclick="switchTab('basic',this)">Basic</button>
|
||||||
|
<button class="tab" onclick="switchTab('art',this)">Art</button>
|
||||||
|
<button class="tab" onclick="switchTab('stats',this)">Stats</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel active" id="tab-basic">
|
||||||
|
<div class="field"><label>Card Name</label><input id="f-name" value="Blazing Champion" oninput="update()"></div>
|
||||||
|
<div class="field"><label>Mana Cost</label><input id="f-mana" value="{2}{R}{R}" oninput="update()"></div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="field"><label>Type</label>
|
||||||
|
<select id="f-type" onchange="update()">
|
||||||
|
<option>Creature</option><option>Instant</option><option>Sorcery</option>
|
||||||
|
<option>Enchantment</option><option>Artifact</option><option>Land</option>
|
||||||
|
<option>Planeswalker</option><option>Battle</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Subtype</label><input id="f-subtype" value="Human Warrior" oninput="update()"></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Color</label>
|
||||||
|
<div class="color-btns">
|
||||||
|
<button class="color-btn" data-color="W" style="background:#f9f6d5" onclick="setColor('W')" title="White">☀</button>
|
||||||
|
<button class="color-btn" data-color="U" style="background:#c6d9e8" onclick="setColor('U')" title="Blue">💧</button>
|
||||||
|
<button class="color-btn" data-color="B" style="background:#1a1a1a;color:#d0c8e0" onclick="setColor('B')" title="Black">💀</button>
|
||||||
|
<button class="color-btn active" data-color="R" style="background:#e87040" onclick="setColor('R')" title="Red">🔥</button>
|
||||||
|
<button class="color-btn" data-color="G" style="background:#3a7a48;color:#d4f5d0" onclick="setColor('G')" title="Green">🌲</button>
|
||||||
|
<button class="color-btn" data-color="C" style="background:#c0bdb5" onclick="setColor('C')" title="Colorless">◇</button>
|
||||||
|
<button class="color-btn" data-color="M" style="background:linear-gradient(135deg,gold,blue,black)" onclick="setColor('M')" title="Multi">★</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Rarity</label>
|
||||||
|
<select id="f-rarity" onchange="update()">
|
||||||
|
<option>Common</option><option>Uncommon</option><option selected>Rare</option><option>Mythic Rare</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Rules Text</label>
|
||||||
|
<textarea id="f-rules" rows="4" oninput="update()">Haste
|
||||||
|
When Blazing Champion enters the battlefield, it deals 2 damage to any target.</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Flavor Text</label>
|
||||||
|
<textarea id="f-flavor" rows="2" oninput="update()" style="font-style:italic">"No retreat. No mercy. Only fire."</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Artist</label><input id="f-artist" value="Proxy Artist" oninput="update()"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel" id="tab-art">
|
||||||
|
<div class="field">
|
||||||
|
<label>Upload Card Art</label>
|
||||||
|
<input type="file" id="fileInput" accept="image/*" style="display:none" onchange="handleFile(event)">
|
||||||
|
<button class="upload-btn" onclick="document.getElementById('fileInput').click()">📁 Click to upload image</button>
|
||||||
|
</div>
|
||||||
|
<div class="or-divider">— or paste an image URL —</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Image URL</label>
|
||||||
|
<div class="url-row">
|
||||||
|
<input id="urlInput" placeholder="https://...">
|
||||||
|
<button class="set-btn" onclick="setImageUrl()">Set</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="artPreview">
|
||||||
|
<img id="artThumb" src="" alt="preview">
|
||||||
|
<button class="remove-btn" onclick="removeImage()">Remove Image</button>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>Set Symbol</label><input id="f-set" value="★" oninput="update()" maxlength="4"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel" id="tab-stats">
|
||||||
|
<div id="creatureStats">
|
||||||
|
<div class="row">
|
||||||
|
<div class="field"><label>Power</label><input id="f-power" value="4" oninput="update()"></div>
|
||||||
|
<div class="field"><label>Toughness</label><input id="f-toughness" value="3" oninput="update()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="pwStats" style="display:none">
|
||||||
|
<div class="field"><label>Starting Loyalty</label><input id="f-loyalty" value="4" oninput="update()"></div>
|
||||||
|
</div>
|
||||||
|
<div id="noStats" class="no-stats" style="display:none">No stats for this card type.</div>
|
||||||
|
<div class="divider">
|
||||||
|
<div class="divider-label">Quick Reset</div>
|
||||||
|
<button class="reset-btn" onclick="resetCard()">↩ Reset to Default</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-col">
|
||||||
|
<div class="preview-label">Preview</div>
|
||||||
|
<div id="card">
|
||||||
|
<div id="cardHeader">
|
||||||
|
<div id="cardName">Blazing Champion</div>
|
||||||
|
<div id="cardMana">{2}{R}{R}</div>
|
||||||
|
</div>
|
||||||
|
<div id="artFrame">
|
||||||
|
<div id="artPlaceholder">🔥</div>
|
||||||
|
<img id="cardArt" src="" alt="art" style="display:none;width:100%;height:100%;object-fit:cover">
|
||||||
|
<div id="setRarity" style="color:#d4af37">★ R</div>
|
||||||
|
</div>
|
||||||
|
<div id="typeLine">
|
||||||
|
<span id="typeDisplay">Creature — Human Warrior</span>
|
||||||
|
<span id="setDisplay">★</span>
|
||||||
|
</div>
|
||||||
|
<div id="textBox">
|
||||||
|
<div id="rulesDisplay"></div>
|
||||||
|
<div id="flavorDisplay"></div>
|
||||||
|
</div>
|
||||||
|
<div id="cardFooter">
|
||||||
|
<span id="artistDisplay">✦ Proxy Artist</span>
|
||||||
|
<div id="ptBox">4/3</div>
|
||||||
|
<div id="loyaltyBox" style="display:none">4</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview-hint">Use the Print button to print on card stock.<br>Cut to 2.5" × 3.5" for standard size.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const COLORS = {
|
||||||
|
W: { bg:'#f9f6d5', border:'#c8b87a', text:'#333', symbol:'☀' },
|
||||||
|
U: { bg:'#c6d9e8', border:'#3a6b9c', text:'#fff', symbol:'💧' },
|
||||||
|
B: { bg:'#1a1a1a', border:'#5a4a6e', text:'#d0c8e0', symbol:'💀' },
|
||||||
|
R: { bg:'#e87040', border:'#8b2500', text:'#fff', symbol:'🔥' },
|
||||||
|
G: { bg:'#3a7a48', border:'#1a4025', text:'#d4f5d0', symbol:'🌲' },
|
||||||
|
C: { bg:'#c0bdb5', border:'#7a7068', text:'#333', symbol:'◇' },
|
||||||
|
M: { bg:'linear-gradient(160deg,#f9f6d5,#c6d9e8,#1a1a1a,#e87040,#3a7a48)', border:'#b8960c', text:'#fff', symbol:'★' },
|
||||||
|
};
|
||||||
|
const RARITY_COLORS = { Common:'#aaa', Uncommon:'#a0c0d0', Rare:'#d4af37', 'Mythic Rare':'#e07020' };
|
||||||
|
let currentColor = 'R';
|
||||||
|
let currentImageUrl = '';
|
||||||
|
|
||||||
|
function setColor(c) {
|
||||||
|
currentColor = c;
|
||||||
|
document.querySelectorAll('.color-btn').forEach(b => b.classList.remove('active'));
|
||||||
|
document.querySelector('[data-color="'+c+'"]').classList.add('active');
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const col = COLORS[currentColor];
|
||||||
|
const type = document.getElementById('f-type').value;
|
||||||
|
const rarity = document.getElementById('f-rarity').value;
|
||||||
|
const sym = document.getElementById('f-set').value || '★';
|
||||||
|
|
||||||
|
const card = document.getElementById('card');
|
||||||
|
card.style.borderColor = col.border;
|
||||||
|
card.style.background = col.bg;
|
||||||
|
card.style.color = col.text;
|
||||||
|
card.style.boxShadow = '0 0 0 2px #111, 0 0 30px '+col.border+'55, 0 8px 32px #0008';
|
||||||
|
|
||||||
|
const hdr = document.getElementById('cardHeader');
|
||||||
|
hdr.style.background = col.border+'cc';
|
||||||
|
hdr.style.borderBottomColor = col.border;
|
||||||
|
document.getElementById('cardName').textContent = document.getElementById('f-name').value || 'Card Name';
|
||||||
|
document.getElementById('cardMana').textContent = document.getElementById('f-mana').value;
|
||||||
|
|
||||||
|
const af = document.getElementById('artFrame');
|
||||||
|
af.style.borderColor = col.border;
|
||||||
|
af.style.background = currentImageUrl ? 'transparent' : 'linear-gradient(135deg,'+col.border+'44,'+col.border+'99)';
|
||||||
|
document.getElementById('artPlaceholder').textContent = col.symbol;
|
||||||
|
const rc = RARITY_COLORS[rarity]||'#aaa';
|
||||||
|
document.getElementById('setRarity').style.color = rc;
|
||||||
|
document.getElementById('setRarity').textContent = sym+' '+(rarity?rarity[0]:'C');
|
||||||
|
|
||||||
|
const tl = document.getElementById('typeLine');
|
||||||
|
tl.style.background = col.border+'99';
|
||||||
|
const sub = document.getElementById('f-subtype').value;
|
||||||
|
document.getElementById('typeDisplay').textContent = [type,sub].filter(Boolean).join(' — ');
|
||||||
|
document.getElementById('setDisplay').textContent = sym;
|
||||||
|
|
||||||
|
document.getElementById('textBox').style.borderColor = col.border+'55';
|
||||||
|
document.getElementById('rulesDisplay').textContent = document.getElementById('f-rules').value;
|
||||||
|
const flavor = document.getElementById('f-flavor').value;
|
||||||
|
const fd = document.getElementById('flavorDisplay');
|
||||||
|
if (flavor) { fd.textContent = flavor; fd.style.display='block'; } else { fd.style.display='none'; }
|
||||||
|
|
||||||
|
document.getElementById('cardFooter').style.borderTopColor = col.border+'44';
|
||||||
|
document.getElementById('artistDisplay').textContent = '✦ '+(document.getElementById('f-artist').value||'Unknown');
|
||||||
|
|
||||||
|
const ptBox = document.getElementById('ptBox');
|
||||||
|
const loyBox = document.getElementById('loyaltyBox');
|
||||||
|
ptBox.style.background = col.border;
|
||||||
|
|
||||||
|
const isCreature = type==='Creature'||type==='Battle';
|
||||||
|
const isPW = type==='Planeswalker';
|
||||||
|
|
||||||
|
if (isCreature) {
|
||||||
|
ptBox.style.display='inline-block';
|
||||||
|
ptBox.textContent = document.getElementById('f-power').value+'/'+document.getElementById('f-toughness').value;
|
||||||
|
loyBox.style.display='none';
|
||||||
|
} else if (isPW) {
|
||||||
|
loyBox.style.display='flex';
|
||||||
|
loyBox.textContent = document.getElementById('f-loyalty').value;
|
||||||
|
ptBox.style.display='none';
|
||||||
|
} else {
|
||||||
|
ptBox.style.display='none';
|
||||||
|
loyBox.style.display='none';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('creatureStats').style.display = isCreature?'block':'none';
|
||||||
|
document.getElementById('pwStats').style.display = isPW?'block':'none';
|
||||||
|
document.getElementById('noStats').style.display = (!isCreature&&!isPW)?'block':'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTab(name, btn) {
|
||||||
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
document.querySelectorAll('.tab-panel').forEach(p => {
|
||||||
|
p.classList.toggle('active', p.id==='tab-'+name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFile(e) {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = ev => applyImage(ev.target.result);
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setImageUrl() {
|
||||||
|
const url = document.getElementById('urlInput').value.trim();
|
||||||
|
if (url) applyImage(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyImage(url) {
|
||||||
|
currentImageUrl = url;
|
||||||
|
const img = document.getElementById('cardArt');
|
||||||
|
img.src = url; img.style.display='block';
|
||||||
|
document.getElementById('artPlaceholder').style.display='none';
|
||||||
|
document.getElementById('artThumb').src = url;
|
||||||
|
document.getElementById('artPreview').style.display='flex';
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeImage() {
|
||||||
|
currentImageUrl = '';
|
||||||
|
const img = document.getElementById('cardArt');
|
||||||
|
img.src=''; img.style.display='none';
|
||||||
|
document.getElementById('artPlaceholder').style.display='block';
|
||||||
|
document.getElementById('artPreview').style.display='none';
|
||||||
|
document.getElementById('urlInput').value='';
|
||||||
|
document.getElementById('fileInput').value='';
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCard() {
|
||||||
|
document.getElementById('f-name').value='Blazing Champion';
|
||||||
|
document.getElementById('f-mana').value='{2}{R}{R}';
|
||||||
|
document.getElementById('f-type').value='Creature';
|
||||||
|
document.getElementById('f-subtype').value='Human Warrior';
|
||||||
|
document.getElementById('f-rarity').value='Rare';
|
||||||
|
document.getElementById('f-rules').value='Haste\nWhen Blazing Champion enters the battlefield, it deals 2 damage to any target.';
|
||||||
|
document.getElementById('f-flavor').value='"No retreat. No mercy. Only fire."';
|
||||||
|
document.getElementById('f-artist').value='Proxy Artist';
|
||||||
|
document.getElementById('f-power').value='4';
|
||||||
|
document.getElementById('f-toughness').value='3';
|
||||||
|
document.getElementById('f-loyalty').value='4';
|
||||||
|
document.getElementById('f-set').value='★';
|
||||||
|
setColor('R');
|
||||||
|
removeImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user