jaml-ui 0.24.16 → 0.24.18
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/dist/components/DeckSprite.js +2 -0
- package/dist/components/GameCard.js +1 -0
- package/dist/components/JamlAestheticSelector.js +3 -2
- package/dist/components/JamlCurator.js +3 -2
- package/dist/components/JamlIde.d.ts +4 -3
- package/dist/components/JamlIde.js +3 -31
- package/dist/components/JamlIdeToolbar.d.ts +1 -1
- package/dist/components/JamlIdeToolbar.js +5 -5
- package/dist/components/JamlMapPreview.js +3 -37
- package/dist/components/Jimbolate.d.ts +7 -0
- package/dist/components/Jimbolate.js +17 -0
- package/dist/components/PaginatedFilterBrowser.d.ts +23 -0
- package/dist/components/PaginatedFilterBrowser.js +54 -0
- package/dist/components/RunConfigModal.js +11 -150
- package/dist/components/jamlMap/CategoryPicker.d.ts +1 -2
- package/dist/components/jamlMap/CategoryPicker.js +2 -1
- package/dist/components/jamlMap/JamlMapEditor.js +8 -10
- package/dist/components/jamlMap/JokerPicker.d.ts +1 -2
- package/dist/components/jamlMap/JokerPicker.js +3 -7
- package/dist/components/jamlMap/MysterySlot.js +0 -15
- package/dist/hooks/searchWorker.d.ts +1 -29
- package/dist/hooks/searchWorker.js +8 -6
- package/dist/hooks/useIntersectionObserver.js +5 -3
- package/dist/hooks/useSearch.js +5 -1
- package/dist/hooks/useShopStream.js +5 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/cardParser.d.ts +1 -1
- package/dist/lib/cardParser.js +19 -17
- package/dist/lib/classes/BuyMetaData.d.ts +2 -2
- package/dist/lib/const.d.ts +22 -13
- package/dist/lib/data/constants.js +10 -9
- package/dist/lib/hooks/useJamlFilter.js +5 -5
- package/dist/lib/hooks/useSeedAnalyzer.js +7 -1
- package/dist/lib/jaml/jamlSchema.d.ts +18 -3
- package/dist/lib/jaml/jamlSchema.js +2 -2
- package/dist/lib/parseDailyRitual.d.ts +1 -1
- package/dist/lib/parseDailyRitual.js +2 -1
- package/dist/r3f/Card3D.js +2 -0
- package/dist/r3f/JimboBillboard.d.ts +1 -1
- package/dist/r3f/JimboBillboard.js +5 -2
- package/dist/ui/JimboInputModal.js +8 -2
- package/dist/ui/PanelSplitter.js +4 -2
- package/dist/ui/hooks.js +14 -5
- package/dist/ui/ide/JamlEditor.js +53 -63
- package/dist/ui/jimbo.css +70 -16
- package/dist/ui/jimboTooltip.js +12 -6
- package/dist/ui/panel.d.ts +1 -2
- package/dist/ui/panel.js +2 -2
- package/dist/ui/radial/RadialButton.js +1 -1
- package/dist/ui/showcase.js +1 -1
- package/dist/utils/jamlMapPreview.js +2 -1
- package/dist/utils/jamlVisualFilter.js +3 -2
- package/package.json +12 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback,
|
|
3
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
4
4
|
import { MysterySlot } from "./MysterySlot.js";
|
|
5
5
|
import { JokerPicker } from "./JokerPicker.js";
|
|
6
6
|
import { CategoryPicker, VOUCHER_PICKER_CONFIG, TAG_PICKER_CONFIG, BOSS_PICKER_CONFIG, TAROT_PICKER_CONFIG, PLANET_PICKER_CONFIG, SPECTRAL_PICKER_CONFIG, PACK_PICKER_CONFIG, } from "./CategoryPicker.js";
|
|
@@ -31,7 +31,6 @@ const ZONE_LABEL = {
|
|
|
31
31
|
mustnot: "Avoid",
|
|
32
32
|
};
|
|
33
33
|
const CATEGORY_CONFIG_MAP = {
|
|
34
|
-
joker: VOUCHER_PICKER_CONFIG,
|
|
35
34
|
voucher: VOUCHER_PICKER_CONFIG,
|
|
36
35
|
tag: TAG_PICKER_CONFIG,
|
|
37
36
|
boss: BOSS_PICKER_CONFIG,
|
|
@@ -43,15 +42,15 @@ const CATEGORY_CONFIG_MAP = {
|
|
|
43
42
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
44
43
|
export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
45
44
|
const onChangeRef = useRef(onChange);
|
|
46
|
-
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
onChangeRef.current = onChange;
|
|
47
|
+
});
|
|
47
48
|
const [currentZone, setCurrentZone] = useState(initialZone);
|
|
48
|
-
const [ante, setAnte] = useState(1);
|
|
49
49
|
const [antesState, setAntesState] = useState({});
|
|
50
50
|
const [activeSlot, setActiveSlot] = useState(null);
|
|
51
51
|
const [activePackDetail, setActivePackDetail] = useState(null);
|
|
52
52
|
const [pickerFlow, setPickerFlow] = useState("category");
|
|
53
53
|
const [activePackSelection, setActivePackSelection] = useState(null);
|
|
54
|
-
const currentAnteSelections = antesState[ante] || {};
|
|
55
54
|
const handleSlotTap = useCallback((anteIndex, id, forceCategory) => {
|
|
56
55
|
const existing = (antesState[anteIndex] || {})[id];
|
|
57
56
|
if (forceCategory === "pack" && existing?.packName) {
|
|
@@ -131,7 +130,6 @@ export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
|
131
130
|
setActiveSlot(null);
|
|
132
131
|
setActivePackDetail(null);
|
|
133
132
|
}, []);
|
|
134
|
-
const jamlText = useMemo(() => buildJamlText(antesState), [antesState]);
|
|
135
133
|
const activePackDetailSelection = activePackDetail
|
|
136
134
|
? (antesState[activePackDetail.ante] || {})[activePackDetail.id]
|
|
137
135
|
: undefined;
|
|
@@ -154,7 +152,7 @@ export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
|
154
152
|
scrollBehavior: "smooth",
|
|
155
153
|
WebkitOverflowScrolling: "touch",
|
|
156
154
|
overscrollBehaviorY: "contain",
|
|
157
|
-
}, children: Array.from({ length:
|
|
155
|
+
}, children: Array.from({ length: 8 }, (_, i) => i + 1).map((a) => (_jsxs("div", { style: {
|
|
158
156
|
scrollSnapAlign: "start",
|
|
159
157
|
scrollSnapStop: "always",
|
|
160
158
|
padding: "24px 8px 64px 8px",
|
|
@@ -164,10 +162,10 @@ export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
|
164
162
|
flexDirection: "column",
|
|
165
163
|
gap: 24,
|
|
166
164
|
borderBottom: `2px solid ${C.DARK_GREY}`
|
|
167
|
-
}, children: [_jsxs(JimboText, { size: "md", tone: "white", style: { textAlign: "center", marginBottom: 8 }, children: ["Ante ", a] }), _jsxs("div", { className: "j-flex j-justify-between j-items-end", children: [_jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Voucher" }), renderSlot(a, `ante_${a}_voucher`, 42, "Vouchers", "voucher")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Small" }), renderSlot(a, `ante_${a}_tag_small`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Big" }), renderSlot(a, `ante_${a}_tag_big`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Boss" }), renderSlot(a, `ante_${a}_boss`, 42, "BlindChips", "boss")] })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "Shop Items" }), _jsx("div", { className: "j-flex hide-scrollbar j-gap-sm", style: { overflowX: "auto", paddingBottom: 8 }, children: [1, 2, 3, 4, 5, 6, 7, 8].map(i => renderSlot(a, `ante_${a}_shop_${i}`, 52, "Jokers")) })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "Packs" }), _jsx("div", { className: "j-flex-col j-gap-sm", children: getPackRows(a).map((row, rowIndex) => (_jsx("div", { className: "j-flex j-gap-sm", style: { flexWrap: "nowrap" }, children: row.map(i => renderSlot(a, `ante_${a}_pack_${i}`, 64, "Boosters", "pack")) }, rowIndex))) })] })] }, a))) }), _jsx(JimboModal, { open: activeSlot !== null, onClose: handlePickerCancel, title: pickerFlow === "category" ? "Select Category" : undefined, className: "j-picker-modal", children: activeSlot !== null && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { className: "j-inner-panel", style: { padding: "8px 10px", marginBottom: 2 }, children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { display: "block", marginBottom: 6 }, children: "This pick will be added as" }), _jsx("div", { className: "j-flex j-gap-sm", style: { flexWrap: "wrap" }, children: ["must", "should", "mustnot"].map((z) => (_jsx(JimboButton, { tone: currentZone === z ? ZONE_TONE[z] : "grey", size: "xs", onClick: () => setCurrentZone(z), children: ZONE_LABEL[z] }, z))) })] }), pickerFlow === "category" ? (_jsx(CategoryMenu, { onSelect: handleCategorySelect })) : pickerFlow === "joker" ? (_jsx(JokerPicker, { onSelect: handleItemSelect
|
|
165
|
+
}, children: [_jsxs(JimboText, { size: "md", tone: "white", style: { textAlign: "center", marginBottom: 8 }, children: ["Ante ", a] }), _jsxs("div", { className: "j-flex j-justify-between j-items-end", children: [_jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Voucher" }), renderSlot(a, `ante_${a}_voucher`, 42, "Vouchers", "voucher")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Small" }), renderSlot(a, `ante_${a}_tag_small`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Big" }), renderSlot(a, `ante_${a}_tag_big`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "Boss" }), renderSlot(a, `ante_${a}_boss`, 42, "BlindChips", "boss")] })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "Shop Items" }), _jsx("div", { className: "j-flex hide-scrollbar j-gap-sm", style: { overflowX: "auto", paddingBottom: 8 }, children: [1, 2, 3, 4, 5, 6, 7, 8].map(i => renderSlot(a, `ante_${a}_shop_${i}`, 52, "Jokers")) })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "Packs" }), _jsx("div", { className: "j-flex-col j-gap-sm", children: getPackRows(a).map((row, rowIndex) => (_jsx("div", { className: "j-flex j-gap-sm", style: { flexWrap: "nowrap" }, children: row.map(i => renderSlot(a, `ante_${a}_pack_${i}`, 64, "Boosters", "pack")) }, rowIndex))) })] })] }, a))) }), _jsx(JimboModal, { open: activeSlot !== null, onClose: handlePickerCancel, title: pickerFlow === "category" ? "Select Category" : undefined, className: "j-picker-modal", children: activeSlot !== null && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { className: "j-inner-panel", style: { padding: "8px 10px", marginBottom: 2 }, children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { display: "block", marginBottom: 6 }, children: "This pick will be added as" }), _jsx("div", { className: "j-flex j-gap-sm", style: { flexWrap: "wrap" }, children: ["must", "should", "mustnot"].map((z) => (_jsx(JimboButton, { tone: currentZone === z ? ZONE_TONE[z] : "grey", size: "xs", onClick: () => setCurrentZone(z), children: ZONE_LABEL[z] }, z))) })] }), pickerFlow === "category" ? (_jsx(CategoryMenu, { onSelect: handleCategorySelect })) : pickerFlow === "joker" ? (_jsx(JokerPicker, { onSelect: handleItemSelect })) : pickerFlow === "packUnsupported" ? (_jsxs("div", { className: "j-flex-col j-gap-sm", style: { padding: 10, maxWidth: 360 }, children: [_jsxs("div", { className: "j-inner-panel", style: { padding: "10px 12px" }, children: [_jsx(JimboText, { size: "sm", tone: "orange", children: "Standard Packs need a dedicated playing-card picker." }), _jsx(JimboText, { size: "xs", tone: "grey", style: { display: "block", marginTop: 6 }, children: "Arcana, Celestial, Spectral, and Buffoon pack flows are wired. Standard Pack support can come next without faking the JAML shape." })] }), _jsx(JimboButton, { tone: "orange", size: "sm", fullWidth: true, onClick: () => {
|
|
168
166
|
setActivePackSelection(null);
|
|
169
167
|
setPickerFlow("pack");
|
|
170
|
-
}, children: "Back to Packs" })] })) : (_jsx(CategoryPicker, { config: CATEGORY_CONFIG_MAP[pickerFlow], onSelect: handleItemSelect
|
|
168
|
+
}, children: "Back to Packs" })] })) : (_jsx(CategoryPicker, { config: CATEGORY_CONFIG_MAP[pickerFlow], onSelect: handleItemSelect }))] })) }), _jsx(JimboModal, { open: activePackDetail !== null && !!activePackDetailSelection?.packName, onClose: handleOverlayClose, title: activePackDetailSelection?.packName ?? "Pack", className: "j-picker-modal", children: activePackDetail !== null && activePackDetailSelection?.packName && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 10, maxWidth: 360 }, children: [_jsx("div", { className: "j-inner-panel", style: { padding: "10px 12px", background: withAlpha(C.DARKEST, 0.84) }, children: _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [_jsx(JimboSprite, { name: activePackDetailSelection.packName, sheet: "Boosters", width: 56 }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [_jsxs(JimboText, { size: "sm", tone: "white", children: ["Ante ", activePackDetail.ante, " pack ", ((activePackDetailSelection.boosterPacks?.[0] ?? 0) + 1)] }), _jsx(JimboText, { size: "xs", tone: "grey", children: getPackHelperText(activePackDetailSelection.packName) })] })] }) }), _jsxs("div", { className: "j-inner-panel", style: { padding: "10px 12px" }, children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { display: "block", marginBottom: 8 }, children: "Peek" }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }, children: [_jsx(JimboSprite, { name: activePackDetailSelection.packName, sheet: "Boosters", width: 44 }), _jsx(JimboText, { size: "sm", tone: "grey", children: "\u2192" }), _jsx(JimboSprite, { name: activePackDetailSelection.value, sheet: categoryToPreviewSheet(activePackDetailSelection.category), width: 48 }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 3 }, children: [_jsx(JimboText, { size: "sm", tone: "white", children: activePackDetailSelection.value }), _jsxs(JimboText, { size: "xs", tone: "grey", children: [getSelectionCategoryLabel(activePackDetailSelection.category), " from ", activePackDetailSelection.packName] })] })] }), _jsxs("div", { className: "j-flex j-gap-sm", style: { flexWrap: "wrap" }, children: [_jsxs("div", { className: "j-inner-panel", style: { padding: "6px 8px", minWidth: 96 }, children: [_jsx(JimboText, { size: "micro", tone: "grey", style: { display: "block", marginBottom: 2 }, children: "Zone" }), _jsx(JimboText, { size: "xs", tone: getZoneTextTone(activePackDetailSelection.zone), children: ZONE_LABEL[activePackDetailSelection.zone] })] }), _jsxs("div", { className: "j-inner-panel", style: { padding: "6px 8px", minWidth: 120 }, children: [_jsx(JimboText, { size: "micro", tone: "grey", style: { display: "block", marginBottom: 2 }, children: "Clause" }), _jsx(JimboText, { size: "xs", tone: "white", children: activePackDetailSelection.clauseKey })] }), _jsxs("div", { className: "j-inner-panel", style: { padding: "6px 8px", minWidth: 96 }, children: [_jsx(JimboText, { size: "micro", tone: "grey", style: { display: "block", marginBottom: 2 }, children: "Source" }), _jsxs(JimboText, { size: "xs", tone: "white", children: ["boosterPacks: [", activePackDetailSelection.boosterPacks?.join(", ") ?? "", "]"] })] })] })] }), _jsxs("div", { className: "j-flex j-gap-sm", style: { flexWrap: "wrap" }, children: [_jsx(JimboButton, { tone: "blue", size: "sm", fullWidth: true, onClick: handlePackChange, children: "Re-pick Contents" }), _jsx(JimboButton, { tone: "red", size: "sm", fullWidth: true, onClick: () => handleSlotClear(activePackDetail.ante, activePackDetail.id), children: "Clear This Pack" })] })] })) })] }));
|
|
171
169
|
}
|
|
172
170
|
// ─── Category Selection Menu ─────────────────────────────────────────────────
|
|
173
171
|
export function CategoryMenu({ onSelect, }) {
|
|
@@ -277,7 +275,7 @@ function buildJamlText(antes) {
|
|
|
277
275
|
}
|
|
278
276
|
}
|
|
279
277
|
}
|
|
280
|
-
|
|
278
|
+
const lines = [];
|
|
281
279
|
lines.push("name: My Custom Seed Map");
|
|
282
280
|
lines.push("author: JamlBuilder");
|
|
283
281
|
lines.push("description: Auto-generated from the visual editor.");
|
|
@@ -2,6 +2,5 @@ import type { SlotSelection } from "./MysterySlot.js";
|
|
|
2
2
|
export type JokerRarity = "common" | "uncommon" | "rare" | "legendary";
|
|
3
3
|
export interface JokerPickerProps {
|
|
4
4
|
onSelect: (selection: SlotSelection) => void;
|
|
5
|
-
onCancel: () => void;
|
|
6
5
|
}
|
|
7
|
-
export declare function JokerPicker({ onSelect
|
|
6
|
+
export declare function JokerPicker({ onSelect }: JokerPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -62,7 +62,7 @@ const RARITY_META = {
|
|
|
62
62
|
rare: { label: "Rare", tone: "red", hint: "Found in shops and Buffoon Packs" },
|
|
63
63
|
legendary: { label: "Legendary", tone: "tarot", hint: "Spawns from The Soul only!" },
|
|
64
64
|
};
|
|
65
|
-
export function JokerPicker({ onSelect
|
|
65
|
+
export function JokerPicker({ onSelect }) {
|
|
66
66
|
const [step, setStep] = useState("rarity");
|
|
67
67
|
const [selectedRarity, setSelectedRarity] = useState(null);
|
|
68
68
|
const [search, setSearch] = useState("");
|
|
@@ -96,11 +96,7 @@ export function JokerPicker({ onSelect, onCancel }) {
|
|
|
96
96
|
return (_jsxs("div", { style: { padding: 0, display: "flex", flexDirection: "column" }, children: [step === "rarity" && (_jsx("div", { className: "j-flex-col j-gap-sm", style: { padding: 10 }, children: ["common", "uncommon", "rare", "legendary"].map((rarity) => {
|
|
97
97
|
const meta = RARITY_META[rarity];
|
|
98
98
|
return (_jsx(JimboButton, { tone: meta.tone, size: "md", fullWidth: true, onClick: () => handleRaritySelect(rarity), children: _jsxs("span", { style: { display: "flex", flexDirection: "column", gap: 2, textAlign: "left", width: "100%" }, children: [_jsx("span", { children: meta.label }), _jsx("span", { style: { fontSize: 9, opacity: 0.7 }, children: meta.hint })] }) }, rarity));
|
|
99
|
-
}) })), step === "specific" && selectedRarity && (_jsxs(_Fragment, { children: [
|
|
100
|
-
justifyContent: "space-between",
|
|
101
|
-
padding: "8px 10px",
|
|
102
|
-
borderBottom: `2px solid ${C.PANEL_EDGE}`,
|
|
103
|
-
}, children: [_jsx(JimboButton, { tone: "orange", size: "xs", onClick: () => setStep("rarity"), children: "\u2190 Back" }), _jsxs(JimboText, { size: "md", children: [RARITY_META[selectedRarity].label, " Jokers"] }), _jsx("div", { style: { width: 44 } })] }), _jsx("div", { className: "j-flex j-gap-sm", style: { padding: "8px 10px 4px" }, children: _jsx("input", { className: "j-seed-input__field", type: "text", placeholder: "Search jokers...", value: search, onChange: (e) => setSearch(e.target.value), style: { fontSize: 13, padding: "6px 10px", textTransform: "none", letterSpacing: "0.04em" } }) }), selectedRarity === "legendary" && (_jsx("div", { className: "j-inner-panel", style: { margin: "4px 10px 6px", padding: "6px 10px" }, children: _jsx(JimboText, { size: "xs", tone: "purple", children: "Legendary jokers spawn from The Soul. Find it in Arcana Pack, Spectral Pack, Charm Tag, or Ethereal Tag only!" }) })), _jsxs("div", { style: {
|
|
99
|
+
}) })), step === "specific" && selectedRarity && (_jsxs(_Fragment, { children: [_jsx("div", { className: "j-flex j-items-center j-justify-center", style: { padding: "8px 10px 4px" }, children: _jsxs(JimboText, { size: "md", children: [RARITY_META[selectedRarity].label, " Jokers"] }) }), _jsx("div", { className: "j-flex j-gap-sm", style: { padding: "4px 10px 4px" }, children: _jsx("input", { className: "j-seed-input__field", type: "text", placeholder: "Search jokers...", value: search, onChange: (e) => setSearch(e.target.value), style: { fontSize: 13, padding: "6px 10px", textTransform: "none", letterSpacing: "0.04em" } }) }), selectedRarity === "legendary" && (_jsx("div", { className: "j-inner-panel", style: { margin: "4px 10px 6px", padding: "6px 10px" }, children: _jsx(JimboText, { size: "xs", tone: "purple", children: "Legendary jokers spawn from The Soul. Find it in Arcana Pack, Spectral Pack, Charm Tag, or Ethereal Tag only!" }) })), _jsxs("div", { style: {
|
|
104
100
|
display: "grid",
|
|
105
101
|
gridTemplateColumns: "repeat(auto-fill, minmax(64px, 1fr))",
|
|
106
102
|
gap: 6,
|
|
@@ -113,5 +109,5 @@ export function JokerPicker({ onSelect, onCancel }) {
|
|
|
113
109
|
padding: 4,
|
|
114
110
|
borderRadius: 4,
|
|
115
111
|
cursor: "pointer",
|
|
116
|
-
}, children: [_jsx(JimboSprite, { name: joker.name, sheet: "Jokers", width: 48 }), _jsx(JimboText, { size: "micro", tone: "grey", style: { maxWidth: 60, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: joker.name })] }, joker.name))), filteredJokers.length === 0 && (_jsx("div", { style: { gridColumn: "1 / -1", padding: 20, textAlign: "center" }, children: _jsxs(JimboText, { size: "sm", tone: "grey", children: ["No jokers match \"", search, "\""] }) }))] })] }))] }));
|
|
112
|
+
}, children: [_jsx(JimboSprite, { name: joker.name, sheet: "Jokers", width: 48 }), _jsx(JimboText, { size: "micro", tone: "grey", style: { maxWidth: 60, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: joker.name })] }, joker.name))), filteredJokers.length === 0 && (_jsx("div", { style: { gridColumn: "1 / -1", padding: 20, textAlign: "center" }, children: _jsxs(JimboText, { size: "sm", tone: "grey", children: ["No jokers match \"", search, "\""] }) }))] }), _jsx("div", { style: { padding: "4px 10px 10px" }, children: _jsx(JimboButton, { tone: "orange", size: "md", fullWidth: true, onClick: () => setStep("rarity"), children: "Back" }) })] }))] }));
|
|
117
113
|
}
|
|
@@ -11,21 +11,6 @@ const ZONE_BORDER = {
|
|
|
11
11
|
mustnot: C.ORANGE,
|
|
12
12
|
};
|
|
13
13
|
// ─── Sheet → "Any" wildcard mapping ─────────────────────────────────────────
|
|
14
|
-
function getWildcardName(category) {
|
|
15
|
-
if (!category)
|
|
16
|
-
return null;
|
|
17
|
-
switch (category) {
|
|
18
|
-
case "joker": return null; // uses generic mystery
|
|
19
|
-
case "voucher": return null;
|
|
20
|
-
case "tag": return null;
|
|
21
|
-
case "boss": return null;
|
|
22
|
-
case "tarot": return null;
|
|
23
|
-
case "spectral": return null;
|
|
24
|
-
case "planet": return null;
|
|
25
|
-
case "pack": return null;
|
|
26
|
-
default: return null;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
14
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
30
15
|
export function MysterySlot({ zone, sheetType, selection, width = 56, onTap, onClear, style, }) {
|
|
31
16
|
const [hover, setHover] = useState(false);
|
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
2
|
-
declare let MotelyWasmEvents: any;
|
|
3
|
-
declare let activeSearch: {
|
|
4
|
-
cancel(): void;
|
|
5
|
-
} | null;
|
|
6
|
-
declare let activeSearchRunId: number;
|
|
7
|
-
type WorkerMessage = {
|
|
8
|
-
type: "init";
|
|
9
|
-
url: string;
|
|
10
|
-
} | {
|
|
11
|
-
type: "start";
|
|
12
|
-
jaml: string;
|
|
13
|
-
mode?: string;
|
|
14
|
-
count?: number;
|
|
15
|
-
aesthetic?: number;
|
|
16
|
-
seeds?: string[];
|
|
17
|
-
keywords?: string;
|
|
18
|
-
padding?: string;
|
|
19
|
-
batchCharCount?: number;
|
|
20
|
-
startBatch?: string;
|
|
21
|
-
endBatch?: string;
|
|
22
|
-
} | {
|
|
23
|
-
type: "stop";
|
|
24
|
-
} | {
|
|
25
|
-
type: "get_tally_labels";
|
|
26
|
-
jaml: string;
|
|
27
|
-
};
|
|
28
|
-
declare function post(message: Record<string, unknown>): void;
|
|
29
|
-
declare function resetEventHandlers(): void;
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
let MotelyWasm = null;
|
|
3
2
|
let MotelyWasmEvents = null;
|
|
4
3
|
let activeSearch = null;
|
|
@@ -46,6 +45,8 @@ self.addEventListener("message", async (e) => {
|
|
|
46
45
|
activeSearch = null;
|
|
47
46
|
}
|
|
48
47
|
}
|
|
48
|
+
if (!MotelyWasmEvents)
|
|
49
|
+
return;
|
|
49
50
|
MotelyWasmEvents.notifyResult = (seed, score, tallyColumns) => {
|
|
50
51
|
if (runId !== activeSearchRunId)
|
|
51
52
|
return;
|
|
@@ -65,19 +66,19 @@ self.addEventListener("message", async (e) => {
|
|
|
65
66
|
try {
|
|
66
67
|
const mode = msg.mode || "random";
|
|
67
68
|
if (mode === "random") {
|
|
68
|
-
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
69
|
+
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count || 1);
|
|
69
70
|
}
|
|
70
71
|
else if (mode === "aesthetic") {
|
|
71
|
-
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic);
|
|
72
|
+
activeSearch = MotelyWasm.startAestheticSearch(msg.jaml, msg.aesthetic || 0);
|
|
72
73
|
}
|
|
73
74
|
else if (mode === "seedList") {
|
|
74
|
-
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds);
|
|
75
|
+
activeSearch = MotelyWasm.startSeedListSearch(msg.jaml, msg.seeds || []);
|
|
75
76
|
}
|
|
76
77
|
else if (mode === "keyword") {
|
|
77
|
-
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords, msg.padding || "");
|
|
78
|
+
activeSearch = MotelyWasm.startKeywordSearch(msg.jaml, msg.keywords || "", msg.padding || "");
|
|
78
79
|
}
|
|
79
80
|
else if (mode === "sequential") {
|
|
80
|
-
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount, BigInt(msg.startBatch || "0"), BigInt(msg.endBatch || "0"));
|
|
81
|
+
activeSearch = MotelyWasm.startSequentialSearch(msg.jaml, msg.batchCharCount || 1, BigInt(msg.startBatch || "0"), BigInt(msg.endBatch || "0"));
|
|
81
82
|
}
|
|
82
83
|
else {
|
|
83
84
|
post({ type: "error", message: `Unknown search mode: ${mode}` });
|
|
@@ -115,3 +116,4 @@ self.addEventListener("message", async (e) => {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
});
|
|
119
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, useCallback } from "react";
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState, useCallback } from "react";
|
|
2
2
|
/**
|
|
3
3
|
* Encapsulates IntersectionObserver logic.
|
|
4
4
|
*/
|
|
@@ -26,7 +26,7 @@ export function useIntersectionObserver(options = {}) {
|
|
|
26
26
|
if (observer.current)
|
|
27
27
|
observer.current.disconnect();
|
|
28
28
|
};
|
|
29
|
-
}, [node, options.root, options.rootMargin, options.threshold, options.freezeOnceVisible]);
|
|
29
|
+
}, [node, options, options.root, options.rootMargin, options.threshold, options.freezeOnceVisible]);
|
|
30
30
|
return { ref, entry };
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
@@ -34,7 +34,9 @@ export function useIntersectionObserver(options = {}) {
|
|
|
34
34
|
*/
|
|
35
35
|
export function useInfiniteScroll(onVisible, options = {}, active = true) {
|
|
36
36
|
const onVisibleRef = useRef(onVisible);
|
|
37
|
-
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
onVisibleRef.current = onVisible;
|
|
39
|
+
});
|
|
38
40
|
const { ref, entry } = useIntersectionObserver({
|
|
39
41
|
...options,
|
|
40
42
|
threshold: options.threshold ?? 0.1,
|
package/dist/hooks/useSearch.js
CHANGED
|
@@ -17,8 +17,12 @@ export function useSearch(motelyWasmUrl) {
|
|
|
17
17
|
const workerRef = useRef(null);
|
|
18
18
|
const readyRef = useRef(false); // Worker is NOT implicitly ready, must wait for 'ready' message
|
|
19
19
|
const speedRef = useRef({ lastSearched: 0n, lastTime: 0, ema: 0 });
|
|
20
|
-
|
|
20
|
+
const [prevUrl, setPrevUrl] = useState(motelyWasmUrl);
|
|
21
|
+
if (motelyWasmUrl !== prevUrl) {
|
|
22
|
+
setPrevUrl(motelyWasmUrl);
|
|
21
23
|
setState((s) => ({ ...s, status: "idle" }));
|
|
24
|
+
}
|
|
25
|
+
useEffect(() => {
|
|
22
26
|
const worker = createWorker();
|
|
23
27
|
workerRef.current = worker;
|
|
24
28
|
if (motelyWasmUrl) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { useState, useEffect, useRef, useCallback, useLayoutEffect } from "react";
|
|
3
3
|
const DEFAULT_PULL = 12;
|
|
4
4
|
/**
|
|
5
5
|
* Generic stream hook for iterating any motely-wasm analyzer stream.
|
|
@@ -16,12 +16,15 @@ export function useMotelyStream(initStream, nextItem, deps, initialItems = []) {
|
|
|
16
16
|
const [loading, setLoading] = useState(true);
|
|
17
17
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
18
18
|
const nextRef = useRef(nextItem);
|
|
19
|
-
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
nextRef.current = nextItem;
|
|
21
|
+
});
|
|
20
22
|
const busyRef = useRef(false);
|
|
21
23
|
const genRef = useRef(0);
|
|
22
24
|
useEffect(() => {
|
|
23
25
|
const gen = ++genRef.current;
|
|
24
26
|
const base = initialItems.map(i => ({ ...i }));
|
|
27
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
25
28
|
setItems(base);
|
|
26
29
|
setError(null);
|
|
27
30
|
setReady(false);
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { useSearch, type SearchResult, type SearchStatus, type UseSearchState, }
|
|
|
25
25
|
export { useAnalyzer, type AnalyzerStatus, type AnalyzerLive, type MotelyJsRunState, } from "./hooks/useAnalyzer.js";
|
|
26
26
|
export { setMotelyEnums as setMotelyDisplayEnums } from "./motelyDisplay.js";
|
|
27
27
|
export { setMotelyEnums as setMotelyDecoderEnums } from "./decode/motelyItemDecoder.js";
|
|
28
|
+
export { PaginatedFilterBrowser, type PaginatedFilterBrowserProps, type FilterItem, } from "./components/PaginatedFilterBrowser.js";
|
|
28
29
|
export { JamlAestheticSelector, type JamlAestheticSelectorProps, type JamlAestheticOption, } from "./components/JamlAestheticSelector.js";
|
|
29
30
|
export { JamlSeedInput, type JamlSeedInputProps, } from "./components/JamlSeedInput.js";
|
|
30
31
|
export { JamlMapEditor, JokerPicker, MysterySlot, CategoryPicker, type JamlMapEditorProps, type JokerPickerProps, type JokerRarity, type MysterySlotProps, type SlotCategory, type SlotSelection, type CategoryPickerConfig, type CategoryPickerProps, VOUCHER_PICKER_CONFIG, TAG_PICKER_CONFIG, BOSS_PICKER_CONFIG, TAROT_PICKER_CONFIG, PLANET_PICKER_CONFIG, SPECTRAL_PICKER_CONFIG, PACK_PICKER_CONFIG, } from "./components/jamlMap/index.js";
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ export { useAnalyzer, } from "./hooks/useAnalyzer.js";
|
|
|
29
29
|
// can resolve enum keys without statically importing motely-wasm.
|
|
30
30
|
export { setMotelyEnums as setMotelyDisplayEnums } from "./motelyDisplay.js";
|
|
31
31
|
export { setMotelyEnums as setMotelyDecoderEnums } from "./decode/motelyItemDecoder.js";
|
|
32
|
+
export { PaginatedFilterBrowser, } from "./components/PaginatedFilterBrowser.js";
|
|
32
33
|
export { JamlAestheticSelector, } from "./components/JamlAestheticSelector.js";
|
|
33
34
|
export { JamlSeedInput, } from "./components/JamlSeedInput.js";
|
|
34
35
|
export { JamlMapEditor, JokerPicker, MysterySlot, CategoryPicker, VOUCHER_PICKER_CONFIG, TAG_PICKER_CONFIG, BOSS_PICKER_CONFIG, TAROT_PICKER_CONFIG, PLANET_PICKER_CONFIG, SPECTRAL_PICKER_CONFIG, PACK_PICKER_CONFIG, } from "./components/jamlMap/index.js";
|
package/dist/lib/cardParser.d.ts
CHANGED
package/dist/lib/cardParser.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
const ENHANCEMENTS = ["Bonus", "Mult", "Wild", "Lucky", "Glass", "Steel", "Stone", "Gold"];
|
|
2
2
|
const SEALS = ["Gold", "Purple", "Red", "Blue"];
|
|
3
3
|
const EDITIONS = ["Foil", "Holographic", "Polychrome", "Negative"];
|
|
4
|
-
const RANKS = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"];
|
|
5
|
-
const SUITS = ["Hearts", "Clubs", "Diamonds", "Spades"];
|
|
6
4
|
// Internal Balatro short codes
|
|
7
5
|
const RANK_MAP = {
|
|
8
6
|
"2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8", "9": "9", "10": "10",
|
|
@@ -15,21 +13,25 @@ export function parseCardToken(item) {
|
|
|
15
13
|
if (!item)
|
|
16
14
|
return null;
|
|
17
15
|
// 1. If it's already an object with rank/suit
|
|
18
|
-
if (typeof item === 'object'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
if (typeof item === 'object') {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const obj = item;
|
|
19
|
+
if (obj.rank || obj.base) {
|
|
20
|
+
let rank = obj.rank || obj.base?.[2] || obj.base?.[0]; // Handle different analyzer versions
|
|
21
|
+
let suit = obj.suit || obj.base?.[0];
|
|
22
|
+
// Normalize
|
|
23
|
+
rank = RANK_MAP[rank] || rank;
|
|
24
|
+
suit = SUIT_MAP[suit] || suit;
|
|
25
|
+
if (suit && !suit.endsWith('s'))
|
|
26
|
+
suit += 's';
|
|
27
|
+
return {
|
|
28
|
+
rank: rank || "Ace",
|
|
29
|
+
suit: suit || "Spades",
|
|
30
|
+
enhancement: obj.enhancement || obj.modifier || null,
|
|
31
|
+
seal: obj.seal || null,
|
|
32
|
+
edition: obj.edition || null
|
|
33
|
+
};
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
// 2. If it's a string token
|
|
35
37
|
if (typeof item === 'string') {
|
package/dist/lib/const.d.ts
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { BuyMetaData } from "./classes/BuyMetaData.ts";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export declare const
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const
|
|
3
|
+
export interface Pos {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
export interface Entity {
|
|
8
|
+
name: string;
|
|
9
|
+
pos: Pos;
|
|
10
|
+
animated?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const jokers: Entity[];
|
|
13
|
+
export declare const jokerFaces: Entity[];
|
|
14
|
+
export declare const consumablesFaces: Entity[];
|
|
15
|
+
export declare const boosterPacks: Entity[];
|
|
16
|
+
export declare const tarotsAndPlanets: Entity[];
|
|
17
|
+
export declare const tags: Entity[];
|
|
18
|
+
export declare const vouchers: Entity[];
|
|
19
|
+
export declare const bosses: Entity[];
|
|
20
|
+
export declare const editionMap: Record<string, number>;
|
|
21
|
+
export declare const stickerMap: Record<string, Pos>;
|
|
22
|
+
export declare const options: string[];
|
|
23
|
+
export declare const blinds: string[];
|
|
15
24
|
export declare const blindsCamelCase: string[];
|
|
16
25
|
export declare const SeedsWithLegendary: Array<string>;
|
|
17
26
|
export declare const popularSeeds: Array<string>;
|
|
@@ -24,7 +33,7 @@ export interface AnalyzeOptions {
|
|
|
24
33
|
[key: string]: BuyMetaData;
|
|
25
34
|
};
|
|
26
35
|
updates: Array<{
|
|
27
|
-
[key: string]:
|
|
36
|
+
[key: string]: unknown;
|
|
28
37
|
}>;
|
|
29
38
|
unlocks: Array<string>;
|
|
30
39
|
events: Array<Event>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const
|
|
1
|
+
import { Motely } from 'motely-wasm';
|
|
2
|
+
import { CLAUSE_TYPE_KEYS, SOURCE_KEYS, } from '../jaml/jamlSchema.js';
|
|
3
|
+
// UI options derived from motely-wasm directly
|
|
4
|
+
export const DECK_OPTIONS = Object.keys(Motely.MotelyDeck).filter(k => isNaN(Number(k)));
|
|
5
|
+
export const STAKE_OPTIONS = Object.keys(Motely.MotelyStake).filter(k => isNaN(Number(k)));
|
|
5
6
|
export const ANTE_OPTIONS = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
6
7
|
export const SLOT_OPTIONS = [1, 2, 3, 4, 5];
|
|
7
|
-
export const RANK_OPTIONS =
|
|
8
|
-
export const SUIT_OPTIONS =
|
|
9
|
-
export const ENHANCEMENT_OPTIONS =
|
|
10
|
-
export const EDITION_OPTIONS =
|
|
11
|
-
export const SEAL_OPTIONS =
|
|
8
|
+
export const RANK_OPTIONS = Object.keys(Motely.MotelyStandardcardRank).filter(k => isNaN(Number(k)));
|
|
9
|
+
export const SUIT_OPTIONS = Object.keys(Motely.MotelyStandardcardSuit).filter(k => isNaN(Number(k)));
|
|
10
|
+
export const ENHANCEMENT_OPTIONS = Object.keys(Motely.MotelyItemEnhancement).filter(k => isNaN(Number(k)) && k !== "None");
|
|
11
|
+
export const EDITION_OPTIONS = Object.keys(Motely.MotelyItemEdition).filter(k => isNaN(Number(k)) && k !== "None");
|
|
12
|
+
export const SEAL_OPTIONS = Object.keys(Motely.MotelyItemSeal).filter(k => isNaN(Number(k)) && k !== "None");
|
|
12
13
|
export const CLAUSE_TYPES = [...CLAUSE_TYPE_KEYS];
|
|
13
14
|
export const SOURCE_OPTIONS = [...SOURCE_KEYS];
|
|
@@ -84,7 +84,7 @@ function parseJamlToFilter(text) {
|
|
|
84
84
|
// 2. Detect Root Properties (indent 0)
|
|
85
85
|
const rootMatch = line.match(/^(\w+):\s*(.+)$/);
|
|
86
86
|
if (rootMatch && !line.startsWith(' ') && !line.startsWith('-')) {
|
|
87
|
-
const [
|
|
87
|
+
const [, key, val] = rootMatch;
|
|
88
88
|
if (['name', 'deck', 'stake', 'description', 'author'].includes(key)) {
|
|
89
89
|
filter[key] = val.trim();
|
|
90
90
|
}
|
|
@@ -95,7 +95,7 @@ function parseJamlToFilter(text) {
|
|
|
95
95
|
if (currentSection === 'defaults') {
|
|
96
96
|
const propMatch = trimmed.match(/^(\w+):\s*(.+)$/);
|
|
97
97
|
if (propMatch) {
|
|
98
|
-
const [
|
|
98
|
+
const [, key, val] = propMatch;
|
|
99
99
|
if (key === 'antes')
|
|
100
100
|
filter.defaults.antes = parseNumArray(val);
|
|
101
101
|
if (key === 'packSlots')
|
|
@@ -118,7 +118,7 @@ function parseJamlToFilter(text) {
|
|
|
118
118
|
if (currentClause) {
|
|
119
119
|
const propMatch = trimmed.match(/^(\w+):\s*(.+)$/);
|
|
120
120
|
if (propMatch) {
|
|
121
|
-
const [
|
|
121
|
+
const [, key, val] = propMatch;
|
|
122
122
|
if (key === 'antes')
|
|
123
123
|
currentClause.antes = parseNumArray(val);
|
|
124
124
|
else if (key === 'sources')
|
|
@@ -140,10 +140,10 @@ function parseJamlToFilter(text) {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
function parseNumArray(str) {
|
|
143
|
-
return str.replace(/[
|
|
143
|
+
return str.replace(/[[\]]/g, '').split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
144
144
|
}
|
|
145
145
|
function parseStringArray(str) {
|
|
146
|
-
return str.replace(/[
|
|
146
|
+
return str.replace(/[[\]]/g, '').split(',').map(s => s.trim()).filter(s => !!s);
|
|
147
147
|
}
|
|
148
148
|
function filterToJaml(filter) {
|
|
149
149
|
const lines = [];
|
|
@@ -4,9 +4,15 @@ export function useSeedAnalyzer(motely, seed) {
|
|
|
4
4
|
const [data, setData] = useState(null);
|
|
5
5
|
const [loading, setLoading] = useState(false);
|
|
6
6
|
const [error, setError] = useState(null);
|
|
7
|
-
|
|
7
|
+
const [prevInputs, setPrevInputs] = useState({ seed, motely });
|
|
8
|
+
if (seed !== prevInputs.seed || motely !== prevInputs.motely) {
|
|
9
|
+
setPrevInputs({ seed, motely });
|
|
8
10
|
if (!seed || seed === "LOCKED" || !motely) {
|
|
9
11
|
setData(null);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!seed || seed === "LOCKED" || !motely) {
|
|
10
16
|
return;
|
|
11
17
|
}
|
|
12
18
|
const abortController = new AbortController();
|
|
@@ -4,9 +4,23 @@
|
|
|
4
4
|
* Derives all JAML schema constants from motely-wasm/jaml.schema.json.
|
|
5
5
|
* All JAML-aware files import from here — single swap point if the API changes.
|
|
6
6
|
*/
|
|
7
|
+
type SchemaNode = {
|
|
8
|
+
enum?: string[];
|
|
9
|
+
items?: {
|
|
10
|
+
enum?: string[];
|
|
11
|
+
};
|
|
12
|
+
properties?: Record<string, SchemaNode>;
|
|
13
|
+
};
|
|
14
|
+
interface JamlSchema {
|
|
15
|
+
version?: string;
|
|
16
|
+
definitions?: {
|
|
17
|
+
clause?: SchemaNode;
|
|
18
|
+
};
|
|
19
|
+
properties?: Record<string, SchemaNode>;
|
|
20
|
+
}
|
|
7
21
|
export declare const JAML_SCHEMA_VERSION: string;
|
|
8
22
|
/** The raw JSON schema object. */
|
|
9
|
-
export declare const jamlSchema:
|
|
23
|
+
export declare const jamlSchema: JamlSchema;
|
|
10
24
|
/** Root-level metadata keys (name, author, deck, etc.) */
|
|
11
25
|
export declare const METADATA_KEYS: readonly string[];
|
|
12
26
|
/** Section keys that contain clause arrays. */
|
|
@@ -26,12 +40,12 @@ export declare function getValidValuesForKey(key: string): readonly string[] | n
|
|
|
26
40
|
* Get the available properties for a given clause type.
|
|
27
41
|
* Returns all clause property keys (the type system doesn't restrict per-type in JSON schema v7).
|
|
28
42
|
*/
|
|
29
|
-
export declare function getAvailablePropsForType(
|
|
43
|
+
export declare function getAvailablePropsForType(): readonly string[];
|
|
30
44
|
/**
|
|
31
45
|
* Check if a property is invalid for a clause type.
|
|
32
46
|
* In the v7 schema, all properties are available on all clause types.
|
|
33
47
|
*/
|
|
34
|
-
export declare function isInvalidPropForType(
|
|
48
|
+
export declare function isInvalidPropForType(): boolean;
|
|
35
49
|
/**
|
|
36
50
|
* Check if a value is invalid for a property.
|
|
37
51
|
*/
|
|
@@ -52,3 +66,4 @@ export declare const STICKER_VALUES: readonly string[];
|
|
|
52
66
|
* All JAML keywords — sections, metadata keys, clause types, and property keys.
|
|
53
67
|
*/
|
|
54
68
|
export declare const ALL_JAML_KEYWORDS: string[];
|
|
69
|
+
export {};
|
|
@@ -51,14 +51,14 @@ export function getValidValuesForKey(key) {
|
|
|
51
51
|
* Get the available properties for a given clause type.
|
|
52
52
|
* Returns all clause property keys (the type system doesn't restrict per-type in JSON schema v7).
|
|
53
53
|
*/
|
|
54
|
-
export function getAvailablePropsForType(_clauseType) {
|
|
54
|
+
export function getAvailablePropsForType( /* _clauseType */) {
|
|
55
55
|
return PROPERTY_KEYS;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* Check if a property is invalid for a clause type.
|
|
59
59
|
* In the v7 schema, all properties are available on all clause types.
|
|
60
60
|
*/
|
|
61
|
-
export function isInvalidPropForType(_prop, _clauseType) {
|
|
61
|
+
export function isInvalidPropForType( /* _prop, _clauseType */) {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
@@ -34,7 +34,7 @@ export interface ParsedSeed {
|
|
|
34
34
|
* ]
|
|
35
35
|
* }
|
|
36
36
|
*/
|
|
37
|
-
export declare function parseDailyRitualSeed(raw:
|
|
37
|
+
export declare function parseDailyRitualSeed(raw: Record<string, unknown>): ParsedSeed;
|
|
38
38
|
/**
|
|
39
39
|
* Group items by type for rendering
|
|
40
40
|
*/
|
|
@@ -36,12 +36,13 @@ export function parseDailyRitualSeed(raw) {
|
|
|
36
36
|
const items = [];
|
|
37
37
|
// Parse each field looking for pattern: letters + number (e.g., "wj2", "hc1")
|
|
38
38
|
Object.keys(raw).forEach(key => {
|
|
39
|
+
if (key === 'id' || key === 't' || key === 's' || key === 'w')
|
|
40
|
+
return;
|
|
39
41
|
const match = key.match(/^([a-z]+)(\d+)$/);
|
|
40
42
|
if (match) {
|
|
41
43
|
const [, code, ante] = match;
|
|
42
44
|
const itemData = ITEM_MAP[code];
|
|
43
45
|
if (itemData) {
|
|
44
|
-
const count = raw[key]; // Could be used for duplicates
|
|
45
46
|
items.push({
|
|
46
47
|
...itemData,
|
|
47
48
|
ante: Number(ante)
|