jaml-ui 0.14.3 → 0.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/DESIGN.md +197 -0
  2. package/package.json +5 -5
  3. package/assets/Balatro Seed Curator (DesignsV2)/.design-canvas.state.json +0 -1
  4. package/assets/Balatro Seed Curator (DesignsV2)/Assets/BlindChips.png +0 -0
  5. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/Boosters.json +0 -303
  6. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/boosters.png +0 -0
  7. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters.png +0 -0
  8. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/BlindChips.png +0 -0
  9. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/blinds_metadata.json +0 -51
  10. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/8BitDeck.png +0 -0
  11. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/Enhancers.png +0 -0
  12. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/balatro-stake-chips.png +0 -0
  13. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/enhancers_metadata.json +0 -52
  14. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/playing_cards_metadata.json +0 -249
  15. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/stakes.json +0 -19
  16. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Editions.png +0 -0
  17. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Enhancers.png +0 -0
  18. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Editions.png +0 -0
  19. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Jokers.png +0 -0
  20. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/jokers.json +0 -1087
  21. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers.png +0 -0
  22. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers_metadata.json +0 -25
  23. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers.png +0 -0
  24. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.json +0 -191
  25. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.png +0 -0
  26. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/Tarots.png +0 -0
  27. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/planets.json +0 -15
  28. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/spectrals.json +0 -21
  29. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/tarots.json +0 -163
  30. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots.png +0 -0
  31. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/Vouchers.png +0 -0
  32. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/vouchers.json +0 -130
  33. package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers.png +0 -0
  34. package/assets/Balatro Seed Curator (DesignsV2)/Assets/blinds.json +0 -51
  35. package/assets/Balatro Seed Curator (DesignsV2)/Assets/boosters.json +0 -303
  36. package/assets/Balatro Seed Curator (DesignsV2)/Assets/fonts/m6x11plusplus.otf +0 -0
  37. package/assets/Balatro Seed Curator (DesignsV2)/Assets/jokers.json +0 -1087
  38. package/assets/Balatro Seed Curator (DesignsV2)/Assets/planets.json +0 -15
  39. package/assets/Balatro Seed Curator (DesignsV2)/Assets/spectrals.json +0 -21
  40. package/assets/Balatro Seed Curator (DesignsV2)/Assets/stakes.png +0 -0
  41. package/assets/Balatro Seed Curator (DesignsV2)/Assets/stickers.png +0 -0
  42. package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.json +0 -191
  43. package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.png +0 -0
  44. package/assets/Balatro Seed Curator (DesignsV2)/Assets/tarots.json +0 -163
  45. package/assets/Balatro Seed Curator (DesignsV2)/Assets/vouchers.json +0 -130
  46. package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail v2.html +0 -40
  47. package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail.html +0 -34
  48. package/assets/Balatro Seed Curator (DesignsV2)/public/fonts/m6x11plusplus.otf +0 -0
  49. package/assets/Balatro Seed Curator (DesignsV2)/src/AntePage.jsx +0 -228
  50. package/assets/Balatro Seed Curator (DesignsV2)/src/SeedDetail.jsx +0 -222
  51. package/assets/Balatro Seed Curator (DesignsV2)/src/app.jsx +0 -35
  52. package/assets/Balatro Seed Curator (DesignsV2)/src/mockData.js +0 -185
  53. package/assets/Balatro Seed Curator (DesignsV2)/src/sprites.jsx +0 -259
  54. package/assets/Balatro Seed Curator (DesignsV2)/src/tokens.js +0 -49
  55. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/AntePageV2.jsx +0 -290
  56. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/BalButton.jsx +0 -107
  57. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlBuilderV2.jsx +0 -594
  58. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlIde.jsx +0 -302
  59. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SearchResultsV2.jsx +0 -286
  60. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedDetailV2.jsx +0 -336
  61. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedOGCard.jsx +0 -251
  62. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/Showcase.jsx +0 -131
  63. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/app.jsx +0 -55
  64. package/assets/Balatro Seed Curator (DesignsV2)/src/v2/data.js +0 -296
  65. package/assets/Balatro Seed Curator (DesignsV2)/starters/design-canvas.jsx +0 -622
  66. package/assets/Balatro Seed Curator (DesignsV2)/uploads/8BitDeck.png +0 -0
  67. package/assets/Balatro Seed Curator (DesignsV2)/uploads/BlindChips.png +0 -0
  68. package/assets/Balatro Seed Curator (DesignsV2)/uploads/Boosters.png +0 -0
  69. package/assets/Balatro Seed Curator (DesignsV2)/uploads/Editions.png +0 -0
  70. package/assets/Balatro Seed Curator (DesignsV2)/uploads/Enhancers.png +0 -0
  71. package/assets/Balatro Seed Curator (DesignsV2)/uploads/Jokers.png +0 -0
  72. package/assets/Balatro Seed Curator (DesignsV2)/uploads/Tarots.png +0 -0
  73. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749540653-0.png +0 -0
  74. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749644934-0.png +0 -0
  75. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749661871-0.png +0 -0
  76. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749674748-0.png +0 -0
  77. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749703076-0.png +0 -0
  78. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749882759-0.png +0 -0
  79. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750354200-0.png +0 -0
  80. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750733265-0.png +0 -0
  81. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776751928925-0.png +0 -0
  82. package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776800975060-0.png +0 -0
  83. package/assets/Balatro Seed Curator (DesignsV2)/uploads/stickers.png +0 -0
  84. package/assets/Balatro Seed Curator (DesignsV2)/uploads/tags.png +0 -0
@@ -1,131 +0,0 @@
1
- // Showcase — mobile landing screen.
2
- // Big "Jammy Seed Finder" title, sprite marquee of recent winning seeds,
3
- // row of 3 big primary actions (Browse filters / New filter / Recent searches),
4
- // community recent results strip, Back docked bottom.
5
-
6
- const { useState: shUS } = React;
7
- const Csh = window.JimboColor;
8
-
9
- function Showcase() {
10
- const hotFilters = [
11
- { name: 'Perkeo Observatory', author: 'Athuny & pifreak', hits: '12.4M', tone: 'blue', sample: ['perkeo','blueprint','brainstorm'] },
12
- { name: 'The Daily Wee', author: 'pifreak', hits: '3.1M', tone: 'red', sample: ['weejoker','hangingchad','hack'] },
13
- { name: 'Lucky Cat', author: 'JamlGenie', hits: '128K', tone: 'gold', sample: ['luckycat','egg'] },
14
- { name: 'Fool Emperor Chain', author: 'ope', hits: '42K', tone: 'green', sample: ['showman'] },
15
- ];
16
-
17
- return (
18
- <div style={{
19
- width: '100%', height: '100%', background: Csh.DARKEST,
20
- display: 'flex', flexDirection: 'column',
21
- fontFamily: 'm6x11plus, monospace', color: Csh.WHITE, overflow: 'hidden',
22
- }}>
23
- {/* Scroll body */}
24
- <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '18px 14px 10px' }}>
25
- {/* Wordmark */}
26
- <div style={{ textAlign: 'center', marginBottom: 18 }}>
27
- <div style={{
28
- fontSize: 32, letterSpacing: 3, lineHeight: 1,
29
- color: Csh.GOLD, textShadow: '2px 2px 0 rgba(0,0,0,.8)',
30
- }}>Balatro</div>
31
- <div style={{
32
- fontSize: 14, letterSpacing: 4, color: Csh.GREY, marginTop: 4,
33
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
34
- }}>SEED · CURATOR</div>
35
- </div>
36
-
37
- {/* Live stats card */}
38
- <div style={{
39
- background: Csh.DARK_GREY, borderRadius: 6, padding: 10,
40
- border: `2px solid ${Csh.PANEL_EDGE}`, boxShadow: `0 2px 0 ${Csh.BLACK}`,
41
- display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, textAlign: 'center', marginBottom: 16,
42
- }}>
43
- {[['15.6B','searched'],['2,847','matches'],['5.4M/s','speed']].map(([n,l]) => (
44
- <div key={l}>
45
- <div style={{ fontSize: 16, color: Csh.GOLD, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }}>{n}</div>
46
- <div style={{ fontSize: 9, color: Csh.GREY, letterSpacing: 2, marginTop: 2 }}>{l.toUpperCase()}</div>
47
- </div>
48
- ))}
49
- </div>
50
-
51
- {/* Section header */}
52
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
53
- <div style={{
54
- fontSize: 11, letterSpacing: 2, padding: '2px 8px',
55
- background: Csh.BLUE, color: Csh.WHITE, borderRadius: 3,
56
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
57
- }}>HOT FILTERS</div>
58
- <div style={{ flex: 1, height: 2, background: `${Csh.BLUE}55`, borderRadius: 1 }} />
59
- </div>
60
-
61
- {/* Filter cards */}
62
- <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 16 }}>
63
- {hotFilters.map((f, i) => {
64
- const tColor = f.tone === 'blue' ? Csh.BLUE : f.tone === 'red' ? Csh.RED : f.tone === 'gold' ? Csh.GOLD : Csh.GREEN;
65
- return (
66
- <div key={i} style={{
67
- background: Csh.DARK_GREY, borderRadius: 6, padding: 10,
68
- border: `2px solid ${tColor}`, boxShadow: `0 2px 0 ${Csh.BLACK}`,
69
- display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer',
70
- }}>
71
- <div style={{ display: 'flex', gap: 2 }}>
72
- {f.sample.map((n, j) => (
73
- <div key={j} style={{ width: 30, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
74
- <JokerMini name={n} size={28} />
75
- </div>
76
- ))}
77
- </div>
78
- <div style={{ flex: 1, minWidth: 0 }}>
79
- <div style={{
80
- fontSize: 13, color: Csh.WHITE, letterSpacing: 1,
81
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
82
- overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
83
- }}>{f.name}</div>
84
- <div style={{ fontSize: 9, color: Csh.GOLD_TEXT, letterSpacing: 1, marginTop: 2 }}>by {f.author}</div>
85
- </div>
86
- <div style={{ textAlign: 'right' }}>
87
- <div style={{ fontSize: 14, color: tColor, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }}>{f.hits}</div>
88
- <div style={{ fontSize: 8, color: Csh.GREY, letterSpacing: 1 }}>seeds</div>
89
- </div>
90
- </div>
91
- );
92
- })}
93
- </div>
94
-
95
- {/* Section — recent community finds */}
96
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
97
- <div style={{
98
- fontSize: 11, letterSpacing: 2, padding: '2px 8px',
99
- background: Csh.GREEN, color: Csh.WHITE, borderRadius: 3,
100
- textShadow: '1px 1px 0 rgba(0,0,0,.8)',
101
- }}>RECENT FINDS</div>
102
- <div style={{ flex: 1, height: 2, background: `${Csh.GREEN}55`, borderRadius: 1 }} />
103
- </div>
104
- <div style={{
105
- background: Csh.DARK_GREY, borderRadius: 6, padding: '8px 10px',
106
- border: `2px solid ${Csh.PANEL_EDGE}`, boxShadow: `0 2px 0 ${Csh.BLACK}`,
107
- fontSize: 11, color: Csh.GREY, letterSpacing: 1, lineHeight: 1.7,
108
- }}>
109
- <div><span style={{ color: Csh.GOLD_TEXT }}>X1B8TW4J</span> · Perkeo Observatory · +8</div>
110
- <div><span style={{ color: Csh.GOLD_TEXT }}>ALEPH999</span> · Lucky Cat · +5</div>
111
- <div><span style={{ color: Csh.GOLD_TEXT }}>UFX3G111</span> · Lucky Cat · +4</div>
112
- <div><span style={{ color: Csh.GOLD_TEXT }}>BETAZ3RO</span> · Daily Wee · +3</div>
113
- </div>
114
-
115
- <div style={{ height: 16 }} />
116
- </div>
117
-
118
- {/* Bottom thumb-zone actions */}
119
- <div style={{
120
- padding: '8px 10px 10px', borderTop: `2px solid ${Csh.BLACK}`, background: Csh.DARK_GREY,
121
- display: 'flex', flexDirection: 'column', gap: 6,
122
- }}>
123
- <window.BalButton tone="green" fullWidth size="md">New Search</window.BalButton>
124
- <window.BalButton tone="blue" fullWidth size="md">Browse Filters</window.BalButton>
125
- <window.BalButton tone="orange" fullWidth size="md">Back</window.BalButton>
126
- </div>
127
- </div>
128
- );
129
- }
130
-
131
- window.Showcase = Showcase;
@@ -1,55 +0,0 @@
1
- // v2 App shell — loads sprite maps then mounts SeedDetailV2 in a design canvas.
2
-
3
- function AppV2() {
4
- const [ready, setReady] = React.useState(false);
5
- React.useEffect(() => {
6
- (async () => {
7
- await Promise.all([
8
- window.loadSpriteMap('jokers', 'assets/jokers.json'),
9
- window.loadSpriteMap('tarots', 'assets/tarots.json'),
10
- window.loadSpriteMap('vouchers', 'assets/vouchers.json'),
11
- window.loadSpriteMap('tags', 'assets/tags.json'),
12
- window.loadSpriteMap('boosters', 'assets/boosters.json'),
13
- window.loadSpriteMap('blinds', 'assets/blinds.json'),
14
- ]);
15
- setReady(true);
16
- })();
17
- }, []);
18
-
19
- if (!ready) return <div style={{ padding: 40, color: '#fff', fontFamily: 'm6x11plus, monospace' }}>Loading sprites…</div>;
20
-
21
- return (
22
- <DesignCanvas>
23
- <DCSection id="v2" title="Seed Detail v2" subtitle="Flat ante model · per-clause score columns · COD-style edge hit indicators">
24
- <DCArtboard id="v2-main" label="Expanded seed (detail)" width={390} height={844}>
25
- <SeedDetailV2 seeds={window.SEEDS_V2} filter={window.FILTER_V2} />
26
- </DCArtboard>
27
- <DCArtboard id="v2-results" label="Search results · tap row to expand" width={390} height={844}>
28
- <SearchResultsV2 filter={window.FILTER_V2} />
29
- </DCArtboard>
30
- </DCSection>
31
- <DCSection id="v2-og" title="OG Card · /seed/[seed]/og.png" subtitle="1200×630 agnostic rollup — Discord unfurl, share link, embed">
32
- <DCArtboard id="v2-og-card" label="OG · winning seed" width={1200} height={630}>
33
- <SeedOGCard seed={window.SEEDS_V2[0]} filter={window.FILTER_V2} />
34
- </DCArtboard>
35
- </DCSection>
36
- <DCSection id="v2-builder" title="JAML Builder · mobile" subtitle="Mystery-slot grid + cascade picker bottom sheet · tap a '?' to open picker · tap a card to edit · X to remove">
37
- <DCArtboard id="v2-builder-main" label="Builder · default state" width={390} height={844}>
38
- <JamlBuilderV2 />
39
- </DCArtboard>
40
- </DCSection>
41
- <DCSection id="v2-showcase" title="Showcase · home/landing" subtitle="Mobile landing · live stats · hot filters · recent community finds">
42
- <DCArtboard id="v2-showcase-main" label="Showcase" width={390} height={844}>
43
- <Showcase />
44
- </DCArtboard>
45
- </DCSection>
46
- <DCSection id="v2-ide" title="JAML IDE · visual editor" subtitle="Drag clauses between MUST / SHOULD / MUST NOT · live YAML tab · what the community needs">
47
- <DCArtboard id="v2-ide-visual" label="IDE · Visual tab" width={390} height={844}>
48
- <JamlIde />
49
- </DCArtboard>
50
- </DCSection>
51
- </DesignCanvas>
52
- );
53
- }
54
-
55
- ReactDOM.createRoot(document.getElementById('root')).render(<AppV2 />);
@@ -1,296 +0,0 @@
1
- // v2 data — flat ante model (matches BSO's AnteAnalysisModel).
2
- // Each ante: boss, voucher, smallBlindTag, bigBlindTag, shopQueue[], boosterPacks[].
3
- // Each item carries hits: [{clauseId, score}] computed against the filter.
4
- // (We pre-compute hits here for the mock; in the real app, a matcher does it.)
5
-
6
- // ── Helpers (must be defined before anything else) ────
7
- function normalizeStr(s) {
8
- return String(s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
9
- }
10
- const RARITY = {
11
- blueprint: 'Legendary', brainstorm: 'Rare', baseball: 'Rare', perkeo: 'Legendary',
12
- yorick: 'Legendary', chicot: 'Legendary', triboulet: 'Legendary', canio: 'Legendary',
13
- hangingchad: 'Common', photograph: 'Common', scholar: 'Common', supernova: 'Common',
14
- showman: 'Uncommon', ceremonialdagger: 'Uncommon', fibonacci: 'Uncommon',
15
- bull: 'Uncommon', banner: 'Common', steeljoker: 'Uncommon',
16
- hologram: 'Rare', invisible: 'Rare', campfire: 'Rare', bootstraps: 'Rare',
17
- stuntdouble: 'Rare', obelisk: 'Rare', throwback: 'Rare', matador: 'Rare',
18
- };
19
- function jokerRarity(name) { return RARITY[normalizeStr(name)] || 'Common'; }
20
- window.jokerRarity = jokerRarity;
21
- window.normalizeStr = normalizeStr;
22
-
23
- // ── Filter: "Blueprint in shop slots 2-3 of Ante 2, +2 points" etc.
24
- window.FILTER_V2 = {
25
- name: 'SkipTagAnte2BlueprintAnte2',
26
- author: 'pifreak',
27
- description: 'Negative Skip Tag Ante 2 lands on a Blueprint',
28
- must: [
29
- { id: 'm1', type: 'smallblindtag', value: 'negativetag', antes: [2], label: 'Negative Tag' },
30
- { id: 'm2', type: 'souljoker', value: 'perkeo', antes: [3,4,5], label: 'Perkeo' },
31
- { id: 'm3', type: 'joker', value: 'showman', antes: [2], sources: { shopSlots: [0,1], packSlots: [0,1] }, label: 'Showman' },
32
- ],
33
- should: [
34
- { id: 's1', type: 'joker', value: 'hangingchad', antes: [2], sources: { shopSlots: [2,3] }, score: 1, label: 'Hanging Chad' },
35
- { id: 's2', type: 'joker', value: 'photograph', antes: [2], sources: { shopSlots: [2,3] }, score: 1, label: 'Photograph' },
36
- { id: 's3', type: 'joker', value: 'blueprint', antes: [2], sources: { shopSlots: [2,3] }, score: 2, label: 'Blueprint' },
37
- { id: 's4', type: 'joker', value: 'brainstorm', antes: [2], sources: { shopSlots: [2,3] }, score: 2, label: 'Brainstorm' },
38
- // Wildcard: "any Rare Joker in ante 3 shop" — the analyzer picks the specific
39
- // joker from each seed to satisfy this.
40
- { id: 's5', type: 'joker', value: 'any', rarity: 'Rare', antes: [3], sources: { shopSlots: [0,1,2,3,4,5] }, score: 1, label: 'Any Rare' },
41
- ],
42
- deck: 'Red',
43
- stake: 'White',
44
- };
45
-
46
- // ── Helpers to build a shop item in mock data ────
47
- function s(value, type = 'joker', { edition = null, slot = null } = {}) {
48
- return { type, value, edition, _slot: slot };
49
- }
50
-
51
- // ── Seed 1: a strong match (hits Negative Tag A2, Blueprint A2 shop#2, Perkeo A3)
52
- const seed1 = {
53
- seed: 'X1B8TW4J',
54
- deck: 'Red',
55
- stake: 'White',
56
- antes: [
57
- {
58
- ante: 1,
59
- boss: 'TheArm',
60
- voucher: 'Overstock',
61
- smallBlindTag: 'UncommonTag',
62
- bigBlindTag: 'RareTag',
63
- shopQueue: [
64
- s('joker', 'joker'), s('greedyjoker'), s('lustyjoker'),
65
- s('jollyjoker'), s('halfjoker'), s('evensteven'),
66
- s('scholar'), s('supernova'), s('banner'),
67
- ],
68
- boosterPacks: [
69
- { type: 'arcanapack', items: ['themagician', 'thehighpriestess', 'theemperor'] },
70
- { type: 'buffoonpack', items: ['fibonacci', 'cartomancer'] },
71
- ],
72
- },
73
- {
74
- ante: 2,
75
- boss: 'TheHouse',
76
- voucher: 'ClearanceSale',
77
- smallBlindTag: 'NegativeTag', // ← hits m1
78
- bigBlindTag: 'FoilTag',
79
- shopQueue: [
80
- s('showman', 'joker'), // slot 0 → hits m3
81
- s('bull'), // slot 1
82
- s('blueprint', 'joker', { edition: 'Foil' }), // slot 2 → hits s3 (×2)
83
- s('hangingchad'), // slot 3 → hits s1
84
- s('scaryface'), // slot 4
85
- s('abstractjoker'), // slot 5
86
- s('delayedgratification'),
87
- s('hack'),
88
- ],
89
- boosterPacks: [
90
- { type: 'arcanapack', items: ['thetower', 'strength', 'justice'] },
91
- { type: 'buffoonpack', items: ['brainstorm', 'ceremonialdagger'] },
92
- ],
93
- },
94
- {
95
- ante: 3,
96
- boss: 'TheClub',
97
- voucher: null,
98
- smallBlindTag: 'HolographicTag',
99
- bigBlindTag: 'PolychromeTag',
100
- shopQueue: [
101
- s('hologram'), s('zanyjoker'), s('madjoker'),
102
- s('crazyjoker'), s('drolljoker'), s('slyjoker'),
103
- s('wilyjoker'), s('cleverjoker'),
104
- ],
105
- boosterPacks: [
106
- { type: 'spectralpack', items: ['thesoul'] }, // spawns SoulJoker
107
- { type: 'celestialpack', items: ['mercury', 'venus'] },
108
- ],
109
- soulJoker: { value: 'perkeo', edition: 'Negative' }, // ← hits m2
110
- },
111
- {
112
- ante: 4,
113
- boss: 'TheFish',
114
- voucher: 'Telescope',
115
- smallBlindTag: 'StandardTag',
116
- bigBlindTag: 'CharmTag',
117
- shopQueue: [
118
- s('gros michel'), s('fibonacci'), s('steeljoker'),
119
- s('scaryface'), s('abstractjoker'), s('delayedgratification'),
120
- ],
121
- boosterPacks: [
122
- { type: 'arcanapack', items: ['death', 'temperance', 'thedevil'] },
123
- ],
124
- },
125
- {
126
- ante: 5,
127
- boss: 'ThePsychic',
128
- voucher: 'Grabber',
129
- smallBlindTag: 'TopUpTag',
130
- bigBlindTag: 'MeteorTag',
131
- shopQueue: [
132
- s('raisedfist'), s('chaos'), s('fibonacci'),
133
- s('steeljoker'), s('scaryface'), s('abstractjoker'),
134
- ],
135
- boosterPacks: [
136
- { type: 'megaarcanapack', items: ['justice','death','temperance','thedevil','thetower'] },
137
- ],
138
- },
139
- {
140
- ante: 6,
141
- boss: 'TheWater',
142
- voucher: 'Hone',
143
- smallBlindTag: 'CouponTag',
144
- bigBlindTag: 'BossTag',
145
- shopQueue: [
146
- s('joker', 'joker'), s('greedyjoker'), s('lustyjoker'),
147
- s('wrathfuljoker'), s('gluttonousjoker'), s('jollyjoker'),
148
- ],
149
- boosterPacks: [
150
- { type: 'standardpack', items: ['kh', 'qd', '10s'] },
151
- ],
152
- },
153
- {
154
- ante: 7,
155
- boss: 'TheWheel',
156
- voucher: null,
157
- smallBlindTag: 'OrbitalTag',
158
- bigBlindTag: 'EtherealTag',
159
- shopQueue: [
160
- s('ceremonial'), s('banner'), s('mysticsummit'),
161
- s('marblejoker'), s('loyaltycard'), s('eightball'),
162
- ],
163
- boosterPacks: [
164
- { type: 'jumbobuffoonpack', items: ['blueprint','cartomancer','astronomer'] },
165
- ],
166
- },
167
- {
168
- ante: 8,
169
- boss: 'ThePillar',
170
- voucher: null,
171
- smallBlindTag: 'DoubleTag',
172
- bigBlindTag: 'JuggleTag',
173
- shopQueue: [
174
- s('blueprint', 'joker'), s('brainstorm'),
175
- s('blueprint', 'joker', { edition: 'Polychrome' }),
176
- s('blueprint', 'joker'), s('scholar'), s('supernova'),
177
- ],
178
- boosterPacks: [
179
- { type: 'buffoonpack', items: ['blueprint','blueprint'] },
180
- ],
181
- },
182
- ],
183
- };
184
-
185
- // ── Compute hits: walk every ante's items and match against filter clauses.
186
- // Wildcard match: clause.value === 'any' means "any joker matching filters"
187
- // (e.g. rarity). The specific matching joker is recorded in `matches[clauseId]`
188
- // so the UI can render it in wildcard columns.
189
- function computeHits(seed, filter) {
190
- const clauses = [...filter.must.map(c => ({...c, _kind:'must'})), ...filter.should.map(c => ({...c, _kind:'should'}))];
191
- const totals = {}; clauses.forEach(c => totals[c.id] = 0);
192
- const matches = {}; clauses.forEach(c => matches[c.id] = []); // [{value, ante, where}]
193
- let totalScore = 0;
194
-
195
- const wildcardOK = (c, itemName) => {
196
- if (c.value !== 'any') return normalizeStr(c.value) === normalizeStr(itemName);
197
- if (c.rarity && jokerRarity(itemName) !== c.rarity) return false;
198
- return true;
199
- };
200
-
201
- for (const ante of seed.antes) {
202
- ante.shopQueue.forEach((item, slot) => {
203
- item._slot = slot;
204
- item.hits = [];
205
- for (const c of clauses) {
206
- if (!c.antes.includes(ante.ante)) continue;
207
- if (c.type === 'joker' && item.type === 'joker' && wildcardOK(c, item.value)) {
208
- if (c.sources?.shopSlots && !c.sources.shopSlots.includes(slot)) continue;
209
- if (c.edition && item.edition !== c.edition) continue;
210
- item.hits.push({ id: c.id, kind: c._kind, score: c.score || 0 });
211
- totals[c.id]++;
212
- matches[c.id].push({ value: item.value, edition: item.edition, ante: ante.ante, where: `Shop #${slot+1}` });
213
- totalScore += c.score || 0;
214
- }
215
- }
216
- });
217
- ante.boosterPacks.forEach((pack, pidx) => {
218
- pack.hits = [];
219
- pack.itemHits = pack.items.map(() => []);
220
- pack.items.forEach((itemName, islot) => {
221
- for (const c of clauses) {
222
- if (!c.antes.includes(ante.ante)) continue;
223
- if (c.type === 'joker' && wildcardOK(c, itemName)) {
224
- if (c.sources?.packSlots && !c.sources.packSlots.includes(pidx)) continue;
225
- pack.itemHits[islot].push({ id: c.id, kind: c._kind, score: c.score || 0 });
226
- totals[c.id]++;
227
- matches[c.id].push({ value: itemName, ante: ante.ante, where: `Pack ${pidx+1}` });
228
- totalScore += c.score || 0;
229
- }
230
- }
231
- });
232
- if (pack.itemHits.some(h => h.length)) pack.hits = ['match'];
233
- });
234
- if (ante.voucher) {
235
- for (const c of clauses) {
236
- if (c.type === 'voucher' && c.antes.includes(ante.ante) && normalizeStr(c.value) === normalizeStr(ante.voucher)) {
237
- ante._voucherHits = (ante._voucherHits || []).concat({ id: c.id, kind: c._kind, score: c.score || 0 });
238
- totals[c.id]++;
239
- matches[c.id].push({ value: ante.voucher, ante: ante.ante, where: 'Voucher' });
240
- }
241
- }
242
- }
243
- ante._smallTagHits = [];
244
- ante._bigTagHits = [];
245
- for (const c of clauses) {
246
- if (!c.antes.includes(ante.ante)) continue;
247
- if (c.type === 'smallblindtag' && normalizeStr(c.value) === normalizeStr(ante.smallBlindTag)) {
248
- ante._smallTagHits.push({id:c.id,kind:c._kind}); totals[c.id]++;
249
- matches[c.id].push({ value: ante.smallBlindTag, ante: ante.ante, where: 'Small Tag' });
250
- }
251
- if (c.type === 'bigblindtag' && normalizeStr(c.value) === normalizeStr(ante.bigBlindTag)) {
252
- ante._bigTagHits.push({id:c.id,kind:c._kind}); totals[c.id]++;
253
- matches[c.id].push({ value: ante.bigBlindTag, ante: ante.ante, where: 'Big Tag' });
254
- }
255
- }
256
- if (ante.soulJoker) {
257
- ante._soulHits = [];
258
- for (const c of clauses) {
259
- if (c.type === 'souljoker' && c.antes.includes(ante.ante) && (c.value === 'any' || normalizeStr(c.value) === normalizeStr(ante.soulJoker.value))) {
260
- if (c.edition && ante.soulJoker.edition !== c.edition) continue;
261
- ante._soulHits.push({id:c.id,kind:c._kind});
262
- totals[c.id]++;
263
- matches[c.id].push({ value: ante.soulJoker.value, edition: ante.soulJoker.edition, ante: ante.ante, where: 'Soul' });
264
- }
265
- }
266
- }
267
- if (ante.boss) {
268
- ante._bossHits = [];
269
- for (const c of clauses) {
270
- if (c.type === 'boss' && c.antes.includes(ante.ante) && normalizeStr(c.value) === normalizeStr(ante.boss)) {
271
- ante._bossHits.push({id:c.id,kind:c._kind});
272
- totals[c.id]++;
273
- matches[c.id].push({ value: ante.boss, ante: ante.ante, where: 'Boss' });
274
- }
275
- }
276
- }
277
- }
278
-
279
- return { totals, matches, totalScore };
280
- }
281
-
282
- // Minimal rarity lookup for a handful of jokers used in the mock data.
283
- const _RARITY_DUP = null; // rarity was hoisted above computeHits
284
-
285
- const summary1 = computeHits(seed1, window.FILTER_V2);
286
- seed1.score = summary1;
287
-
288
- // two lean siblings for swipe nav
289
- const seed2 = JSON.parse(JSON.stringify(seed1)); seed2.seed = 'ALEPH999';
290
- seed2.score = computeHits(seed2, window.FILTER_V2);
291
- const seed3 = JSON.parse(JSON.stringify(seed1)); seed3.seed = 'BETAZ3RO';
292
- seed3.score = computeHits(seed3, window.FILTER_V2);
293
-
294
- window.SEEDS_V2 = [seed1, seed2, seed3];
295
- window.computeHits = computeHits;
296
- window.normalizeStr = normalizeStr;