Генератор Колод Beta 1.1

This commit is contained in:
2026-03-29 00:19:59 +05:00
parent 112d101f54
commit 082c1b308b
7 changed files with 251 additions and 47 deletions

View File

@@ -7,6 +7,8 @@ module ClashDeckGenerator2
include Deps[view: "views.home.index"]
def handle(_req, res)
res.headers.delete("Content-Security-Policy")
roles_repo = ClashDeckGenerator2::Services::CardRolesRepository.new
cards_repo = ClashDeckGenerator2::Repos::CardsRepo.new

View File

@@ -4,7 +4,8 @@ module ClashDeckGenerator2
module Actions
module Home
class Index < ClashDeckGenerator2::Action
def handle(_req, _res)
def handle(_req, res)
res.headers.delete("Content-Security-Policy")
end
end
end

View File

@@ -1,11 +1,209 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ru" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clash deck generator2</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= content_for(:title) { "Генератор колод — Clash Royale" } %></title>
<%# Tailwind CSS v4 via CDN %>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<%# Monrope font via CDN %>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<style type="text/tailwindcss">
@theme {
--color-vs-bg: #1e1e1e;
--color-vs-sidebar: #252526;
--color-vs-panel: #2d2d30;
--color-vs-border: #3e3e42;
--color-vs-hover: #2a2d2e;
--color-vs-text: #d4d4d4;
--color-vs-muted: #858585;
--color-vs-blue: #569cd6;
--color-vs-orange: #ce9178;
--color-vs-green: #6a9955;
--color-vs-yellow: #dcdcaa;
--color-vs-purple: #c586c0;
--color-vs-cyan: #9cdcfe;
--color-vs-red: #f44747;
--color-vs-accent: #007acc;
--font-mono: "Manrope", ui-monospace, monospace;
}
* { font-family: "Manrope", sans-serif; }
body {
background-color: var(--color-vs-bg);
color: var(--color-vs-text);
}
.tab-bar {
background-color: var(--color-vs-sidebar);
border-bottom: 1px solid var(--color-vs-border);
}
.tab-active {
background-color: var(--color-vs-bg);
border-top: 1px solid var(--color-vs-accent);
color: var(--color-vs-text);
}
.tab-inactive {
background-color: var(--color-vs-sidebar);
color: var(--color-vs-muted);
}
.panel {
background-color: var(--color-vs-panel);
border: 1px solid var(--color-vs-border);
border-radius: 2px;
}
.status-bar {
background-color: var(--color-vs-accent);
color: #fff;
font-size: 12px;
}
.card-slot {
background-color: var(--color-vs-panel);
border: 1px solid var(--color-vs-border);
border-radius: 3px;
transition: border-color 0.15s, background-color 0.15s;
}
.card-slot:hover {
border-color: var(--color-vs-accent);
background-color: var(--color-vs-hover);
}
.card-slot.filled {
border-color: var(--color-vs-blue);
}
.btn-primary {
background-color: var(--color-vs-accent);
color: #fff;
border: none;
border-radius: 2px;
cursor: pointer;
transition: opacity 0.15s;
}
.btn-primary:hover { opacity: 0.85; }
.btn-primary:active { opacity: 0.7; }
.btn-ghost {
background-color: transparent;
color: var(--color-vs-muted);
border: 1px solid var(--color-vs-border);
border-radius: 2px;
cursor: pointer;
transition: color 0.15s, border-color 0.15s;
}
.btn-ghost:hover {
color: var(--color-vs-text);
border-color: var(--color-vs-muted);
}
.input-field {
background-color: var(--color-vs-bg);
border: 1px solid var(--color-vs-border);
color: var(--color-vs-text);
border-radius: 2px;
outline: none;
transition: border-color 0.15s;
}
.input-field:focus {
border-color: var(--color-vs-accent);
}
.badge-elixir {
background-color: #6b21a8;
color: #e9d5ff;
font-size: 11px;
border-radius: 2px;
padding: 1px 5px;
}
.rarity-common { border-left: 3px solid #9ca3af; }
.rarity-rare { border-left: 3px solid var(--color-vs-blue); }
.rarity-epic { border-left: 3px solid var(--color-vs-purple); }
.rarity-legendary { border-left: 3px solid var(--color-vs-yellow); }
.sidebar-item {
color: var(--color-vs-muted);
cursor: pointer;
border-radius: 2px;
transition: color 0.15s, background-color 0.15s;
}
.sidebar-item:hover, .sidebar-item.active {
color: var(--color-vs-text);
background-color: var(--color-vs-hover);
}
.scrollbar-vs::-webkit-scrollbar { width: 6px; }
.scrollbar-vs::-webkit-scrollbar-track { background: transparent; }
.scrollbar-vs::-webkit-scrollbar-thumb {
background: var(--color-vs-border);
border-radius: 3px;
}
.scrollbar-vs::-webkit-scrollbar-thumb:hover {
background: var(--color-vs-muted);
}
</style>
</head>
<body>
<%= yield %>
<body class="h-full flex flex-col">
<%# ── Верхняя строка с вкладками (имитация VS Code) ── %>
<div class="tab-bar flex items-center px-0 h-9 shrink-0 select-none">
<div class="flex items-stretch h-full">
<div class="tab-active flex items-center gap-2 px-4 text-sm h-full border-r border-[--color-vs-border]">
<svg class="w-3.5 h-3.5 text-[--color-vs-orange]" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 1a6 6 0 1 1 0 12A6 6 0 0 1 8 2z"/>
<path d="M8 4a.5.5 0 0 1 .5.5v3.793l2.146 2.147a.5.5 0 0 1-.708.707L7.5 8.707V4.5A.5.5 0 0 1 8 4z"/>
</svg>
deck_builder.rb
</div>
<div class="tab-inactive flex items-center gap-2 px-4 text-sm h-full border-r border-[--color-vs-border] hover:text-[--color-vs-text] transition-colors cursor-pointer">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 16 16">
<path d="M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5zm0 1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z"/>
</svg>
cards.json
</div>
</div>
</div>
<%# ── Основная область ── %>
<main class="flex-1 flex overflow-hidden">
<%= yield %>
</main>
<%# ── Строка состояния ── %>
<div class="status-bar flex items-center justify-between px-3 h-6 shrink-0">
<div class="flex items-center gap-4">
<span class="flex items-center gap-1">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>
Clash Royale
</span>
<span>UTF-8</span>
<span>ERB</span>
</div>
<div class="flex items-center gap-4">
<span>Hanami 2.3</span>
<span>Tailwind 4</span>
</div>
</div>
</body>
</html>
</html>

View File

@@ -4,5 +4,8 @@ require "hanami"
module ClashDeckGenerator2
class App < Hanami::App
config do
config.actions.content_security_policy = false
end
end
end

View File

@@ -1,12 +1,12 @@
# frozen_string_literal: true
CHAMPION_CARDS = [
{ name: "Little Prince", elixir_cost: 3, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Golden Knight", elixir_cost: 4, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Little Prince", elixir_cost: 3, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Golden Knight", elixir_cost: 4, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Skeleton King", elixir_cost: 4, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Mighty Miner", elixir_cost: 4, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Archer Queen", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Goblinstein", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Monk", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Mighty Miner", elixir_cost: 4, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Archer Queen", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Goblinstein", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 1 },
{ name: "Monk", elixir_cost: 5, rarity: "champion", type: "troop", is_meta: 0 },
{ name: "Boss Bandit", elixir_cost: 6, rarity: "champion", type: "troop", is_meta: 0 }
].freeze
].freeze

View File

@@ -4,38 +4,38 @@ EVOLUTION_CARDS = [
{ name: "Archers Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Baby Dragon Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Barbarians Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Battle Ram Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Battle Ram Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Bats Evolution", elixir_cost: 2, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Bomber Evolution", elixir_cost: 2, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Bomber Evolution", elixir_cost: 2, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Cannon Evolution", elixir_cost: 3, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Dart Goblin Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Electro Dragon Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Electro Dragon Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Executioner Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Firecracker Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Firecracker Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Furnace Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Giant Snowball Evolution", elixir_cost: 2, rarity: "evolution", type: "spell", is_meta: 0 },
{ name: "Goblin Barrel Evolution", elixir_cost: 3, rarity: "evolution", type: "spell", is_meta: 1 },
{ name: "Goblin Cage Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Goblin Drill Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 1 },
{ name: "Goblin Barrel Evolution", elixir_cost: 3, rarity: "evolution", type: "spell", is_meta: 0 },
{ name: "Goblin Cage Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 1 },
{ name: "Goblin Drill Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Goblin Giant Evolution", elixir_cost: 6, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Hunter Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Ice Spirit Evolution", elixir_cost: 1, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Ice Spirit Evolution", elixir_cost: 1, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Inferno Dragon Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Knight Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Lumberjack Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Mega Knight Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Mortar Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Musketeer Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "P.E.K.K.A Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Royal Ghost Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Knight Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Lumberjack Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Mega Knight Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Mortar Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 1 },
{ name: "Musketeer Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "P.E.K.K.A Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Royal Ghost Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Royal Giant Evolution", elixir_cost: 6, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Royal Hogs Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Royal Recruits Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Royal Recruits Evolution", elixir_cost: 7, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Skeleton Army Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Skeleton Barrel Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Skeletons Evolution", elixir_cost: 1, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Tesla Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 1 },
{ name: "Valkyrie Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Skeleton Barrel Evolution", elixir_cost: 3, rarity: "evolution", type: "troop", is_meta: 1 },
{ name: "Skeletons Evolution", elixir_cost: 1, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Tesla Evolution", elixir_cost: 4, rarity: "evolution", type: "building", is_meta: 0 },
{ name: "Valkyrie Evolution", elixir_cost: 4, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Wall Breakers Evolution", elixir_cost: 2, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Witch Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },
{ name: "Wizard Evolution", elixir_cost: 5, rarity: "evolution", type: "troop", is_meta: 0 },

View File

@@ -2,37 +2,37 @@
RARE_CARDS = [
# Юниты
{ name: "Heal Spirit", elixir_cost: 1, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Heal Spirit", elixir_cost: 1, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Ice Golem", elixir_cost: 2, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Suspicious Bush", elixir_cost: 2, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Suspicious Bush", elixir_cost: 2, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Mega Minion", elixir_cost: 3, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Dart Goblin", elixir_cost: 3, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Elixir Golem", elixir_cost: 3, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Mini P.E.K.K.A", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Musketeer", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Valkyrie", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Mini P.E.K.K.A", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Musketeer", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Valkyrie", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Battle Ram", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Hog Rider", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Hog Rider", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Battle Healer", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Goblin Demolisher", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Giant", elixir_cost: 5, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Wizard", elixir_cost: 5, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Royal Hogs", elixir_cost: 5, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Three Musketeers", elixir_cost: 9, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Zappies", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
{ name: "Zappies", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 1 },
{ name: "Furnace", elixir_cost: 4, rarity: "rare", type: "troop", is_meta: 0 },
# Заклинания
{ name: "Earthquake", elixir_cost: 3, rarity: "rare", type: "spell", is_meta: 1 },
{ name: "Earthquake", elixir_cost: 3, rarity: "rare", type: "spell", is_meta: 0 },
{ name: "Fireball", elixir_cost: 4, rarity: "rare", type: "spell", is_meta: 1 },
{ name: "Rocket", elixir_cost: 6, rarity: "rare", type: "spell", is_meta: 0 },
# Здания
{ name: "Tombstone", elixir_cost: 3, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Tombstone", elixir_cost: 3, rarity: "rare", type: "building", is_meta: 1 },
{ name: "Goblin Cage", elixir_cost: 4, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Goblin Hut", elixir_cost: 5, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Goblin Hut", elixir_cost: 5, rarity: "rare", type: "building", is_meta: 1 },
{ name: "Bomb Tower", elixir_cost: 4, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Inferno Tower", elixir_cost: 5, rarity: "rare", type: "building", is_meta: 1 },
{ name: "Inferno Tower", elixir_cost: 5, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Barbarian Hut", elixir_cost: 6, rarity: "rare", type: "building", is_meta: 0 },
{ name: "Elixir Collector", elixir_cost: 6, rarity: "rare", type: "building", is_meta: 0 }
{ name: "Elixir Collector", elixir_cost: 6, rarity: "rare", type: "building", is_meta: 1 }
].freeze