jaml-ui 0.24.16 → 0.24.18

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 (54) hide show
  1. package/dist/components/DeckSprite.js +2 -0
  2. package/dist/components/GameCard.js +1 -0
  3. package/dist/components/JamlAestheticSelector.js +3 -2
  4. package/dist/components/JamlCurator.js +3 -2
  5. package/dist/components/JamlIde.d.ts +4 -3
  6. package/dist/components/JamlIde.js +3 -31
  7. package/dist/components/JamlIdeToolbar.d.ts +1 -1
  8. package/dist/components/JamlIdeToolbar.js +5 -5
  9. package/dist/components/JamlMapPreview.js +3 -37
  10. package/dist/components/Jimbolate.d.ts +7 -0
  11. package/dist/components/Jimbolate.js +17 -0
  12. package/dist/components/PaginatedFilterBrowser.d.ts +23 -0
  13. package/dist/components/PaginatedFilterBrowser.js +54 -0
  14. package/dist/components/RunConfigModal.js +11 -150
  15. package/dist/components/jamlMap/CategoryPicker.d.ts +1 -2
  16. package/dist/components/jamlMap/CategoryPicker.js +2 -1
  17. package/dist/components/jamlMap/JamlMapEditor.js +8 -10
  18. package/dist/components/jamlMap/JokerPicker.d.ts +1 -2
  19. package/dist/components/jamlMap/JokerPicker.js +3 -7
  20. package/dist/components/jamlMap/MysterySlot.js +0 -15
  21. package/dist/hooks/searchWorker.d.ts +1 -29
  22. package/dist/hooks/searchWorker.js +8 -6
  23. package/dist/hooks/useIntersectionObserver.js +5 -3
  24. package/dist/hooks/useSearch.js +5 -1
  25. package/dist/hooks/useShopStream.js +5 -2
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/lib/cardParser.d.ts +1 -1
  29. package/dist/lib/cardParser.js +19 -17
  30. package/dist/lib/classes/BuyMetaData.d.ts +2 -2
  31. package/dist/lib/const.d.ts +22 -13
  32. package/dist/lib/data/constants.js +10 -9
  33. package/dist/lib/hooks/useJamlFilter.js +5 -5
  34. package/dist/lib/hooks/useSeedAnalyzer.js +7 -1
  35. package/dist/lib/jaml/jamlSchema.d.ts +18 -3
  36. package/dist/lib/jaml/jamlSchema.js +2 -2
  37. package/dist/lib/parseDailyRitual.d.ts +1 -1
  38. package/dist/lib/parseDailyRitual.js +2 -1
  39. package/dist/r3f/Card3D.js +2 -0
  40. package/dist/r3f/JimboBillboard.d.ts +1 -1
  41. package/dist/r3f/JimboBillboard.js +5 -2
  42. package/dist/ui/JimboInputModal.js +8 -2
  43. package/dist/ui/PanelSplitter.js +4 -2
  44. package/dist/ui/hooks.js +14 -5
  45. package/dist/ui/ide/JamlEditor.js +53 -63
  46. package/dist/ui/jimbo.css +70 -16
  47. package/dist/ui/jimboTooltip.js +12 -6
  48. package/dist/ui/panel.d.ts +1 -2
  49. package/dist/ui/panel.js +2 -2
  50. package/dist/ui/radial/RadialButton.js +1 -1
  51. package/dist/ui/showcase.js +1 -1
  52. package/dist/utils/jamlMapPreview.js +2 -1
  53. package/dist/utils/jamlVisualFilter.js +3 -2
  54. package/package.json +12 -6
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { resolveJamlAssetUrl } from '../assets.js';
4
+ // eslint-disable-next-line react-refresh/only-export-components
4
5
  export const DECK_SPRITE_POS = {
5
6
  red: { x: 0, y: 0 },
6
7
  nebula: { x: 3, y: 0 },
@@ -20,6 +21,7 @@ export const DECK_SPRITE_POS = {
20
21
  anaglyph: { x: 2, y: 4 },
21
22
  zodiac: { x: 3, y: 4 },
22
23
  };
24
+ // eslint-disable-next-line react-refresh/only-export-components
23
25
  export const STAKE_SPRITE_POS = {
24
26
  white: { x: 1, y: 0 },
25
27
  red: { x: 2, y: 0 },
@@ -153,6 +153,7 @@ function resolvePackedAnalyzerItem(item, scale) {
153
153
  }
154
154
  return { kind: "unknown", label: displayName };
155
155
  }
156
+ // eslint-disable-next-line react-refresh/only-export-components
156
157
  export function resolveAnalyzerShopItem(item, scale = 1) {
157
158
  const displayName = String(item.name || "").trim();
158
159
  if (!displayName) {
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { JimboText } from "../ui/jimboText.js";
4
+ import { JimboButton } from "../ui/panel.js";
4
5
  const AESTHETICS = [
5
6
  { id: "Palindrome", value: 0, label: "Palindrome", desc: "Seeds that read the same forwards and backwards" },
6
7
  { id: "Psychosis", value: 1, label: "Psychosis", desc: "Unsettling or eerie seed patterns" },
@@ -14,8 +15,8 @@ const AESTHETICS = [
14
15
  * All styling via jimbo.css `.j-aesthetic-selector` / `.j-aesthetic-pill` — zero inline styles.
15
16
  */
16
17
  export function JamlAestheticSelector({ value, onChange, className, style }) {
17
- return (_jsxs("div", { className: `j-aesthetic-selector ${className ?? ""}`, style: style, children: [_jsx(JimboText, { size: "xs", tone: "grey", children: "Seed Aesthetics" }), _jsx("div", { className: "j-aesthetic-selector__list", children: AESTHETICS.map((a) => {
18
+ return (_jsxs("div", { className: `j-aesthetic-selector ${className ?? ""}`, style: style, children: [_jsx(JimboText, { size: "xs", tone: "grey", children: "Seed Aesthetics" }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginTop: 4 }, children: AESTHETICS.map((a) => {
18
19
  const isActive = value === a.id;
19
- return (_jsx("button", { type: "button", className: "j-aesthetic-pill", "data-active": isActive, onClick: () => onChange(isActive ? null : a.id, a.value), title: a.desc, children: a.label }, a.id));
20
+ return (_jsx(JimboButton, { tone: isActive ? "red" : "grey", size: "sm", onClick: () => onChange(isActive ? null : a.id, a.value), children: a.label }, a.id));
20
21
  }) })] }));
21
22
  }
@@ -40,8 +40,9 @@ export function JamlCurator({ motely, motelyWasmUrl }) {
40
40
  };
41
41
  return (_jsx("div", { style: {
42
42
  width: "100%",
43
- maxWidth: 320,
44
- height: "100svh",
43
+ maxWidth: 375,
44
+ height: "100dvh",
45
+ maxHeight: 812,
45
46
  margin: "0 auto",
46
47
  position: "relative",
47
48
  background: C.DARKEST,
@@ -25,8 +25,9 @@ export interface JamlIdeProps {
25
25
  codePlaceholder?: string;
26
26
  onSearch?: () => void;
27
27
  isSearching?: boolean;
28
- /** Hide the Balatro attribution footer. Default: false (always shown). */
29
- hideFooter?: boolean;
28
+ onTestSeed?: (seed: string) => void;
29
+ jimbolateResult?: "idle" | "match" | "nomatch" | "running" | "error";
30
+ jimbolateError?: string | null;
30
31
  /**
31
32
  * Controlled visual filter. When provided alongside `onVisualFilterChange`, the Visual tab
32
33
  * is fully controlled by the parent. When absent, the Visual tab auto-derives from the text.
@@ -36,4 +37,4 @@ export interface JamlIdeProps {
36
37
  }
37
38
  export type { JamlVisualFilter } from "./JamlIdeVisual.js";
38
39
  export type { JamlVisualClause, JamlZone } from "./JamlIdeVisual.js";
39
- export declare function JamlIde({ jaml, defaultJaml, onChange, defaultMode, searchResults, className, style, title, subtitle, compactHeader, actions, codePlaceholder, onSearch, isSearching, hideFooter, visualFilter, onVisualFilterChange, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
40
+ export declare function JamlIde({ jaml, defaultJaml, onChange, defaultMode, searchResults, className, style, title, subtitle, compactHeader, actions, codePlaceholder, onSearch, isSearching, onTestSeed, jimbolateResult, jimbolateError, visualFilter, onVisualFilterChange, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
@@ -1,16 +1,15 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from "react";
4
- import { JimboBalatroFooter } from "../ui/footer.js";
5
4
  import { JamlMapPreview } from "./JamlMapPreview.js";
6
5
  import { JamlMapEditor, CategoryMenu, JokerPicker, CategoryPicker, VOUCHER_PICKER_CONFIG, TAG_PICKER_CONFIG, BOSS_PICKER_CONFIG, TAROT_PICKER_CONFIG, PLANET_PICKER_CONFIG, SPECTRAL_PICKER_CONFIG, PACK_PICKER_CONFIG, } from "./jamlMap/index.js";
7
6
  import { JamlIdeToolbar } from "./JamlIdeToolbar.js";
8
7
  import { JamlIdeVisual } from "./JamlIdeVisual.js";
9
8
  import { JamlCodeEditor } from "./JamlCodeEditor.js";
9
+ import { Jimbolate } from "./Jimbolate.js";
10
10
  import { JimboColorOption } from "../ui/tokens.js";
11
11
  import { JimboModal } from "../ui/panel.js";
12
12
  import { jamlTextToVisualFilter, visualFilterToJamlText } from "../utils/jamlVisualFilter.js";
13
- import { DeckSprite } from "./DeckSprite.js";
14
13
  const CATEGORY_CONFIG_MAP = {
15
14
  voucher: VOUCHER_PICKER_CONFIG,
16
15
  tag: TAG_PICKER_CONFIG,
@@ -90,34 +89,7 @@ function ResultsView({ results, jaml }) {
90
89
  })] })] })) : null] }, result.seed));
91
90
  }) }));
92
91
  }
93
- function readRootValue(jaml, key, fallback) {
94
- const match = jaml.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
95
- return match?.[1]?.trim() || fallback;
96
- }
97
- function setRootValue(jaml, key, value) {
98
- const line = `${key}: ${value}`;
99
- const pattern = new RegExp(`^${key}:\\s*.*$`, "m");
100
- if (pattern.test(jaml)) {
101
- return jaml.replace(pattern, line);
102
- }
103
- const trimmed = jaml.trimEnd();
104
- return trimmed.length > 0 ? `${line}\n${trimmed}` : line;
105
- }
106
- import { RunConfigModal } from "./RunConfigModal.js";
107
- function DeckStakeSelector({ jaml, onChange, onSearch, }) {
108
- const deck = readRootValue(jaml, "deck", "Red");
109
- const stake = readRootValue(jaml, "stake", "White");
110
- const [modalOpen, setModalOpen] = useState(false);
111
- const handleApply = (nextDeck, nextStake) => {
112
- let next = setRootValue(jaml, "deck", nextDeck);
113
- next = setRootValue(next, "stake", nextStake);
114
- onChange(next);
115
- onSearch?.();
116
- };
117
- return (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => setModalOpen(true), className: "j-btn j-btn--red", style: { height: 32, padding: 0, borderRadius: 8, overflow: 'hidden' }, children: _jsxs("div", { className: "j-btn__face", style: { display: 'flex', alignItems: 'center', gap: 8, padding: '0 8px 0 4px', height: '100%' }, children: [_jsx(DeckSprite, { deck: deck, stake: stake, size: compactDeckSpriteSize }), _jsxs("span", { style: { fontFamily: "m6x11plus, monospace", fontSize: 14 }, children: [deck, " / ", stake] })] }) }), _jsx(RunConfigModal, { open: modalOpen, onClose: () => setModalOpen(false), deck: deck, stake: stake, onChange: handleApply })] }));
118
- }
119
- const compactDeckSpriteSize = 24;
120
- export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", searchResults = [], className = "", style, title = "JAML IDE", subtitle = "Jimbo's Ante Markup Language", compactHeader = false, actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, hideFooter = false, visualFilter, onVisualFilterChange, }) {
92
+ export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", searchResults = [], className = "", style, title = "JAML IDE", subtitle = "Jimbo's Ante Markup Language", compactHeader = false, actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, onTestSeed, jimbolateResult = "idle", jimbolateError, visualFilter, onVisualFilterChange, }) {
121
93
  const [mode, setMode] = useState(defaultMode);
122
94
  const [internalText, setInternalText] = useState(jaml ?? defaultJaml ?? "");
123
95
  const [lastJamlProp, setLastJamlProp] = useState(jaml);
@@ -217,5 +189,5 @@ export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", sea
217
189
  padding: compactHeader ? "8px 10px" : "10px 14px",
218
190
  borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
219
191
  background: JimboColorOption.TEAL_GREY,
220
- }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: "normal", fontFamily: "m6x11plus, monospace", color: JimboColorOption.GOLD_TEXT }, children: title }), subtitle ? _jsx("div", { style: { fontSize: 11, color: JimboColorOption.GREY }, children: subtitle }) : null] }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" }, children: [_jsx(DeckStakeSelector, { jaml: text, onChange: handleTextChange, onSearch: onSearch }), actions] })] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length, onSearch: onSearch, isSearching: isSearching }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: mode === "map" ? "hidden" : "auto", background: JimboColorOption.DARKEST }, children: [mode === "visual" ? (_jsx(JamlIdeVisual, { filter: activeFilter, onChange: handleVisualFilterChange, onAddClause: handleAddClause })) : null, mode === "code" ? (_jsx(JamlCodeEditor, { value: text, onChange: handleTextChange, placeholder: codePlaceholder })) : null, mode === "map" ? _jsx(JamlMapEditor, { onChange: handleTextChange }) : null, mode === "results" ? (_jsx("div", { style: { padding: 12 }, children: _jsx(ResultsView, { results: results, jaml: text }) })) : null] }), !hideFooter && _jsx(JimboBalatroFooter, {}), _jsx(JimboModal, { open: addZone !== null, onClose: handlePickerClose, children: addZone !== null && (pickerFlow === "category" ? (_jsx(CategoryMenu, { onSelect: (cat) => setPickerFlow(cat) })) : pickerFlow === "joker" ? (_jsx(JokerPicker, { onSelect: handlePickerSelect, onCancel: handlePickerClose })) : (_jsx(CategoryPicker, { config: CATEGORY_CONFIG_MAP[pickerFlow], onSelect: handlePickerSelect, onCancel: handlePickerClose }))) })] }));
192
+ }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: "normal", fontFamily: "m6x11plus, monospace", color: JimboColorOption.GOLD_TEXT }, children: title }), subtitle ? _jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.6)" }, children: subtitle }) : null] }), actions && (_jsx("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" }, children: actions }))] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length, onSearch: onSearch, isSearching: isSearching }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: mode === "map" ? "hidden" : "auto", background: JimboColorOption.DARKEST }, children: [mode === "visual" ? (_jsx(JamlIdeVisual, { filter: activeFilter, onChange: handleVisualFilterChange, onAddClause: handleAddClause })) : null, mode === "code" ? (_jsx(JamlCodeEditor, { value: text, onChange: handleTextChange, placeholder: codePlaceholder })) : null, mode === "map" ? _jsx(JamlMapEditor, { onChange: handleTextChange }) : null, mode === "results" ? (_jsx("div", { style: { padding: 12 }, children: _jsx(ResultsView, { results: results, jaml: text }) })) : null, mode === "jimbolate" ? (_jsx(Jimbolate, { jaml: text, onTest: (seed) => onTestSeed?.(seed), result: jimbolateResult, error: jimbolateError })) : null] }), _jsx(JimboModal, { open: addZone !== null, onClose: handlePickerClose, children: addZone !== null && (pickerFlow === "category" ? (_jsx(CategoryMenu, { onSelect: (cat) => setPickerFlow(cat) })) : pickerFlow === "joker" ? (_jsx(JokerPicker, { onSelect: handlePickerSelect })) : (_jsx(CategoryPicker, { config: CATEGORY_CONFIG_MAP[pickerFlow], onSelect: handlePickerSelect }))) })] }));
221
193
  }
@@ -1,4 +1,4 @@
1
- export type JamlIdeMode = "visual" | "code" | "map" | "results";
1
+ export type JamlIdeMode = "visual" | "code" | "map" | "results" | "jimbolate";
2
2
  export interface JamlIdeToolbarProps {
3
3
  mode: JamlIdeMode;
4
4
  onModeChange: (mode: JamlIdeMode) => void;
@@ -6,18 +6,18 @@ import { JimboColorOption } from "../ui/tokens.js";
6
6
  export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className = "", onSearch, isSearching = false }) {
7
7
  const tabs = [
8
8
  { id: "visual", label: "Visual" },
9
- { id: "code", label: ".jaml" },
9
+ { id: "code", label: "JAML" },
10
10
  { id: "map", label: "Map" },
11
11
  { id: "results", label: resultCount > 0 ? `Results (${resultCount})` : "Results" },
12
+ { id: "jimbolate", label: "Jimbolate" },
12
13
  ];
13
14
  return (_jsxs("div", { className: className, style: {
14
- display: "grid",
15
- gridTemplateColumns: onSearch ? "1fr auto 1fr" : "1fr",
15
+ display: "flex",
16
16
  alignItems: "center",
17
17
  gap: 8,
18
- minWidth: 0,
19
18
  padding: "10px 10px 6px",
20
19
  borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
21
20
  background: JimboColorOption.DARKEST,
22
- }, children: [onSearch ? _jsx("div", {}) : null, _jsx("div", { style: { minWidth: 0, width: "100%", overflow: "visible", paddingBottom: 3 }, children: _jsx(JimboTabs, { tabs: tabs, activeTab: mode, onTabChange: (id) => onModeChange(id) }) }), onSearch ? (_jsx("div", { style: { justifySelf: "end" }, children: _jsx(JimboButton, { tone: isSearching ? "red" : "blue", size: "xs", onClick: onSearch, children: isSearching ? "Stop" : "Search" }) })) : null] }));
21
+ minWidth: 0,
22
+ }, children: [_jsx("div", { style: { flex: 1, minWidth: 0, paddingBottom: 3 }, children: _jsx(JimboTabs, { tabs: tabs, activeTab: mode, onTabChange: (id) => onModeChange(id) }) }), onSearch && (_jsx("div", { style: { flexShrink: 0 }, children: _jsx(JimboButton, { tone: isSearching ? "red" : "orange", size: "sm", onClick: onSearch, children: isSearching ? "Stop" : "Search" }) }))] }));
23
23
  }
@@ -29,7 +29,7 @@ const GLOW_ANIMATION = `
29
29
  100% { box-shadow: 0 0 0 1px var(--glow-color), 0 0 4px var(--glow-color); opacity: 0.8; }
30
30
  }
31
31
  `;
32
- function ClausePill({ item, color, glow, matchCount }) {
32
+ function ClausePill({ item, glow, matchCount }) {
33
33
  const isHit = matchCount > 0;
34
34
  const hasData = matchCount !== undefined && matchCount >= 0;
35
35
  return (_jsxs("div", { style: {
@@ -42,6 +42,7 @@ function ClausePill({ item, color, glow, matchCount }) {
42
42
  padding: "3px 8px",
43
43
  position: "relative",
44
44
  opacity: isHit ? 1 : 0.6,
45
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
45
46
  // @ts-ignore -- CSS custom property
46
47
  "--glow-color": glow,
47
48
  animation: isHit ? "j-glow-pulse 1.6s ease-in-out infinite" : "none",
@@ -63,41 +64,6 @@ function ClausePill({ item, color, glow, matchCount }) {
63
64
  boxShadow: `0 1px 0 ${C.BLACK}`,
64
65
  }, children: matchCount > 1 ? `x${matchCount}` : "✓" }))] }));
65
66
  }
66
- function VisualChip({ item, matchCount, compact }) {
67
- const isHit = matchCount > 0;
68
- const hasData = matchCount !== undefined;
69
- const glow = ZONES[item.section].glow;
70
- return (_jsxs("div", { style: {
71
- display: "flex",
72
- alignItems: "center",
73
- gap: compact ? 3 : 6,
74
- background: isHit ? `${glow}33` : C.DARKEST,
75
- border: `2px solid ${isHit ? glow : C.PANEL_EDGE}`,
76
- borderRadius: 4,
77
- padding: compact ? "2px 4px" : "3px 8px",
78
- position: "relative",
79
- opacity: isHit ? 1 : 0.6,
80
- // @ts-ignore
81
- "--glow-color": glow,
82
- animation: isHit ? "j-glow-pulse 1.6s ease-in-out infinite" : "none",
83
- }, title: `${item.clauseKey}: ${item.value}${hasData ? ` (Found: ${matchCount})` : ""}`, children: [_jsx("style", { children: GLOW_ANIMATION }), _jsx("div", { style: { color: C.GREY, fontSize: 10, lineHeight: 1, padding: "0 2px" }, children: "\u22EE\u22EE" }), _jsx(JimboSprite, { name: item.value, sheet: SHEET_FOR_VISUAL[item.visualType], width: compact ? 20 : 26 }), _jsx("div", { style: {
84
- fontSize: compact ? 9 : 10,
85
- color: C.WHITE,
86
- letterSpacing: 0.5,
87
- textShadow: "1px 1px 0 rgba(0,0,0,.8)",
88
- }, children: item.value }), isHit && (_jsx("div", { style: {
89
- position: "absolute",
90
- top: compact ? -4 : -6,
91
- right: compact ? -4 : -6,
92
- background: C.GREEN,
93
- color: C.WHITE,
94
- fontSize: 7,
95
- padding: "1px 3px",
96
- borderRadius: 3,
97
- border: `1px solid ${C.BLACK}`,
98
- boxShadow: `0 1px 0 ${C.BLACK}`,
99
- }, children: matchCount > 1 ? `x${matchCount}` : "✓" }))] }));
100
- }
101
67
  function ZoneRail({ zone, items, matchMap, compact = false }) {
102
68
  const meta = ZONES[zone];
103
69
  return (_jsxs("div", { style: {
@@ -117,7 +83,7 @@ function ZoneRail({ zone, items, matchMap, compact = false }) {
117
83
  // or "must: rareJoker: Blueprint". We try to match the item value and section.
118
84
  const labelKey = `${item.section}: ${item.clauseKey}: ${item.value}`;
119
85
  const count = matchMap[labelKey] ?? -1;
120
- return (_jsx(ClausePill, { item: item, color: meta.color, glow: meta.glow, matchCount: count }, item.id));
86
+ return (_jsx(ClausePill, { item: item, glow: meta.glow, matchCount: count }, item.id));
121
87
  })) })] }));
122
88
  }
123
89
  export function JamlMapPreview({ jaml, className = "", emptyMessage = "No visual JAML clauses found yet.", tallyColumns, tallyLabels, compact = false, }) {
@@ -0,0 +1,7 @@
1
+ export interface JimbolateProps {
2
+ jaml: string;
3
+ onTest: (seed: string) => void;
4
+ result: "idle" | "match" | "nomatch" | "running" | "error";
5
+ error?: string | null;
6
+ }
7
+ export declare function Jimbolate({ jaml, onTest, result, error }: JimbolateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef } from "react";
4
+ import { JimboPanel, JimboButton } from "../ui/panel.js";
5
+ import { JimboText } from "../ui/jimboText.js";
6
+ import { JimboColorOption } from "../ui/tokens.js";
7
+ export function Jimbolate({ jaml, onTest, result, error }) {
8
+ const [seed, setSeed] = useState("");
9
+ const inputRef = useRef(null);
10
+ const handleTest = () => {
11
+ const s = seed.trim().toUpperCase();
12
+ if (!s)
13
+ return;
14
+ onTest(s);
15
+ };
16
+ return (_jsxs("div", { style: { padding: 10, display: "flex", flexDirection: "column", gap: 8 }, children: [_jsx(JimboPanel, { children: _jsxs("div", { style: { display: "flex", gap: 6 }, children: [_jsx("input", { ref: inputRef, className: "j-seed-input__field", type: "text", placeholder: "Enter seed...", value: seed, maxLength: 12, onChange: (e) => setSeed(e.target.value.toUpperCase()), onKeyDown: (e) => e.key === "Enter" && handleTest(), style: { flex: 1, fontSize: 15, padding: "6px 10px", letterSpacing: "0.1em", textTransform: "uppercase" } }), _jsx(JimboButton, { tone: result === "running" ? "red" : "orange", size: "sm", onClick: handleTest, disabled: !seed.trim() || !jaml.trim(), children: result === "running" ? "..." : "Test" })] }) }), result === "match" && (_jsxs(JimboPanel, { style: { background: `${JimboColorOption.GREEN_TEXT}22`, textAlign: "center" }, children: [_jsx(JimboText, { size: "xl", tone: "gold", style: { letterSpacing: 3, display: "block", marginBottom: 4 }, children: seed }), _jsx(JimboText, { size: "md", tone: "green", children: "MATCH" })] })), result === "nomatch" && (_jsxs(JimboPanel, { style: { textAlign: "center" }, children: [_jsx(JimboText, { size: "xl", tone: "grey", style: { letterSpacing: 3, display: "block", marginBottom: 4 }, children: seed }), _jsx(JimboText, { size: "md", tone: "red", children: "no match" })] })), result === "error" && (_jsx(JimboPanel, { children: _jsx(JimboText, { size: "xs", tone: "red", style: { display: "block", textAlign: "center" }, children: error ?? "Error" }) })), result === "idle" && !jaml.trim() && (_jsx(JimboPanel, { children: _jsx(JimboText, { size: "xs", tone: "grey", style: { display: "block", textAlign: "center" }, children: "Write a JAML filter in the JAML tab first" }) }))] }));
17
+ }
@@ -0,0 +1,23 @@
1
+ export interface FilterItem {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ authorText?: string;
6
+ dateText?: string;
7
+ statsText?: string;
8
+ }
9
+ export interface PaginatedFilterBrowserProps {
10
+ filters: FilterItem[];
11
+ itemsPerPage?: number;
12
+ onSelectFilter?: (filter: FilterItem) => void;
13
+ onMainAction?: (filter: FilterItem) => void;
14
+ onSecondaryAction?: (filter: FilterItem) => void;
15
+ onDeleteFilter?: (filter: FilterItem) => void;
16
+ mainActionText?: string;
17
+ secondaryActionText?: string;
18
+ showSecondary?: boolean;
19
+ showDelete?: boolean;
20
+ emptyText?: string;
21
+ }
22
+ export declare function PaginatedFilterBrowser({ filters, itemsPerPage, // matching Balatro challenges layout pages
23
+ onSelectFilter, onMainAction, onSecondaryAction, onDeleteFilter, mainActionText, secondaryActionText, showSecondary, showDelete, emptyText, }: PaginatedFilterBrowserProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { JimboButton, JimboPanel } from "../ui/panel.js";
4
+ import { JimboText } from "../ui/jimboText.js";
5
+ import { JimboFlankNav } from "../ui/jimboFlankNav.js";
6
+ import { JimboColorOption } from "../ui/tokens.js";
7
+ const C = JimboColorOption;
8
+ export function PaginatedFilterBrowser({ filters, itemsPerPage = 120, // matching Balatro challenges layout pages
9
+ onSelectFilter, onMainAction, onSecondaryAction, onDeleteFilter, mainActionText = "SELECT", secondaryActionText = "EDIT", showSecondary = false, showDelete = false, emptyText = "No items found.", }) {
10
+ const [currentPage, setCurrentPage] = useState(0);
11
+ const [selectedId, setSelectedId] = useState(null);
12
+ const totalPages = Math.max(1, Math.ceil(filters.length / itemsPerPage));
13
+ // Bound current page just in case filters array shrinks
14
+ const safePage = Math.min(currentPage, totalPages - 1);
15
+ const pageFilters = filters.slice(safePage * itemsPerPage, (safePage + 1) * itemsPerPage);
16
+ const selectedFilter = filters.find((f) => f.id === selectedId) || null;
17
+ const handlePrevPage = () => setCurrentPage((p) => Math.max(0, p - 1));
18
+ const handleNextPage = () => setCurrentPage((p) => Math.min(totalPages - 1, p + 1));
19
+ return (_jsxs("div", { className: "j-flex-col j-gap-md", style: {
20
+ width: "100%",
21
+ height: "100%",
22
+ boxSizing: "border-box",
23
+ overflow: "hidden",
24
+ }, children: [_jsxs(JimboPanel, { style: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children: [_jsxs("div", { style: { flex: 1, overflowY: "auto", position: "relative" }, className: "hide-scrollbar j-flex-col j-items-center j-gap-xs", children: [pageFilters.map((filter) => {
25
+ const isSelected = selectedId === filter.id;
26
+ return (_jsxs("div", { style: {
27
+ position: "relative",
28
+ width: "100%",
29
+ maxWidth: 240, // Matches filter-list-item MinWidth=210 approx
30
+ display: "flex",
31
+ justifyContent: "center",
32
+ }, children: [isSelected && (_jsx("div", { className: "j-animate-jimbo-bounce", style: {
33
+ position: "absolute",
34
+ left: -20, // push out to left
35
+ top: "50%",
36
+ transform: "translateY(-50%)",
37
+ width: 0,
38
+ height: 0,
39
+ borderTop: "8px solid transparent",
40
+ borderBottom: "8px solid transparent",
41
+ borderLeft: `12px solid ${C.GOLD}`, // Balatro usually uses gold/white for selection triangle
42
+ filter: `drop-shadow(2px 2px 2px rgba(0,0,0,0.5))`,
43
+ } })), _jsx("div", { style: { width: "100%" }, children: _jsx(JimboButton, { tone: isSelected ? "red" : "grey", size: "sm", style: { width: "100%" }, onClick: () => {
44
+ setSelectedId(filter.id);
45
+ onSelectFilter?.(filter);
46
+ }, children: filter.name }) })] }, filter.id));
47
+ }), pageFilters.length === 0 && (_jsx("div", { className: "j-p-md", children: _jsx(JimboText, { size: "sm", tone: "grey", className: "j-text-center", children: emptyText }) }))] }), _jsx("div", { className: "j-flex j-justify-center j-mt-md", style: { flexShrink: 0 }, children: _jsx(JimboFlankNav, { canPrev: safePage > 0, canNext: safePage < totalPages - 1, onPrev: handlePrevPage, onNext: handleNextPage, children: _jsx("div", { style: {
48
+ background: C.RED,
49
+ padding: "8px 24px",
50
+ borderRadius: 8,
51
+ minWidth: 100,
52
+ boxShadow: `inset 0 2px 4px rgba(255,255,255,0.2), 0 4px 8px rgba(0,0,0,0.4)`,
53
+ }, className: "j-flex j-justify-center j-items-center", children: _jsxs(JimboText, { size: "sm", tone: "white", className: "j-text-center", style: { textShadow: "1px 2px 2px rgba(0,0,0,0.5)" }, children: [safePage + 1, " / ", totalPages] }) }) }) })] }), _jsx(JimboPanel, { style: { flexShrink: 0, minHeight: 180, display: "flex", flexDirection: "column", justifyContent: "center" }, children: !selectedFilter ? (_jsx(JimboText, { size: "md", tone: "grey", className: "j-text-center", children: "Select an item to view details" })) : (_jsxs("div", { className: "j-flex-col j-gap-sm j-items-center", style: { width: "100%" }, children: [_jsx(JimboText, { size: "lg", tone: "gold", className: "j-text-center", children: selectedFilter.name }), _jsx(JimboText, { size: "sm", tone: "white", className: "j-text-center", children: selectedFilter.description }), _jsxs("div", { className: "j-flex-col j-gap-xs j-items-center j-mt-xs", style: { opacity: 0.7 }, children: [selectedFilter.authorText && _jsx(JimboText, { size: "xs", tone: "grey", children: selectedFilter.authorText }), selectedFilter.dateText && _jsx(JimboText, { size: "xs", tone: "grey", children: selectedFilter.dateText }), selectedFilter.statsText && _jsx(JimboText, { size: "xs", tone: "grey", children: selectedFilter.statsText })] }), _jsxs("div", { className: "j-flex j-gap-sm j-mt-md j-justify-center j-flex-wrap", children: [_jsx(JimboButton, { tone: "blue", size: "sm", onClick: () => onMainAction?.(selectedFilter), children: mainActionText }), showSecondary && onSecondaryAction && (_jsx(JimboButton, { tone: "orange", size: "sm", onClick: () => onSecondaryAction(selectedFilter), children: secondaryActionText })), showDelete && onDeleteFilter && (_jsx(JimboButton, { tone: "red", size: "sm", onClick: () => onDeleteFilter(selectedFilter), children: "DELETE" }))] })] })) })] }));
54
+ }
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useState } from "react";
3
- import { JimboModal } from "../ui/panel.js";
3
+ import { JimboModal, JimboButton } from "../ui/panel.js";
4
+ import { JimboText } from "../ui/jimboText.js";
4
5
  import { DECK_OPTIONS, STAKE_OPTIONS } from "../lib/data/constants.js";
5
6
  import { DeckSprite } from "./DeckSprite.js";
6
7
  import { StakeSprite } from "../ui/sprites.js";
7
- import { JimboColorOption } from "../ui/tokens.js";
8
8
  const DECK_DESCRIPTIONS = {
9
9
  "Red": "+1 discard every round",
10
10
  "Blue": "+1 hand every round",
@@ -24,49 +24,21 @@ const DECK_DESCRIPTIONS = {
24
24
  };
25
25
  const STAKE_DESCRIPTIONS = {
26
26
  "White": "Base Difficulty",
27
- "Red": "Small Blind gives\nno reward money\nApplies all previous Stakes",
28
- "Green": "Required score scales\nfaster for each Ante\nApplies all previous Stakes",
29
- "Black": "Shop can have Jokers\nwith Eternal\nApplies all previous Stakes",
30
- "Blue": "-1 Discard\nApplies all previous Stakes",
31
- "Purple": "Required score scales\nfaster for each Ante\nApplies all previous Stakes",
32
- "Orange": "Shop can have Jokers\nwith Perishable\nApplies all previous Stakes",
33
- "Gold": "-1 hand size\nShop can have Jokers\nwith Rental\nApplies all previous Stakes",
27
+ "Red": "Small Blind gives\nno reward money",
28
+ "Green": "Required score scales\nfaster for each Ante",
29
+ "Black": "Shop can have Jokers\nwith Eternal",
30
+ "Blue": "-1 Discard",
31
+ "Purple": "Required score scales\nfaster for each Ante",
32
+ "Orange": "Shop can have Jokers\nwith Perishable",
33
+ "Gold": "-1 hand size\nShop can have Jokers\nwith Rental",
34
34
  };
35
- function formatTextWithColors(text) {
36
- // Hacky basic coloration for Balatro text
37
- const parts = text.split(/(\d+|Spades|Hearts|Clubs|Diamonds|\$[\d]+|Eternal|Perishable|Rental|Chips|Mult)/g);
38
- return parts.map((part, i) => {
39
- let color = JimboColorOption.WHITE;
40
- if (/^\d+$/.test(part))
41
- color = JimboColorOption.RED;
42
- else if (part === 'Spades')
43
- color = JimboColorOption.BLUE; // Spades are typically blue in high contrast
44
- else if (part === 'Hearts')
45
- color = JimboColorOption.RED;
46
- else if (part === 'Clubs')
47
- color = JimboColorOption.GREEN_TEXT;
48
- else if (part === 'Diamonds')
49
- color = JimboColorOption.ORANGE_TEXT;
50
- else if (part.startsWith('$'))
51
- color = JimboColorOption.GOLD_TEXT;
52
- else if (['Eternal', 'Perishable', 'Rental'].includes(part))
53
- color = JimboColorOption.GOLD_TEXT; // Should be specific colours
54
- else if (part === 'Chips')
55
- color = JimboColorOption.BLUE;
56
- else if (part === 'Mult')
57
- color = JimboColorOption.RED;
58
- if (color !== JimboColorOption.WHITE) {
59
- return _jsx("span", { style: { color }, children: part }, i);
60
- }
61
- return _jsx("span", { children: part }, i);
62
- });
63
- }
64
35
  export function RunConfigModal({ open, onClose, deck, stake, onChange, }) {
65
36
  const [activeDeck, setActiveDeck] = useState(deck);
66
37
  const [activeStake, setActiveStake] = useState(stake);
67
38
  // Sync state if props change when opened
68
39
  React.useEffect(() => {
69
40
  if (open) {
41
+ // eslint-disable-next-line react-hooks/set-state-in-effect
70
42
  setActiveDeck(deck);
71
43
  setActiveStake(stake);
72
44
  }
@@ -83,116 +55,5 @@ export function RunConfigModal({ open, onClose, deck, stake, onChange, }) {
83
55
  onChange(activeDeck, activeStake);
84
56
  onClose();
85
57
  };
86
- const CarouselButton = ({ onClick, children }) => (_jsx("button", { onClick: onClick, style: {
87
- width: 32,
88
- height: "100%",
89
- borderRadius: 8,
90
- background: JimboColorOption.RED,
91
- border: `2px solid ${JimboColorOption.WHITE}`, // Should be dark edge with inner shadow but let's use standard red btn style
92
- color: JimboColorOption.WHITE,
93
- fontSize: 24,
94
- fontFamily: "m6x11plus, monospace",
95
- display: "flex",
96
- alignItems: "center",
97
- justifyContent: "center",
98
- cursor: "pointer",
99
- flexShrink: 0,
100
- }, className: "j-btn j-btn--red" // piggyback on Jimbo button styles
101
- , children: _jsx("div", { className: "j-btn__face", style: { padding: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: children }) }));
102
- return (_jsx(JimboModal, { open: open, onClose: onClose, children: _jsxs("div", { style: {
103
- width: 600,
104
- maxWidth: "95vw",
105
- background: JimboColorOption.TEAL_GREY, // A greenish-grey slate color
106
- borderRadius: 16,
107
- border: `3px solid ${JimboColorOption.BORDER_SILVER}`,
108
- boxShadow: `inset 0 0 0 2px ${JimboColorOption.DARKEST}, 0 8px 16px rgba(0,0,0,0.5)`,
109
- padding: "24px 20px",
110
- display: "flex",
111
- flexDirection: "column",
112
- gap: 24,
113
- position: "relative",
114
- }, children: [_jsx("div", { style: {
115
- position: "absolute",
116
- top: -20,
117
- left: 0,
118
- right: 0,
119
- display: "flex",
120
- justifyContent: "center",
121
- gap: 8,
122
- }, children: _jsx("div", { className: "j-btn j-btn--red", style: { height: 36, pointerEvents: "none" }, children: _jsx("div", { className: "j-btn__face", style: { fontSize: 16, padding: "0 16px" }, children: "Seed Finder" }) }) }), _jsxs("div", { style: { display: "flex", height: 160, gap: 12 }, children: [_jsx(CarouselButton, { onClick: prevDeck, children: "<" }), _jsxs("div", { style: {
123
- flex: 1,
124
- background: JimboColorOption.DARK_GREY,
125
- borderRadius: 12,
126
- border: `2px solid ${JimboColorOption.PANEL_EDGE}`,
127
- display: "flex",
128
- alignItems: "center",
129
- padding: "16px",
130
- gap: 16,
131
- }, children: [_jsx(DeckSprite, { deck: activeDeck, size: 100, style: { flexShrink: 0 } }), _jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }, children: [_jsxs("h2", { style: {
132
- fontFamily: "m6x11plus, monospace",
133
- fontSize: 28,
134
- color: JimboColorOption.WHITE,
135
- margin: "0 0 8px 0",
136
- textShadow: `1px 1px 0 ${JimboColorOption.DARKEST}`,
137
- }, children: [activeDeck, " Deck"] }), _jsx("div", { style: {
138
- background: JimboColorOption.WHITE,
139
- borderRadius: 8,
140
- padding: "12px",
141
- width: "100%",
142
- minHeight: 80,
143
- display: "flex",
144
- flexDirection: "column",
145
- alignItems: "center",
146
- justifyContent: "center",
147
- boxShadow: `inset 0 -2px 0 0 ${JimboColorOption.PANEL_EDGE}`,
148
- }, children: _jsx("div", { style: {
149
- fontFamily: "m6x11plus, monospace",
150
- fontSize: 16,
151
- color: JimboColorOption.DARKEST,
152
- textAlign: "center",
153
- whiteSpace: "pre-line",
154
- lineHeight: 1.2,
155
- }, children: formatTextWithColors(DECK_DESCRIPTIONS[activeDeck] || "Standard 52 card deck") }) })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4, paddingRight: 4 }, children: [_jsx("div", { style: { width: 8, height: 8, borderRadius: 4, background: JimboColorOption.GREY } }), _jsx("div", { style: { width: 8, height: 8, borderRadius: 4, background: JimboColorOption.GREY } }), _jsx("div", { style: { width: 8, height: 8, borderRadius: 4, background: JimboColorOption.GREEN } }), _jsx("div", { style: { width: 8, height: 8, borderRadius: 4, background: JimboColorOption.RED } }), _jsx("div", { style: { width: 8, height: 8, borderRadius: 4, background: JimboColorOption.WHITE } })] })] }), _jsx(CarouselButton, { onClick: nextDeck, children: ">" })] }), _jsxs("div", { style: { display: "flex", height: 120, gap: 12 }, children: [_jsx(CarouselButton, { onClick: prevStake, children: "<" }), _jsxs("div", { style: {
156
- flex: 1,
157
- background: JimboColorOption.DARK_GREY,
158
- borderRadius: 12,
159
- border: `2px solid ${JimboColorOption.PANEL_EDGE}`,
160
- display: "flex",
161
- alignItems: "center",
162
- padding: "12px 16px",
163
- gap: 16,
164
- position: "relative",
165
- }, children: [_jsx("div", { style: {
166
- position: 'absolute',
167
- left: -14, // Cut out effect text
168
- top: '50%',
169
- transform: 'translateY(-50%) rotate(-90deg)',
170
- fontFamily: "m6x11plus, monospace",
171
- fontSize: 16,
172
- color: JimboColorOption.GREY,
173
- }, children: "Stake" }), _jsx("div", { style: { paddingLeft: 20 }, children: _jsx(StakeSprite, { stake: activeStake, width: 64, style: { flexShrink: 0 } }) }), _jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }, children: [_jsxs("h2", { style: {
174
- fontFamily: "m6x11plus, monospace",
175
- fontSize: 22,
176
- color: JimboColorOption.WHITE,
177
- margin: "0 0 6px 0",
178
- textShadow: `1px 1px 0 ${JimboColorOption.DARKEST}`,
179
- }, children: [activeStake, " Stake"] }), _jsx("div", { style: {
180
- background: JimboColorOption.WHITE,
181
- borderRadius: 8,
182
- padding: "8px 12px",
183
- width: "100%",
184
- minHeight: 60,
185
- display: "flex",
186
- flexDirection: "column",
187
- alignItems: "center",
188
- justifyContent: "center",
189
- boxShadow: `inset 0 -2px 0 0 ${JimboColorOption.PANEL_EDGE}`,
190
- }, children: _jsx("div", { style: {
191
- fontFamily: "m6x11plus, monospace",
192
- fontSize: 14,
193
- color: JimboColorOption.DARKEST,
194
- textAlign: "center",
195
- whiteSpace: "pre-line",
196
- lineHeight: 1.2,
197
- }, children: formatTextWithColors(STAKE_DESCRIPTIONS[activeStake] || "Base Difficulty") }) })] })] }), _jsx(CarouselButton, { onClick: nextStake, children: ">" })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12, marginTop: 8 }, children: [_jsx("button", { onClick: handleApply, className: "j-btn j-btn--blue j-btn--lg", style: { width: "80%", height: 50, fontSize: 24, padding: 0 }, children: _jsx("div", { className: "j-btn__face", style: { display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: "APPLY" }) }), _jsx("button", { onClick: onClose, className: "j-btn j-btn--orange j-btn--md", style: { width: "100%", height: 40, fontSize: 18, padding: 0 }, children: _jsx("div", { className: "j-btn__face", style: { display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: "Back" }) })] })] }) }));
58
+ return (_jsxs(JimboModal, { open: open, onClose: onClose, title: "Run Config", children: [_jsxs("div", { className: "j-flex j-items-center j-gap-sm", children: [_jsx(JimboButton, { tone: "red", size: "sm", onClick: prevDeck, children: "<" }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", style: { flex: 1 }, children: [_jsx(DeckSprite, { deck: activeDeck, size: 64 }), _jsxs(JimboText, { size: "md", tone: "white", children: [activeDeck, " Deck"] }), _jsx(JimboText, { size: "micro", tone: "grey", className: "j-text-center", style: { whiteSpace: "pre-line" }, children: DECK_DESCRIPTIONS[activeDeck] || "Standard 52 card deck" })] }), _jsx(JimboButton, { tone: "red", size: "sm", onClick: nextDeck, children: ">" })] }), _jsxs("div", { className: "j-flex j-items-center j-gap-sm", style: { marginTop: 8 }, children: [_jsx(JimboButton, { tone: "red", size: "sm", onClick: prevStake, children: "<" }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", style: { flex: 1 }, children: [_jsx(StakeSprite, { stake: activeStake, width: 48 }), _jsxs(JimboText, { size: "md", tone: "white", children: [activeStake, " Stake"] }), _jsx(JimboText, { size: "micro", tone: "grey", className: "j-text-center", style: { whiteSpace: "pre-line" }, children: STAKE_DESCRIPTIONS[activeStake] || "Base Difficulty" })] }), _jsx(JimboButton, { tone: "red", size: "sm", onClick: nextStake, children: ">" })] }), _jsxs("div", { className: "j-flex-col j-gap-sm", style: { marginTop: 8 }, children: [_jsx(JimboButton, { tone: "blue", size: "lg", fullWidth: true, onClick: handleApply, children: "Apply" }), _jsx(JimboButton, { tone: "orange", size: "lg", fullWidth: true, onClick: onClose, children: "Back" })] })] }));
198
59
  }
@@ -20,9 +20,8 @@ export interface CategoryPickerConfig {
20
20
  export interface CategoryPickerProps {
21
21
  config: CategoryPickerConfig;
22
22
  onSelect: (selection: SlotSelection) => void;
23
- onCancel: () => void;
24
23
  }
25
- export declare function CategoryPicker({ config, onSelect, onCancel }: CategoryPickerProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function CategoryPicker({ config, onSelect }: CategoryPickerProps): import("react/jsx-runtime").JSX.Element;
26
25
  export declare const VOUCHER_PICKER_CONFIG: CategoryPickerConfig;
27
26
  export declare const TAG_PICKER_CONFIG: CategoryPickerConfig;
28
27
  export declare const BOSS_PICKER_CONFIG: CategoryPickerConfig;
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /* eslint-disable react-refresh/only-export-components */
3
4
  import { useState, useCallback, useMemo } from "react";
4
5
  import { JimboSprite } from "../../ui/sprites.js";
5
6
  import { JimboColorOption } from "../../ui/tokens.js";
6
7
  import { JimboText } from "../../ui/jimboText.js";
7
8
  // ─── Component ───────────────────────────────────────────────────────────────
8
9
  const C = JimboColorOption;
9
- export function CategoryPicker({ config, onSelect, onCancel }) {
10
+ export function CategoryPicker({ config, onSelect }) {
10
11
  const [search, setSearch] = useState("");
11
12
  const filtered = useMemo(() => {
12
13
  if (!search)