jaml-ui 0.21.2 → 0.21.4

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 (119) hide show
  1. package/DESIGN.md +36 -6
  2. package/dist/components/JamlAnalyzerFullscreen.d.ts +1 -1
  3. package/dist/components/JamlAnalyzerFullscreen.js +5 -81
  4. package/dist/components/JamlCurator.js +1 -1
  5. package/dist/components/JamlSpeedometer.d.ts +7 -2
  6. package/dist/components/JamlSpeedometer.js +8 -15
  7. package/dist/components/jamlMap/JamlMapEditor.js +42 -38
  8. package/dist/components/jamlMap/JokerPicker.js +2 -2
  9. package/dist/components/jamlMap/MysterySlot.js +4 -4
  10. package/dist/hooks/useSearch.d.ts +2 -1
  11. package/dist/hooks/useSearch.js +111 -8
  12. package/dist/lib/SpriteMapper.d.ts +10 -0
  13. package/dist/lib/SpriteMapper.js +48 -0
  14. package/dist/lib/cardParser.d.ts +8 -0
  15. package/dist/lib/cardParser.js +65 -0
  16. package/dist/lib/classes/BuyMetaData.d.ts +11 -0
  17. package/dist/lib/classes/BuyMetaData.js +1 -0
  18. package/dist/lib/config.d.ts +13 -0
  19. package/dist/lib/config.js +15 -0
  20. package/dist/lib/const.d.ts +61 -0
  21. package/dist/lib/const.js +521 -0
  22. package/dist/lib/data/constants.d.ts +11 -0
  23. package/dist/lib/data/constants.js +17 -0
  24. package/dist/lib/hooks/useDragScroll.d.ts +4 -0
  25. package/dist/lib/hooks/useDragScroll.js +48 -0
  26. package/dist/lib/hooks/useJamlFilter.d.ts +48 -0
  27. package/dist/lib/hooks/useJamlFilter.js +219 -0
  28. package/dist/lib/hooks/useSeedAnalyzer.d.ts +6 -0
  29. package/dist/lib/hooks/useSeedAnalyzer.js +48 -0
  30. package/dist/lib/jaml/jamlCompletion.d.ts +12 -0
  31. package/dist/lib/jaml/jamlCompletion.js +13 -0
  32. package/dist/lib/jaml/jamlData.d.ts +3 -0
  33. package/dist/lib/jaml/jamlData.js +8 -0
  34. package/dist/lib/jaml/jamlObjectives.d.ts +13 -0
  35. package/dist/lib/jaml/jamlObjectives.js +97 -0
  36. package/dist/lib/jaml/jamlParser.d.ts +14 -0
  37. package/dist/lib/jaml/jamlParser.js +47 -0
  38. package/dist/lib/jaml/jamlPresets.d.ts +8 -0
  39. package/dist/lib/jaml/jamlPresets.js +61 -0
  40. package/dist/lib/jaml/jamlSchema.d.ts +54 -0
  41. package/dist/lib/jaml/jamlSchema.js +91 -0
  42. package/dist/lib/parseDailyRitual.d.ts +45 -0
  43. package/dist/lib/parseDailyRitual.js +69 -0
  44. package/dist/lib/tts/getRevealPos.d.ts +5 -0
  45. package/dist/lib/tts/getRevealPos.js +16 -0
  46. package/dist/lib/tts/splitTtsDisplay.d.ts +19 -0
  47. package/dist/lib/tts/splitTtsDisplay.js +35 -0
  48. package/dist/lib/types.d.ts +121 -0
  49. package/dist/lib/types.js +1 -0
  50. package/dist/lib/utils.d.ts +2 -0
  51. package/dist/lib/utils.js +5 -0
  52. package/dist/ui/JimboIconButton.d.ts +10 -0
  53. package/dist/ui/JimboIconButton.js +28 -0
  54. package/dist/ui/JimboInputModal.d.ts +13 -0
  55. package/dist/ui/JimboInputModal.js +60 -0
  56. package/dist/ui/JimboSelect.d.ts +18 -0
  57. package/dist/ui/JimboSelect.js +43 -0
  58. package/dist/ui/PanelSplitter.d.ts +7 -0
  59. package/dist/ui/PanelSplitter.js +76 -0
  60. package/dist/ui/ide/AgnosticSeedCard.d.ts +19 -0
  61. package/dist/ui/ide/AgnosticSeedCard.js +48 -0
  62. package/dist/ui/ide/DeckSprite.d.ts +1 -0
  63. package/dist/ui/ide/DeckSprite.js +2 -0
  64. package/dist/ui/ide/JamlBuilder.d.ts +1 -0
  65. package/dist/ui/ide/JamlBuilder.js +112 -0
  66. package/dist/ui/ide/JamlEditor.d.ts +7 -0
  67. package/dist/ui/ide/JamlEditor.js +496 -0
  68. package/dist/ui/ide/JamlEditorMonaco.d.ts +8 -0
  69. package/dist/ui/ide/JamlEditorMonaco.js +78 -0
  70. package/dist/ui/ide/WasmStatus.d.ts +1 -0
  71. package/dist/ui/ide/WasmStatus.js +42 -0
  72. package/dist/ui/jimbo.css +336 -31
  73. package/dist/ui/jimboApp.d.ts +12 -0
  74. package/dist/ui/jimboApp.js +15 -0
  75. package/dist/ui/jimboInfoCard.d.ts +31 -0
  76. package/dist/ui/jimboInfoCard.js +26 -0
  77. package/dist/ui/jimboInset.d.ts +9 -0
  78. package/dist/ui/jimboInset.js +9 -0
  79. package/dist/ui/jimboSectionHeader.d.ts +11 -0
  80. package/dist/ui/jimboSectionHeader.js +9 -0
  81. package/dist/ui/jimboStatGrid.d.ts +13 -0
  82. package/dist/ui/jimboStatGrid.js +9 -0
  83. package/dist/ui/jimboWordmark.d.ts +10 -0
  84. package/dist/ui/jimboWordmark.js +9 -0
  85. package/dist/ui/mascot/JammySpeechBox.d.ts +9 -0
  86. package/dist/ui/mascot/JammySpeechBox.js +30 -0
  87. package/dist/ui/mascot/SeedMascot.d.ts +37 -0
  88. package/dist/ui/mascot/SeedMascot.js +17 -0
  89. package/dist/ui/mascot/index.d.ts +3 -0
  90. package/dist/ui/mascot/index.js +3 -0
  91. package/dist/ui/mascot/menuConfig.d.ts +102 -0
  92. package/dist/ui/mascot/menuConfig.js +12 -0
  93. package/dist/ui/panel.d.ts +1 -1
  94. package/dist/ui/panel.js +3 -21
  95. package/dist/ui/radial/RadialBadge.d.ts +17 -0
  96. package/dist/ui/radial/RadialBadge.js +43 -0
  97. package/dist/ui/radial/RadialBreadcrumb.d.ts +12 -0
  98. package/dist/ui/radial/RadialBreadcrumb.js +18 -0
  99. package/dist/ui/radial/RadialButton.d.ts +61 -0
  100. package/dist/ui/radial/RadialButton.js +102 -0
  101. package/dist/ui/radial/RadialMenu.d.ts +38 -0
  102. package/dist/ui/radial/RadialMenu.js +168 -0
  103. package/dist/ui/radial/RadialPill.d.ts +18 -0
  104. package/dist/ui/radial/RadialPill.js +15 -0
  105. package/dist/ui/radial/index.d.ts +16 -0
  106. package/dist/ui/radial/index.js +18 -0
  107. package/dist/ui/radial/radialMenuStore.d.ts +31 -0
  108. package/dist/ui/radial/radialMenuStore.js +122 -0
  109. package/dist/ui/radial/radialMenuViewport.d.ts +6 -0
  110. package/dist/ui/radial/radialMenuViewport.js +59 -0
  111. package/dist/ui/radial/useRadialMenu.d.ts +35 -0
  112. package/dist/ui/radial/useRadialMenu.js +107 -0
  113. package/dist/ui/showcase.d.ts +14 -6
  114. package/dist/ui/showcase.js +13 -21
  115. package/dist/ui/tokens.d.ts +5 -19
  116. package/dist/ui/tokens.js +5 -21
  117. package/dist/ui.d.ts +14 -0
  118. package/dist/ui.js +15 -0
  119. package/package.json +145 -146
@@ -0,0 +1,91 @@
1
+ /**
2
+ * JAML Schema Bridge
3
+ *
4
+ * Derives all JAML schema constants from motely-wasm/jaml.schema.json.
5
+ * All JAML-aware files import from here — single swap point if the API changes.
6
+ */
7
+ import jamlSchemaJson from '../../../jaml.schema.json';
8
+ const schema = jamlSchemaJson;
9
+ const clauseDef = schema.definitions?.clause?.properties ?? {};
10
+ export const JAML_SCHEMA_VERSION = schema.version ?? 'unknown';
11
+ /** The raw JSON schema object. */
12
+ export const jamlSchema = schema;
13
+ // ── Key groups ─────────────────────────────────────────────────────────────
14
+ /** Root-level metadata keys (name, author, deck, etc.) */
15
+ export const METADATA_KEYS = [
16
+ 'id', 'name', 'description', 'author', 'dateCreated',
17
+ 'deck', 'stake', 'seeds', 'hashtags', 'aesthetics', 'defaults',
18
+ ];
19
+ /** Section keys that contain clause arrays. */
20
+ export const SECTION_KEYS = ['must', 'should', 'mustNot'];
21
+ /** Clause type keys — the primary discriminators (joker, voucher, tarot, etc.). */
22
+ export const CLAUSE_TYPE_KEYS = (clauseDef.type?.enum ?? []);
23
+ /** Property keys available inside a clause. */
24
+ export const PROPERTY_KEYS = Object.keys(clauseDef);
25
+ /** Source-configuration keys (shopItems, boosterPacks, etc.). */
26
+ export const SOURCE_KEYS = (() => {
27
+ const srcProps = clauseDef.sources?.properties;
28
+ return srcProps ? Object.keys(srcProps) : ['shopItems', 'boosterPacks'];
29
+ })();
30
+ // ── Value lookups ──────────────────────────────────────────────────────────
31
+ /**
32
+ * Get the valid enum values for a given JAML key.
33
+ * Checks top-level properties first, then falls back to clause properties.
34
+ */
35
+ export function getValidValuesForKey(key) {
36
+ // Top-level enum (deck, stake)
37
+ const topProp = schema.properties?.[key];
38
+ if (topProp?.enum)
39
+ return topProp.enum;
40
+ if (topProp?.items?.enum)
41
+ return topProp.items.enum;
42
+ // Clause-level enum
43
+ const clauseProp = clauseDef[key];
44
+ if (clauseProp?.enum)
45
+ return clauseProp.enum;
46
+ if (clauseProp?.items?.enum)
47
+ return clauseProp.items.enum;
48
+ return null;
49
+ }
50
+ /**
51
+ * Get the available properties for a given clause type.
52
+ * Returns all clause property keys (the type system doesn't restrict per-type in JSON schema v7).
53
+ */
54
+ export function getAvailablePropsForType(_clauseType) {
55
+ return PROPERTY_KEYS;
56
+ }
57
+ /**
58
+ * Check if a property is invalid for a clause type.
59
+ * In the v7 schema, all properties are available on all clause types.
60
+ */
61
+ export function isInvalidPropForType(_prop, _clauseType) {
62
+ return false;
63
+ }
64
+ /**
65
+ * Check if a value is invalid for a property.
66
+ */
67
+ export function isInvalidValueForProp(value, prop) {
68
+ const valid = getValidValuesForKey(prop);
69
+ if (!valid)
70
+ return false; // No enum constraint — anything goes
71
+ return !valid.includes(value);
72
+ }
73
+ // ── Convenience lookups ────────────────────────────────────────────────────
74
+ export const DECK_VALUES = (getValidValuesForKey('deck') ?? []);
75
+ export const STAKE_VALUES = (getValidValuesForKey('stake') ?? []);
76
+ export const EDITION_VALUES = (getValidValuesForKey('edition') ?? []);
77
+ export const SEAL_VALUES = (getValidValuesForKey('seal') ?? []);
78
+ export const ENHANCEMENT_VALUES = (getValidValuesForKey('enhancement') ?? []);
79
+ export const RANK_VALUES = (getValidValuesForKey('rank') ?? []);
80
+ export const SUIT_VALUES = (getValidValuesForKey('suit') ?? []);
81
+ export const STICKER_VALUES = (getValidValuesForKey('stickers') ?? []);
82
+ /**
83
+ * All JAML keywords — sections, metadata keys, clause types, and property keys.
84
+ */
85
+ export const ALL_JAML_KEYWORDS = [
86
+ ...SECTION_KEYS,
87
+ ...METADATA_KEYS,
88
+ ...CLAUSE_TYPE_KEYS,
89
+ ...PROPERTY_KEYS,
90
+ ...SOURCE_KEYS,
91
+ ];
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Parser for daily_ritual.json abbreviated format
3
+ *
4
+ * Expands abbreviated fields like "wj2", "hc1" into full item objects
5
+ */
6
+ export interface ParsedItem {
7
+ id: string;
8
+ name: string;
9
+ type: 'joker' | 'consumable' | 'voucher';
10
+ ante: number;
11
+ }
12
+ export interface ParsedSeed {
13
+ seed: string;
14
+ title: string;
15
+ score: number;
16
+ twos: number;
17
+ items: ParsedItem[];
18
+ }
19
+ /**
20
+ * Parse daily_ritual.json seed format
21
+ *
22
+ * Example input:
23
+ * {"id":"J4SPZMWW","t":"Twosday","j":"Joker","s":206,"w":16,"wj2":1,"hc2":1}
24
+ *
25
+ * Output:
26
+ * {
27
+ * seed: "J4SPZMWW",
28
+ * title: "Twosday",
29
+ * score: 206,
30
+ * twos: 16,
31
+ * items: [
32
+ * { id: 'weejoker', name: 'Wee Joker', type: 'joker', ante: 2 },
33
+ * { id: 'hangingchad', name: 'Hanging Chad', type: 'joker', ante: 2 }
34
+ * ]
35
+ * }
36
+ */
37
+ export declare function parseDailyRitualSeed(raw: any): ParsedSeed;
38
+ /**
39
+ * Group items by type for rendering
40
+ */
41
+ export declare function groupItemsByType(items: ParsedItem[]): {
42
+ jokers: ParsedItem[];
43
+ consumables: ParsedItem[];
44
+ vouchers: ParsedItem[];
45
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Parser for daily_ritual.json abbreviated format
3
+ *
4
+ * Expands abbreviated fields like "wj2", "hc1" into full item objects
5
+ */
6
+ // Mapping of abbreviations to full item data
7
+ const ITEM_MAP = {
8
+ wj: { id: 'weejoker', name: 'Wee Joker', type: 'joker' },
9
+ hc: { id: 'hangingchad', name: 'Hanging Chad', type: 'joker' },
10
+ hk: { id: 'hack', name: 'Hack', type: 'joker' },
11
+ bp: { id: 'blueprint', name: 'Blueprint', type: 'joker' },
12
+ bs: { id: 'brainstorm', name: 'Brainstorm', type: 'joker' },
13
+ sh: { id: 'showman', name: 'Showman', type: 'joker' },
14
+ cp: { id: 'copy', name: 'Copy', type: 'consumable' }, // Placeholder
15
+ // Add more as needed
16
+ };
17
+ /**
18
+ * Parse daily_ritual.json seed format
19
+ *
20
+ * Example input:
21
+ * {"id":"J4SPZMWW","t":"Twosday","j":"Joker","s":206,"w":16,"wj2":1,"hc2":1}
22
+ *
23
+ * Output:
24
+ * {
25
+ * seed: "J4SPZMWW",
26
+ * title: "Twosday",
27
+ * score: 206,
28
+ * twos: 16,
29
+ * items: [
30
+ * { id: 'weejoker', name: 'Wee Joker', type: 'joker', ante: 2 },
31
+ * { id: 'hangingchad', name: 'Hanging Chad', type: 'joker', ante: 2 }
32
+ * ]
33
+ * }
34
+ */
35
+ export function parseDailyRitualSeed(raw) {
36
+ const items = [];
37
+ // Parse each field looking for pattern: letters + number (e.g., "wj2", "hc1")
38
+ Object.keys(raw).forEach(key => {
39
+ const match = key.match(/^([a-z]+)(\d+)$/);
40
+ if (match) {
41
+ const [, code, ante] = match;
42
+ const itemData = ITEM_MAP[code];
43
+ if (itemData) {
44
+ const count = raw[key]; // Could be used for duplicates
45
+ items.push({
46
+ ...itemData,
47
+ ante: Number(ante)
48
+ });
49
+ }
50
+ }
51
+ });
52
+ return {
53
+ seed: raw.id || '',
54
+ title: raw.t || '',
55
+ score: raw.s || 0,
56
+ twos: raw.w || 0,
57
+ items
58
+ };
59
+ }
60
+ /**
61
+ * Group items by type for rendering
62
+ */
63
+ export function groupItemsByType(items) {
64
+ return {
65
+ jokers: items.filter(i => i.type === 'joker'),
66
+ consumables: items.filter(i => i.type === 'consumable'),
67
+ vouchers: items.filter(i => i.type === 'voucher')
68
+ };
69
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Advance past trailing punctuation and whitespace so reveal snaps to word boundaries.
3
+ * Used by both JammySpeechBox (home announcement) and ChatInterface (streaming chat).
4
+ */
5
+ export declare function getRevealPos(text: string, highlightPos: number): number;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Advance past trailing punctuation and whitespace so reveal snaps to word boundaries.
3
+ * Used by both JammySpeechBox (home announcement) and ChatInterface (streaming chat).
4
+ */
5
+ export function getRevealPos(text, highlightPos) {
6
+ if (highlightPos <= 0)
7
+ return 0;
8
+ if (highlightPos >= text.length)
9
+ return text.length;
10
+ let i = highlightPos;
11
+ while (i < text.length && /[)\]}'"'\u2018\u2019.,!?;:]/.test(text[i]))
12
+ i++;
13
+ while (i < text.length && /\s/.test(text[i]))
14
+ i++;
15
+ return i;
16
+ }
@@ -0,0 +1,19 @@
1
+ /** Strip markdown markers only — do not collapse whitespace (TTS highlight indices are raw offsets). */
2
+ export declare function stripMarkdownForTtsDisplay(text: string): string;
3
+ export type TtsDisplaySplit = {
4
+ prefix: string;
5
+ spoken: string;
6
+ pending: string;
7
+ suffix: string;
8
+ useWindowedView: boolean;
9
+ };
10
+ /**
11
+ * Split plain text into spoken vs pending regions for TTS word sync.
12
+ * When `stripMarkdown` is false, indices must refer to `text` as-is (matches streaming TTS offsets).
13
+ */
14
+ export declare function splitTtsDisplay(text: string, highlightPos: number | null | undefined, activeSentenceRange: {
15
+ start: number;
16
+ end: number;
17
+ } | null | undefined, options?: {
18
+ stripMarkdown?: boolean;
19
+ }): TtsDisplaySplit | null;
@@ -0,0 +1,35 @@
1
+ import { getRevealPos } from "./getRevealPos";
2
+ /** Strip markdown markers only — do not collapse whitespace (TTS highlight indices are raw offsets). */
3
+ export function stripMarkdownForTtsDisplay(text) {
4
+ return text
5
+ .replace(/```[\s\S]*?```/g, "")
6
+ .replace(/\*\*/g, "")
7
+ .trim();
8
+ }
9
+ /**
10
+ * Split plain text into spoken vs pending regions for TTS word sync.
11
+ * When `stripMarkdown` is false, indices must refer to `text` as-is (matches streaming TTS offsets).
12
+ */
13
+ export function splitTtsDisplay(text, highlightPos, activeSentenceRange, options) {
14
+ const body = options?.stripMarkdown ? stripMarkdownForTtsDisplay(text) : text;
15
+ if (!body)
16
+ return null;
17
+ const ttsDisabled = highlightPos === null || highlightPos === undefined;
18
+ if (ttsDisabled)
19
+ return null;
20
+ const hasTtsHighlight = typeof highlightPos === "number" && highlightPos >= 0;
21
+ const hasActiveSentence = Boolean(activeSentenceRange && activeSentenceRange.start >= 0 && activeSentenceRange.end > activeSentenceRange.start);
22
+ const sentenceStart = hasActiveSentence ? Math.min(activeSentenceRange.start, body.length) : 0;
23
+ const sentenceEnd = hasActiveSentence ? Math.min(activeSentenceRange.end, body.length) : body.length;
24
+ const revealPos = hasTtsHighlight
25
+ ? Math.min(Math.max(getRevealPos(body, highlightPos), sentenceStart), sentenceEnd)
26
+ : 0;
27
+ const useWindowedView = hasTtsHighlight && hasActiveSentence;
28
+ const displayStart = useWindowedView ? sentenceStart : 0;
29
+ const displayEnd = useWindowedView ? sentenceEnd : body.length;
30
+ const prefix = useWindowedView ? "" : body.slice(0, sentenceStart);
31
+ const spoken = body.slice(displayStart, Math.min(revealPos, displayEnd));
32
+ const pending = body.slice(Math.min(revealPos, displayEnd), displayEnd);
33
+ const suffix = useWindowedView ? "" : body.slice(sentenceEnd);
34
+ return { prefix, spoken, pending, suffix, useWindowedView };
35
+ }
@@ -0,0 +1,121 @@
1
+ export type DeckType = 'red' | 'blue' | 'yellow' | 'green' | 'black' | 'magic' | 'nebula' | 'ghost' | 'abandoned' | 'checkered' | 'zodiac' | 'painted' | 'anaglyph' | 'plasma' | 'erratic';
2
+ export type StakeType = 'white' | 'red' | 'green' | 'black' | 'blue' | 'purple' | 'orange' | 'gold';
3
+ /**
4
+ * Configuration for a Daily Ritual challenge
5
+ * Matches the dailyritual.schema.json structure
6
+ */
7
+ export interface RitualConfig {
8
+ filterId: string;
9
+ searchId: string;
10
+ deck: DeckType;
11
+ stake: StakeType;
12
+ seeds: string;
13
+ name: string;
14
+ author: string;
15
+ description?: string;
16
+ tutorial?: string;
17
+ epoch?: string;
18
+ icon?: string;
19
+ color?: string;
20
+ }
21
+ /**
22
+ * Seed data record from the seeds file.
23
+ * All scoring fields are dynamic — driven by the JAML filter's label columns.
24
+ */
25
+ export interface SeedData {
26
+ seed: string;
27
+ score: number;
28
+ stake?: StakeType;
29
+ startingDeck?: string[];
30
+ relevantEvents?: RelevantEvent[];
31
+ [key: string]: string | number | boolean | string[] | RelevantEvent[] | undefined;
32
+ }
33
+ export interface ParsedCard {
34
+ rank: string;
35
+ suit: string;
36
+ enhancement: string | null;
37
+ seal: string | null;
38
+ edition: string | null;
39
+ }
40
+ /**
41
+ * The type of a game event/item
42
+ */
43
+ export type RelevantEventType = 'joker' | 'tarot' | 'spectral' | 'planet' | 'voucher';
44
+ /**
45
+ * The source/location where the event occurs in game
46
+ */
47
+ export type EventSource = 'shop' | 'arcana_pack' | 'spectral_pack' | 'celestial_pack' | 'buffoon_pack' | 'standard_pack' | 'tag' | 'boss_blind' | 'voucher';
48
+ /**
49
+ * A "relevant" event as defined by the JAML filter's Should rules.
50
+ * Represents anything the ritual cares about tracking (jokers, consumables, etc.)
51
+ */
52
+ export interface RelevantEvent {
53
+ /** The ante number (1, 2, 3, ...) when this event occurs */
54
+ ante: number;
55
+ /** Where this event occurs (shop, pack, tag, etc.) */
56
+ source: EventSource;
57
+ /** The category of item */
58
+ type: RelevantEventType;
59
+ /** The item's identifier (e.g., "weejoker", "temperance", "blueprint") */
60
+ id: string;
61
+ /** Optional edition modifier (e.g., "negative", "polychrome", "foil", "holographic") */
62
+ edition?: string;
63
+ /** Optional enhancement (for standard cards: "bonus", "mult", "wild", etc.) */
64
+ enhancement?: string;
65
+ /** Optional seal (for standard cards: "gold", "red", "blue", "purple") */
66
+ seal?: string;
67
+ /** Count of this item (defaults to 1 if not specified) */
68
+ count?: number;
69
+ /** Optional display name override */
70
+ displayName?: string;
71
+ }
72
+ /**
73
+ * Complete seed data as exported in a JAMZ file.
74
+ * This is the "rich" data format containing everything needed to render the UI.
75
+ */
76
+ export interface JamzSeedData {
77
+ /** The 8-character Balatro seed string */
78
+ seed: string;
79
+ /** Calculated "goodness" score from the JAML filter */
80
+ score: number;
81
+ /** Count of Rank 2s in the starting deck (for Erratic deck display) */
82
+ twos?: number;
83
+ /** The exact 52 cards in starting deck. Format: {rank}_{suit} e.g., ["2_C", "10_H", "A_S"] */
84
+ startingDeck?: string[];
85
+ /** All relevant events matching the JAML filter's Should rules */
86
+ relevantEvents?: RelevantEvent[];
87
+ /** Legacy: individual JAML column values (for backward compatibility) */
88
+ [key: string]: string | number | boolean | string[] | RelevantEvent[] | undefined;
89
+ }
90
+ /**
91
+ * Header metadata in a JAMZ file
92
+ */
93
+ export interface JamzHeader {
94
+ /** JAMZ format version (e.g., "1") */
95
+ version: string;
96
+ /** Hash of the source JAML filter for verification */
97
+ jamlHash: string;
98
+ /** Total number of seeds in this file */
99
+ seedCount: number;
100
+ /** ISO 8601 timestamp when this file was generated */
101
+ generatedAt: string;
102
+ /** Ritual metadata */
103
+ ritual: {
104
+ id: string;
105
+ name: string;
106
+ author: string;
107
+ description?: string;
108
+ tutorial?: string;
109
+ deck: DeckType;
110
+ stake: StakeType;
111
+ icon?: string;
112
+ color?: string;
113
+ };
114
+ }
115
+ /**
116
+ * Complete JAMZ file structure (after decompression and JSON parsing)
117
+ */
118
+ export interface JamzFile {
119
+ header: JamzHeader;
120
+ seeds: JamzSeedData[];
121
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,10 @@
1
+ import type React from "react";
2
+ export interface JimboIconButtonProps {
3
+ "aria-label"?: string;
4
+ children: React.ReactNode;
5
+ disabled?: boolean;
6
+ onClick?: () => void;
7
+ size?: "sm" | "md";
8
+ title?: string;
9
+ }
10
+ export declare function JimboIconButton({ onClick, title, "aria-label": ariaLabel, disabled, size, children, }: JimboIconButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ // PORTABLE — intended for jaml-ui/src/ui/jimboIconButton.tsx
2
+ // On paste, replace `from 'jaml-ui'` with `from './tokens.js'`.
3
+ "use client";
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ import { JimboColorOption } from "./tokens.js";
6
+ import { useState } from "react";
7
+ const C = JimboColorOption;
8
+ export function JimboIconButton({ onClick, title, "aria-label": ariaLabel, disabled = false, size = "md", children, }) {
9
+ const [hover, setHover] = useState(false);
10
+ const side = size === "sm" ? 26 : 30;
11
+ return (_jsx("button", { "aria-label": ariaLabel ?? title, disabled: disabled, onClick: () => !disabled && onClick?.(), onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
12
+ width: side,
13
+ height: side,
14
+ display: "inline-flex",
15
+ alignItems: "center",
16
+ justifyContent: "center",
17
+ background: hover && !disabled ? C.DARK_GREY : C.DARKEST,
18
+ color: C.WHITE,
19
+ border: `1px solid ${C.PANEL_EDGE}`,
20
+ borderRadius: 4,
21
+ cursor: disabled ? "not-allowed" : "pointer",
22
+ opacity: disabled ? 0.55 : 1,
23
+ padding: 0,
24
+ fontSize: 14,
25
+ lineHeight: 1,
26
+ transition: "background 80ms ease",
27
+ }, title: title, type: "button", children: children }));
28
+ }
@@ -0,0 +1,13 @@
1
+ export interface JimboInputModalProps {
2
+ cancelLabel?: string;
3
+ confirmLabel?: string;
4
+ initialValue?: string;
5
+ message?: string;
6
+ onCancel: () => void;
7
+ onConfirm: (value: string) => void;
8
+ open: boolean;
9
+ placeholder?: string;
10
+ title: string;
11
+ validate?: (value: string) => string | null;
12
+ }
13
+ export declare function JimboInputModal({ open, title, message, placeholder, initialValue, confirmLabel, cancelLabel, validate, onConfirm, onCancel, }: JimboInputModalProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,60 @@
1
+ // PORTABLE — intended for jaml-ui/src/ui/jimboInputModal.tsx
2
+ // On paste, replace `from 'jaml-ui'` with `from './panel.js'`/`./tokens.js` as appropriate.
3
+ "use client";
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { JimboButton, JimboModal } from "./panel.js";
6
+ import { JimboColorOption } from "./tokens.js";
7
+ import { JimboText } from "./jimboText.js";
8
+ import { useEffect, useRef, useState } from "react";
9
+ const C = JimboColorOption;
10
+ export function JimboInputModal({ open, title, message, placeholder, initialValue = "", confirmLabel = "Confirm", cancelLabel = "Cancel", validate, onConfirm, onCancel, }) {
11
+ const [value, setValue] = useState(initialValue);
12
+ const [error, setError] = useState(null);
13
+ const inputRef = useRef(null);
14
+ useEffect(() => {
15
+ if (open) {
16
+ setValue(initialValue);
17
+ setError(null);
18
+ const t = setTimeout(() => inputRef.current?.focus(), 30);
19
+ return () => clearTimeout(t);
20
+ }
21
+ }, [open, initialValue]);
22
+ function submit() {
23
+ const err = validate?.(value) ?? null;
24
+ if (err) {
25
+ setError(err);
26
+ return;
27
+ }
28
+ onConfirm(value);
29
+ }
30
+ return (_jsxs(JimboModal, { onClose: onCancel, open: open, title: title, children: [message && (_jsx(JimboText, { size: "sm", style: { display: "block", marginBottom: 8 }, tone: "grey", children: message })), _jsx("input", { "aria-invalid": error ? "true" : "false", onChange: (e) => {
31
+ setValue(e.target.value);
32
+ if (error) {
33
+ setError(null);
34
+ }
35
+ }, onKeyDown: (e) => {
36
+ if (e.key === "Enter") {
37
+ submit();
38
+ }
39
+ if (e.key === "Escape") {
40
+ onCancel();
41
+ }
42
+ }, placeholder: placeholder, ref: inputRef, style: {
43
+ width: "100%",
44
+ padding: "8px 10px",
45
+ background: C.DARKEST,
46
+ color: C.WHITE,
47
+ border: `1px solid ${error ? C.RED : C.PANEL_EDGE}`,
48
+ borderRadius: 4,
49
+ fontSize: 13,
50
+ fontFamily: "m6x11plus, monospace",
51
+ letterSpacing: 1,
52
+ outline: "none",
53
+ boxSizing: "border-box",
54
+ }, type: "text", value: value }), error && (_jsx(JimboText, { size: "sm", style: { display: "block", marginTop: 6 }, tone: "red", children: error })), _jsxs("div", { style: {
55
+ display: "flex",
56
+ justifyContent: "flex-end",
57
+ gap: 8,
58
+ marginTop: 14,
59
+ }, children: [_jsx(JimboButton, { onClick: onCancel, size: "sm", tone: "red", children: cancelLabel }), _jsx(JimboButton, { onClick: submit, size: "sm", tone: "blue", children: confirmLabel })] })] }));
60
+ }
@@ -0,0 +1,18 @@
1
+ import type React from "react";
2
+ export interface JimboSelectOption {
3
+ disabled?: boolean;
4
+ label?: string;
5
+ value: string;
6
+ }
7
+ export interface JimboSelectProps {
8
+ "aria-label"?: string;
9
+ disabled?: boolean;
10
+ fullWidth?: boolean;
11
+ onChange: (value: string) => void;
12
+ options: JimboSelectOption[] | string[];
13
+ placeholder?: string;
14
+ size?: "sm" | "md";
15
+ style?: React.CSSProperties;
16
+ value: string;
17
+ }
18
+ export declare function JimboSelect({ value, options, onChange, placeholder, disabled, fullWidth, size, style, "aria-label": ariaLabel, }: JimboSelectProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,43 @@
1
+ // PORTABLE — intended for jaml-ui/src/ui/jimboSelect.tsx
2
+ // On paste, replace `from 'jaml-ui'` with `from './tokens.js'`.
3
+ "use client";
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { JimboColorOption } from "./tokens.js";
6
+ const C = JimboColorOption;
7
+ export function JimboSelect({ value, options, onChange, placeholder, disabled = false, fullWidth = true, size = "md", style, "aria-label": ariaLabel, }) {
8
+ let normalized;
9
+ if (options.length === 0) {
10
+ normalized = [];
11
+ }
12
+ else if (typeof options[0] === "string") {
13
+ normalized = options.map((v) => ({ value: v }));
14
+ }
15
+ else {
16
+ normalized = options;
17
+ }
18
+ const pad = size === "sm" ? "4px 8px" : "6px 10px";
19
+ const fontSize = size === "sm" ? 11 : 12;
20
+ return (_jsxs("select", { "aria-label": ariaLabel, disabled: disabled, onChange: (e) => onChange(e.target.value), style: {
21
+ width: fullWidth ? "100%" : undefined,
22
+ padding: pad,
23
+ background: C.DARKEST,
24
+ color: C.WHITE,
25
+ border: `1px solid ${C.PANEL_EDGE}`,
26
+ borderRadius: 4,
27
+ fontSize,
28
+ fontFamily: "m6x11plus, monospace",
29
+ cursor: disabled ? "not-allowed" : "pointer",
30
+ opacity: disabled ? 0.55 : 1,
31
+ appearance: "none",
32
+ backgroundImage: "linear-gradient(45deg, transparent 50%, " +
33
+ C.GOLD_TEXT +
34
+ " 50%), linear-gradient(135deg, " +
35
+ C.GOLD_TEXT +
36
+ " 50%, transparent 50%)",
37
+ backgroundPosition: "calc(100% - 12px) 50%, calc(100% - 7px) 50%",
38
+ backgroundSize: "5px 5px, 5px 5px",
39
+ backgroundRepeat: "no-repeat",
40
+ paddingRight: 22,
41
+ ...style,
42
+ }, value: value, children: [placeholder !== undefined && (_jsx("option", { disabled: true, value: "", children: placeholder })), normalized.map((opt) => (_jsx("option", { disabled: opt.disabled, value: opt.value, children: opt.label ?? opt.value }, opt.value)))] }));
43
+ }
@@ -0,0 +1,7 @@
1
+ export interface PanelSplitterProps {
2
+ "aria-label"?: string;
3
+ onDrag: (delta: number) => void;
4
+ onKeyAdjust?: (delta: number) => void;
5
+ orientation?: "vertical" | "horizontal";
6
+ }
7
+ export declare function PanelSplitter({ orientation, onDrag, onKeyAdjust, "aria-label": ariaLabel, }: PanelSplitterProps): import("react/jsx-runtime").JSX.Element;