jaml-ui 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +197 -0
- package/dist/components/AnalyzerExplorer.js +0 -11
- package/dist/components/JamlAestheticSelector.js +1 -3
- package/dist/components/JamlAnalyzerFullscreen.js +3 -3
- package/dist/components/JamlIde.js +0 -2
- package/dist/components/JamlSeedInput.js +1 -2
- package/dist/components/JamlSpeedometer.js +1 -1
- package/dist/ui/codeBlock.js +1 -1
- package/dist/ui/jimboCopyRow.js +1 -2
- package/dist/ui/jimboFilterBar.js +2 -4
- package/dist/ui/showcase.js +4 -4
- package/package.json +8 -5
- package/assets/Balatro Seed Curator (DesignsV2)/.design-canvas.state.json +0 -1
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/Boosters.json +0 -303
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/blinds_metadata.json +0 -51
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/8BitDeck.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/balatro-stake-chips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/enhancers_metadata.json +0 -52
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/playing_cards_metadata.json +0 -249
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/stakes.json +0 -19
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/jokers.json +0 -1087
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers_metadata.json +0 -25
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.json +0 -191
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/planets.json +0 -15
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/spectrals.json +0 -21
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/tarots.json +0 -163
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/Vouchers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/vouchers.json +0 -130
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/blinds.json +0 -51
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/boosters.json +0 -303
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/fonts/m6x11plusplus.otf +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/jokers.json +0 -1087
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/planets.json +0 -15
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/spectrals.json +0 -21
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/stakes.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.json +0 -191
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tarots.json +0 -163
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/vouchers.json +0 -130
- package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail v2.html +0 -40
- package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail.html +0 -34
- package/assets/Balatro Seed Curator (DesignsV2)/public/fonts/m6x11plusplus.otf +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/src/AntePage.jsx +0 -228
- package/assets/Balatro Seed Curator (DesignsV2)/src/SeedDetail.jsx +0 -222
- package/assets/Balatro Seed Curator (DesignsV2)/src/app.jsx +0 -35
- package/assets/Balatro Seed Curator (DesignsV2)/src/mockData.js +0 -185
- package/assets/Balatro Seed Curator (DesignsV2)/src/sprites.jsx +0 -259
- package/assets/Balatro Seed Curator (DesignsV2)/src/tokens.js +0 -49
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/AntePageV2.jsx +0 -290
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/BalButton.jsx +0 -107
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlBuilderV2.jsx +0 -594
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlIde.jsx +0 -302
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SearchResultsV2.jsx +0 -286
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedDetailV2.jsx +0 -336
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedOGCard.jsx +0 -251
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/Showcase.jsx +0 -131
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/app.jsx +0 -55
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/data.js +0 -296
- package/assets/Balatro Seed Curator (DesignsV2)/starters/design-canvas.jsx +0 -622
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/8BitDeck.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749540653-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749644934-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749661871-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749674748-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749703076-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749882759-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750354200-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750733265-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776751928925-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776800975060-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/tags.png +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
// <Sprite> — positions a slice of a sprite sheet via background-image.
|
|
2
|
-
// Works with any sheet registered in window.SHEETS and any name map loaded
|
|
3
|
-
// via loadSpriteMap(). Sizes are in display-px; pixel-art is preserved via
|
|
4
|
-
// image-rendering: pixelated.
|
|
5
|
-
|
|
6
|
-
const { useState, useEffect, useRef } = React;
|
|
7
|
-
|
|
8
|
-
// ─── Name maps, loaded async from JSON. Keys are normalized (lowercase, no
|
|
9
|
-
// spaces, no punctuation) to tolerate both "Blueprint" and "blueprint"
|
|
10
|
-
// inputs.
|
|
11
|
-
const SPRITE_MAPS = {};
|
|
12
|
-
|
|
13
|
-
function normalizeName(s) {
|
|
14
|
-
return String(s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function loadSpriteMap(sheet, url, { field = 'pos' } = {}) {
|
|
18
|
-
const r = await fetch(url);
|
|
19
|
-
const data = await r.json();
|
|
20
|
-
const out = {};
|
|
21
|
-
if (Array.isArray(data)) {
|
|
22
|
-
for (const e of data) out[normalizeName(e.name)] = e[field];
|
|
23
|
-
} else if (data.sprites) {
|
|
24
|
-
// blinds_metadata shape
|
|
25
|
-
const walk = (obj) => {
|
|
26
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
27
|
-
if (v && typeof v === 'object' && 'x' in v && 'y' in v) {
|
|
28
|
-
out[normalizeName(k)] = { x: v.x, y: v.y };
|
|
29
|
-
} else if (v && typeof v === 'object') {
|
|
30
|
-
walk(v);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
walk(data.sprites);
|
|
35
|
-
}
|
|
36
|
-
SPRITE_MAPS[sheet] = { ...(SPRITE_MAPS[sheet] || {}), ...out };
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getSpritePos(sheet, name) {
|
|
41
|
-
const m = SPRITE_MAPS[sheet];
|
|
42
|
-
if (!m) return null;
|
|
43
|
-
return m[normalizeName(name)] || null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Hook: re-render when any sprite map loads. Used by Sprite to pick up
|
|
47
|
-
// positions that arrive after first render.
|
|
48
|
-
const mapListeners = new Set();
|
|
49
|
-
function useSpriteMapTick() {
|
|
50
|
-
const [, set] = useState(0);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
const fn = () => set((n) => n + 1);
|
|
53
|
-
mapListeners.add(fn);
|
|
54
|
-
return () => mapListeners.delete(fn);
|
|
55
|
-
}, []);
|
|
56
|
-
}
|
|
57
|
-
const _origLoadSpriteMap = loadSpriteMap;
|
|
58
|
-
window.loadSpriteMap = async (...args) => {
|
|
59
|
-
const r = await _origLoadSpriteMap(...args);
|
|
60
|
-
for (const fn of mapListeners) fn();
|
|
61
|
-
return r;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// ─── <Sprite> — pos is {x,y} grid index. width/height in display px.
|
|
65
|
-
function Sprite({ sheet, name, pos: posOverride, width, height, className = '', style = {}, children }) {
|
|
66
|
-
useSpriteMapTick();
|
|
67
|
-
const sheetInfo = window.SHEETS[sheet];
|
|
68
|
-
if (!sheetInfo) return null;
|
|
69
|
-
const pos = posOverride || getSpritePos(sheet, name);
|
|
70
|
-
|
|
71
|
-
// Native sprite aspect (before display sizing). Pull from the sheet image
|
|
72
|
-
// if already cached, else assume 71:95 joker ratio as a safe default.
|
|
73
|
-
const aspect = SHEET_ASPECTS[sheet] || (95 / 71);
|
|
74
|
-
const w = width || 71;
|
|
75
|
-
const h = height || Math.round(w * aspect);
|
|
76
|
-
|
|
77
|
-
if (!pos) {
|
|
78
|
-
return (
|
|
79
|
-
<div className={className} style={{ width: w, height: h, background: 'rgba(255,0,0,0.08)', border: '1px dashed rgba(255,0,0,0.3)', boxSizing: 'border-box', ...style }} title={`missing: ${sheet}/${name}`}>
|
|
80
|
-
{children}
|
|
81
|
-
</div>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const bgW = w * sheetInfo.cols;
|
|
86
|
-
const bgH = h * sheetInfo.rows;
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<div
|
|
90
|
-
className={className}
|
|
91
|
-
style={{
|
|
92
|
-
width: w,
|
|
93
|
-
height: h,
|
|
94
|
-
backgroundImage: `url(${sheetInfo.src})`,
|
|
95
|
-
backgroundSize: `${bgW}px ${bgH}px`,
|
|
96
|
-
backgroundPosition: `-${pos.x * w}px -${pos.y * h}px`,
|
|
97
|
-
backgroundRepeat: 'no-repeat',
|
|
98
|
-
imageRendering: 'pixelated',
|
|
99
|
-
flexShrink: 0,
|
|
100
|
-
position: 'relative',
|
|
101
|
-
...style,
|
|
102
|
-
}}
|
|
103
|
-
>
|
|
104
|
-
{children}
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Aspect ratios per sheet (h/w), so Sprite can size correctly from just a
|
|
110
|
-
// width. Derived from image-metadata; fallback is joker-like.
|
|
111
|
-
const SHEET_ASPECTS = {
|
|
112
|
-
jokers: 95 / 71, // 710/10 = 71, 1520/16 = 95
|
|
113
|
-
tarots: 95 / 71,
|
|
114
|
-
vouchers: 95 / 71,
|
|
115
|
-
tags: 34 / 34, // tags are square-ish
|
|
116
|
-
boosters: 190 / 142, // 568/4 = 142, 1710/9 = 190
|
|
117
|
-
blinds: 1, // 68×68 square
|
|
118
|
-
editions: 95 / 71,
|
|
119
|
-
enhancers: 95 / 71,
|
|
120
|
-
stickers: 95 / 71,
|
|
121
|
-
deck: 95 / 71,
|
|
122
|
-
stakes: 1,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// ─── <HitStamp> — small corner badge: green check for should-hit, gold
|
|
126
|
-
// star for must-hit. Positioned absolutely, so parent must be relative.
|
|
127
|
-
function HitStamp({ kind }) {
|
|
128
|
-
if (!kind) return null;
|
|
129
|
-
const palette = kind === 'must'
|
|
130
|
-
? { bg: window.JimboColor.GOLD, border: '#8a6a1f', glyph: '★', color: '#1e2b2d' }
|
|
131
|
-
: { bg: window.JimboColor.GREEN_TEXT, border: '#1f7a55', glyph: '✓', color: '#fff' };
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<div
|
|
135
|
-
style={{
|
|
136
|
-
position: 'absolute',
|
|
137
|
-
top: -4,
|
|
138
|
-
right: -4,
|
|
139
|
-
width: 16,
|
|
140
|
-
height: 16,
|
|
141
|
-
borderRadius: 8,
|
|
142
|
-
background: palette.bg,
|
|
143
|
-
border: `1.5px solid ${palette.border}`,
|
|
144
|
-
color: palette.color,
|
|
145
|
-
fontSize: 11,
|
|
146
|
-
lineHeight: '13px',
|
|
147
|
-
textAlign: 'center',
|
|
148
|
-
fontFamily: 'm6x11plus, monospace',
|
|
149
|
-
boxShadow: '0 1px 2px rgba(0,0,0,0.35)',
|
|
150
|
-
zIndex: 2,
|
|
151
|
-
pointerEvents: 'none',
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
{palette.glyph}
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ─── <JokerMini>, <TarotMini>, etc. — pre-sized wrappers with optional hit.
|
|
160
|
-
function JokerMini({ name, hit, size = 64, edition, onClick }) {
|
|
161
|
-
const w = size;
|
|
162
|
-
const h = Math.round(size * 95 / 71);
|
|
163
|
-
return (
|
|
164
|
-
<div
|
|
165
|
-
onClick={onClick}
|
|
166
|
-
style={{
|
|
167
|
-
position: 'relative',
|
|
168
|
-
width: w,
|
|
169
|
-
height: h,
|
|
170
|
-
cursor: onClick ? 'pointer' : 'default',
|
|
171
|
-
filter: hit ? 'none' : 'brightness(0.78) saturate(0.85)',
|
|
172
|
-
transition: 'filter 180ms',
|
|
173
|
-
}}
|
|
174
|
-
>
|
|
175
|
-
<Sprite sheet="jokers" name={name} width={w} height={h} />
|
|
176
|
-
{edition && <Sprite sheet="editions" pos={EDITION_POS[edition] || { x: 0, y: 0 }} width={w} height={h} style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen', opacity: 0.85 }} />}
|
|
177
|
-
<HitStamp kind={hit} />
|
|
178
|
-
</div>
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const EDITION_POS = { Foil: { x: 0, y: 0 }, Holographic: { x: 1, y: 0 }, Polychrome: { x: 2, y: 0 }, Negative: { x: 3, y: 0 } };
|
|
183
|
-
|
|
184
|
-
function TarotMini({ name, hit, size = 64, onClick }) {
|
|
185
|
-
const w = size;
|
|
186
|
-
const h = Math.round(size * 95 / 71);
|
|
187
|
-
return (
|
|
188
|
-
<div onClick={onClick} style={{ position: 'relative', width: w, height: h, cursor: onClick ? 'pointer' : 'default', filter: hit ? 'none' : 'brightness(0.78) saturate(0.85)', transition: 'filter 180ms' }}>
|
|
189
|
-
<Sprite sheet="tarots" name={name} width={w} height={h} />
|
|
190
|
-
<HitStamp kind={hit} />
|
|
191
|
-
</div>
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function VoucherMini({ name, hit, size = 64, onClick }) {
|
|
196
|
-
const w = size;
|
|
197
|
-
const h = Math.round(size * 95 / 71);
|
|
198
|
-
return (
|
|
199
|
-
<div onClick={onClick} style={{ position: 'relative', width: w, height: h, cursor: onClick ? 'pointer' : 'default', filter: hit ? 'none' : 'brightness(0.85) saturate(0.9)', transition: 'filter 180ms' }}>
|
|
200
|
-
<Sprite sheet="vouchers" name={name} width={w} height={h} />
|
|
201
|
-
<HitStamp kind={hit} />
|
|
202
|
-
</div>
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function TagChip({ name, hit, size = 28, onClick }) {
|
|
207
|
-
return (
|
|
208
|
-
<div onClick={onClick} style={{ position: 'relative', cursor: onClick ? 'pointer' : 'default', filter: hit ? 'none' : 'none' }}>
|
|
209
|
-
<Sprite sheet="tags" name={name} width={size} height={size} />
|
|
210
|
-
<HitStamp kind={hit} />
|
|
211
|
-
</div>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function BossChip({ name, hit, size = 48, onClick }) {
|
|
216
|
-
// BlindChips.png is an animation strip (21 frames per row). Show frame 0
|
|
217
|
-
// by overriding pos.y to the row for this boss and pos.x = 0.
|
|
218
|
-
const basePos = getSpritePos('blinds', name);
|
|
219
|
-
if (!basePos) return <Sprite sheet="blinds" name={name} width={size} height={size} />;
|
|
220
|
-
return (
|
|
221
|
-
<div onClick={onClick} style={{ position: 'relative', cursor: onClick ? 'pointer' : 'default' }}>
|
|
222
|
-
<Sprite sheet="blinds" pos={{ x: 0, y: basePos.y }} width={size} height={size} />
|
|
223
|
-
<HitStamp kind={hit} />
|
|
224
|
-
</div>
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function PackSprite({ kind, size = 56, onClick, open, hit }) {
|
|
229
|
-
// kind: 'arcana' | 'celestial' | 'spectral' | 'buffoon' | 'standard' + 'jumbo' / 'mega' variants
|
|
230
|
-
const w = size;
|
|
231
|
-
const h = Math.round(size * 190 / 142);
|
|
232
|
-
return (
|
|
233
|
-
<div
|
|
234
|
-
onClick={onClick}
|
|
235
|
-
style={{
|
|
236
|
-
position: 'relative',
|
|
237
|
-
width: w,
|
|
238
|
-
height: h,
|
|
239
|
-
cursor: onClick ? 'pointer' : 'default',
|
|
240
|
-
transform: open ? 'translateY(-4px) rotate(-2deg)' : 'none',
|
|
241
|
-
transition: 'transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
242
|
-
}}
|
|
243
|
-
>
|
|
244
|
-
<Sprite sheet="boosters" name={kind} width={w} height={h} />
|
|
245
|
-
<HitStamp kind={hit} />
|
|
246
|
-
</div>
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
window.Sprite = Sprite;
|
|
251
|
-
window.HitStamp = HitStamp;
|
|
252
|
-
window.JokerMini = JokerMini;
|
|
253
|
-
window.TarotMini = TarotMini;
|
|
254
|
-
window.VoucherMini = VoucherMini;
|
|
255
|
-
window.TagChip = TagChip;
|
|
256
|
-
window.BossChip = BossChip;
|
|
257
|
-
window.PackSprite = PackSprite;
|
|
258
|
-
window.normalizeName = normalizeName;
|
|
259
|
-
window.getSpritePos = getSpritePos;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Balatro design tokens — from jaml-ui/src/ui/tokens.ts
|
|
2
|
-
// DO NOT substitute with Lua HEX values; these are shader-output eyedropped.
|
|
3
|
-
|
|
4
|
-
window.JimboColor = {
|
|
5
|
-
RED: '#ff4c40',
|
|
6
|
-
BLUE: '#0093ff',
|
|
7
|
-
GREEN: '#429f79',
|
|
8
|
-
ORANGE: '#ff9800',
|
|
9
|
-
GOLD: '#e4b643',
|
|
10
|
-
PURPLE: '#9e74ce',
|
|
11
|
-
|
|
12
|
-
DARK_RED: '#a02721',
|
|
13
|
-
DARK_BLUE: '#0057a1',
|
|
14
|
-
DARK_ORANGE: '#a05b00',
|
|
15
|
-
DARK_GREEN: '#215f46',
|
|
16
|
-
DARK_PURPLE: '#5e437e',
|
|
17
|
-
|
|
18
|
-
DARK_GREY: '#3a5055',
|
|
19
|
-
DARKEST: '#1e2b2d',
|
|
20
|
-
GREY: '#708386',
|
|
21
|
-
TEAL_GREY: '#404c4e',
|
|
22
|
-
|
|
23
|
-
PANEL_EDGE: '#1e2e32',
|
|
24
|
-
INNER_BORDER: '#334461',
|
|
25
|
-
|
|
26
|
-
BORDER_SILVER: '#b9c2d2',
|
|
27
|
-
BORDER_SOUTH: '#777e89',
|
|
28
|
-
|
|
29
|
-
GOLD_TEXT: '#e4b643',
|
|
30
|
-
GREEN_TEXT: '#35bd86',
|
|
31
|
-
ORANGE_TEXT: '#ff8f00',
|
|
32
|
-
WHITE: '#ffffff',
|
|
33
|
-
BLACK: '#000000',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Sprite sheet grid dimensions (from jaml-ui/src/sprites/spriteData.ts)
|
|
37
|
-
window.SHEETS = {
|
|
38
|
-
jokers: { src: 'assets/Jokers.png', cols: 10, rows: 16 },
|
|
39
|
-
tarots: { src: 'assets/Tarots.png', cols: 10, rows: 6 },
|
|
40
|
-
vouchers: { src: 'assets/Vouchers.png', cols: 9, rows: 4 },
|
|
41
|
-
tags: { src: 'assets/tags.png', cols: 6, rows: 5 },
|
|
42
|
-
boosters: { src: 'assets/Boosters.png', cols: 4, rows: 9 },
|
|
43
|
-
blinds: { src: 'assets/BlindChips.png',cols: 21, rows: 31 },
|
|
44
|
-
editions: { src: 'assets/Editions.png', cols: 5, rows: 1 },
|
|
45
|
-
enhancers: { src: 'assets/Enhancers.png', cols: 7, rows: 5 },
|
|
46
|
-
stickers: { src: 'assets/stickers.png', cols: 5, rows: 3 },
|
|
47
|
-
deck: { src: 'assets/8BitDeck.png', cols: 13, rows: 4 },
|
|
48
|
-
stakes: { src: 'assets/stakes.png', cols: 4, rows: 2 },
|
|
49
|
-
};
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
// AntePageV2 — per the reorg:
|
|
2
|
-
//
|
|
3
|
-
// ANTE n smTag bgTag BOSS ← header row, tags+boss right
|
|
4
|
-
// ┌────┬──────────────────────────────────────┐
|
|
5
|
-
// │ V │ pack0 pack2 pack4 │ ← voucher column (left), packs grid (2 rows × N cols)
|
|
6
|
-
// │ │ pack1 pack3 pack5 │
|
|
7
|
-
// ├────┴──────────────────────────────────────┤
|
|
8
|
-
// │ SHOP ▸ [joker][joker][tarot]… (tape) │ ← infinite horizontal tape, edge fades
|
|
9
|
-
// └───────────────────────────────────────────┘
|
|
10
|
-
//
|
|
11
|
-
// Ante 1 special: two shop tapes stacked, labeled "Round 1" / "Round 2" (since ante 1 starts at
|
|
12
|
-
// Small Blind — you haven't earned a shop yet — so only 4 booster packs, but shop refreshes twice).
|
|
13
|
-
|
|
14
|
-
const { useState: aUS, useRef: aUR, useEffect: aUE } = React;
|
|
15
|
-
|
|
16
|
-
const C = window.JimboColor;
|
|
17
|
-
|
|
18
|
-
// ── bestHit / hitColor ──
|
|
19
|
-
const bestHit = (hits) => {
|
|
20
|
-
if (!hits || !hits.length) return null;
|
|
21
|
-
if (hits.some(h => h.kind === 'must')) return 'must';
|
|
22
|
-
return 'should';
|
|
23
|
-
};
|
|
24
|
-
const hitColor = (k) => k === 'must' ? C.BLUE : k === 'should' ? C.RED : null;
|
|
25
|
-
|
|
26
|
-
// ── Panel ─
|
|
27
|
-
function Panel({ children, style, bg = C.DARK_GREY, edge = C.PANEL_EDGE }) {
|
|
28
|
-
return (
|
|
29
|
-
<div style={{
|
|
30
|
-
background: bg,
|
|
31
|
-
border: `2px solid ${edge}`,
|
|
32
|
-
borderRadius: 6,
|
|
33
|
-
boxShadow: `inset 0 0 0 1px rgba(255,255,255,0.04), 0 2px 0 ${C.BLACK}`,
|
|
34
|
-
...style,
|
|
35
|
-
}}>{children}</div>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ── GlowRing ──
|
|
40
|
-
function GlowRing({ kind, children }) {
|
|
41
|
-
if (!kind) return children;
|
|
42
|
-
const color = hitColor(kind);
|
|
43
|
-
return (
|
|
44
|
-
<div style={{ position: 'relative', display: 'inline-block' }}>
|
|
45
|
-
<div style={{
|
|
46
|
-
position: 'absolute', inset: -3, borderRadius: 6,
|
|
47
|
-
boxShadow: `0 0 0 2px ${color}, 0 0 10px ${color}`,
|
|
48
|
-
opacity: 0.8,
|
|
49
|
-
animation: 'pulseGlow 1.6s ease-in-out infinite',
|
|
50
|
-
pointerEvents: 'none',
|
|
51
|
-
zIndex: 1,
|
|
52
|
-
}} />
|
|
53
|
-
<div style={{ position: 'relative', zIndex: 2 }}>{children}</div>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Inject keyframes once
|
|
59
|
-
(function injectKeyframes(){
|
|
60
|
-
if (document.getElementById('v2-kf')) return;
|
|
61
|
-
const s = document.createElement('style');
|
|
62
|
-
s.id = 'v2-kf';
|
|
63
|
-
s.textContent = `
|
|
64
|
-
@keyframes pulseGlow { 0%,100% { opacity:.55;} 50% { opacity:1;} }
|
|
65
|
-
.v2-shop-scroll::-webkit-scrollbar { display: none; }
|
|
66
|
-
`;
|
|
67
|
-
document.head.appendChild(s);
|
|
68
|
-
})();
|
|
69
|
-
|
|
70
|
-
// ── Tag chip small ──
|
|
71
|
-
function MiniChip({ children, tight, color = C.WHITE }) {
|
|
72
|
-
return (
|
|
73
|
-
<div style={{
|
|
74
|
-
fontFamily: 'm6x11plus, monospace', fontSize: 9, color,
|
|
75
|
-
letterSpacing: 1, textTransform: 'uppercase',
|
|
76
|
-
padding: tight ? '1px 3px' : '2px 4px',
|
|
77
|
-
lineHeight: 1,
|
|
78
|
-
}}>{children}</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ── HeaderTile — compact tag/boss tile for top-right row ──
|
|
83
|
-
function HeaderTile({ kind, value, hit, label }) {
|
|
84
|
-
const sz = 32;
|
|
85
|
-
const inner =
|
|
86
|
-
kind === 'boss' ? <BossChip name={value} size={sz} /> :
|
|
87
|
-
<TagChip name={value} size={sz} />;
|
|
88
|
-
return (
|
|
89
|
-
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
|
|
90
|
-
<div style={{ width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
91
|
-
<GlowRing kind={hit}>{inner}</GlowRing>
|
|
92
|
-
</div>
|
|
93
|
-
<MiniChip color={hit ? hitColor(hit) : C.GREY}>{label}</MiniChip>
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ── VoucherSquare — tall left column tile ──
|
|
99
|
-
function VoucherSquare({ name, hit }) {
|
|
100
|
-
return (
|
|
101
|
-
<div style={{
|
|
102
|
-
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 4,
|
|
103
|
-
background: C.DARKEST, border: `2px solid ${C.PANEL_EDGE}`, borderRadius: 6,
|
|
104
|
-
padding: '8px 6px', minWidth: 68,
|
|
105
|
-
}}>
|
|
106
|
-
<MiniChip color={hit ? hitColor(hit) : C.GREY}>V</MiniChip>
|
|
107
|
-
{name ? <GlowRing kind={hit}><VoucherMini name={name} size={42} /></GlowRing>
|
|
108
|
-
: <div style={{ width: 42, height: 42, opacity: 0.25, border: `1px dashed ${C.GREY}`, borderRadius: 4 }} />}
|
|
109
|
-
<div style={{
|
|
110
|
-
fontFamily: 'm6x11plus, monospace', fontSize: 9, color: C.WHITE,
|
|
111
|
-
maxWidth: 70, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
|
|
112
|
-
textAlign: 'center', letterSpacing: 0.5,
|
|
113
|
-
}}>{name || '—'}</div>
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ── PackCell — tap to fan ──
|
|
119
|
-
function PackCell({ pack, idx }) {
|
|
120
|
-
const [open, setOpen] = aUS(false);
|
|
121
|
-
const anyHit = pack.itemHits?.some(h => h.length) ? bestHit(pack.itemHits.flat()) : null;
|
|
122
|
-
return (
|
|
123
|
-
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', minWidth: 0, position: 'relative' }}>
|
|
124
|
-
<GlowRing kind={anyHit}>
|
|
125
|
-
<PackSprite kind={pack.type.replace('pack','')} size={44} open={open} onClick={() => setOpen(o => !o)} />
|
|
126
|
-
</GlowRing>
|
|
127
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 8, color: anyHit ? hitColor(anyHit) : C.GREY, letterSpacing: 1, marginTop: 2 }}>
|
|
128
|
-
P{idx + 1}
|
|
129
|
-
</div>
|
|
130
|
-
{open && (
|
|
131
|
-
<div style={{
|
|
132
|
-
position: 'absolute', top: 50, left: '50%', transform: 'translateX(-50%)',
|
|
133
|
-
display: 'flex', gap: 4, padding: '6px 8px',
|
|
134
|
-
background: C.DARKEST, border: `2px solid ${C.PANEL_EDGE}`, borderRadius: 5,
|
|
135
|
-
zIndex: 10, whiteSpace: 'nowrap',
|
|
136
|
-
}}>
|
|
137
|
-
{pack.items.map((it, i) => {
|
|
138
|
-
const itemHit = bestHit(pack.itemHits?.[i]);
|
|
139
|
-
return <GlowRing key={i} kind={itemHit}><JokerMini name={it} size={30} /></GlowRing>;
|
|
140
|
-
})}
|
|
141
|
-
</div>
|
|
142
|
-
)}
|
|
143
|
-
</div>
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── PackGrid — 2 rows × ceil(n/2) cols ──
|
|
148
|
-
function PackGrid({ packs }) {
|
|
149
|
-
const cols = Math.max(1, Math.ceil(packs.length / 2));
|
|
150
|
-
return (
|
|
151
|
-
<div style={{
|
|
152
|
-
display: 'grid',
|
|
153
|
-
gridTemplateColumns: `repeat(${cols}, auto)`,
|
|
154
|
-
gridTemplateRows: 'repeat(2, auto)',
|
|
155
|
-
gridAutoFlow: 'column',
|
|
156
|
-
gap: '10px 12px',
|
|
157
|
-
alignItems: 'start', justifyItems: 'center',
|
|
158
|
-
}}>
|
|
159
|
-
{packs.map((p, i) => <PackCell key={i} pack={p} idx={i} />)}
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ── ShopTape — horizontal grab-scroll with edge fades ──
|
|
165
|
-
function ShopTape({ items, label = 'SHOP' }) {
|
|
166
|
-
const ref = aUR(null);
|
|
167
|
-
const drag = aUR({ down: false, x0: 0, sl0: 0 });
|
|
168
|
-
|
|
169
|
-
const onDown = (e) => {
|
|
170
|
-
const el = ref.current; if (!el) return;
|
|
171
|
-
const x = e.touches ? e.touches[0].clientX : e.clientX;
|
|
172
|
-
drag.current = { down: true, x0: x, sl0: el.scrollLeft };
|
|
173
|
-
el.style.cursor = 'grabbing';
|
|
174
|
-
};
|
|
175
|
-
const onMove = (e) => {
|
|
176
|
-
if (!drag.current.down) return;
|
|
177
|
-
const el = ref.current; if (!el) return;
|
|
178
|
-
const x = e.touches ? e.touches[0].clientX : e.clientX;
|
|
179
|
-
el.scrollLeft = drag.current.sl0 - (x - drag.current.x0);
|
|
180
|
-
};
|
|
181
|
-
const onUp = () => { const el = ref.current; if (el) el.style.cursor = 'grab'; drag.current.down = false; };
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
<div>
|
|
185
|
-
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
|
|
186
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 10, color: C.GREY, letterSpacing: 2 }}>{label} ▸</div>
|
|
187
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 10, color: C.GREY }}>{items.length}</div>
|
|
188
|
-
</div>
|
|
189
|
-
<Panel style={{ padding: '8px 0', position: 'relative' }}>
|
|
190
|
-
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, width: 24, background: `linear-gradient(90deg, ${C.DARK_GREY}, transparent)`, pointerEvents: 'none', zIndex: 2 }} />
|
|
191
|
-
<div style={{ position: 'absolute', top: 0, bottom: 0, right: 0, width: 24, background: `linear-gradient(-90deg, ${C.DARK_GREY}, transparent)`, pointerEvents: 'none', zIndex: 2 }} />
|
|
192
|
-
<div
|
|
193
|
-
ref={ref}
|
|
194
|
-
className="v2-shop-scroll"
|
|
195
|
-
onMouseDown={onDown} onMouseMove={onMove} onMouseUp={onUp} onMouseLeave={onUp}
|
|
196
|
-
onTouchStart={onDown} onTouchMove={onMove} onTouchEnd={onUp}
|
|
197
|
-
style={{
|
|
198
|
-
overflowX: 'auto', overflowY: 'hidden',
|
|
199
|
-
display: 'flex', gap: 8, padding: '2px 18px',
|
|
200
|
-
cursor: 'grab', userSelect: 'none', scrollbarWidth: 'none',
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
{items.map((item, i) => {
|
|
204
|
-
const hit = bestHit(item.hits);
|
|
205
|
-
return (
|
|
206
|
-
<div key={i} style={{ flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
207
|
-
<GlowRing kind={hit}>
|
|
208
|
-
<JokerMini name={item.value} size={50} edition={item.edition} />
|
|
209
|
-
</GlowRing>
|
|
210
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 8, color: hit ? hitColor(hit) : C.GREY, letterSpacing: 1, marginTop: 2 }}>
|
|
211
|
-
#{i + 1}
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
);
|
|
215
|
-
})}
|
|
216
|
-
</div>
|
|
217
|
-
</Panel>
|
|
218
|
-
</div>
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// ── AntePageV2 ───────
|
|
223
|
-
function AntePageV2({ ante }) {
|
|
224
|
-
const voucherHit = bestHit(ante._voucherHits);
|
|
225
|
-
const smallHit = bestHit(ante._smallTagHits);
|
|
226
|
-
const bigHit = bestHit(ante._bigTagHits);
|
|
227
|
-
const bossHit = bestHit(ante._bossHits);
|
|
228
|
-
const soulHit = bestHit(ante._soulHits);
|
|
229
|
-
|
|
230
|
-
// Ante 1 quirk: split shopQueue into two rounds for display
|
|
231
|
-
const isAnte1 = ante.ante === 1;
|
|
232
|
-
const shopRounds = isAnte1
|
|
233
|
-
? [
|
|
234
|
-
{ label: 'SHOP · ROUND 1', items: ante.shopQueue.slice(0, Math.ceil(ante.shopQueue.length / 2)) },
|
|
235
|
-
{ label: 'SHOP · ROUND 2', items: ante.shopQueue.slice(Math.ceil(ante.shopQueue.length / 2)) },
|
|
236
|
-
]
|
|
237
|
-
: [{ label: 'SHOP', items: ante.shopQueue }];
|
|
238
|
-
|
|
239
|
-
return (
|
|
240
|
-
<div style={{ padding: '12px 10px 32px', display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
241
|
-
{/* Header row: ANTE n smTag bgTag Boss */}
|
|
242
|
-
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
|
243
|
-
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
|
|
244
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 10, color: C.GREY, letterSpacing: 2 }}>ANTE</div>
|
|
245
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 26, color: C.WHITE, lineHeight: 1 }}>{ante.ante}</div>
|
|
246
|
-
</div>
|
|
247
|
-
<div style={{ display: 'flex', gap: 8 }}>
|
|
248
|
-
<HeaderTile kind="smalltag" value={ante.smallBlindTag} hit={smallHit} label="SMALL" />
|
|
249
|
-
<HeaderTile kind="bigtag" value={ante.bigBlindTag} hit={bigHit} label="BIG" />
|
|
250
|
-
<HeaderTile kind="boss" value={ante.boss} hit={bossHit} label="BOSS" />
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
|
|
254
|
-
{/* Voucher + Packs row */}
|
|
255
|
-
<Panel style={{ padding: 10 }}>
|
|
256
|
-
<div style={{ display: 'flex', gap: 10, alignItems: 'stretch' }}>
|
|
257
|
-
<VoucherSquare name={ante.voucher} hit={voucherHit} />
|
|
258
|
-
<div style={{ flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
259
|
-
{ante.boosterPacks?.length > 0
|
|
260
|
-
? <PackGrid packs={ante.boosterPacks} />
|
|
261
|
-
: <div style={{ color: C.GREY, fontSize: 10, fontFamily: 'm6x11plus, monospace' }}>NO PACKS</div>}
|
|
262
|
-
</div>
|
|
263
|
-
</div>
|
|
264
|
-
</Panel>
|
|
265
|
-
|
|
266
|
-
{/* Shop tape(s) */}
|
|
267
|
-
{shopRounds.map((r, i) => (
|
|
268
|
-
<ShopTape key={i} items={r.items} label={r.label} />
|
|
269
|
-
))}
|
|
270
|
-
|
|
271
|
-
{/* Soul joker callout */}
|
|
272
|
-
{ante.soulJoker && (
|
|
273
|
-
<Panel style={{ padding: 8, borderColor: soulHit === 'must' ? C.GOLD : C.DARK_PURPLE }}>
|
|
274
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
275
|
-
<GlowRing kind={soulHit}><JokerMini name={ante.soulJoker.value} size={44} edition={ante.soulJoker.edition} /></GlowRing>
|
|
276
|
-
<div>
|
|
277
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 9, color: C.GOLD_TEXT, letterSpacing: 2 }}>SOUL JOKER</div>
|
|
278
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 14, color: C.WHITE }}>{ante.soulJoker.value}</div>
|
|
279
|
-
</div>
|
|
280
|
-
</div>
|
|
281
|
-
</Panel>
|
|
282
|
-
)}
|
|
283
|
-
</div>
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
window.AntePageV2 = AntePageV2;
|
|
288
|
-
window.GlowRing = GlowRing;
|
|
289
|
-
window.bestHit = bestHit;
|
|
290
|
-
window.hitColor = hitColor;
|