jaml-ui 0.14.2 → 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.
- package/DESIGN.md +197 -0
- package/dist/components/AnalyzerExplorer.js +0 -11
- package/dist/components/JamlAestheticSelector.js +1 -3
- package/dist/components/JamlAnalyzerFullscreen.js +3 -3
- package/dist/components/JamlIde.js +0 -2
- package/dist/components/JamlSeedInput.js +1 -2
- package/dist/components/JamlSpeedometer.js +1 -1
- package/dist/ui/codeBlock.js +1 -1
- package/dist/ui/jimboCopyRow.js +1 -2
- package/dist/ui/jimboFilterBar.js +2 -4
- package/dist/ui/showcase.js +4 -4
- package/package.json +8 -5
- package/assets/Balatro Seed Curator (DesignsV2)/.design-canvas.state.json +0 -1
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/Boosters.json +0 -303
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters/boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Bosses/blinds_metadata.json +0 -51
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/8BitDeck.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/balatro-stake-chips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/enhancers_metadata.json +0 -52
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/playing_cards_metadata.json +0 -249
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/stakes.json +0 -19
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/jokers.json +0 -1087
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers/stickers_metadata.json +0 -25
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.json +0 -191
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tags/tags.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/planets.json +0 -15
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/spectrals.json +0 -21
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots/tarots.json +0 -163
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/Vouchers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers/vouchers.json +0 -130
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/Vouchers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/blinds.json +0 -51
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/boosters.json +0 -303
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/fonts/m6x11plusplus.otf +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/jokers.json +0 -1087
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/planets.json +0 -15
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/spectrals.json +0 -21
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/stakes.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.json +0 -191
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tags.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/tarots.json +0 -163
- package/assets/Balatro Seed Curator (DesignsV2)/Assets/vouchers.json +0 -130
- package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail v2.html +0 -40
- package/assets/Balatro Seed Curator (DesignsV2)/Seed Detail.html +0 -34
- package/assets/Balatro Seed Curator (DesignsV2)/public/fonts/m6x11plusplus.otf +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/src/AntePage.jsx +0 -228
- package/assets/Balatro Seed Curator (DesignsV2)/src/SeedDetail.jsx +0 -222
- package/assets/Balatro Seed Curator (DesignsV2)/src/app.jsx +0 -35
- package/assets/Balatro Seed Curator (DesignsV2)/src/mockData.js +0 -185
- package/assets/Balatro Seed Curator (DesignsV2)/src/sprites.jsx +0 -259
- package/assets/Balatro Seed Curator (DesignsV2)/src/tokens.js +0 -49
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/AntePageV2.jsx +0 -290
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/BalButton.jsx +0 -107
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlBuilderV2.jsx +0 -594
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/JamlIde.jsx +0 -302
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SearchResultsV2.jsx +0 -286
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedDetailV2.jsx +0 -336
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/SeedOGCard.jsx +0 -251
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/Showcase.jsx +0 -131
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/app.jsx +0 -55
- package/assets/Balatro Seed Curator (DesignsV2)/src/v2/data.js +0 -296
- package/assets/Balatro Seed Curator (DesignsV2)/starters/design-canvas.jsx +0 -622
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/8BitDeck.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/BlindChips.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Boosters.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Editions.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Enhancers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Jokers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/Tarots.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749540653-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749644934-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749661871-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749674748-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749703076-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776749882759-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750354200-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776750733265-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776751928925-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/pasted-1776800975060-0.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/stickers.png +0 -0
- package/assets/Balatro Seed Curator (DesignsV2)/uploads/tags.png +0 -0
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
// JamlIde — mobile visual editor for .jaml files.
|
|
2
|
-
//
|
|
3
|
-
// What the community needs: a side-by-side where the LEFT is a drag-droppable
|
|
4
|
-
// clause canvas (zones: must/should/mustnot) and the RIGHT shows the live .jaml
|
|
5
|
-
// text. Editing either side updates the other. Drag a clause between zones to
|
|
6
|
-
// change its kind. Long-press to reorder within a zone.
|
|
7
|
-
//
|
|
8
|
-
// Since this is a mobile artboard (390×844), side-by-side becomes TOP/BOTTOM
|
|
9
|
-
// tabs: [Visual] / [YAML] toggle at the very top. We show the Visual tab.
|
|
10
|
-
//
|
|
11
|
-
// Drag & drop: each clause card has a grip handle on the left; touchstart +
|
|
12
|
-
// touchmove translates the card; on touchend we hit-test zone rails and move
|
|
13
|
-
// the clause between them.
|
|
14
|
-
|
|
15
|
-
const { useState: idUS, useRef: idUR, useEffect: idUE } = React;
|
|
16
|
-
const Cid = window.JimboColor;
|
|
17
|
-
|
|
18
|
-
const ZONE_META = {
|
|
19
|
-
must: { label: 'MUST', color: Cid.BLUE },
|
|
20
|
-
should: { label: 'SHOULD', color: Cid.RED },
|
|
21
|
-
mustnot: { label: 'MUST NOT', color: Cid.ORANGE },
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// Render a clause sprite (reused mini from builder).
|
|
25
|
-
function ideSprite(c, s = 32) {
|
|
26
|
-
if (c.type === 'joker' || c.type === 'souljoker') return <JokerMini name={c.value} size={s} />;
|
|
27
|
-
if (c.type === 'voucher') return <VoucherMini name={c.value} size={s} />;
|
|
28
|
-
if (c.type === 'smallblindtag' || c.type === 'bigblindtag') return <TagChip name={c.value} size={s} />;
|
|
29
|
-
if (c.type === 'boss') return <BossChip name={c.value} size={s} />;
|
|
30
|
-
if (c.type === 'tarot') return <TarotMini name={c.value} size={s} />;
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// serialize filter → YAML-ish text (no external lib; good enough for preview)
|
|
35
|
-
function toJamlText(f) {
|
|
36
|
-
const lines = [];
|
|
37
|
-
lines.push(`name: ${f.name || 'Untitled'}`);
|
|
38
|
-
if (f.author) lines.push(`author: ${f.author}`);
|
|
39
|
-
if (f.description) lines.push(`description: ${f.description}`);
|
|
40
|
-
if (f.deck) lines.push(`deck: ${f.deck}`);
|
|
41
|
-
if (f.stake) lines.push(`stake: ${f.stake}`);
|
|
42
|
-
const blocks = { must: f.must, should: f.should, mustnot: f.mustnot };
|
|
43
|
-
for (const [k, arr] of Object.entries(blocks)) {
|
|
44
|
-
if (!arr || !arr.length) continue;
|
|
45
|
-
lines.push('');
|
|
46
|
-
lines.push(`${k}:`);
|
|
47
|
-
for (const c of arr) {
|
|
48
|
-
const key = c.type === 'souljoker' ? 'soulJoker' :
|
|
49
|
-
c.type === 'smallblindtag' ? 'smallBlindTag' :
|
|
50
|
-
c.type === 'bigblindtag' ? 'bigBlindTag' :
|
|
51
|
-
c.type;
|
|
52
|
-
lines.push(` - ${key}: ${c.label || c.value}`);
|
|
53
|
-
if (c.antes && c.antes.length) lines.push(` antes: [${c.antes.join(',')}]`);
|
|
54
|
-
if (c.score) lines.push(` score: ${c.score}`);
|
|
55
|
-
if (c.edition) lines.push(` edition: ${c.edition}`);
|
|
56
|
-
if (c.sources) {
|
|
57
|
-
lines.push(` sources:`);
|
|
58
|
-
for (const [sk, sv] of Object.entries(c.sources)) {
|
|
59
|
-
lines.push(` ${sk}: [${Array.isArray(sv) ? sv.join(',') : sv}]`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return lines.join('\n');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Syntax-ish-highlighted YAML block.
|
|
68
|
-
function YamlText({ text }) {
|
|
69
|
-
const lines = text.split('\n');
|
|
70
|
-
return (
|
|
71
|
-
<pre style={{
|
|
72
|
-
margin: 0, padding: 10, background: '#0f1719',
|
|
73
|
-
fontFamily: 'JetBrains Mono, ui-monospace, monospace',
|
|
74
|
-
fontSize: 11, lineHeight: 1.55, color: Cid.WHITE, whiteSpace: 'pre',
|
|
75
|
-
overflow: 'auto', borderRadius: 4, border: `1px solid ${Cid.PANEL_EDGE}`,
|
|
76
|
-
}}>
|
|
77
|
-
{lines.map((ln, i) => {
|
|
78
|
-
const m = ln.match(/^(\s*-?\s*)([a-zA-Z][a-zA-Z0-9_]*)(:)(.*)$/);
|
|
79
|
-
if (m) {
|
|
80
|
-
return (
|
|
81
|
-
<div key={i}>
|
|
82
|
-
<span style={{ color: Cid.GREY }}>{m[1]}</span>
|
|
83
|
-
<span style={{ color: Cid.BLUE }}>{m[2]}</span>
|
|
84
|
-
<span style={{ color: Cid.GREY }}>{m[3]}</span>
|
|
85
|
-
<span style={{ color: /\[/.test(m[4]) ? Cid.GOLD_TEXT : Cid.GREEN_TEXT }}>{m[4]}</span>
|
|
86
|
-
</div>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (ln.trim().startsWith('must') || ln.trim().startsWith('should') || ln.trim().startsWith('mustnot')) {
|
|
90
|
-
return <div key={i} style={{ color: Cid.ORANGE_TEXT }}>{ln}</div>;
|
|
91
|
-
}
|
|
92
|
-
return <div key={i} style={{ color: Cid.GREY }}>{ln || '\u00A0'}</div>;
|
|
93
|
-
})}
|
|
94
|
-
</pre>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Draggable clause pill.
|
|
99
|
-
function DragClause({ clause, zone, onDragStart }) {
|
|
100
|
-
const z = ZONE_META[zone];
|
|
101
|
-
return (
|
|
102
|
-
<div
|
|
103
|
-
onMouseDown={(e) => onDragStart(e, clause, zone)}
|
|
104
|
-
onTouchStart={(e) => onDragStart(e, clause, zone)}
|
|
105
|
-
style={{
|
|
106
|
-
display: 'flex', alignItems: 'center', gap: 6,
|
|
107
|
-
background: Cid.DARK_GREY, border: `2px solid ${z.color}`,
|
|
108
|
-
borderRadius: 6, padding: '5px 8px 5px 4px',
|
|
109
|
-
boxShadow: `0 2px 0 ${Cid.BLACK}`,
|
|
110
|
-
cursor: 'grab', userSelect: 'none', touchAction: 'none',
|
|
111
|
-
}}
|
|
112
|
-
>
|
|
113
|
-
{/* grip */}
|
|
114
|
-
<div style={{ color: Cid.GREY, fontSize: 12, lineHeight: 1, padding: '0 2px' }}>⋮⋮</div>
|
|
115
|
-
{ideSprite(clause, 26)}
|
|
116
|
-
<div style={{ fontSize: 10, color: Cid.WHITE, letterSpacing: 1, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }}>
|
|
117
|
-
{clause.label || clause.value}
|
|
118
|
-
</div>
|
|
119
|
-
{clause.antes && (
|
|
120
|
-
<div style={{ display: 'flex', gap: 2 }}>
|
|
121
|
-
{clause.antes.slice(0,3).map(a => (
|
|
122
|
-
<div key={a} style={{ fontSize: 8, padding: '0 3px', background: Cid.DARKEST, color: z.color, borderRadius: 2 }}>{a}</div>
|
|
123
|
-
))}
|
|
124
|
-
{clause.antes.length > 3 && <div style={{ fontSize: 8, color: Cid.GREY }}>+{clause.antes.length-3}</div>}
|
|
125
|
-
</div>
|
|
126
|
-
)}
|
|
127
|
-
{clause.score != null && (
|
|
128
|
-
<div style={{ fontSize: 9, padding: '0 4px', background: Cid.RED, color: Cid.WHITE, borderRadius: 2 }}>+{clause.score}</div>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function ZoneDropRail({ zone, clauses, onDragStart, highlight }) {
|
|
135
|
-
const z = ZONE_META[zone];
|
|
136
|
-
return (
|
|
137
|
-
<div data-zone={zone} style={{
|
|
138
|
-
border: `2px dashed ${highlight ? z.color : `${z.color}55`}`,
|
|
139
|
-
background: highlight ? `${z.color}22` : 'transparent',
|
|
140
|
-
borderRadius: 6, padding: 8,
|
|
141
|
-
transition: 'background 100ms, border-color 100ms',
|
|
142
|
-
}}>
|
|
143
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
|
|
144
|
-
<div style={{
|
|
145
|
-
fontSize: 10, letterSpacing: 2, padding: '2px 8px',
|
|
146
|
-
background: z.color, color: Cid.WHITE, borderRadius: 3,
|
|
147
|
-
textShadow: '1px 1px 0 rgba(0,0,0,.8)',
|
|
148
|
-
}}>{z.label}</div>
|
|
149
|
-
<div style={{ flex: 1, height: 1, background: `${z.color}44` }} />
|
|
150
|
-
<div style={{ fontSize: 8, color: Cid.GREY }}>{clauses.length}</div>
|
|
151
|
-
</div>
|
|
152
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
153
|
-
{clauses.map(c => (
|
|
154
|
-
<DragClause key={c.id} clause={c} zone={zone} onDragStart={onDragStart} />
|
|
155
|
-
))}
|
|
156
|
-
{clauses.length === 0 && (
|
|
157
|
-
<div style={{ fontSize: 10, color: Cid.GREY, padding: 10, fontStyle: 'italic' }}>drop clauses here</div>
|
|
158
|
-
)}
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function JamlIde() {
|
|
165
|
-
const base = window.FILTER_V2;
|
|
166
|
-
const [tab, setTab] = idUS('visual'); // visual | yaml
|
|
167
|
-
const [state, setState] = idUS(() => ({
|
|
168
|
-
must: (base.must || []).map(c => ({ ...c })),
|
|
169
|
-
should: (base.should || []).map(c => ({ ...c })),
|
|
170
|
-
mustnot: [],
|
|
171
|
-
}));
|
|
172
|
-
|
|
173
|
-
// drag state
|
|
174
|
-
const [drag, setDrag] = idUS(null); // {clause, fromZone, x, y, offX, offY}
|
|
175
|
-
const [hoverZone, setHoverZone] = idUS(null);
|
|
176
|
-
const rootRef = idUR(null);
|
|
177
|
-
|
|
178
|
-
const onDragStart = (e, clause, fromZone) => {
|
|
179
|
-
e.preventDefault();
|
|
180
|
-
const t = e.touches ? e.touches[0] : e;
|
|
181
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
182
|
-
setDrag({ clause, fromZone, x: t.clientX, y: t.clientY, offX: t.clientX - rect.left, offY: t.clientY - rect.top, w: rect.width, h: rect.height });
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
idUE(() => {
|
|
186
|
-
if (!drag) return;
|
|
187
|
-
const move = (e) => {
|
|
188
|
-
const t = e.touches ? e.touches[0] : e;
|
|
189
|
-
setDrag(d => d && { ...d, x: t.clientX, y: t.clientY });
|
|
190
|
-
// hit-test zones
|
|
191
|
-
const rails = rootRef.current?.querySelectorAll('[data-zone]') || [];
|
|
192
|
-
let found = null;
|
|
193
|
-
for (const r of rails) {
|
|
194
|
-
const rc = r.getBoundingClientRect();
|
|
195
|
-
if (t.clientX >= rc.left && t.clientX <= rc.right && t.clientY >= rc.top && t.clientY <= rc.bottom) {
|
|
196
|
-
found = r.getAttribute('data-zone'); break;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
setHoverZone(found);
|
|
200
|
-
};
|
|
201
|
-
const up = () => {
|
|
202
|
-
if (hoverZone && hoverZone !== drag.fromZone) {
|
|
203
|
-
setState(s => {
|
|
204
|
-
const from = s[drag.fromZone].filter(c => c.id !== drag.clause.id);
|
|
205
|
-
const to = [...s[hoverZone], { ...drag.clause }];
|
|
206
|
-
return { ...s, [drag.fromZone]: from, [hoverZone]: to };
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
setDrag(null); setHoverZone(null);
|
|
210
|
-
};
|
|
211
|
-
window.addEventListener('mousemove', move);
|
|
212
|
-
window.addEventListener('mouseup', up);
|
|
213
|
-
window.addEventListener('touchmove', move, { passive: false });
|
|
214
|
-
window.addEventListener('touchend', up);
|
|
215
|
-
return () => {
|
|
216
|
-
window.removeEventListener('mousemove', move);
|
|
217
|
-
window.removeEventListener('mouseup', up);
|
|
218
|
-
window.removeEventListener('touchmove', move);
|
|
219
|
-
window.removeEventListener('touchend', up);
|
|
220
|
-
};
|
|
221
|
-
}, [drag, hoverZone]);
|
|
222
|
-
|
|
223
|
-
const yamlText = toJamlText({ ...base, ...state });
|
|
224
|
-
|
|
225
|
-
return (
|
|
226
|
-
<div ref={rootRef} style={{
|
|
227
|
-
width: '100%', height: '100%', background: Cid.DARKEST,
|
|
228
|
-
display: 'flex', flexDirection: 'column',
|
|
229
|
-
fontFamily: 'm6x11plus, monospace', color: Cid.WHITE, overflow: 'hidden',
|
|
230
|
-
}}>
|
|
231
|
-
{/* Tab strip */}
|
|
232
|
-
<div style={{
|
|
233
|
-
display: 'flex', padding: '10px 10px 6px', gap: 6,
|
|
234
|
-
background: Cid.DARK_GREY, borderBottom: `2px solid ${Cid.BLACK}`,
|
|
235
|
-
}}>
|
|
236
|
-
{[['visual','Visual'], ['yaml','.jaml']].map(([k,l]) => (
|
|
237
|
-
<div key={k}
|
|
238
|
-
onClick={() => setTab(k)}
|
|
239
|
-
style={{
|
|
240
|
-
flex: 1, textAlign: 'center', padding: '7px 0',
|
|
241
|
-
background: tab === k ? Cid.GOLD : Cid.DARKEST,
|
|
242
|
-
color: tab === k ? Cid.BLACK : Cid.GREY,
|
|
243
|
-
fontSize: 12, letterSpacing: 2, borderRadius: 4,
|
|
244
|
-
border: `2px solid ${tab === k ? Cid.GOLD_TEXT : Cid.PANEL_EDGE}`,
|
|
245
|
-
textShadow: tab === k ? 'none' : '1px 1px 0 rgba(0,0,0,.8)',
|
|
246
|
-
cursor: 'pointer', userSelect: 'none',
|
|
247
|
-
}}>{l}</div>
|
|
248
|
-
))}
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
{/* Body */}
|
|
252
|
-
<div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: 10, display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
253
|
-
{tab === 'visual' ? (
|
|
254
|
-
<>
|
|
255
|
-
{/* File metadata */}
|
|
256
|
-
<div style={{
|
|
257
|
-
background: Cid.DARK_GREY, border: `2px solid ${Cid.PANEL_EDGE}`, borderRadius: 6,
|
|
258
|
-
padding: 8, boxShadow: `0 2px 0 ${Cid.BLACK}`,
|
|
259
|
-
}}>
|
|
260
|
-
<div style={{ fontSize: 9, color: Cid.GREY, letterSpacing: 2 }}>FILE</div>
|
|
261
|
-
<div style={{ fontSize: 14, color: Cid.WHITE, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }}>{base.name}.jaml</div>
|
|
262
|
-
<div style={{ fontSize: 9, color: Cid.GOLD_TEXT, marginTop: 2 }}>by {base.author}</div>
|
|
263
|
-
</div>
|
|
264
|
-
|
|
265
|
-
{/* Hint */}
|
|
266
|
-
<div style={{ fontSize: 9, color: Cid.GREY, letterSpacing: 1, textAlign: 'center' }}>
|
|
267
|
-
⋮⋮ drag clauses between zones · tap to edit
|
|
268
|
-
</div>
|
|
269
|
-
|
|
270
|
-
<ZoneDropRail zone="must" clauses={state.must} onDragStart={onDragStart} highlight={hoverZone==='must'} />
|
|
271
|
-
<ZoneDropRail zone="should" clauses={state.should} onDragStart={onDragStart} highlight={hoverZone==='should'} />
|
|
272
|
-
<ZoneDropRail zone="mustnot" clauses={state.mustnot} onDragStart={onDragStart} highlight={hoverZone==='mustnot'} />
|
|
273
|
-
</>
|
|
274
|
-
) : (
|
|
275
|
-
<YamlText text={yamlText} />
|
|
276
|
-
)}
|
|
277
|
-
</div>
|
|
278
|
-
|
|
279
|
-
{/* Bottom actions */}
|
|
280
|
-
<div style={{
|
|
281
|
-
padding: '8px 10px 10px', borderTop: `2px solid ${Cid.BLACK}`, background: Cid.DARK_GREY,
|
|
282
|
-
display: 'flex', flexDirection: 'column', gap: 6,
|
|
283
|
-
}}>
|
|
284
|
-
<window.BalButton tone="green" fullWidth size="md">Save</window.BalButton>
|
|
285
|
-
<window.BalButton tone="orange" fullWidth size="md">Back</window.BalButton>
|
|
286
|
-
</div>
|
|
287
|
-
|
|
288
|
-
{/* Drag ghost */}
|
|
289
|
-
{drag && (
|
|
290
|
-
<div style={{
|
|
291
|
-
position: 'fixed', left: drag.x - drag.offX, top: drag.y - drag.offY,
|
|
292
|
-
pointerEvents: 'none', zIndex: 999, transform: 'rotate(-2deg) scale(1.05)',
|
|
293
|
-
filter: 'drop-shadow(0 4px 6px rgba(0,0,0,.6))', opacity: 0.92,
|
|
294
|
-
}}>
|
|
295
|
-
<DragClause clause={drag.clause} zone={drag.fromZone} onDragStart={() => {}} />
|
|
296
|
-
</div>
|
|
297
|
-
)}
|
|
298
|
-
</div>
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
window.JamlIde = JamlIde;
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
// SearchResultsV2 — vault/results table.
|
|
2
|
-
// Each row is: [seed] [deck] [ scoreCols (same as SeedDetail top bar) ] [total]
|
|
3
|
-
// Tap a row → expands inline into the full SeedDetailV2 for that seed.
|
|
4
|
-
// Header row shows the SAME score columns as column headers — the filter, the
|
|
5
|
-
// column headers, and the per-row chips are all the same visual object.
|
|
6
|
-
|
|
7
|
-
const { useState: rUS, useMemo: rUM } = React;
|
|
8
|
-
const Cr = window.JimboColor;
|
|
9
|
-
|
|
10
|
-
// Generate a handful of pseudo-seeds by mutating the base one so the list
|
|
11
|
-
// has varied scores.
|
|
12
|
-
function makeResults(base, filter, count = 12) {
|
|
13
|
-
const out = [];
|
|
14
|
-
const pool = [
|
|
15
|
-
'blueprint','brainstorm','hangingchad','photograph','showman','baseball',
|
|
16
|
-
'fibonacci','ceremonialdagger','bull','scholar','banner','supernova',
|
|
17
|
-
];
|
|
18
|
-
for (let i = 0; i < count; i++) {
|
|
19
|
-
const seed = JSON.parse(JSON.stringify(base));
|
|
20
|
-
seed.seed = randSeed();
|
|
21
|
-
// Mutate some ante 2 shop slots to produce different hit distributions
|
|
22
|
-
for (const a of seed.antes) {
|
|
23
|
-
a.shopQueue.forEach(s => { delete s.hits; });
|
|
24
|
-
a.boosterPacks.forEach(p => { delete p.itemHits; delete p.hits; });
|
|
25
|
-
delete a._voucherHits; delete a._smallTagHits; delete a._bigTagHits; delete a._bossHits; delete a._soulHits;
|
|
26
|
-
}
|
|
27
|
-
const a2 = seed.antes[1];
|
|
28
|
-
if (i % 3 === 1) a2.shopQueue[2] = { type: 'joker', value: pool[(i * 7) % pool.length] };
|
|
29
|
-
if (i % 4 === 2) a2.shopQueue[3] = { type: 'joker', value: pool[(i * 3) % pool.length] };
|
|
30
|
-
if (i === 3) a2.smallBlindTag = 'RareTag'; // break must
|
|
31
|
-
if (i === 7) seed.antes[2].soulJoker = { value: 'yorick', edition: null }; // break must
|
|
32
|
-
seed.score = window.computeHits(seed, filter);
|
|
33
|
-
out.push(seed);
|
|
34
|
-
}
|
|
35
|
-
// Always put the perfect base seed first
|
|
36
|
-
out.unshift(base);
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
function randSeed() {
|
|
40
|
-
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
|
41
|
-
let s = '';
|
|
42
|
-
for (let i = 0; i < 8; i++) s += chars[Math.floor(Math.random() * chars.length)];
|
|
43
|
-
return s;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ── MiniScoreChip — smaller variant of the SeedDetail ScoreCol for table cells.
|
|
47
|
-
function MiniScoreChip({ clause, hits, kind }) {
|
|
48
|
-
const lit = hits > 0;
|
|
49
|
-
const must = kind === 'must';
|
|
50
|
-
const color = must ? Cr.BLUE : lit ? Cr.RED : Cr.GREY;
|
|
51
|
-
|
|
52
|
-
const spriteKind =
|
|
53
|
-
clause.type === 'joker' || clause.type === 'souljoker' ? 'jokers' :
|
|
54
|
-
clause.type === 'voucher' ? 'vouchers' :
|
|
55
|
-
clause.type === 'smallblindtag' || clause.type === 'bigblindtag' ? 'tags' :
|
|
56
|
-
clause.type === 'boss' ? 'blinds' : null;
|
|
57
|
-
|
|
58
|
-
const w = 22, h = spriteKind === 'tags' || spriteKind === 'blinds' ? 22 : 30;
|
|
59
|
-
const sprite =
|
|
60
|
-
spriteKind === 'jokers' ? <Sprite sheet="jokers" name={clause.value} width={w} height={h} /> :
|
|
61
|
-
spriteKind === 'vouchers' ? <Sprite sheet="vouchers" name={clause.value} width={w} height={h} /> :
|
|
62
|
-
spriteKind === 'tags' ? <Sprite sheet="tags" name={clause.value} width={h} height={h} /> :
|
|
63
|
-
spriteKind === 'blinds' ? <BossChip name={clause.value} size={h} /> : null;
|
|
64
|
-
|
|
65
|
-
if (must) {
|
|
66
|
-
return (
|
|
67
|
-
<div style={{
|
|
68
|
-
position: 'relative', padding: '2px 3px', borderRadius: 3,
|
|
69
|
-
border: `1.5px solid ${lit ? color : Cr.DARK_GREY}`,
|
|
70
|
-
background: lit ? `${color}22` : 'transparent',
|
|
71
|
-
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
72
|
-
opacity: lit ? 1 : 0.5,
|
|
73
|
-
minWidth: 28,
|
|
74
|
-
}}>{sprite}
|
|
75
|
-
{!lit && (
|
|
76
|
-
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
77
|
-
color: Cr.DARK_RED, fontSize: 22, lineHeight: 1, textShadow: '0 0 4px #000' }}>✗</div>
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
return (
|
|
83
|
-
<div style={{
|
|
84
|
-
position: 'relative',
|
|
85
|
-
opacity: lit ? 1 : 0.35, filter: lit ? 'none' : 'grayscale(0.6)',
|
|
86
|
-
display: 'flex', alignItems: 'flex-end', justifyContent: 'center', minWidth: 28,
|
|
87
|
-
}}>
|
|
88
|
-
{sprite}
|
|
89
|
-
{lit && (
|
|
90
|
-
<div style={{
|
|
91
|
-
position: 'absolute', bottom: -3, right: -4,
|
|
92
|
-
minWidth: 14, height: 13, padding: '0 2px',
|
|
93
|
-
background: Cr.DARKEST,
|
|
94
|
-
fontFamily: 'm6x11plus, monospace', fontSize: 9, lineHeight: '13px',
|
|
95
|
-
color, textAlign: 'center', borderRadius: 6,
|
|
96
|
-
}}>×{hits}</div>
|
|
97
|
-
)}
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ── ColumnHeader — shows just the tiny sprite in grey (column identity)
|
|
103
|
-
function ColumnHeader({ clause, kind }) {
|
|
104
|
-
const color = kind === 'must' ? Cr.BLUE : Cr.RED;
|
|
105
|
-
const spriteKind =
|
|
106
|
-
clause.type === 'joker' || clause.type === 'souljoker' ? 'jokers' :
|
|
107
|
-
clause.type === 'voucher' ? 'vouchers' :
|
|
108
|
-
clause.type === 'smallblindtag' || clause.type === 'bigblindtag' ? 'tags' :
|
|
109
|
-
clause.type === 'boss' ? 'blinds' : null;
|
|
110
|
-
const w = 18, h = spriteKind === 'tags' || spriteKind === 'blinds' ? 18 : 24;
|
|
111
|
-
return (
|
|
112
|
-
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, minWidth: 28 }}>
|
|
113
|
-
<div style={{ opacity: 0.8 }}>
|
|
114
|
-
{spriteKind === 'jokers' && <Sprite sheet="jokers" name={clause.value} width={w} height={h} />}
|
|
115
|
-
{spriteKind === 'vouchers' && <Sprite sheet="vouchers" name={clause.value} width={w} height={h} />}
|
|
116
|
-
{spriteKind === 'tags' && <Sprite sheet="tags" name={clause.value} width={h} height={h} />}
|
|
117
|
-
{spriteKind === 'blinds' && <BossChip name={clause.value} size={h} />}
|
|
118
|
-
</div>
|
|
119
|
-
<div style={{ width: 16, height: 2, background: color, borderRadius: 1 }} />
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ── ResultRow — one seed
|
|
125
|
-
function ResultRow({ seed, filter, expanded, onToggle, onCopy }) {
|
|
126
|
-
const must = filter.must, should = filter.should;
|
|
127
|
-
const score = seed.score?.totalScore ?? 0;
|
|
128
|
-
const [copied, setCopied] = rUS(false);
|
|
129
|
-
|
|
130
|
-
// Top hit line: the matched clause worth the most points — what the user is bragging about.
|
|
131
|
-
let topHit = null;
|
|
132
|
-
let topScore = -1;
|
|
133
|
-
for (const c of [...must, ...should]) {
|
|
134
|
-
const n = seed.score?.totals[c.id] || 0;
|
|
135
|
-
if (!n) continue;
|
|
136
|
-
const s = (c.score || 1) * n;
|
|
137
|
-
if (s > topScore) {
|
|
138
|
-
topScore = s;
|
|
139
|
-
const m = seed.score.matches[c.id]?.[0];
|
|
140
|
-
topHit = m ? `${c.label || m.value} · A${m.ante}` : (c.label || c.value);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const copy = (e) => {
|
|
145
|
-
e.stopPropagation();
|
|
146
|
-
if (navigator.clipboard) navigator.clipboard.writeText(seed.seed).catch(() => {});
|
|
147
|
-
setCopied(true);
|
|
148
|
-
onCopy && onCopy(seed.seed);
|
|
149
|
-
setTimeout(() => setCopied(false), 1200);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
return (
|
|
153
|
-
<div style={{ borderBottom: `1px solid ${Cr.PANEL_EDGE}`, background: expanded ? Cr.DARK_GREY : (copied ? `${Cr.GOLD}22` : 'transparent'), transition: 'background 180ms' }}>
|
|
154
|
-
<div
|
|
155
|
-
onClick={copy}
|
|
156
|
-
style={{
|
|
157
|
-
display: 'flex', alignItems: 'center', gap: 8,
|
|
158
|
-
padding: '8px 10px', cursor: 'pointer',
|
|
159
|
-
borderLeft: `3px solid ${copied ? Cr.GOLD : Cr.BLUE}`,
|
|
160
|
-
}}
|
|
161
|
-
>
|
|
162
|
-
{/* Seed + top hit (replaces useless deck/stake repetition) */}
|
|
163
|
-
<div style={{ minWidth: 88 }}>
|
|
164
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 13, color: copied ? Cr.GOLD : Cr.WHITE, letterSpacing: 2, textShadow: '1px 1px 0 rgba(0,0,0,.8)' }}>
|
|
165
|
-
{copied ? 'COPIED!' : seed.seed}
|
|
166
|
-
</div>
|
|
167
|
-
{topHit && !copied && (
|
|
168
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 8, color: Cr.GOLD_TEXT, letterSpacing: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 90 }}>♪ {topHit}</div>
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
171
|
-
{/* Should chips only — musts are assumed to have matched */}
|
|
172
|
-
<div style={{ display: 'flex', gap: 6, flex: 1 }}>
|
|
173
|
-
{should.map(c => <MiniScoreChip key={c.id} clause={c} hits={seed.score?.totals[c.id] || 0} kind="should" />)}
|
|
174
|
-
</div>
|
|
175
|
-
{/* Score */}
|
|
176
|
-
<div style={{
|
|
177
|
-
fontFamily: 'm6x11plus, monospace', fontSize: 16, color: Cr.RED, letterSpacing: 1,
|
|
178
|
-
minWidth: 28, textAlign: 'right',
|
|
179
|
-
}}>{score}</div>
|
|
180
|
-
{/* chevron — opens detail (stops propagation so tap still copies) */}
|
|
181
|
-
<div onClick={(e) => { e.stopPropagation(); onToggle(); }} style={{ padding: '6px 4px' }}>
|
|
182
|
-
<svg width="10" height="10" viewBox="0 0 10 10" style={{
|
|
183
|
-
color: Cr.GREY, transform: expanded ? 'rotate(90deg)' : 'none', transition: 'transform 180ms',
|
|
184
|
-
}}><path d="M3 1 L7 5 L3 9" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" /></svg>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
{expanded && (
|
|
189
|
-
<div style={{
|
|
190
|
-
borderTop: `2px solid ${Cr.BLACK}`,
|
|
191
|
-
background: Cr.DARKEST,
|
|
192
|
-
height: 640, overflow: 'hidden',
|
|
193
|
-
}}>
|
|
194
|
-
<SeedDetailV2 seeds={[seed]} filter={filter} embedded />
|
|
195
|
-
</div>
|
|
196
|
-
)}
|
|
197
|
-
</div>
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ── SearchHeader — top chrome: filter name + count + column header row
|
|
202
|
-
function SearchHeader({ filter, count }) {
|
|
203
|
-
return (
|
|
204
|
-
<div style={{
|
|
205
|
-
position: 'sticky', top: 0, zIndex: 10,
|
|
206
|
-
background: Cr.DARKEST, borderBottom: `2px solid ${Cr.BLACK}`,
|
|
207
|
-
boxShadow: `0 2px 0 ${Cr.BLACK}`,
|
|
208
|
-
}}>
|
|
209
|
-
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 10px 6px' }}>
|
|
210
|
-
<div>
|
|
211
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 8, color: Cr.GREY, letterSpacing: 1.5 }}>FILTER</div>
|
|
212
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 13, color: Cr.WHITE, letterSpacing: 1 }}>{filter.name}</div>
|
|
213
|
-
</div>
|
|
214
|
-
<div style={{ textAlign: 'right' }}>
|
|
215
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 8, color: Cr.GREY, letterSpacing: 1.5 }}>RESULTS</div>
|
|
216
|
-
<div style={{ fontFamily: 'm6x11plus, monospace', fontSize: 13, color: Cr.WHITE }}>{count}</div>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
{/* Column header row: aligned to match ResultRow's column structure */}
|
|
220
|
-
<div style={{
|
|
221
|
-
display: 'flex', alignItems: 'flex-end', gap: 8, padding: '6px 10px 8px',
|
|
222
|
-
background: `linear-gradient(180deg, ${Cr.DARKEST}, ${Cr.DARK_GREY})`,
|
|
223
|
-
borderTop: `1px solid ${Cr.BLACK}`,
|
|
224
|
-
}}>
|
|
225
|
-
<div style={{ minWidth: 88, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
226
|
-
<div style={{ fontSize: 8, color: Cr.GREY, letterSpacing: 1.5 }}>SEED</div>
|
|
227
|
-
</div>
|
|
228
|
-
<div style={{ display: 'flex', gap: 6, flex: 1 }}>
|
|
229
|
-
{filter.should.map(c => <ColumnHeader key={c.id} clause={c} kind="should" />)}
|
|
230
|
-
</div>
|
|
231
|
-
<div style={{ fontSize: 8, color: Cr.RED, letterSpacing: 1.5, minWidth: 28, textAlign: 'right' }}>SCORE</div>
|
|
232
|
-
<div style={{ width: 10 }} />
|
|
233
|
-
</div>
|
|
234
|
-
</div>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function SearchResultsV2({ filter }) {
|
|
239
|
-
const results = rUM(() => makeResults(window.SEEDS_V2[0], filter, 12), [filter]);
|
|
240
|
-
const [sortBy, setSortBy] = rUS('score');
|
|
241
|
-
const [openId, setOpenId] = rUS(null);
|
|
242
|
-
const [toast, setToast] = rUS(null);
|
|
243
|
-
|
|
244
|
-
const sorted = rUM(() => {
|
|
245
|
-
const a = [...results];
|
|
246
|
-
if (sortBy === 'score') a.sort((x, y) => y.score.totalScore - x.score.totalScore);
|
|
247
|
-
return a;
|
|
248
|
-
}, [results, sortBy]);
|
|
249
|
-
|
|
250
|
-
const onCopy = (seed) => {
|
|
251
|
-
setToast(seed);
|
|
252
|
-
setTimeout(() => setToast(t => t === seed ? null : t), 1400);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
return (
|
|
256
|
-
<div style={{
|
|
257
|
-
height: '100%', width: '100%', background: Cr.DARKEST,
|
|
258
|
-
display: 'flex', flexDirection: 'column', overflow: 'hidden',
|
|
259
|
-
fontFamily: 'm6x11plus, monospace', color: Cr.WHITE, position: 'relative',
|
|
260
|
-
}}>
|
|
261
|
-
<SearchHeader filter={filter} count={results.length} />
|
|
262
|
-
<div style={{ flex: 1, overflowY: 'auto', scrollbarWidth: 'none' }}>
|
|
263
|
-
{sorted.map(s => (
|
|
264
|
-
<ResultRow
|
|
265
|
-
key={s.seed}
|
|
266
|
-
seed={s}
|
|
267
|
-
filter={filter}
|
|
268
|
-
expanded={openId === s.seed}
|
|
269
|
-
onToggle={() => setOpenId(openId === s.seed ? null : s.seed)}
|
|
270
|
-
onCopy={onCopy}
|
|
271
|
-
/>
|
|
272
|
-
))}
|
|
273
|
-
</div>
|
|
274
|
-
{toast && (
|
|
275
|
-
<div style={{
|
|
276
|
-
position: 'absolute', left: '50%', bottom: 24, transform: 'translateX(-50%)',
|
|
277
|
-
background: Cr.GOLD, color: Cr.BLACK, padding: '8px 16px', borderRadius: 6,
|
|
278
|
-
fontSize: 13, letterSpacing: 2, boxShadow: '0 4px 0 rgba(0,0,0,.6)',
|
|
279
|
-
border: `2px solid ${Cr.GOLD_TEXT}`, zIndex: 50,
|
|
280
|
-
}}>✓ {toast} COPIED</div>
|
|
281
|
-
)}
|
|
282
|
-
</div>
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
window.SearchResultsV2 = SearchResultsV2;
|