jaml-ui 0.8.0 → 0.10.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.
Files changed (63) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +135 -135
  3. package/dist/assets.js +1 -1
  4. package/dist/components/CardFan.d.ts +22 -0
  5. package/dist/components/CardFan.js +80 -0
  6. package/dist/components/DeckSprite.d.ts +26 -0
  7. package/dist/components/DeckSprite.js +73 -0
  8. package/dist/components/JamlCodeEditor.d.ts +7 -0
  9. package/dist/components/JamlCodeEditor.js +58 -0
  10. package/dist/components/JamlIde.js +2 -14
  11. package/dist/components/JamlIdeVisual.js +4 -4
  12. package/dist/components/MotelyVersionBadge.d.ts +30 -0
  13. package/dist/components/MotelyVersionBadge.js +31 -0
  14. package/dist/hooks/searchWorkerCode.js +59 -59
  15. package/dist/index.d.ts +11 -0
  16. package/dist/index.js +11 -0
  17. package/dist/ui/footer.js +5 -5
  18. package/dist/ui/jimboBackground.d.ts +14 -0
  19. package/dist/ui/jimboBackground.js +155 -0
  20. package/dist/ui/jimboFilterBar.d.ts +25 -0
  21. package/dist/ui/jimboFilterBar.js +77 -0
  22. package/dist/ui/jimboFlankNav.d.ts +18 -0
  23. package/dist/ui/jimboFlankNav.js +43 -0
  24. package/dist/ui/jimboTabs.d.ts +25 -0
  25. package/dist/ui/jimboTabs.js +59 -0
  26. package/dist/ui/jimboText.d.ts +26 -0
  27. package/dist/ui/jimboText.js +45 -0
  28. package/dist/ui/jimboTooltip.d.ts +35 -0
  29. package/dist/ui/jimboTooltip.js +109 -0
  30. package/dist/ui/panel.js +4 -6
  31. package/dist/ui/tokens.d.ts +2 -1
  32. package/dist/ui/tokens.js +2 -0
  33. package/dist/ui.d.ts +6 -0
  34. package/dist/ui.js +6 -0
  35. package/fonts.css +5 -0
  36. package/package.json +14 -3
  37. package/dist/data/balatro-jokers.json +0 -1241
  38. package/dist/r3f/BalatroJokerMesh3D.d.ts +0 -8
  39. package/dist/r3f/BalatroJokerMesh3D.js +0 -98
  40. package/dist/r3f/BalatroJokerPreview3D.d.ts +0 -14
  41. package/dist/r3f/BalatroJokerPreview3D.js +0 -30
  42. package/dist/r3f/BalatroPlayingCard3D.d.ts +0 -22
  43. package/dist/r3f/BalatroPlayingCard3D.js +0 -62
  44. package/dist/r3f/cardConstants.d.ts +0 -16
  45. package/dist/r3f/cardConstants.js +0 -14
  46. package/dist/r3f/compositedAtlas.d.ts +0 -5
  47. package/dist/r3f/compositedAtlas.js +0 -56
  48. package/dist/r3f/gridUV.d.ts +0 -22
  49. package/dist/r3f/gridUV.js +0 -30
  50. package/dist/r3f/index.d.ts +0 -12
  51. package/dist/r3f/index.js +0 -13
  52. package/dist/r3f/jokerRegistry.d.ts +0 -28
  53. package/dist/r3f/jokerRegistry.js +0 -40
  54. package/dist/r3f/jokerTilt.d.ts +0 -8
  55. package/dist/r3f/jokerTilt.js +0 -41
  56. package/dist/r3f/magneticTilt.d.ts +0 -18
  57. package/dist/r3f/magneticTilt.js +0 -34
  58. package/dist/r3f/playingCardTypes.d.ts +0 -24
  59. package/dist/r3f/playingCardTypes.js +0 -32
  60. package/dist/r3f/playingCardVisuals.d.ts +0 -7
  61. package/dist/r3f/playingCardVisuals.js +0 -45
  62. package/dist/r3f/usePlayingCardTexture.d.ts +0 -7
  63. package/dist/r3f/usePlayingCardTexture.js +0 -92
@@ -35,7 +35,7 @@ function DragClausePill({ clause, zone, onDragStart, }) {
35
35
  borderRadius: 6, padding: "5px 8px 5px 4px",
36
36
  boxShadow: `0 2px 0 ${JimboColorOption.BLACK}`,
37
37
  cursor: "grab", userSelect: "none", touchAction: "none",
38
- }, children: [_jsx("div", { style: { color: JimboColorOption.GREY, fontSize: 12, lineHeight: 1, padding: "0 2px" }, children: "\u22EE\u22EE" }), _jsx(ClauseSprite, { clause: clause, size: 26 }), _jsx("div", { style: { fontSize: 10, color: JimboColorOption.WHITE, letterSpacing: 1, textShadow: "1px 1px 0 rgba(0,0,0,.8)" }, children: clause.label || clause.value }), clause.antes && clause.antes.length > 0 && (_jsxs("div", { style: { display: "flex", gap: 2 }, children: [clause.antes.slice(0, 3).map((a) => (_jsx("div", { style: { fontSize: 8, padding: "0 3px", background: JimboColorOption.DARKEST, color: z.color, borderRadius: 2 }, children: a }, a))), clause.antes.length > 3 && _jsxs("div", { style: { fontSize: 8, color: JimboColorOption.GREY }, children: ["+", clause.antes.length - 3] })] })), clause.score != null && (_jsxs("div", { style: { fontSize: 9, padding: "0 4px", background: JimboColorOption.RED, color: JimboColorOption.WHITE, borderRadius: 2 }, children: ["+", clause.score] }))] }));
38
+ }, children: [_jsx("div", { style: { color: JimboColorOption.GREY, fontSize: 12, lineHeight: 1, padding: "0 2px" }, children: "\u22EE\u22EE" }), _jsx(ClauseSprite, { clause: clause, size: 26 }), _jsx("div", { style: { fontSize: 10, color: JimboColorOption.WHITE, letterSpacing: 1, textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc` }, children: clause.label || clause.value }), clause.antes && clause.antes.length > 0 && (_jsxs("div", { style: { display: "flex", gap: 2 }, children: [clause.antes.slice(0, 3).map((a) => (_jsx("div", { style: { fontSize: 8, padding: "0 3px", background: JimboColorOption.DARKEST, color: z.color, borderRadius: 2 }, children: a }, a))), clause.antes.length > 3 && _jsxs("div", { style: { fontSize: 8, color: JimboColorOption.GREY }, children: ["+", clause.antes.length - 3] })] })), clause.score != null && (_jsxs("div", { style: { fontSize: 9, padding: "0 4px", background: JimboColorOption.RED, color: JimboColorOption.WHITE, borderRadius: 2 }, children: ["+", clause.score] }))] }));
39
39
  }
40
40
  function ZoneDropRail({ zone, clauses, onDragStart, highlight, }) {
41
41
  const z = ZONE_META[zone];
@@ -47,7 +47,7 @@ function ZoneDropRail({ zone, clauses, onDragStart, highlight, }) {
47
47
  }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [_jsx("div", { style: {
48
48
  fontSize: 10, letterSpacing: 2, padding: "2px 8px",
49
49
  background: z.color, color: JimboColorOption.WHITE, borderRadius: 3,
50
- textShadow: "1px 1px 0 rgba(0,0,0,.8)",
50
+ textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc`,
51
51
  }, children: z.label }), _jsx("div", { style: { flex: 1, height: 1, background: `${z.color}44` } }), _jsx("div", { style: { fontSize: 8, color: JimboColorOption.GREY }, children: clauses.length })] }), _jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [clauses.map((c) => (_jsx(DragClausePill, { clause: c, zone: zone, onDragStart: onDragStart }, c.id))), clauses.length === 0 && (_jsx("div", { style: { fontSize: 10, color: JimboColorOption.GREY, padding: 10, fontStyle: "italic" }, children: "drop clauses here" }))] })] }));
52
52
  }
53
53
  export function JamlIdeVisual({ filter, onChange, onSave, onBack }) {
@@ -103,11 +103,11 @@ export function JamlIdeVisual({ filter, onChange, onSave, onBack }) {
103
103
  return (_jsxs("div", { ref: rootRef, style: { display: "flex", flexDirection: "column", gap: 10, padding: 10 }, children: [_jsxs("div", { style: {
104
104
  background: JimboColorOption.DARK_GREY, border: `2px solid ${JimboColorOption.PANEL_EDGE}`,
105
105
  borderRadius: 6, padding: 8, boxShadow: `0 2px 0 ${JimboColorOption.BLACK}`,
106
- }, children: [_jsx("div", { style: { fontSize: 9, color: JimboColorOption.GREY, letterSpacing: 2 }, children: "FILE" }), _jsxs("div", { style: { fontSize: 14, color: JimboColorOption.WHITE, textShadow: "1px 1px 0 rgba(0,0,0,.8)" }, children: [filter.name || "Untitled", ".jaml"] }), filter.author && (_jsxs("div", { style: { fontSize: 9, color: JimboColorOption.GOLD_TEXT, marginTop: 2 }, children: ["by ", filter.author] }))] }), _jsx("div", { style: { fontSize: 9, color: JimboColorOption.GREY, letterSpacing: 1, textAlign: "center" }, children: "\u22EE\u22EE drag clauses between zones \u00B7 tap to edit" }), _jsx(ZoneDropRail, { zone: "must", clauses: filter.must, onDragStart: onDragStart, highlight: hoverZone === "must" }), _jsx(ZoneDropRail, { zone: "should", clauses: filter.should, onDragStart: onDragStart, highlight: hoverZone === "should" }), _jsx(ZoneDropRail, { zone: "mustnot", clauses: filter.mustnot, onDragStart: onDragStart, highlight: hoverZone === "mustnot" }), drag && (_jsx("div", { style: {
106
+ }, children: [_jsx("div", { style: { fontSize: 9, color: JimboColorOption.GREY, letterSpacing: 2 }, children: "FILE" }), _jsxs("div", { style: { fontSize: 14, color: JimboColorOption.WHITE, textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc` }, children: [filter.name || "Untitled", ".jaml"] }), filter.author && (_jsxs("div", { style: { fontSize: 9, color: JimboColorOption.GOLD_TEXT, marginTop: 2 }, children: ["by ", filter.author] }))] }), _jsx(ZoneDropRail, { zone: "must", clauses: filter.must, onDragStart: onDragStart, highlight: hoverZone === "must" }), _jsx(ZoneDropRail, { zone: "should", clauses: filter.should, onDragStart: onDragStart, highlight: hoverZone === "should" }), _jsx(ZoneDropRail, { zone: "mustnot", clauses: filter.mustnot, onDragStart: onDragStart, highlight: hoverZone === "mustnot" }), drag && (_jsx("div", { style: {
107
107
  position: "fixed",
108
108
  left: drag.x - drag.offX, top: drag.y - drag.offY,
109
109
  pointerEvents: "none", zIndex: 999,
110
110
  transform: "rotate(-2deg) scale(1.05)",
111
- filter: "drop-shadow(0 4px 6px rgba(0,0,0,.6))", opacity: 0.92,
111
+ filter: `drop-shadow(0 4px 6px ${JimboColorOption.BLACK}99)`, opacity: 0.92,
112
112
  }, children: _jsx(DragClausePill, { clause: drag.clause, zone: drag.fromZone, onDragStart: () => { } }) }))] }));
113
113
  }
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ export interface MotelyCapabilities {
3
+ version: string;
4
+ simd?: boolean;
5
+ threads?: boolean;
6
+ }
7
+ export interface MotelyVersionBadgeProps {
8
+ /**
9
+ * Runtime capabilities from `motely.getCapabilities()`. If omitted, the
10
+ * consumer can pass a static `version` (typically from motely-wasm's
11
+ * own package.json) and the component will render without the SIMD /
12
+ * threads indicators.
13
+ */
14
+ caps?: MotelyCapabilities | null;
15
+ /** Static fallback version when `caps` is null/undefined. */
16
+ version?: string;
17
+ /** Compact single-line badge instead of the labelled chip. Default false. */
18
+ minimal?: boolean;
19
+ /** Loading placeholder (shown while caps are being fetched). */
20
+ loading?: boolean;
21
+ className?: string;
22
+ style?: React.CSSProperties;
23
+ }
24
+ /**
25
+ * Badge showing the loaded motely-wasm version + optional SIMD / threads
26
+ * capability indicators. Ported from weejoker.app with no dependency on
27
+ * weejoker's lib/api — the consumer owns capability fetching and passes
28
+ * the result in.
29
+ */
30
+ export declare function MotelyVersionBadge({ caps, version, minimal, loading, className, style, }: MotelyVersionBadgeProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { JimboColorOption } from '../ui/tokens.js';
4
+ import { JimboText } from '../ui/jimboText.js';
5
+ /**
6
+ * Badge showing the loaded motely-wasm version + optional SIMD / threads
7
+ * capability indicators. Ported from weejoker.app with no dependency on
8
+ * weejoker's lib/api — the consumer owns capability fetching and passes
9
+ * the result in.
10
+ */
11
+ export function MotelyVersionBadge({ caps, version, minimal = false, loading = false, className = '', style, }) {
12
+ if (loading) {
13
+ return (_jsx("span", { className: className, style: style, children: _jsx(JimboText, { size: "xs", tone: "grey", children: "Initializing\u2026" }) }));
14
+ }
15
+ const resolved = caps?.version ?? version ?? '?';
16
+ const simd = caps?.simd;
17
+ const threads = caps?.threads;
18
+ if (minimal) {
19
+ return (_jsxs("span", { className: className, style: { display: 'inline-flex', alignItems: 'center', gap: 6, ...style }, children: [_jsxs(JimboText, { size: "xs", tone: "grey", children: ["v", resolved] }), simd ? (_jsx(JimboText, { size: "xs", tone: "blue", title: "SIMD enabled", children: "\u26A1" })) : null, threads ? (_jsx(JimboText, { size: "xs", tone: "green", title: "Multi-threaded", children: "\uD83E\uDDF5" })) : null] }));
20
+ }
21
+ return (_jsxs("div", { className: className, style: {
22
+ display: 'inline-flex',
23
+ alignItems: 'center',
24
+ gap: 6,
25
+ padding: '3px 8px',
26
+ borderRadius: 4,
27
+ background: JimboColorOption.DARKEST,
28
+ border: `1px solid ${JimboColorOption.PANEL_EDGE}`,
29
+ ...style,
30
+ }, children: [_jsxs(JimboText, { size: "xs", tone: "gold", uppercase: true, children: ["motely v", resolved] }), simd ? _jsx(JimboText, { size: "xs", tone: "blue", title: "SIMD enabled", children: "\u26A1" }) : null, threads ? _jsx(JimboText, { size: "xs", tone: "green", title: "Multi-threaded", children: "\uD83E\uDDF5" }) : null] }));
31
+ }
@@ -1,62 +1,62 @@
1
1
  // Worker code as an inline string — created as a Blob URL at runtime.
2
2
  // This avoids bundler/import.meta.url issues when shipped as an npm package.
3
- export const SEARCH_WORKER_CODE = `
4
- let MotelyWasm = null;
5
- let MotelyWasmEvents = null;
6
- let activeSearch = null;
7
-
8
- self.addEventListener('message', async function(e) {
9
- const msg = e.data;
10
-
11
- if (msg.type === 'init') {
12
- try {
13
- const mod = await import(msg.url);
14
- await mod.default.boot();
15
- MotelyWasm = mod.MotelyWasm;
16
- MotelyWasmEvents = mod.MotelyWasmEvents;
17
- self.postMessage({ type: 'ready' });
18
- } catch (err) {
19
- self.postMessage({ type: 'error', message: String(err) });
20
- }
21
- return;
22
- }
23
-
24
- if (msg.type === 'start') {
25
- if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
26
- const validation = MotelyWasm.validateJaml(msg.jaml);
27
- if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
28
-
29
- let rId, pId, cId;
30
- function cleanup() {
31
- MotelyWasmEvents.onResult.unsubscribeById(rId);
32
- MotelyWasmEvents.onProgress.unsubscribeById(pId);
33
- MotelyWasmEvents.onComplete.unsubscribeById(cId);
34
- activeSearch = null;
35
- }
36
-
37
- rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {
38
- self.postMessage({ type: 'result', seed, score });
39
- });
40
- pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {
41
- self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
42
- });
43
- cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {
44
- cleanup();
45
- self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
46
- });
47
-
48
- try {
49
- activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
50
- } catch (err) {
51
- cleanup();
52
- self.postMessage({ type: 'error', message: String(err) });
53
- }
54
- return;
55
- }
56
-
57
- if (msg.type === 'stop') {
58
- if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
59
- self.postMessage({ type: 'cancelled' });
60
- }
61
- });
3
+ export const SEARCH_WORKER_CODE = `
4
+ let MotelyWasm = null;
5
+ let MotelyWasmEvents = null;
6
+ let activeSearch = null;
7
+
8
+ self.addEventListener('message', async function(e) {
9
+ const msg = e.data;
10
+
11
+ if (msg.type === 'init') {
12
+ try {
13
+ const mod = await import(msg.url);
14
+ await mod.default.boot();
15
+ MotelyWasm = mod.MotelyWasm;
16
+ MotelyWasmEvents = mod.MotelyWasmEvents;
17
+ self.postMessage({ type: 'ready' });
18
+ } catch (err) {
19
+ self.postMessage({ type: 'error', message: String(err) });
20
+ }
21
+ return;
22
+ }
23
+
24
+ if (msg.type === 'start') {
25
+ if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
26
+ const validation = MotelyWasm.validateJaml(msg.jaml);
27
+ if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
28
+
29
+ let rId, pId, cId;
30
+ function cleanup() {
31
+ MotelyWasmEvents.onResult.unsubscribeById(rId);
32
+ MotelyWasmEvents.onProgress.unsubscribeById(pId);
33
+ MotelyWasmEvents.onComplete.unsubscribeById(cId);
34
+ activeSearch = null;
35
+ }
36
+
37
+ rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {
38
+ self.postMessage({ type: 'result', seed, score });
39
+ });
40
+ pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {
41
+ self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
42
+ });
43
+ cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {
44
+ cleanup();
45
+ self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
46
+ });
47
+
48
+ try {
49
+ activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
50
+ } catch (err) {
51
+ cleanup();
52
+ self.postMessage({ type: 'error', message: String(err) });
53
+ }
54
+ return;
55
+ }
56
+
57
+ if (msg.type === 'stop') {
58
+ if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
59
+ self.postMessage({ type: 'cancelled' });
60
+ }
61
+ });
62
62
  `;
package/dist/index.d.ts CHANGED
@@ -6,7 +6,18 @@ export { AnalyzerExplorer, type AnalyzerAnteView, type AnalyzerBadge, type Analy
6
6
  export { JamlMapPreview, type JamlMapPreviewProps } from "./components/JamlMapPreview.js";
7
7
  export { JamlIde, type JamlIdeProps, type JamlIdeSearchResult, type JamlVisualFilter, type JamlVisualClause, type JamlZone, } from "./components/JamlIde.js";
8
8
  export { JamlIdeVisual, type JamlIdeVisualProps, } from "./components/JamlIdeVisual.js";
9
+ export { JamlCodeEditor, type JamlCodeEditorProps, } from "./components/JamlCodeEditor.js";
10
+ export { JimboText, type JimboTextProps, type JimboTextTone, type JimboTextSize, } from "./ui/jimboText.js";
11
+ export { JimboTabs, JimboVerticalTabs, type JimboTabItem, type JimboTabsProps, } from "./ui/jimboTabs.js";
12
+ export { JimboFlankNav, type JimboFlankNavProps, } from "./ui/jimboFlankNav.js";
13
+ export { JimboFilterBar, type JimboFilterBarProps, type JimboFilterSortOption, } from "./ui/jimboFilterBar.js";
14
+ export { JimboBackground } from "./ui/jimboBackground.js";
15
+ export { JimboTooltip, type JimboTooltipProps, type JimboTooltipMode, type JimboTooltipPlacement, } from "./ui/jimboTooltip.js";
9
16
  export { JamlIdeToolbar, type JamlIdeMode, type JamlIdeToolbarProps, } from "./components/JamlIdeToolbar.js";
10
17
  export { CardList, type CardListProps } from "./components/CardList.js";
18
+ export { CardFan, type CardFanProps } from "./components/CardFan.js";
19
+ export { DeckSprite, DECK_SPRITE_POS, STAKE_SPRITE_POS, type DeckSpriteProps, } from "./components/DeckSprite.js";
20
+ export { MotelyVersionBadge, type MotelyVersionBadgeProps, type MotelyCapabilities, } from "./components/MotelyVersionBadge.js";
11
21
  export { extractVisualJamlItems, type JamlPreviewGroups, type JamlPreviewItem, type JamlPreviewSection, type JamlPreviewVisualType, } from "./utils/jamlMapPreview.js";
12
22
  export { useMotelyStream, type StreamItem, type StreamState } from "./hooks/useShopStream.js";
23
+ export { useSearch, type SearchResult, type SearchStatus, type UseSearchState, } from "./hooks/useSearch.js";
package/dist/index.js CHANGED
@@ -7,7 +7,18 @@ export { AnalyzerExplorer, } from "./components/AnalyzerExplorer.js";
7
7
  export { JamlMapPreview } from "./components/JamlMapPreview.js";
8
8
  export { JamlIde, } from "./components/JamlIde.js";
9
9
  export { JamlIdeVisual, } from "./components/JamlIdeVisual.js";
10
+ export { JamlCodeEditor, } from "./components/JamlCodeEditor.js";
11
+ export { JimboText, } from "./ui/jimboText.js";
12
+ export { JimboTabs, JimboVerticalTabs, } from "./ui/jimboTabs.js";
13
+ export { JimboFlankNav, } from "./ui/jimboFlankNav.js";
14
+ export { JimboFilterBar, } from "./ui/jimboFilterBar.js";
15
+ export { JimboBackground } from "./ui/jimboBackground.js";
16
+ export { JimboTooltip, } from "./ui/jimboTooltip.js";
10
17
  export { JamlIdeToolbar, } from "./components/JamlIdeToolbar.js";
11
18
  export { CardList } from "./components/CardList.js";
19
+ export { CardFan } from "./components/CardFan.js";
20
+ export { DeckSprite, DECK_SPRITE_POS, STAKE_SPRITE_POS, } from "./components/DeckSprite.js";
21
+ export { MotelyVersionBadge, } from "./components/MotelyVersionBadge.js";
12
22
  export { extractVisualJamlItems, } from "./utils/jamlMapPreview.js";
13
23
  export { useMotelyStream } from "./hooks/useShopStream.js";
24
+ export { useSearch, } from "./hooks/useSearch.js";
package/dist/ui/footer.js CHANGED
@@ -9,10 +9,10 @@ const SUITS = [
9
9
  ];
10
10
  const CYCLE = '5s';
11
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} }
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
17
  ` })] }));
18
18
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Fullscreen WebGL CRT/spin background — the authentic Balatro hypnotic
3
+ * swirl, pixelated and animated. Ported from weejoker.app's
4
+ * BackgroundShader.tsx; no config required.
5
+ *
6
+ * Renders a fixed-position canvas at z-index: -10 that fills the viewport
7
+ * and ignores pointer events. Drop it once at the root of your page:
8
+ *
9
+ * <JimboBackground />
10
+ * <YourAppContent />
11
+ *
12
+ * Resizes automatically. Disposes the animation frame + shader on unmount.
13
+ */
14
+ export declare function JimboBackground(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,155 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from 'react';
4
+ /**
5
+ * Fullscreen WebGL CRT/spin background — the authentic Balatro hypnotic
6
+ * swirl, pixelated and animated. Ported from weejoker.app's
7
+ * BackgroundShader.tsx; no config required.
8
+ *
9
+ * Renders a fixed-position canvas at z-index: -10 that fills the viewport
10
+ * and ignores pointer events. Drop it once at the root of your page:
11
+ *
12
+ * <JimboBackground />
13
+ * <YourAppContent />
14
+ *
15
+ * Resizes automatically. Disposes the animation frame + shader on unmount.
16
+ */
17
+ export function JimboBackground() {
18
+ const canvasRef = useRef(null);
19
+ useEffect(() => {
20
+ const canvas = canvasRef.current;
21
+ if (!canvas)
22
+ return;
23
+ const gl = canvas.getContext('webgl');
24
+ if (!gl)
25
+ return;
26
+ const vsSource = `
27
+ attribute vec2 position;
28
+ void main() {
29
+ gl_Position = vec4(position, 0.0, 1.0);
30
+ }
31
+ `;
32
+ const fsSource = `
33
+ precision mediump float;
34
+
35
+ uniform float u_time;
36
+ uniform vec2 u_resolution;
37
+
38
+ const float SPIN_ROTATION = -2.0;
39
+ const float SPIN_SPEED = 4.5;
40
+ const vec4 COLOUR_1 = vec4(1.0, 0.2, 0.2, 1.0);
41
+ const vec4 COLOUR_2 = vec4(0.0, 0.5, 1.0, 1.0);
42
+ const vec4 COLOUR_3 = vec4(0.05, 0.08, 0.1, 1.0);
43
+ const float CONTRAST = 4.5;
44
+ const float LIGTHING = 0.5;
45
+ const float SPIN_AMOUNT = 0.35;
46
+ const float PIXEL_FILTER = 1024.0;
47
+ const float PI = 3.14159265359;
48
+
49
+ void main() {
50
+ vec2 screenSize = u_resolution;
51
+ float pixel_size = length(screenSize.xy) / PIXEL_FILTER;
52
+ vec2 uv = (floor(gl_FragCoord.xy*(1.0/pixel_size))*pixel_size - 0.5*screenSize.xy)/length(screenSize.xy);
53
+ float uv_len = length(uv);
54
+
55
+ float speed = (SPIN_ROTATION * 0.2) + 302.2;
56
+ float new_pixel_angle = atan(uv.y, uv.x) + speed - 20.0*(1.0*SPIN_AMOUNT*uv_len + (1.0 - 1.0*SPIN_AMOUNT));
57
+
58
+ vec2 mid = (screenSize.xy/length(screenSize.xy))/2.0;
59
+ uv = (vec2((uv_len * cos(new_pixel_angle) + mid.x), (uv_len * sin(new_pixel_angle) + mid.y)) - mid);
60
+
61
+ uv *= 30.0;
62
+ speed = u_time * SPIN_SPEED;
63
+ vec2 uv2 = vec2(uv.x, uv.y);
64
+
65
+ for(int i=0; i < 5; i++) {
66
+ uv2 += sin(max(uv.x, uv.y)) + uv;
67
+ uv += 0.5*vec2(cos(5.1123314 + 0.353*uv2.y + speed*0.131121), sin(uv2.x - 0.113*speed));
68
+ uv -= 1.0*cos(uv.x + uv.y) - 1.0*sin(uv.x*0.711 - uv.y);
69
+ }
70
+
71
+ float contrast_mod = (0.25*CONTRAST + 0.5*SPIN_AMOUNT + 1.2);
72
+ float paint_res = min(2.0, max(0.0, length(uv)*(0.035)*contrast_mod));
73
+ float c1p = max(0.0, 1.0 - contrast_mod*abs(1.0 - paint_res));
74
+ float c2p = max(0.0, 1.0 - contrast_mod*abs(paint_res));
75
+ float c3p = 1.0 - min(1.0, c1p + c2p);
76
+ float light = (LIGTHING - 0.2)*max(c1p*5.0 - 4.0, 0.0) + LIGTHING*max(c2p*5.0 - 4.0, 0.0);
77
+
78
+ vec4 finalCol = (0.3/CONTRAST)*COLOUR_1 + (1.0 - 0.3/CONTRAST)*(COLOUR_1*c1p + COLOUR_2*c2p + vec4(c3p*COLOUR_3.rgb, c3p*COLOUR_1.a)) + light;
79
+
80
+ gl_FragColor = finalCol;
81
+ }
82
+ `;
83
+ const createShader = (type, source) => {
84
+ const shader = gl.createShader(type);
85
+ if (!shader)
86
+ return null;
87
+ gl.shaderSource(shader, source);
88
+ gl.compileShader(shader);
89
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
90
+ console.error('[JimboBackground] shader compile error:', gl.getShaderInfoLog(shader));
91
+ gl.deleteShader(shader);
92
+ return null;
93
+ }
94
+ return shader;
95
+ };
96
+ const vertexShader = createShader(gl.VERTEX_SHADER, vsSource);
97
+ const fragmentShader = createShader(gl.FRAGMENT_SHADER, fsSource);
98
+ if (!vertexShader || !fragmentShader)
99
+ return;
100
+ const program = gl.createProgram();
101
+ if (!program)
102
+ return;
103
+ gl.attachShader(program, vertexShader);
104
+ gl.attachShader(program, fragmentShader);
105
+ gl.linkProgram(program);
106
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
107
+ console.error('[JimboBackground] program link error:', gl.getProgramInfoLog(program));
108
+ return;
109
+ }
110
+ gl.useProgram(program);
111
+ const positionBuffer = gl.createBuffer();
112
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
113
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
114
+ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
115
+ -1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
116
+ ]), gl.STATIC_DRAW);
117
+ const positionLocation = gl.getAttribLocation(program, 'position');
118
+ gl.enableVertexAttribArray(positionLocation);
119
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
120
+ const timeLocation = gl.getUniformLocation(program, 'u_time');
121
+ const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
122
+ const startTime = Date.now();
123
+ let animationFrameId;
124
+ const render = () => {
125
+ const displayWidth = canvas.clientWidth;
126
+ const displayHeight = canvas.clientHeight;
127
+ if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
128
+ canvas.width = displayWidth;
129
+ canvas.height = displayHeight;
130
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
131
+ }
132
+ const currentTime = (Date.now() - startTime) / 1000.0;
133
+ gl.uniform1f(timeLocation, currentTime);
134
+ gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
135
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
136
+ animationFrameId = requestAnimationFrame(render);
137
+ };
138
+ render();
139
+ return () => {
140
+ cancelAnimationFrame(animationFrameId);
141
+ gl.deleteProgram(program);
142
+ gl.deleteShader(vertexShader);
143
+ gl.deleteShader(fragmentShader);
144
+ gl.deleteBuffer(positionBuffer);
145
+ };
146
+ }, []);
147
+ return (_jsx("canvas", { ref: canvasRef, "aria-hidden": true, style: {
148
+ position: 'fixed',
149
+ inset: 0,
150
+ width: '100%',
151
+ height: '100%',
152
+ zIndex: -10,
153
+ pointerEvents: 'none',
154
+ } }));
155
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ export interface JimboFilterSortOption {
3
+ value: string;
4
+ label: string;
5
+ }
6
+ export interface JimboFilterBarProps {
7
+ search?: string;
8
+ onSearchChange?: (query: string) => void;
9
+ searchPlaceholder?: string;
10
+ searchLabel?: string;
11
+ sort?: string;
12
+ onSortChange?: (value: string) => void;
13
+ sortLabel?: string;
14
+ sortOptions?: JimboFilterSortOption[];
15
+ className?: string;
16
+ style?: React.CSSProperties;
17
+ }
18
+ /**
19
+ * Generic Balatro-styled filter row: search input with floating pill label
20
+ * + optional sort dropdown with floating pill label. Adapted from
21
+ * weejoker's FilterBar — no hardcoded sort options, no lucide dep.
22
+ *
23
+ * Pass `sortOptions` to show the sort side; omit to show search only.
24
+ */
25
+ export declare function JimboFilterBar({ search, onSearchChange, searchPlaceholder, searchLabel, sort, onSortChange, sortLabel, sortOptions, className, style, }: JimboFilterBarProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { JimboColorOption } from './tokens.js';
4
+ import { JimboText } from './jimboText.js';
5
+ /**
6
+ * Generic Balatro-styled filter row: search input with floating pill label
7
+ * + optional sort dropdown with floating pill label. Adapted from
8
+ * weejoker's FilterBar — no hardcoded sort options, no lucide dep.
9
+ *
10
+ * Pass `sortOptions` to show the sort side; omit to show search only.
11
+ */
12
+ export function JimboFilterBar({ search, onSearchChange, searchPlaceholder = 'SEARCH...', searchLabel = 'Search', sort, onSortChange, sortLabel = 'Sort By', sortOptions, className = '', style, }) {
13
+ return (_jsxs("div", { className: className, style: {
14
+ display: 'flex',
15
+ gap: 24,
16
+ padding: 16,
17
+ backgroundColor: JimboColorOption.DARK_GREY,
18
+ border: `4px solid ${JimboColorOption.BORDER_SILVER}`,
19
+ boxShadow: `0 3px 0 0 ${JimboColorOption.BORDER_SOUTH}`,
20
+ borderRadius: 12,
21
+ position: 'relative',
22
+ flexWrap: 'wrap',
23
+ ...style,
24
+ }, children: [onSearchChange ? (_jsx(FloatingLabelField, { label: searchLabel, children: _jsxs("div", { style: { position: 'relative' }, children: [_jsx("div", { style: { position: 'absolute', left: 0, top: 0, bottom: 0, width: 48, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', color: JimboColorOption.BLUE, zIndex: 1 }, children: _jsx(SearchIcon, {}) }), _jsx("input", { type: "text", value: search ?? '', onChange: (e) => onSearchChange(e.target.value), placeholder: searchPlaceholder, style: {
25
+ width: '100%',
26
+ paddingLeft: 48,
27
+ paddingRight: 16,
28
+ paddingTop: 14,
29
+ paddingBottom: 14,
30
+ backgroundColor: JimboColorOption.DARKEST,
31
+ border: 'none',
32
+ borderBottom: `4px solid ${JimboColorOption.PANEL_EDGE}`,
33
+ borderRadius: 8,
34
+ color: JimboColorOption.WHITE,
35
+ fontFamily: "'m6x11plus', 'Courier New', monospace",
36
+ fontSize: 20,
37
+ letterSpacing: 2,
38
+ textTransform: 'uppercase',
39
+ outline: 'none',
40
+ } })] }) })) : null, sortOptions && onSortChange ? (_jsx(FloatingLabelField, { label: sortLabel, children: _jsxs("div", { style: { position: 'relative' }, children: [_jsx("select", { value: sort ?? sortOptions[0]?.value, onChange: (e) => onSortChange(e.target.value), style: {
41
+ appearance: 'none',
42
+ WebkitAppearance: 'none',
43
+ MozAppearance: 'none',
44
+ backgroundColor: JimboColorOption.ORANGE,
45
+ color: JimboColorOption.WHITE,
46
+ border: 'none',
47
+ borderBottom: `4px solid ${JimboColorOption.DARK_ORANGE}`,
48
+ borderRadius: 8,
49
+ cursor: 'pointer',
50
+ fontFamily: "'m6x11plus', 'Courier New', monospace",
51
+ fontSize: 18,
52
+ letterSpacing: 2,
53
+ textTransform: 'uppercase',
54
+ padding: '14px 48px 14px 24px',
55
+ minWidth: 200,
56
+ textAlign: 'center',
57
+ outline: 'none',
58
+ }, children: sortOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) }), _jsx("div", { style: { position: 'absolute', right: 16, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: JimboColorOption.WHITE, opacity: 0.85 }, children: _jsx(SortIcon, {}) })] }) })) : null] }));
59
+ }
60
+ function FloatingLabelField({ label, children }) {
61
+ return (_jsxs("div", { style: { flex: 1, minWidth: 200, position: 'relative', marginTop: 10 }, children: [_jsx("div", { style: {
62
+ position: 'absolute',
63
+ top: -14,
64
+ left: 16,
65
+ backgroundColor: JimboColorOption.RED,
66
+ border: `2px solid ${JimboColorOption.DARK_RED}`,
67
+ borderRadius: 6,
68
+ padding: '4px 12px',
69
+ zIndex: 2,
70
+ }, children: _jsx(JimboText, { size: "xs", uppercase: true, children: label }) }), children] }));
71
+ }
72
+ function SearchIcon() {
73
+ return (_jsxs("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("circle", { cx: 11, cy: 11, r: 8 }), _jsx("line", { x1: 21, y1: 21, x2: 16.65, y2: 16.65 })] }));
74
+ }
75
+ function SortIcon() {
76
+ return (_jsxs("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("polyline", { points: "7 4 7 20" }), _jsx("polyline", { points: "3 8 7 4 11 8" }), _jsx("polyline", { points: "17 20 17 4" }), _jsx("polyline", { points: "21 16 17 20 13 16" })] }));
77
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ export interface JimboFlankNavProps {
3
+ onPrev: () => void;
4
+ onNext: () => void;
5
+ canPrev?: boolean;
6
+ canNext?: boolean;
7
+ prevLabel?: string;
8
+ nextLabel?: string;
9
+ children: React.ReactNode;
10
+ className?: string;
11
+ style?: React.CSSProperties;
12
+ }
13
+ /**
14
+ * Prev/next navigation with flanking buttons around a central stage.
15
+ * Generic adaptation of weejoker's DayNavigation — no hardcoded "Day"
16
+ * labels, no lucide dep (inline chevron SVGs).
17
+ */
18
+ export declare function JimboFlankNav({ onPrev, onNext, canPrev, canNext, prevLabel, nextLabel, children, className, style, }: JimboFlankNavProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { JimboColorOption, JIMBO_ANIMATIONS } from './tokens.js';
5
+ /**
6
+ * Prev/next navigation with flanking buttons around a central stage.
7
+ * Generic adaptation of weejoker's DayNavigation — no hardcoded "Day"
8
+ * labels, no lucide dep (inline chevron SVGs).
9
+ */
10
+ export function JimboFlankNav({ onPrev, onNext, canPrev = true, canNext = true, prevLabel = 'Previous', nextLabel = 'Next', children, className = '', style, }) {
11
+ return (_jsxs("div", { className: className, style: {
12
+ display: 'flex',
13
+ alignItems: 'stretch',
14
+ justifyContent: 'center',
15
+ gap: 8,
16
+ width: '100%',
17
+ position: 'relative',
18
+ ...style,
19
+ }, children: [_jsx(NavButton, { direction: "left", onClick: onPrev, disabled: !canPrev, "aria-label": prevLabel }), _jsx("div", { style: { position: 'relative', flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column' }, children: children }), _jsx(NavButton, { direction: "right", onClick: onNext, disabled: !canNext, "aria-label": nextLabel })] }));
20
+ }
21
+ function NavButton({ direction, onClick, disabled, 'aria-label': ariaLabel, }) {
22
+ const [pressed, setPressed] = React.useState(false);
23
+ return (_jsx("button", { type: "button", onClick: onClick, disabled: disabled, "aria-label": ariaLabel, title: ariaLabel, onMouseDown: () => !disabled && setPressed(true), onMouseUp: () => setPressed(false), onMouseLeave: () => setPressed(false), onTouchStart: () => !disabled && setPressed(true), onTouchEnd: () => setPressed(false), style: {
24
+ flexShrink: 0,
25
+ width: 48,
26
+ border: 'none',
27
+ borderRadius: 8,
28
+ cursor: disabled ? 'not-allowed' : 'pointer',
29
+ opacity: disabled ? 0.35 : 1,
30
+ backgroundColor: JimboColorOption.RED,
31
+ color: JimboColorOption.WHITE,
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ transform: pressed ? `translateY(${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px)` : 'translateY(0)',
36
+ boxShadow: pressed ? 'none' : `0 ${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px 0 0 ${JimboColorOption.DARK_RED}`,
37
+ transition: `transform ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease, box-shadow ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease`,
38
+ }, children: _jsx(ChevronSvg, { direction: direction }) }));
39
+ }
40
+ function ChevronSvg({ direction }) {
41
+ const points = direction === 'left' ? '20,4 8,16 20,28' : '12,4 24,16 12,28';
42
+ return (_jsx("svg", { width: 32, height: 32, viewBox: "0 0 32 32", fill: "none", stroke: "currentColor", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: _jsx("polyline", { points: points }) }));
43
+ }