jaml-ui 0.16.0 → 0.17.1

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.
Files changed (86) hide show
  1. package/DESIGN.md +9 -11
  2. package/dist/assets.d.ts +6 -0
  3. package/dist/assets.js +9 -0
  4. package/dist/components/AnalyzerExplorer.d.ts +4 -1
  5. package/dist/components/AnalyzerExplorer.js +14 -48
  6. package/dist/components/GameCard.js +8 -7
  7. package/dist/components/JamlAestheticSelector.d.ts +4 -0
  8. package/dist/components/JamlAestheticSelector.js +6 -19
  9. package/dist/components/JamlAnalyzerFullscreen.d.ts +7 -1
  10. package/dist/components/JamlAnalyzerFullscreen.js +18 -47
  11. package/dist/components/JamlIde.js +12 -24
  12. package/dist/components/JamlIdeVisual.js +3 -56
  13. package/dist/components/JamlMapPreview.d.ts +6 -1
  14. package/dist/components/JamlMapPreview.js +99 -21
  15. package/dist/components/JamlSeedInput.d.ts +5 -0
  16. package/dist/components/JamlSeedInput.js +11 -14
  17. package/dist/components/JamlSpeedometer.d.ts +8 -8
  18. package/dist/components/JamlSpeedometer.js +24 -46
  19. package/dist/components/MotelyVersionBadge.d.ts +1 -3
  20. package/dist/components/MotelyVersionBadge.js +4 -16
  21. package/dist/components/jamlMap/JamlMapEditorDemo.d.ts +8 -0
  22. package/dist/components/jamlMap/JamlMapEditorDemo.js +170 -0
  23. package/dist/components/jamlMap/JokerPicker.d.ts +7 -0
  24. package/dist/components/jamlMap/JokerPicker.js +258 -0
  25. package/dist/components/jamlMap/MysterySlot.d.ts +32 -0
  26. package/dist/components/jamlMap/MysterySlot.js +109 -0
  27. package/dist/components/jamlMap/index.d.ts +3 -0
  28. package/dist/components/jamlMap/index.js +3 -0
  29. package/dist/core.d.ts +0 -2
  30. package/dist/core.js +0 -2
  31. package/dist/decode/motelyItemDecoder.d.ts +10 -23
  32. package/dist/decode/motelyItemDecoder.js +103 -272
  33. package/dist/decode/motelySprite.d.ts +4 -0
  34. package/dist/decode/motelySprite.js +57 -0
  35. package/dist/hooks/analyzerStreamRegistry.js +30 -82
  36. package/dist/hooks/useAnalyzer.d.ts +10 -3
  37. package/dist/hooks/useAnalyzer.js +11 -6
  38. package/dist/hooks/useIntersectionObserver.d.ts +14 -0
  39. package/dist/hooks/useIntersectionObserver.js +50 -0
  40. package/dist/index.d.ts +5 -8
  41. package/dist/index.js +4 -7
  42. package/dist/motely.d.ts +2 -2
  43. package/dist/motely.js +2 -2
  44. package/dist/motelyDisplay.d.ts +4 -623
  45. package/dist/motelyDisplay.js +26 -165
  46. package/dist/r3f/Card3D.d.ts +2 -2
  47. package/dist/r3f/Card3D.js +13 -48
  48. package/dist/r3f/JimboText3D.js +3 -2
  49. package/dist/render/CanvasRenderer.js +7 -171
  50. package/dist/sprites/spriteMapper.d.ts +71 -0
  51. package/dist/sprites/spriteMapper.js +40 -0
  52. package/dist/ui/JimboBadge.d.ts +8 -2
  53. package/dist/ui/JimboBadge.js +6 -22
  54. package/dist/ui/JimboToggleList.js +2 -7
  55. package/dist/ui/codeBlock.js +2 -3
  56. package/dist/ui/footer.d.ts +4 -0
  57. package/dist/ui/footer.js +6 -4
  58. package/dist/ui/hooks.d.ts +89 -0
  59. package/dist/ui/hooks.js +551 -0
  60. package/dist/ui/jimboBackground.js +2 -131
  61. package/dist/ui/jimboCopyRow.d.ts +4 -0
  62. package/dist/ui/jimboCopyRow.js +5 -22
  63. package/dist/ui/jimboFilterBar.d.ts +1 -4
  64. package/dist/ui/jimboFilterBar.js +2 -61
  65. package/dist/ui/jimboFlankNav.d.ts +1 -2
  66. package/dist/ui/jimboFlankNav.js +5 -30
  67. package/dist/ui/jimboTabs.d.ts +1 -5
  68. package/dist/ui/jimboTabs.js +6 -41
  69. package/dist/ui/jimboText.d.ts +1 -1
  70. package/dist/ui/jimboText.js +15 -32
  71. package/dist/ui/jimboTooltip.d.ts +1 -12
  72. package/dist/ui/jimboTooltip.js +6 -82
  73. package/dist/ui/panel.d.ts +2 -1
  74. package/dist/ui/panel.js +11 -47
  75. package/dist/ui/showcase.d.ts +4 -0
  76. package/dist/ui/showcase.js +9 -36
  77. package/dist/ui/sprites.js +3 -2
  78. package/dist/ui.d.ts +1 -0
  79. package/dist/ui.js +2 -0
  80. package/package.json +7 -6
  81. package/dist/decode/packedBalatroItem.d.ts +0 -13
  82. package/dist/decode/packedBalatroItem.js +0 -26
  83. package/dist/hooks/loadMotelyWasm.d.ts +0 -7
  84. package/dist/hooks/loadMotelyWasm.js +0 -16
  85. package/dist/utils/itemUtils.d.ts +0 -11
  86. package/dist/utils/itemUtils.js +0 -71
@@ -0,0 +1,258 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useCallback, useMemo } from "react";
4
+ import { JimboSprite } from "../../ui/sprites.js";
5
+ import { JimboColorOption, withAlpha } from "../../ui/tokens.js";
6
+ import { JOKERS } from "../../sprites/spriteData.js";
7
+ const LEGENDARY_JOKERS = new Set([
8
+ "Canio", "Triboulet", "Yorick", "Chicot", "Perkeo",
9
+ ]);
10
+ const RARE_JOKERS = new Set([
11
+ "Blueprint", "Brainstorm", "Drivers License", "Burnt Joker",
12
+ "Cartomancer", "Astronomer", "Satellite", "Shoot the Moon",
13
+ "The Idol", "Seeing Double", "Matador", "Hit the Road",
14
+ "The Duo", "The Trio", "The Family", "The Order", "The Tribe",
15
+ "Stuntman", "Invisible Joker", "Showman", "Flower Pot",
16
+ "Glass Joker", "Wee Joker", "Merry Andy", "Oops! All 6s",
17
+ "Certificate", "Smeared Joker", "Throwback", "Hanging Chad",
18
+ "Rough Gem", "Bloodstone", "Arrowhead", "Onyx Agate",
19
+ ]);
20
+ const UNCOMMON_JOKERS = new Set([
21
+ "Greedy Joker", "Lusty Joker", "Wrathful Joker", "Gluttonous Joker",
22
+ "Jolly Joker", "Zany Joker", "Mad Joker", "Crazy Joker", "Droll Joker",
23
+ "Sly Joker", "Wily Joker", "Clever Joker", "Devious Joker", "Crafty Joker",
24
+ "Joker Stencil", "Four Fingers", "Mime", "Credit Card",
25
+ "Ceremonial Dagger", "Banner", "Mystic Summit", "Marble Joker",
26
+ "Loyalty Card", "8 Ball", "Misprint", "Dusk", "Raised Fist",
27
+ "Fibonacci", "Steel Joker", "Scary Face", "Abstract Joker",
28
+ "Delayed Gratification", "Hack", "Pareidolia", "Gros Michel",
29
+ "Even Steven", "Odd Todd", "Scholar", "Business Card", "Supernova",
30
+ "Ride the Bus", "Space Joker", "Egg", "Burglar", "Blackboard",
31
+ "Runner", "Ice Cream", "DNA", "Splash", "Blue Joker",
32
+ "Sixth Sense", "Constellation", "Hiker", "Faceless Joker",
33
+ "Green Joker", "Superposition", "To Do List", "Cavendish",
34
+ "Card Sharp", "Red Card", "Madness", "Square Joker",
35
+ "Seance", "Riff-raff", "Vampire", "Shortcut",
36
+ "Hologram", "Vagabond", "Baron", "Cloud 9", "Rocket", "Obelisk",
37
+ "Midas Mask", "Luchador", "Photograph", "Gift Card", "Turtle Bean",
38
+ "Erosion", "Reserved Parking", "Mail In Rebate", "To the Moon", "Hallucination",
39
+ "Fortune Teller", "Golden Joker", "Lucky Cat", "Baseball Card", "Bull",
40
+ "Diet Cola", "Trading Card", "Flash Card", "Popcorn",
41
+ "Spare Trousers", "Ancient Joker", "Ramen", "Walkie Talkie",
42
+ "Seltzer", "Castle", "Smiley Face", "Campfire",
43
+ "Golden Ticket", "Mr. Bones", "Acrobat", "Sock and Buskin",
44
+ "Swashbuckler", "Troubadour", "Bootstraps",
45
+ ]);
46
+ function getJokerRarity(name) {
47
+ if (LEGENDARY_JOKERS.has(name))
48
+ return "legendary";
49
+ if (RARE_JOKERS.has(name))
50
+ return "rare";
51
+ if (UNCOMMON_JOKERS.has(name))
52
+ return "uncommon";
53
+ return "common";
54
+ }
55
+ // ─── Rarity colors ───────────────────────────────────────────────────────────
56
+ const C = JimboColorOption;
57
+ const RARITY_META = {
58
+ common: { label: "Common", color: C.BLUE, bg: withAlpha(C.BLUE, 0.12), hint: "Found in shops and Buffoon Packs" },
59
+ uncommon: { label: "Uncommon", color: C.GREEN, bg: withAlpha(C.GREEN, 0.12), hint: "Found in shops and Buffoon Packs" },
60
+ rare: { label: "Rare", color: C.RED, bg: withAlpha(C.RED, 0.12), hint: "Found in shops and Buffoon Packs" },
61
+ legendary: { label: "Legendary", color: C.PURPLE, bg: withAlpha(C.PURPLE, 0.12), hint: "Spawns from The Soul only! Find in Arcana Pack, Spectral Pack, Charm Tag, or Ethereal Tag." },
62
+ };
63
+ export function JokerPicker({ onSelect, onCancel }) {
64
+ const [step, setStep] = useState("rarity");
65
+ const [selectedRarity, setSelectedRarity] = useState(null);
66
+ const [search, setSearch] = useState("");
67
+ const filteredJokers = useMemo(() => {
68
+ if (!selectedRarity)
69
+ return [];
70
+ return JOKERS.filter((j) => {
71
+ if (getJokerRarity(j.name) !== selectedRarity)
72
+ return false;
73
+ if (search && !j.name.toLowerCase().includes(search.toLowerCase()))
74
+ return false;
75
+ return true;
76
+ });
77
+ }, [selectedRarity, search]);
78
+ const handleRaritySelect = useCallback((rarity) => {
79
+ setSelectedRarity(rarity);
80
+ setStep("specific");
81
+ setSearch("");
82
+ }, []);
83
+ const handleJokerSelect = useCallback((joker) => {
84
+ onSelect({
85
+ category: "joker",
86
+ value: joker.name,
87
+ clauseKey: selectedRarity === "legendary" ? "legendaryJoker"
88
+ : selectedRarity === "rare" ? "rareJoker"
89
+ : selectedRarity === "uncommon" ? "uncommonJoker"
90
+ : "commonJoker",
91
+ rarity: selectedRarity ?? "common",
92
+ });
93
+ }, [onSelect, selectedRarity]);
94
+ const handleAnySelect = useCallback(() => {
95
+ const rarity = selectedRarity ?? "common";
96
+ onSelect({
97
+ category: "joker",
98
+ value: "Any",
99
+ clauseKey: rarity === "legendary" ? "legendaryJoker"
100
+ : rarity === "rare" ? "rareJoker"
101
+ : rarity === "uncommon" ? "uncommonJoker"
102
+ : "commonJoker",
103
+ rarity,
104
+ });
105
+ }, [onSelect, selectedRarity]);
106
+ return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: step === "rarity" ? onCancel : () => setStep("rarity"), style: styles.backBtn, children: step === "rarity" ? "✕" : "← Back" }), _jsx("span", { style: styles.title, children: step === "rarity" ? "Select Joker Rarity" : `${RARITY_META[selectedRarity].label} Jokers` }), _jsx("div", { style: { width: 44 } })] }), step === "rarity" && (_jsx("div", { style: styles.rarityGrid, children: ["common", "uncommon", "rare", "legendary"].map((rarity) => {
107
+ const meta = RARITY_META[rarity];
108
+ return (_jsxs("button", { onClick: () => handleRaritySelect(rarity), style: {
109
+ ...styles.rarityBtn,
110
+ borderColor: meta.color,
111
+ background: meta.bg,
112
+ }, children: [_jsx("span", { style: { ...styles.rarityLabel, color: meta.color }, children: meta.label }), _jsx("span", { style: styles.rarityHint, children: meta.hint })] }, rarity));
113
+ }) })), step === "specific" && selectedRarity && (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.searchRow, children: [_jsx("input", { type: "text", placeholder: "Search jokers...", value: search, onChange: (e) => setSearch(e.target.value), style: styles.searchInput }), _jsx("button", { onClick: handleAnySelect, style: styles.anyBtn, children: "Any" })] }), selectedRarity === "legendary" && (_jsxs("div", { style: styles.legendaryBanner, children: [_jsx("span", { style: { fontSize: 14 }, children: "\uD83D\uDC51" }), _jsxs("span", { children: ["Legendary jokers spawn from ", _jsx("b", { children: "The Soul" }), ". Find it in ", _jsx("b", { children: "Arcana Pack" }), ",", " ", _jsx("b", { children: "Spectral Pack" }), ", ", _jsx("b", { children: "Charm Tag" }), ", or ", _jsx("b", { children: "Ethereal Tag" }), " only!"] })] })), _jsxs("div", { style: styles.jokerGrid, children: [filteredJokers.map((joker) => (_jsxs("div", { onClick: () => handleJokerSelect(joker), title: joker.name, style: styles.jokerCell, children: [_jsx(JimboSprite, { name: joker.name, sheet: "Jokers", width: 48 }), _jsx("span", { style: styles.jokerName, children: joker.name })] }, joker.name))), filteredJokers.length === 0 && (_jsxs("div", { style: styles.emptyState, children: ["No jokers match \"", search, "\""] }))] })] }))] }));
114
+ }
115
+ // ─── Styles ──────────────────────────────────────────────────────────────────
116
+ const styles = {
117
+ container: {
118
+ background: C.DARKEST,
119
+ border: `2px solid ${C.TEAL_GREY}`,
120
+ borderRadius: 8,
121
+ padding: 0,
122
+ maxWidth: 400,
123
+ maxHeight: "80vh",
124
+ overflow: "hidden",
125
+ display: "flex",
126
+ flexDirection: "column",
127
+ fontFamily: "m6x11plus, ui-monospace, monospace",
128
+ boxShadow: `0 8px 32px ${withAlpha(C.BLACK, 0.6)}`,
129
+ },
130
+ header: {
131
+ display: "flex",
132
+ alignItems: "center",
133
+ justifyContent: "space-between",
134
+ padding: "10px 12px",
135
+ borderBottom: `1px solid ${C.TEAL_GREY}`,
136
+ background: withAlpha(C.DARK_GREY, 0.5),
137
+ },
138
+ backBtn: {
139
+ background: "none",
140
+ border: "none",
141
+ color: C.GREY,
142
+ fontFamily: "m6x11plus, ui-monospace, monospace",
143
+ fontSize: 14,
144
+ cursor: "pointer",
145
+ padding: "4px 8px",
146
+ },
147
+ title: {
148
+ color: C.WHITE,
149
+ fontSize: 16,
150
+ fontWeight: "bold",
151
+ letterSpacing: 0.5,
152
+ },
153
+ rarityGrid: {
154
+ display: "flex",
155
+ flexDirection: "column",
156
+ gap: 8,
157
+ padding: 12,
158
+ },
159
+ rarityBtn: {
160
+ display: "flex",
161
+ flexDirection: "column",
162
+ alignItems: "flex-start",
163
+ gap: 4,
164
+ padding: "12px 14px",
165
+ border: "2px solid",
166
+ borderRadius: 6,
167
+ cursor: "pointer",
168
+ textAlign: "left",
169
+ transition: "background 150ms",
170
+ },
171
+ rarityLabel: {
172
+ fontSize: 16,
173
+ fontWeight: "bold",
174
+ fontFamily: "m6x11plus, ui-monospace, monospace",
175
+ },
176
+ rarityHint: {
177
+ fontSize: 11,
178
+ color: C.GREY,
179
+ fontFamily: "m6x11plus, ui-monospace, monospace",
180
+ lineHeight: "1.3",
181
+ },
182
+ searchRow: {
183
+ display: "flex",
184
+ gap: 8,
185
+ padding: "10px 12px 6px",
186
+ },
187
+ searchInput: {
188
+ flex: 1,
189
+ padding: "6px 10px",
190
+ borderRadius: 4,
191
+ border: `1px solid ${C.TEAL_GREY}`,
192
+ background: withAlpha(C.DARK_GREY, 0.8),
193
+ color: C.WHITE,
194
+ fontSize: 13,
195
+ fontFamily: "m6x11plus, ui-monospace, monospace",
196
+ outline: "none",
197
+ },
198
+ anyBtn: {
199
+ padding: "6px 14px",
200
+ borderRadius: 4,
201
+ border: `1px solid ${C.GOLD}`,
202
+ background: withAlpha(C.GOLD, 0.15),
203
+ color: C.GOLD,
204
+ fontSize: 13,
205
+ fontFamily: "m6x11plus, ui-monospace, monospace",
206
+ cursor: "pointer",
207
+ fontWeight: "bold",
208
+ },
209
+ legendaryBanner: {
210
+ display: "flex",
211
+ alignItems: "flex-start",
212
+ gap: 6,
213
+ margin: "4px 12px 8px",
214
+ padding: "8px 10px",
215
+ borderRadius: 4,
216
+ background: withAlpha(C.PURPLE, 0.12),
217
+ border: `1px solid ${withAlpha(C.PURPLE, 0.3)}`,
218
+ color: C.PURPLE,
219
+ fontSize: 11,
220
+ lineHeight: "1.4",
221
+ },
222
+ jokerGrid: {
223
+ display: "grid",
224
+ gridTemplateColumns: "repeat(auto-fill, minmax(64px, 1fr))",
225
+ gap: 6,
226
+ padding: "8px 12px 12px",
227
+ overflowY: "auto",
228
+ flex: 1,
229
+ },
230
+ jokerCell: {
231
+ display: "flex",
232
+ flexDirection: "column",
233
+ alignItems: "center",
234
+ gap: 3,
235
+ padding: 4,
236
+ borderRadius: 4,
237
+ cursor: "pointer",
238
+ transition: "background 120ms",
239
+ background: "transparent",
240
+ },
241
+ jokerName: {
242
+ fontSize: 9,
243
+ color: C.GREY,
244
+ textAlign: "center",
245
+ lineHeight: "1.2",
246
+ maxWidth: 60,
247
+ overflow: "hidden",
248
+ textOverflow: "ellipsis",
249
+ whiteSpace: "nowrap",
250
+ },
251
+ emptyState: {
252
+ gridColumn: "1 / -1",
253
+ textAlign: "center",
254
+ color: C.GREY,
255
+ fontSize: 13,
256
+ padding: 20,
257
+ },
258
+ };
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import type { SpriteSheetType } from "../../sprites/spriteMapper.js";
3
+ export type JamlZone = "must" | "should" | "mustnot";
4
+ /** The item category that can appear in a slot. */
5
+ export type SlotCategory = "joker" | "voucher" | "tag" | "boss" | "tarot" | "spectral" | "planet" | "pack";
6
+ /** Result of selecting a specific item in a slot. */
7
+ export interface SlotSelection {
8
+ category: SlotCategory;
9
+ /** The specific item name, or "Any" for wildcards. */
10
+ value: string;
11
+ /** JAML clause key (e.g. "commonJoker", "legendaryJoker", "voucher"). */
12
+ clauseKey: string;
13
+ /** Optional rarity for jokers. */
14
+ rarity?: "common" | "uncommon" | "rare" | "legendary";
15
+ }
16
+ export interface MysterySlotProps {
17
+ /** Which zone this slot belongs to — determines border color. */
18
+ zone: JamlZone;
19
+ /** What sheet type to render mystery card from. */
20
+ sheetType: SpriteSheetType;
21
+ /** Current selection, if any. */
22
+ selection?: SlotSelection;
23
+ /** Width of the card sprite. */
24
+ width?: number;
25
+ /** Called when the slot is tapped (to open a picker). */
26
+ onTap?: () => void;
27
+ /** Called when the selection is cleared. */
28
+ onClear?: () => void;
29
+ /** Additional inline styles. */
30
+ style?: React.CSSProperties;
31
+ }
32
+ export declare function MysterySlot({ zone, sheetType, selection, width, onTap, onClear, style, }: MysterySlotProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,109 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { JimboSprite } from "../../ui/sprites.js";
5
+ import { JimboColorOption, withAlpha, JIMBO_ANIMATIONS } from "../../ui/tokens.js";
6
+ // ─── Zone colors ─────────────────────────────────────────────────────────────
7
+ const C = JimboColorOption;
8
+ const ZONE_BORDER = {
9
+ must: C.BLUE,
10
+ should: C.RED,
11
+ mustnot: C.ORANGE,
12
+ };
13
+ // ─── Sheet → "Any" wildcard mapping ─────────────────────────────────────────
14
+ function getWildcardName(category) {
15
+ if (!category)
16
+ return null;
17
+ switch (category) {
18
+ case "joker": return null; // uses generic mystery
19
+ case "voucher": return null;
20
+ case "tag": return null;
21
+ case "boss": return null;
22
+ case "tarot": return null;
23
+ case "spectral": return null;
24
+ case "planet": return null;
25
+ case "pack": return null;
26
+ default: return null;
27
+ }
28
+ }
29
+ // ─── Component ───────────────────────────────────────────────────────────────
30
+ export function MysterySlot({ zone, sheetType, selection, width = 56, onTap, onClear, style, }) {
31
+ const [hover, setHover] = useState(false);
32
+ const [pressed, setPressed] = useState(false);
33
+ const borderColor = ZONE_BORDER[zone];
34
+ const isEmpty = !selection;
35
+ const cardH = Math.round((width * 95) / 71);
36
+ // Determine what to render
37
+ const spriteName = selection?.value ?? "";
38
+ const spriteSheet = selection ? categoryToSheet(selection.category) ?? sheetType : sheetType;
39
+ const scale = pressed
40
+ ? 0.95
41
+ : hover
42
+ ? JIMBO_ANIMATIONS.JUICE_UP_SCALE
43
+ : 1;
44
+ return (_jsxs("div", { onClick: onTap, onMouseEnter: () => setHover(true), onMouseLeave: () => { setHover(false); setPressed(false); }, onMouseDown: () => setPressed(true), onMouseUp: () => setPressed(false), style: {
45
+ position: "relative",
46
+ width: width + 8,
47
+ height: cardH + 8,
48
+ display: "flex",
49
+ alignItems: "center",
50
+ justifyContent: "center",
51
+ cursor: onTap ? "pointer" : "default",
52
+ borderRadius: 6,
53
+ border: isEmpty
54
+ ? `2px dashed ${withAlpha(borderColor, 0.4)}`
55
+ : `3px solid ${borderColor}`,
56
+ background: isEmpty
57
+ ? withAlpha(borderColor, 0.06)
58
+ : withAlpha(C.DARKEST, 0.8),
59
+ transform: `scale(${scale})`,
60
+ transition: `transform ${JIMBO_ANIMATIONS.JUICE_DURATION}ms ${JIMBO_ANIMATIONS.JUICE_EASING}, border-color 200ms`,
61
+ ...style,
62
+ }, children: [_jsx(JimboSprite, { name: spriteName, sheet: spriteSheet, width: width, style: {
63
+ opacity: isEmpty ? 0.5 : 1,
64
+ filter: isEmpty ? "saturate(0.3)" : "none",
65
+ transition: "opacity 200ms, filter 200ms",
66
+ } }), selection && onClear && (_jsx("div", { onClick: (e) => { e.stopPropagation(); onClear(); }, style: {
67
+ position: "absolute",
68
+ top: -6,
69
+ right: -6,
70
+ width: 18,
71
+ height: 18,
72
+ borderRadius: "50%",
73
+ background: C.RED,
74
+ color: C.WHITE,
75
+ display: "flex",
76
+ alignItems: "center",
77
+ justifyContent: "center",
78
+ fontSize: 11,
79
+ fontFamily: "m6x11plus, ui-monospace, monospace",
80
+ cursor: "pointer",
81
+ lineHeight: 1,
82
+ boxShadow: `0 1px 4px ${withAlpha(C.BLACK, 0.5)}`,
83
+ }, children: "\u00D7" })), isEmpty && hover && (_jsx("div", { style: {
84
+ position: "absolute",
85
+ bottom: -16,
86
+ left: "50%",
87
+ transform: "translateX(-50%)",
88
+ fontFamily: "m6x11plus, ui-monospace, monospace",
89
+ fontSize: 10,
90
+ color: borderColor,
91
+ whiteSpace: "nowrap",
92
+ textTransform: "uppercase",
93
+ letterSpacing: 1,
94
+ }, children: "+ tap" }))] }));
95
+ }
96
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
97
+ function categoryToSheet(cat) {
98
+ switch (cat) {
99
+ case "joker": return "Jokers";
100
+ case "voucher": return "Vouchers";
101
+ case "tag": return "tags";
102
+ case "boss": return "BlindChips";
103
+ case "tarot":
104
+ case "spectral":
105
+ case "planet": return "Tarots";
106
+ case "pack": return "Boosters";
107
+ default: return null;
108
+ }
109
+ }
@@ -0,0 +1,3 @@
1
+ export { MysterySlot, type MysterySlotProps, type SlotSelection, type SlotCategory, type JamlZone } from "./MysterySlot.js";
2
+ export { JokerPicker, type JokerPickerProps, type JokerRarity } from "./JokerPicker.js";
3
+ export { JamlMapEditorDemo, type JamlMapEditorDemoProps } from "./JamlMapEditorDemo.js";
@@ -0,0 +1,3 @@
1
+ export { MysterySlot } from "./MysterySlot.js";
2
+ export { JokerPicker } from "./JokerPicker.js";
3
+ export { JamlMapEditorDemo } from "./JamlMapEditorDemo.js";
package/dist/core.d.ts CHANGED
@@ -2,6 +2,4 @@ export { JAML_ASSET_FILES, clearJamlAssetBaseUrl, getDefaultJamlAssetUrlMap, res
2
2
  export { Layer, type LayerOptions } from "./render/Layer.js";
3
3
  export { getSpriteData, type SpriteData, type SpriteSheetType } from "./sprites/spriteMapper.js";
4
4
  export { SPRITE_SHEETS, JOKERS, JOKER_FACES, TAROTS_AND_PLANETS, CONSUMABLE_FACES, VOUCHERS, BOSSES, TAGS, BOOSTER_PACKS, EDITION_MAP, STICKER_MAP, RANK_MAP, SUIT_MAP, ENHANCER_MAP, SEAL_MAP, type SpritePos, type SpriteEntry, type SpriteSheetInfo, } from "./sprites/spriteData.js";
5
- export { BalatroItemCategory, packedItemCategory, packedJokerRarity, packedItemIndex, isPackedItemValid, } from "./decode/packedBalatroItem.js";
6
- export { getItemDisplayName, getItemCategory, getSuitColor } from "./utils/itemUtils.js";
7
5
  export { getStandardCardPosition, getSealPosition, getEnhancerPosition } from "./utils/gameCardUtils.js";
package/dist/core.js CHANGED
@@ -2,6 +2,4 @@ export { JAML_ASSET_FILES, clearJamlAssetBaseUrl, getDefaultJamlAssetUrlMap, res
2
2
  export { Layer } from "./render/Layer.js";
3
3
  export { getSpriteData } from "./sprites/spriteMapper.js";
4
4
  export { SPRITE_SHEETS, JOKERS, JOKER_FACES, TAROTS_AND_PLANETS, CONSUMABLE_FACES, VOUCHERS, BOSSES, TAGS, BOOSTER_PACKS, EDITION_MAP, STICKER_MAP, RANK_MAP, SUIT_MAP, ENHANCER_MAP, SEAL_MAP, } from "./sprites/spriteData.js";
5
- export { BalatroItemCategory, packedItemCategory, packedJokerRarity, packedItemIndex, isPackedItemValid, } from "./decode/packedBalatroItem.js";
6
- export { getItemDisplayName, getItemCategory, getSuitColor } from "./utils/itemUtils.js";
7
5
  export { getStandardCardPosition, getSealPosition, getEnhancerPosition } from "./utils/gameCardUtils.js";
@@ -1,12 +1,9 @@
1
1
  /**
2
- * Motely item decoder.
3
- *
4
- * MotelyItem.Value is a packed integer. The MotelyItemType enum
5
- * uses packed integers where the top nibble encodes category:
6
- * 0x1000 = Standardcard, 0x2000 = Spectral, 0x3000 = Tarot,
7
- * 0x4000 = Planet, 0x5000 = Joker, 0xF000 = Invalid
2
+ * Motely item decoder — thin wrapper over motely-wasm runtime enums.
3
+ * No hand-rolled bitmask tables. The WASM enum IS the source of truth.
8
4
  */
9
- import { type CardCategory } from "../utils/itemUtils.js";
5
+ export type CardCategory = "joker" | "consumable" | "playing" | "spectral" | "tarot" | "planet";
6
+ export type MotelyRenderableCategory = CardCategory | "unknown";
10
7
  export type MotelyItemInput = number | MotelyRuntimeItem | null | undefined;
11
8
  export interface MotelyRuntimeItem {
12
9
  type?: number;
@@ -17,7 +14,6 @@ export interface MotelyRuntimeItem {
17
14
  suit?: number;
18
15
  rank?: number;
19
16
  }
20
- export type MotelyRenderableCategory = CardCategory | "unknown";
21
17
  export interface DecodedMotelyItem {
22
18
  itemType: number;
23
19
  enumKey: string;
@@ -42,26 +38,17 @@ export interface MotelyJamlCard {
42
38
  };
43
39
  }
44
40
  export declare function resolveMotelyItemType(input: MotelyItemInput): number | null;
41
+ export declare function motelyItemTypeName(input: MotelyItemInput): string;
42
+ export declare function motelyItemCategory(itemType: number): MotelyRenderableCategory;
45
43
  export declare function motelyItemRenderCategory(input: MotelyItemInput): MotelyRenderableCategory;
44
+ export declare function motelyItemDisplayName(input: MotelyItemInput): string;
46
45
  export declare function motelyItemEditionName(input: MotelyItemInput): "Foil" | "Holographic" | "Polychrome" | "Negative" | null;
47
46
  export declare function motelyItemSealName(input: MotelyItemInput): "Gold" | "Red" | "Blue" | "Purple" | null;
48
47
  export declare function motelyItemEnhancementName(input: MotelyItemInput): string | null;
49
- export declare function motelyStandardcardSuitName(input: MotelyItemInput): "Clubs" | "Diamonds" | "Hearts" | "Spades" | null;
50
48
  export declare function motelyStandardcardRankName(input: MotelyItemInput): string | null;
51
- /** Get the enum key name for a MotelyItemType value. */
52
- export declare function motelyItemTypeName(input: MotelyItemInput): string;
53
- /** Get the category string for a MotelyItemType value. */
54
- export declare function motelyItemCategory(input: MotelyItemInput): string;
55
- /** Convert PascalCase enum key to display name. */
56
- export declare function motelyItemDisplayName(input: MotelyItemInput): string;
49
+ export declare function motelyStandardcardSuitName(input: MotelyItemInput): "Clubs" | "Diamonds" | "Hearts" | "Spades" | null;
50
+ export declare function decodeMotelyItemName(input: MotelyItemInput): string;
57
51
  export declare function decodeMotelyItem(input: MotelyItemInput): DecodedMotelyItem | null;
58
52
  export declare function decodeMotelyItemToJamlCard(input: MotelyItemInput, scale?: number): MotelyJamlCard | null;
59
- /**
60
- * Decode a MotelyItemType integer to a display name for sprite lookup.
61
- * Cached per value.
62
- */
63
- export declare function decodeMotelyItemName(input: MotelyItemInput): string | null;
64
- /** Warm the cache for a batch of item type values. */
65
- export declare function warmMotelyItemCache(itemTypes: readonly MotelyItemInput[]): void;
66
- /** Number of unique items decoded so far. */
53
+ export declare function warmMotelyItemCache(): void;
67
54
  export declare function motelyItemCacheSize(): number;