jaml-ui 0.24.20 → 0.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -13
- package/assets/WeeJokerExampleDAilyGame.png +0 -0
- package/assets/balatro-stake-chips.png +0 -0
- package/dist/assets.d.ts +1 -2
- package/dist/chunks/Layer-BBPJFHfs.js +17 -0
- package/dist/chunks/Layer-BBPJFHfs.js.map +1 -0
- package/dist/chunks/assets-RWUiFSTc.js +37 -0
- package/dist/chunks/assets-RWUiFSTc.js.map +1 -0
- package/dist/chunks/motelyItemDecoder-CueyZ0XD.js +6039 -0
- package/dist/chunks/motelyItemDecoder-CueyZ0XD.js.map +1 -0
- package/dist/chunks/spriteMapper-CFjN0_TV.js +2415 -0
- package/dist/chunks/spriteMapper-CFjN0_TV.js.map +1 -0
- package/dist/chunks/tokens-B65Fzble.js +57 -0
- package/dist/chunks/tokens-B65Fzble.js.map +1 -0
- package/dist/chunks/ui-5cBy3zAm.js +1387 -0
- package/dist/chunks/ui-5cBy3zAm.js.map +1 -0
- package/dist/components/AnalyzerExplorer.d.ts +1 -1
- package/dist/components/CardFan.d.ts +1 -1
- package/dist/components/CardList.d.ts +1 -1
- package/dist/components/DeckSprite.d.ts +1 -1
- package/dist/components/JamlAestheticSelector.d.ts +1 -1
- package/dist/components/JamlAnalyzerFullscreen.d.ts +5 -5
- package/dist/components/JamlCurator.d.ts +1 -6
- package/dist/components/JamlIde.d.ts +5 -5
- package/dist/components/JamlSeedInput.d.ts +1 -1
- package/dist/components/JamlSpeedometer.d.ts +1 -1
- package/dist/components/MotelyVersionBadge.d.ts +1 -1
- package/dist/components/Standardcard.d.ts +1 -1
- package/dist/components/jamlMap/CategoryPicker.d.ts +3 -3
- package/dist/components/jamlMap/JamlMapEditor.d.ts +1 -1
- package/dist/components/jamlMap/JokerPicker.d.ts +1 -1
- package/dist/components/jamlMap/MysterySlot.d.ts +2 -2
- package/dist/components/jamlMap/index.d.ts +4 -4
- package/dist/core.d.ts +5 -5
- package/dist/core.js +27 -5
- package/dist/core.js.map +1 -0
- package/dist/decode/motelyItemDecoder.d.ts +0 -10
- package/dist/decode/motelySprite.d.ts +1 -1
- package/dist/fonts/m6x11plus.otf +0 -0
- package/dist/hooks/analyzerStreamRegistry.d.ts +2 -2
- package/dist/hooks/useAnalyzer.d.ts +3 -3
- package/dist/hooks/useSearch.d.ts +2 -2
- package/dist/index.d.ts +29 -31
- package/dist/index.js +16721 -34
- package/dist/index.js.map +1 -0
- package/dist/lib/const.d.ts +2 -2
- package/dist/lib/hooks/useDragScroll.d.ts +1 -1
- package/dist/lib/hooks/useJamlFilter.d.ts +2 -2
- package/dist/lib/hooks/useSeedAnalyzer.d.ts +2 -2
- package/dist/lib/utils.d.ts +1 -1
- package/dist/motely.d.ts +4 -3
- package/dist/motely.js +65 -3
- package/dist/motely.js.map +1 -0
- package/dist/motelyBoot.d.ts +2 -0
- package/dist/motelyDisplay.d.ts +0 -2
- package/dist/r3f/Card3D.d.ts +2 -2
- package/dist/r3f/JimboBillboard.d.ts +1 -1
- package/dist/r3f.js +235 -3
- package/dist/r3f.js.map +1 -0
- package/dist/render/CanvasRenderer.d.ts +1 -1
- package/dist/sprites/spriteData.d.ts +1 -6
- package/dist/sprites/spriteMapper.d.ts +1 -1
- package/dist/ui/JimboBadge.d.ts +1 -1
- package/dist/ui/JimboFloating.d.ts +1 -1
- package/dist/ui/JimboIconButton.d.ts +1 -1
- package/dist/ui/JimboSelect.d.ts +1 -1
- package/dist/ui/footer.d.ts +2 -3
- package/dist/ui/hooks.d.ts +1 -1
- package/dist/ui/ide/DeckSprite.d.ts +1 -1
- package/dist/ui/jimbo.css +2 -1856
- package/dist/ui/jimboApp.d.ts +1 -1
- package/dist/ui/jimboFilterBar.d.ts +1 -1
- package/dist/ui/jimboFlankNav.d.ts +1 -1
- package/dist/ui/jimboInfoCard.d.ts +1 -1
- package/dist/ui/jimboInset.d.ts +1 -1
- package/dist/ui/jimboStatGrid.d.ts +1 -1
- package/dist/ui/jimboText.d.ts +1 -1
- package/dist/ui/jimboTooltip.d.ts +2 -2
- package/dist/ui/mascot/SeedMascot.d.ts +2 -2
- package/dist/ui/panel.d.ts +1 -1
- package/dist/ui/radial/RadialBadge.d.ts +1 -2
- package/dist/ui/radial/RadialButton.d.ts +1 -2
- package/dist/ui/radial/RadialMenu.d.ts +2 -2
- package/dist/ui/radial/RadialPill.d.ts +1 -2
- package/dist/ui/radial/index.d.ts +16 -16
- package/dist/ui/radial/radialMenuStore.d.ts +1 -1
- package/dist/ui/showcase.d.ts +1 -1
- package/dist/ui/sprites.d.ts +2 -2
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +3 -36
- package/dist/utils/gameCardUtils.d.ts +1 -1
- package/dist/utils/jamlVisualFilter.d.ts +1 -7
- package/package.json +13 -25
- package/dist/assets.js +0 -48
- package/dist/components/AnalyzerExplorer.js +0 -391
- package/dist/components/CardFan.js +0 -80
- package/dist/components/CardList.js +0 -5
- package/dist/components/DeckSprite.js +0 -75
- package/dist/components/GameCard.js +0 -355
- package/dist/components/JamlAestheticSelector.js +0 -22
- package/dist/components/JamlAnalyzerFullscreen.js +0 -263
- package/dist/components/JamlCodeEditor.js +0 -137
- package/dist/components/JamlCurator.js +0 -64
- package/dist/components/JamlCurator.stories.d.ts +0 -6
- package/dist/components/JamlCurator.stories.js +0 -14
- package/dist/components/JamlIde.js +0 -193
- package/dist/components/JamlIdeToolbar.js +0 -23
- package/dist/components/JamlIdeVisual.js +0 -218
- package/dist/components/JamlMapPreview.js +0 -121
- package/dist/components/JamlSeedInput.js +0 -26
- package/dist/components/JamlSpeedometer.js +0 -70
- package/dist/components/Jimbolate.js +0 -17
- package/dist/components/MotelyVersionBadge.js +0 -19
- package/dist/components/PaginatedFilterBrowser.js +0 -54
- package/dist/components/RunConfigModal.js +0 -59
- package/dist/components/Standardcard.js +0 -80
- package/dist/components/jamlMap/CategoryPicker.js +0 -135
- package/dist/components/jamlMap/JamlMapEditor.js +0 -304
- package/dist/components/jamlMap/JamlMapEditor.stories.d.ts +0 -7
- package/dist/components/jamlMap/JamlMapEditor.stories.js +0 -26
- package/dist/components/jamlMap/JamlMapEditorDemo.d.ts +0 -8
- package/dist/components/jamlMap/JamlMapEditorDemo.js +0 -323
- package/dist/components/jamlMap/JokerPicker.js +0 -113
- package/dist/components/jamlMap/MysterySlot.js +0 -128
- package/dist/components/jamlMap/MysterySlot.stories.d.ts +0 -7
- package/dist/components/jamlMap/MysterySlot.stories.js +0 -31
- package/dist/components/jamlMap/index.js +0 -4
- package/dist/decode/motelyItemDecoder.js +0 -164
- package/dist/decode/motelySprite.js +0 -84
- package/dist/hooks/analyzerStreamRegistry.js +0 -96
- package/dist/hooks/searchWorker.d.ts +0 -1
- package/dist/hooks/searchWorker.js +0 -119
- package/dist/hooks/searchWorkerCode.d.ts +0 -1
- package/dist/hooks/searchWorkerCode.js +0 -85
- package/dist/hooks/useAnalyzer.js +0 -91
- package/dist/hooks/useIntersectionObserver.js +0 -52
- package/dist/hooks/useSearch.js +0 -161
- package/dist/hooks/useShopStream.js +0 -85
- package/dist/lib/SpriteMapper.js +0 -48
- package/dist/lib/cardParser.js +0 -67
- package/dist/lib/classes/BuyMetaData.js +0 -1
- package/dist/lib/config.js +0 -15
- package/dist/lib/const.js +0 -521
- package/dist/lib/data/constants.js +0 -14
- package/dist/lib/hooks/useDragScroll.js +0 -48
- package/dist/lib/hooks/useJamlFilter.js +0 -219
- package/dist/lib/hooks/useSeedAnalyzer.js +0 -50
- package/dist/lib/jaml/jamlCompletion.js +0 -13
- package/dist/lib/jaml/jamlData.js +0 -6
- package/dist/lib/jaml/jamlObjectives.js +0 -97
- package/dist/lib/jaml/jamlParser.js +0 -47
- package/dist/lib/jaml/jamlPresets.js +0 -61
- package/dist/lib/jaml/jamlSchema.js +0 -91
- package/dist/lib/parseDailyRitual.js +0 -70
- package/dist/lib/tts/getRevealPos.js +0 -16
- package/dist/lib/tts/splitTtsDisplay.js +0 -35
- package/dist/lib/types.js +0 -1
- package/dist/lib/utils.js +0 -5
- package/dist/motelyDisplay.js +0 -59
- package/dist/r3f/Card3D.js +0 -72
- package/dist/r3f/JimboBillboard.js +0 -32
- package/dist/r3f/JimboText3D.js +0 -8
- package/dist/render/CanvasRenderer.js +0 -11
- package/dist/render/Layer.js +0 -18
- package/dist/sprites/spriteData.js +0 -100
- package/dist/sprites/spriteMapper.js +0 -75
- package/dist/stories/Button.d.ts +0 -15
- package/dist/stories/Button.js +0 -7
- package/dist/stories/Button.stories.d.ts +0 -24
- package/dist/stories/Button.stories.js +0 -50
- package/dist/stories/Header.d.ts +0 -12
- package/dist/stories/Header.js +0 -4
- package/dist/stories/Header.stories.d.ts +0 -18
- package/dist/stories/Header.stories.js +0 -26
- package/dist/stories/Page.d.ts +0 -3
- package/dist/stories/Page.js +0 -8
- package/dist/stories/Page.stories.d.ts +0 -12
- package/dist/stories/Page.stories.js +0 -24
- package/dist/ui/Jimbo.stories.d.ts +0 -7
- package/dist/ui/Jimbo.stories.js +0 -28
- package/dist/ui/JimboBadge.js +0 -8
- package/dist/ui/JimboFloating.js +0 -17
- package/dist/ui/JimboIconButton.js +0 -28
- package/dist/ui/JimboInputModal.js +0 -66
- package/dist/ui/JimboSelect.js +0 -43
- package/dist/ui/JimboToggleList.js +0 -5
- package/dist/ui/PanelSplitter.js +0 -78
- package/dist/ui/codeBlock.js +0 -14
- package/dist/ui/footer.js +0 -20
- package/dist/ui/hooks.js +0 -634
- package/dist/ui/ide/AgnosticSeedCard.d.ts +0 -19
- package/dist/ui/ide/AgnosticSeedCard.js +0 -48
- package/dist/ui/ide/DeckSprite.js +0 -2
- package/dist/ui/ide/JamlBuilder.d.ts +0 -1
- package/dist/ui/ide/JamlBuilder.js +0 -112
- package/dist/ui/ide/JamlEditor.js +0 -486
- package/dist/ui/ide/JamlEditorMonaco.d.ts +0 -8
- package/dist/ui/ide/JamlEditorMonaco.js +0 -78
- package/dist/ui/ide/WasmStatus.d.ts +0 -1
- package/dist/ui/ide/WasmStatus.js +0 -42
- package/dist/ui/jimboApp.js +0 -15
- package/dist/ui/jimboBackground.js +0 -27
- package/dist/ui/jimboCopyRow.js +0 -18
- package/dist/ui/jimboFilterBar.js +0 -16
- package/dist/ui/jimboFlankNav.js +0 -18
- package/dist/ui/jimboInfoCard.js +0 -26
- package/dist/ui/jimboInset.js +0 -9
- package/dist/ui/jimboSectionHeader.js +0 -9
- package/dist/ui/jimboStatGrid.js +0 -9
- package/dist/ui/jimboTabs.js +0 -22
- package/dist/ui/jimboText.js +0 -33
- package/dist/ui/jimboTooltip.js +0 -39
- package/dist/ui/jimboWordmark.js +0 -9
- package/dist/ui/mascot/JammySpeechBox.js +0 -30
- package/dist/ui/mascot/SeedMascot.js +0 -17
- package/dist/ui/mascot/index.js +0 -3
- package/dist/ui/mascot/menuConfig.js +0 -12
- package/dist/ui/panel.js +0 -24
- package/dist/ui/radial/RadialBadge.js +0 -43
- package/dist/ui/radial/RadialBreadcrumb.js +0 -18
- package/dist/ui/radial/RadialButton.js +0 -102
- package/dist/ui/radial/RadialMenu.js +0 -168
- package/dist/ui/radial/RadialPill.js +0 -15
- package/dist/ui/radial/index.js +0 -18
- package/dist/ui/radial/radialMenuStore.js +0 -122
- package/dist/ui/radial/radialMenuViewport.js +0 -59
- package/dist/ui/radial/useRadialMenu.js +0 -107
- package/dist/ui/showcase.js +0 -20
- package/dist/ui/sprites.js +0 -77
- package/dist/ui/tokens.js +0 -64
- package/dist/utils/gameCardUtils.js +0 -15
- package/dist/utils/jamlMapPreview.js +0 -106
- package/dist/utils/jamlVisualFilter.js +0 -210
|
@@ -1,80 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
/* eslint-disable react-refresh/only-export-components */
|
|
4
|
-
import { useState, useCallback, useMemo } from "react";
|
|
5
|
-
import { JimboSprite } from "../../ui/sprites.js";
|
|
6
|
-
import { JimboColorOption } from "../../ui/tokens.js";
|
|
7
|
-
import { JimboText } from "../../ui/jimboText.js";
|
|
8
|
-
// ─── Component ───────────────────────────────────────────────────────────────
|
|
9
|
-
const C = JimboColorOption;
|
|
10
|
-
export function CategoryPicker({ config, onSelect }) {
|
|
11
|
-
const [search, setSearch] = useState("");
|
|
12
|
-
const filtered = useMemo(() => {
|
|
13
|
-
if (!search)
|
|
14
|
-
return config.items;
|
|
15
|
-
const q = search.toLowerCase();
|
|
16
|
-
return config.items.filter((item) => item.name.toLowerCase().includes(q));
|
|
17
|
-
}, [config.items, search]);
|
|
18
|
-
const pairedVouchers = useMemo(() => {
|
|
19
|
-
if (config.category !== "voucher")
|
|
20
|
-
return null;
|
|
21
|
-
const bases = config.items.filter((item) => item.pos.y % 2 === 0);
|
|
22
|
-
const pairs = bases.map((base) => {
|
|
23
|
-
const upgrade = config.items.find((u) => u.pos.x === base.pos.x && u.pos.y === base.pos.y + 1);
|
|
24
|
-
return { base, upgrade };
|
|
25
|
-
});
|
|
26
|
-
if (!search)
|
|
27
|
-
return pairs;
|
|
28
|
-
const q = search.toLowerCase();
|
|
29
|
-
return pairs.filter(p => p.base.name.toLowerCase().includes(q) || p.upgrade?.name.toLowerCase().includes(q));
|
|
30
|
-
}, [config.items, search, config.category]);
|
|
31
|
-
const handleSelect = useCallback((item) => {
|
|
32
|
-
onSelect({
|
|
33
|
-
category: config.category,
|
|
34
|
-
value: item.name,
|
|
35
|
-
clauseKey: config.clauseKey,
|
|
36
|
-
});
|
|
37
|
-
}, [onSelect, config]);
|
|
38
|
-
const renderItem = (item, isMuted = false) => (_jsxs("div", { className: "j-juice-hover", onClick: () => handleSelect(item), title: item.name, style: {
|
|
39
|
-
display: "flex",
|
|
40
|
-
flexDirection: "column",
|
|
41
|
-
alignItems: "center",
|
|
42
|
-
gap: 3,
|
|
43
|
-
padding: 4,
|
|
44
|
-
borderRadius: 4,
|
|
45
|
-
cursor: "pointer",
|
|
46
|
-
opacity: isMuted ? 0.3 : 1,
|
|
47
|
-
}, children: [_jsx(JimboSprite, { name: item.name, sheet: config.sheet, width: 48 }), _jsx(JimboText, { size: "micro", tone: "white", style: { lineHeight: 1.1, whiteSpace: "normal", textAlign: "center" }, children: item.name })] }, item.name));
|
|
48
|
-
return (_jsxs("div", { style: { padding: 0, maxWidth: 420, maxHeight: "80vh", display: "flex", flexDirection: "column" }, children: [_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 ${config.title.toLowerCase()}...`, value: search, onChange: (e) => setSearch(e.target.value), style: { fontSize: 13, padding: "6px 10px", textTransform: "none", letterSpacing: "0.04em" } }) }), config.hint && (_jsx("div", { className: "j-inner-panel", style: { margin: "4px 10px 6px", padding: "6px 10px" }, children: _jsx(JimboText, { size: "xs", tone: "grey", children: config.hint }) })), _jsxs("div", { className: "hide-scrollbar", style: {
|
|
49
|
-
display: "flex",
|
|
50
|
-
flexWrap: "wrap",
|
|
51
|
-
gap: 6,
|
|
52
|
-
padding: "8px 10px 10px",
|
|
53
|
-
overflowY: "auto",
|
|
54
|
-
flex: 1,
|
|
55
|
-
alignContent: "flex-start",
|
|
56
|
-
}, children: [config.category === "voucher" && pairedVouchers ? (pairedVouchers.map((pair) => (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: 64 }, children: [renderItem(pair.base, search ? !pair.base.name.toLowerCase().includes(search.toLowerCase()) : false), pair.upgrade && renderItem(pair.upgrade, search ? !pair.upgrade.name.toLowerCase().includes(search.toLowerCase()) : false)] }, pair.base.name)))) : (filtered.map((item) => (_jsx("div", { style: { width: 64 }, children: renderItem(item) }, item.name)))), ((config.category === "voucher" && pairedVouchers?.length === 0) ||
|
|
57
|
-
(config.category !== "voucher" && filtered.length === 0)) && (_jsx("div", { style: { width: "100%", padding: 20, textAlign: "center" }, children: _jsxs(JimboText, { size: "sm", tone: "grey", children: ["No matches for \"", search, "\""] }) }))] })] }));
|
|
58
|
-
}
|
|
59
|
-
// ─── Pre-built configs ───────────────────────────────────────────────────────
|
|
60
|
-
import { VOUCHERS, TAGS, BOSSES, BOOSTER_PACKS, TAROTS_AND_PLANETS } from "../../sprites/spriteData.js";
|
|
61
|
-
// Split consumables by type
|
|
62
|
-
const TAROT_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
63
|
-
const y = c.pos.y;
|
|
64
|
-
return y <= 2 && c.name !== "The Soul" && c.name !== "Black Hole";
|
|
65
|
-
}).filter((c) => {
|
|
66
|
-
return c.pos.y <= 1 || (c.pos.y === 2 && c.pos.x <= 1);
|
|
67
|
-
});
|
|
68
|
-
const PLANET_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
69
|
-
return c.pos.y === 3 ||
|
|
70
|
-
c.name === "Planet X" || c.name === "Ceres" || c.name === "Eris" ||
|
|
71
|
-
c.name === "Black Hole";
|
|
72
|
-
});
|
|
73
|
-
const SPECTRAL_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
74
|
-
return c.pos.y >= 4 || c.name === "The Soul";
|
|
75
|
-
});
|
|
76
|
-
export const VOUCHER_PICKER_CONFIG = {
|
|
77
|
-
title: "Vouchers",
|
|
78
|
-
category: "voucher",
|
|
79
|
-
clauseKey: "voucher",
|
|
80
|
-
sheet: "Vouchers",
|
|
81
|
-
items: VOUCHERS,
|
|
82
|
-
accent: C.ORANGE,
|
|
83
|
-
};
|
|
84
|
-
export const TAG_PICKER_CONFIG = {
|
|
85
|
-
title: "Tags",
|
|
86
|
-
category: "tag",
|
|
87
|
-
clauseKey: "tag",
|
|
88
|
-
sheet: "tags",
|
|
89
|
-
items: TAGS,
|
|
90
|
-
accent: C.GREEN,
|
|
91
|
-
};
|
|
92
|
-
export const BOSS_PICKER_CONFIG = {
|
|
93
|
-
title: "Boss Blinds",
|
|
94
|
-
category: "boss",
|
|
95
|
-
clauseKey: "boss",
|
|
96
|
-
sheet: "BlindChips",
|
|
97
|
-
items: BOSSES,
|
|
98
|
-
accent: C.RED,
|
|
99
|
-
hint: "Boss Blinds appear at the end of each Ante.",
|
|
100
|
-
};
|
|
101
|
-
export const TAROT_PICKER_CONFIG = {
|
|
102
|
-
title: "Tarot Cards",
|
|
103
|
-
category: "tarot",
|
|
104
|
-
clauseKey: "tarotCard",
|
|
105
|
-
sheet: "Tarots",
|
|
106
|
-
items: TAROT_CARDS,
|
|
107
|
-
accent: C.PURPLE,
|
|
108
|
-
hint: "Found in Arcana Packs and shops.",
|
|
109
|
-
};
|
|
110
|
-
export const PLANET_PICKER_CONFIG = {
|
|
111
|
-
title: "Planet Cards",
|
|
112
|
-
category: "planet",
|
|
113
|
-
clauseKey: "planetCard",
|
|
114
|
-
sheet: "Tarots",
|
|
115
|
-
items: PLANET_CARDS,
|
|
116
|
-
accent: C.BLUE,
|
|
117
|
-
hint: "Found in Celestial Packs and shops.",
|
|
118
|
-
};
|
|
119
|
-
export const SPECTRAL_PICKER_CONFIG = {
|
|
120
|
-
title: "Spectral Cards",
|
|
121
|
-
category: "spectral",
|
|
122
|
-
clauseKey: "spectralCard",
|
|
123
|
-
sheet: "Tarots",
|
|
124
|
-
items: SPECTRAL_CARDS,
|
|
125
|
-
accent: C.TEAL_GREY,
|
|
126
|
-
hint: "Found in Spectral Packs. Ghost Deck only for shop spawns!",
|
|
127
|
-
};
|
|
128
|
-
export const PACK_PICKER_CONFIG = {
|
|
129
|
-
title: "Booster Packs",
|
|
130
|
-
category: "pack",
|
|
131
|
-
clauseKey: "pack",
|
|
132
|
-
sheet: "Boosters",
|
|
133
|
-
items: BOOSTER_PACKS,
|
|
134
|
-
accent: C.ORANGE,
|
|
135
|
-
};
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback, useRef, useEffect } from "react";
|
|
4
|
-
import { MysterySlot } from "./MysterySlot.js";
|
|
5
|
-
import { JokerPicker } from "./JokerPicker.js";
|
|
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";
|
|
7
|
-
import { JimboButton, JimboModal } from "../../ui/panel.js";
|
|
8
|
-
import { JimboText } from "../../ui/jimboText.js";
|
|
9
|
-
import { JimboColorOption, withAlpha } from "../../ui/tokens.js";
|
|
10
|
-
import { JimboSprite } from "../../ui/sprites.js";
|
|
11
|
-
// ─── Category menu items ─────────────────────────────────────────────────────
|
|
12
|
-
const C = JimboColorOption;
|
|
13
|
-
const CATEGORIES = [
|
|
14
|
-
{ key: "joker", label: "Joker", sprite: "Joker", sheet: "Jokers", tone: "blue", hint: "Shop, Buffoon Pack" },
|
|
15
|
-
{ key: "voucher", label: "Voucher", sprite: "Blank", sheet: "Vouchers", tone: "orange", hint: "1 per Ante in shop" },
|
|
16
|
-
{ key: "tarot", label: "Tarot Card", sprite: "The Fool", sheet: "Tarots", tone: "tarot", hint: "Arcana Pack, shop" },
|
|
17
|
-
{ key: "planet", label: "Planet Card", sprite: "Mercury", sheet: "Tarots", tone: "planet", hint: "Celestial Pack, shop" },
|
|
18
|
-
{ key: "spectral", label: "Spectral Card", sprite: "Grim", sheet: "Tarots", tone: "spectral", hint: "Ghost Deck, Spectral Pack" },
|
|
19
|
-
{ key: "tag", label: "Tag", sprite: "Uncommon Tag", sheet: "tags", tone: "green", hint: "Skip blind reward" },
|
|
20
|
-
{ key: "boss", label: "Boss Blind", sprite: "The Hook", sheet: "BlindChips", tone: "red", hint: "End of each Ante" },
|
|
21
|
-
{ key: "pack", label: "Booster Pack", sprite: "Arcana Pack", sheet: "Boosters", tone: "orange", hint: "Arcana, Celestial, etc." },
|
|
22
|
-
];
|
|
23
|
-
const ZONE_TONE = {
|
|
24
|
-
must: "blue",
|
|
25
|
-
should: "green",
|
|
26
|
-
mustnot: "red",
|
|
27
|
-
};
|
|
28
|
-
const ZONE_LABEL = {
|
|
29
|
-
must: "Required",
|
|
30
|
-
should: "Bonus",
|
|
31
|
-
mustnot: "Avoid",
|
|
32
|
-
};
|
|
33
|
-
const CATEGORY_CONFIG_MAP = {
|
|
34
|
-
voucher: VOUCHER_PICKER_CONFIG,
|
|
35
|
-
tag: TAG_PICKER_CONFIG,
|
|
36
|
-
boss: BOSS_PICKER_CONFIG,
|
|
37
|
-
tarot: TAROT_PICKER_CONFIG,
|
|
38
|
-
planet: PLANET_PICKER_CONFIG,
|
|
39
|
-
spectral: SPECTRAL_PICKER_CONFIG,
|
|
40
|
-
pack: PACK_PICKER_CONFIG,
|
|
41
|
-
};
|
|
42
|
-
// ─── Component ───────────────────────────────────────────────────────────────
|
|
43
|
-
export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
44
|
-
const onChangeRef = useRef(onChange);
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
onChangeRef.current = onChange;
|
|
47
|
-
});
|
|
48
|
-
const [currentZone, setCurrentZone] = useState(initialZone);
|
|
49
|
-
const [antesState, setAntesState] = useState({});
|
|
50
|
-
const [activeSlot, setActiveSlot] = useState(null);
|
|
51
|
-
const [activePackDetail, setActivePackDetail] = useState(null);
|
|
52
|
-
const [pickerFlow, setPickerFlow] = useState("category");
|
|
53
|
-
const [activePackSelection, setActivePackSelection] = useState(null);
|
|
54
|
-
const handleSlotTap = useCallback((anteIndex, id, forceCategory) => {
|
|
55
|
-
const existing = (antesState[anteIndex] || {})[id];
|
|
56
|
-
if (forceCategory === "pack" && existing?.packName) {
|
|
57
|
-
setActivePackDetail({ ante: anteIndex, id });
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
setActiveSlot({ ante: anteIndex, id, forceCategory });
|
|
61
|
-
setPickerFlow(forceCategory || "category");
|
|
62
|
-
}, [antesState]);
|
|
63
|
-
const handleSlotClear = useCallback((anteIndex, id) => {
|
|
64
|
-
setAntesState((prev) => {
|
|
65
|
-
const next = { ...prev };
|
|
66
|
-
if (!next[anteIndex])
|
|
67
|
-
return next;
|
|
68
|
-
const nextAnte = { ...next[anteIndex] };
|
|
69
|
-
delete nextAnte[id];
|
|
70
|
-
next[anteIndex] = nextAnte;
|
|
71
|
-
onChangeRef.current?.(buildJamlText(next));
|
|
72
|
-
return next;
|
|
73
|
-
});
|
|
74
|
-
setActivePackDetail((prev) => prev && prev.ante === anteIndex && prev.id === id ? null : prev);
|
|
75
|
-
}, []);
|
|
76
|
-
const handlePackChange = useCallback(() => {
|
|
77
|
-
if (!activePackDetail)
|
|
78
|
-
return;
|
|
79
|
-
setActivePackDetail(null);
|
|
80
|
-
setActivePackSelection(null);
|
|
81
|
-
setActiveSlot({ ante: activePackDetail.ante, id: activePackDetail.id, forceCategory: "pack" });
|
|
82
|
-
setPickerFlow("pack");
|
|
83
|
-
}, [activePackDetail]);
|
|
84
|
-
const handleCategorySelect = useCallback((cat) => {
|
|
85
|
-
setPickerFlow(cat);
|
|
86
|
-
}, []);
|
|
87
|
-
const handleItemSelect = useCallback((selection) => {
|
|
88
|
-
if (!activeSlot)
|
|
89
|
-
return;
|
|
90
|
-
if (activeSlot.forceCategory === "pack" && selection.category === "pack") {
|
|
91
|
-
const slotIndex = getPackSlotIndex(activeSlot.id);
|
|
92
|
-
if (slotIndex === null)
|
|
93
|
-
return;
|
|
94
|
-
const nextFlow = getPackFollowupFlow(selection.value);
|
|
95
|
-
setActivePackSelection({ packName: selection.value, slotIndex });
|
|
96
|
-
setPickerFlow(nextFlow);
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const finalSelection = activePackSelection
|
|
100
|
-
? { ...selection, packName: activePackSelection.packName, boosterPacks: [activePackSelection.slotIndex] }
|
|
101
|
-
: selection;
|
|
102
|
-
setAntesState((prev) => {
|
|
103
|
-
const next = { ...prev };
|
|
104
|
-
const nextAnte = { ...(next[activeSlot.ante] || {}) };
|
|
105
|
-
nextAnte[activeSlot.id] = { ...finalSelection, zone: currentZone };
|
|
106
|
-
next[activeSlot.ante] = nextAnte;
|
|
107
|
-
onChangeRef.current?.(buildJamlText(next));
|
|
108
|
-
return next;
|
|
109
|
-
});
|
|
110
|
-
setActivePackSelection(null);
|
|
111
|
-
setActiveSlot(null);
|
|
112
|
-
}, [activePackSelection, activeSlot, currentZone]);
|
|
113
|
-
const handlePickerCancel = useCallback(() => {
|
|
114
|
-
if (activeSlot?.forceCategory === "pack" && activePackSelection) {
|
|
115
|
-
setActivePackSelection(null);
|
|
116
|
-
setPickerFlow("pack");
|
|
117
|
-
}
|
|
118
|
-
else if (activeSlot?.forceCategory) {
|
|
119
|
-
setActiveSlot(null);
|
|
120
|
-
}
|
|
121
|
-
else if (pickerFlow !== "category") {
|
|
122
|
-
setPickerFlow("category");
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
setActiveSlot(null);
|
|
126
|
-
}
|
|
127
|
-
}, [activePackSelection, activeSlot, pickerFlow]);
|
|
128
|
-
const handleOverlayClose = useCallback(() => {
|
|
129
|
-
setActivePackSelection(null);
|
|
130
|
-
setActiveSlot(null);
|
|
131
|
-
setActivePackDetail(null);
|
|
132
|
-
}, []);
|
|
133
|
-
const activePackDetailSelection = activePackDetail
|
|
134
|
-
? (antesState[activePackDetail.ante] || {})[activePackDetail.id]
|
|
135
|
-
: undefined;
|
|
136
|
-
const handleScrollAttach = useCallback((node) => {
|
|
137
|
-
if (!node)
|
|
138
|
-
return;
|
|
139
|
-
const firstChild = node.children[1];
|
|
140
|
-
if (firstChild)
|
|
141
|
-
node.scrollTop = firstChild.offsetTop;
|
|
142
|
-
}, []);
|
|
143
|
-
const renderSlot = (anteIndex, id, width, sheetType, forceCategory) => {
|
|
144
|
-
const sel = (antesState[anteIndex] || {})[id];
|
|
145
|
-
return (_jsx(MysterySlot, { zone: sel ? sel.zone : "must", sheetType: sheetType, selection: sel, width: width, onTap: () => handleSlotTap(anteIndex, id, forceCategory), onClear: sel ? () => handleSlotClear(anteIndex, id) : undefined, style: { flexShrink: 0 } }, id));
|
|
146
|
-
};
|
|
147
|
-
return (_jsxs("div", { style: { width: "100%", height: "100%", display: "flex", flexDirection: "column" }, children: [_jsx("div", { ref: handleScrollAttach, className: "hide-scrollbar", style: {
|
|
148
|
-
flex: 1,
|
|
149
|
-
overflowY: "scroll",
|
|
150
|
-
overflowX: "hidden",
|
|
151
|
-
scrollSnapType: "y mandatory",
|
|
152
|
-
scrollBehavior: "smooth",
|
|
153
|
-
WebkitOverflowScrolling: "touch",
|
|
154
|
-
overscrollBehaviorY: "contain",
|
|
155
|
-
}, children: Array.from({ length: 8 }, (_, i) => i + 1).map((a) => (_jsxs("div", { style: {
|
|
156
|
-
scrollSnapAlign: "start",
|
|
157
|
-
scrollSnapStop: "always",
|
|
158
|
-
padding: "24px 8px 64px 8px",
|
|
159
|
-
minHeight: "100%",
|
|
160
|
-
boxSizing: "border-box",
|
|
161
|
-
display: "flex",
|
|
162
|
-
flexDirection: "column",
|
|
163
|
-
gap: 24,
|
|
164
|
-
borderBottom: `2px solid ${C.DARK_GREY}`
|
|
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: () => {
|
|
166
|
-
setActivePackSelection(null);
|
|
167
|
-
setPickerFlow("pack");
|
|
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" })] })] })) })] }));
|
|
169
|
-
}
|
|
170
|
-
// ─── Category Selection Menu ─────────────────────────────────────────────────
|
|
171
|
-
export function CategoryMenu({ onSelect, }) {
|
|
172
|
-
return (_jsx("div", { className: "hide-scrollbar", style: {
|
|
173
|
-
display: "flex",
|
|
174
|
-
flexDirection: "column",
|
|
175
|
-
gap: 12,
|
|
176
|
-
padding: "10px 4px",
|
|
177
|
-
maxHeight: "70vh",
|
|
178
|
-
overflowY: "auto",
|
|
179
|
-
}, children: CATEGORIES.map((cat) => (_jsx(JimboButton, { tone: cat.tone, size: "sm", fullWidth: true, onClick: () => onSelect(cat.key), children: _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, width: "100%", textAlign: "left" }, children: [_jsx(JimboSprite, { name: cat.sprite, sheet: cat.sheet, width: 24 }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 1 }, children: [_jsx("span", { style: { fontSize: 11 }, children: cat.label }), _jsx("span", { style: { fontSize: 8, opacity: 0.7, letterSpacing: "0.04em", lineHeight: 1, whiteSpace: "normal" }, children: cat.hint })] })] }) }, cat.key))) }));
|
|
180
|
-
}
|
|
181
|
-
// ─── Build JAML text from slots ──────────────────────────────────────────────
|
|
182
|
-
function getPackSlotIndex(slotId) {
|
|
183
|
-
const match = /_pack_(\d+)$/.exec(slotId);
|
|
184
|
-
if (!match)
|
|
185
|
-
return null;
|
|
186
|
-
return Number(match[1]) - 1;
|
|
187
|
-
}
|
|
188
|
-
function getPackRows(ante) {
|
|
189
|
-
const packCount = ante < 2 ? 4 : 6;
|
|
190
|
-
const rows = [];
|
|
191
|
-
for (let i = 1; i <= packCount; i += 2) {
|
|
192
|
-
rows.push([i, i + 1].filter((slot) => slot <= packCount));
|
|
193
|
-
}
|
|
194
|
-
return rows;
|
|
195
|
-
}
|
|
196
|
-
function getPackFollowupFlow(packName) {
|
|
197
|
-
if (packName.includes("Buffoon"))
|
|
198
|
-
return "joker";
|
|
199
|
-
if (packName.includes("Arcana"))
|
|
200
|
-
return "tarot";
|
|
201
|
-
if (packName.includes("Celestial"))
|
|
202
|
-
return "planet";
|
|
203
|
-
if (packName.includes("Spectral"))
|
|
204
|
-
return "spectral";
|
|
205
|
-
return "packUnsupported";
|
|
206
|
-
}
|
|
207
|
-
function categoryToPreviewSheet(category) {
|
|
208
|
-
if (category === "joker")
|
|
209
|
-
return "Jokers";
|
|
210
|
-
if (category === "voucher")
|
|
211
|
-
return "Vouchers";
|
|
212
|
-
if (category === "tag")
|
|
213
|
-
return "tags";
|
|
214
|
-
if (category === "boss")
|
|
215
|
-
return "BlindChips";
|
|
216
|
-
if (category === "pack")
|
|
217
|
-
return "Boosters";
|
|
218
|
-
return "Tarots";
|
|
219
|
-
}
|
|
220
|
-
function getPackHelperText(packName) {
|
|
221
|
-
if (packName.includes("Buffoon"))
|
|
222
|
-
return "Peek the joker this pack is meant to carry.";
|
|
223
|
-
if (packName.includes("Arcana"))
|
|
224
|
-
return "Peek the tarot card currently attached to this pack.";
|
|
225
|
-
if (packName.includes("Celestial"))
|
|
226
|
-
return "Peek the planet card currently attached to this pack.";
|
|
227
|
-
if (packName.includes("Spectral"))
|
|
228
|
-
return "Peek the spectral card currently attached to this pack.";
|
|
229
|
-
return "Peek the item currently attached to this pack.";
|
|
230
|
-
}
|
|
231
|
-
function getSelectionCategoryLabel(category) {
|
|
232
|
-
if (category === "joker")
|
|
233
|
-
return "Joker";
|
|
234
|
-
if (category === "voucher")
|
|
235
|
-
return "Voucher";
|
|
236
|
-
if (category === "tag")
|
|
237
|
-
return "Tag";
|
|
238
|
-
if (category === "boss")
|
|
239
|
-
return "Boss Blind";
|
|
240
|
-
if (category === "tarot")
|
|
241
|
-
return "Tarot Card";
|
|
242
|
-
if (category === "planet")
|
|
243
|
-
return "Planet Card";
|
|
244
|
-
if (category === "spectral")
|
|
245
|
-
return "Spectral Card";
|
|
246
|
-
return "Pack";
|
|
247
|
-
}
|
|
248
|
-
function getZoneTextTone(zone) {
|
|
249
|
-
if (zone === "must")
|
|
250
|
-
return "blue";
|
|
251
|
-
if (zone === "should")
|
|
252
|
-
return "green";
|
|
253
|
-
return "red";
|
|
254
|
-
}
|
|
255
|
-
function buildJamlText(antes) {
|
|
256
|
-
const byZone = {
|
|
257
|
-
must: {}, should: {}, mustnot: {}
|
|
258
|
-
};
|
|
259
|
-
for (const [anteStr, selections] of Object.entries(antes)) {
|
|
260
|
-
const anteNum = parseInt(anteStr, 10);
|
|
261
|
-
for (const sel of Object.values(selections)) {
|
|
262
|
-
const zone = sel.zone;
|
|
263
|
-
const key = sel.clauseKey;
|
|
264
|
-
if (!byZone[zone][key]) {
|
|
265
|
-
byZone[zone][key] = [];
|
|
266
|
-
}
|
|
267
|
-
const existing = byZone[zone][key].find(item => item.value === sel.value &&
|
|
268
|
-
(item.boosterPacks ?? []).join(",") === (sel.boosterPacks ?? []).join(","));
|
|
269
|
-
if (existing) {
|
|
270
|
-
if (!existing.antes.includes(anteNum))
|
|
271
|
-
existing.antes.push(anteNum);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
byZone[zone][key].push({ value: sel.value, antes: [anteNum], boosterPacks: sel.boosterPacks });
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
const lines = [];
|
|
279
|
-
lines.push("name: My Custom Seed Map");
|
|
280
|
-
lines.push("author: JamlBuilder");
|
|
281
|
-
lines.push("description: Auto-generated from the visual editor.");
|
|
282
|
-
lines.push("deck: Red");
|
|
283
|
-
lines.push("stake: White");
|
|
284
|
-
for (const [zone, label] of [["must", "must"], ["should", "should"], ["mustnot", "mustNot"]]) {
|
|
285
|
-
const clauses = byZone[zone];
|
|
286
|
-
if (Object.keys(clauses).length === 0)
|
|
287
|
-
continue;
|
|
288
|
-
lines.push(`${label}:`);
|
|
289
|
-
for (const [key, items] of Object.entries(clauses)) {
|
|
290
|
-
for (const item of items) {
|
|
291
|
-
lines.push(` - ${key}: ${item.value}`);
|
|
292
|
-
// Only emit `antes:` if it's not all 8 antes (simplification, or just emit it)
|
|
293
|
-
if (item.antes.length < 8) {
|
|
294
|
-
lines.push(` antes: [${item.antes.sort((a, b) => a - b).join(", ")}]`);
|
|
295
|
-
}
|
|
296
|
-
if (item.boosterPacks && item.boosterPacks.length > 0) {
|
|
297
|
-
lines.push(` sources:`);
|
|
298
|
-
lines.push(` boosterPacks: [${item.boosterPacks.join(", ")}]`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return lines.join("\n") + "\n";
|
|
304
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { StoryObj } from '@storybook/react';
|
|
2
|
-
import { JamlMapEditor } from './JamlMapEditor';
|
|
3
|
-
import "../../ui/jimbo.css";
|
|
4
|
-
declare const meta: Meta<typeof JamlMapEditor>;
|
|
5
|
-
export default meta;
|
|
6
|
-
type Story = StoryObj<typeof meta>;
|
|
7
|
-
export declare const Default: Story;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { JamlMapEditor } from './JamlMapEditor';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { JimboModalRoot } from '../../ui/panel';
|
|
5
|
-
import "../../ui/jimbo.css"; // Ensure global CSS is loaded
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'JamlMap/JamlMapEditor',
|
|
8
|
-
component: JamlMapEditor,
|
|
9
|
-
parameters: {
|
|
10
|
-
layout: 'fullscreen',
|
|
11
|
-
viewport: {
|
|
12
|
-
defaultViewport: 'mobile1', // iPhone SE
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
decorators: [
|
|
16
|
-
(Story) => (_jsxs("div", { style: { width: "375px", height: "667px", margin: "0 auto", overflow: "hidden", border: "1px solid #333" }, children: [_jsx(Story, {}), _jsx(JimboModalRoot, {})] })),
|
|
17
|
-
],
|
|
18
|
-
};
|
|
19
|
-
export default meta;
|
|
20
|
-
export const Default = {
|
|
21
|
-
render: () => {
|
|
22
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
23
|
-
const [clauses, setClauses] = useState([]);
|
|
24
|
-
return (_jsx(JamlMapEditor, { clauses: clauses, onChange: setClauses }));
|
|
25
|
-
},
|
|
26
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type SlotSelection, type JamlZone } from "./MysterySlot.js";
|
|
2
|
-
export interface JamlMapEditorDemoProps {
|
|
3
|
-
/** Initial zone for the demo. */
|
|
4
|
-
zone?: JamlZone;
|
|
5
|
-
/** Callback when selections change. */
|
|
6
|
-
onChange?: (slots: (SlotSelection | null)[]) => void;
|
|
7
|
-
}
|
|
8
|
-
export declare function JamlMapEditorDemo({ zone: initialZone, onChange, }: JamlMapEditorDemoProps): import("react/jsx-runtime").JSX.Element;
|