jaml-ui 0.14.3 → 0.16.0
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/assets.d.ts +1 -0
- package/dist/assets.js +2 -0
- package/dist/decode/motelyItemDecoder.js +27 -3
- package/dist/decode/motelySprite.d.ts +15 -0
- package/dist/decode/motelySprite.js +27 -0
- package/dist/motely.d.ts +1 -0
- package/dist/motely.js +1 -0
- package/dist/r3f/Card3D.js +2 -0
- package/dist/r3f/JimboBillboard.d.ts +10 -0
- package/dist/r3f/JimboBillboard.js +29 -0
- package/dist/r3f/JimboText3D.d.ts +9 -0
- package/dist/r3f/JimboText3D.js +7 -0
- package/dist/r3f.d.ts +2 -0
- package/dist/r3f.js +2 -0
- package/dist/sprites/spriteData.d.ts +1 -0
- package/dist/sprites/spriteData.js +1 -0
- package/dist/sprites/spriteMapper.d.ts +7 -1
- package/dist/sprites/spriteMapper.js +12 -0
- package/dist/ui/JimboBadge.d.ts +7 -0
- package/dist/ui/JimboBadge.js +24 -0
- package/dist/ui/JimboFloating.d.ts +8 -0
- package/dist/ui/JimboFloating.js +17 -0
- package/dist/ui/JimboToggleList.d.ts +11 -0
- package/dist/ui/JimboToggleList.js +10 -0
- package/dist/ui/footer.js +1 -1
- package/dist/ui/jimboFlankNav.js +5 -5
- package/dist/ui/panel.d.ts +2 -1
- package/dist/ui/panel.js +4 -4
- package/dist/ui/sprites.d.ts +14 -0
- package/dist/ui/sprites.js +52 -12
- package/dist/ui.d.ts +3 -0
- package/dist/ui.js +3 -0
- package/package.json +129 -122
- 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
package/DESIGN.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Jimbo
|
|
3
|
+
description: Balatro-inspired design system for JAML seed finder tools. Eyedropped from actual game shader output — not Lua hex values.
|
|
4
|
+
colors:
|
|
5
|
+
red: "#ff4c40"
|
|
6
|
+
blue: "#0093ff"
|
|
7
|
+
green: "#429f79"
|
|
8
|
+
orange: "#ff9800"
|
|
9
|
+
gold: "#e4b643"
|
|
10
|
+
purple: "#9e74ce"
|
|
11
|
+
dark-red: "#a02721"
|
|
12
|
+
dark-blue: "#0057a1"
|
|
13
|
+
dark-orange: "#a05b00"
|
|
14
|
+
dark-green: "#215f46"
|
|
15
|
+
dark-purple: "#5e437e"
|
|
16
|
+
dark-grey: "#3a5055"
|
|
17
|
+
darkest: "#1e2b2d"
|
|
18
|
+
grey: "#708386"
|
|
19
|
+
teal-grey: "#404c4e"
|
|
20
|
+
panel-edge: "#1e2e32"
|
|
21
|
+
inner-border: "#334461"
|
|
22
|
+
border-silver: "#b9c2d2"
|
|
23
|
+
border-south: "#777e89"
|
|
24
|
+
gold-text: "#e4b643"
|
|
25
|
+
green-text: "#35bd86"
|
|
26
|
+
orange-text: "#ff8f00"
|
|
27
|
+
white: "#ffffff"
|
|
28
|
+
black: "#000000"
|
|
29
|
+
tarot-button: "#9e74ce"
|
|
30
|
+
planet-button: "#00a7ca"
|
|
31
|
+
spectral-button: "#2e76fd"
|
|
32
|
+
typography:
|
|
33
|
+
display:
|
|
34
|
+
fontFamily: m6x11plus, monospace
|
|
35
|
+
fontSize: 26px
|
|
36
|
+
fontWeight: 400
|
|
37
|
+
lineHeight: 1
|
|
38
|
+
letterSpacing: 0.04em
|
|
39
|
+
heading:
|
|
40
|
+
fontFamily: m6x11plus, monospace
|
|
41
|
+
fontSize: 14px
|
|
42
|
+
fontWeight: 400
|
|
43
|
+
lineHeight: 1.2
|
|
44
|
+
letterSpacing: 0.08em
|
|
45
|
+
body:
|
|
46
|
+
fontFamily: m6x11plus, monospace
|
|
47
|
+
fontSize: 11px
|
|
48
|
+
fontWeight: 400
|
|
49
|
+
lineHeight: 1.3
|
|
50
|
+
letterSpacing: 0.05em
|
|
51
|
+
label:
|
|
52
|
+
fontFamily: m6x11plus, monospace
|
|
53
|
+
fontSize: 9px
|
|
54
|
+
fontWeight: 400
|
|
55
|
+
lineHeight: 1
|
|
56
|
+
letterSpacing: 0.1em
|
|
57
|
+
micro:
|
|
58
|
+
fontFamily: m6x11plus, monospace
|
|
59
|
+
fontSize: 8px
|
|
60
|
+
fontWeight: 400
|
|
61
|
+
lineHeight: 1
|
|
62
|
+
letterSpacing: 0.08em
|
|
63
|
+
rounded:
|
|
64
|
+
sm: 4px
|
|
65
|
+
md: 6px
|
|
66
|
+
lg: 8px
|
|
67
|
+
pill: 10px
|
|
68
|
+
spacing:
|
|
69
|
+
xs: 2px
|
|
70
|
+
sm: 4px
|
|
71
|
+
md: 8px
|
|
72
|
+
lg: 12px
|
|
73
|
+
xl: 16px
|
|
74
|
+
components:
|
|
75
|
+
panel:
|
|
76
|
+
backgroundColor: "{colors.dark-grey}"
|
|
77
|
+
rounded: "{rounded.md}"
|
|
78
|
+
panel-edge:
|
|
79
|
+
backgroundColor: "{colors.panel-edge}"
|
|
80
|
+
panel-darkest:
|
|
81
|
+
backgroundColor: "{colors.darkest}"
|
|
82
|
+
rounded: "{rounded.md}"
|
|
83
|
+
button-primary:
|
|
84
|
+
backgroundColor: "{colors.red}"
|
|
85
|
+
textColor: "{colors.white}"
|
|
86
|
+
rounded: "{rounded.md}"
|
|
87
|
+
padding: 8px
|
|
88
|
+
button-primary-hover:
|
|
89
|
+
backgroundColor: "{colors.dark-red}"
|
|
90
|
+
button-secondary:
|
|
91
|
+
backgroundColor: "{colors.blue}"
|
|
92
|
+
textColor: "{colors.white}"
|
|
93
|
+
rounded: "{rounded.md}"
|
|
94
|
+
padding: 8px
|
|
95
|
+
button-secondary-hover:
|
|
96
|
+
backgroundColor: "{colors.dark-blue}"
|
|
97
|
+
button-back:
|
|
98
|
+
backgroundColor: "{colors.orange}"
|
|
99
|
+
textColor: "{colors.white}"
|
|
100
|
+
rounded: "{rounded.md}"
|
|
101
|
+
padding: 8px
|
|
102
|
+
button-back-hover:
|
|
103
|
+
backgroundColor: "{colors.dark-orange}"
|
|
104
|
+
tab-active:
|
|
105
|
+
backgroundColor: "{colors.gold}"
|
|
106
|
+
textColor: "{colors.black}"
|
|
107
|
+
rounded: "{rounded.sm}"
|
|
108
|
+
tab-inactive:
|
|
109
|
+
backgroundColor: "transparent"
|
|
110
|
+
textColor: "{colors.grey}"
|
|
111
|
+
score-must:
|
|
112
|
+
backgroundColor: "{colors.blue}"
|
|
113
|
+
textColor: "{colors.white}"
|
|
114
|
+
score-should:
|
|
115
|
+
backgroundColor: "{colors.red}"
|
|
116
|
+
textColor: "{colors.white}"
|
|
117
|
+
glow-must:
|
|
118
|
+
backgroundColor: "{colors.gold}"
|
|
119
|
+
glow-should:
|
|
120
|
+
backgroundColor: "{colors.green-text}"
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Overview
|
|
124
|
+
|
|
125
|
+
Jimbo is the design system for Balatro seed finder tools (JAML-UI, WeeJoker, Seed Finder). It recreates the cozy, tactile, chunky feel of LocalThunk's Balatro — dark panels with silver borders, 3D-press buttons, pixel typography, juice animations. Everything feels like a physical object you can poke.
|
|
126
|
+
|
|
127
|
+
The system is built **Mobile First**. The absolute minimum viewport width is **320px**. All components must be accessible and usable at 320px without breaking layouts or horizontal scrolling. No fat padding, no bloated margins — every pixel earns its place.
|
|
128
|
+
|
|
129
|
+
## Colors
|
|
130
|
+
|
|
131
|
+
All colors are eyedropped from Balatro's actual rendered shader output. Do NOT substitute with Lua source hex values — the game's shader pipeline transforms them.
|
|
132
|
+
|
|
133
|
+
- **Red (#ff4c40):** Primary action, mult scoring, should-clause hits. The "play" color.
|
|
134
|
+
- **Blue (#0093ff):** Secondary action, chips scoring, must-clause gates. The "requirement" color.
|
|
135
|
+
- **Green (#429f79):** Success, positive state, money.
|
|
136
|
+
- **Orange (#ff9800):** Back/return actions, warning.
|
|
137
|
+
- **Gold (#e4b643):** Seed text, premium highlights, active tab. The "treasure" color.
|
|
138
|
+
- **Purple (#9e74ce):** Joker rarity, tarot cards.
|
|
139
|
+
- **Dark Grey (#3a5055):** Panel backgrounds — the primary surface.
|
|
140
|
+
- **Darkest (#1e2b2d):** Deepest background, inset areas.
|
|
141
|
+
- **Grey (#708386):** Disabled text, labels, inactive elements.
|
|
142
|
+
- **Border Silver (#b9c2d2):** Panel top/side borders — the "silver frame."
|
|
143
|
+
- **Border South (#777e89):** Panel bottom border — creates the 3D depth illusion.
|
|
144
|
+
- **Panel Edge (#1e2e32):** Thin outer edge on panels.
|
|
145
|
+
|
|
146
|
+
Must-clause items glow blue. Should-clause items glow gold/green. Non-matching items render at 40-60% opacity with slight grayscale.
|
|
147
|
+
|
|
148
|
+
## Typography
|
|
149
|
+
|
|
150
|
+
m6x11plus (m6x11plusplus.otf) is the ONLY font. It is a single-weight pixel font. NEVER apply font-weight bold, semibold, or any weight other than 400. Bold makes it look muddy. Use size and letter-spacing for hierarchy instead.
|
|
151
|
+
|
|
152
|
+
All text is uppercase with generous letter-spacing (0.04em-0.1em) for labels and micro text. Seed codes use the display size (26px) in gold (#e4b643) with 0.04em tracking.
|
|
153
|
+
|
|
154
|
+
## Layout
|
|
155
|
+
|
|
156
|
+
Target: Minimum 320px portrait width. Components must scale gracefully using relative units and flexible layouts. Avoid fixed widths that break at 320px. Vertical snap-scroll for ante pages. Horizontal swipe for seed navigation.
|
|
157
|
+
|
|
158
|
+
Panels use 2px solid borders with border-silver on top/sides and border-south on bottom, creating a subtle 3D card effect. Inner shadow: `inset 0 0 0 1px rgba(255,255,255,0.04)`. Outer shadow: `0 2px 0 #000`.
|
|
159
|
+
|
|
160
|
+
## Elevation & Depth
|
|
161
|
+
|
|
162
|
+
Buttons have a colored "underside" via box-shadow (not blur). On press, translateY increases by 2-3px and the shadow collapses — the button physically sinks. On hover, translateY decreases by 2px (lifts) with a tiny brightness bump.
|
|
163
|
+
|
|
164
|
+
Panels sit on a dark south-shadow (`0 3px 0 rgba(0,0,0,0.55)`). Translucent panels (for swirl-background contexts) use `rgba(15, 24, 26, 0.78)` with `backdrop-filter: blur(2px)`.
|
|
165
|
+
|
|
166
|
+
JAML-hit items get a GlowRing: `box-shadow: 0 0 0 2px [color], 0 0 10px [color]` with a 1.6s pulse animation. Must = blue glow, should = gold/green glow.
|
|
167
|
+
|
|
168
|
+
## Components
|
|
169
|
+
|
|
170
|
+
**Button:** Chunky 3D press. Colored underside via box-shadow. Hover lifts -2px + brightness. Press sinks +2-3px + shadow collapse. Variants: primary (red), secondary (blue), back (orange), ghost (transparent). Sizes via padding, not font-size. Easing: `cubic-bezier(0.34, 1.56, 0.64, 1)`.
|
|
171
|
+
|
|
172
|
+
**Panel:** Dark grey (#3a5055) background, 2px solid border (silver top/sides, south bottom), border-radius 6px. Inner highlight: `inset 0 0 0 1px rgba(255,255,255,0.04)`. Drop: `0 2px 0 #000`.
|
|
173
|
+
|
|
174
|
+
**Tabs (JimboTabs):** Active tab = gold background + black text. Inactive = transparent + grey text. No bold. Small radius (4px). Tabs are the ONLY navigation — no hamburger menus, no sidebars on mobile.
|
|
175
|
+
|
|
176
|
+
**ScoreCol:** Must-clauses show as blue-framed boxes with checkmark/cross badge. Should-clauses show as bare sprites with a red corner count badge (x2, x3...). Unlit = 40% opacity + grayscale.
|
|
177
|
+
|
|
178
|
+
**ShopTape:** Horizontal grab-scrollable strip of item sprites. Edge fades (linear-gradient masks) on left/right. Hidden scrollbar. Cursor: grab/grabbing.
|
|
179
|
+
|
|
180
|
+
**PackCell:** Tap to fan — cards explode out with springy stagger (40ms delay, juice easing). Tap again to collapse. Closed state = single pack sprite with type label.
|
|
181
|
+
|
|
182
|
+
**GlowRing:** Pulsing outline around JAML-hit items. `0 0 0 2px [color], 0 0 10px [color]`. Animation: opacity 0.55 → 1.0 over 1.6s ease-in-out infinite.
|
|
183
|
+
|
|
184
|
+
**SeedPagerHeader:** Three columns: [left stride arrow] [identity panel with seed + copy + deck/stake] [right stride arrow]. Stride arrows are tall red bars with dark-red inset shadow. Identity panel is translucent with counter pip.
|
|
185
|
+
|
|
186
|
+
## Do's and Don'ts
|
|
187
|
+
|
|
188
|
+
- DO use m6x11plus for everything. No fallback display fonts.
|
|
189
|
+
- DO eyedrop colors from the game. Never guess or approximate.
|
|
190
|
+
- DO design for 320px portrait first. Desktop is an expanded view of the mobile baseline.
|
|
191
|
+
- DO use translateY + box-shadow for button depth. Not CSS 3D transforms.
|
|
192
|
+
- DO dim non-matching items (opacity 0.4 + grayscale 0.6). They stay visible for context.
|
|
193
|
+
- DON'T use font-weight bold. m6x11plus is single-weight. Bold = muddy.
|
|
194
|
+
- DON'T use fat padding or margins. Balatro UI is dense and cozy.
|
|
195
|
+
- DON'T add horizontal scroll. Vertical snap-scroll + horizontal swipe only.
|
|
196
|
+
- DON'T use rounded corners larger than 10px. Balatro is chunky, not bubbly.
|
|
197
|
+
- DON'T use blur-based shadows for depth. Use solid colored box-shadows.
|
package/dist/assets.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const JAML_ASSET_FILES: {
|
|
|
9
9
|
readonly vouchers: "Vouchers.png";
|
|
10
10
|
readonly stickers: "stickers.png";
|
|
11
11
|
readonly tags: "tags.png";
|
|
12
|
+
readonly stakes: "balatro-stake-chips.png";
|
|
12
13
|
};
|
|
13
14
|
export type JamlAssetKey = keyof typeof JAML_ASSET_FILES;
|
|
14
15
|
export type JamlAssetFile = (typeof JAML_ASSET_FILES)[JamlAssetKey];
|
package/dist/assets.js
CHANGED
|
@@ -9,6 +9,7 @@ export const JAML_ASSET_FILES = {
|
|
|
9
9
|
vouchers: "Vouchers.png",
|
|
10
10
|
stickers: "stickers.png",
|
|
11
11
|
tags: "tags.png",
|
|
12
|
+
stakes: "balatro-stake-chips.png",
|
|
12
13
|
};
|
|
13
14
|
const assetKeyByFileName = Object.fromEntries(Object.entries(JAML_ASSET_FILES).map(([key, fileName]) => [fileName, key]));
|
|
14
15
|
const defaultAssetUrls = {
|
|
@@ -22,6 +23,7 @@ const defaultAssetUrls = {
|
|
|
22
23
|
vouchers: new URL(`../assets/${JAML_ASSET_FILES.vouchers}`, import.meta.url).href,
|
|
23
24
|
stickers: new URL(`../assets/${JAML_ASSET_FILES.stickers}`, import.meta.url).href,
|
|
24
25
|
tags: new URL(`../assets/${JAML_ASSET_FILES.tags}`, import.meta.url).href,
|
|
26
|
+
stakes: new URL(`../assets/${JAML_ASSET_FILES.stakes}`, import.meta.url).href,
|
|
25
27
|
};
|
|
26
28
|
let customAssetBaseUrl = null;
|
|
27
29
|
function normalizeBaseUrl(baseUrl) {
|
|
@@ -23,15 +23,39 @@ const CATEGORY_TO_TYPE = {
|
|
|
23
23
|
};
|
|
24
24
|
// ─── Reverse lookup: MotelyItemType integer → string name ────────────────────
|
|
25
25
|
const _itemTypeToName = new Map();
|
|
26
|
+
const MOTELY_ITEM_TYPES_STANDARD = [
|
|
27
|
+
"TwoOfClubs", "ThreeOfClubs", "FourOfClubs", "FiveOfClubs", "SixOfClubs", "SevenOfClubs", "EightOfClubs", "NineOfClubs", "TenOfClubs", "JackOfClubs", "QueenOfClubs", "KingOfClubs", "AceOfClubs",
|
|
28
|
+
"TwoOfDiamonds", "ThreeOfDiamonds", "FourOfDiamonds", "FiveOfDiamonds", "SixOfDiamonds", "SevenOfDiamonds", "EightOfDiamonds", "NineOfDiamonds", "TenOfDiamonds", "JackOfDiamonds", "QueenOfDiamonds", "KingOfDiamonds", "AceOfDiamonds",
|
|
29
|
+
"TwoOfHearts", "ThreeOfHearts", "FourOfHearts", "FiveOfHearts", "SixOfHearts", "SevenOfHearts", "EightOfHearts", "NineOfHearts", "TenOfHearts", "JackOfHearts", "QueenOfHearts", "KingOfHearts", "AceOfHearts",
|
|
30
|
+
"TwoOfSpades", "ThreeOfSpades", "FourOfSpades", "FiveOfSpades", "SixOfSpades", "SevenOfSpades", "EightOfSpades", "NineOfSpades", "TenOfSpades", "JackOfSpades", "QueenOfSpades", "KingOfSpades", "AceOfSpades"
|
|
31
|
+
];
|
|
32
|
+
const MOTELY_ITEM_TYPES_SPECTRAL = ["Familiar", "Grim", "Incantation", "Talisman", "Aura", "Wraith", "Sigil", "Ouija", "Ectoplasm", "Immolate", "Ankh", "DejaVu", "Hex", "Trance", "Medium", "Cryptid", "TheSoul", "BlackHole"];
|
|
33
|
+
const MOTELY_ITEM_TYPES_TAROT = ["TheFool", "TheMagician", "TheHighPriestess", "TheEmpress", "TheEmperor", "TheHierophant", "TheLovers", "TheChariot", "Justice", "TheHermit", "TheWheelOfFortune", "Strength", "TheHangedMan", "Death", "Temperance", "TheDevil", "TheTower", "TheStar", "TheMoon", "TheSun", "Judgement", "TheWorld"];
|
|
34
|
+
const MOTELY_ITEM_TYPES_PLANET = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "PlanetX", "Ceres", "Eris"];
|
|
35
|
+
const MOTELY_ITEM_TYPES_JOKER = ["Joker", "GreedyJoker", "LustyJoker", "WrathfulJoker", "GluttonousJoker", "JollyJoker", "ZanyJoker", "MadJoker", "CrazyJoker", "DrollJoker", "SlyJoker", "WilyJoker", "CleverJoker", "DeviousJoker", "CraftyJoker", "HalfJoker", "CreditCard", "Banner", "MysticSummit", "EightBall", "Misprint", "RaisedFist", "ChaostheClown", "ScaryFace", "AbstractJoker", "DelayedGratification", "GrosMichel", "EvenSteven", "OddTodd", "Scholar", "BusinessCard", "Supernova", "RideTheBus", "Egg", "Runner", "IceCream", "Splash", "BlueJoker", "FacelessJoker", "GreenJoker", "Superposition", "ToDoList", "Cavendish", "RedCard", "SquareJoker", "RiffRaff", "Photograph", "ReservedParking", "MailInRebate", "Hallucination", "FortuneTeller", "Juggler", "Drunkard", "GoldenJoker", "Popcorn", "WalkieTalkie", "SmileyFace", "GoldenTicket", "Swashbuckler", "HangingChad", "ShootTheMoon", "JokerStencil", "FourFingers", "Mime", "CeremonialDagger", "MarbleJoker", "LoyaltyCard", "Dusk", "Fibonacci", "SteelJoker", "Hack", "Pareidolia", "SpaceJoker", "Burglar", "Blackboard", "SixthSense", "Constellation", "Hiker", "CardSharp", "Madness", "Seance", "Vampire", "Shortcut", "Hologram", "Cloud9", "Rocket", "MidasMask", "Luchador", "GiftCard", "TurtleBean", "Erosion", "ToTheMoon", "StoneJoker", "LuckyCat", "Bull", "DietCola", "TradingCard", "FlashCard", "SpareTrousers", "Ramen", "Seltzer", "Castle", "MrBones", "Acrobat", "SockAndBuskin", "Troubadour", "Certificate", "SmearedJoker", "Throwback", "RoughGem", "Bloodstone", "Arrowhead", "OnyxAgate", "GlassJoker", "Showman", "FlowerPot", "MerryAndy", "OopsAll6s", "TheIdol", "SeeingDouble", "Matador", "Satellite", "Cartomancer", "Astronomer", "Bootstraps", "DNA", "Vagabond", "Baron", "Obelisk", "BaseballCard", "AncientJoker", "Campfire", "Blueprint", "WeeJoker", "HitTheRoad", "TheDuo", "TheTrio", "TheFamily", "TheOrder", "TheTribe", "Stuntman", "InvisibleJoker", "Brainstorm", "DriversLicense", "BurntJoker", "Canio", "Triboulet", "Yorick", "Chicot", "Perkeo"];
|
|
36
|
+
const MOTELY_ITEM_TYPES_INVALID = ["Invalid", "NotImplemented", "JokerExcludedByStream", "PlanetExcludedByStream", "TarotExcludedByStream", "SpectralExcludedByStream"];
|
|
26
37
|
function ensureItemTypeMap() {
|
|
27
38
|
if (_itemTypeToName.size > 0)
|
|
28
39
|
return;
|
|
40
|
+
// Fallback to runtime enum if present (motely-wasm < 14)
|
|
29
41
|
const e = Motely.MotelyItemType;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
if (e && Object.keys(e).length > 0) {
|
|
43
|
+
for (const [key, val] of Object.entries(e)) {
|
|
44
|
+
if (typeof val === "number" && typeof key === "string" && !/^\d+$/.test(key)) {
|
|
45
|
+
_itemTypeToName.set(val, key);
|
|
46
|
+
}
|
|
33
47
|
}
|
|
34
48
|
}
|
|
49
|
+
else {
|
|
50
|
+
// Populate using hardcoded categories (motely-wasm 14+)
|
|
51
|
+
_itemTypeToName.set(0, "None"); // Handle 0
|
|
52
|
+
MOTELY_ITEM_TYPES_STANDARD.forEach((name, i) => _itemTypeToName.set(0x1000 + i, name));
|
|
53
|
+
MOTELY_ITEM_TYPES_SPECTRAL.forEach((name, i) => _itemTypeToName.set(0x2000 + i, name));
|
|
54
|
+
MOTELY_ITEM_TYPES_TAROT.forEach((name, i) => _itemTypeToName.set(0x3000 + i, name));
|
|
55
|
+
MOTELY_ITEM_TYPES_PLANET.forEach((name, i) => _itemTypeToName.set(0x4000 + i, name));
|
|
56
|
+
MOTELY_ITEM_TYPES_JOKER.forEach((name, i) => _itemTypeToName.set(0x5000 + i, name));
|
|
57
|
+
MOTELY_ITEM_TYPES_INVALID.forEach((name, i) => _itemTypeToName.set(0xf000 + i, name));
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
60
|
function asRuntimeItem(input) {
|
|
37
61
|
return input !== null && typeof input === "object" ? input : null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type MotelyRenderableCategory } from "./motelyItemDecoder.js";
|
|
2
|
+
export interface MotelySpriteData {
|
|
3
|
+
atlasPath: string;
|
|
4
|
+
gridCol: number;
|
|
5
|
+
gridRow: number;
|
|
6
|
+
gridCols: number;
|
|
7
|
+
gridRows: number;
|
|
8
|
+
displayName: string;
|
|
9
|
+
category: MotelyRenderableCategory;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),
|
|
13
|
+
* resolves it to a sprite atlas path and grid coordinates for rendering.
|
|
14
|
+
*/
|
|
15
|
+
export declare function motelyItemToSprite(rawValue: number): MotelySpriteData | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { decodeMotelyItem } from "./motelyItemDecoder.js";
|
|
2
|
+
import { getSpriteData, SHEET_META } from "../sprites/spriteMapper.js";
|
|
3
|
+
import { resolveJamlAssetUrl } from "../assets.js";
|
|
4
|
+
/**
|
|
5
|
+
* Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),
|
|
6
|
+
* resolves it to a sprite atlas path and grid coordinates for rendering.
|
|
7
|
+
*/
|
|
8
|
+
export function motelyItemToSprite(rawValue) {
|
|
9
|
+
const decoded = decodeMotelyItem(rawValue);
|
|
10
|
+
if (!decoded)
|
|
11
|
+
return null;
|
|
12
|
+
const sprite = getSpriteData(decoded.displayName);
|
|
13
|
+
if (!sprite)
|
|
14
|
+
return null;
|
|
15
|
+
const meta = SHEET_META[sprite.type];
|
|
16
|
+
if (!meta)
|
|
17
|
+
return null;
|
|
18
|
+
return {
|
|
19
|
+
atlasPath: resolveJamlAssetUrl(meta.assetKey),
|
|
20
|
+
gridCol: sprite.pos.x,
|
|
21
|
+
gridRow: sprite.pos.y,
|
|
22
|
+
gridCols: meta.cols,
|
|
23
|
+
gridRows: meta.rows,
|
|
24
|
+
displayName: decoded.displayName,
|
|
25
|
+
category: decoded.category
|
|
26
|
+
};
|
|
27
|
+
}
|
package/dist/motely.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { decodeMotelyItem, decodeMotelyItemToJamlCard, motelyItemTypeName, motelyItemCategory, motelyItemDisplayName, motelyItemRenderCategory, motelyItemEditionName, motelyItemSealName, motelyItemEnhancementName, motelyStandardcardRankName, motelyStandardcardSuitName, decodeMotelyItemName, resolveMotelyItemType, warmMotelyItemCache, motelyItemCacheSize, type DecodedMotelyItem, type MotelyItemInput, type MotelyJamlCard, type MotelyRenderableCategory, type MotelyRuntimeItem, } from "./decode/motelyItemDecoder.js";
|
|
2
|
+
export { motelyItemToSprite, type MotelySpriteData, } from "./decode/motelySprite.js";
|
|
2
3
|
export { MOTELY_DISPLAY_SCHEMA, motelyBossDisplayName, motelyBossDisplayNameFromKey, motelyBossKeyFromDisplayName, motelyBoosterPackDisplayName, motelyBoosterPackDisplayNameFromKey, motelyBoosterPackKeyFromDisplayName, motelyItemDisplayNameFromKey, motelyItemDisplayNameFromValue, motelyTagDisplayName, motelyTagDisplayNameFromKey, motelyTagKeyFromDisplayName, motelyVoucherDisplayName, motelyVoucherDisplayNameFromKey, motelyVoucherKeyFromDisplayName, type MotelyBoosterPackKey, type MotelyBossKey, type MotelyDisplaySchema, type MotelyTagKey, type MotelyVoucherKey, } from "./motelyDisplay.js";
|
package/dist/motely.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
export { decodeMotelyItem, decodeMotelyItemToJamlCard, motelyItemTypeName, motelyItemCategory, motelyItemDisplayName, motelyItemRenderCategory, motelyItemEditionName, motelyItemSealName, motelyItemEnhancementName, motelyStandardcardRankName, motelyStandardcardSuitName, decodeMotelyItemName, resolveMotelyItemType, warmMotelyItemCache, motelyItemCacheSize, } from "./decode/motelyItemDecoder.js";
|
|
3
|
+
export { motelyItemToSprite, } from "./decode/motelySprite.js";
|
|
3
4
|
export { MOTELY_DISPLAY_SCHEMA, motelyBossDisplayName, motelyBossDisplayNameFromKey, motelyBossKeyFromDisplayName, motelyBoosterPackDisplayName, motelyBoosterPackDisplayNameFromKey, motelyBoosterPackKeyFromDisplayName, motelyItemDisplayNameFromKey, motelyItemDisplayNameFromValue, motelyTagDisplayName, motelyTagDisplayNameFromKey, motelyTagKeyFromDisplayName, motelyVoucherDisplayName, motelyVoucherDisplayNameFromKey, motelyVoucherKeyFromDisplayName, } from "./motelyDisplay.js";
|
package/dist/r3f/Card3D.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MotelySpriteData } from '../decode/motelySprite.js';
|
|
2
|
+
export interface JimboBillboardProps {
|
|
3
|
+
sprite: MotelySpriteData | null;
|
|
4
|
+
label?: string;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
yLockOnly?: boolean;
|
|
8
|
+
position?: [number, number, number];
|
|
9
|
+
}
|
|
10
|
+
export declare function JimboBillboard({ sprite, label, width, height, yLockOnly, position }: JimboBillboardProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { Billboard } from '@react-three/drei';
|
|
4
|
+
import { useLoader } from '@react-three/fiber';
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
export function JimboBillboard({ sprite, label, width = 3.4, height = 4.5, yLockOnly = false, position = [0, 0, 0] }) {
|
|
7
|
+
if (!sprite)
|
|
8
|
+
return null;
|
|
9
|
+
// Memoize texture to avoid per-render allocation
|
|
10
|
+
const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);
|
|
11
|
+
const clonedTexture = useMemo(() => {
|
|
12
|
+
const tex = texture.clone();
|
|
13
|
+
tex.magFilter = THREE.NearestFilter;
|
|
14
|
+
tex.minFilter = THREE.NearestFilter;
|
|
15
|
+
// Set up sprite cropping
|
|
16
|
+
tex.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);
|
|
17
|
+
tex.offset.set(sprite.gridCol / sprite.gridCols, 1 - ((sprite.gridRow + 1) / sprite.gridRows));
|
|
18
|
+
tex.needsUpdate = true;
|
|
19
|
+
return tex;
|
|
20
|
+
}, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);
|
|
21
|
+
const material = useMemo(() => {
|
|
22
|
+
return new THREE.MeshBasicMaterial({
|
|
23
|
+
map: clonedTexture,
|
|
24
|
+
transparent: true,
|
|
25
|
+
alphaTest: 0.5
|
|
26
|
+
});
|
|
27
|
+
}, [clonedTexture]);
|
|
28
|
+
return (_jsx(Billboard, { lockY: yLockOnly, lockX: false, lockZ: false, position: position, children: _jsx("mesh", { material: material, children: _jsx("planeGeometry", { args: [width, height] }) }) }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface JimboText3DProps {
|
|
2
|
+
children: string;
|
|
3
|
+
color?: string;
|
|
4
|
+
outlineColor?: string;
|
|
5
|
+
outlineWidth?: number;
|
|
6
|
+
position?: [number, number, number];
|
|
7
|
+
fontSize?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function JimboText3D({ children, color, outlineColor, outlineWidth, position, fontSize }: JimboText3DProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '@react-three/drei';
|
|
3
|
+
import { resolveJamlAssetUrl } from '../assets.js';
|
|
4
|
+
export function JimboText3D({ children, color = '#ffffff', outlineColor = '#000000', outlineWidth = 0.05, position = [0, 0, 0], fontSize = 1 }) {
|
|
5
|
+
// We use the m6x11plus font from assets if possible, or fallback
|
|
6
|
+
return (_jsx(Text, { position: position, fontSize: fontSize, color: color, outlineColor: outlineColor, outlineWidth: outlineWidth, font: resolveJamlAssetUrl('fonts') /* TODO: ensure m6x11plus ttf is accessible */, anchorX: "center", anchorY: "middle", children: children }));
|
|
7
|
+
}
|
package/dist/r3f.d.ts
CHANGED
package/dist/r3f.js
CHANGED
|
@@ -32,6 +32,7 @@ export declare const SPRITE_SHEETS: {
|
|
|
32
32
|
readonly vouchers: SpriteSheetInfo;
|
|
33
33
|
readonly tags: SpriteSheetInfo;
|
|
34
34
|
readonly boosters: SpriteSheetInfo;
|
|
35
|
+
readonly stakes: SpriteSheetInfo;
|
|
35
36
|
};
|
|
36
37
|
export declare const JOKERS: SpriteEntry[];
|
|
37
38
|
export declare const JOKER_FACES: SpriteEntry[];
|
|
@@ -27,6 +27,7 @@ export const SPRITE_SHEETS = {
|
|
|
27
27
|
vouchers: defineSpriteSheet("vouchers", 9, 4),
|
|
28
28
|
tags: defineSpriteSheet("tags", 6, 5),
|
|
29
29
|
boosters: defineSpriteSheet("boosters", 4, 9),
|
|
30
|
+
stakes: defineSpriteSheet("stakes", 5, 2),
|
|
30
31
|
};
|
|
31
32
|
export const JOKERS = [
|
|
32
33
|
{ name: "Joker", pos: { x: 0, y: 0 } }, { name: "Greedy Joker", pos: { x: 6, y: 1 } }, { name: "Lusty Joker", pos: { x: 7, y: 1 } }, { name: "Wrathful Joker", pos: { x: 8, y: 1 } }, { name: "Gluttonous Joker", pos: { x: 9, y: 1 } }, { name: "Jolly Joker", pos: { x: 2, y: 0 } }, { name: "Zany Joker", pos: { x: 3, y: 0 } }, { name: "Mad Joker", pos: { x: 4, y: 0 } }, { name: "Crazy Joker", pos: { x: 5, y: 0 } }, { name: "Droll Joker", pos: { x: 6, y: 0 } }, { name: "Sly Joker", pos: { x: 0, y: 14 } }, { name: "Wily Joker", pos: { x: 1, y: 14 } }, { name: "Clever Joker", pos: { x: 2, y: 14 } }, { name: "Devious Joker", pos: { x: 3, y: 14 } }, { name: "Crafty Joker", pos: { x: 4, y: 14 } }, { name: "Half Joker", pos: { x: 7, y: 0 } }, { name: "Joker Stencil", pos: { x: 2, y: 5 } }, { name: "Four Fingers", pos: { x: 6, y: 6 } }, { name: "Mime", pos: { x: 4, y: 1 } }, { name: "Credit Card", pos: { x: 5, y: 1 } }, { name: "Ceremonial Dagger", pos: { x: 5, y: 5 } }, { name: "Banner", pos: { x: 1, y: 2 } }, { name: "Mystic Summit", pos: { x: 2, y: 2 } }, { name: "Marble Joker", pos: { x: 3, y: 2 } }, { name: "Loyalty Card", pos: { x: 4, y: 2 } }, { name: "8 Ball", pos: { x: 0, y: 5 } }, { name: "Misprint", pos: { x: 6, y: 2 } }, { name: "Dusk", pos: { x: 4, y: 7 } }, { name: "Raised Fist", pos: { x: 8, y: 2 } }, { name: "Chaos the Clown", pos: { x: 1, y: 0 } }, { name: "Fibonacci", pos: { x: 1, y: 5 } }, { name: "Steel Joker", pos: { x: 7, y: 2 } }, { name: "Scary Face", pos: { x: 2, y: 3 } }, { name: "Abstract Joker", pos: { x: 3, y: 3 } }, { name: "Delayed Gratification", pos: { x: 4, y: 3 } }, { name: "Hack", pos: { x: 5, y: 2 } }, { name: "Pareidolia", pos: { x: 6, y: 3 } }, { name: "Gros Michel", pos: { x: 7, y: 6 } }, { name: "Even Steven", pos: { x: 8, y: 3 } }, { name: "Odd Todd", pos: { x: 9, y: 3 } }, { name: "Scholar", pos: { x: 3, y: 6 } }, { name: "Business Card", pos: { x: 1, y: 4 } }, { name: "Supernova", pos: { x: 2, y: 4 } }, { name: "Ride the Bus", pos: { x: 1, y: 6 } }, { name: "Space Joker", pos: { x: 3, y: 5 } }, { name: "Egg", pos: { x: 0, y: 10 } }, { name: "Burglar", pos: { x: 1, y: 10 } }, { name: "Blackboard", pos: { x: 2, y: 10 } }, { name: "Runner", pos: { x: 3, y: 10 } }, { name: "Ice Cream", pos: { x: 4, y: 10 } }, { name: "DNA", pos: { x: 5, y: 10 } }, { name: "Splash", pos: { x: 6, y: 10 } }, { name: "Blue Joker", pos: { x: 7, y: 10 } }, { name: "Sixth Sense", pos: { x: 8, y: 10 } }, { name: "Constellation", pos: { x: 9, y: 10 } }, { name: "Hiker", pos: { x: 0, y: 11 } }, { name: "Faceless Joker", pos: { x: 1, y: 11 } }, { name: "Green Joker", pos: { x: 2, y: 11 } }, { name: "Superposition", pos: { x: 3, y: 11 } }, { name: "To Do List", pos: { x: 4, y: 11 } }, { name: "Cavendish", pos: { x: 5, y: 11 } }, { name: "Card Sharp", pos: { x: 6, y: 11 } }, { name: "Red Card", pos: { x: 7, y: 11 } }, { name: "Madness", pos: { x: 8, y: 11 } }, { name: "Square Joker", pos: { x: 9, y: 11 } }, { name: "Seance", pos: { x: 0, y: 12 } }, { name: "Riff-raff", pos: { x: 1, y: 12 } }, { name: "Vampire", pos: { x: 2, y: 12 } }, { name: "Shortcut", pos: { x: 3, y: 12 } }, { name: "Hologram", pos: { x: 4, y: 12 } }, { name: "Vagabond", pos: { x: 5, y: 12 } }, { name: "Baron", pos: { x: 6, y: 12 } }, { name: "Cloud 9", pos: { x: 7, y: 12 } }, { name: "Rocket", pos: { x: 8, y: 12 } }, { name: "Obelisk", pos: { x: 9, y: 12 } }, { name: "Midas Mask", pos: { x: 0, y: 13 } }, { name: "Luchador", pos: { x: 1, y: 13 } }, { name: "Photograph", pos: { x: 2, y: 13 } }, { name: "Gift Card", pos: { x: 3, y: 13 } }, { name: "Turtle Bean", pos: { x: 4, y: 13 } }, { name: "Erosion", pos: { x: 5, y: 13 } }, { name: "Reserved Parking", pos: { x: 6, y: 13 } }, { name: "Mail In Rebate", pos: { x: 7, y: 13 } }, { name: "To the Moon", pos: { x: 8, y: 13 } }, { name: "Hallucination", pos: { x: 9, y: 13 } }, { name: "Fortune Teller", pos: { x: 7, y: 5 } }, { name: "Juggler", pos: { x: 0, y: 1 } }, { name: "Drunkard", pos: { x: 1, y: 1 } }, { name: "Stone Joker", pos: { x: 9, y: 0 } }, { name: "Golden Joker", pos: { x: 9, y: 2 } }, { name: "Lucky Cat", pos: { x: 5, y: 14 } }, { name: "Baseball Card", pos: { x: 6, y: 14 } }, { name: "Bull", pos: { x: 7, y: 14 } }, { name: "Diet Cola", pos: { x: 8, y: 14 } }, { name: "Trading Card", pos: { x: 9, y: 14 } }, { name: "Flash Card", pos: { x: 0, y: 15 } }, { name: "Popcorn", pos: { x: 1, y: 15 } }, { name: "Spare Trousers", pos: { x: 4, y: 15 } }, { name: "Ancient Joker", pos: { x: 7, y: 15 } }, { name: "Ramen", pos: { x: 2, y: 15 } }, { name: "Walkie Talkie", pos: { x: 8, y: 15 } }, { name: "Seltzer", pos: { x: 3, y: 15 } }, { name: "Castle", pos: { x: 9, y: 15 } }, { name: "Smiley Face", pos: { x: 6, y: 15 } }, { name: "Campfire", pos: { x: 5, y: 15 } }, { name: "Golden Ticket", pos: { x: 5, y: 3 } }, { name: "Mr. Bones", pos: { x: 3, y: 4 } }, { name: "Acrobat", pos: { x: 2, y: 1 } }, { name: "Sock and Buskin", pos: { x: 3, y: 1 } }, { name: "Swashbuckler", pos: { x: 9, y: 5 } }, { name: "Troubadour", pos: { x: 0, y: 2 } }, { name: "Certificate", pos: { x: 8, y: 8 } }, { name: "Smeared Joker", pos: { x: 4, y: 6 } }, { name: "Throwback", pos: { x: 5, y: 7 } }, { name: "Hanging Chad", pos: { x: 9, y: 6 } }, { name: "Rough Gem", pos: { x: 9, y: 7 } }, { name: "Bloodstone", pos: { x: 0, y: 8 } }, { name: "Arrowhead", pos: { x: 1, y: 8 } }, { name: "Onyx Agate", pos: { x: 2, y: 8 } }, { name: "Glass Joker", pos: { x: 1, y: 3 } }, { name: "Showman", pos: { x: 6, y: 5 } }, { name: "Flower Pot", pos: { x: 0, y: 6 } }, { name: "Blueprint", pos: { x: 0, y: 3 } }, { name: "Wee Joker", pos: { x: 0, y: 4 } }, { name: "Merry Andy", pos: { x: 8, y: 0 } }, { name: "Oops! All 6s", pos: { x: 5, y: 6 } }, { name: "The Idol", pos: { x: 6, y: 7 } }, { name: "Seeing Double", pos: { x: 4, y: 4 } }, { name: "Matador", pos: { x: 4, y: 5 } }, { name: "Hit the Road", pos: { x: 8, y: 5 } }, { name: "The Duo", pos: { x: 5, y: 4 } }, { name: "The Trio", pos: { x: 6, y: 4 } }, { name: "The Family", pos: { x: 7, y: 4 } }, { name: "The Order", pos: { x: 8, y: 4 } }, { name: "The Tribe", pos: { x: 9, y: 4 } }, { name: "Stuntman", pos: { x: 8, y: 6 } }, { name: "Invisible Joker", pos: { x: 1, y: 7 } }, { name: "Brainstorm", pos: { x: 7, y: 7 } }, { name: "Satellite", pos: { x: 8, y: 7 } }, { name: "Shoot the Moon", pos: { x: 2, y: 6 } }, { name: "Drivers License", pos: { x: 0, y: 7 } }, { name: "Cartomancer", pos: { x: 7, y: 3 } }, { name: "Astronomer", pos: { x: 2, y: 7 } }, { name: "Burnt Joker", pos: { x: 3, y: 7 } }, { name: "Bootstraps", pos: { x: 9, y: 8 } }, { name: "Canio", pos: { x: 3, y: 8 } }, { name: "Triboulet", pos: { x: 4, y: 8 } }, { name: "Yorick", pos: { x: 5, y: 8 } }, { name: "Chicot", pos: { x: 6, y: 8 } }, { name: "Perkeo", pos: { x: 7, y: 8 } },
|
|
@@ -2,7 +2,13 @@ export interface SpritePos {
|
|
|
2
2
|
x: number;
|
|
3
3
|
y: number;
|
|
4
4
|
}
|
|
5
|
-
export type SpriteSheetType = "Jokers" | "Tarots" | "Vouchers" | "Boosters" | "Enhancers" | "Editions" | "BlindChips" | "tags";
|
|
5
|
+
export type SpriteSheetType = "Jokers" | "Tarots" | "Vouchers" | "Boosters" | "Enhancers" | "Editions" | "BlindChips" | "tags" | "Stakes" | "Decks";
|
|
6
|
+
import type { JamlAssetKey } from '../assets.js';
|
|
7
|
+
export declare const SHEET_META: Record<SpriteSheetType, {
|
|
8
|
+
cols: number;
|
|
9
|
+
rows: number;
|
|
10
|
+
assetKey: JamlAssetKey;
|
|
11
|
+
}>;
|
|
6
12
|
export interface SpriteData {
|
|
7
13
|
pos: SpritePos;
|
|
8
14
|
type: SpriteSheetType;
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { JOKERS, JOKER_FACES, TAROTS_AND_PLANETS, CONSUMABLE_FACES, VOUCHERS, BOSSES, TAGS, BOOSTER_PACKS, } from "./spriteData.js";
|
|
2
|
+
export const SHEET_META = {
|
|
3
|
+
Jokers: { cols: 10, rows: 16, assetKey: 'jokers' },
|
|
4
|
+
Tarots: { cols: 10, rows: 6, assetKey: 'tarots' },
|
|
5
|
+
Vouchers: { cols: 9, rows: 4, assetKey: 'vouchers' },
|
|
6
|
+
Boosters: { cols: 4, rows: 9, assetKey: 'boosters' },
|
|
7
|
+
BlindChips: { cols: 21, rows: 31, assetKey: 'blinds' },
|
|
8
|
+
tags: { cols: 6, rows: 5, assetKey: 'tags' },
|
|
9
|
+
Enhancers: { cols: 7, rows: 5, assetKey: 'enhancers' },
|
|
10
|
+
Editions: { cols: 5, rows: 1, assetKey: 'editions' },
|
|
11
|
+
Stakes: { cols: 5, rows: 2, assetKey: 'stakes' },
|
|
12
|
+
Decks: { cols: 13, rows: 4, assetKey: 'deck' },
|
|
13
|
+
};
|
|
2
14
|
const normalize = (name) => name.toLowerCase().replace(/\s+/g, "");
|
|
3
15
|
const stripPrefix = (name) => name.replace(/^(Joker|Tarot|Planet|Voucher|Pack|Edition|Tag) [|:] /i, "").trim();
|
|
4
16
|
const ITEM_MAP = new Map();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface JimboBadgeProps {
|
|
3
|
+
size?: 'sm' | 'md';
|
|
4
|
+
tone?: 'dark' | 'blue' | 'red' | 'green' | 'gold' | 'grey';
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare function JimboBadge({ size, tone, children }: JimboBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { JimboColorOption as C } from './tokens.js';
|
|
3
|
+
export function JimboBadge({ size = 'sm', tone = 'dark', children }) {
|
|
4
|
+
const bgColors = {
|
|
5
|
+
dark: C.DARKEST,
|
|
6
|
+
blue: C.BLUE,
|
|
7
|
+
red: C.RED,
|
|
8
|
+
green: C.GREEN,
|
|
9
|
+
gold: C.GOLD,
|
|
10
|
+
grey: C.GREY
|
|
11
|
+
};
|
|
12
|
+
return (_jsx("span", { style: {
|
|
13
|
+
display: 'inline-flex',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
padding: size === 'sm' ? '2px 6px' : '4px 8px',
|
|
16
|
+
fontSize: size === 'sm' ? 10 : 12,
|
|
17
|
+
background: bgColors[tone],
|
|
18
|
+
color: tone === 'dark' || tone === 'grey' ? C.WHITE : C.DARKEST,
|
|
19
|
+
border: `1px solid ${tone === 'dark' ? C.PANEL_EDGE : C.BLACK}`,
|
|
20
|
+
borderRadius: 4,
|
|
21
|
+
fontFamily: 'var(--font-sans, m6x11plus), monospace',
|
|
22
|
+
whiteSpace: 'nowrap'
|
|
23
|
+
}, children: children }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface JimboFloatingProps {
|
|
3
|
+
anchor?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center';
|
|
4
|
+
offset?: number;
|
|
5
|
+
zIndex?: number;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function JimboFloating({ anchor, offset, zIndex, children }: JimboFloatingProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export function JimboFloating({ anchor = 'top-right', offset = 12, zIndex = 20, children }) {
|
|
3
|
+
const pos = { position: 'absolute', zIndex };
|
|
4
|
+
if (anchor.includes('top'))
|
|
5
|
+
pos.top = offset;
|
|
6
|
+
if (anchor.includes('bottom'))
|
|
7
|
+
pos.bottom = offset;
|
|
8
|
+
if (anchor.includes('left'))
|
|
9
|
+
pos.left = offset;
|
|
10
|
+
if (anchor.includes('right'))
|
|
11
|
+
pos.right = offset;
|
|
12
|
+
if (anchor.includes('center')) {
|
|
13
|
+
pos.left = '50%';
|
|
14
|
+
pos.transform = 'translateX(-50%)';
|
|
15
|
+
}
|
|
16
|
+
return (_jsx("div", { style: pos, children: children }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ToggleItem {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
on: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface JimboToggleListProps {
|
|
7
|
+
items: ToggleItem[];
|
|
8
|
+
onToggle: (id: string) => void;
|
|
9
|
+
title?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function JimboToggleList({ items, onToggle, title }: JimboToggleListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { JimboPanel, JimboButton } from './panel.js';
|
|
3
|
+
export function JimboToggleList({ items, onToggle, title }) {
|
|
4
|
+
return (_jsx(JimboPanel, { children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [title && _jsx("div", { style: { fontSize: 12, color: 'var(--c-grey)', marginBottom: 4 }, children: title }), items.map(item => (_jsxs(JimboButton, { tone: "grey", size: "sm", onClick: () => onToggle(item.id), style: { justifyContent: 'flex-start', textAlign: 'left', display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("div", { style: {
|
|
5
|
+
width: 10, height: 10, flexShrink: 0,
|
|
6
|
+
background: item.on ? 'var(--c-orange)' : 'var(--c-darkest)',
|
|
7
|
+
border: '1px solid var(--c-dark-grey)',
|
|
8
|
+
boxShadow: 'inset 0 1px 2px rgba(0,0,0,0.5)'
|
|
9
|
+
} }), item.label] }, item.id)))] }) }));
|
|
10
|
+
}
|
package/dist/ui/footer.js
CHANGED
|
@@ -9,7 +9,7 @@ const SUITS = [
|
|
|
9
9
|
];
|
|
10
10
|
const CYCLE = '5s';
|
|
11
11
|
export function JimboBalatroFooter({ hidden = false, className = '' }) {
|
|
12
|
-
return (_jsxs("div", { className: ['
|
|
12
|
+
return (_jsxs("div", { className: ['w-full transition-opacity duration-200', hidden ? 'pointer-events-none opacity-0' : 'opacity-100', className].filter(Boolean).join(' '), children: [_jsx("div", { style: { width: '100%', borderTop: '1px solid rgba(255,255,255,0.1)', background: 'rgba(0,0,0,0.9)', padding: '0 1rem 3px', textAlign: 'center' }, children: _jsxs("p", { style: { fontFamily: 'm6x11plus, monospace', fontSize: 'clamp(11px, 0.8vw + 8px, 14px)', display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center', gap: '0 0.5rem', color: 'white', margin: 0 }, children: [_jsx("span", { children: "Not affiliated with LocalThunk or PlayStack" }), _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }, children: ["Made with", ' ', _jsx("span", { style: { position: 'relative', display: 'inline-block', width: '1.5em', height: '1em', verticalAlign: 'middle' }, children: SUITS.map(({ char, kf }) => (_jsx("span", { style: { position: 'absolute', inset: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', opacity: 0, animationName: kf, animationDuration: CYCLE, animationDelay: '0s', animationIterationCount: 'infinite', animationTimingFunction: 'ease-out' }, children: char }, char))) }), ' ', "for the", ' ', _jsx("a", { href: "https://playbalatro.com", target: "_blank", rel: "noopener noreferrer", style: { color: JimboColorOption.GOLD, textDecoration: 'none' }, children: "Balatro" }), ' ', "community"] })] }) }), _jsx("style", { children: `
|
|
13
13
|
@keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
|
|
14
14
|
@keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
|
|
15
15
|
@keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
|
package/dist/ui/jimboFlankNav.js
CHANGED
|
@@ -25,16 +25,16 @@ function NavButton({ direction, onClick, disabled, 'aria-label': ariaLabel, }) {
|
|
|
25
25
|
width: 48,
|
|
26
26
|
border: 'none',
|
|
27
27
|
borderRadius: 8,
|
|
28
|
-
cursor: disabled ? '
|
|
29
|
-
opacity:
|
|
30
|
-
backgroundColor: JimboColorOption.RED,
|
|
28
|
+
cursor: disabled ? 'default' : 'pointer',
|
|
29
|
+
opacity: 1,
|
|
30
|
+
backgroundColor: disabled ? JimboColorOption.DARK_RED : JimboColorOption.RED,
|
|
31
31
|
color: JimboColorOption.WHITE,
|
|
32
32
|
display: 'flex',
|
|
33
33
|
alignItems: 'center',
|
|
34
34
|
justifyContent: 'center',
|
|
35
35
|
transform: pressed ? `translateY(${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px)` : 'translateY(0)',
|
|
36
|
-
boxShadow: pressed ? 'none' : `0 ${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px 0 0 ${JimboColorOption.DARK_RED}`,
|
|
37
|
-
transition: `transform ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease, box-shadow ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease`,
|
|
36
|
+
boxShadow: pressed || disabled ? 'none' : `0 ${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px 0 0 ${JimboColorOption.DARK_RED}`,
|
|
37
|
+
transition: `transform ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease, box-shadow ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease, background-color ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease`,
|
|
38
38
|
}, children: _jsx(ChevronSvg, { direction: direction }) }));
|
|
39
39
|
}
|
|
40
40
|
function ChevronSvg({ direction }) {
|
package/dist/ui/panel.d.ts
CHANGED
|
@@ -14,11 +14,12 @@ export interface JimboButtonProps {
|
|
|
14
14
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
15
15
|
fullWidth?: boolean;
|
|
16
16
|
disabled?: boolean;
|
|
17
|
+
uppercase?: boolean;
|
|
17
18
|
onClick?: () => void;
|
|
18
19
|
style?: React.CSSProperties;
|
|
19
20
|
children?: React.ReactNode;
|
|
20
21
|
}
|
|
21
|
-
export declare function JimboButton({ tone, size, fullWidth, disabled, onClick, style, children, }: JimboButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function JimboButton({ tone, size, fullWidth, disabled, uppercase, onClick, style, children, }: JimboButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
22
23
|
export declare function JimboBackButton({ onClick }: {
|
|
23
24
|
onClick?: () => void;
|
|
24
25
|
}): import("react/jsx-runtime").JSX.Element;
|