jaml-ui 0.14.4 → 0.17.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.
Files changed (97) hide show
  1. package/DESIGN.md +9 -11
  2. package/dist/assets.d.ts +7 -0
  3. package/dist/assets.js +11 -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/MotelyVersionBadge.d.ts +1 -3
  18. package/dist/components/MotelyVersionBadge.js +4 -16
  19. package/dist/components/jamlMap/JamlMapEditorDemo.d.ts +8 -0
  20. package/dist/components/jamlMap/JamlMapEditorDemo.js +170 -0
  21. package/dist/components/jamlMap/JokerPicker.d.ts +7 -0
  22. package/dist/components/jamlMap/JokerPicker.js +258 -0
  23. package/dist/components/jamlMap/MysterySlot.d.ts +32 -0
  24. package/dist/components/jamlMap/MysterySlot.js +109 -0
  25. package/dist/components/jamlMap/index.d.ts +3 -0
  26. package/dist/components/jamlMap/index.js +3 -0
  27. package/dist/core.d.ts +0 -2
  28. package/dist/core.js +0 -2
  29. package/dist/decode/motelyItemDecoder.d.ts +10 -23
  30. package/dist/decode/motelyItemDecoder.js +103 -248
  31. package/dist/decode/motelySprite.d.ts +19 -0
  32. package/dist/decode/motelySprite.js +84 -0
  33. package/dist/hooks/analyzerStreamRegistry.js +30 -82
  34. package/dist/hooks/useAnalyzer.d.ts +10 -3
  35. package/dist/hooks/useAnalyzer.js +11 -6
  36. package/dist/hooks/useIntersectionObserver.d.ts +14 -0
  37. package/dist/hooks/useIntersectionObserver.js +50 -0
  38. package/dist/index.d.ts +3 -8
  39. package/dist/index.js +2 -7
  40. package/dist/motely.d.ts +2 -1
  41. package/dist/motely.js +2 -1
  42. package/dist/motelyDisplay.d.ts +4 -623
  43. package/dist/motelyDisplay.js +26 -165
  44. package/dist/r3f/Card3D.d.ts +2 -2
  45. package/dist/r3f/Card3D.js +13 -46
  46. package/dist/r3f/JimboBillboard.d.ts +10 -0
  47. package/dist/r3f/JimboBillboard.js +29 -0
  48. package/dist/r3f/JimboText3D.d.ts +9 -0
  49. package/dist/r3f/JimboText3D.js +8 -0
  50. package/dist/r3f.d.ts +2 -0
  51. package/dist/r3f.js +2 -0
  52. package/dist/render/CanvasRenderer.js +7 -171
  53. package/dist/sprites/spriteData.d.ts +1 -0
  54. package/dist/sprites/spriteData.js +1 -0
  55. package/dist/sprites/spriteMapper.d.ts +78 -1
  56. package/dist/sprites/spriteMapper.js +52 -0
  57. package/dist/ui/JimboBadge.d.ts +13 -0
  58. package/dist/ui/JimboBadge.js +8 -0
  59. package/dist/ui/JimboFloating.d.ts +8 -0
  60. package/dist/ui/JimboFloating.js +17 -0
  61. package/dist/ui/JimboToggleList.d.ts +11 -0
  62. package/dist/ui/JimboToggleList.js +5 -0
  63. package/dist/ui/codeBlock.js +2 -3
  64. package/dist/ui/footer.d.ts +4 -0
  65. package/dist/ui/footer.js +6 -4
  66. package/dist/ui/hooks.d.ts +89 -0
  67. package/dist/ui/hooks.js +551 -0
  68. package/dist/ui/jimboBackground.js +2 -131
  69. package/dist/ui/jimboCopyRow.d.ts +4 -0
  70. package/dist/ui/jimboCopyRow.js +5 -22
  71. package/dist/ui/jimboFilterBar.d.ts +1 -4
  72. package/dist/ui/jimboFilterBar.js +2 -61
  73. package/dist/ui/jimboFlankNav.d.ts +1 -2
  74. package/dist/ui/jimboFlankNav.js +5 -30
  75. package/dist/ui/jimboTabs.d.ts +1 -5
  76. package/dist/ui/jimboTabs.js +6 -41
  77. package/dist/ui/jimboText.d.ts +1 -1
  78. package/dist/ui/jimboText.js +15 -32
  79. package/dist/ui/jimboTooltip.d.ts +1 -12
  80. package/dist/ui/jimboTooltip.js +6 -82
  81. package/dist/ui/panel.d.ts +3 -1
  82. package/dist/ui/panel.js +11 -47
  83. package/dist/ui/showcase.d.ts +4 -0
  84. package/dist/ui/showcase.js +9 -36
  85. package/dist/ui/sprites.d.ts +14 -0
  86. package/dist/ui/sprites.js +54 -13
  87. package/dist/ui.d.ts +4 -0
  88. package/dist/ui.js +5 -0
  89. package/package.json +130 -122
  90. package/dist/components/JamlSpeedometer.d.ts +0 -11
  91. package/dist/components/JamlSpeedometer.js +0 -54
  92. package/dist/decode/packedBalatroItem.d.ts +0 -13
  93. package/dist/decode/packedBalatroItem.js +0 -26
  94. package/dist/hooks/loadMotelyWasm.d.ts +0 -7
  95. package/dist/hooks/loadMotelyWasm.js +0 -16
  96. package/dist/utils/itemUtils.d.ts +0 -11
  97. package/dist/utils/itemUtils.js +0 -71
@@ -9,46 +9,19 @@ const TONE_COLOR = {
9
9
  gold: JimboColorOption.GOLD,
10
10
  green: JimboColorOption.GREEN,
11
11
  };
12
- const DEFAULT_STATS = { searched: '15.6B', matches: '2,847', speed: '5.4M/s' };
12
+ const DEFAULT_STATS = { searched: '0', matches: '0', speed: '0' };
13
+ /**
14
+ * Landing/showcase screen for the seed curator.
15
+ * All styling via jimbo.css `.j-showcase` classes — zero inline styles.
16
+ */
13
17
  export function Showcase({ hotFilters = [], recentFinds = [], stats = DEFAULT_STATS, onNewSearch, onBrowseFilters, onBack, }) {
14
18
  const C = JimboColorOption;
15
- return (_jsxs("div", { style: {
16
- width: '100%', height: '100%', background: C.DARKEST,
17
- display: 'flex', flexDirection: 'column',
18
- fontFamily: 'm6x11plus, monospace', color: C.WHITE, overflow: 'hidden',
19
- }, children: [_jsxs("div", { style: { flex: 1, minHeight: 0, overflowY: 'auto', padding: '18px 14px 10px' }, children: [_jsxs("div", { style: { textAlign: 'center', marginBottom: 18 }, children: [_jsx("div", { style: { fontSize: 32, letterSpacing: 3, lineHeight: 1, color: C.GOLD, textShadow: '2px 2px 0 rgba(0,0,0,.8)' }, children: "Balatro" }), _jsx("div", { style: { fontSize: 14, letterSpacing: 4, color: C.GREY, marginTop: 4, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }, children: "Seed \u00B7 Curator" })] }), _jsx("div", { style: {
20
- background: C.DARK_GREY, borderRadius: 6, padding: 10,
21
- border: `2px solid ${C.PANEL_EDGE}`, boxShadow: `0 2px 0 ${C.BLACK}`,
22
- display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, textAlign: 'center', marginBottom: 16,
23
- }, children: [
19
+ return (_jsxs("div", { className: "j-showcase", children: [_jsxs("div", { className: "j-showcase__scroll", children: [_jsxs("div", { className: "j-showcase__wordmark", children: [_jsx("div", { className: "j-showcase__wordmark-title", children: "Balatro" }), _jsx("div", { className: "j-showcase__wordmark-sub", children: "Seed \u00B7 Curator" })] }), _jsx("div", { className: "j-showcase__stats", children: [
24
20
  [stats.searched, 'searched'],
25
21
  [stats.matches, 'matches'],
26
22
  [stats.speed, 'speed'],
27
- ].map(([n, l]) => (_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, color: C.GOLD, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }, children: n }), _jsx("div", { style: { fontSize: 9, color: C.GREY, letterSpacing: 2, marginTop: 2 }, children: l })] }, l))) }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }, children: [_jsx("div", { style: {
28
- fontSize: 11, letterSpacing: 2, padding: '2px 8px',
29
- background: C.BLUE, color: C.WHITE, borderRadius: 3,
30
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
31
- }, children: "Hot Filters" }), _jsx("div", { style: { flex: 1, height: 2, background: `${C.BLUE}55`, borderRadius: 1 } })] }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 16 }, children: hotFilters.map((f, i) => {
23
+ ].map(([n, l]) => (_jsxs("div", { children: [_jsx("div", { className: "j-showcase__stat-value", children: n }), _jsx("div", { className: "j-showcase__stat-label", children: l })] }, l))) }), _jsxs("div", { className: "j-showcase__section-header", children: [_jsx("div", { className: "j-showcase__section-tag", style: { background: C.BLUE }, children: "Hot Filters" }), _jsx("div", { className: "j-showcase__section-rule", style: { background: `${C.BLUE}55` } })] }), _jsx("div", { className: "j-showcase__filter-list", children: hotFilters.map((f, i) => {
32
24
  const tColor = TONE_COLOR[f.tone];
33
- return (_jsxs("div", { style: {
34
- background: C.DARK_GREY, borderRadius: 6, padding: 10,
35
- border: `2px solid ${tColor}`, boxShadow: `0 2px 0 ${C.BLACK}`,
36
- display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer',
37
- }, children: [_jsx("div", { style: { display: 'flex', gap: 2 }, children: f.sample.map((name, j) => (_jsx("div", { style: { width: 30, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: _jsx(JimboSprite, { name: name, width: 28 }) }, j))) }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: {
38
- fontSize: 13, color: C.WHITE, letterSpacing: 1,
39
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
40
- overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
41
- }, children: f.name }), _jsxs("div", { style: { fontSize: 9, color: C.GOLD_TEXT, letterSpacing: 1, marginTop: 2 }, children: ["by ", f.author] })] }), _jsxs("div", { style: { textAlign: 'right' }, children: [_jsx("div", { style: { fontSize: 14, color: tColor, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }, children: f.hits }), _jsx("div", { style: { fontSize: 8, color: C.GREY, letterSpacing: 1 }, children: "seeds" })] })] }, i));
42
- }) }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }, children: [_jsx("div", { style: {
43
- fontSize: 11, letterSpacing: 2, padding: '2px 8px',
44
- background: C.GREEN, color: C.WHITE, borderRadius: 3,
45
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
46
- }, children: "Recent Finds" }), _jsx("div", { style: { flex: 1, height: 2, background: `${C.GREEN}55`, borderRadius: 1 } })] }), _jsx("div", { style: {
47
- background: C.DARK_GREY, borderRadius: 6, padding: '8px 10px',
48
- border: `2px solid ${C.PANEL_EDGE}`, boxShadow: `0 2px 0 ${C.BLACK}`,
49
- fontSize: 11, color: C.GREY, letterSpacing: 1, lineHeight: 1.7,
50
- }, children: recentFinds.length === 0 ? (_jsx("div", { style: { color: C.GREY }, children: "No recent finds yet." })) : recentFinds.map((r, i) => (_jsxs("div", { children: [_jsx("span", { style: { color: C.GOLD_TEXT }, children: r.seed }), ' · ', r.filterName, r.score > 0 && _jsxs("span", { style: { color: C.GREEN_TEXT }, children: [" +", r.score] })] }, i))) }), _jsx("div", { style: { height: 16 } })] }), _jsxs("div", { style: {
51
- padding: '8px 10px 10px', borderTop: `2px solid ${C.BLACK}`, background: C.DARK_GREY,
52
- display: 'flex', flexDirection: 'column', gap: 6,
53
- }, children: [_jsx(JimboButton, { tone: "green", fullWidth: true, size: "md", onClick: onNewSearch, children: "New Search" }), _jsx(JimboButton, { tone: "blue", fullWidth: true, size: "md", onClick: onBrowseFilters, children: "Browse Filters" }), _jsx(JimboButton, { tone: "orange", fullWidth: true, size: "md", onClick: onBack, children: "Back" })] })] }));
25
+ return (_jsxs("div", { className: "j-showcase__filter-card", style: { border: `2px solid ${tColor}` }, children: [_jsx("div", { className: "j-showcase__filter-sprites", children: f.sample.map((name, j) => (_jsx("div", { className: "j-showcase__filter-sprite", children: _jsx(JimboSprite, { name: name, width: 28 }) }, j))) }), _jsxs("div", { className: "j-showcase__filter-info", children: [_jsx("div", { className: "j-showcase__filter-name", children: f.name }), _jsxs("div", { className: "j-showcase__filter-author", children: ["by ", f.author] })] }), _jsxs("div", { className: "j-showcase__filter-hits", children: [_jsx("div", { className: "j-showcase__filter-hits-value", style: { color: tColor }, children: f.hits }), _jsx("div", { className: "j-showcase__filter-hits-label", children: "seeds" })] })] }, i));
26
+ }) }), _jsxs("div", { className: "j-showcase__section-header", children: [_jsx("div", { className: "j-showcase__section-tag", style: { background: C.GREEN }, children: "Recent Finds" }), _jsx("div", { className: "j-showcase__section-rule", style: { background: `${C.GREEN}55` } })] }), _jsx("div", { className: "j-showcase__recent", children: recentFinds.length === 0 ? (_jsx("div", { children: "No recent finds yet." })) : recentFinds.map((r, i) => (_jsxs("div", { children: [_jsx("span", { style: { color: C.GOLD_TEXT }, children: r.seed }), ' · ', r.filterName, r.score > 0 && _jsxs("span", { style: { color: C.GREEN_TEXT }, children: [" +", r.score] })] }, i))) }), _jsx("div", { style: { height: 16 } })] }), _jsxs("div", { className: "j-showcase__actions", children: [_jsx(JimboButton, { tone: "green", fullWidth: true, size: "md", onClick: onNewSearch, children: "New Search" }), _jsx(JimboButton, { tone: "blue", fullWidth: true, size: "md", onClick: onBrowseFilters, children: "Browse Filters" }), _jsx(JimboButton, { tone: "orange", fullWidth: true, size: "md", onClick: onBack, children: "Back" })] })] }));
54
27
  }
@@ -8,3 +8,17 @@ export interface JimboSpriteProps {
8
8
  style?: React.CSSProperties;
9
9
  }
10
10
  export declare function JimboSprite({ name, sheet, width, height, style }: JimboSpriteProps): import("react/jsx-runtime").JSX.Element | null;
11
+ export interface StakeSpriteProps {
12
+ stake: string;
13
+ width?: number;
14
+ height?: number;
15
+ style?: React.CSSProperties;
16
+ }
17
+ export declare function StakeSprite({ stake, width, height, style }: StakeSpriteProps): import("react/jsx-runtime").JSX.Element;
18
+ export interface DeckSpriteProps {
19
+ deck: string;
20
+ width?: number;
21
+ height?: number;
22
+ style?: React.CSSProperties;
23
+ }
24
+ export declare function DeckSprite({ deck, width, height, style }: DeckSpriteProps): import("react/jsx-runtime").JSX.Element;
@@ -1,23 +1,18 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { resolveJamlAssetUrl } from '../assets.js';
4
- import { getSpriteData } from '../sprites/spriteMapper.js';
5
- const SHEET_META = {
6
- Jokers: { cols: 10, rows: 16, assetKey: 'jokers' },
7
- Tarots: { cols: 10, rows: 6, assetKey: 'tarots' },
8
- Vouchers: { cols: 9, rows: 4, assetKey: 'vouchers' },
9
- Boosters: { cols: 4, rows: 9, assetKey: 'boosters' },
10
- BlindChips: { cols: 21, rows: 31, assetKey: 'blinds' },
11
- tags: { cols: 6, rows: 5, assetKey: 'tags' },
12
- Enhancers: { cols: 7, rows: 5, assetKey: 'enhancers' },
13
- Editions: { cols: 5, rows: 1, assetKey: 'editions' },
14
- };
4
+ import { getSpriteData, getMysterySprite, SHEET_META } from '../sprites/spriteMapper.js';
15
5
  export function JimboSprite({ name, sheet, width = 40, height, style }) {
16
6
  const sprite = getSpriteData(name);
17
7
  const resolvedSheet = sheet ?? sprite?.type ?? 'Jokers';
18
8
  const meta = SHEET_META[resolvedSheet];
19
- const pos = sprite?.pos ?? { x: 0, y: 0 };
20
- const h = height ?? width;
9
+ const mystery = getMysterySprite(resolvedSheet);
10
+ const pos = sprite?.pos ?? mystery.pos;
11
+ let defaultH = width;
12
+ if (["Jokers", "Tarots", "Vouchers", "Boosters", "Decks", "Enhancers", "Editions"].includes(resolvedSheet)) {
13
+ defaultH = Math.round((width * 95) / 71);
14
+ }
15
+ const h = height ?? defaultH;
21
16
  if (!meta)
22
17
  return null;
23
18
  const bgW = width * meta.cols;
@@ -34,3 +29,49 @@ export function JimboSprite({ name, sheet, width = 40, height, style }) {
34
29
  ...style,
35
30
  } }));
36
31
  }
32
+ const STAKE_MAP = ["White", "Red", "Green", "Black", "Blue", "Purple", "Orange", "Gold"];
33
+ export function StakeSprite({ stake, width = 29, height, style }) {
34
+ const index = STAKE_MAP.indexOf(stake.replace(" Stake", ""));
35
+ const idx = index >= 0 ? index : 0;
36
+ const x = idx % 5;
37
+ const y = Math.floor(idx / 5);
38
+ const h = height ?? width;
39
+ const bgW = width * 5;
40
+ const bgH = h * 2;
41
+ return (_jsx("div", { style: {
42
+ width, height: h, flexShrink: 0,
43
+ backgroundImage: `url(${resolveJamlAssetUrl('stakes')})`,
44
+ backgroundSize: `${bgW}px ${bgH}px`,
45
+ backgroundPosition: `-${x * width}px -${y * h}px`,
46
+ backgroundRepeat: 'no-repeat',
47
+ imageRendering: 'pixelated',
48
+ ...style,
49
+ } }));
50
+ }
51
+ const DECK_ROWS = {
52
+ Red: 0,
53
+ Blue: 1,
54
+ Yellow: 2,
55
+ Green: 3,
56
+ Black: 0,
57
+ Magic: 1,
58
+ Nebula: 2,
59
+ Ghost: 3,
60
+ };
61
+ export function DeckSprite({ deck, width = 71, height, style }) {
62
+ const baseDeck = deck.replace(" Deck", "");
63
+ const y = DECK_ROWS[baseDeck] ?? 0;
64
+ const x = 12;
65
+ const h = height ?? (width * 95 / 71);
66
+ const bgW = width * 13;
67
+ const bgH = h * 4;
68
+ return (_jsx("div", { style: {
69
+ width, height: h, flexShrink: 0,
70
+ backgroundImage: `url(${resolveJamlAssetUrl('deck')})`,
71
+ backgroundSize: `${bgW}px ${bgH}px`,
72
+ backgroundPosition: `-${x * width}px -${y * h}px`,
73
+ backgroundRepeat: 'no-repeat',
74
+ imageRendering: 'pixelated',
75
+ ...style,
76
+ } }));
77
+ }
package/dist/ui.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  export * from './ui/tokens.js';
2
+ import './ui/jimbo.css';
2
3
  export * from './ui/jimboText.js';
3
4
  export * from './ui/panel.js';
4
5
  export * from './ui/jimboTabs.js';
6
+ export * from './ui/JimboFloating.js';
7
+ export * from './ui/JimboToggleList.js';
8
+ export * from './ui/JimboBadge.js';
5
9
  export * from './ui/jimboFlankNav.js';
6
10
  export * from './ui/jimboFilterBar.js';
7
11
  export * from './ui/jimboBackground.js';
package/dist/ui.js CHANGED
@@ -1,7 +1,12 @@
1
1
  export * from './ui/tokens.js';
2
+ // Side-effect: design system CSS custom properties + component classes
3
+ import './ui/jimbo.css';
2
4
  export * from './ui/jimboText.js';
3
5
  export * from './ui/panel.js';
4
6
  export * from './ui/jimboTabs.js';
7
+ export * from './ui/JimboFloating.js';
8
+ export * from './ui/JimboToggleList.js';
9
+ export * from './ui/JimboBadge.js';
5
10
  export * from './ui/jimboFlankNav.js';
6
11
  export * from './ui/jimboFilterBar.js';
7
12
  export * from './ui/jimboBackground.js';
package/package.json CHANGED
@@ -1,122 +1,130 @@
1
- {
2
- "name": "jaml-ui",
3
- "version": "0.14.4",
4
- "description": "Balatro rendering components, sprite metadata, and optional Motely helpers for React apps.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- },
13
- "./core": {
14
- "types": "./dist/core.d.ts",
15
- "import": "./dist/core.js"
16
- },
17
- "./motely": {
18
- "types": "./dist/motely.d.ts",
19
- "import": "./dist/motely.js"
20
- },
21
- "./ui": {
22
- "types": "./dist/ui.d.ts",
23
- "import": "./dist/ui.js"
24
- },
25
- "./r3f": {
26
- "types": "./dist/r3f.d.ts",
27
- "import": "./dist/r3f.js"
28
- },
29
- "./fonts.css": "./fonts.css",
30
- "./package.json": "./package.json"
31
- },
32
- "sideEffects": [
33
- "./fonts.css"
34
- ],
35
- "files": [
36
- "dist",
37
- "assets/*.png",
38
- "assets/fonts",
39
- "fonts.css",
40
- "README.md",
41
- "DESIGN.md",
42
- "LICENSE"
43
- ],
44
- "scripts": {
45
- "build": "tsc --pretty false",
46
- "dev": "tsc --watch",
47
- "demo": "vite --config demo/vite.config.ts",
48
- "typecheck": "tsc --noEmit --pretty false",
49
- "prepack": "npm run build"
50
- },
51
- "engines": {
52
- "node": ">=18"
53
- },
54
- "publishConfig": {
55
- "access": "public"
56
- },
57
- "repository": {
58
- "type": "git",
59
- "url": "https://github.com/OptimusPi/jaml-ui"
60
- },
61
- "homepage": "https://github.com/OptimusPi/jaml-ui#readme",
62
- "bugs": {
63
- "url": "https://github.com/OptimusPi/jaml-ui/issues"
64
- },
65
- "keywords": [
66
- "balatro",
67
- "jaml",
68
- "motely",
69
- "seed",
70
- "card",
71
- "sprite",
72
- "ui"
73
- ],
74
- "author": "pifreak",
75
- "license": "MIT",
76
- "peerDependencies": {
77
- "@monaco-editor/react": ">=4.0.0",
78
- "@react-spring/three": ">=9.0.0",
79
- "@react-three/fiber": ">=8.0.0",
80
- "monaco-editor": ">=0.50.0",
81
- "motely-wasm": "^10.2.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
82
- "react": "^18.2.0 || ^19.0.0",
83
- "react-dom": "^18.2.0 || ^19.0.0",
84
- "react-icons": ">=5.0.0",
85
- "three": ">=0.150.0"
86
- },
87
- "peerDependenciesMeta": {
88
- "@react-spring/three": {
89
- "optional": true
90
- },
91
- "@react-three/fiber": {
92
- "optional": true
93
- },
94
- "motely-wasm": {
95
- "optional": true
96
- },
97
- "react-icons": {
98
- "optional": true
99
- },
100
- "three": {
101
- "optional": true
102
- }
103
- },
104
- "devDependencies": {
105
- "@monaco-editor/react": "^4.7.0",
106
- "@react-spring/three": "^10.0.3",
107
- "@react-three/fiber": "^9.6.0",
108
- "@types/react": "^19.2.14",
109
- "@types/react-dom": "^19.2.3",
110
- "@types/three": "^0.184.0",
111
- "@vitejs/plugin-react": "^5.0.4",
112
- "monaco-editor": "^0.55.1",
113
- "motely-wasm": "^14.0.2",
114
- "react": "^19.2.4",
115
- "react-dom": "^19.2.4",
116
- "react-icons": "^5.6.0",
117
- "three": "^0.184.0",
118
- "typescript": "^5.9.3",
119
- "vite": "^8.0.9",
120
- "@google/design.md": "^0.1.1"
121
- }
122
- }
1
+ {
2
+ "name": "jaml-ui",
3
+ "version": "0.17.0",
4
+ "description": "Balatro rendering components, sprite metadata, and optional Motely helpers for React apps.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/core.d.ts",
15
+ "import": "./dist/core.js"
16
+ },
17
+ "./motely": {
18
+ "types": "./dist/motely.d.ts",
19
+ "import": "./dist/motely.js"
20
+ },
21
+ "./ui": {
22
+ "types": "./dist/ui.d.ts",
23
+ "import": "./dist/ui.js"
24
+ },
25
+ "./r3f": {
26
+ "types": "./dist/r3f.d.ts",
27
+ "import": "./dist/r3f.js"
28
+ },
29
+ "./fonts.css": "./fonts.css",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "sideEffects": [
33
+ "./fonts.css"
34
+ ],
35
+ "files": [
36
+ "dist",
37
+ "assets/*.png",
38
+ "assets/fonts",
39
+ "fonts.css",
40
+ "README.md",
41
+ "DESIGN.md",
42
+ "LICENSE"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsc --pretty false",
46
+ "dev": "tsc --watch",
47
+ "demo": "vite --config demo/vite.config.ts",
48
+ "typecheck": "tsc --noEmit --pretty false",
49
+ "prepack": "npm run build"
50
+ },
51
+ "engines": {
52
+ "node": ">=18"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "https://github.com/OptimusPi/jaml-ui"
60
+ },
61
+ "homepage": "https://github.com/OptimusPi/jaml-ui#readme",
62
+ "bugs": {
63
+ "url": "https://github.com/OptimusPi/jaml-ui/issues"
64
+ },
65
+ "keywords": [
66
+ "balatro",
67
+ "jaml",
68
+ "motely",
69
+ "seed",
70
+ "card",
71
+ "sprite",
72
+ "ui"
73
+ ],
74
+ "author": "pifreak",
75
+ "license": "MIT",
76
+ "peerDependencies": {
77
+ "@monaco-editor/react": ">=4.0.0",
78
+ "@react-spring/three": ">=9.0.0",
79
+ "@react-three/drei": ">=9.0.0",
80
+ "@react-three/fiber": ">=8.0.0",
81
+ "monaco-editor": ">=0.50.0",
82
+ "motely-wasm": "^14.3.1",
83
+ "react": "^18.2.0 || ^19.0.0",
84
+ "react-dom": "^18.2.0 || ^19.0.0",
85
+ "react-icons": ">=5.0.0",
86
+ "three": ">=0.150.0"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "@react-spring/three": {
90
+ "optional": true
91
+ },
92
+ "@react-three/fiber": {
93
+ "optional": true
94
+ },
95
+ "motely-wasm": {
96
+ "optional": true
97
+ },
98
+ "react-icons": {
99
+ "optional": true
100
+ },
101
+ "three": {
102
+ "optional": true
103
+ },
104
+ "@react-three/drei": {
105
+ "optional": true
106
+ }
107
+ },
108
+ "devDependencies": {
109
+ "@google/design.md": "^0.1.1",
110
+ "@monaco-editor/react": "^4.7.0",
111
+ "@react-spring/three": "^10.0.3",
112
+ "@react-three/fiber": "^9.6.0",
113
+ "@types/node": "^25.6.0",
114
+ "@types/react": "^19.2.14",
115
+ "@types/react-dom": "^19.2.3",
116
+ "@types/three": "^0.184.0",
117
+ "@vitejs/plugin-react": "^5.0.4",
118
+ "monaco-editor": "^0.55.1",
119
+ "motely-wasm": "^14.3.1",
120
+ "react": "^19.2.4",
121
+ "react-dom": "^19.2.4",
122
+ "react-icons": "^5.6.0",
123
+ "three": "^0.184.0",
124
+ "typescript": "^5.9.3",
125
+ "vite": "^8.0.9"
126
+ },
127
+ "dependencies": {
128
+ "@react-three/drei": ">=9.0.0"
129
+ }
130
+ }
@@ -1,11 +0,0 @@
1
- import React from "react";
2
- import type { SearchStatus } from "../hooks/useSearch.js";
3
- export interface JamlSpeedometerProps {
4
- seedsPerSecond: number;
5
- totalSearched: bigint;
6
- matchingSeeds: bigint;
7
- status: SearchStatus;
8
- className?: string;
9
- style?: React.CSSProperties;
10
- }
11
- export declare function JamlSpeedometer({ seedsPerSecond, totalSearched, matchingSeeds, status, className, style, }: JamlSpeedometerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,54 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { JimboColorOption } from "../ui/tokens.js";
4
- import { JimboText } from "../ui/jimboText.js";
5
- function formatCount(n) {
6
- if (n >= 1000000000n)
7
- return `${(Number(n / 1000000n) / 1000).toFixed(1)}B`;
8
- if (n >= 1000000n)
9
- return `${(Number(n / 1000n) / 1000).toFixed(1)}M`;
10
- if (n >= 1000n)
11
- return `${(Number(n) / 1000).toFixed(1)}K`;
12
- return n.toString();
13
- }
14
- function formatSpeed(sps) {
15
- if (sps >= 1_000_000)
16
- return `${(sps / 1_000_000).toFixed(1)}M`;
17
- if (sps >= 1_000)
18
- return `${(sps / 1_000).toFixed(0)}K`;
19
- return sps.toString();
20
- }
21
- function needleAngle(sps) {
22
- if (sps <= 0)
23
- return -90;
24
- const maxLog = Math.log10(5_000_000);
25
- const clamped = Math.min(sps, 5_000_000);
26
- const pct = Math.log10(Math.max(clamped, 1)) / maxLog;
27
- return -90 + pct * 180;
28
- }
29
- export function JamlSpeedometer({ seedsPerSecond, totalSearched, matchingSeeds, status, className, style, }) {
30
- const isActive = status === "running" || status === "booting";
31
- const angle = needleAngle(seedsPerSecond);
32
- const speedColor = seedsPerSecond >= 500_000
33
- ? JimboColorOption.GREEN
34
- : seedsPerSecond >= 100_000
35
- ? JimboColorOption.GOLD
36
- : seedsPerSecond > 0
37
- ? JimboColorOption.ORANGE
38
- : JimboColorOption.GREY;
39
- return (_jsxs("div", { className: className, style: {
40
- display: "flex",
41
- flexDirection: "column",
42
- alignItems: "center",
43
- gap: 6,
44
- padding: "12px 16px",
45
- borderRadius: 10,
46
- background: `${JimboColorOption.DARKEST}cc`,
47
- border: `1px solid ${JimboColorOption.PANEL_EDGE}`,
48
- ...style,
49
- }, children: [_jsx("div", { style: { position: "relative", width: 120, height: 68, overflow: "hidden" }, children: _jsxs("svg", { viewBox: "0 0 120 68", width: 120, height: 68, children: [_jsx("path", { d: "M 10 65 A 50 50 0 0 1 110 65", fill: "none", stroke: JimboColorOption.DARK_GREY, strokeWidth: 6, strokeLinecap: "round" }), isActive && (_jsx("path", { d: "M 10 65 A 50 50 0 0 1 110 65", fill: "none", stroke: speedColor, strokeWidth: 6, strokeLinecap: "round", strokeDasharray: "157", strokeDashoffset: 157 - (157 * ((angle + 90) / 180)), style: { transition: "stroke-dashoffset 300ms ease, stroke 300ms ease" } })), _jsx("line", { x1: 60, y1: 65, x2: 60, y2: 20, stroke: isActive ? JimboColorOption.RED : JimboColorOption.GREY, strokeWidth: 2, strokeLinecap: "round", style: {
50
- transformOrigin: "60px 65px",
51
- transform: `rotate(${angle}deg)`,
52
- transition: "transform 300ms ease",
53
- } }), _jsx("circle", { cx: 60, cy: 65, r: 4, fill: JimboColorOption.RED })] }) }), _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 20, fontFamily: "m6x11plus, monospace", color: isActive ? speedColor : JimboColorOption.GREY }, children: isActive ? formatSpeed(seedsPerSecond) : "---" }), _jsx(JimboText, { size: "xs", tone: "grey", children: "seeds / sec" })] }), _jsxs("div", { style: { display: "flex", gap: 16, marginTop: 2 }, children: [_jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 13, fontFamily: "m6x11plus, monospace", color: JimboColorOption.WHITE }, children: formatCount(totalSearched) }), _jsx(JimboText, { size: "xs", tone: "grey", children: "searched" })] }), _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 13, fontFamily: "m6x11plus, monospace", color: JimboColorOption.GREEN_TEXT }, children: formatCount(matchingSeeds) }), _jsx(JimboText, { size: "xs", tone: "grey", children: "matches" })] })] })] }));
54
- }
@@ -1,13 +0,0 @@
1
- /** Bit-packed shop/card ids (Balatro item encoding). */
2
- export declare const BalatroItemCategory: {
3
- readonly Standardcard: 1;
4
- readonly Spectral: 2;
5
- readonly Tarot: 3;
6
- readonly Planet: 4;
7
- readonly Joker: 5;
8
- readonly Invalid: 15;
9
- };
10
- export declare function packedItemCategory(packed: number): number;
11
- export declare function packedJokerRarity(packed: number): number;
12
- export declare function packedItemIndex(packed: number): number;
13
- export declare function isPackedItemValid(packed: number): boolean;
@@ -1,26 +0,0 @@
1
- /** Bit-packed shop/card ids (Balatro item encoding). */
2
- export const BalatroItemCategory = {
3
- Standardcard: 1,
4
- Spectral: 2,
5
- Tarot: 3,
6
- Planet: 4,
7
- Joker: 5,
8
- Invalid: 0xf,
9
- };
10
- const CATEGORY_OFFSET = 12;
11
- const CATEGORY_MASK = 0xf000;
12
- const RARITY_OFFSET = 10;
13
- const RARITY_MASK = 0x0c00;
14
- export function packedItemCategory(packed) {
15
- return (packed & CATEGORY_MASK) >> CATEGORY_OFFSET;
16
- }
17
- export function packedJokerRarity(packed) {
18
- return (packed & RARITY_MASK) >> RARITY_OFFSET;
19
- }
20
- export function packedItemIndex(packed) {
21
- return packed & ~(CATEGORY_MASK | RARITY_MASK);
22
- }
23
- export function isPackedItemValid(packed) {
24
- const category = packedItemCategory(packed);
25
- return category >= BalatroItemCategory.Standardcard && category <= BalatroItemCategory.Joker;
26
- }
@@ -1,7 +0,0 @@
1
- interface MotelyModules {
2
- MotelyWasm: any;
3
- MotelyWasmEvents: any;
4
- Motely: any;
5
- }
6
- export declare function loadMotelyWasm(url: string): Promise<MotelyModules>;
7
- export {};
@@ -1,16 +0,0 @@
1
- // Module-level cache so multiple hooks share a single boot per URL.
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- const cache = new Map();
4
- export function loadMotelyWasm(url) {
5
- if (!cache.has(url)) {
6
- cache.set(url, (async () => {
7
- const mod = await import(/* @vite-ignore */ url);
8
- await mod.default.boot();
9
- return { MotelyWasm: mod.MotelyWasm, MotelyWasmEvents: mod.MotelyWasmEvents, Motely: mod.Motely };
10
- })().catch((err) => {
11
- cache.delete(url);
12
- throw err;
13
- }));
14
- }
15
- return cache.get(url);
16
- }
@@ -1,11 +0,0 @@
1
- /** Map MotelyItemType enum names to display-friendly strings */
2
- export declare function getItemDisplayName(enumKey: string): string;
3
- export type CardCategory = "joker" | "tarot" | "planet" | "spectral" | "playing" | "unknown";
4
- export declare function getItemCategory(enumKey: string): CardCategory;
5
- export declare const CATEGORY_COLORS: Record<CardCategory, {
6
- bg: string;
7
- border: string;
8
- text: string;
9
- }>;
10
- /** Suit color for standard cards */
11
- export declare function getSuitColor(enumKey: string): string;