jaml-ui 0.17.3 → 0.18.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 +7 -3
- package/dist/components/JamlAnalyzerFullscreen.d.ts +4 -1
- package/dist/components/JamlAnalyzerFullscreen.js +2 -2
- package/dist/components/JamlCurator.d.ts +4 -0
- package/dist/components/JamlCurator.js +63 -0
- package/dist/components/JamlCurator.stories.d.ts +6 -0
- package/dist/components/JamlCurator.stories.js +14 -0
- package/dist/components/JamlIde.js +1 -1
- package/dist/components/JamlIdeVisual.js +12 -20
- package/dist/components/jamlMap/CategoryPicker.js +38 -120
- package/dist/components/jamlMap/JamlMapEditor.d.ts +11 -0
- package/dist/components/jamlMap/JamlMapEditor.js +170 -0
- package/dist/components/jamlMap/JamlMapEditor.stories.d.ts +7 -0
- package/dist/components/jamlMap/JamlMapEditor.stories.js +26 -0
- package/dist/components/jamlMap/JokerPicker.js +28 -157
- package/dist/components/jamlMap/MysterySlot.js +32 -5
- package/dist/components/jamlMap/MysterySlot.stories.d.ts +7 -0
- package/dist/components/jamlMap/MysterySlot.stories.js +31 -0
- package/dist/components/jamlMap/index.d.ts +1 -1
- package/dist/components/jamlMap/index.js +1 -1
- package/dist/hooks/useAnalyzer.d.ts +4 -8
- package/dist/hooks/useAnalyzer.js +3 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/stories/Button.d.ts +15 -0
- package/dist/stories/Button.js +7 -0
- package/dist/stories/Button.stories.d.ts +24 -0
- package/dist/stories/Button.stories.js +50 -0
- package/dist/stories/Header.d.ts +12 -0
- package/dist/stories/Header.js +4 -0
- package/dist/stories/Header.stories.d.ts +18 -0
- package/dist/stories/Header.stories.js +26 -0
- package/dist/stories/Page.d.ts +3 -0
- package/dist/stories/Page.js +8 -0
- package/dist/stories/Page.stories.d.ts +12 -0
- package/dist/stories/Page.stories.js +24 -0
- package/dist/ui/Jimbo.stories.d.ts +7 -0
- package/dist/ui/Jimbo.stories.js +28 -0
- package/dist/ui/jimbo.css +20 -11
- package/dist/ui/jimboText.d.ts +1 -1
- package/dist/ui/panel.d.ts +1 -1
- package/dist/ui/panel.js +7 -5
- package/package.json +16 -3
package/DESIGN.md
CHANGED
|
@@ -147,9 +147,11 @@ Must-clause items glow blue. Should-clause items glow gold/green. Non-matching i
|
|
|
147
147
|
|
|
148
148
|
## Typography
|
|
149
149
|
|
|
150
|
-
m6x11plus (m6x11plusplus.otf) is the ONLY font. It is a single-weight pixel font. NEVER apply font-weight bold, semibold, or any weight other than 400. Bold makes it look muddy.
|
|
150
|
+
m6x11plus (m6x11plusplus.otf) is the ONLY font. It is a single-weight pixel font. NEVER apply font-weight bold, semibold, or any weight other than 400. Bold makes it look muddy. NEVER USE HEAVY!
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
Text is NEVER ALL CAPS. Use proper Title Case or Sentence case. Seed codes use the display size (26px) in gold (#e4b643).
|
|
153
|
+
|
|
154
|
+
Contrast is critical. NEVER make grey text on top of a grey background. If using a dark grey background, use white or light contrasting text.
|
|
153
155
|
|
|
154
156
|
## Layout
|
|
155
157
|
|
|
@@ -188,7 +190,9 @@ JAML-hit items get a GlowRing: `box-shadow: 0 0 0 2px [color], 0 0 10px [color]`
|
|
|
188
190
|
- DO use m6x11plus for everything except code/monospace.
|
|
189
191
|
- DO design for 375px portrait.
|
|
190
192
|
- DO use translateY + box-shadow for button depth. Not CSS 3D transforms.
|
|
191
|
-
- DON'T use font-weight bold. m6x11plus is single-weight.
|
|
193
|
+
- DON'T use font-weight bold or heavy. m6x11plus is single-weight.
|
|
194
|
+
- DON'T use ALL CAPS. It is considered an embellishment and ruins the aesthetic.
|
|
195
|
+
- DON'T put grey text on top of a grey background.
|
|
192
196
|
- DON'T use fat padding or margins. Balatro UI is dense and cozy.
|
|
193
197
|
- DON'T add horizontal scroll. Vertical snap-scroll + horizontal swipe only.
|
|
194
198
|
- DON'T use rounded corners larger than 10px. Balatro is chunky, not bubbly.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import type { AnalyzerAnteView, AnalyzerItem } from "./AnalyzerExplorer.js";
|
|
2
3
|
import type { AnalyzerLive } from "../hooks/useAnalyzer.js";
|
|
3
4
|
import { type AnalyzerStreamKey } from "../hooks/analyzerStreamRegistry.js";
|
|
@@ -21,7 +22,9 @@ export interface JamlAnalyzerFullscreenProps {
|
|
|
21
22
|
/** Pull size on each lazy load. */
|
|
22
23
|
chunkSize?: number;
|
|
23
24
|
className?: string;
|
|
25
|
+
/** Custom top page to render as Slide 0 */
|
|
26
|
+
topPage?: React.ReactNode;
|
|
24
27
|
}
|
|
25
|
-
export declare function JamlAnalyzerFullscreen({ antes, live, jaml, tallyColumns, tallyLabels, enabledStreams, onEnabledStreamsChange, hidePicker, chunkSize, className, }: JamlAnalyzerFullscreenProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export declare function JamlAnalyzerFullscreen({ antes, live, jaml, tallyColumns, tallyLabels, enabledStreams, onEnabledStreamsChange, hidePicker, chunkSize, className, topPage, }: JamlAnalyzerFullscreenProps): import("react/jsx-runtime").JSX.Element;
|
|
26
29
|
export type { AnalyzerItem };
|
|
27
30
|
export { ANALYZER_STREAM_META, type AnalyzerStreamKey } from "../hooks/analyzerStreamRegistry.js";
|
|
@@ -17,7 +17,7 @@ const TONE_COLORS = {
|
|
|
17
17
|
default: C.GOLD_TEXT,
|
|
18
18
|
};
|
|
19
19
|
import { JamlMapPreview } from "./JamlMapPreview.js";
|
|
20
|
-
export function JamlAnalyzerFullscreen({ antes, live, jaml, tallyColumns, tallyLabels, enabledStreams, onEnabledStreamsChange, hidePicker = false, chunkSize = 12, className = "", }) {
|
|
20
|
+
export function JamlAnalyzerFullscreen({ antes, live, jaml, tallyColumns, tallyLabels, enabledStreams, onEnabledStreamsChange, hidePicker = false, chunkSize = 12, className = "", topPage, }) {
|
|
21
21
|
const [internalEnabled, setInternalEnabled] = useState(enabledStreams ?? DEFAULT_ENABLED_STREAMS);
|
|
22
22
|
const effectiveEnabled = enabledStreams ?? internalEnabled;
|
|
23
23
|
const setEnabled = useCallback((next) => {
|
|
@@ -26,7 +26,7 @@ export function JamlAnalyzerFullscreen({ antes, live, jaml, tallyColumns, tallyL
|
|
|
26
26
|
}, [onEnabledStreamsChange]);
|
|
27
27
|
const { currentAnte, scrollRef, scrollToAnte, registerAnteRef } = useAnteTracker(antes);
|
|
28
28
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
29
|
-
return (_jsxs("div", { className: className, style: styles.root, children: [_jsxs("div", { ref: scrollRef, style: styles.scroller, children: [jaml && (_jsxs("section", { style: { ...styles.section, scrollSnapAlign: "start", justifyContent: 'center' }, children: [_jsxs("div", { style: { marginBottom: 20 }, children: [_jsx("div", { style: styles.anteLabel, children: "JAML" }), _jsx("div", { style: styles.anteNumber, children: "MAP" })] }), _jsx(JamlMapPreview, { jaml: jaml, tallyColumns: tallyColumns, tallyLabels: tallyLabels }), _jsx("div", { style: { marginTop: 24, textAlign: 'center', opacity: 0.6 }, children: _jsx(JimboText, { size: "xs", tone: "grey", children: "Scroll down to explore seed details" }) })] })), antes.map((ante) => (_jsx(AnteSection, { ante: ante, live: live, enabledStreams: effectiveEnabled, chunkSize: chunkSize, registerRef: (el) => registerAnteRef(ante.ante, el) }, ante.ante)))] }), _jsx(SideRail, { antes: antes.map((a) => a.ante), currentAnte: currentAnte, onJump: scrollToAnte }), !hidePicker && (_jsx(StreamPicker, { enabled: effectiveEnabled, onChange: setEnabled, open: pickerOpen, onToggle: () => setPickerOpen((v) => !v) }))] }));
|
|
29
|
+
return (_jsxs("div", { className: className, style: styles.root, children: [_jsxs("div", { ref: scrollRef, style: styles.scroller, children: [topPage ? topPage : jaml && (_jsxs("section", { style: { ...styles.section, scrollSnapAlign: "start", justifyContent: 'center' }, children: [_jsxs("div", { style: { marginBottom: 20 }, children: [_jsx("div", { style: styles.anteLabel, children: "JAML" }), _jsx("div", { style: styles.anteNumber, children: "MAP" })] }), _jsx(JamlMapPreview, { jaml: jaml, tallyColumns: tallyColumns, tallyLabels: tallyLabels }), _jsx("div", { style: { marginTop: 24, textAlign: 'center', opacity: 0.6 }, children: _jsx(JimboText, { size: "xs", tone: "grey", children: "Scroll down to explore seed details" }) })] })), antes.map((ante) => (_jsx(AnteSection, { ante: ante, live: live, enabledStreams: effectiveEnabled, chunkSize: chunkSize, registerRef: (el) => registerAnteRef(ante.ante, el) }, ante.ante)))] }), _jsx(SideRail, { antes: antes.map((a) => a.ante), currentAnte: currentAnte, onJump: scrollToAnte }), !hidePicker && (_jsx(StreamPicker, { enabled: effectiveEnabled, onChange: setEnabled, open: pickerOpen, onToggle: () => setPickerOpen((v) => !v) }))] }));
|
|
30
30
|
}
|
|
31
31
|
function AnteSection({ ante, live, enabledStreams, chunkSize, registerRef }) {
|
|
32
32
|
return (_jsxs("section", { ref: registerRef, "data-ante": ante.ante, style: styles.section, children: [_jsxs("header", { style: styles.header, children: [_jsxs("div", { children: [_jsx("div", { style: styles.anteLabel, children: "Ante" }), _jsx("div", { style: styles.anteNumber, children: ante.ante })] }), ante.voucher && (_jsxs("div", { style: styles.voucherBlock, children: [_jsx(JamlVoucher, { voucherName: ante.voucher, scale: 0.85 }), _jsx("div", { style: styles.voucherCaption, children: ante.voucher })] }))] }), _jsxs("div", { style: styles.blindRow, children: [_jsx(BlindCell, { label: "Small", tag: ante.smallBlindTag }), _jsx(BlindCell, { label: "Big", tag: ante.bigBlindTag }), ante.boss && (_jsxs("div", { style: styles.bossCell, children: [_jsx("div", { style: styles.cellLabel, children: "Boss" }), _jsx(JamlBoss, { bossName: ante.boss, scale: 0.7 }), _jsx("div", { style: styles.cellCaption, children: ante.boss })] }))] }), ante.packs && ante.packs.length > 0 && (_jsxs("div", { style: styles.streamLane, children: [_jsx("div", { style: styles.streamLabel, children: "Packs" }), _jsx("div", { style: styles.packRow, children: ante.packs.map((pack, i) => (_jsx("div", { style: styles.packPill, children: pack }, `${ante.ante}-pack-${i}`))) })] })), enabledStreams.map((key) => {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { JimboButton, JimboPanel } from "../ui/panel.js";
|
|
5
|
+
import { JimboText } from "../ui/jimboText.js";
|
|
6
|
+
import { JimboColorOption } from "../ui/tokens.js";
|
|
7
|
+
import { JimboFlankNav } from "../ui/jimboFlankNav.js";
|
|
8
|
+
import { JamlMapEditor } from "./jamlMap/JamlMapEditor.js";
|
|
9
|
+
import { JamlAnalyzerFullscreen } from "./JamlAnalyzerFullscreen.js";
|
|
10
|
+
import { useSearch } from "../hooks/useSearch.js";
|
|
11
|
+
import { useAnalyzer } from "../hooks/useAnalyzer.js";
|
|
12
|
+
import { JamlSpeedometer } from "./JamlSpeedometer.js";
|
|
13
|
+
const C = JimboColorOption;
|
|
14
|
+
export function JamlCurator({ motelyWasmUrl }) {
|
|
15
|
+
// Use map editor by default to generate JAML
|
|
16
|
+
const [jamlText, setJamlText] = useState("");
|
|
17
|
+
const search = useSearch(motelyWasmUrl);
|
|
18
|
+
const analyzer = useAnalyzer();
|
|
19
|
+
// Search results pagination
|
|
20
|
+
const [resultIndex, setResultIndex] = useState(0);
|
|
21
|
+
const isSearching = search.status === "running";
|
|
22
|
+
const handleSearch = () => {
|
|
23
|
+
if (isSearching) {
|
|
24
|
+
search.cancel();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
setResultIndex(0);
|
|
28
|
+
search.start(jamlText, 1_000_000);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const currentSeed = search.results[resultIndex]?.seed;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (currentSeed) {
|
|
34
|
+
analyzer.analyze(currentSeed, "Red", "White", jamlText);
|
|
35
|
+
}
|
|
36
|
+
}, [currentSeed, jamlText]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
37
|
+
// Map Editor changes
|
|
38
|
+
const handleMapChange = (jamlString) => {
|
|
39
|
+
setJamlText(jamlString);
|
|
40
|
+
};
|
|
41
|
+
return (_jsx("div", { style: {
|
|
42
|
+
width: "100%",
|
|
43
|
+
maxWidth: 375,
|
|
44
|
+
height: "100svh",
|
|
45
|
+
margin: "0 auto",
|
|
46
|
+
position: "relative",
|
|
47
|
+
background: C.DARKEST,
|
|
48
|
+
overflow: "hidden",
|
|
49
|
+
borderLeft: `1px solid ${C.PANEL_EDGE}`,
|
|
50
|
+
borderRight: `1px solid ${C.PANEL_EDGE}`,
|
|
51
|
+
boxShadow: `0 0 20px rgba(0,0,0,0.5)`,
|
|
52
|
+
}, children: _jsx(JamlAnalyzerFullscreen, { antes: analyzer.antes, live: analyzer.live, hidePicker: true, topPage: _jsxs("section", { style: {
|
|
53
|
+
width: "100%",
|
|
54
|
+
height: "100svh",
|
|
55
|
+
scrollSnapAlign: "start",
|
|
56
|
+
display: "flex",
|
|
57
|
+
flexDirection: "column",
|
|
58
|
+
gap: 12,
|
|
59
|
+
padding: "16px 12px 24px",
|
|
60
|
+
boxSizing: "border-box",
|
|
61
|
+
borderBottom: `2px solid ${C.GOLD}`,
|
|
62
|
+
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(JimboText, { size: "lg", tone: "gold", children: "JAML Curator" }), _jsx(JimboButton, { tone: isSearching ? "red" : "green", size: "sm", onClick: handleSearch, children: isSearching ? "STOP" : "SEARCH" })] }), _jsx("div", { style: { flex: 1, minHeight: 0, overflowY: 'auto' }, className: "hide-scrollbar", children: _jsx(JamlMapEditor, { onChange: handleMapChange }) }), _jsx("div", { style: { flexShrink: 0 }, children: _jsx(JamlSpeedometer, { status: search.status, seedsPerSecond: search.seedsPerSecond, totalSearched: search.totalSearched, matchingSeeds: search.matchingSeeds }) }), _jsx("div", { style: { flexShrink: 0 }, children: _jsx(JimboPanel, { children: search.results.length === 0 ? (_jsx(JimboText, { size: "sm", tone: "grey", className: "j-text-center", children: isSearching ? "Searching..." : "No results yet." })) : (_jsxs("div", { className: "j-flex-col j-gap-sm", children: [_jsxs("div", { className: "j-flex j-items-center j-justify-between", children: [_jsx(JimboText, { size: "xs", tone: "grey", children: "SEED MATCHES" }), _jsxs(JimboText, { size: "xs", tone: "gold", children: [search.matchingSeeds, " FOUND"] })] }), _jsx(JimboFlankNav, { canPrev: resultIndex > 0, canNext: resultIndex < search.results.length - 1, onPrev: () => setResultIndex(i => Math.max(0, i - 1)), onNext: () => setResultIndex(i => Math.min(search.results.length - 1, i + 1)), children: _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "lg", tone: "gold", style: { letterSpacing: 2 }, children: currentSeed }), _jsx(JimboButton, { tone: "grey", size: "xs", children: "Copy Seed" })] }) }), _jsx(JimboText, { size: "micro", tone: "grey", className: "j-text-center", style: { opacity: 0.7, marginTop: 8 }, children: "\u25BC SWIPE DOWN FOR ANTES \u25BC" })] })) }) })] }) }) }));
|
|
63
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { JamlCurator } from "./JamlCurator.js";
|
|
2
|
+
const meta = {
|
|
3
|
+
title: "JAML/JamlCurator",
|
|
4
|
+
component: JamlCurator,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: "fullscreen",
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
export default meta;
|
|
10
|
+
export const Default = {
|
|
11
|
+
args: {
|
|
12
|
+
motelyWasmUrl: "https://unpkg.com/@nims11/motely@0.2.2/motely_bg.wasm",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -69,7 +69,7 @@ function ResultsView({ results, jaml }) {
|
|
|
69
69
|
display: "flex",
|
|
70
70
|
flexDirection: "column",
|
|
71
71
|
gap: 8,
|
|
72
|
-
}, children: [_jsx(JamlMapPreview, { jaml: jaml, tallyColumns: result.tallyColumns, tallyLabels: result.tallyLabels }), _jsxs("div", { style: { padding: "4px 8px 8px", display: "flex", flexDirection: "column", gap: 5 }, children: [_jsx("span", { style: { fontSize:
|
|
72
|
+
}, children: [_jsx(JamlMapPreview, { jaml: jaml, tallyColumns: result.tallyColumns, tallyLabels: result.tallyLabels }), _jsxs("div", { style: { padding: "4px 8px 8px", display: "flex", flexDirection: "column", gap: 5 }, children: [_jsx("span", { style: { fontSize: 9, color: JimboColorOption.WHITE, opacity: 0.8 }, children: "Raw Tally Data" }), (result.tallyLabels ?? []).map((label, i) => {
|
|
73
73
|
const val = result.tallyColumns[i] ?? 0;
|
|
74
74
|
if (val === 0)
|
|
75
75
|
return null;
|
|
@@ -6,9 +6,9 @@ import { JimboColorOption } from "../ui/tokens.js";
|
|
|
6
6
|
import { JimboSprite } from "../ui/sprites.js";
|
|
7
7
|
const C = JimboColorOption;
|
|
8
8
|
const ZONE_META = {
|
|
9
|
-
must: { label: "
|
|
10
|
-
should: { label: "
|
|
11
|
-
mustnot: { label: "
|
|
9
|
+
must: { label: "Must", hint: "Seed must contain all of these.", color: C.BLUE, accent: "#4db5ff" },
|
|
10
|
+
should: { label: "Should", hint: "Bonus points per match.", color: C.RED, accent: "#ff8076" },
|
|
11
|
+
mustnot: { label: "Must Not", hint: "Rejected if any appear.", color: C.ORANGE, accent: "#ffb84d" },
|
|
12
12
|
};
|
|
13
13
|
function clauseSpriteSheet(type) {
|
|
14
14
|
if (type === "joker" ||
|
|
@@ -127,8 +127,7 @@ function MysteryAddTile({ zone, onTap }) {
|
|
|
127
127
|
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
128
128
|
fontSize: 12,
|
|
129
129
|
color: z.accent,
|
|
130
|
-
|
|
131
|
-
}, children: ["ADD TO ", z.label] })] }));
|
|
130
|
+
}, children: ["Add to ", z.label] })] }));
|
|
132
131
|
}
|
|
133
132
|
function ZoneRail({ zone, clauses, onAdd, onRemove, onEdit, onDragStart, highlight, }) {
|
|
134
133
|
const z = ZONE_META[zone];
|
|
@@ -140,23 +139,16 @@ function ZoneRail({ zone, clauses, onAdd, onRemove, onEdit, onDragStart, highlig
|
|
|
140
139
|
transition: "background 100ms, border-color 100ms",
|
|
141
140
|
}, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 }, children: [_jsx("div", { style: {
|
|
142
141
|
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
143
|
-
fontSize:
|
|
144
|
-
padding: "2px
|
|
142
|
+
fontSize: 12,
|
|
143
|
+
padding: "2px 6px",
|
|
145
144
|
background: z.color,
|
|
146
145
|
color: C.WHITE,
|
|
147
146
|
borderRadius: 3,
|
|
148
|
-
letterSpacing: 2,
|
|
149
147
|
boxShadow: `0 2px 0 ${C.BLACK}`,
|
|
150
148
|
}, children: z.label }), _jsx("div", { style: { flex: 1, height: 2, background: `${z.color}55`, borderRadius: 1 } }), _jsx("div", { style: { fontFamily: "m6x11plus, ui-monospace, monospace", fontSize: 8, color: C.GREY }, children: clauses.length })] }), _jsx("div", { style: { fontFamily: "m6x11plus, ui-monospace, monospace", fontSize: 9, color: C.GREY, letterSpacing: 0.5, marginBottom: 8 }, children: z.hint }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [clauses.map((c) => (_jsx(ClauseCard, { clause: c, zone: zone, onRemove: () => onRemove(c.id), onEdit: () => onEdit(c), onDragStart: onDragStart }, c.id))), _jsx(MysteryAddTile, { zone: zone, onTap: onAdd })] })] }));
|
|
151
149
|
}
|
|
152
150
|
function TopMatter({ filter, onChange, }) {
|
|
153
|
-
return (_jsxs("div", { style: {
|
|
154
|
-
background: C.DARK_GREY,
|
|
155
|
-
borderRadius: 6,
|
|
156
|
-
padding: 10,
|
|
157
|
-
border: `2px solid ${C.PANEL_EDGE}`,
|
|
158
|
-
boxShadow: `0 2px 0 ${C.BLACK}`,
|
|
159
|
-
}, children: [_jsx("input", { value: filter.name ?? "", placeholder: "Untitled", onChange: (e) => onChange({ ...filter, name: e.target.value }), style: {
|
|
151
|
+
return (_jsxs("div", { className: "j-inner-panel", style: { padding: 10 }, children: [_jsx("input", { value: filter.name ?? "", placeholder: "Untitled", onChange: (e) => onChange({ ...filter, name: e.target.value }), style: {
|
|
160
152
|
display: "block",
|
|
161
153
|
width: "100%",
|
|
162
154
|
background: "transparent",
|
|
@@ -168,7 +160,7 @@ function TopMatter({ filter, onChange, }) {
|
|
|
168
160
|
letterSpacing: 1,
|
|
169
161
|
padding: 0,
|
|
170
162
|
marginBottom: 4,
|
|
171
|
-
} }), _jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [_jsx("div", { style: { fontFamily: "m6x11plus, ui-monospace, monospace", fontSize:
|
|
163
|
+
} }), _jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [_jsx("div", { style: { fontFamily: "m6x11plus, ui-monospace, monospace", fontSize: 10, color: C.WHITE }, children: "By" }), _jsx("input", { value: filter.author ?? "", placeholder: "anonymous", onChange: (e) => onChange({ ...filter, author: e.target.value }), style: {
|
|
172
164
|
flex: 1,
|
|
173
165
|
background: "transparent",
|
|
174
166
|
border: "none",
|
|
@@ -176,7 +168,6 @@ function TopMatter({ filter, onChange, }) {
|
|
|
176
168
|
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
177
169
|
fontSize: 12,
|
|
178
170
|
color: C.GOLD_TEXT,
|
|
179
|
-
letterSpacing: 1,
|
|
180
171
|
padding: 0,
|
|
181
172
|
} })] }), _jsx("input", { value: filter.description ?? "", placeholder: "description", onChange: (e) => onChange({ ...filter, description: e.target.value }), style: {
|
|
182
173
|
display: "block",
|
|
@@ -186,8 +177,9 @@ function TopMatter({ filter, onChange, }) {
|
|
|
186
177
|
border: "none",
|
|
187
178
|
outline: "none",
|
|
188
179
|
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
189
|
-
fontSize:
|
|
190
|
-
color: C.
|
|
180
|
+
fontSize: 11,
|
|
181
|
+
color: C.WHITE,
|
|
182
|
+
opacity: 0.8,
|
|
191
183
|
lineHeight: 1.35,
|
|
192
184
|
padding: 0,
|
|
193
185
|
} })] }));
|
|
@@ -205,7 +197,7 @@ export function JamlIdeVisual({ filter, onChange, onEditClause, onAddClause }) {
|
|
|
205
197
|
padding: 10,
|
|
206
198
|
background: C.DARKEST,
|
|
207
199
|
color: C.WHITE,
|
|
208
|
-
}, children: [_jsx(TopMatter, { filter: filter, onChange: onChange }),
|
|
200
|
+
}, children: [_jsx(TopMatter, { filter: filter, onChange: onChange }), _jsxs("div", { style: { display: "flex", gap: 10 }, children: [_jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsx(ZoneRail, { zone: "must", clauses: filter.must, onAdd: onAddClause ? () => onAddClause("must") : undefined, onRemove: (id) => removeClause("must", id), onEdit: (c) => onEditClause?.("must", c), onDragStart: onDragStart, highlight: hoverZone === "must" }) }), _jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsx(ZoneRail, { zone: "mustnot", clauses: filter.mustnot, onAdd: onAddClause ? () => onAddClause("mustnot") : undefined, onRemove: (id) => removeClause("mustnot", id), onEdit: (c) => onEditClause?.("mustnot", c), onDragStart: onDragStart, highlight: hoverZone === "mustnot" }) })] }), _jsx(ZoneRail, { zone: "should", clauses: filter.should, onAdd: onAddClause ? () => onAddClause("should") : undefined, onRemove: (id) => removeClause("should", id), onEdit: (c) => onEditClause?.("should", c), onDragStart: onDragStart, highlight: hoverZone === "should" }), drag && (_jsx("div", { style: {
|
|
209
201
|
position: "fixed",
|
|
210
202
|
left: drag.x - drag.offX,
|
|
211
203
|
top: drag.y - drag.offY,
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
4
|
import { JimboSprite } from "../../ui/sprites.js";
|
|
5
|
-
import { JimboColorOption
|
|
5
|
+
import { JimboColorOption } from "../../ui/tokens.js";
|
|
6
|
+
import { JimboButton } from "../../ui/panel.js";
|
|
7
|
+
import { JimboText } from "../../ui/jimboText.js";
|
|
6
8
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
9
|
+
const C = JimboColorOption;
|
|
7
10
|
export function CategoryPicker({ config, onSelect, onCancel }) {
|
|
8
11
|
const [search, setSearch] = useState("");
|
|
9
12
|
const filtered = useMemo(() => {
|
|
@@ -12,6 +15,19 @@ export function CategoryPicker({ config, onSelect, onCancel }) {
|
|
|
12
15
|
const q = search.toLowerCase();
|
|
13
16
|
return config.items.filter((item) => item.name.toLowerCase().includes(q));
|
|
14
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]);
|
|
15
31
|
const handleSelect = useCallback((item) => {
|
|
16
32
|
onSelect({
|
|
17
33
|
category: config.category,
|
|
@@ -26,8 +42,26 @@ export function CategoryPicker({ config, onSelect, onCancel }) {
|
|
|
26
42
|
clauseKey: config.clauseKey,
|
|
27
43
|
});
|
|
28
44
|
}, [onSelect, config]);
|
|
29
|
-
const
|
|
30
|
-
|
|
45
|
+
const renderItem = (item, isMuted = false) => (_jsxs("div", { onClick: () => handleSelect(item), title: item.name, style: {
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
gap: 3,
|
|
50
|
+
padding: 4,
|
|
51
|
+
borderRadius: 4,
|
|
52
|
+
cursor: "pointer",
|
|
53
|
+
opacity: isMuted ? 0.3 : 1,
|
|
54
|
+
}, children: [_jsx(JimboSprite, { name: item.name, sheet: config.sheet, width: 48 }), _jsx(JimboText, { size: "micro", tone: "grey", style: { maxWidth: 60, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: item.name })] }, item.name));
|
|
55
|
+
return (_jsxs("div", { style: { padding: 0, maxWidth: 420, maxHeight: "80vh", display: "flex", flexDirection: "column" }, children: [_jsxs("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" } }), _jsx(JimboButton, { tone: "gold", size: "sm", onClick: handleAny, children: "Any" })] }), 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", { style: {
|
|
56
|
+
display: "flex",
|
|
57
|
+
flexWrap: "wrap",
|
|
58
|
+
gap: 6,
|
|
59
|
+
padding: "8px 10px 10px",
|
|
60
|
+
overflowY: "auto",
|
|
61
|
+
flex: 1,
|
|
62
|
+
alignContent: "flex-start",
|
|
63
|
+
}, 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) ||
|
|
64
|
+
(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, "\""] }) }))] })] }));
|
|
31
65
|
}
|
|
32
66
|
// ─── Pre-built configs ───────────────────────────────────────────────────────
|
|
33
67
|
import { VOUCHERS, TAGS, BOSSES, BOOSTER_PACKS, TAROTS_AND_PLANETS } from "../../sprites/spriteData.js";
|
|
@@ -36,18 +70,16 @@ const TAROT_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
|
36
70
|
const y = c.pos.y;
|
|
37
71
|
return y <= 2 && c.name !== "The Soul" && c.name !== "Black Hole";
|
|
38
72
|
}).filter((c) => {
|
|
39
|
-
// Tarots are y=0..1 + Judgement (y=2,x=0) + The World (y=2,x=1)
|
|
40
73
|
return c.pos.y <= 1 || (c.pos.y === 2 && c.pos.x <= 1);
|
|
41
74
|
});
|
|
42
75
|
const PLANET_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
43
|
-
return c.pos.y === 3 ||
|
|
76
|
+
return c.pos.y === 3 ||
|
|
44
77
|
c.name === "Planet X" || c.name === "Ceres" || c.name === "Eris" ||
|
|
45
78
|
c.name === "Black Hole";
|
|
46
79
|
});
|
|
47
80
|
const SPECTRAL_CARDS = TAROTS_AND_PLANETS.filter((c) => {
|
|
48
81
|
return c.pos.y >= 4 || c.name === "The Soul";
|
|
49
82
|
});
|
|
50
|
-
const C = JimboColorOption;
|
|
51
83
|
export const VOUCHER_PICKER_CONFIG = {
|
|
52
84
|
title: "Vouchers",
|
|
53
85
|
category: "voucher",
|
|
@@ -108,117 +140,3 @@ export const PACK_PICKER_CONFIG = {
|
|
|
108
140
|
items: BOOSTER_PACKS,
|
|
109
141
|
accent: C.ORANGE,
|
|
110
142
|
};
|
|
111
|
-
// ─── Styles ──────────────────────────────────────────────────────────────────
|
|
112
|
-
const styles = {
|
|
113
|
-
container: {
|
|
114
|
-
background: JimboColorOption.DARKEST,
|
|
115
|
-
border: `2px solid ${JimboColorOption.TEAL_GREY}`,
|
|
116
|
-
borderRadius: 8,
|
|
117
|
-
padding: 0,
|
|
118
|
-
maxWidth: 420,
|
|
119
|
-
maxHeight: "80vh",
|
|
120
|
-
overflow: "hidden",
|
|
121
|
-
display: "flex",
|
|
122
|
-
flexDirection: "column",
|
|
123
|
-
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
124
|
-
boxShadow: `0 8px 32px ${withAlpha(JimboColorOption.BLACK, 0.6)}`,
|
|
125
|
-
},
|
|
126
|
-
header: {
|
|
127
|
-
display: "flex",
|
|
128
|
-
alignItems: "center",
|
|
129
|
-
justifyContent: "space-between",
|
|
130
|
-
padding: "10px 12px",
|
|
131
|
-
borderBottom: `1px solid ${JimboColorOption.TEAL_GREY}`,
|
|
132
|
-
background: withAlpha(JimboColorOption.DARK_GREY, 0.5),
|
|
133
|
-
},
|
|
134
|
-
backBtn: {
|
|
135
|
-
background: "none",
|
|
136
|
-
border: "none",
|
|
137
|
-
color: JimboColorOption.GREY,
|
|
138
|
-
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
139
|
-
fontSize: 14,
|
|
140
|
-
cursor: "pointer",
|
|
141
|
-
padding: "4px 8px",
|
|
142
|
-
},
|
|
143
|
-
title: {
|
|
144
|
-
fontSize: 16,
|
|
145
|
-
fontWeight: "bold",
|
|
146
|
-
letterSpacing: 0.5,
|
|
147
|
-
},
|
|
148
|
-
searchRow: {
|
|
149
|
-
display: "flex",
|
|
150
|
-
gap: 8,
|
|
151
|
-
padding: "10px 12px 6px",
|
|
152
|
-
},
|
|
153
|
-
searchInput: {
|
|
154
|
-
flex: 1,
|
|
155
|
-
padding: "6px 10px",
|
|
156
|
-
borderRadius: 4,
|
|
157
|
-
border: `1px solid ${JimboColorOption.TEAL_GREY}`,
|
|
158
|
-
background: withAlpha(JimboColorOption.DARK_GREY, 0.8),
|
|
159
|
-
color: JimboColorOption.WHITE,
|
|
160
|
-
fontSize: 13,
|
|
161
|
-
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
162
|
-
outline: "none",
|
|
163
|
-
},
|
|
164
|
-
anyBtn: {
|
|
165
|
-
padding: "6px 14px",
|
|
166
|
-
borderRadius: 4,
|
|
167
|
-
border: `1px solid ${JimboColorOption.GOLD}`,
|
|
168
|
-
background: withAlpha(JimboColorOption.GOLD, 0.15),
|
|
169
|
-
color: JimboColorOption.GOLD,
|
|
170
|
-
fontSize: 13,
|
|
171
|
-
fontFamily: "m6x11plus, ui-monospace, monospace",
|
|
172
|
-
cursor: "pointer",
|
|
173
|
-
fontWeight: "bold",
|
|
174
|
-
},
|
|
175
|
-
hint: {
|
|
176
|
-
display: "flex",
|
|
177
|
-
alignItems: "flex-start",
|
|
178
|
-
gap: 6,
|
|
179
|
-
margin: "4px 12px 8px",
|
|
180
|
-
padding: "8px 10px",
|
|
181
|
-
borderRadius: 4,
|
|
182
|
-
background: withAlpha(JimboColorOption.TEAL_GREY, 0.12),
|
|
183
|
-
border: `1px solid ${withAlpha(JimboColorOption.TEAL_GREY, 0.3)}`,
|
|
184
|
-
color: JimboColorOption.GREY,
|
|
185
|
-
fontSize: 11,
|
|
186
|
-
lineHeight: "1.4",
|
|
187
|
-
},
|
|
188
|
-
grid: {
|
|
189
|
-
display: "grid",
|
|
190
|
-
gridTemplateColumns: "repeat(auto-fill, minmax(64px, 1fr))",
|
|
191
|
-
gap: 6,
|
|
192
|
-
padding: "8px 12px 12px",
|
|
193
|
-
overflowY: "auto",
|
|
194
|
-
flex: 1,
|
|
195
|
-
},
|
|
196
|
-
cell: {
|
|
197
|
-
display: "flex",
|
|
198
|
-
flexDirection: "column",
|
|
199
|
-
alignItems: "center",
|
|
200
|
-
gap: 3,
|
|
201
|
-
padding: 4,
|
|
202
|
-
borderRadius: 4,
|
|
203
|
-
cursor: "pointer",
|
|
204
|
-
transition: "background 120ms",
|
|
205
|
-
background: "transparent",
|
|
206
|
-
},
|
|
207
|
-
label: {
|
|
208
|
-
fontSize: 9,
|
|
209
|
-
color: JimboColorOption.GREY,
|
|
210
|
-
textAlign: "center",
|
|
211
|
-
lineHeight: "1.2",
|
|
212
|
-
maxWidth: 60,
|
|
213
|
-
overflow: "hidden",
|
|
214
|
-
textOverflow: "ellipsis",
|
|
215
|
-
whiteSpace: "nowrap",
|
|
216
|
-
},
|
|
217
|
-
emptyState: {
|
|
218
|
-
gridColumn: "1 / -1",
|
|
219
|
-
textAlign: "center",
|
|
220
|
-
color: JimboColorOption.GREY,
|
|
221
|
-
fontSize: 13,
|
|
222
|
-
padding: 20,
|
|
223
|
-
},
|
|
224
|
-
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SlotSelection, type JamlZone } from "./MysterySlot.js";
|
|
2
|
+
export interface JamlMapEditorProps {
|
|
3
|
+
/** Initial zone for the demo. */
|
|
4
|
+
zone?: JamlZone;
|
|
5
|
+
/** Callback when selections change. Returns JAML string. */
|
|
6
|
+
onChange?: (jamlString: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export interface MapSlotSelection extends SlotSelection {
|
|
9
|
+
zone: JamlZone;
|
|
10
|
+
}
|
|
11
|
+
export declare function JamlMapEditor({ zone: initialZone, onChange, }: JamlMapEditorProps): import("react/jsx-runtime").JSX.Element;
|