jaml-ui 0.13.1 → 0.14.1
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/assets/Balatro Seed Curator (DesignsV2)/Assets/Decks/playing_cards_metadata.json +230 -55
- package/dist/components/AnalyzerExplorer.js +75 -90
- package/dist/components/CardFan.js +2 -2
- package/dist/components/GameCard.js +9 -9
- package/dist/components/JamlAestheticSelector.d.ts +9 -0
- package/dist/components/JamlAestheticSelector.js +36 -0
- package/dist/components/JamlIde.d.ts +2 -1
- package/dist/components/JamlIde.js +6 -4
- package/dist/components/JamlIdeToolbar.js +1 -0
- package/dist/components/JamlSeedInput.d.ts +9 -0
- package/dist/components/JamlSeedInput.js +30 -0
- package/dist/components/JamlSpeedometer.d.ts +11 -0
- package/dist/components/JamlSpeedometer.js +54 -0
- package/dist/components/Standardcard.d.ts +18 -0
- package/dist/components/Standardcard.js +80 -0
- package/dist/decode/motelyItemDecoder.d.ts +3 -3
- package/dist/decode/motelyItemDecoder.js +12 -12
- package/dist/decode/packedBalatroItem.d.ts +1 -1
- package/dist/decode/packedBalatroItem.js +2 -2
- package/dist/hooks/searchWorkerCode.d.ts +1 -1
- package/dist/hooks/searchWorkerCode.js +31 -5
- package/dist/hooks/useSearch.d.ts +9 -0
- package/dist/hooks/useSearch.js +73 -17
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/motely.d.ts +1 -1
- package/dist/motely.js +1 -1
- package/dist/ui/jimboTabs.js +1 -1
- package/dist/utils/fileSystem.d.ts +1 -0
- package/dist/utils/fileSystem.js +23 -0
- package/dist/utils/itemUtils.d.ts +1 -1
- package/dist/utils/itemUtils.js +3 -3
- package/package.json +4 -3
|
@@ -28,7 +28,7 @@ function normalizeCardSuit(raw) {
|
|
|
28
28
|
return "Spades";
|
|
29
29
|
return raw.trim();
|
|
30
30
|
}
|
|
31
|
-
function
|
|
31
|
+
function parseStandardcardName(name) {
|
|
32
32
|
const trimmed = name.trim();
|
|
33
33
|
const ofMatch = /^(A|K|Q|J|10|[2-9]|Ace|King|Queen|Jack)\s+of\s+(Hearts|Clubs|Diamonds|Spades)$/i.exec(trimmed);
|
|
34
34
|
if (ofMatch) {
|
|
@@ -137,15 +137,15 @@ function resolvePackedAnalyzerItem(item, scale) {
|
|
|
137
137
|
return { kind: "voucher", voucherName: baseName };
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
140
|
+
const standardcard = parseStandardcardName(displayName) ?? parseStandardcardName(baseName);
|
|
141
|
+
if (standardcard) {
|
|
142
142
|
return {
|
|
143
143
|
kind: "playing",
|
|
144
144
|
type: "playing",
|
|
145
145
|
card: {
|
|
146
146
|
name: displayName,
|
|
147
|
-
rank:
|
|
148
|
-
suit:
|
|
147
|
+
rank: standardcard.rank,
|
|
148
|
+
suit: standardcard.suit,
|
|
149
149
|
scale,
|
|
150
150
|
},
|
|
151
151
|
};
|
|
@@ -182,15 +182,15 @@ export function resolveAnalyzerShopItem(item, scale = 1) {
|
|
|
182
182
|
return { kind: "voucher", voucherName: baseName };
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
185
|
+
const standardcard = parseStandardcardName(displayName) ?? parseStandardcardName(baseName);
|
|
186
|
+
if (standardcard) {
|
|
187
187
|
return {
|
|
188
188
|
kind: "playing",
|
|
189
189
|
type: "playing",
|
|
190
190
|
card: {
|
|
191
191
|
name: displayName,
|
|
192
|
-
rank:
|
|
193
|
-
suit:
|
|
192
|
+
rank: standardcard.rank,
|
|
193
|
+
suit: standardcard.suit,
|
|
194
194
|
scale,
|
|
195
195
|
},
|
|
196
196
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type JamlAestheticOption = "Palindrome" | "Psychosis" | "Gross" | "Nsfw" | "Funny" | "Balatro";
|
|
3
|
+
export interface JamlAestheticSelectorProps {
|
|
4
|
+
value?: JamlAestheticOption | null;
|
|
5
|
+
onChange: (aesthetic: JamlAestheticOption | null, numericValue: number) => void;
|
|
6
|
+
className?: string;
|
|
7
|
+
style?: React.CSSProperties;
|
|
8
|
+
}
|
|
9
|
+
export declare function JamlAestheticSelector({ value, onChange, className, style }: JamlAestheticSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { JimboColorOption } from "../ui/tokens.js";
|
|
4
|
+
import { JimboText } from "../ui/jimboText.js";
|
|
5
|
+
const AESTHETICS = [
|
|
6
|
+
{ id: "Palindrome", value: 0, label: "Palindrome", desc: "Seeds that read the same forwards and backwards" },
|
|
7
|
+
{ id: "Psychosis", value: 1, label: "Psychosis", desc: "Unsettling or eerie seed patterns" },
|
|
8
|
+
{ id: "Gross", value: 2, label: "Gross", desc: "Seeds with crude or disgusting words" },
|
|
9
|
+
{ id: "Nsfw", value: 3, label: "NSFW", desc: "Seeds with adult content" },
|
|
10
|
+
{ id: "Funny", value: 4, label: "Funny", desc: "Seeds that spell funny words" },
|
|
11
|
+
{ id: "Balatro", value: 5, label: "Balatro", desc: "Seeds referencing the game itself" },
|
|
12
|
+
];
|
|
13
|
+
export function JamlAestheticSelector({ value, onChange, className, style }) {
|
|
14
|
+
return (_jsxs("div", { className: className, style: {
|
|
15
|
+
display: "flex",
|
|
16
|
+
flexDirection: "column",
|
|
17
|
+
gap: 4,
|
|
18
|
+
...style,
|
|
19
|
+
}, children: [_jsx(JimboText, { size: "xs", tone: "grey", uppercase: true, children: "Seed Aesthetics" }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4 }, children: AESTHETICS.map((a) => {
|
|
20
|
+
const isActive = value === a.id;
|
|
21
|
+
return (_jsx("button", { type: "button", onClick: () => onChange(isActive ? null : a.id, a.value), title: a.desc, style: {
|
|
22
|
+
padding: "4px 10px",
|
|
23
|
+
borderRadius: 6,
|
|
24
|
+
border: `2px solid ${isActive ? JimboColorOption.GOLD : JimboColorOption.PANEL_EDGE}`,
|
|
25
|
+
background: isActive ? `${JimboColorOption.GOLD}22` : JimboColorOption.DARKEST,
|
|
26
|
+
color: isActive ? JimboColorOption.GOLD_TEXT : JimboColorOption.GREY,
|
|
27
|
+
cursor: "pointer",
|
|
28
|
+
fontSize: 11,
|
|
29
|
+
fontWeight: 700,
|
|
30
|
+
fontFamily: "m6x11plus, monospace",
|
|
31
|
+
textTransform: "uppercase",
|
|
32
|
+
letterSpacing: 0.5,
|
|
33
|
+
transition: "border-color 100ms, background 100ms",
|
|
34
|
+
}, children: a.label }, a.id));
|
|
35
|
+
}) })] }));
|
|
36
|
+
}
|
|
@@ -17,6 +17,7 @@ export interface JamlIdeProps {
|
|
|
17
17
|
defaultMode?: JamlIdeMode;
|
|
18
18
|
searchResults?: JamlIdeSearchResult[];
|
|
19
19
|
className?: string;
|
|
20
|
+
style?: React.CSSProperties;
|
|
20
21
|
title?: string;
|
|
21
22
|
actions?: React.ReactNode;
|
|
22
23
|
codePlaceholder?: string;
|
|
@@ -31,4 +32,4 @@ export interface JamlIdeProps {
|
|
|
31
32
|
}
|
|
32
33
|
export type { JamlVisualFilter } from "./JamlIdeVisual.js";
|
|
33
34
|
export type { JamlVisualClause, JamlZone } from "./JamlIdeVisual.js";
|
|
34
|
-
export declare function JamlIde({ jaml, defaultJaml, onChange, defaultMode, searchResults, className, title, actions, codePlaceholder, onSearch, isSearching, visualFilter, onVisualFilterChange, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare function JamlIde({ jaml, defaultJaml, onChange, defaultMode, searchResults, className, style, title, actions, codePlaceholder, onSearch, isSearching, visualFilter, onVisualFilterChange, }: JamlIdeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -54,7 +54,7 @@ function ResultsView({ results }) {
|
|
|
54
54
|
textAlign: "left",
|
|
55
55
|
}, children: [_jsx("span", { style: {
|
|
56
56
|
fontFamily: "m6x11plus, monospace",
|
|
57
|
-
fontWeight:
|
|
57
|
+
fontWeight: "normal",
|
|
58
58
|
fontSize: 14,
|
|
59
59
|
letterSpacing: 1,
|
|
60
60
|
color: JimboColorOption.GOLD_TEXT,
|
|
@@ -65,7 +65,7 @@ function ResultsView({ results }) {
|
|
|
65
65
|
color: result.score > 0 ? JimboColorOption.GREEN_TEXT : JimboColorOption.GREY,
|
|
66
66
|
minWidth: 36,
|
|
67
67
|
textAlign: "right",
|
|
68
|
-
}, children: result.score })] })) : null, hasTally ? (_jsx("span", { style: { fontSize:
|
|
68
|
+
}, children: result.score })] })) : null, hasTally ? (_jsx("span", { style: { fontSize: 11, color: JimboColorOption.GREY, marginLeft: 2 }, children: isOpen ? "▲" : "▼" })) : null] }), isOpen && hasTally ? (_jsx("div", { style: {
|
|
69
69
|
borderTop: `1px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
70
70
|
padding: "8px 12px 10px",
|
|
71
71
|
display: "flex",
|
|
@@ -91,7 +91,7 @@ function ResultsView({ results }) {
|
|
|
91
91
|
}) })) : null] }, result.seed));
|
|
92
92
|
}) }));
|
|
93
93
|
}
|
|
94
|
-
export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", searchResults = [], className = "", title = "JAML IDE", actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, visualFilter, onVisualFilterChange, }) {
|
|
94
|
+
export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", searchResults = [], className = "", style, title = "JAML IDE", actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, visualFilter, onVisualFilterChange, }) {
|
|
95
95
|
const [mode, setMode] = useState(defaultMode);
|
|
96
96
|
const [internalText, setInternalText] = useState(jaml ?? defaultJaml ?? "");
|
|
97
97
|
const [lastJamlProp, setLastJamlProp] = useState(jaml);
|
|
@@ -151,13 +151,15 @@ export function JamlIde({ jaml, defaultJaml, onChange, defaultMode = "code", sea
|
|
|
151
151
|
boxShadow: `0 3px 0 0 ${JimboColorOption.BORDER_SOUTH}`,
|
|
152
152
|
background: JimboColorOption.DARK_GREY,
|
|
153
153
|
color: JimboColorOption.WHITE,
|
|
154
|
+
...style,
|
|
154
155
|
}, children: [_jsxs("div", { style: {
|
|
155
156
|
display: "flex",
|
|
156
157
|
alignItems: "center",
|
|
157
158
|
justifyContent: "space-between",
|
|
159
|
+
flexWrap: "wrap",
|
|
158
160
|
gap: 12,
|
|
159
161
|
padding: "10px 14px",
|
|
160
162
|
borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
161
163
|
background: JimboColorOption.TEAL_GREY,
|
|
162
|
-
}, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize:
|
|
164
|
+
}, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: "normal", fontFamily: "m6x11plus, monospace", color: JimboColorOption.GOLD_TEXT }, children: title }), _jsx("div", { style: { fontSize: 11, color: JimboColorOption.GREY }, children: "Jimbo's Ante Markup Language" })] }), 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", background: JimboColorOption.DARKEST }, children: [mode === "visual" ? (_jsx(JamlIdeVisual, { filter: activeFilter, onChange: handleVisualFilterChange })) : null, mode === "code" ? (_jsx(JamlCodeEditor, { value: text, onChange: handleTextChange, placeholder: codePlaceholder })) : null, mode === "map" ? _jsx(JamlMapPreview, { jaml: text }) : null, mode === "results" ? (_jsx("div", { style: { padding: 12 }, children: _jsx(ResultsView, { results: results }) })) : null] })] }));
|
|
163
165
|
}
|
|
@@ -14,6 +14,7 @@ export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className
|
|
|
14
14
|
display: "flex",
|
|
15
15
|
alignItems: "center",
|
|
16
16
|
justifyContent: "space-between",
|
|
17
|
+
flexWrap: "wrap",
|
|
17
18
|
gap: 8,
|
|
18
19
|
padding: "10px 10px 6px",
|
|
19
20
|
borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface JamlSeedInputProps {
|
|
3
|
+
value?: string;
|
|
4
|
+
onChange?: (seed: string) => void;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
style?: React.CSSProperties;
|
|
8
|
+
}
|
|
9
|
+
export declare function JamlSeedInput({ value, onChange, placeholder, className, style }: JamlSeedInputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { JimboColorOption } from "../ui/tokens.js";
|
|
5
|
+
import { JimboText } from "../ui/jimboText.js";
|
|
6
|
+
const SEED_PATTERN = /^[A-Z0-9]{0,8}$/;
|
|
7
|
+
export function JamlSeedInput({ value, onChange, placeholder = "Enter seed (e.g. J4SPZMWW)", className, style }) {
|
|
8
|
+
const [internal, setInternal] = useState(value ?? "");
|
|
9
|
+
const display = value ?? internal;
|
|
10
|
+
const isValid = display.length === 0 || SEED_PATTERN.test(display);
|
|
11
|
+
const handleChange = (e) => {
|
|
12
|
+
const raw = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 8);
|
|
13
|
+
setInternal(raw);
|
|
14
|
+
onChange?.(raw);
|
|
15
|
+
};
|
|
16
|
+
return (_jsxs("div", { className: className, style: { display: "flex", flexDirection: "column", gap: 4, ...style }, children: [_jsx(JimboText, { size: "xs", tone: "grey", uppercase: true, children: "Seed" }), _jsx("input", { type: "text", value: display, onChange: handleChange, placeholder: placeholder, maxLength: 8, spellCheck: false, autoComplete: "off", style: {
|
|
17
|
+
padding: "6px 10px",
|
|
18
|
+
borderRadius: 6,
|
|
19
|
+
border: `2px solid ${!isValid ? JimboColorOption.RED : display.length === 8 ? JimboColorOption.GREEN : JimboColorOption.PANEL_EDGE}`,
|
|
20
|
+
background: JimboColorOption.DARKEST,
|
|
21
|
+
color: JimboColorOption.GOLD_TEXT,
|
|
22
|
+
fontSize: 16,
|
|
23
|
+
fontWeight: 900,
|
|
24
|
+
fontFamily: "m6x11plus, monospace",
|
|
25
|
+
letterSpacing: 2,
|
|
26
|
+
textTransform: "uppercase",
|
|
27
|
+
outline: "none",
|
|
28
|
+
transition: "border-color 100ms",
|
|
29
|
+
} }), display.length > 0 && display.length < 8 && (_jsxs(JimboText, { size: "xs", tone: "grey", children: [8 - display.length, " more characters"] }))] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { SearchStatus } from "../hooks/useSearch.js";
|
|
3
|
+
export interface JamlSpeedometerProps {
|
|
4
|
+
seedsPerSecond: number;
|
|
5
|
+
totalSearched: bigint;
|
|
6
|
+
matchingSeeds: bigint;
|
|
7
|
+
status: SearchStatus;
|
|
8
|
+
className?: string;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
}
|
|
11
|
+
export declare function JamlSpeedometer({ seedsPerSecond, totalSearched, matchingSeeds, status, className, style, }: JamlSpeedometerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { JimboColorOption } from "../ui/tokens.js";
|
|
4
|
+
import { JimboText } from "../ui/jimboText.js";
|
|
5
|
+
function formatCount(n) {
|
|
6
|
+
if (n >= 1000000000n)
|
|
7
|
+
return `${(Number(n / 1000000n) / 1000).toFixed(1)}B`;
|
|
8
|
+
if (n >= 1000000n)
|
|
9
|
+
return `${(Number(n / 1000n) / 1000).toFixed(1)}M`;
|
|
10
|
+
if (n >= 1000n)
|
|
11
|
+
return `${(Number(n) / 1000).toFixed(1)}K`;
|
|
12
|
+
return n.toString();
|
|
13
|
+
}
|
|
14
|
+
function formatSpeed(sps) {
|
|
15
|
+
if (sps >= 1_000_000)
|
|
16
|
+
return `${(sps / 1_000_000).toFixed(1)}M`;
|
|
17
|
+
if (sps >= 1_000)
|
|
18
|
+
return `${(sps / 1_000).toFixed(0)}K`;
|
|
19
|
+
return sps.toString();
|
|
20
|
+
}
|
|
21
|
+
function needleAngle(sps) {
|
|
22
|
+
if (sps <= 0)
|
|
23
|
+
return -90;
|
|
24
|
+
const maxLog = Math.log10(5_000_000);
|
|
25
|
+
const clamped = Math.min(sps, 5_000_000);
|
|
26
|
+
const pct = Math.log10(Math.max(clamped, 1)) / maxLog;
|
|
27
|
+
return -90 + pct * 180;
|
|
28
|
+
}
|
|
29
|
+
export function JamlSpeedometer({ seedsPerSecond, totalSearched, matchingSeeds, status, className, style, }) {
|
|
30
|
+
const isActive = status === "running" || status === "booting";
|
|
31
|
+
const angle = needleAngle(seedsPerSecond);
|
|
32
|
+
const speedColor = seedsPerSecond >= 500_000
|
|
33
|
+
? JimboColorOption.GREEN
|
|
34
|
+
: seedsPerSecond >= 100_000
|
|
35
|
+
? JimboColorOption.GOLD
|
|
36
|
+
: seedsPerSecond > 0
|
|
37
|
+
? JimboColorOption.ORANGE
|
|
38
|
+
: JimboColorOption.GREY;
|
|
39
|
+
return (_jsxs("div", { className: className, style: {
|
|
40
|
+
display: "flex",
|
|
41
|
+
flexDirection: "column",
|
|
42
|
+
alignItems: "center",
|
|
43
|
+
gap: 6,
|
|
44
|
+
padding: "12px 16px",
|
|
45
|
+
borderRadius: 10,
|
|
46
|
+
background: `${JimboColorOption.DARKEST}cc`,
|
|
47
|
+
border: `1px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
48
|
+
...style,
|
|
49
|
+
}, children: [_jsx("div", { style: { position: "relative", width: 120, height: 68, overflow: "hidden" }, children: _jsxs("svg", { viewBox: "0 0 120 68", width: 120, height: 68, children: [_jsx("path", { d: "M 10 65 A 50 50 0 0 1 110 65", fill: "none", stroke: JimboColorOption.DARK_GREY, strokeWidth: 6, strokeLinecap: "round" }), isActive && (_jsx("path", { d: "M 10 65 A 50 50 0 0 1 110 65", fill: "none", stroke: speedColor, strokeWidth: 6, strokeLinecap: "round", strokeDasharray: "157", strokeDashoffset: 157 - (157 * ((angle + 90) / 180)), style: { transition: "stroke-dashoffset 300ms ease, stroke 300ms ease" } })), _jsx("line", { x1: 60, y1: 65, x2: 60, y2: 20, stroke: isActive ? JimboColorOption.RED : JimboColorOption.GREY, strokeWidth: 2, strokeLinecap: "round", style: {
|
|
50
|
+
transformOrigin: "60px 65px",
|
|
51
|
+
transform: `rotate(${angle}deg)`,
|
|
52
|
+
transition: "transform 300ms ease",
|
|
53
|
+
} }), _jsx("circle", { cx: 60, cy: 65, r: 4, fill: JimboColorOption.RED })] }) }), _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 20, fontWeight: 900, fontFamily: "m6x11plus, monospace", color: isActive ? speedColor : JimboColorOption.GREY }, children: isActive ? formatSpeed(seedsPerSecond) : "---" }), _jsx(JimboText, { size: "xs", tone: "grey", uppercase: true, children: "seeds / sec" })] }), _jsxs("div", { style: { display: "flex", gap: 16, marginTop: 2 }, children: [_jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 13, fontWeight: 700, fontFamily: "m6x11plus, monospace", color: JimboColorOption.WHITE }, children: formatCount(totalSearched) }), _jsx(JimboText, { size: "xs", tone: "grey", uppercase: true, children: "searched" })] }), _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("div", { style: { fontSize: 13, fontWeight: 700, fontFamily: "m6x11plus, monospace", color: JimboColorOption.GREEN_TEXT }, children: formatCount(matchingSeeds) }), _jsx(JimboText, { size: "xs", tone: "grey", uppercase: true, children: "matches" })] })] })] }));
|
|
54
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type CardSuit = 'Hearts' | 'Diamonds' | 'Clubs' | 'Spades' | 'hearts' | 'diamonds' | 'clubs' | 'spades';
|
|
3
|
+
export type CardRank = 'Ace' | 'King' | 'Queen' | 'Jack' | '10' | '9' | '8' | '7' | '6' | '5' | '4' | '3' | '2' | 'A' | 'K' | 'Q' | 'J';
|
|
4
|
+
export type CardEnhancement = 'bonus' | 'mult' | 'wild' | 'glass' | 'steel' | 'stone' | 'gold' | 'lucky' | null;
|
|
5
|
+
export type CardSeal = 'gold' | 'red' | 'blue' | 'purple' | null;
|
|
6
|
+
export type CardEdition = 'Foil' | 'Holographic' | 'Polychrome' | 'Negative' | null;
|
|
7
|
+
interface RealStandardcardProps {
|
|
8
|
+
suit: CardSuit;
|
|
9
|
+
rank: CardRank;
|
|
10
|
+
enhancement?: CardEnhancement;
|
|
11
|
+
seal?: CardSeal;
|
|
12
|
+
edition?: CardEdition;
|
|
13
|
+
className?: string;
|
|
14
|
+
size?: number;
|
|
15
|
+
style?: React.CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export declare function RealStandardcard({ suit, rank, enhancement, seal, edition, className, size, style }: RealStandardcardProps): import("react/jsx-runtime").JSX.Element | null;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { resolveJamlAssetUrl } from '../assets.js';
|
|
4
|
+
import { RANK_MAP, SUIT_MAP, ENHANCER_MAP, SEAL_MAP, EDITION_MAP } from '../sprites/spriteData.js';
|
|
5
|
+
function cn(...classes) { return classes.filter(Boolean).join(" "); }
|
|
6
|
+
const CARD_WIDTH = 71;
|
|
7
|
+
const CARD_HEIGHT = 95;
|
|
8
|
+
const RANK_ALIAS = { A: 'Ace', K: 'King', Q: 'Queen', J: 'Jack' };
|
|
9
|
+
const pascal = (s) => s[0].toUpperCase() + s.slice(1).toLowerCase();
|
|
10
|
+
export function RealStandardcard({ suit, rank, enhancement, seal, edition, className, size = 71, style }) {
|
|
11
|
+
const rankKey = RANK_ALIAS[rank] ?? rank;
|
|
12
|
+
const suitKey = pascal(suit);
|
|
13
|
+
const col = RANK_MAP[rankKey];
|
|
14
|
+
const row = SUIT_MAP[suitKey];
|
|
15
|
+
if (col === undefined || row === undefined) {
|
|
16
|
+
console.warn(`Invalid card: ${rank} of ${suit}`);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const scale = size / CARD_WIDTH;
|
|
20
|
+
const finalH = size * (CARD_HEIGHT / CARD_WIDTH);
|
|
21
|
+
// Base card position
|
|
22
|
+
const bgX = -col * CARD_WIDTH;
|
|
23
|
+
const bgY = -row * CARD_HEIGHT;
|
|
24
|
+
// Enhancement background (if any) — ENHANCER_MAP is PascalCase, prop is lowercase
|
|
25
|
+
const enhPos = enhancement ? ENHANCER_MAP[pascal(enhancement)] ?? { x: 0, y: 0 } : { x: 0, y: 0 };
|
|
26
|
+
const enhBgX = -enhPos.x * CARD_WIDTH;
|
|
27
|
+
const enhBgY = -enhPos.y * CARD_HEIGHT;
|
|
28
|
+
// Seal overlay — SEAL_MAP is keyed by "Gold"/"Red"/"Blue"/"Purple"
|
|
29
|
+
const sealPos = seal ? SEAL_MAP[pascal(seal)] ?? null : null;
|
|
30
|
+
const sealBgX = sealPos ? -sealPos.x * CARD_WIDTH : 0;
|
|
31
|
+
const sealBgY = sealPos ? -sealPos.y * CARD_HEIGHT : 0;
|
|
32
|
+
// Edition overlay — EDITION_MAP gives column index on 5-wide editions sheet
|
|
33
|
+
const editionCol = edition ? EDITION_MAP[edition] : undefined;
|
|
34
|
+
const editionBgX = editionCol !== undefined ? -editionCol * CARD_WIDTH : 0;
|
|
35
|
+
const editionBgY = 0;
|
|
36
|
+
const isNegative = edition === 'Negative';
|
|
37
|
+
const baseFilter = isNegative ? 'invert(0.94)' : 'none';
|
|
38
|
+
const enhancersUrl = resolveJamlAssetUrl('enhancers');
|
|
39
|
+
const deckUrl = resolveJamlAssetUrl('deck');
|
|
40
|
+
const editionsUrl = resolveJamlAssetUrl('editions');
|
|
41
|
+
return (_jsxs("div", { className: cn('relative overflow-hidden inline-block select-none', className), style: {
|
|
42
|
+
width: size,
|
|
43
|
+
height: finalH,
|
|
44
|
+
imageRendering: 'pixelated',
|
|
45
|
+
...style
|
|
46
|
+
}, title: `${rank} of ${suit}${enhancement ? ` (${enhancement})` : ''}${seal ? ` [${seal} seal]` : ''}${edition ? ` {${edition}}` : ''}`, children: [_jsx("div", { className: "absolute inset-0", style: {
|
|
47
|
+
backgroundImage: `url(${enhancersUrl})`,
|
|
48
|
+
backgroundPosition: `${enhBgX}px ${enhBgY}px`,
|
|
49
|
+
width: CARD_WIDTH,
|
|
50
|
+
height: CARD_HEIGHT,
|
|
51
|
+
transform: `scale(${scale})`,
|
|
52
|
+
transformOrigin: 'top left',
|
|
53
|
+
backgroundRepeat: 'no-repeat',
|
|
54
|
+
} }), _jsx("div", { className: "absolute inset-0 z-[1]", style: {
|
|
55
|
+
backgroundImage: `url(${deckUrl})`,
|
|
56
|
+
backgroundPosition: `${bgX}px ${bgY}px`,
|
|
57
|
+
width: CARD_WIDTH,
|
|
58
|
+
height: CARD_HEIGHT,
|
|
59
|
+
transform: `scale(${scale})`,
|
|
60
|
+
transformOrigin: 'top left',
|
|
61
|
+
backgroundRepeat: 'no-repeat',
|
|
62
|
+
filter: baseFilter
|
|
63
|
+
} }), edition && edition !== 'Negative' && (_jsx("div", { className: "absolute inset-0 z-[2] mix-blend-screen opacity-60", style: {
|
|
64
|
+
backgroundImage: `url(${editionsUrl})`,
|
|
65
|
+
backgroundPosition: `${editionBgX}px ${editionBgY}px`,
|
|
66
|
+
width: CARD_WIDTH,
|
|
67
|
+
height: CARD_HEIGHT,
|
|
68
|
+
transform: `scale(${scale})`,
|
|
69
|
+
transformOrigin: 'top left',
|
|
70
|
+
backgroundRepeat: 'no-repeat',
|
|
71
|
+
} })), seal && (_jsx("div", { className: "absolute inset-0 z-[3]", style: {
|
|
72
|
+
backgroundImage: `url(${enhancersUrl})`,
|
|
73
|
+
backgroundPosition: `${sealBgX}px ${sealBgY}px`,
|
|
74
|
+
width: CARD_WIDTH,
|
|
75
|
+
height: CARD_HEIGHT,
|
|
76
|
+
transform: `scale(${scale})`,
|
|
77
|
+
transformOrigin: 'top left',
|
|
78
|
+
backgroundRepeat: 'no-repeat',
|
|
79
|
+
} })), isNegative && (_jsx("div", { className: "absolute inset-0 z-[4] bg-red-500/10 pointer-events-none mix-blend-overlay" }))] }));
|
|
80
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* MotelyItem.Value is a packed integer. The MotelyItemType enum
|
|
5
5
|
* uses packed integers where the top nibble encodes category:
|
|
6
|
-
* 0x1000 =
|
|
6
|
+
* 0x1000 = Standardcard, 0x2000 = Spectral, 0x3000 = Tarot,
|
|
7
7
|
* 0x4000 = Planet, 0x5000 = Joker, 0xF000 = Invalid
|
|
8
8
|
*/
|
|
9
9
|
import { type CardCategory } from "../utils/itemUtils.js";
|
|
@@ -46,8 +46,8 @@ export declare function motelyItemRenderCategory(input: MotelyItemInput): Motely
|
|
|
46
46
|
export declare function motelyItemEditionName(input: MotelyItemInput): "Foil" | "Holographic" | "Polychrome" | "Negative" | null;
|
|
47
47
|
export declare function motelyItemSealName(input: MotelyItemInput): "Gold" | "Red" | "Blue" | "Purple" | null;
|
|
48
48
|
export declare function motelyItemEnhancementName(input: MotelyItemInput): string | null;
|
|
49
|
-
export declare function
|
|
50
|
-
export declare function
|
|
49
|
+
export declare function motelyStandardcardSuitName(input: MotelyItemInput): "Clubs" | "Diamonds" | "Hearts" | "Spades" | null;
|
|
50
|
+
export declare function motelyStandardcardRankName(input: MotelyItemInput): string | null;
|
|
51
51
|
/** Get the enum key name for a MotelyItemType value. */
|
|
52
52
|
export declare function motelyItemTypeName(input: MotelyItemInput): string;
|
|
53
53
|
/** Get the category string for a MotelyItemType value. */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* MotelyItem.Value is a packed integer. The MotelyItemType enum
|
|
5
5
|
* uses packed integers where the top nibble encodes category:
|
|
6
|
-
* 0x1000 =
|
|
6
|
+
* 0x1000 = Standardcard, 0x2000 = Spectral, 0x3000 = Tarot,
|
|
7
7
|
* 0x4000 = Planet, 0x5000 = Joker, 0xF000 = Invalid
|
|
8
8
|
*/
|
|
9
9
|
import { Motely } from "motely-wasm";
|
|
@@ -15,7 +15,7 @@ const VALUE_SEAL_MASK = 0x70000;
|
|
|
15
15
|
const VALUE_ENHANCEMENT_MASK = 0x780000;
|
|
16
16
|
const VALUE_EDITION_MASK = 0x3800000;
|
|
17
17
|
const CATEGORY_TO_TYPE = {
|
|
18
|
-
0x1000: "
|
|
18
|
+
0x1000: "Standard card",
|
|
19
19
|
0x2000: "Spectral",
|
|
20
20
|
0x3000: "Tarot",
|
|
21
21
|
0x4000: "Planet",
|
|
@@ -45,7 +45,7 @@ function runtimeEnumName(enumObject, value) {
|
|
|
45
45
|
const enumKey = enumObject[String(value)];
|
|
46
46
|
return typeof enumKey === "string" && enumKey.length > 0 ? enumKey : null;
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function parseStandardcardEnumKey(enumKey) {
|
|
49
49
|
const match = /^([CDHS])(10|[2-9JQKA])$/.exec(enumKey);
|
|
50
50
|
if (!match)
|
|
51
51
|
return null;
|
|
@@ -142,22 +142,22 @@ export function motelyItemEnhancementName(input) {
|
|
|
142
142
|
const enumKey = runtimeEnumName(Motely.MotelyItemEnhancement, resolveEnhancementValue(input));
|
|
143
143
|
return enumKey === null || enumKey === "None" ? null : enumKey;
|
|
144
144
|
}
|
|
145
|
-
export function
|
|
145
|
+
export function motelyStandardcardSuitName(input) {
|
|
146
146
|
const runtimeItem = asRuntimeItem(input);
|
|
147
|
-
const directSuit = runtimeEnumName(Motely.
|
|
147
|
+
const directSuit = runtimeEnumName(Motely.MotelyStandardcardSuit, finiteNumber(runtimeItem?.suit));
|
|
148
148
|
if (directSuit === "Clubs" || directSuit === "Diamonds" || directSuit === "Hearts" || directSuit === "Spades") {
|
|
149
149
|
return directSuit;
|
|
150
150
|
}
|
|
151
|
-
const parsed =
|
|
151
|
+
const parsed = parseStandardcardEnumKey(motelyItemTypeName(input));
|
|
152
152
|
return parsed?.suit ?? null;
|
|
153
153
|
}
|
|
154
|
-
export function
|
|
154
|
+
export function motelyStandardcardRankName(input) {
|
|
155
155
|
const runtimeItem = asRuntimeItem(input);
|
|
156
|
-
const directRank = runtimeEnumName(Motely.
|
|
156
|
+
const directRank = runtimeEnumName(Motely.MotelyStandardcardRank, finiteNumber(runtimeItem?.rank));
|
|
157
157
|
const normalizedDirect = rankNameFromEnum(directRank);
|
|
158
158
|
if (normalizedDirect !== null)
|
|
159
159
|
return normalizedDirect;
|
|
160
|
-
const parsed =
|
|
160
|
+
const parsed = parseStandardcardEnumKey(motelyItemTypeName(input));
|
|
161
161
|
return parsed?.rank ?? null;
|
|
162
162
|
}
|
|
163
163
|
/** Get the enum key name for a MotelyItemType value. */
|
|
@@ -175,7 +175,7 @@ export function motelyItemCategory(input) {
|
|
|
175
175
|
return "Unknown";
|
|
176
176
|
const renderCategory = motelyItemRenderCategory(itemType);
|
|
177
177
|
if (renderCategory === "playing")
|
|
178
|
-
return "
|
|
178
|
+
return "Standard card";
|
|
179
179
|
if (renderCategory === "spectral")
|
|
180
180
|
return "Spectral";
|
|
181
181
|
if (renderCategory === "tarot")
|
|
@@ -206,8 +206,8 @@ export function decodeMotelyItem(input) {
|
|
|
206
206
|
return null;
|
|
207
207
|
}
|
|
208
208
|
const category = motelyItemRenderCategory(itemType);
|
|
209
|
-
const rank =
|
|
210
|
-
const suit =
|
|
209
|
+
const rank = motelyStandardcardRankName(itemType);
|
|
210
|
+
const suit = motelyStandardcardSuitName(itemType);
|
|
211
211
|
base = {
|
|
212
212
|
itemType,
|
|
213
213
|
enumKey,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Bit-packed shop/card ids (Balatro item encoding). */
|
|
2
2
|
export const BalatroItemCategory = {
|
|
3
|
-
|
|
3
|
+
Standardcard: 1,
|
|
4
4
|
Spectral: 2,
|
|
5
5
|
Tarot: 3,
|
|
6
6
|
Planet: 4,
|
|
@@ -22,5 +22,5 @@ export function packedItemIndex(packed) {
|
|
|
22
22
|
}
|
|
23
23
|
export function isPackedItemValid(packed) {
|
|
24
24
|
const category = packedItemCategory(packed);
|
|
25
|
-
return category >= BalatroItemCategory.
|
|
25
|
+
return category >= BalatroItemCategory.Standardcard && category <= BalatroItemCategory.Joker;
|
|
26
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SEARCH_WORKER_CODE = "\nlet MotelyWasm = null;\nlet MotelyWasmEvents = null;\nlet activeSearch = null;\n\nself.addEventListener('message', async function(e) {\n const msg = e.data;\n\n if (msg.type === 'init') {\n try {\n const mod = await import(msg.url);\n await mod.default.boot();\n MotelyWasm = mod.MotelyWasm;\n MotelyWasmEvents = mod.MotelyWasmEvents;\n self.postMessage({ type: 'ready' });\n } catch (err) {\n self.postMessage({ type: 'error', message: String(err) });\n }\n return;\n }\n\n if (msg.type === 'start') {\n if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }\n const validation = MotelyWasm.validateJaml(msg.jaml);\n if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }\n\n let rId, pId, cId;\n function cleanup() {\n MotelyWasmEvents.onResult.unsubscribeById(rId);\n MotelyWasmEvents.onProgress.unsubscribeById(pId);\n MotelyWasmEvents.onComplete.unsubscribeById(cId);\n activeSearch = null;\n }\n\n rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {\n self.postMessage({ type: 'result', seed, score });\n });\n pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {\n self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });\n });\n cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {\n cleanup();\n self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });\n });\n\n try {\n activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);\n } catch (err) {\n cleanup();\n self.postMessage({ type: 'error', message: String(err) });\n }\n return;\n }\n\n if (msg.type === 'stop') {\n if (activeSearch) { activeSearch.cancel(); activeSearch = null; }\n self.postMessage({ type: 'cancelled' });\n }\n});\n";
|
|
1
|
+
export declare const SEARCH_WORKER_CODE = "\nlet MotelyWasm = null;\nlet MotelyWasmEvents = null;\nlet Filters = null;\nlet activeSearch = null;\n\nself.addEventListener('message', async function(e) {\n const msg = e.data;\n\n if (msg.type === 'init') {\n try {\n const mod = await import(msg.url);\n await mod.default.boot();\n MotelyWasm = mod.MotelyWasm;\n MotelyWasmEvents = mod.MotelyWasmEvents;\n Filters = mod.Filters;\n self.postMessage({ type: 'ready' });\n } catch (err) {\n self.postMessage({ type: 'error', message: String(err) });\n }\n return;\n }\n\n if (msg.type === 'start') {\n if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }\n const validation = MotelyWasm.validateJaml(msg.jaml);\n if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }\n\n let rId, pId, cId;\n function cleanup() {\n MotelyWasmEvents.onResult.unsubscribeById(rId);\n MotelyWasmEvents.onProgress.unsubscribeById(pId);\n MotelyWasmEvents.onComplete.unsubscribeById(cId);\n activeSearch = null;\n }\n\n rId = MotelyWasmEvents.onResult.subscribe(function(seed, score, tallyColumns) {\n self.postMessage({ type: 'result', seed, score, tallyColumns: Array.from(tallyColumns) });\n });\n pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {\n self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });\n });\n cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {\n cleanup();\n self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });\n });\n\n try {\n const mode = msg.mode || 'random';\n\n if (mode === 'random') {\n activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);\n } else if (mode === 'aesthetic') {\n activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);\n } else if (mode === 'seedList') {\n activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);\n } else if (mode === 'keyword') {\n activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || '');\n } else if (mode === 'sequential') {\n activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch), BigInt(msg.endBatch));\n } else {\n self.postMessage({ type: 'error', message: 'Unknown search mode: ' + mode });\n cleanup();\n return;\n }\n } catch (err) {\n cleanup();\n self.postMessage({ type: 'error', message: String(err) });\n }\n return;\n }\n\n if (msg.type === 'stop') {\n if (activeSearch) { activeSearch.cancel(); activeSearch = null; }\n self.postMessage({ type: 'cancelled' });\n }\n\n if (msg.type === 'get_tally_labels') {\n if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }\n try {\n const labels = MotelyWasm.getTallyLabels(msg.jaml);\n self.postMessage({ type: 'tally_labels', labels });\n } catch (err) {\n self.postMessage({ type: 'error', message: String(err) });\n }\n }\n});\n";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// Worker code as an inline string — created as a Blob URL at runtime.
|
|
2
|
-
// This avoids bundler/import.meta.url issues when shipped as an npm package.
|
|
3
1
|
export const SEARCH_WORKER_CODE = `
|
|
4
2
|
let MotelyWasm = null;
|
|
5
3
|
let MotelyWasmEvents = null;
|
|
4
|
+
let Filters = null;
|
|
6
5
|
let activeSearch = null;
|
|
7
6
|
|
|
8
7
|
self.addEventListener('message', async function(e) {
|
|
@@ -14,6 +13,7 @@ self.addEventListener('message', async function(e) {
|
|
|
14
13
|
await mod.default.boot();
|
|
15
14
|
MotelyWasm = mod.MotelyWasm;
|
|
16
15
|
MotelyWasmEvents = mod.MotelyWasmEvents;
|
|
16
|
+
Filters = mod.Filters;
|
|
17
17
|
self.postMessage({ type: 'ready' });
|
|
18
18
|
} catch (err) {
|
|
19
19
|
self.postMessage({ type: 'error', message: String(err) });
|
|
@@ -34,8 +34,8 @@ self.addEventListener('message', async function(e) {
|
|
|
34
34
|
activeSearch = null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {
|
|
38
|
-
self.postMessage({ type: 'result', seed, score });
|
|
37
|
+
rId = MotelyWasmEvents.onResult.subscribe(function(seed, score, tallyColumns) {
|
|
38
|
+
self.postMessage({ type: 'result', seed, score, tallyColumns: Array.from(tallyColumns) });
|
|
39
39
|
});
|
|
40
40
|
pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {
|
|
41
41
|
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
@@ -46,7 +46,23 @@ self.addEventListener('message', async function(e) {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
|
-
|
|
49
|
+
const mode = msg.mode || 'random';
|
|
50
|
+
|
|
51
|
+
if (mode === 'random') {
|
|
52
|
+
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
53
|
+
} else if (mode === 'aesthetic') {
|
|
54
|
+
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);
|
|
55
|
+
} else if (mode === 'seedList') {
|
|
56
|
+
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);
|
|
57
|
+
} else if (mode === 'keyword') {
|
|
58
|
+
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || '');
|
|
59
|
+
} else if (mode === 'sequential') {
|
|
60
|
+
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch), BigInt(msg.endBatch));
|
|
61
|
+
} else {
|
|
62
|
+
self.postMessage({ type: 'error', message: 'Unknown search mode: ' + mode });
|
|
63
|
+
cleanup();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
50
66
|
} catch (err) {
|
|
51
67
|
cleanup();
|
|
52
68
|
self.postMessage({ type: 'error', message: String(err) });
|
|
@@ -58,5 +74,15 @@ self.addEventListener('message', async function(e) {
|
|
|
58
74
|
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
59
75
|
self.postMessage({ type: 'cancelled' });
|
|
60
76
|
}
|
|
77
|
+
|
|
78
|
+
if (msg.type === 'get_tally_labels') {
|
|
79
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
80
|
+
try {
|
|
81
|
+
const labels = MotelyWasm.getTallyLabels(msg.jaml);
|
|
82
|
+
self.postMessage({ type: 'tally_labels', labels });
|
|
83
|
+
} catch (err) {
|
|
84
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
61
87
|
});
|
|
62
88
|
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface SearchResult {
|
|
2
2
|
seed: string;
|
|
3
3
|
score: number;
|
|
4
|
+
tallyColumns?: number[];
|
|
4
5
|
}
|
|
5
6
|
export type SearchStatus = "idle" | "booting" | "running" | "completed" | "cancelled" | "error";
|
|
6
7
|
export interface UseSearchState {
|
|
@@ -9,14 +10,22 @@ export interface UseSearchState {
|
|
|
9
10
|
matchingSeeds: bigint;
|
|
10
11
|
status: SearchStatus;
|
|
11
12
|
error: string | null;
|
|
13
|
+
seedsPerSecond: number;
|
|
14
|
+
tallyLabels: string[];
|
|
12
15
|
}
|
|
13
16
|
export declare function useSearch(motelyWasmUrl: string): {
|
|
14
17
|
start: (jaml: string, count: number) => void;
|
|
18
|
+
startAesthetic: (jaml: string, aesthetic: number) => void;
|
|
19
|
+
startSeedList: (jaml: string, seeds: string[]) => void;
|
|
20
|
+
startKeyword: (jaml: string, keywords: string, padding?: string) => void;
|
|
15
21
|
cancel: () => void;
|
|
16
22
|
clearError: () => void;
|
|
23
|
+
fetchTallyLabels: (jaml: string) => void;
|
|
17
24
|
results: SearchResult[];
|
|
18
25
|
totalSearched: bigint;
|
|
19
26
|
matchingSeeds: bigint;
|
|
20
27
|
status: SearchStatus;
|
|
21
28
|
error: string | null;
|
|
29
|
+
seedsPerSecond: number;
|
|
30
|
+
tallyLabels: string[];
|
|
22
31
|
};
|