jaml-ui 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -3,6 +3,8 @@ import { type JamlIdeMode } from "./JamlIdeToolbar.js";
3
3
  export interface JamlIdeSearchResult {
4
4
  seed: string;
5
5
  score?: number;
6
+ tallyColumns?: number[];
7
+ tallyLabels?: string[];
6
8
  }
7
9
  export interface JamlIdeProps {
8
10
  jaml: string;
@@ -1,29 +1,92 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from "react";
4
4
  import { JamlMapPreview } from "./JamlMapPreview.js";
5
5
  import { JamlIdeToolbar } from "./JamlIdeToolbar.js";
6
+ import { JimboColorOption } from "../ui/tokens.js";
7
+ function TallyBar({ value, max }) {
8
+ const pct = max > 0 ? Math.min(1, value / max) : 0;
9
+ return (_jsx("div", { style: { flex: 1, height: 4, borderRadius: 999, background: `${JimboColorOption.DARK_GREY}88`, overflow: "hidden" }, children: _jsx("div", { style: {
10
+ height: "100%",
11
+ width: `${pct * 100}%`,
12
+ borderRadius: 999,
13
+ background: value > 0 ? JimboColorOption.GREEN : JimboColorOption.GREY,
14
+ transition: "width 200ms ease",
15
+ } }) }));
16
+ }
6
17
  function ResultsView({ results }) {
18
+ const [expanded, setExpanded] = useState(null);
7
19
  if (results.length === 0) {
8
20
  return (_jsx("div", { style: {
9
- border: "1px dashed rgba(255,255,255,0.18)",
10
- borderRadius: 12,
11
- padding: 14,
21
+ border: `1px dashed ${JimboColorOption.DARK_GREY}`,
22
+ borderRadius: 10,
23
+ padding: 16,
12
24
  fontSize: 12,
13
- opacity: 0.72,
14
- background: "rgba(255,255,255,0.03)",
15
- }, children: "No results yet." }));
25
+ color: JimboColorOption.GREY,
26
+ background: `${JimboColorOption.DARKEST}88`,
27
+ textAlign: "center",
28
+ }, children: "No results yet. Run a search to find seeds." }));
16
29
  }
17
- return (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: results.map((result, index) => (_jsxs("div", { style: {
18
- display: "flex",
19
- alignItems: "center",
20
- justifyContent: "space-between",
21
- gap: 12,
22
- borderRadius: 12,
23
- border: "1px solid rgba(255,255,255,0.08)",
24
- background: "rgba(255,255,255,0.03)",
25
- padding: "10px 12px",
26
- }, children: [_jsx("div", { style: { fontWeight: 700, letterSpacing: 0.4 }, children: result.seed }), _jsx("div", { style: { fontSize: 12, opacity: 0.7 }, children: result.score !== undefined ? result.score : "-" })] }, `${result.seed}-${index}`))) }));
30
+ const labels = results[0]?.tallyLabels ?? [];
31
+ const maxScore = Math.max(...results.map((r) => r.score ?? 0));
32
+ return (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: results.map((result) => {
33
+ const isOpen = expanded === result.seed;
34
+ const hasTally = result.tallyColumns && result.tallyColumns.length > 0 && labels.length > 0;
35
+ return (_jsxs("div", { style: {
36
+ borderRadius: 10,
37
+ border: `1px solid ${isOpen ? JimboColorOption.GOLD + "55" : JimboColorOption.PANEL_EDGE}`,
38
+ background: isOpen ? `${JimboColorOption.GOLD}0a` : `${JimboColorOption.DARKEST}cc`,
39
+ overflow: "hidden",
40
+ transition: "border-color 120ms",
41
+ }, children: [_jsxs("button", { type: "button", onClick: () => hasTally && setExpanded(isOpen ? null : result.seed), style: {
42
+ width: "100%",
43
+ display: "flex",
44
+ alignItems: "center",
45
+ gap: 10,
46
+ padding: "9px 12px",
47
+ background: "none",
48
+ border: "none",
49
+ cursor: hasTally ? "pointer" : "default",
50
+ color: "inherit",
51
+ textAlign: "left",
52
+ }, children: [_jsx("span", { style: {
53
+ fontFamily: "m6x11plus, monospace",
54
+ fontWeight: 700,
55
+ fontSize: 14,
56
+ letterSpacing: 1,
57
+ color: JimboColorOption.GOLD_TEXT,
58
+ minWidth: 80,
59
+ }, children: result.seed }), result.score !== undefined ? (_jsxs(_Fragment, { children: [_jsx(TallyBar, { value: result.score, max: maxScore }), _jsx("span", { style: {
60
+ fontSize: 12,
61
+ fontWeight: 700,
62
+ color: result.score > 0 ? JimboColorOption.GREEN_TEXT : JimboColorOption.GREY,
63
+ minWidth: 36,
64
+ textAlign: "right",
65
+ }, children: result.score })] })) : null, hasTally ? (_jsx("span", { style: { fontSize: 10, color: JimboColorOption.GREY, marginLeft: 2 }, children: isOpen ? "▲" : "▼" })) : null] }), isOpen && hasTally ? (_jsx("div", { style: {
66
+ borderTop: `1px solid ${JimboColorOption.PANEL_EDGE}`,
67
+ padding: "8px 12px 10px",
68
+ display: "flex",
69
+ flexDirection: "column",
70
+ gap: 5,
71
+ }, children: labels.map((label, i) => {
72
+ const val = result.tallyColumns[i] ?? 0;
73
+ const maxVal = Math.max(...results.map((r) => r.tallyColumns?.[i] ?? 0));
74
+ return (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: {
75
+ fontSize: 11,
76
+ color: val > 0 ? JimboColorOption.WHITE : JimboColorOption.GREY,
77
+ minWidth: 140,
78
+ overflow: "hidden",
79
+ textOverflow: "ellipsis",
80
+ whiteSpace: "nowrap",
81
+ }, children: label }), _jsx(TallyBar, { value: val, max: maxVal }), _jsx("span", { style: {
82
+ fontSize: 11,
83
+ fontWeight: 700,
84
+ color: val > 0 ? JimboColorOption.GREEN_TEXT : JimboColorOption.DARK_GREY,
85
+ minWidth: 24,
86
+ textAlign: "right",
87
+ }, children: val })] }, label));
88
+ }) })) : null] }, result.seed));
89
+ }) }));
27
90
  }
28
91
  export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults = [], className = "", title = "JAML IDE", actions, codePlaceholder = "Enter JAML...", onSearch, isSearching = false, }) {
29
92
  const [mode, setMode] = useState(defaultMode);
@@ -32,20 +95,21 @@ export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults =
32
95
  display: "flex",
33
96
  flexDirection: "column",
34
97
  minHeight: 420,
35
- borderRadius: 16,
98
+ borderRadius: 12,
36
99
  overflow: "hidden",
37
- border: "1px solid rgba(255,255,255,0.08)",
38
- background: "#17181c",
39
- color: "#f5f5f5",
100
+ border: `2px solid ${JimboColorOption.BORDER_SILVER}`,
101
+ boxShadow: `0 3px 0 0 ${JimboColorOption.BORDER_SOUTH}`,
102
+ background: JimboColorOption.DARK_GREY,
103
+ color: JimboColorOption.WHITE,
40
104
  }, children: [_jsxs("div", { style: {
41
105
  display: "flex",
42
106
  alignItems: "center",
43
107
  justifyContent: "space-between",
44
108
  gap: 12,
45
- padding: "12px 14px",
46
- borderBottom: "1px solid rgba(255,255,255,0.08)",
47
- background: "rgba(255,255,255,0.03)",
48
- }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 16, fontWeight: 800 }, children: title }), _jsx("div", { style: { fontSize: 11, opacity: 0.66 }, children: "Reusable JAML authoring and preview surface." })] }), actions ? _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: actions }) : null] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length, onSearch: onSearch, isSearching: isSearching }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: [mode === "code" ? (_jsx("textarea", { title: "JAML IDE Editor", value: jaml, onChange: (event) => onChange(event.target.value), placeholder: codePlaceholder, spellCheck: false, autoCapitalize: "off", autoCorrect: "off", style: {
109
+ padding: "10px 14px",
110
+ borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
111
+ background: JimboColorOption.TEAL_GREY,
112
+ }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontSize: 14, fontWeight: 800, fontFamily: "m6x11plus, monospace", color: JimboColorOption.GOLD_TEXT }, children: title }), _jsx("div", { style: { fontSize: 11, color: JimboColorOption.GREY }, children: "Jimbo's Ante Markup Language" })] }), actions ? _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: actions }) : null] }), _jsx(JamlIdeToolbar, { mode: mode, onModeChange: setMode, resultCount: results.length, onSearch: onSearch, isSearching: isSearching }), _jsxs("div", { style: { flex: 1, minHeight: 0, overflow: "auto", background: JimboColorOption.DARKEST }, children: [mode === "code" ? (_jsx("textarea", { title: "JAML IDE Editor", value: jaml, onChange: (event) => onChange(event.target.value), placeholder: codePlaceholder, spellCheck: false, autoCapitalize: "off", autoCorrect: "off", style: {
49
113
  width: "100%",
50
114
  minHeight: 320,
51
115
  resize: "vertical",
@@ -53,9 +117,9 @@ export function JamlIde({ jaml, onChange, defaultMode = "code", searchResults =
53
117
  outline: 0,
54
118
  padding: 16,
55
119
  background: "transparent",
56
- color: "inherit",
120
+ color: JimboColorOption.WHITE,
57
121
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
58
122
  fontSize: 13,
59
123
  lineHeight: 1.7,
60
- } })) : null, mode === "map" ? _jsx(JamlMapPreview, { jaml: jaml }) : null, mode === "results" ? _jsx("div", { style: { padding: 16 }, children: _jsx(ResultsView, { results: results }) }) : null] })] }));
124
+ } })) : null, mode === "map" ? _jsx(JamlMapPreview, { jaml: jaml }) : null, mode === "results" ? (_jsx("div", { style: { padding: 12 }, children: _jsx(ResultsView, { results: results }) })) : null] })] }));
61
125
  }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { JimboColorOption } from "../ui/tokens.js";
3
4
  const TABS = [
4
5
  { id: "code", label: "Code" },
5
6
  { id: "map", label: "Map" },
@@ -11,35 +12,43 @@ export function JamlIdeToolbar({ mode, onModeChange, resultCount = 0, className
11
12
  alignItems: "center",
12
13
  justifyContent: "space-between",
13
14
  gap: 8,
14
- padding: "8px 10px",
15
- borderBottom: "1px solid rgba(255,255,255,0.08)",
16
- background: "rgba(255,255,255,0.04)",
17
- }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: TABS.map((tab) => {
15
+ padding: "6px 10px",
16
+ borderBottom: `1px solid ${JimboColorOption.PANEL_EDGE}`,
17
+ background: JimboColorOption.DARKEST,
18
+ }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: TABS.map((tab) => {
18
19
  const selected = mode === tab.id;
19
20
  return (_jsxs("button", { type: "button", onClick: () => onModeChange(tab.id), style: {
20
21
  cursor: "pointer",
21
- borderRadius: 8,
22
- border: selected ? "1px solid rgba(247,185,85,0.55)" : "1px solid transparent",
23
- background: selected ? "rgba(247,185,85,0.16)" : "transparent",
24
- color: selected ? "#f7b955" : "rgba(255,255,255,0.58)",
25
- padding: "6px 10px",
22
+ borderRadius: 6,
23
+ border: selected ? `1px solid ${JimboColorOption.GOLD}` : "1px solid transparent",
24
+ background: selected ? `${JimboColorOption.GOLD}22` : "transparent",
25
+ color: selected ? JimboColorOption.GOLD_TEXT : JimboColorOption.GREY,
26
+ padding: "5px 10px",
26
27
  fontSize: 11,
27
- fontWeight: 600,
28
+ fontWeight: 700,
29
+ fontFamily: "m6x11plus, monospace",
30
+ transition: "background 120ms, color 120ms",
28
31
  }, children: [tab.label, tab.id === "results" && resultCount > 0 ? (_jsx("span", { style: {
29
32
  marginLeft: 6,
30
33
  borderRadius: 999,
31
- background: "rgba(0,0,0,0.25)",
34
+ background: `${JimboColorOption.GOLD}33`,
35
+ color: JimboColorOption.GOLD_TEXT,
32
36
  padding: "1px 6px",
33
37
  fontSize: 10,
34
38
  }, children: resultCount })) : null] }, tab.id));
35
- }) }), onSearch ? (_jsx("button", { type: "button", onClick: onSearch, disabled: isSearching, style: {
36
- cursor: isSearching ? "not-allowed" : "pointer",
37
- borderRadius: 8,
38
- border: "1px solid rgba(74,222,128,0.4)",
39
- background: isSearching ? "rgba(239,68,68,0.2)" : "rgba(74,222,128,0.15)",
40
- color: isSearching ? "#ef4444" : "#4ade80",
41
- padding: "6px 14px",
39
+ }) }), onSearch ? (_jsx("button", { type: "button", onClick: onSearch, style: {
40
+ cursor: "pointer",
41
+ borderRadius: 6,
42
+ border: isSearching
43
+ ? `1px solid ${JimboColorOption.DARK_RED}`
44
+ : `1px solid ${JimboColorOption.GREEN}`,
45
+ background: isSearching
46
+ ? `${JimboColorOption.RED}22`
47
+ : `${JimboColorOption.GREEN}22`,
48
+ color: isSearching ? JimboColorOption.RED : JimboColorOption.GREEN_TEXT,
49
+ padding: "5px 14px",
42
50
  fontSize: 11,
43
51
  fontWeight: 700,
52
+ fontFamily: "m6x11plus, monospace",
44
53
  }, children: isSearching ? "Stop" : "Search" })) : null] }));
45
54
  }
@@ -0,0 +1,25 @@
1
+ import type { SpriteData } from '../sprites/spriteMapper.js';
2
+ export declare const CARD_DIMENSIONS: {
3
+ readonly WIDTH: 0.7;
4
+ readonly HEIGHT: 0.95;
5
+ readonly DEPTH: 0.02;
6
+ };
7
+ export declare const CARD_MAGNET: {
8
+ readonly MAX_TILT_X: 0.36;
9
+ readonly MAX_TILT_Y: 0.42;
10
+ readonly MAX_SHIFT: 0.038;
11
+ readonly TWIST_Z: 0.11;
12
+ readonly LERP_IN: 18;
13
+ readonly LERP_OUT: 10;
14
+ };
15
+ export interface Card3DProps {
16
+ sprite: SpriteData;
17
+ position?: [number, number, number];
18
+ rotation?: [number, number, number];
19
+ selected?: boolean;
20
+ highlighted?: boolean;
21
+ onClick?: () => void;
22
+ onPointerEnter?: () => void;
23
+ onPointerLeave?: () => void;
24
+ }
25
+ export declare const Card3D: import("react").NamedExoticComponent<Card3DProps>;
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useMemo, useState, useEffect, memo } from 'react';
4
+ import { useFrame } from '@react-three/fiber';
5
+ import { useSpring, animated } from '@react-spring/three';
6
+ import * as THREE from 'three';
7
+ import { SPRITE_SHEETS } from '../sprites/spriteData.js';
8
+ export const CARD_DIMENSIONS = { WIDTH: 0.7, HEIGHT: 0.95, DEPTH: 0.02 };
9
+ export const CARD_MAGNET = {
10
+ MAX_TILT_X: 0.36,
11
+ MAX_TILT_Y: 0.42,
12
+ MAX_SHIFT: 0.038,
13
+ TWIST_Z: 0.11,
14
+ LERP_IN: 18,
15
+ LERP_OUT: 10,
16
+ };
17
+ const SHEET_KEY_MAP = {
18
+ Jokers: 'jokers',
19
+ Tarots: 'tarots',
20
+ Vouchers: 'vouchers',
21
+ Boosters: 'boosters',
22
+ Enhancers: 'enhancers',
23
+ Editions: 'editions',
24
+ BlindChips: 'blinds',
25
+ tags: 'tags',
26
+ };
27
+ const _textureCache = new Map();
28
+ function useSpriteTexture(sprite) {
29
+ const [texture, setTexture] = useState(null);
30
+ const serial = useRef(0);
31
+ useEffect(() => {
32
+ const id = ++serial.current;
33
+ const sheet = SPRITE_SHEETS[SHEET_KEY_MAP[sprite.type]];
34
+ const url = sheet.src;
35
+ const cols = sheet.columns;
36
+ const rows = sheet.rows;
37
+ const { x, y } = sprite.pos;
38
+ const applySlice = (base) => {
39
+ const t = base.clone();
40
+ t.repeat.set(1 / cols, 1 / rows);
41
+ t.offset.set(x / cols, (rows - y - 1) / rows);
42
+ t.needsUpdate = true;
43
+ return t;
44
+ };
45
+ if (_textureCache.has(url)) {
46
+ setTexture(applySlice(_textureCache.get(url)));
47
+ return;
48
+ }
49
+ const loader = new THREE.TextureLoader();
50
+ loader.load(url, (loaded) => {
51
+ if (id !== serial.current)
52
+ return;
53
+ loaded.colorSpace = THREE.SRGBColorSpace;
54
+ loaded.magFilter = THREE.NearestFilter;
55
+ loaded.minFilter = THREE.NearestFilter;
56
+ _textureCache.set(url, loaded);
57
+ setTexture(applySlice(loaded));
58
+ }, undefined, (err) => console.error('[Card3D] texture load failed:', url, err));
59
+ }, [sprite.type, sprite.pos.x, sprite.pos.y]);
60
+ return texture;
61
+ }
62
+ export const Card3D = memo(function Card3D({ sprite, position = [0, 0, 0], rotation = [0, 0, 0], selected = false, highlighted = false, onClick, onPointerEnter, onPointerLeave, }) {
63
+ const tiltRef = useRef(null);
64
+ const target = useRef({ rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 });
65
+ const [hovered, setHovered] = useState(false);
66
+ const texture = useSpriteTexture(sprite);
67
+ const { posY, scale } = useSpring({
68
+ posY: selected ? 0.3 : hovered ? 0.15 : 0,
69
+ scale: hovered ? 1.08 : selected ? 1.05 : 1,
70
+ config: { tension: 300, friction: 20 },
71
+ });
72
+ useFrame((_state, dt) => {
73
+ const g = tiltRef.current;
74
+ if (!g)
75
+ return;
76
+ const t = target.current;
77
+ const rate = hovered ? CARD_MAGNET.LERP_IN : CARD_MAGNET.LERP_OUT;
78
+ const a = 1 - Math.exp(-rate * dt);
79
+ g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, t.rx, a);
80
+ g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, t.ry, a);
81
+ g.rotation.z = THREE.MathUtils.lerp(g.rotation.z, t.rz, a);
82
+ g.position.x = THREE.MathUtils.lerp(g.position.x, t.ox, a);
83
+ g.position.y = THREE.MathUtils.lerp(g.position.y, t.oy, a);
84
+ });
85
+ const glowColor = useMemo(() => highlighted ? '#e4b643' : '#ffffff', [highlighted]);
86
+ const onMove = (e) => {
87
+ e.stopPropagation();
88
+ const uv = e.uv;
89
+ if (!uv)
90
+ return;
91
+ const nx = THREE.MathUtils.clamp((uv.x - 0.5) * 2, -1, 1);
92
+ const ny = THREE.MathUtils.clamp((uv.y - 0.5) * 2, -1, 1);
93
+ target.current.ry = -nx * CARD_MAGNET.MAX_TILT_Y;
94
+ target.current.rx = ny * CARD_MAGNET.MAX_TILT_X;
95
+ target.current.rz = -nx * ny * CARD_MAGNET.TWIST_Z;
96
+ target.current.ox = nx * CARD_MAGNET.MAX_SHIFT;
97
+ target.current.oy = -ny * CARD_MAGNET.MAX_SHIFT * 0.65;
98
+ };
99
+ const reset = () => { target.current = { rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 }; };
100
+ if (!texture)
101
+ return null;
102
+ return (_jsx(animated.group, { "position-x": position[0], "position-y": posY.to((y) => position[1] + y), "position-z": position[2], "rotation-x": rotation[0], "rotation-y": rotation[1], "rotation-z": rotation[2], scale: scale, children: _jsxs("group", { ref: tiltRef, children: [highlighted && (_jsx("pointLight", { color: glowColor, intensity: 1.5, distance: 1, position: [0, 0, 0.1] })), _jsxs("mesh", { onClick: (e) => { e.stopPropagation(); onClick?.(); }, onPointerMove: onMove, onPointerEnter: (e) => { e.stopPropagation(); setHovered(true); onPointerEnter?.(); document.body.style.cursor = 'pointer'; }, onPointerLeave: (e) => { e.stopPropagation(); setHovered(false); reset(); onPointerLeave?.(); document.body.style.cursor = 'auto'; }, castShadow: true, receiveShadow: true, children: [_jsx("boxGeometry", { args: [CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH] }), _jsx("meshBasicMaterial", { attach: "material-4", map: texture, toneMapped: false }), _jsx("meshStandardMaterial", { attach: "material-5", color: "#1a1a2e", metalness: 0.2, roughness: 0.8 }), _jsx("meshStandardMaterial", { attach: "material-0", color: "#f5f5dc" }), _jsx("meshStandardMaterial", { attach: "material-1", color: "#f5f5dc" }), _jsx("meshStandardMaterial", { attach: "material-2", color: "#f5f5dc" }), _jsx("meshStandardMaterial", { attach: "material-3", color: "#f5f5dc" })] }), selected && (_jsxs("mesh", { position: [0, 0, -CARD_DIMENSIONS.DEPTH], children: [_jsx("ringGeometry", { args: [0.45, 0.5, 32] }), _jsx("meshBasicMaterial", { color: "#e4b643", transparent: true, opacity: 0.8 })] }))] }) }));
103
+ });
package/dist/r3f.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './r3f/Card3D.js';
package/dist/r3f.js ADDED
@@ -0,0 +1 @@
1
+ export * from './r3f/Card3D.js';
@@ -0,0 +1,7 @@
1
+ export interface JimboCodeBlockProps {
2
+ code: string;
3
+ language?: string;
4
+ filename?: string;
5
+ className?: string;
6
+ }
7
+ export declare function JimboCodeBlock({ code, language, filename, className }: JimboCodeBlockProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { FiCopy, FiCheck } from 'react-icons/fi';
5
+ import { JimboColorOption } from './tokens.js';
6
+ export function JimboCodeBlock({ code, language, filename, className = '' }) {
7
+ const [copied, setCopied] = useState(false);
8
+ const copy = () => {
9
+ void navigator.clipboard.writeText(code);
10
+ setCopied(true);
11
+ setTimeout(() => setCopied(false), 2000);
12
+ };
13
+ return (_jsxs("div", { className: 'rounded-xl overflow-hidden flex flex-col border-2 ' + className, style: { backgroundColor: JimboColorOption.DARKEST, borderColor: JimboColorOption.PANEL_EDGE, boxShadow: '0 3px 0 0 rgba(0,0,0,0.5)' }, children: [_jsxs("div", { style: { padding: '0.5rem 1rem', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: `1px solid ${JimboColorOption.INNER_BORDER}` }, children: [_jsxs("div", { style: { display: 'flex', gap: '0.5rem', alignItems: 'center' }, children: [filename && _jsx("span", { style: { fontSize: 10, textTransform: 'uppercase', opacity: 0.6 }, children: filename }), language && _jsx("span", { style: { fontSize: 9, padding: '1px 6px', borderRadius: 3, background: 'rgba(0,0,0,0.4)', color: '#60a5fa', textTransform: 'uppercase' }, children: language })] }), _jsx("button", { onClick: copy, title: "Copy", style: { padding: 4, background: 'none', border: 'none', cursor: 'pointer', color: copied ? '#4ade80' : 'rgba(255,255,255,0.5)', display: 'flex' }, children: copied ? _jsx(FiCheck, { size: 14 }) : _jsx(FiCopy, { size: 14 }) })] }), _jsx("pre", { style: { padding: '1rem', overflowX: 'auto', fontFamily: 'monospace', fontSize: '0.875rem', lineHeight: 1.6, color: '#f6f0d5', margin: 0 }, children: _jsx("code", { children: code }) })] }));
14
+ }
@@ -0,0 +1,5 @@
1
+ export interface JimboBalatroFooterProps {
2
+ hidden?: boolean;
3
+ className?: string;
4
+ }
5
+ export declare function JimboBalatroFooter({ hidden, className }: JimboBalatroFooterProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { JimboColorOption } from './tokens.js';
4
+ const SUITS = [
5
+ { char: '♥️', kf: 'jaml-heart' },
6
+ { char: '♠️', kf: 'jaml-spade' },
7
+ { char: '♦️', kf: 'jaml-diamond' },
8
+ { char: '♣️', kf: 'jaml-club' },
9
+ ];
10
+ const CYCLE = '5s';
11
+ export function JimboBalatroFooter({ hidden = false, className = '' }) {
12
+ return (_jsxs("div", { className: ['fixed right-0 bottom-0 left-0 w-screen min-w-full transition-opacity duration-200', hidden ? 'pointer-events-none opacity-0' : 'opacity-100', className].filter(Boolean).join(' '), children: [_jsx("div", { style: { width: '100%', borderTop: '1px solid rgba(255,255,255,0.1)', background: 'rgba(0,0,0,0.9)', padding: '0 1rem 3px', textAlign: 'center' }, children: _jsxs("p", { style: { fontFamily: 'm6x11plus, monospace', fontSize: 'clamp(11px, 0.8vw + 8px, 14px)', display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center', gap: '0 0.5rem', color: 'white', margin: 0 }, children: [_jsx("span", { children: "Not affiliated with LocalThunk or PlayStack" }), _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }, children: ["Made with", ' ', _jsx("span", { style: { position: 'relative', display: 'inline-block', width: '1.5em', height: '1em', verticalAlign: 'middle' }, children: SUITS.map(({ char, kf }) => (_jsx("span", { style: { position: 'absolute', inset: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', opacity: 0, animationName: kf, animationDuration: CYCLE, animationDelay: '0s', animationIterationCount: 'infinite', animationTimingFunction: 'ease-out' }, children: char }, char))) }), ' ', "for the", ' ', _jsx("a", { href: "https://playbalatro.com", target: "_blank", rel: "noopener noreferrer", style: { color: JimboColorOption.GOLD, textDecoration: 'none' }, children: "Balatro" }), ' ', "community"] })] }) }), _jsx("style", { children: `
13
+ @keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
14
+ @keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
15
+ @keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
16
+ @keyframes jaml-club { 0%,81.5%{opacity:0} 82%{opacity:1;transform:scale(1.45)} 84.5%{opacity:1;transform:scale(1)} 95%{opacity:1} 96%{opacity:0} 100%{opacity:0} }
17
+ ` })] }));
18
+ }
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { type ButtonVariant } from './tokens.js';
3
+ export interface JimboPanelProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ sway?: boolean;
5
+ onBack?: () => void;
6
+ backLabel?: string;
7
+ hideBack?: boolean;
8
+ }
9
+ export declare const JimboPanel: React.MemoExoticComponent<({ children, className, sway, onBack, backLabel, hideBack, style, ...props }: JimboPanelProps) => import("react/jsx-runtime").JSX.Element>;
10
+ export interface JimboInnerPanelProps extends React.HTMLAttributes<HTMLDivElement> {
11
+ }
12
+ export declare const JimboInnerPanel: React.MemoExoticComponent<({ children, className, style, ...props }: JimboInnerPanelProps) => import("react/jsx-runtime").JSX.Element>;
13
+ export interface JimboButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
14
+ variant?: ButtonVariant;
15
+ size?: 'xs' | 'sm' | 'md' | 'lg';
16
+ fullWidth?: boolean;
17
+ }
18
+ export declare function JimboButton({ children, variant, size, fullWidth, className, style, disabled, ...props }: JimboButtonProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function JimboBackButton({ label, ...props }: Omit<JimboButtonProps, 'variant' | 'children'> & {
20
+ label?: string;
21
+ }): import("react/jsx-runtime").JSX.Element;
22
+ export interface JimboModalProps {
23
+ children: React.ReactNode;
24
+ open: boolean;
25
+ onClose: () => void;
26
+ title?: string;
27
+ className?: string;
28
+ }
29
+ export declare function JimboModal({ children, open, onClose, title, className }: JimboModalProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef, memo } from 'react';
4
+ import { JimboColorOption, JIMBO_ANIMATIONS } from './tokens.js';
5
+ export const JimboPanel = memo(({ children, className = '', sway = false, onBack, backLabel = 'Back', hideBack = false, style, ...props }) => {
6
+ const panelRef = useRef(null);
7
+ useEffect(() => {
8
+ if (!sway || !panelRef.current)
9
+ return;
10
+ let frame;
11
+ const start = Date.now();
12
+ const el = panelRef.current;
13
+ const tick = () => {
14
+ const t = ((Date.now() - start) % JIMBO_ANIMATIONS.SWAY_DURATION) / JIMBO_ANIMATIONS.SWAY_DURATION * Math.PI * 2;
15
+ el.style.transform = `translate(${Math.sin(t) * JIMBO_ANIMATIONS.SWAY_AMOUNT * 0.3}px, ${Math.sin(t * 0.8) * JIMBO_ANIMATIONS.SWAY_AMOUNT}px)`;
16
+ frame = requestAnimationFrame(tick);
17
+ };
18
+ frame = requestAnimationFrame(tick);
19
+ return () => { cancelAnimationFrame(frame); el.style.transform = ''; };
20
+ }, [sway]);
21
+ return (_jsxs("div", { ref: panelRef, className: 'rounded-xl p-4 flex flex-col items-stretch overflow-hidden ' + className, style: {
22
+ backgroundColor: JimboColorOption.DARK_GREY,
23
+ border: `3px solid ${JimboColorOption.BORDER_SILVER}`,
24
+ boxShadow: `0 3px 0 0 ${JimboColorOption.BORDER_SOUTH}`,
25
+ ...style,
26
+ }, ...props, children: [_jsx("div", { className: "flex-1 overflow-auto", children: children }), onBack && !hideBack && (_jsx("div", { className: "mt-4 pt-2 shrink-0", children: _jsx(JimboBackButton, { onClick: onBack, label: backLabel }) }))] }));
27
+ });
28
+ JimboPanel.displayName = 'JimboPanel';
29
+ export const JimboInnerPanel = memo(({ children, className = '', style, ...props }) => (_jsx("div", { className: 'rounded-lg p-3 ' + className, style: { backgroundColor: JimboColorOption.INNER_BORDER, border: `2px solid ${JimboColorOption.PANEL_EDGE}`, ...style }, ...props, children: children })));
30
+ JimboInnerPanel.displayName = 'JimboInnerPanel';
31
+ // ─── Button ──────────────────────────────────────────────────────────────────
32
+ const VARIANT_COLORS = {
33
+ primary: { bg: JimboColorOption.RED, hover: JimboColorOption.DARK_RED, text: '#fff' },
34
+ secondary: { bg: JimboColorOption.BLUE, hover: JimboColorOption.DARK_BLUE, text: '#fff' },
35
+ danger: { bg: JimboColorOption.RED, hover: JimboColorOption.DARK_RED, text: '#fff' },
36
+ back: { bg: JimboColorOption.ORANGE, hover: JimboColorOption.DARK_ORANGE, text: '#fff' },
37
+ ghost: { bg: 'transparent', hover: 'rgba(255,255,255,0.1)', text: '#fff' },
38
+ };
39
+ export function JimboButton({ children, variant = 'primary', size = 'md', fullWidth = false, className = '', style, disabled, ...props }) {
40
+ const [hovered, setHovered] = useState(false);
41
+ const [pressed, setPressed] = useState(false);
42
+ const c = VARIANT_COLORS[variant];
43
+ const pad = { xs: '0.2rem 0.5rem', sm: '0.25rem 0.75rem', md: '0.375rem 1rem', lg: '0.5rem 1.5rem' }[size];
44
+ return (_jsx("button", { disabled: disabled, onMouseEnter: () => { if (!disabled)
45
+ setHovered(true); }, onMouseLeave: () => { setHovered(false); setPressed(false); }, onMouseDown: () => { if (!disabled)
46
+ setPressed(true); }, onMouseUp: () => setPressed(false), className: className, style: {
47
+ fontFamily: 'm6x11plus, monospace',
48
+ backgroundColor: hovered ? c.hover : c.bg,
49
+ color: c.text,
50
+ padding: pad,
51
+ borderRadius: '0.5rem',
52
+ border: 'none',
53
+ cursor: disabled ? 'not-allowed' : 'pointer',
54
+ width: fullWidth ? '100%' : undefined,
55
+ opacity: disabled ? 0.5 : 1,
56
+ transform: pressed ? 'translateY(3px)' : 'none',
57
+ boxShadow: pressed ? 'none' : '0 3px 0 0 rgba(0,0,0,0.5)',
58
+ textShadow: '1px 1px 0 rgba(0,0,0,0.8)',
59
+ userSelect: 'none',
60
+ ...style,
61
+ }, ...props, children: children }));
62
+ }
63
+ export function JimboBackButton({ label = 'Back', ...props }) {
64
+ return _jsx(JimboButton, { variant: "back", size: "sm", fullWidth: true, ...props, children: label });
65
+ }
66
+ export function JimboModal({ children, open, onClose, title, className }) {
67
+ const [visible, setVisible] = useState(open);
68
+ const [opacity, setOpacity] = useState(open ? 1 : 0);
69
+ useEffect(() => {
70
+ if (open) {
71
+ setVisible(true);
72
+ requestAnimationFrame(() => setOpacity(1));
73
+ }
74
+ else {
75
+ setOpacity(0);
76
+ const t = setTimeout(() => setVisible(false), JIMBO_ANIMATIONS.MENU_SINK_DURATION);
77
+ return () => clearTimeout(t);
78
+ }
79
+ }, [open]);
80
+ if (!visible)
81
+ return null;
82
+ return (_jsx("div", { style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1rem', background: 'rgba(0,0,0,0.7)', opacity, transition: `opacity ${JIMBO_ANIMATIONS.MENU_SINK_DURATION}ms ease` }, onClick: onClose, children: _jsxs(JimboPanel, { sway: true, onBack: onClose, backLabel: "Close", className: 'w-full flex flex-col max-h-[90vh] ' + (className ?? 'max-w-lg'), onClick: (e) => e.stopPropagation(), children: [title && _jsx("h2", { style: { fontFamily: 'm6x11plus, monospace', color: '#fff', textAlign: 'center', margin: '0 0 1rem', fontSize: '1.25rem' }, children: title }), children] }) }));
83
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Balatro design tokens — colors eyedropped from actual game pixels.
3
+ * Do NOT replace with Lua HEX values; the game's shader pipeline transforms them.
4
+ */
5
+ export declare const JimboColorOption: {
6
+ readonly RED: "#ff4c40";
7
+ readonly BLUE: "#0093ff";
8
+ readonly GREEN: "#429f79";
9
+ readonly ORANGE: "#ff9800";
10
+ readonly GOLD: "#e4b643";
11
+ readonly PURPLE: "#9e74ce";
12
+ readonly DARK_RED: "#a02721";
13
+ readonly DARK_BLUE: "#0057a1";
14
+ readonly DARK_ORANGE: "#a05b00";
15
+ readonly DARK_GREEN: "#215f46";
16
+ readonly DARK_PURPLE: "#5e437e";
17
+ readonly DARK_GREY: "#3a5055";
18
+ readonly DARKEST: "#1e2b2d";
19
+ readonly GREY: "#708386";
20
+ readonly TEAL_GREY: "#404c4e";
21
+ readonly PANEL_EDGE: "#1e2e32";
22
+ readonly INNER_BORDER: "#334461";
23
+ readonly BORDER_SILVER: "#b9c2d2";
24
+ readonly BORDER_SOUTH: "#777e89";
25
+ readonly GOLD_TEXT: "#e4b643";
26
+ readonly GREEN_TEXT: "#35bd86";
27
+ readonly ORANGE_TEXT: "#ff8f00";
28
+ readonly WHITE: "#ffffff";
29
+ readonly BLACK: "#000000";
30
+ readonly TAROT_BUTTON: "#9e74ce";
31
+ readonly PLANET_BUTTON: "#00a7ca";
32
+ readonly SPECTRAL_BUTTON: "#2e76fd";
33
+ readonly TAROT_BUTTON_DARK: "#5e437e";
34
+ readonly PLANET_BUTTON_DARK: "#00657c";
35
+ readonly SPECTRAL_BUTTON_DARK: "#14449e";
36
+ };
37
+ export declare const JAML_COLORS: {
38
+ readonly RED: "#ff4c40";
39
+ readonly BLUE: "#0093ff";
40
+ readonly GREEN: "#429f79";
41
+ readonly ORANGE: "#ff9800";
42
+ readonly PURPLE: "#9e74ce";
43
+ readonly WHITE: "#ffffff";
44
+ readonly DARK_RED: "#a02721";
45
+ readonly DARK_BLUE: "#0057a1";
46
+ readonly DARK_ORANGE: "#a05b00";
47
+ readonly DARK_GREEN: "#215f46";
48
+ readonly DARK_PURPLE: "#5e437e";
49
+ };
50
+ export type JimboPaletteColor = keyof typeof JimboColorOption;
51
+ export declare function withAlpha(hex: string, alpha: number): string;
52
+ export declare const JIMBO_ANIMATIONS: {
53
+ readonly JUICE_UP_SCALE: 1.05;
54
+ readonly JUICE_DOWN_SCALE: 1;
55
+ readonly JUICE_DURATION: 150;
56
+ readonly JUICE_EASING: "cubic-bezier(0.34, 1.56, 0.64, 1)";
57
+ readonly SWAY_AMOUNT: 1.5;
58
+ readonly SWAY_DURATION: 4000;
59
+ readonly PRESS_TRANSLATE_Y: 2;
60
+ readonly PRESS_DURATION: 50;
61
+ readonly CARD_TILT_MAX: 6;
62
+ readonly MENU_SINK_DURATION: 200;
63
+ readonly MENU_RISE_DURATION: 300;
64
+ readonly LETTER_POP_RATE: 3;
65
+ readonly LETTER_BUMP_RATE: 2.666;
66
+ };
67
+ export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'back' | 'ghost';
68
+ export declare const BUTTON_COLORS: Record<ButtonVariant, {
69
+ bg: string;
70
+ hover: string;
71
+ text: string;
72
+ }>;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Balatro design tokens — colors eyedropped from actual game pixels.
3
+ * Do NOT replace with Lua HEX values; the game's shader pipeline transforms them.
4
+ */
5
+ export const JimboColorOption = {
6
+ RED: '#ff4c40',
7
+ BLUE: '#0093ff',
8
+ GREEN: '#429f79',
9
+ ORANGE: '#ff9800',
10
+ GOLD: '#e4b643',
11
+ PURPLE: '#9e74ce',
12
+ DARK_RED: '#a02721',
13
+ DARK_BLUE: '#0057a1',
14
+ DARK_ORANGE: '#a05b00',
15
+ DARK_GREEN: '#215f46',
16
+ DARK_PURPLE: '#5e437e',
17
+ DARK_GREY: '#3a5055',
18
+ DARKEST: '#1e2b2d',
19
+ GREY: '#708386',
20
+ TEAL_GREY: '#404c4e',
21
+ PANEL_EDGE: '#1e2e32',
22
+ INNER_BORDER: '#334461',
23
+ BORDER_SILVER: '#b9c2d2',
24
+ BORDER_SOUTH: '#777e89',
25
+ GOLD_TEXT: '#e4b643',
26
+ GREEN_TEXT: '#35bd86',
27
+ ORANGE_TEXT: '#ff8f00',
28
+ WHITE: '#ffffff',
29
+ BLACK: '#000000',
30
+ TAROT_BUTTON: '#9e74ce',
31
+ PLANET_BUTTON: '#00a7ca',
32
+ SPECTRAL_BUTTON: '#2e76fd',
33
+ TAROT_BUTTON_DARK: '#5e437e',
34
+ PLANET_BUTTON_DARK: '#00657c',
35
+ SPECTRAL_BUTTON_DARK: '#14449e',
36
+ };
37
+ export const JAML_COLORS = {
38
+ RED: JimboColorOption.RED,
39
+ BLUE: JimboColorOption.BLUE,
40
+ GREEN: JimboColorOption.GREEN,
41
+ ORANGE: JimboColorOption.ORANGE,
42
+ PURPLE: JimboColorOption.PURPLE,
43
+ WHITE: JimboColorOption.WHITE,
44
+ DARK_RED: JimboColorOption.DARK_RED,
45
+ DARK_BLUE: JimboColorOption.DARK_BLUE,
46
+ DARK_ORANGE: JimboColorOption.DARK_ORANGE,
47
+ DARK_GREEN: JimboColorOption.DARK_GREEN,
48
+ DARK_PURPLE: JimboColorOption.DARK_PURPLE,
49
+ };
50
+ export function withAlpha(hex, alpha) {
51
+ const clean = hex.replace('#', '');
52
+ const r = parseInt(clean.slice(0, 2), 16);
53
+ const g = parseInt(clean.slice(2, 4), 16);
54
+ const b = parseInt(clean.slice(4, 6), 16);
55
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
56
+ }
57
+ export const JIMBO_ANIMATIONS = {
58
+ JUICE_UP_SCALE: 1.05,
59
+ JUICE_DOWN_SCALE: 1.0,
60
+ JUICE_DURATION: 150,
61
+ JUICE_EASING: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
62
+ SWAY_AMOUNT: 1.5,
63
+ SWAY_DURATION: 4000,
64
+ PRESS_TRANSLATE_Y: 2,
65
+ PRESS_DURATION: 50,
66
+ CARD_TILT_MAX: 6,
67
+ MENU_SINK_DURATION: 200,
68
+ MENU_RISE_DURATION: 300,
69
+ LETTER_POP_RATE: 3,
70
+ LETTER_BUMP_RATE: 2.666,
71
+ };
72
+ export const BUTTON_COLORS = {
73
+ primary: { bg: JimboColorOption.RED, hover: JimboColorOption.DARK_RED, text: JimboColorOption.WHITE },
74
+ secondary: { bg: JimboColorOption.BLUE, hover: JimboColorOption.DARK_BLUE, text: JimboColorOption.WHITE },
75
+ danger: { bg: JimboColorOption.RED, hover: JimboColorOption.DARK_RED, text: JimboColorOption.WHITE },
76
+ back: { bg: JimboColorOption.ORANGE, hover: JimboColorOption.DARK_ORANGE, text: JimboColorOption.WHITE },
77
+ ghost: { bg: 'transparent', hover: 'rgba(255,255,255,0.1)', text: JimboColorOption.WHITE },
78
+ };
package/dist/ui.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './ui/tokens.js';
2
+ export * from './ui/panel.js';
3
+ export * from './ui/codeBlock.js';
4
+ export * from './ui/footer.js';
package/dist/ui.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './ui/tokens.js';
2
+ export * from './ui/panel.js';
3
+ export * from './ui/codeBlock.js';
4
+ export * from './ui/footer.js';
package/package.json CHANGED
@@ -1,80 +1,109 @@
1
- {
2
- "name": "jaml-ui",
3
- "version": "0.4.1",
4
- "description": "Balatro rendering components, sprite metadata, and optional Motely helpers for React apps.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- },
13
- "./core": {
14
- "types": "./dist/core.d.ts",
15
- "import": "./dist/core.js"
16
- },
17
- "./motely": {
18
- "types": "./dist/motely.d.ts",
19
- "import": "./dist/motely.js"
20
- },
21
- "./assets/*": "./assets/*",
22
- "./package.json": "./package.json"
23
- },
24
- "sideEffects": false,
25
- "files": [
26
- "dist",
27
- "assets",
28
- "README.md",
29
- "LICENSE"
30
- ],
31
- "scripts": {
32
- "build": "tsc --pretty false",
33
- "dev": "tsc --watch",
34
- "typecheck": "tsc --noEmit --pretty false",
35
- "prepack": "npm run build"
36
- },
37
- "engines": {
38
- "node": ">=18"
39
- },
40
- "publishConfig": {
41
- "access": "public"
42
- },
43
- "repository": {
44
- "type": "git",
45
- "url": "https://github.com/OptimusPi/jaml-ui"
46
- },
47
- "homepage": "https://github.com/OptimusPi/jaml-ui#readme",
48
- "bugs": {
49
- "url": "https://github.com/OptimusPi/jaml-ui/issues"
50
- },
51
- "keywords": [
52
- "balatro",
53
- "jaml",
54
- "motely",
55
- "seed",
56
- "card",
57
- "sprite",
58
- "ui"
59
- ],
60
- "author": "pifreak",
61
- "license": "MIT",
62
- "peerDependencies": {
63
- "motely-wasm": "^10.2.0 || ^11.0.0 || ^12.0.0",
64
- "react": "^18.2.0 || ^19.0.0",
65
- "react-dom": "^18.2.0 || ^19.0.0"
66
- },
67
- "peerDependenciesMeta": {
68
- "motely-wasm": {
69
- "optional": true
70
- }
71
- },
72
- "devDependencies": {
73
- "@types/react": "^19.2.14",
74
- "@types/react-dom": "^19.2.3",
75
- "motely-wasm": "^12.0.0",
76
- "react": "^19.2.4",
77
- "react-dom": "^19.2.4",
78
- "typescript": "^5.9.3"
79
- }
80
- }
1
+ {
2
+ "name": "jaml-ui",
3
+ "version": "0.5.1",
4
+ "description": "Balatro rendering components, sprite metadata, and optional Motely helpers for React apps.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/core.d.ts",
15
+ "import": "./dist/core.js"
16
+ },
17
+ "./motely": {
18
+ "types": "./dist/motely.d.ts",
19
+ "import": "./dist/motely.js"
20
+ },
21
+ "./ui": {
22
+ "types": "./dist/ui.d.ts",
23
+ "import": "./dist/ui.js"
24
+ },
25
+ "./r3f": {
26
+ "types": "./dist/r3f.d.ts",
27
+ "import": "./dist/r3f.js"
28
+ },
29
+ "./assets/*": "./assets/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "sideEffects": false,
33
+ "files": [
34
+ "dist",
35
+ "assets",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc --pretty false",
41
+ "dev": "tsc --watch",
42
+ "typecheck": "tsc --noEmit --pretty false",
43
+ "prepack": "npm run build"
44
+ },
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/OptimusPi/jaml-ui"
54
+ },
55
+ "homepage": "https://github.com/OptimusPi/jaml-ui#readme",
56
+ "bugs": {
57
+ "url": "https://github.com/OptimusPi/jaml-ui/issues"
58
+ },
59
+ "keywords": [
60
+ "balatro",
61
+ "jaml",
62
+ "motely",
63
+ "seed",
64
+ "card",
65
+ "sprite",
66
+ "ui"
67
+ ],
68
+ "author": "pifreak",
69
+ "license": "MIT",
70
+ "peerDependencies": {
71
+ "@react-spring/three": ">=9.0.0",
72
+ "@react-three/fiber": ">=8.0.0",
73
+ "motely-wasm": "^10.2.0 || ^11.0.0 || ^12.0.0",
74
+ "react": "^18.2.0 || ^19.0.0",
75
+ "react-dom": "^18.2.0 || ^19.0.0",
76
+ "react-icons": ">=5.0.0",
77
+ "three": ">=0.150.0"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@react-spring/three": {
81
+ "optional": true
82
+ },
83
+ "@react-three/fiber": {
84
+ "optional": true
85
+ },
86
+ "motely-wasm": {
87
+ "optional": true
88
+ },
89
+ "react-icons": {
90
+ "optional": true
91
+ },
92
+ "three": {
93
+ "optional": true
94
+ }
95
+ },
96
+ "devDependencies": {
97
+ "@react-spring/three": "^10.0.3",
98
+ "@react-three/fiber": "^9.6.0",
99
+ "@types/react": "^19.2.14",
100
+ "@types/react-dom": "^19.2.3",
101
+ "@types/three": "^0.184.0",
102
+ "motely-wasm": "^12.0.0",
103
+ "react": "^19.2.4",
104
+ "react-dom": "^19.2.4",
105
+ "react-icons": "^5.6.0",
106
+ "three": "^0.184.0",
107
+ "typescript": "^5.9.3"
108
+ }
109
+ }