jaml-ui 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # jaml-ui
2
2
 
3
- Balatro rendering components, sprite metadata, and optional Motely helpers for React apps.
4
-
5
- `jaml-ui` is the shared UI layer for Balatro/JAML surfaces: low-level renderers, asset helpers, visual JAML previews, and a lightweight browser-first JAML IDE shell.
3
+ React components and utilities for Balatro/JAML. Pair with **`motely-wasm`** when you need the engine (peer). Update both in consumers with normal **`pnpm update`** / semver like any other deps.
6
4
 
7
5
  ## Package shape
8
6
 
@@ -0,0 +1,44 @@
1
+ import { type AnalyzerShopItem } from "./GameCard.js";
2
+ export interface AnalyzerBadge {
3
+ label: string;
4
+ tone?: "default" | "accent" | "muted";
5
+ }
6
+ export interface AnalyzerFact {
7
+ label: string;
8
+ value: string;
9
+ }
10
+ export interface AnalyzerItem extends AnalyzerShopItem {
11
+ desired?: boolean;
12
+ badges?: AnalyzerBadge[];
13
+ detail?: string;
14
+ }
15
+ export interface AnalyzerAnteView {
16
+ ante: number;
17
+ boss?: string;
18
+ voucher?: string;
19
+ smallBlindTag?: string;
20
+ bigBlindTag?: string;
21
+ packs?: string[];
22
+ shop?: AnalyzerItem[];
23
+ facts?: AnalyzerFact[];
24
+ }
25
+ export interface AnalyzerHighlight {
26
+ id: string;
27
+ ante: number;
28
+ title: string;
29
+ subtitle?: string;
30
+ desired?: boolean;
31
+ item?: AnalyzerItem;
32
+ boss?: string;
33
+ voucher?: string;
34
+ tag?: string;
35
+ badges?: AnalyzerBadge[];
36
+ }
37
+ export interface AnalyzerExplorerProps {
38
+ antes: AnalyzerAnteView[];
39
+ highlights?: AnalyzerHighlight[];
40
+ visibleAntes?: number;
41
+ totalAntes?: number;
42
+ className?: string;
43
+ }
44
+ export declare function AnalyzerExplorer({ antes, highlights, visibleAntes, totalAntes, className, }: AnalyzerExplorerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,451 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { JamlBoss, JamlGameCard, JamlTag, JamlVoucher, resolveAnalyzerShopItem, } from "./GameCard.js";
5
+ export function AnalyzerExplorer({ antes, highlights = [], visibleAntes, totalAntes, className = "", }) {
6
+ const [currentAnte, setCurrentAnte] = useState(antes[0]?.ante ?? 0);
7
+ const scrollRef = useRef(null);
8
+ const anteRefs = useRef(new Map());
9
+ const highlightRefs = useRef(new Map());
10
+ useEffect(() => {
11
+ setCurrentAnte(antes[0]?.ante ?? 0);
12
+ }, [antes]);
13
+ useEffect(() => {
14
+ const root = scrollRef.current;
15
+ if (!root) {
16
+ return;
17
+ }
18
+ const observer = new IntersectionObserver((entries) => {
19
+ const mostVisibleEntry = entries
20
+ .filter((entry) => entry.isIntersecting)
21
+ .sort((left, right) => right.intersectionRatio - left.intersectionRatio)[0];
22
+ if (!mostVisibleEntry) {
23
+ return;
24
+ }
25
+ const ante = Number(mostVisibleEntry.target.dataset.ante);
26
+ if (!Number.isNaN(ante)) {
27
+ setCurrentAnte(ante);
28
+ }
29
+ }, {
30
+ root,
31
+ threshold: [0.45, 0.72, 0.9],
32
+ });
33
+ for (const [, element] of anteRefs.current) {
34
+ observer.observe(element);
35
+ }
36
+ return () => observer.disconnect();
37
+ }, [antes]);
38
+ useEffect(() => {
39
+ const activeHighlight = highlights.find((highlight) => highlight.ante === currentAnte);
40
+ if (!activeHighlight) {
41
+ return;
42
+ }
43
+ const element = highlightRefs.current.get(activeHighlight.id);
44
+ element?.scrollIntoView({
45
+ behavior: "smooth",
46
+ inline: "center",
47
+ block: "nearest",
48
+ });
49
+ }, [currentAnte, highlights]);
50
+ const scrollToAnte = useCallback((ante) => {
51
+ anteRefs.current.get(ante)?.scrollIntoView({
52
+ behavior: "smooth",
53
+ block: "start",
54
+ });
55
+ }, []);
56
+ const currentAnteIndex = antes.findIndex((ante) => ante.ante === currentAnte);
57
+ const previousAnte = currentAnteIndex > 0 ? antes[currentAnteIndex - 1]?.ante ?? null : null;
58
+ const nextAnte = currentAnteIndex >= 0 && currentAnteIndex < antes.length - 1 ? antes[currentAnteIndex + 1]?.ante ?? null : null;
59
+ const shownAntes = visibleAntes ?? antes.length;
60
+ const availableAntes = totalAntes ?? shownAntes;
61
+ return (_jsxs("div", { className: className, style: styles.root, children: [highlights.length > 0 ? (_jsxs("section", { style: styles.highlightSection, children: [_jsxs("div", { style: styles.highlightHeader, children: [_jsx("span", { style: styles.highlightTitle, children: "Highlights" }), _jsx("span", { style: styles.highlightSubtitle, children: "Swipe, tap, jump" })] }), _jsx("div", { style: styles.highlightRail, children: highlights.map((highlight) => {
62
+ const isActive = highlight.ante === currentAnte;
63
+ return (_jsxs("button", { ref: (element) => {
64
+ if (element) {
65
+ highlightRefs.current.set(highlight.id, element);
66
+ }
67
+ else {
68
+ highlightRefs.current.delete(highlight.id);
69
+ }
70
+ }, type: "button", onClick: () => scrollToAnte(highlight.ante), style: isActive ? styles.highlightCardActive : styles.highlightCard, children: [_jsx("div", { style: styles.highlightVisual, children: renderHighlightVisual(highlight) }), _jsxs("div", { style: styles.highlightMeta, children: [_jsxs("span", { style: styles.highlightAnte, children: ["Ante ", highlight.ante] }), _jsx("span", { style: styles.highlightLabel, children: highlight.title }), highlight.subtitle ? (_jsx("span", { style: styles.highlightDescription, children: highlight.subtitle })) : null, _jsx(BadgeRow, { badges: highlight.badges, desired: highlight.desired })] })] }, highlight.id));
71
+ }) })] })) : null, _jsxs("div", { style: styles.navBar, children: [_jsx("button", { type: "button", onClick: () => previousAnte !== null && scrollToAnte(previousAnte), disabled: previousAnte === null, style: {
72
+ ...styles.navButton,
73
+ opacity: previousAnte !== null ? 1 : 0.25,
74
+ }, children: "\u25B2" }), _jsxs("div", { style: styles.navLabel, children: ["Ante ", currentAnte, _jsxs("span", { style: styles.navSubLabel, children: ["of ", shownAntes, availableAntes > shownAntes ? ` / ${availableAntes}` : ""] })] }), _jsx("button", { type: "button", onClick: () => nextAnte !== null && scrollToAnte(nextAnte), disabled: nextAnte === null, style: {
75
+ ...styles.navButton,
76
+ opacity: nextAnte !== null ? 1 : 0.25,
77
+ }, children: "\u25BC" })] }), _jsx("div", { ref: scrollRef, style: styles.scrollRegion, children: antes.map((ante) => (_jsx("div", { "data-ante": ante.ante, ref: (element) => {
78
+ if (element) {
79
+ anteRefs.current.set(ante.ante, element);
80
+ }
81
+ else {
82
+ anteRefs.current.delete(ante.ante);
83
+ }
84
+ }, style: styles.antePage, children: _jsx(AnteSection, { ante: ante }) }, ante.ante))) })] }));
85
+ }
86
+ function AnteSection({ ante }) {
87
+ return (_jsxs("section", { style: styles.anteSection, children: [_jsxs("div", { style: styles.anteHeader, children: [_jsxs("span", { style: styles.anteHeading, children: ["Ante ", ante.ante] }), ante.boss ? (_jsxs("div", { style: styles.bossRow, children: [_jsx(JamlBoss, { bossName: ante.boss, scale: 0.62 }), _jsx("span", { style: styles.bossName, children: ante.boss })] })) : null] }), ante.smallBlindTag || ante.bigBlindTag ? (_jsxs(AnalyzerRow, { label: "Tags", children: [ante.smallBlindTag ? (_jsx(CompactCard, { label: "Small", visual: _jsx(JamlTag, { tagName: ante.smallBlindTag, scale: 0.58, hoverTilt: true }), text: ante.smallBlindTag })) : null, ante.bigBlindTag ? (_jsx(CompactCard, { label: "Big", visual: _jsx(JamlTag, { tagName: ante.bigBlindTag, scale: 0.58, hoverTilt: true }), text: ante.bigBlindTag })) : null] })) : null, ante.voucher ? (_jsx(AnalyzerRow, { label: "Voucher", children: _jsx(CompactCard, { visual: _jsx(JamlVoucher, { voucherName: ante.voucher, scale: 0.58, hoverTilt: true }), text: ante.voucher }) })) : null, ante.shop && ante.shop.length > 0 ? (_jsx(AnalyzerRow, { label: "Shop", dense: true, children: ante.shop.map((item) => (_jsx(ResolvedItemCard, { item: item }, `${ante.ante}-${item.id}-${item.name}`))) })) : null, ante.packs && ante.packs.length > 0 ? (_jsx(AnalyzerRow, { label: "Packs", children: ante.packs.map((pack) => (_jsx("div", { style: styles.packChip, children: pack }, `${ante.ante}-${pack}`))) })) : null, ante.facts && ante.facts.length > 0 ? (_jsx(AnalyzerRow, { label: "Facts", children: ante.facts.map((fact) => (_jsxs("div", { style: styles.factCard, children: [_jsx("span", { style: styles.factLabel, children: fact.label }), _jsx("span", { style: styles.factValue, children: fact.value })] }, `${ante.ante}-${fact.label}-${fact.value}`))) })) : null] }));
88
+ }
89
+ function AnalyzerRow({ label, children, dense = false, }) {
90
+ return (_jsxs("div", { style: styles.row, children: [_jsx("div", { style: styles.rowLabel, children: label }), _jsx("div", { style: dense ? styles.denseGrid : styles.cardFlow, children: children })] }));
91
+ }
92
+ function CompactCard({ label, visual, text, }) {
93
+ return (_jsxs("div", { style: styles.compactCard, children: [label ? _jsx("span", { style: styles.compactLabel, children: label }) : null, visual, _jsx("span", { style: styles.itemText, children: text })] }));
94
+ }
95
+ function ResolvedItemCard({ item }) {
96
+ const resolved = resolveAnalyzerShopItem(item, 0.58);
97
+ const wrapperStyle = item.desired ? styles.itemCardDesired : styles.itemCard;
98
+ if (resolved.kind === "joker" || resolved.kind === "consumable" || resolved.kind === "playing") {
99
+ return (_jsxs("div", { style: wrapperStyle, children: [_jsx(JamlGameCard, { card: resolved.card, type: resolved.type, hoverTilt: true }), _jsx("span", { style: styles.itemText, children: item.name }), _jsx(BadgeRow, { badges: item.badges, desired: item.desired }), item.detail ? _jsx("span", { style: styles.itemDetail, children: item.detail }) : null] }));
100
+ }
101
+ if (resolved.kind === "voucher") {
102
+ return (_jsxs("div", { style: wrapperStyle, children: [_jsx(JamlVoucher, { voucherName: resolved.voucherName, scale: 0.58, hoverTilt: true }), _jsx("span", { style: styles.itemText, children: item.name }), _jsx(BadgeRow, { badges: item.badges, desired: item.desired }), item.detail ? _jsx("span", { style: styles.itemDetail, children: item.detail }) : null] }));
103
+ }
104
+ return (_jsxs("div", { style: wrapperStyle, children: [_jsx("div", { style: styles.packChip, children: item.name }), _jsx(BadgeRow, { badges: item.badges, desired: item.desired }), item.detail ? _jsx("span", { style: styles.itemDetail, children: item.detail }) : null] }));
105
+ }
106
+ function BadgeRow({ badges, desired = false }) {
107
+ const combinedBadges = desired
108
+ ? [{ label: "desired", tone: "accent" }, ...(badges ?? [])]
109
+ : (badges ?? []);
110
+ if (combinedBadges.length === 0) {
111
+ return null;
112
+ }
113
+ return (_jsx("div", { style: styles.badgeRow, children: combinedBadges.map((badge) => {
114
+ const badgeStyle = badge.tone === "accent"
115
+ ? styles.badgeAccent
116
+ : badge.tone === "muted"
117
+ ? styles.badgeMuted
118
+ : styles.badge;
119
+ return (_jsx("span", { style: badgeStyle, children: badge.label }, `${badge.label}-${badge.tone ?? "default"}`));
120
+ }) }));
121
+ }
122
+ function renderHighlightVisual(highlight) {
123
+ if (highlight.item) {
124
+ return _jsx(ResolvedItemCard, { item: highlight.item });
125
+ }
126
+ if (highlight.boss) {
127
+ return _jsx(JamlBoss, { bossName: highlight.boss, scale: 0.92, hoverTilt: true });
128
+ }
129
+ if (highlight.voucher) {
130
+ return _jsx(JamlVoucher, { voucherName: highlight.voucher, scale: 0.92, hoverTilt: true });
131
+ }
132
+ if (highlight.tag) {
133
+ return _jsx(JamlTag, { tagName: highlight.tag, scale: 0.92, hoverTilt: true });
134
+ }
135
+ return _jsx("div", { style: styles.packChip, children: highlight.title });
136
+ }
137
+ const styles = {
138
+ root: {
139
+ display: "flex",
140
+ flexDirection: "column",
141
+ height: "100%",
142
+ minHeight: 0,
143
+ },
144
+ highlightSection: {
145
+ padding: "10px 16px 8px",
146
+ borderBottom: "1px solid #1a1a34",
147
+ background: "#0f0f22",
148
+ },
149
+ highlightHeader: {
150
+ display: "flex",
151
+ alignItems: "center",
152
+ justifyContent: "space-between",
153
+ gap: 12,
154
+ marginBottom: 8,
155
+ },
156
+ highlightTitle: {
157
+ fontSize: 11,
158
+ color: "#6f6fa1",
159
+ textTransform: "uppercase",
160
+ letterSpacing: "0.08em",
161
+ },
162
+ highlightSubtitle: {
163
+ fontSize: 11,
164
+ color: "#6f6fa1",
165
+ },
166
+ highlightRail: {
167
+ display: "flex",
168
+ gap: 10,
169
+ overflowX: "auto",
170
+ paddingBottom: 4,
171
+ scrollSnapType: "x mandatory",
172
+ },
173
+ highlightCard: {
174
+ minWidth: 150,
175
+ maxWidth: 150,
176
+ display: "flex",
177
+ flexDirection: "column",
178
+ gap: 6,
179
+ padding: 8,
180
+ borderRadius: 12,
181
+ border: "1px solid #2a2a55",
182
+ background: "#131326",
183
+ scrollSnapAlign: "center",
184
+ cursor: "pointer",
185
+ },
186
+ highlightCardActive: {
187
+ minWidth: 150,
188
+ maxWidth: 150,
189
+ display: "flex",
190
+ flexDirection: "column",
191
+ gap: 6,
192
+ padding: 8,
193
+ borderRadius: 12,
194
+ border: "1px solid rgba(245,200,66,0.45)",
195
+ background: "rgba(245,200,66,0.08)",
196
+ scrollSnapAlign: "center",
197
+ cursor: "pointer",
198
+ },
199
+ highlightVisual: {
200
+ display: "flex",
201
+ justifyContent: "center",
202
+ alignItems: "flex-start",
203
+ minHeight: 88,
204
+ },
205
+ highlightMeta: {
206
+ display: "flex",
207
+ flexDirection: "column",
208
+ gap: 3,
209
+ alignItems: "flex-start",
210
+ },
211
+ highlightAnte: {
212
+ fontSize: 10,
213
+ color: "#a855f7",
214
+ fontWeight: 700,
215
+ textTransform: "uppercase",
216
+ letterSpacing: "0.08em",
217
+ },
218
+ highlightLabel: {
219
+ fontSize: 12,
220
+ color: "#ececff",
221
+ fontWeight: 700,
222
+ lineHeight: 1.2,
223
+ textAlign: "left",
224
+ },
225
+ highlightDescription: {
226
+ fontSize: 11,
227
+ color: "#8e8eb6",
228
+ lineHeight: 1.2,
229
+ textAlign: "left",
230
+ },
231
+ navBar: {
232
+ display: "flex",
233
+ alignItems: "center",
234
+ justifyContent: "center",
235
+ gap: 12,
236
+ padding: "8px 16px",
237
+ background: "#0f0f22",
238
+ borderBottom: "1px solid #1a1a34",
239
+ },
240
+ navButton: {
241
+ background: "none",
242
+ border: "1px solid #2a2a55",
243
+ borderRadius: 6,
244
+ color: "#9898c0",
245
+ fontSize: 14,
246
+ padding: "4px 10px",
247
+ cursor: "pointer",
248
+ },
249
+ navLabel: {
250
+ fontSize: 12,
251
+ fontWeight: 700,
252
+ color: "#a855f7",
253
+ textTransform: "uppercase",
254
+ letterSpacing: "0.08em",
255
+ minWidth: 130,
256
+ textAlign: "center",
257
+ },
258
+ navSubLabel: {
259
+ color: "#5a5a88",
260
+ fontWeight: 500,
261
+ textTransform: "none",
262
+ letterSpacing: "normal",
263
+ fontSize: 11,
264
+ marginLeft: 4,
265
+ },
266
+ scrollRegion: {
267
+ flex: 1,
268
+ overflowY: "auto",
269
+ overflowX: "hidden",
270
+ scrollSnapType: "y mandatory",
271
+ scrollBehavior: "smooth",
272
+ },
273
+ antePage: {
274
+ scrollSnapAlign: "start",
275
+ scrollSnapStop: "always",
276
+ minHeight: "100%",
277
+ boxSizing: "border-box",
278
+ paddingBottom: 16,
279
+ borderBottom: "1px solid #1a1a34",
280
+ },
281
+ anteSection: {
282
+ paddingBottom: 8,
283
+ },
284
+ anteHeader: {
285
+ display: "flex",
286
+ alignItems: "center",
287
+ gap: 12,
288
+ padding: "8px 16px",
289
+ },
290
+ anteHeading: {
291
+ fontSize: 12,
292
+ fontWeight: 700,
293
+ color: "#a855f7",
294
+ textTransform: "uppercase",
295
+ letterSpacing: "0.1em",
296
+ },
297
+ bossRow: {
298
+ display: "flex",
299
+ alignItems: "center",
300
+ gap: 6,
301
+ },
302
+ bossName: {
303
+ fontSize: 13,
304
+ color: "#e84040",
305
+ fontWeight: 600,
306
+ },
307
+ row: {
308
+ padding: "4px 16px 8px",
309
+ },
310
+ rowLabel: {
311
+ fontSize: 10,
312
+ color: "#5a5a88",
313
+ textTransform: "uppercase",
314
+ letterSpacing: "0.07em",
315
+ marginBottom: 6,
316
+ },
317
+ cardFlow: {
318
+ display: "flex",
319
+ flexWrap: "wrap",
320
+ gap: 8,
321
+ alignItems: "flex-start",
322
+ },
323
+ denseGrid: {
324
+ display: "grid",
325
+ gridTemplateColumns: "repeat(auto-fit, minmax(82px, 1fr))",
326
+ gap: 8,
327
+ alignItems: "start",
328
+ },
329
+ compactCard: {
330
+ display: "flex",
331
+ flexDirection: "column",
332
+ alignItems: "center",
333
+ gap: 4,
334
+ minWidth: 62,
335
+ },
336
+ compactLabel: {
337
+ padding: "2px 6px",
338
+ borderRadius: 999,
339
+ background: "#18182e",
340
+ color: "#7f7fa7",
341
+ border: "1px solid #2a2a55",
342
+ fontSize: 9,
343
+ fontWeight: 700,
344
+ textTransform: "uppercase",
345
+ letterSpacing: "0.05em",
346
+ },
347
+ itemCard: {
348
+ display: "flex",
349
+ flexDirection: "column",
350
+ alignItems: "center",
351
+ gap: 4,
352
+ width: "100%",
353
+ minWidth: 0,
354
+ },
355
+ itemCardDesired: {
356
+ display: "flex",
357
+ flexDirection: "column",
358
+ alignItems: "center",
359
+ gap: 4,
360
+ width: "100%",
361
+ minWidth: 0,
362
+ padding: "4px 4px 6px",
363
+ borderRadius: 10,
364
+ border: "1px solid rgba(245,200,66,0.4)",
365
+ background: "rgba(245,200,66,0.08)",
366
+ },
367
+ itemText: {
368
+ fontSize: 10,
369
+ color: "#8e8eb6",
370
+ textAlign: "center",
371
+ lineHeight: 1.2,
372
+ maxWidth: 84,
373
+ overflow: "hidden",
374
+ textOverflow: "ellipsis",
375
+ whiteSpace: "nowrap",
376
+ },
377
+ itemDetail: {
378
+ fontSize: 10,
379
+ color: "#d8d8ea",
380
+ textAlign: "center",
381
+ lineHeight: 1.2,
382
+ },
383
+ badgeRow: {
384
+ display: "flex",
385
+ flexWrap: "wrap",
386
+ gap: 4,
387
+ justifyContent: "center",
388
+ },
389
+ badge: {
390
+ padding: "2px 6px",
391
+ borderRadius: 999,
392
+ background: "#202043",
393
+ color: "#c7c7ef",
394
+ border: "1px solid #35356d",
395
+ fontSize: 9,
396
+ fontWeight: 700,
397
+ textTransform: "uppercase",
398
+ letterSpacing: "0.05em",
399
+ },
400
+ badgeAccent: {
401
+ padding: "2px 6px",
402
+ borderRadius: 999,
403
+ background: "rgba(245,200,66,0.2)",
404
+ color: "#f5c842",
405
+ border: "1px solid rgba(245,200,66,0.35)",
406
+ fontSize: 9,
407
+ fontWeight: 700,
408
+ textTransform: "uppercase",
409
+ letterSpacing: "0.05em",
410
+ },
411
+ badgeMuted: {
412
+ padding: "2px 6px",
413
+ borderRadius: 999,
414
+ background: "#18182e",
415
+ color: "#7f7fa7",
416
+ border: "1px solid #2a2a55",
417
+ fontSize: 9,
418
+ fontWeight: 700,
419
+ textTransform: "uppercase",
420
+ letterSpacing: "0.05em",
421
+ },
422
+ packChip: {
423
+ fontSize: 12,
424
+ color: "#5a5a88",
425
+ background: "#131326",
426
+ border: "1px solid #2a2a55",
427
+ borderRadius: 6,
428
+ padding: "4px 8px",
429
+ },
430
+ factCard: {
431
+ display: "flex",
432
+ flexDirection: "column",
433
+ gap: 2,
434
+ minWidth: 110,
435
+ padding: "8px 10px",
436
+ borderRadius: 10,
437
+ background: "#131326",
438
+ border: "1px solid #2a2a55",
439
+ },
440
+ factLabel: {
441
+ fontSize: 10,
442
+ color: "#7f7fa7",
443
+ textTransform: "uppercase",
444
+ letterSpacing: "0.07em",
445
+ },
446
+ factValue: {
447
+ fontSize: 12,
448
+ color: "#ececff",
449
+ fontWeight: 700,
450
+ },
451
+ };
@@ -13,5 +13,7 @@ export interface JamlIdeProps {
13
13
  title?: string;
14
14
  actions?: React.ReactNode;
15
15
  codePlaceholder?: string;
16
+ onSearch?: () => void;
17
+ isSearching?: boolean;
16
18
  }
17
- export declare function JamlIde({ jaml, onChange, defaultMode, searchResults, className, title, actions, codePlaceholder, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function JamlIde({ jaml, onChange, defaultMode, searchResults, className, title, actions, codePlaceholder, onSearch, isSearching, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
@@ -25,7 +25,7 @@ function ResultsView({ results }) {
25
25
  padding: "10px 12px",
26
26
  }, children: [_jsx("div", { style: { fontWeight: 700, letterSpacing: 0.4 }, children: result.seed }), _jsx("div", { style: { fontSize: 12, opacity: 0.7 }, children: result.score !== undefined ? result.score : "-" })] }, `${result.seed}-${index}`))) }));
27
27
  }
28
- export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults = [], className = "", title = "JAML IDE", actions, codePlaceholder = "Enter JAML...", }) {
28
+ export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults = [], className = "", title = "JAML IDE", actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, }) {
29
29
  const [mode, setMode] = useState(defaultMode);
30
30
  const results = useMemo(() => searchResults, [searchResults]);
31
31
  return (_jsxs("div", { className: className, style: {
@@ -45,7 +45,7 @@ export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults =
45
45
  padding: "12px 14px",
46
46
  borderBottom: "1px solid rgba(255,255,255,0.08)",
47
47
  background: "rgba(255,255,255,0.03)",
48
- }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: 800 }, children: title }), _jsx("div", { style: { fontSize: 11, opacity: 0.66 }, children: "Reusable JAML authoring and preview surface." })] }), actions ? _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: actions }) : null] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: [mode === "code" ? (_jsx("textarea", { title: "JAML IDE Editor", value: jaml, onChange: (event) => onChange(event.target.value), placeholder: codePlaceholder, spellCheck: false, autoCapitalize: "off", autoCorrect: "off", style: {
48
+ }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: 800 }, children: title }), _jsx("div", { style: { fontSize: 11, opacity: 0.66 }, children: "Reusable JAML authoring and preview surface." })] }), actions ? _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: actions }) : null] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length, onSearch: onSearch, isSearching: isSearching }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: [mode === "code" ? (_jsx("textarea", { title: "JAML IDE Editor", value: jaml, onChange: (event) => onChange(event.target.value), placeholder: codePlaceholder, spellCheck: false, autoCapitalize: "off", autoCorrect: "off", style: {
49
49
  width: "100%",
50
50
  minHeight: 320,
51
51
  resize: "vertical",
@@ -4,5 +4,7 @@ export interface JamlIdeToolbarProps {
4
4
  onModeChange: (mode: JamlIdeMode) => void;
5
5
  resultCount?: number;
6
6
  className?: string;
7
+ onSearch?: () => void;
8
+ isSearching?: boolean;
7
9
  }
8
- export declare function JamlIdeToolbar({ mode, onModeChange, resultCount, className }: JamlIdeToolbarProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function JamlIdeToolbar({ mode, onModeChange, resultCount, className, onSearch, isSearching }: JamlIdeToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -5,8 +5,8 @@ const TABS = [
5
5
  { id: "map", label: "Map" },
6
6
  { id: "results", label: "Results" },
7
7
  ];
8
- export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className = "" }) {
9
- return (_jsx("div", { className: className, style: {
8
+ export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className = "", onSearch, isSearching = false }) {
9
+ return (_jsxs("div", { className: className, style: {
10
10
  display: "flex",
11
11
  alignItems: "center",
12
12
  justifyContent: "space-between",
@@ -14,23 +14,32 @@ export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className
14
14
  padding: "8px 10px",
15
15
  borderBottom: "1px solid rgba(255,255,255,0.08)",
16
16
  background: "rgba(255,255,255,0.04)",
17
- }, children: _jsx("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: TABS.map((tab) => {
18
- const selected = mode === tab.id;
19
- return (_jsxs("button", { type: "button", onClick: () => onModeChange(tab.id), style: {
20
- cursor: "pointer",
21
- borderRadius: 8,
22
- border: selected ? "1px solid rgba(247,185,85,0.55)" : "1px solid transparent",
23
- background: selected ? "rgba(247,185,85,0.16)" : "transparent",
24
- color: selected ? "#f7b955" : "rgba(255,255,255,0.58)",
25
- padding: "6px 10px",
26
- fontSize: 11,
27
- fontWeight: 600,
28
- }, children: [tab.label, tab.id === "results" && resultCount > 0 ? (_jsx("span", { style: {
29
- marginLeft: 6,
30
- borderRadius: 999,
31
- background: "rgba(0,0,0,0.25)",
32
- padding: "1px 6px",
33
- fontSize: 10,
34
- }, children: resultCount })) : null] }, tab.id));
35
- }) }) }));
17
+ }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: TABS.map((tab) => {
18
+ const selected = mode === tab.id;
19
+ return (_jsxs("button", { type: "button", onClick: () => onModeChange(tab.id), style: {
20
+ cursor: "pointer",
21
+ borderRadius: 8,
22
+ border: selected ? "1px solid rgba(247,185,85,0.55)" : "1px solid transparent",
23
+ background: selected ? "rgba(247,185,85,0.16)" : "transparent",
24
+ color: selected ? "#f7b955" : "rgba(255,255,255,0.58)",
25
+ padding: "6px 10px",
26
+ fontSize: 11,
27
+ fontWeight: 600,
28
+ }, children: [tab.label, tab.id === "results" && resultCount > 0 ? (_jsx("span", { style: {
29
+ marginLeft: 6,
30
+ borderRadius: 999,
31
+ background: "rgba(0,0,0,0.25)",
32
+ padding: "1px 6px",
33
+ fontSize: 10,
34
+ }, children: resultCount })) : null] }, tab.id));
35
+ }) }), onSearch ? (_jsx("button", { type: "button", onClick: onSearch, disabled: isSearching, style: {
36
+ cursor: isSearching ? "not-allowed" : "pointer",
37
+ borderRadius: 8,
38
+ border: "1px solid rgba(74,222,128,0.4)",
39
+ background: isSearching ? "rgba(239,68,68,0.2)" : "rgba(74,222,128,0.15)",
40
+ color: isSearching ? "#ef4444" : "#4ade80",
41
+ padding: "6px 14px",
42
+ fontSize: 11,
43
+ fontWeight: 700,
44
+ }, children: isSearching ? "Stop" : "Search" })) : null] }));
36
45
  }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { JAML_ASSET_FILES, clearJamlAssetBaseUrl, getDefaultJamlAssetUrlMap, res
2
2
  export { Layer, type LayerOptions } from "./render/Layer.js";
3
3
  export { JamlCardRenderer, type JamlCardRendererProps } from "./render/CanvasRenderer.js";
4
4
  export { JamlGameCard, JamlVoucher, JamlTag, JamlBoss, resolveAnalyzerShopItem, type JamlGameCardProps, type AnalyzerShopItem, type AnalyzerResolvedItem, } from "./components/GameCard.js";
5
+ export { AnalyzerExplorer, type AnalyzerAnteView, type AnalyzerBadge, type AnalyzerExplorerProps, type AnalyzerFact, type AnalyzerHighlight, type AnalyzerItem, } from "./components/AnalyzerExplorer.js";
5
6
  export { JamlMapPreview, type JamlMapPreviewProps } from "./components/JamlMapPreview.js";
6
7
  export { JamlIde, type JamlIdeProps, type JamlIdeSearchResult, } from "./components/JamlIde.js";
7
8
  export { JamlIdeToolbar, type JamlIdeMode, type JamlIdeToolbarProps, } from "./components/JamlIdeToolbar.js";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { JAML_ASSET_FILES, clearJamlAssetBaseUrl, getDefaultJamlAssetUrlMap, res
3
3
  export { Layer } from "./render/Layer.js";
4
4
  export { JamlCardRenderer } from "./render/CanvasRenderer.js";
5
5
  export { JamlGameCard, JamlVoucher, JamlTag, JamlBoss, resolveAnalyzerShopItem, } from "./components/GameCard.js";
6
+ export { AnalyzerExplorer, } from "./components/AnalyzerExplorer.js";
6
7
  export { JamlMapPreview } from "./components/JamlMapPreview.js";
7
8
  export { JamlIde, } from "./components/JamlIde.js";
8
9
  export { JamlIdeToolbar, } from "./components/JamlIdeToolbar.js";
package/dist/motely.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { decodeMotelyItem, decodeMotelyItemToJamlCard, motelyItemTypeName, motelyItemCategory, motelyItemDisplayName, motelyItemRenderCategory, motelyItemEditionName, motelyItemSealName, motelyItemEnhancementName, motelyPlayingCardRankName, motelyPlayingCardSuitName, decodeMotelyItemName, resolveMotelyItemType, warmMotelyItemCache, motelyItemCacheSize, type DecodedMotelyItem, type MotelyItemInput, type MotelyJamlCard, type MotelyRenderableCategory, type MotelyRuntimeItem, } from "./decode/motelyItemDecoder.js";
2
+ export { MOTELY_DISPLAY_SCHEMA, motelyBossDisplayName, motelyBossDisplayNameFromKey, motelyBossKeyFromDisplayName, motelyBoosterPackDisplayName, motelyBoosterPackDisplayNameFromKey, motelyBoosterPackKeyFromDisplayName, motelyItemDisplayNameFromKey, motelyItemDisplayNameFromValue, motelyTagDisplayName, motelyTagDisplayNameFromKey, motelyTagKeyFromDisplayName, motelyVoucherDisplayName, motelyVoucherDisplayNameFromKey, motelyVoucherKeyFromDisplayName, type MotelyBoosterPackKey, type MotelyBossKey, type MotelyDisplaySchema, type MotelyTagKey, type MotelyVoucherKey, } from "./motelyDisplay.js";
package/dist/motely.js CHANGED
@@ -1,2 +1,3 @@
1
1
  "use client";
2
2
  export { decodeMotelyItem, decodeMotelyItemToJamlCard, motelyItemTypeName, motelyItemCategory, motelyItemDisplayName, motelyItemRenderCategory, motelyItemEditionName, motelyItemSealName, motelyItemEnhancementName, motelyPlayingCardRankName, motelyPlayingCardSuitName, decodeMotelyItemName, resolveMotelyItemType, warmMotelyItemCache, motelyItemCacheSize, } from "./decode/motelyItemDecoder.js";
3
+ export { MOTELY_DISPLAY_SCHEMA, motelyBossDisplayName, motelyBossDisplayNameFromKey, motelyBossKeyFromDisplayName, motelyBoosterPackDisplayName, motelyBoosterPackDisplayNameFromKey, motelyBoosterPackKeyFromDisplayName, motelyItemDisplayNameFromKey, motelyItemDisplayNameFromValue, motelyTagDisplayName, motelyTagDisplayNameFromKey, motelyTagKeyFromDisplayName, motelyVoucherDisplayName, motelyVoucherDisplayNameFromKey, motelyVoucherKeyFromDisplayName, } from "./motelyDisplay.js";