jaml-ui 0.22.5 → 0.23.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/DESIGN.md +236 -236
- package/README.md +147 -147
- package/dist/hooks/searchWorkerCode.js +84 -84
- package/dist/hooks/useSearch.js +86 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/ui/footer.js +5 -5
- package/dist/ui/hooks.js +55 -55
- package/package.json +145 -145
package/README.md
CHANGED
|
@@ -1,147 +1,147 @@
|
|
|
1
|
-
# jaml-ui
|
|
2
|
-
|
|
3
|
-
React components, UI tokens, sprites, and utilities for Balatro/JAML apps.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install jaml-ui react react-dom
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Package exports
|
|
12
|
-
|
|
13
|
-
| Entry | Contents |
|
|
14
|
-
|-------|----------|
|
|
15
|
-
| `jaml-ui` | Game card components, JAML IDE, Analyzer Explorer, hooks |
|
|
16
|
-
| `jaml-ui/ui` | Jimbo design system — JimboPanel, JimboButton, JimboModal, tokens |
|
|
17
|
-
| `jaml-ui/core` | Pure asset helpers, sprite metadata, decode utilities (no React) |
|
|
18
|
-
| `jaml-ui/motely` | motely-wasm decode helpers (requires `motely-wasm` peer) |
|
|
19
|
-
| `jaml-ui/r3f` | 3D card component via React Three Fiber (requires r3f peers) |
|
|
20
|
-
|
|
21
|
-
## Quick start
|
|
22
|
-
|
|
23
|
-
```tsx
|
|
24
|
-
import { JamlGameCard, AnalyzerExplorer, JamlIde } from "jaml-ui";
|
|
25
|
-
import { JimboPanel, JimboButton } from "jaml-ui/ui";
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Game card
|
|
29
|
-
|
|
30
|
-
```tsx
|
|
31
|
-
import { JamlGameCard } from "jaml-ui";
|
|
32
|
-
|
|
33
|
-
<JamlGameCard
|
|
34
|
-
type="joker"
|
|
35
|
-
card={{ name: "Blueprint", edition: "Foil", isEternal: true, scale: 1.5 }}
|
|
36
|
-
/>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Jimbo UI (Balatro design system)
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
import { JimboPanel, JimboButton, JimboModal } from "jaml-ui/ui";
|
|
43
|
-
import { JimboColorOption } from "jaml-ui/ui";
|
|
44
|
-
|
|
45
|
-
<JimboPanel sway onBack={() => setOpen(false)}>
|
|
46
|
-
<JimboButton variant="primary" onClick={handleSearch}>Search</JimboButton>
|
|
47
|
-
</JimboPanel>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Available variants: `primary`, `secondary`, `danger`, `back`, `ghost`
|
|
51
|
-
|
|
52
|
-
### JAML IDE
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
import { JamlIde } from "jaml-ui";
|
|
56
|
-
|
|
57
|
-
<JamlIde
|
|
58
|
-
jaml={jaml}
|
|
59
|
-
onChange={setJaml}
|
|
60
|
-
searchResults={results}
|
|
61
|
-
onSearch={handleSearch}
|
|
62
|
-
isSearching={isSearching}
|
|
63
|
-
/>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Analyzer Explorer
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import { AnalyzerExplorer } from "jaml-ui";
|
|
70
|
-
|
|
71
|
-
// antes: AnalyzerAnteView[] — stream from motely-wasm createSearchContext
|
|
72
|
-
<AnalyzerExplorer antes={antes} totalAntes={8} highlights={highlights} />
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### JAML Map Preview
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
import { JamlMapPreview } from "jaml-ui";
|
|
79
|
-
|
|
80
|
-
<JamlMapPreview jaml={jaml} />
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Asset handling
|
|
84
|
-
|
|
85
|
-
By default sprites resolve from the package `assets/` directory via `import.meta.url`.
|
|
86
|
-
|
|
87
|
-
Override at app startup:
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import { setJamlAssetBaseUrl, clearJamlAssetBaseUrl } from "jaml-ui";
|
|
91
|
-
|
|
92
|
-
setJamlAssetBaseUrl("/vendor/jaml-ui/"); // custom CDN
|
|
93
|
-
clearJamlAssetBaseUrl(); // back to default
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Core utilities
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
import { SPRITE_SHEETS, getSpriteData, resolveJamlAssetUrl } from "jaml-ui/core";
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Motely decode helpers
|
|
103
|
-
|
|
104
|
-
```ts
|
|
105
|
-
import { decodeMotelyItemName, motelyItemTypeName } from "jaml-ui/motely";
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## 3D card (optional)
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
npm install three @react-three/fiber @react-three/drei @react-spring/three
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
```tsx
|
|
115
|
-
import { Card3D } from "jaml-ui/r3f";
|
|
116
|
-
|
|
117
|
-
<Card3D itemName="Blueprint" />
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Next.js
|
|
121
|
-
|
|
122
|
-
Import pure helpers from `jaml-ui/core` for server components. For local workspace installs add:
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
// next.config.ts
|
|
126
|
-
const nextConfig = { transpilePackages: ["jaml-ui"] };
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Search Worker Architecture
|
|
130
|
-
|
|
131
|
-
The library provides `useAnalyzer` to interact with `motely-wasm`'s search context natively.
|
|
132
|
-
**Important:** As of `motely-wasm@14.3.3`, the search worker utilizes Vite's `?worker&inline` pattern. You no longer need to pass `motelyWasmUrl`.
|
|
133
|
-
|
|
134
|
-
Ensure `motely-wasm` is imported and booted at the module level in your application:
|
|
135
|
-
|
|
136
|
-
```tsx
|
|
137
|
-
import { boot } from "motely-wasm";
|
|
138
|
-
boot(); // Call early in your application lifecycle
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
## Peer dependencies
|
|
142
|
-
|
|
143
|
-
| Peer | Required for |
|
|
144
|
-
|------|-------------|
|
|
145
|
-
| `react`, `react-dom` | All components |
|
|
146
|
-
| `motely-wasm ^14.3.3` | `jaml-ui/motely`, `AnalyzerExplorer`, `useAnalyzer` data |
|
|
147
|
-
| `three`, `@react-three/fiber`, `@react-three/drei`, `@react-spring/three` | `jaml-ui/r3f` only |
|
|
1
|
+
# jaml-ui
|
|
2
|
+
|
|
3
|
+
React components, UI tokens, sprites, and utilities for Balatro/JAML apps.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install jaml-ui react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Package exports
|
|
12
|
+
|
|
13
|
+
| Entry | Contents |
|
|
14
|
+
|-------|----------|
|
|
15
|
+
| `jaml-ui` | Game card components, JAML IDE, Analyzer Explorer, hooks |
|
|
16
|
+
| `jaml-ui/ui` | Jimbo design system — JimboPanel, JimboButton, JimboModal, tokens |
|
|
17
|
+
| `jaml-ui/core` | Pure asset helpers, sprite metadata, decode utilities (no React) |
|
|
18
|
+
| `jaml-ui/motely` | motely-wasm decode helpers (requires `motely-wasm` peer) |
|
|
19
|
+
| `jaml-ui/r3f` | 3D card component via React Three Fiber (requires r3f peers) |
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { JamlGameCard, AnalyzerExplorer, JamlIde } from "jaml-ui";
|
|
25
|
+
import { JimboPanel, JimboButton } from "jaml-ui/ui";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Game card
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { JamlGameCard } from "jaml-ui";
|
|
32
|
+
|
|
33
|
+
<JamlGameCard
|
|
34
|
+
type="joker"
|
|
35
|
+
card={{ name: "Blueprint", edition: "Foil", isEternal: true, scale: 1.5 }}
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Jimbo UI (Balatro design system)
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { JimboPanel, JimboButton, JimboModal } from "jaml-ui/ui";
|
|
43
|
+
import { JimboColorOption } from "jaml-ui/ui";
|
|
44
|
+
|
|
45
|
+
<JimboPanel sway onBack={() => setOpen(false)}>
|
|
46
|
+
<JimboButton variant="primary" onClick={handleSearch}>Search</JimboButton>
|
|
47
|
+
</JimboPanel>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Available variants: `primary`, `secondary`, `danger`, `back`, `ghost`
|
|
51
|
+
|
|
52
|
+
### JAML IDE
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { JamlIde } from "jaml-ui";
|
|
56
|
+
|
|
57
|
+
<JamlIde
|
|
58
|
+
jaml={jaml}
|
|
59
|
+
onChange={setJaml}
|
|
60
|
+
searchResults={results}
|
|
61
|
+
onSearch={handleSearch}
|
|
62
|
+
isSearching={isSearching}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Analyzer Explorer
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { AnalyzerExplorer } from "jaml-ui";
|
|
70
|
+
|
|
71
|
+
// antes: AnalyzerAnteView[] — stream from motely-wasm createSearchContext
|
|
72
|
+
<AnalyzerExplorer antes={antes} totalAntes={8} highlights={highlights} />
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### JAML Map Preview
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { JamlMapPreview } from "jaml-ui";
|
|
79
|
+
|
|
80
|
+
<JamlMapPreview jaml={jaml} />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Asset handling
|
|
84
|
+
|
|
85
|
+
By default sprites resolve from the package `assets/` directory via `import.meta.url`.
|
|
86
|
+
|
|
87
|
+
Override at app startup:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { setJamlAssetBaseUrl, clearJamlAssetBaseUrl } from "jaml-ui";
|
|
91
|
+
|
|
92
|
+
setJamlAssetBaseUrl("/vendor/jaml-ui/"); // custom CDN
|
|
93
|
+
clearJamlAssetBaseUrl(); // back to default
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Core utilities
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { SPRITE_SHEETS, getSpriteData, resolveJamlAssetUrl } from "jaml-ui/core";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Motely decode helpers
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { decodeMotelyItemName, motelyItemTypeName } from "jaml-ui/motely";
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 3D card (optional)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm install three @react-three/fiber @react-three/drei @react-spring/three
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { Card3D } from "jaml-ui/r3f";
|
|
116
|
+
|
|
117
|
+
<Card3D itemName="Blueprint" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Next.js
|
|
121
|
+
|
|
122
|
+
Import pure helpers from `jaml-ui/core` for server components. For local workspace installs add:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// next.config.ts
|
|
126
|
+
const nextConfig = { transpilePackages: ["jaml-ui"] };
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Search Worker Architecture
|
|
130
|
+
|
|
131
|
+
The library provides `useAnalyzer` to interact with `motely-wasm`'s search context natively.
|
|
132
|
+
**Important:** As of `motely-wasm@14.3.3`, the search worker utilizes Vite's `?worker&inline` pattern. You no longer need to pass `motelyWasmUrl`.
|
|
133
|
+
|
|
134
|
+
Ensure `motely-wasm` is imported and booted at the module level in your application:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { boot } from "motely-wasm";
|
|
138
|
+
boot(); // Call early in your application lifecycle
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Peer dependencies
|
|
142
|
+
|
|
143
|
+
| Peer | Required for |
|
|
144
|
+
|------|-------------|
|
|
145
|
+
| `react`, `react-dom` | All components |
|
|
146
|
+
| `motely-wasm ^14.3.3` | `jaml-ui/motely`, `AnalyzerExplorer`, `useAnalyzer` data |
|
|
147
|
+
| `three`, `@react-three/fiber`, `@react-three/drei`, `@react-spring/three` | `jaml-ui/r3f` only |
|
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
export const SEARCH_WORKER_CODE = `
|
|
2
|
-
let MotelyWasm = null;
|
|
3
|
-
let MotelyWasmEvents = null;
|
|
4
|
-
let activeSearch = null;
|
|
5
|
-
|
|
6
|
-
self.addEventListener('message', async function(e) {
|
|
7
|
-
const msg = e.data;
|
|
8
|
-
|
|
9
|
-
if (msg.type === 'init') {
|
|
10
|
-
try {
|
|
11
|
-
const mod = await import(msg.url);
|
|
12
|
-
await mod.default.boot();
|
|
13
|
-
const motely = mod.Motely;
|
|
14
|
-
MotelyWasm = motely.MotelyWasm;
|
|
15
|
-
MotelyWasmEvents = motely.MotelyWasmEvents;
|
|
16
|
-
self.postMessage({ type: 'ready' });
|
|
17
|
-
} catch (err) {
|
|
18
|
-
self.postMessage({ type: 'error', message: String(err) });
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (msg.type === 'start') {
|
|
24
|
-
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
25
|
-
const validation = MotelyWasm.validateJaml(msg.jaml);
|
|
26
|
-
if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
|
|
27
|
-
|
|
28
|
-
function cleanup() {
|
|
29
|
-
MotelyWasmEvents.notifyResult = () => {};
|
|
30
|
-
MotelyWasmEvents.notifyProgress = () => {};
|
|
31
|
-
MotelyWasmEvents.notifyComplete = () => {};
|
|
32
|
-
activeSearch = null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
MotelyWasmEvents.notifyResult = function(seed, score, tallyColumns) {
|
|
36
|
-
self.postMessage({ type: 'result', seed, score, tallyColumns: Array.from(tallyColumns) });
|
|
37
|
-
};
|
|
38
|
-
MotelyWasmEvents.notifyProgress = function(searched, matching) {
|
|
39
|
-
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
40
|
-
};
|
|
41
|
-
MotelyWasmEvents.notifyComplete = function(status, searched, matched) {
|
|
42
|
-
cleanup();
|
|
43
|
-
self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const mode = msg.mode || 'random';
|
|
48
|
-
if (mode === 'random') {
|
|
49
|
-
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
50
|
-
} else if (mode === 'aesthetic') {
|
|
51
|
-
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);
|
|
52
|
-
} else if (mode === 'seedList') {
|
|
53
|
-
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);
|
|
54
|
-
} else if (mode === 'keyword') {
|
|
55
|
-
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || '');
|
|
56
|
-
} else if (mode === 'sequential') {
|
|
57
|
-
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch), BigInt(msg.endBatch));
|
|
58
|
-
} else {
|
|
59
|
-
self.postMessage({ type: 'error', message: 'Unknown search mode: ' + mode });
|
|
60
|
-
cleanup();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
} catch (err) {
|
|
64
|
-
cleanup();
|
|
65
|
-
self.postMessage({ type: 'error', message: String(err) });
|
|
66
|
-
}
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (msg.type === 'stop') {
|
|
71
|
-
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
72
|
-
self.postMessage({ type: 'cancelled' });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (msg.type === 'get_tally_labels') {
|
|
76
|
-
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
77
|
-
try {
|
|
78
|
-
const labels = MotelyWasm.getTallyLabels(msg.jaml);
|
|
79
|
-
self.postMessage({ type: 'tally_labels', labels: Array.from(labels) });
|
|
80
|
-
} catch (err) {
|
|
81
|
-
self.postMessage({ type: 'error', message: String(err) });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
});
|
|
1
|
+
export const SEARCH_WORKER_CODE = `
|
|
2
|
+
let MotelyWasm = null;
|
|
3
|
+
let MotelyWasmEvents = null;
|
|
4
|
+
let activeSearch = null;
|
|
5
|
+
|
|
6
|
+
self.addEventListener('message', async function(e) {
|
|
7
|
+
const msg = e.data;
|
|
8
|
+
|
|
9
|
+
if (msg.type === 'init') {
|
|
10
|
+
try {
|
|
11
|
+
const mod = await import(msg.url);
|
|
12
|
+
await mod.default.boot();
|
|
13
|
+
const motely = mod.Motely;
|
|
14
|
+
MotelyWasm = motely.MotelyWasm;
|
|
15
|
+
MotelyWasmEvents = motely.MotelyWasmEvents;
|
|
16
|
+
self.postMessage({ type: 'ready' });
|
|
17
|
+
} catch (err) {
|
|
18
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (msg.type === 'start') {
|
|
24
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
25
|
+
const validation = MotelyWasm.validateJaml(msg.jaml);
|
|
26
|
+
if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
|
|
27
|
+
|
|
28
|
+
function cleanup() {
|
|
29
|
+
MotelyWasmEvents.notifyResult = () => {};
|
|
30
|
+
MotelyWasmEvents.notifyProgress = () => {};
|
|
31
|
+
MotelyWasmEvents.notifyComplete = () => {};
|
|
32
|
+
activeSearch = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
MotelyWasmEvents.notifyResult = function(seed, score, tallyColumns) {
|
|
36
|
+
self.postMessage({ type: 'result', seed, score, tallyColumns: Array.from(tallyColumns) });
|
|
37
|
+
};
|
|
38
|
+
MotelyWasmEvents.notifyProgress = function(searched, matching) {
|
|
39
|
+
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
40
|
+
};
|
|
41
|
+
MotelyWasmEvents.notifyComplete = function(status, searched, matched) {
|
|
42
|
+
cleanup();
|
|
43
|
+
self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const mode = msg.mode || 'random';
|
|
48
|
+
if (mode === 'random') {
|
|
49
|
+
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
50
|
+
} else if (mode === 'aesthetic') {
|
|
51
|
+
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);
|
|
52
|
+
} else if (mode === 'seedList') {
|
|
53
|
+
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);
|
|
54
|
+
} else if (mode === 'keyword') {
|
|
55
|
+
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || '');
|
|
56
|
+
} else if (mode === 'sequential') {
|
|
57
|
+
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch), BigInt(msg.endBatch));
|
|
58
|
+
} else {
|
|
59
|
+
self.postMessage({ type: 'error', message: 'Unknown search mode: ' + mode });
|
|
60
|
+
cleanup();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
cleanup();
|
|
65
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (msg.type === 'stop') {
|
|
71
|
+
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
72
|
+
self.postMessage({ type: 'cancelled' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (msg.type === 'get_tally_labels') {
|
|
76
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
77
|
+
try {
|
|
78
|
+
const labels = MotelyWasm.getTallyLabels(msg.jaml);
|
|
79
|
+
self.postMessage({ type: 'tally_labels', labels: Array.from(labels) });
|
|
80
|
+
} catch (err) {
|
|
81
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
85
|
`;
|
package/dist/hooks/useSearch.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
3
|
-
import { SEARCH_WORKER_CODE } from "./searchWorkerCode.js";
|
|
4
3
|
const INITIAL_STATE = {
|
|
5
4
|
results: [],
|
|
6
5
|
totalSearched: 0n,
|
|
@@ -10,6 +9,92 @@ const INITIAL_STATE = {
|
|
|
10
9
|
seedsPerSecond: 0,
|
|
11
10
|
tallyLabels: [],
|
|
12
11
|
};
|
|
12
|
+
const SEARCH_WORKER_CODE = `
|
|
13
|
+
let MotelyWasm = null;
|
|
14
|
+
let MotelyWasmEvents = null;
|
|
15
|
+
let activeSearch = null;
|
|
16
|
+
|
|
17
|
+
self.addEventListener('message', async function(e) {
|
|
18
|
+
const msg = e.data;
|
|
19
|
+
|
|
20
|
+
if (msg.type === 'init') {
|
|
21
|
+
try {
|
|
22
|
+
const mod = await import(msg.url);
|
|
23
|
+
await mod.default.boot();
|
|
24
|
+
const motely = mod.Motely;
|
|
25
|
+
MotelyWasm = motely.MotelyWasm;
|
|
26
|
+
MotelyWasmEvents = motely.MotelyWasmEvents;
|
|
27
|
+
self.postMessage({ type: 'ready' });
|
|
28
|
+
} catch (err) {
|
|
29
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (msg.type === 'start') {
|
|
35
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
36
|
+
const validation = MotelyWasm.validateJaml(msg.jaml);
|
|
37
|
+
if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
|
|
38
|
+
|
|
39
|
+
function cleanup() {
|
|
40
|
+
MotelyWasmEvents.notifyResult = () => {};
|
|
41
|
+
MotelyWasmEvents.notifyProgress = () => {};
|
|
42
|
+
MotelyWasmEvents.notifyComplete = () => {};
|
|
43
|
+
activeSearch = null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
MotelyWasmEvents.notifyResult = function(seed, score, tallyColumns) {
|
|
47
|
+
self.postMessage({ type: 'result', seed, score, tallyColumns: Array.from(tallyColumns) });
|
|
48
|
+
};
|
|
49
|
+
MotelyWasmEvents.notifyProgress = function(searched, matching) {
|
|
50
|
+
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
51
|
+
};
|
|
52
|
+
MotelyWasmEvents.notifyComplete = function(status, searched, matched) {
|
|
53
|
+
cleanup();
|
|
54
|
+
self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const mode = msg.mode || 'random';
|
|
59
|
+
|
|
60
|
+
if (mode === 'random') {
|
|
61
|
+
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
62
|
+
} else if (mode === 'aesthetic') {
|
|
63
|
+
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);
|
|
64
|
+
} else if (mode === 'seedList') {
|
|
65
|
+
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);
|
|
66
|
+
} else if (mode === 'keyword') {
|
|
67
|
+
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || '');
|
|
68
|
+
} else if (mode === 'sequential') {
|
|
69
|
+
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch), BigInt(msg.endBatch));
|
|
70
|
+
} else {
|
|
71
|
+
self.postMessage({ type: 'error', message: 'Unknown search mode: ' + mode });
|
|
72
|
+
cleanup();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
cleanup();
|
|
77
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (msg.type === 'stop') {
|
|
83
|
+
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
84
|
+
self.postMessage({ type: 'cancelled' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (msg.type === 'get_tally_labels') {
|
|
88
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
89
|
+
try {
|
|
90
|
+
const labels = MotelyWasm.getTallyLabels(msg.jaml);
|
|
91
|
+
self.postMessage({ type: 'tally_labels', labels: Array.from(labels) });
|
|
92
|
+
} catch (err) {
|
|
93
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
`;
|
|
13
98
|
function createWorker() {
|
|
14
99
|
const blob = new Blob([SEARCH_WORKER_CODE], { type: "application/javascript" });
|
|
15
100
|
return new Worker(URL.createObjectURL(blob), { type: "module" });
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,6 @@ export { extractVisualJamlItems, type JamlPreviewGroups, type JamlPreviewItem, t
|
|
|
22
22
|
export { useMotelyStream, type StreamItem, type StreamState } from "./hooks/useShopStream.js";
|
|
23
23
|
export { useSearch, type SearchResult, type SearchStatus, type UseSearchState, } from "./hooks/useSearch.js";
|
|
24
24
|
export { useAnalyzer, type AnalyzerStatus, type AnalyzerLive, type MotelyJsRunState, } from "./hooks/useAnalyzer.js";
|
|
25
|
-
export { SEARCH_WORKER_CODE } from "./hooks/searchWorkerCode.js";
|
|
26
25
|
export { setMotelyEnums as setMotelyDisplayEnums } from "./motelyDisplay.js";
|
|
27
26
|
export { setMotelyEnums as setMotelyDecoderEnums } from "./decode/motelyItemDecoder.js";
|
|
28
27
|
export { JamlAestheticSelector, type JamlAestheticSelectorProps, type JamlAestheticOption, } from "./components/JamlAestheticSelector.js";
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,6 @@ export { extractVisualJamlItems, } from "./utils/jamlMapPreview.js";
|
|
|
23
23
|
export { useMotelyStream } from "./hooks/useShopStream.js";
|
|
24
24
|
export { useSearch, } from "./hooks/useSearch.js";
|
|
25
25
|
export { useAnalyzer, } from "./hooks/useAnalyzer.js";
|
|
26
|
-
export { SEARCH_WORKER_CODE } from "./hooks/searchWorkerCode.js";
|
|
27
26
|
// Setter pattern for motely-wasm runtime enums. Consumers boot motely-wasm
|
|
28
27
|
// and call setMotelyEnums(Motely) once after boot so display/decoder helpers
|
|
29
28
|
// can resolve enum keys without statically importing motely-wasm.
|
package/dist/ui/footer.js
CHANGED
|
@@ -11,10 +11,10 @@ const SUITS = [
|
|
|
11
11
|
* All styling via jimbo.css `.j-footer` classes — zero inline styles.
|
|
12
12
|
*/
|
|
13
13
|
export function JimboBalatroFooter({ hidden = false, className = '' }) {
|
|
14
|
-
return (_jsxs("div", { className: `j-footer ${hidden ? 'j-footer--hidden' : ''} ${className}`, children: [_jsx("div", { className: "j-footer__bar", children: _jsxs("p", { className: "j-footer__text", children: [_jsxs("span", { children: ["Not affiliated with LocalThunk or PlayStack \u2022", ' '] }), _jsx("a", { href: "https://store.steampowered.com/app/2379780/Balatro/", target: "_blank", rel: "noopener noreferrer", className: "j-footer__link", children: "BUY BALATRO" }), _jsxs("span", { children: [' ', "\u2022 Created with", ' '] }), _jsxs("span", { className: "j-footer__suits", children: [_jsx("span", { className: "j-footer__suit-stage", children: SUITS.map(({ char, kf }) => (_jsx("span", { className: "j-footer__suit-char", style: { animationName: kf }, children: char }, char))) }), ' ', "for the Balatro community"] })] }) }), _jsx("style", { children: `
|
|
15
|
-
@keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
|
|
16
|
-
@keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
|
|
17
|
-
@keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
|
|
18
|
-
@keyframes jaml-club { 0%,81.5%{opacity:0} 82%{opacity:1;transform:scale(1.45)} 84.5%{opacity:1;transform:scale(1)} 95%{opacity:1} 96%{opacity:0} 100%{opacity:0} }
|
|
14
|
+
return (_jsxs("div", { className: `j-footer ${hidden ? 'j-footer--hidden' : ''} ${className}`, children: [_jsx("div", { className: "j-footer__bar", children: _jsxs("p", { className: "j-footer__text", children: [_jsxs("span", { children: ["Not affiliated with LocalThunk or PlayStack \u2022", ' '] }), _jsx("a", { href: "https://store.steampowered.com/app/2379780/Balatro/", target: "_blank", rel: "noopener noreferrer", className: "j-footer__link", children: "BUY BALATRO" }), _jsxs("span", { children: [' ', "\u2022 Created with", ' '] }), _jsxs("span", { className: "j-footer__suits", children: [_jsx("span", { className: "j-footer__suit-stage", children: SUITS.map(({ char, kf }) => (_jsx("span", { className: "j-footer__suit-char", style: { animationName: kf }, children: char }, char))) }), ' ', "for the Balatro community"] })] }) }), _jsx("style", { children: `
|
|
15
|
+
@keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
|
|
16
|
+
@keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
|
|
17
|
+
@keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
|
|
18
|
+
@keyframes jaml-club { 0%,81.5%{opacity:0} 82%{opacity:1;transform:scale(1.45)} 84.5%{opacity:1;transform:scale(1)} 95%{opacity:1} 96%{opacity:0} 100%{opacity:0} }
|
|
19
19
|
` })] }));
|
|
20
20
|
}
|