jaml-ui 0.30.0 → 0.32.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.
Files changed (100) hide show
  1. package/README.md +193 -61
  2. package/assets/fonts/README.md +14 -0
  3. package/assets/fonts/m6x11.ttf +0 -0
  4. package/assets/fonts/m6x11plus.ttf +0 -0
  5. package/dist/.gitkeep +0 -0
  6. package/dist/assets/{searchPoolWorker-C4xj1WA_.js → searchPoolWorker-6mF4VfgO.js} +5 -5
  7. package/dist/assets/searchPoolWorker-6mF4VfgO.js.map +1 -0
  8. package/dist/assets.d.ts +1 -1
  9. package/dist/chunks/{assets-D80i9sMq.js → assets-Bb6wV80_.js} +3 -3
  10. package/dist/chunks/assets-Bb6wV80_.js.map +1 -0
  11. package/dist/chunks/jamlSeeds-3ILzc_rj.js +66 -0
  12. package/dist/chunks/jamlSeeds-3ILzc_rj.js.map +1 -0
  13. package/dist/chunks/{runtime-C-It0krH.js → runtime-Bklr4A-D.js} +2 -2
  14. package/dist/chunks/{runtime-C-It0krH.js.map → runtime-Bklr4A-D.js.map} +1 -1
  15. package/dist/chunks/{searchPoolWorker-hVVM49SD.js → searchPoolWorker-Dqhc9lfv.js} +2 -2
  16. package/dist/chunks/searchPoolWorker-Dqhc9lfv.js.map +1 -0
  17. package/dist/chunks/{spriteMapper-C4_5G1Z6.js → spriteMapper-CajFGgUU.js} +2 -2
  18. package/dist/chunks/spriteMapper-CajFGgUU.js.map +1 -0
  19. package/dist/chunks/tokens-Qrhlekc4.js.map +1 -1
  20. package/dist/chunks/{ui-D_9HO9OO.js → ui-UqEG1EZ9.js} +1670 -1191
  21. package/dist/chunks/ui-UqEG1EZ9.js.map +1 -0
  22. package/dist/components/CardFan.d.ts +0 -9
  23. package/dist/components/JamlAestheticSelector.d.ts +3 -3
  24. package/dist/components/JamlIde.d.ts +1 -4
  25. package/dist/components/JamlSeedSpinner.d.ts +3 -1
  26. package/dist/components/Jamlyzer.d.ts +5 -8
  27. package/dist/components/MotelyHello.d.ts +5 -0
  28. package/dist/components/StandardCard.d.ts +14 -0
  29. package/dist/components/cardEnums.d.ts +48 -0
  30. package/dist/components/jamlMap/JokerPicker.d.ts +2 -1
  31. package/dist/components/jamlMap/MysterySlot.d.ts +2 -1
  32. package/dist/core.d.ts +1 -0
  33. package/dist/core.js +7 -7
  34. package/dist/core.js.map +1 -1
  35. package/dist/hooks/useAnalyzer.d.ts +1 -1
  36. package/dist/hooks/useSearchPool.d.ts +1 -1
  37. package/dist/index.d.ts +6 -4
  38. package/dist/index.js +3319 -3278
  39. package/dist/index.js.map +1 -1
  40. package/dist/lib/data/constants.d.ts +3 -3
  41. package/dist/lib/jaml/jamlData.d.ts +1 -0
  42. package/dist/lib/jaml/jamlSchema.d.ts +10 -60
  43. package/dist/lib/jaml/jamlSeeds.d.ts +7 -0
  44. package/dist/lib/types.d.ts +3 -2
  45. package/dist/motely.d.ts +1 -2
  46. package/dist/motely.js +380 -428
  47. package/dist/motely.js.map +1 -1
  48. package/dist/r3f.js +16 -16
  49. package/dist/r3f.js.map +1 -1
  50. package/dist/ui/JimboBadge.d.ts +1 -1
  51. package/dist/ui/{footer.d.ts → JimboBalatroFooter.d.ts} +4 -2
  52. package/dist/ui/JimboButton.d.ts +62 -0
  53. package/dist/ui/JimboCopyButton.d.ts +27 -0
  54. package/dist/ui/JimboDeckAndStakeSelectorModal.d.ts +30 -0
  55. package/dist/ui/JimboDualChip.d.ts +17 -0
  56. package/dist/ui/JimboIconButton.d.ts +8 -3
  57. package/dist/ui/JimboInlineEdit.d.ts +16 -0
  58. package/dist/ui/JimboListItem.d.ts +11 -0
  59. package/dist/ui/JimboPanelSplitter.d.ts +8 -0
  60. package/dist/ui/JimboSeedCopyChip.d.ts +16 -0
  61. package/dist/ui/JimboSelect.d.ts +15 -3
  62. package/dist/ui/JimboSlider.d.ts +11 -0
  63. package/dist/ui/JimboSpinner.d.ts +18 -0
  64. package/dist/ui/JimboStepper.d.ts +22 -0
  65. package/dist/ui/JimboToggleList.d.ts +2 -1
  66. package/dist/ui/JimboValueBadge.d.ts +19 -0
  67. package/dist/ui/codeBlock.d.ts +4 -0
  68. package/dist/ui/hooks.d.ts +30 -3
  69. package/dist/ui/jimbo.css +1 -1
  70. package/dist/ui/jimboApp.d.ts +2 -2
  71. package/dist/ui/jimboBackground.d.ts +12 -5
  72. package/dist/ui/jimboCopyRow.d.ts +2 -2
  73. package/dist/ui/jimboInset.d.ts +3 -3
  74. package/dist/ui/jimboLayout.d.ts +25 -0
  75. package/dist/ui/jimboLink.d.ts +12 -0
  76. package/dist/ui/jimboTabs.d.ts +6 -6
  77. package/dist/ui/jimboText.d.ts +2 -3
  78. package/dist/ui/jimboTooltip.d.ts +10 -1
  79. package/dist/ui/panel.d.ts +1 -13
  80. package/dist/ui.d.ts +16 -4
  81. package/dist/ui.js +3 -3
  82. package/fonts.css +20 -5
  83. package/package.json +14 -14
  84. package/assets/fonts/m6x11plusplus.otf +0 -0
  85. package/dist/assets/searchPoolWorker-C4xj1WA_.js.map +0 -1
  86. package/dist/chunks/Layer-Ckd_T6Fw.js +0 -17
  87. package/dist/chunks/Layer-Ckd_T6Fw.js.map +0 -1
  88. package/dist/chunks/assets-D80i9sMq.js.map +0 -1
  89. package/dist/chunks/searchPoolWorker-hVVM49SD.js.map +0 -1
  90. package/dist/chunks/spriteMapper-C4_5G1Z6.js.map +0 -1
  91. package/dist/chunks/ui-D_9HO9OO.js.map +0 -1
  92. package/dist/components/CardList.d.ts +0 -8
  93. package/dist/components/Standardcard.d.ts +0 -18
  94. package/dist/config.d.ts +0 -10
  95. package/dist/fonts/m6x11plus.otf +0 -0
  96. package/dist/hooks/useMotelyRuntime.d.ts +0 -11
  97. package/dist/providers/MotelyProvider.d.ts +0 -11
  98. package/dist/ui/JimboFloating.d.ts +0 -8
  99. package/dist/ui/PanelSplitter.d.ts +0 -8
  100. package/jaml.schema.json +0 -1219
package/dist/r3f.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"r3f.js","names":[],"sources":["../src/r3f/Card3D.tsx","../src/r3f/JimboBillboard.tsx","../src/r3f/JimboText3D.tsx"],"sourcesContent":["'use client'\r\n\r\nimport { useRef, useMemo, useState, memo } from 'react'\r\nimport { useFrame, useLoader, type ThreeEvent } from '@react-three/fiber'\r\nimport { useSpring, animated } from '@react-spring/three'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_DIMENSIONS = { WIDTH: 0.7, HEIGHT: 0.95, DEPTH: 0.02 } as const\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_MAGNET = {\r\n MAX_TILT_X: 0.36,\r\n MAX_TILT_Y: 0.42,\r\n MAX_SHIFT: 0.038,\r\n TWIST_Z: 0.11,\r\n LERP_IN: 18,\r\n LERP_OUT: 10,\r\n} as const\r\n\r\nfunction useSpriteTexture(sprite: MotelySpriteData) {\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n \r\n return useMemo(() => {\r\n const t = texture.clone();\r\n t.colorSpace = THREE.SRGBColorSpace;\r\n t.magFilter = THREE.NearestFilter;\r\n t.minFilter = THREE.NearestFilter;\r\n t.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n t.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n t.needsUpdate = true;\r\n return t;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n}\r\n\r\nexport interface Card3DProps {\r\n sprite: MotelySpriteData\r\n position?: [number, number, number]\r\n rotation?: [number, number, number]\r\n selected?: boolean\r\n highlighted?: boolean\r\n onClick?: () => void\r\n onPointerEnter?: () => void\r\n onPointerLeave?: () => void\r\n}\r\n\r\nexport const Card3D = memo(function Card3D({\r\n sprite,\r\n position = [0, 0, 0],\r\n rotation = [0, 0, 0],\r\n selected = false,\r\n highlighted = false,\r\n onClick,\r\n onPointerEnter,\r\n onPointerLeave,\r\n}: Card3DProps) {\r\n const tiltRef = useRef<THREE.Group>(null)\r\n const target = useRef({ rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 })\r\n const [hovered, setHovered] = useState(false)\r\n\r\n const texture = useSpriteTexture(sprite)\r\n\r\n const { posY, scale } = useSpring({\r\n posY: selected ? 0.3 : hovered ? 0.15 : 0,\r\n scale: hovered ? 1.08 : selected ? 1.05 : 1,\r\n config: { tension: 300, friction: 20 },\r\n })\r\n\r\n useFrame((_state, dt) => {\r\n const g = tiltRef.current\r\n if (!g) return\r\n const t = target.current\r\n const rate = hovered ? CARD_MAGNET.LERP_IN : CARD_MAGNET.LERP_OUT\r\n const a = 1 - Math.exp(-rate * dt)\r\n g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, t.rx, a)\r\n g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, t.ry, a)\r\n g.rotation.z = THREE.MathUtils.lerp(g.rotation.z, t.rz, a)\r\n g.position.x = THREE.MathUtils.lerp(g.position.x, t.ox, a)\r\n g.position.y = THREE.MathUtils.lerp(g.position.y, t.oy, a)\r\n })\r\n\r\n const glowColor = useMemo(() => highlighted ? '#e4b643' : '#ffffff', [highlighted])\r\n\r\n const onMove = (e: ThreeEvent<PointerEvent>) => {\r\n e.stopPropagation()\r\n const uv = e.uv\r\n if (!uv) return\r\n const nx = THREE.MathUtils.clamp((uv.x - 0.5) * 2, -1, 1)\r\n const ny = THREE.MathUtils.clamp((uv.y - 0.5) * 2, -1, 1)\r\n target.current.ry = -nx * CARD_MAGNET.MAX_TILT_Y\r\n target.current.rx = ny * CARD_MAGNET.MAX_TILT_X\r\n target.current.rz = -nx * ny * CARD_MAGNET.TWIST_Z\r\n target.current.ox = nx * CARD_MAGNET.MAX_SHIFT\r\n target.current.oy = -ny * CARD_MAGNET.MAX_SHIFT * 0.65\r\n }\r\n\r\n const reset = () => { target.current = { rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 } }\r\n\r\n if (!texture) return null\r\n\r\n return (\r\n <animated.group\r\n position-x={position[0]}\r\n position-y={posY.to((y) => position[1] + y)}\r\n position-z={position[2]}\r\n rotation-x={rotation[0]}\r\n rotation-y={rotation[1]}\r\n rotation-z={rotation[2]}\r\n scale={scale}\r\n >\r\n {/* Invisible hit mesh that does not shift or twist, preventing boop-cancel loops */}\r\n <mesh\r\n visible={false}\r\n onClick={(e) => { e.stopPropagation(); onClick?.() }}\r\n onPointerMove={onMove}\r\n onPointerEnter={(e) => { e.stopPropagation(); setHovered(true); onPointerEnter?.(); document.body.style.cursor = 'pointer' }}\r\n onPointerLeave={(e) => { e.stopPropagation(); setHovered(false); reset(); onPointerLeave?.(); document.body.style.cursor = 'auto' }}\r\n >\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH * 2]} />\r\n <meshBasicMaterial />\r\n </mesh>\r\n\r\n <group ref={tiltRef}>\r\n {highlighted && (\r\n <pointLight color={glowColor} intensity={1.5} distance={1} position={[0, 0, 0.1]} />\r\n )}\r\n <mesh castShadow receiveShadow>\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH]} />\r\n <meshBasicMaterial attach=\"material-4\" map={texture} toneMapped={false} />\r\n <meshStandardMaterial attach=\"material-5\" color=\"#1a1a2e\" metalness={0.2} roughness={0.8} />\r\n <meshStandardMaterial attach=\"material-0\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-1\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-2\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-3\" color=\"#f5f5dc\" />\r\n </mesh>\r\n {selected && (\r\n <mesh position={[0, 0, -CARD_DIMENSIONS.DEPTH]}>\r\n <ringGeometry args={[0.45, 0.5, 32]} />\r\n <meshBasicMaterial color=\"#e4b643\" transparent opacity={0.8} />\r\n </mesh>\r\n )}\r\n </group>\r\n </animated.group>\r\n )\r\n})\r\n","import React, { useMemo } from 'react'\r\nimport { Billboard } from '@react-three/drei'\r\nimport { useLoader } from '@react-three/fiber'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\nexport interface JimboBillboardProps {\r\n sprite: MotelySpriteData | null\r\n label?: string\r\n width?: number\r\n height?: number\r\n yLockOnly?: boolean\r\n position?: [number, number, number]\r\n}\r\n\r\nexport function JimboBillboard(props: JimboBillboardProps) {\r\n if (!props.sprite) return null;\r\n return <JimboBillboardInner {...props} sprite={props.sprite} />;\r\n}\r\n\r\nfunction JimboBillboardInner({\r\n sprite,\r\n width = 3.4,\r\n height = 4.5,\r\n yLockOnly = false,\r\n position = [0, 0, 0]\r\n}: Omit<JimboBillboardProps, 'sprite'> & { sprite: MotelySpriteData }) {\r\n // Memoize texture to avoid per-render allocation\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n\r\n const clonedTexture = useMemo(() => {\r\n const tex = texture.clone();\r\n tex.magFilter = THREE.NearestFilter;\r\n tex.minFilter = THREE.NearestFilter;\r\n // Set up sprite cropping\r\n tex.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n tex.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n tex.needsUpdate = true;\r\n return tex;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n\r\n const material = useMemo(() => {\r\n return new THREE.MeshBasicMaterial({\r\n map: clonedTexture,\r\n transparent: true,\r\n alphaTest: 0.5\r\n });\r\n }, [clonedTexture]);\r\n\r\n return (\r\n <Billboard\r\n lockY={yLockOnly}\r\n lockX={false}\r\n lockZ={false}\r\n position={position}\r\n >\r\n <mesh material={material}>\r\n <planeGeometry args={[width, height]} />\r\n </mesh>\r\n </Billboard>\r\n )\r\n}\r\n","import React from 'react'\r\nimport { Text } from '@react-three/drei'\r\nimport { resolveJamlAssetUrl } from '../assets.js'\r\nimport { JimboColorOption } from '../ui/tokens.js'\r\n\r\nexport interface JimboText3DProps {\r\n children: string\r\n color?: string\r\n outlineColor?: string\r\n outlineWidth?: number\r\n position?: [number, number, number]\r\n fontSize?: number\r\n}\r\n\r\nexport function JimboText3D({\r\n children,\r\n color = JimboColorOption.WHITE,\r\n outlineColor = JimboColorOption.BLACK,\r\n outlineWidth = 0.05,\r\n position = [0, 0, 0],\r\n fontSize = 1\r\n}: JimboText3DProps) {\r\n // We use the m6x11plus font from assets if possible, or fallback\r\n return (\r\n <Text\r\n position={position}\r\n fontSize={fontSize}\r\n color={color}\r\n outlineColor={outlineColor}\r\n outlineWidth={outlineWidth}\r\n font={resolveJamlAssetUrl('font')}\r\n anchorX=\"center\"\r\n anchorY=\"middle\"\r\n >\r\n {children}\r\n </Text>\r\n )\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"r3f.js","names":[],"sources":["../src/r3f/Card3D.tsx","../src/r3f/JimboBillboard.tsx","../src/r3f/JimboText3D.tsx"],"sourcesContent":["'use client'\n\nimport { useRef, useMemo, useState, memo } from 'react'\nimport { useFrame, useLoader, type ThreeEvent } from '@react-three/fiber'\nimport { useSpring, animated } from '@react-spring/three'\nimport * as THREE from 'three'\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const CARD_DIMENSIONS = { WIDTH: 0.7, HEIGHT: 0.95, DEPTH: 0.02 } as const\n// eslint-disable-next-line react-refresh/only-export-components\nexport const CARD_MAGNET = {\n MAX_TILT_X: 0.36,\n MAX_TILT_Y: 0.42,\n MAX_SHIFT: 0.038,\n TWIST_Z: 0.11,\n LERP_IN: 18,\n LERP_OUT: 10,\n} as const\n\nfunction useSpriteTexture(sprite: MotelySpriteData) {\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\n \n return useMemo(() => {\n const t = texture.clone();\n t.colorSpace = THREE.SRGBColorSpace;\n t.magFilter = THREE.NearestFilter;\n t.minFilter = THREE.NearestFilter;\n t.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\n t.offset.set(\n sprite.gridCol / sprite.gridCols,\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\n );\n t.needsUpdate = true;\n return t;\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\n}\n\nexport interface Card3DProps {\n sprite: MotelySpriteData\n position?: [number, number, number]\n rotation?: [number, number, number]\n selected?: boolean\n highlighted?: boolean\n onClick?: () => void\n onPointerEnter?: () => void\n onPointerLeave?: () => void\n}\n\nexport const Card3D = memo(function Card3D({\n sprite,\n position = [0, 0, 0],\n rotation = [0, 0, 0],\n selected = false,\n highlighted = false,\n onClick,\n onPointerEnter,\n onPointerLeave,\n}: Card3DProps) {\n const tiltRef = useRef<THREE.Group>(null)\n const target = useRef({ rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 })\n const [hovered, setHovered] = useState(false)\n\n const texture = useSpriteTexture(sprite)\n\n const { posY, scale } = useSpring({\n posY: selected ? 0.3 : hovered ? 0.15 : 0,\n scale: hovered ? 1.08 : selected ? 1.05 : 1,\n config: { tension: 300, friction: 20 },\n })\n\n useFrame((_state, dt) => {\n const g = tiltRef.current\n if (!g) return\n const t = target.current\n const rate = hovered ? CARD_MAGNET.LERP_IN : CARD_MAGNET.LERP_OUT\n const a = 1 - Math.exp(-rate * dt)\n g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, t.rx, a)\n g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, t.ry, a)\n g.rotation.z = THREE.MathUtils.lerp(g.rotation.z, t.rz, a)\n g.position.x = THREE.MathUtils.lerp(g.position.x, t.ox, a)\n g.position.y = THREE.MathUtils.lerp(g.position.y, t.oy, a)\n })\n\n const glowColor = useMemo(() => highlighted ? '#e4b643' : '#ffffff', [highlighted])\n\n const onMove = (e: ThreeEvent<PointerEvent>) => {\n e.stopPropagation()\n const uv = e.uv\n if (!uv) return\n const nx = THREE.MathUtils.clamp((uv.x - 0.5) * 2, -1, 1)\n const ny = THREE.MathUtils.clamp((uv.y - 0.5) * 2, -1, 1)\n target.current.ry = -nx * CARD_MAGNET.MAX_TILT_Y\n target.current.rx = ny * CARD_MAGNET.MAX_TILT_X\n target.current.rz = -nx * ny * CARD_MAGNET.TWIST_Z\n target.current.ox = nx * CARD_MAGNET.MAX_SHIFT\n target.current.oy = -ny * CARD_MAGNET.MAX_SHIFT * 0.65\n }\n\n const reset = () => { target.current = { rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 } }\n\n if (!texture) return null\n\n return (\n <animated.group\n position-x={position[0]}\n position-y={posY.to((y) => position[1] + y)}\n position-z={position[2]}\n rotation-x={rotation[0]}\n rotation-y={rotation[1]}\n rotation-z={rotation[2]}\n scale={scale}\n >\n {/* Invisible hit mesh that does not shift or twist, preventing boop-cancel loops */}\n <mesh\n visible={false}\n onClick={(e) => { e.stopPropagation(); onClick?.() }}\n onPointerMove={onMove}\n onPointerEnter={(e) => { e.stopPropagation(); setHovered(true); onPointerEnter?.(); document.body.style.cursor = 'pointer' }}\n onPointerLeave={(e) => { e.stopPropagation(); setHovered(false); reset(); onPointerLeave?.(); document.body.style.cursor = 'auto' }}\n >\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH * 2]} />\n <meshBasicMaterial />\n </mesh>\n\n <group ref={tiltRef}>\n {highlighted && (\n <pointLight color={glowColor} intensity={1.5} distance={1} position={[0, 0, 0.1]} />\n )}\n <mesh castShadow receiveShadow>\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH]} />\n <meshBasicMaterial attach=\"material-4\" map={texture} toneMapped={false} />\n <meshStandardMaterial attach=\"material-5\" color=\"#1a1a2e\" metalness={0.2} roughness={0.8} />\n <meshStandardMaterial attach=\"material-0\" color=\"#f5f5dc\" />\n <meshStandardMaterial attach=\"material-1\" color=\"#f5f5dc\" />\n <meshStandardMaterial attach=\"material-2\" color=\"#f5f5dc\" />\n <meshStandardMaterial attach=\"material-3\" color=\"#f5f5dc\" />\n </mesh>\n {selected && (\n <mesh position={[0, 0, -CARD_DIMENSIONS.DEPTH]}>\n <ringGeometry args={[0.45, 0.5, 32]} />\n <meshBasicMaterial color=\"#e4b643\" transparent opacity={0.8} />\n </mesh>\n )}\n </group>\n </animated.group>\n )\n})\n","import React, { useMemo } from 'react'\nimport { Billboard } from '@react-three/drei'\nimport { useLoader } from '@react-three/fiber'\nimport * as THREE from 'three'\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\n\nexport interface JimboBillboardProps {\n sprite: MotelySpriteData | null\n label?: string\n width?: number\n height?: number\n yLockOnly?: boolean\n position?: [number, number, number]\n}\n\nexport function JimboBillboard(props: JimboBillboardProps) {\n if (!props.sprite) return null;\n return <JimboBillboardInner {...props} sprite={props.sprite} />;\n}\n\nfunction JimboBillboardInner({\n sprite,\n width = 3.4,\n height = 4.5,\n yLockOnly = false,\n position = [0, 0, 0]\n}: Omit<JimboBillboardProps, 'sprite'> & { sprite: MotelySpriteData }) {\n // Memoize texture to avoid per-render allocation\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\n\n const clonedTexture = useMemo(() => {\n const tex = texture.clone();\n tex.magFilter = THREE.NearestFilter;\n tex.minFilter = THREE.NearestFilter;\n // Set up sprite cropping\n tex.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\n tex.offset.set(\n sprite.gridCol / sprite.gridCols,\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\n );\n tex.needsUpdate = true;\n return tex;\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\n\n const material = useMemo(() => {\n return new THREE.MeshBasicMaterial({\n map: clonedTexture,\n transparent: true,\n alphaTest: 0.5\n });\n }, [clonedTexture]);\n\n return (\n <Billboard\n lockY={yLockOnly}\n lockX={false}\n lockZ={false}\n position={position}\n >\n <mesh material={material}>\n <planeGeometry args={[width, height]} />\n </mesh>\n </Billboard>\n )\n}\n","import React from 'react'\nimport { Text } from '@react-three/drei'\nimport { resolveJamlAssetUrl } from '../assets.js'\nimport { JimboColorOption } from '../ui/tokens.js'\n\nexport interface JimboText3DProps {\n children: string\n color?: string\n outlineColor?: string\n outlineWidth?: number\n position?: [number, number, number]\n fontSize?: number\n}\n\nexport function JimboText3D({\n children,\n color = JimboColorOption.WHITE,\n outlineColor = JimboColorOption.BLACK,\n outlineWidth = 0.05,\n position = [0, 0, 0],\n fontSize = 1\n}: JimboText3DProps) {\n // We use the m6x11plus font from assets if possible, or fallback\n return (\n <Text\n position={position}\n fontSize={fontSize}\n color={color}\n outlineColor={outlineColor}\n outlineWidth={outlineWidth}\n font={resolveJamlAssetUrl('font')}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n {children}\n </Text>\n )\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- export type JimboBadgeTone = 'dark' | 'blue' | 'red' | 'green' | 'gold' | 'grey' | 'orange' | 'purple';
2
+ export type JimboBadgeTone = 'dark' | 'blue' | 'red' | 'green' | 'grey' | 'orange' | 'purple';
3
3
  export interface JimboBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
4
4
  size?: 'sm' | 'md';
5
5
  tone?: JimboBadgeTone;
@@ -8,7 +8,9 @@ export interface JimboBalatroFooterProps {
8
8
  children?: React.ReactNode;
9
9
  }
10
10
  /**
11
- * Attribution footer with animated suit cycle.
12
- * Always rendered required attribution for using Balatro art.
11
+ * Fan-site attribution footer with Balatro link. The "Balatro" in the name is
12
+ * load-bearingthis footer is the public disclosure that the project is a
13
+ * non-profit, rule-following, PlayStack-aware fan site. Always rendered;
14
+ * required attribution for using Balatro art.
13
15
  */
14
16
  export declare function JimboBalatroFooter({ hidden, className, children }: JimboBalatroFooterProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,62 @@
1
+ export type JimboTone = 'orange' | 'red' | 'blue' | 'green' | 'tarot' | 'planet' | 'spectral' | 'grey';
2
+ declare const SIZE_TO_DIMS: {
3
+ readonly xs: {
4
+ readonly w: 1.4;
5
+ readonly h: 0.42;
6
+ readonly depth: 0.12;
7
+ readonly fontSize: 0.22;
8
+ };
9
+ readonly sm: {
10
+ readonly w: 1.8;
11
+ readonly h: 0.52;
12
+ readonly depth: 0.14;
13
+ readonly fontSize: 0.26;
14
+ };
15
+ readonly md: {
16
+ readonly w: 2.4;
17
+ readonly h: 0.66;
18
+ readonly depth: 0.16;
19
+ readonly fontSize: 0.3;
20
+ };
21
+ readonly lg: {
22
+ readonly w: 3;
23
+ readonly h: 0.82;
24
+ readonly depth: 0.18;
25
+ readonly fontSize: 0.36;
26
+ };
27
+ };
28
+ export type JimboButtonSize = keyof typeof SIZE_TO_DIMS;
29
+ export interface JimboButtonProps {
30
+ tone?: JimboTone;
31
+ size?: JimboButtonSize;
32
+ disabled?: boolean;
33
+ fullWidth?: boolean;
34
+ onClick?: () => void;
35
+ /** Button label. Non-string children are coerced via `String()`. For
36
+ * buttons that need icons + multi-line text, use a different primitive
37
+ * (e.g. JimboInfoCard with an onClick), not JimboButton. */
38
+ children: React.ReactNode;
39
+ /** Outer wrapper width in pixels. Default sized to the button. */
40
+ canvasWidth?: number;
41
+ /** Outer wrapper height in pixels. Default sized to the button. */
42
+ canvasHeight?: number;
43
+ /** Inline style applied to the outer wrapper div (not the 3D mesh). */
44
+ style?: React.CSSProperties;
45
+ className?: string;
46
+ }
47
+ /**
48
+ * The Jimbo button. R3F-native: each button is its own Canvas with
49
+ * @react-spring/three driving hover lift + click press, magnetic tilt on
50
+ * pointer move, and sub-pixel idle sway at rest. There is no DOM fallback —
51
+ * R3F is the only implementation.
52
+ */
53
+ export declare const JimboButton: import('react').NamedExoticComponent<JimboButtonProps>;
54
+ /**
55
+ * Canonical "back" button. Orange, full-width, label "Back". Used by
56
+ * `JimboPanel` and `JimboModal` when `onBack` / `showBack` is set.
57
+ */
58
+ export declare function JimboBackButton({ onClick, size }: {
59
+ onClick?: () => void;
60
+ size?: JimboButtonSize;
61
+ }): import("react/jsx-runtime").JSX.Element;
62
+ export {};
@@ -0,0 +1,27 @@
1
+ import { JimboTone } from './panel.js';
2
+ export interface JimboCopyButtonProps {
3
+ /** Value placed on the clipboard when the button is clicked. */
4
+ value: string;
5
+ /** Label while idle. Default "Copy". */
6
+ label?: string;
7
+ /** Label briefly shown after a successful copy. Default "Copied". */
8
+ copiedLabel?: string;
9
+ /** Button tone. Default blue for standalone copy actions; seed rows use
10
+ * JimboSeedCopyChip (WeeJoker harvest) instead. */
11
+ tone?: JimboTone;
12
+ /** Button size. Default "sm". */
13
+ size?: 'xs' | 'sm' | 'md' | 'lg';
14
+ /** How long to display the copied label, in ms. Default 1500. */
15
+ copiedDurationMs?: number;
16
+ /** Fired after the clipboard write resolves. */
17
+ onCopy?: () => void;
18
+ className?: string;
19
+ }
20
+ /**
21
+ * The canonical copy-to-clipboard button. Single tone (red by default —
22
+ * stays red through the click; only the label briefly swaps to "Copied").
23
+ * Reusable everywhere a copy action lives: seed copy rows, code blocks,
24
+ * filter sharing, etc. — instead of every consumer rolling its own
25
+ * `<JimboButton onClick={...}>Copy</JimboButton>` + setTimeout dance.
26
+ */
27
+ export declare function JimboCopyButton({ value, label, copiedLabel, tone, size, copiedDurationMs, onCopy, className, }: JimboCopyButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { default as React } from 'react';
2
+ export interface JimboDeckAndStakeSelectorModalProps {
3
+ /** Whether the modal is open. */
4
+ open: boolean;
5
+ /** Called when the user dismisses via the back button. */
6
+ onClose: () => void;
7
+ /** All decks the user can cycle through, in spinner order. */
8
+ decks: string[];
9
+ /** All stakes the user can cycle through, in spinner order. */
10
+ stakes: string[];
11
+ /** Currently selected deck name (must appear in `decks`). */
12
+ deck: string;
13
+ /** Currently selected stake name (must appear in `stakes`). */
14
+ stake: string;
15
+ onDeckChange: (next: string) => void;
16
+ onStakeChange: (next: string) => void;
17
+ /** Modal title text. Default: "Search seeds for". */
18
+ title?: string;
19
+ /** Description rendered under each deck / stake title. */
20
+ deckDescription?: (deck: string) => React.ReactNode;
21
+ stakeDescription?: (stake: string) => React.ReactNode;
22
+ }
23
+ /**
24
+ * Modal that lets a seed hunter pick the deck + stake combination they want
25
+ * to search seeds against. Pure JimboUI composition — `JimboModal`, two
26
+ * `JimboPanelSpinner`s, deck + stake sprites. No motely-wasm dependency; the
27
+ * caller passes deck/stake names as strings (convert from `MotelyDeck` /
28
+ * `MotelyStake` enums at the boundary).
29
+ */
30
+ export declare function JimboDeckAndStakeSelectorModal({ open, onClose, decks, stakes, deck, stake, onDeckChange, onStakeChange, title, deckDescription, stakeDescription, }: JimboDeckAndStakeSelectorModalProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { default as React } from 'react';
2
+ export type JimboDualChipTone = 'blue' | 'red' | 'green' | 'gold' | 'dark';
3
+ export interface JimboDualChipHalf {
4
+ value: React.ReactNode;
5
+ tone: JimboDualChipTone;
6
+ label?: string;
7
+ }
8
+ export interface JimboDualChipProps {
9
+ left: JimboDualChipHalf;
10
+ right: JimboDualChipHalf;
11
+ className?: string;
12
+ }
13
+ /**
14
+ * Two stat halves joined into one pill. Used for the `0 × 0` Hands × Discards
15
+ * chip on Balatro's run-info side panel.
16
+ */
17
+ export declare function JimboDualChip({ left, right, className }: JimboDualChipProps): import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,15 @@
1
1
  import { default as React } from 'react';
2
+ export type JimboIconButtonTone = "default" | "destructive";
2
3
  export interface JimboIconButtonProps {
3
4
  "aria-label"?: string;
4
5
  children: React.ReactNode;
5
6
  disabled?: boolean;
6
- onClick?: () => void;
7
- size?: "sm" | "md";
7
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
8
+ /** Pass-through for drag-start guards (e.g. stopPropagation inside draggable rows). */
9
+ onMouseDown?: (e: React.MouseEvent<HTMLButtonElement>) => void;
10
+ onTouchStart?: (e: React.TouchEvent<HTMLButtonElement>) => void;
11
+ size?: "xs" | "sm" | "md";
12
+ tone?: JimboIconButtonTone;
8
13
  title?: string;
9
14
  }
10
- export declare function JimboIconButton({ onClick, title, "aria-label": ariaLabel, disabled, size, children, }: JimboIconButtonProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function JimboIconButton({ onClick, onMouseDown, onTouchStart, title, "aria-label": ariaLabel, disabled, size, tone, children, }: JimboIconButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { default as React } from 'react';
2
+ export type JimboInlineEditSize = 'xs' | 'sm' | 'md' | 'lg';
3
+ export type JimboInlineEditTone = 'white' | 'gold' | 'grey';
4
+ export interface JimboInlineEditProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
5
+ /** Visual size — echoes JimboText size scale. */
6
+ size?: JimboInlineEditSize;
7
+ tone?: JimboInlineEditTone;
8
+ /** Render at reduced opacity (e.g. description-style sub-text). */
9
+ dim?: boolean;
10
+ }
11
+ /**
12
+ * Borderless transparent input that reads as inline text until focused —
13
+ * for editable titles, bylines, or descriptions inside cards/panels.
14
+ * Focus chrome only shows on keyboard navigation so mouse use stays clean.
15
+ */
16
+ export declare const JimboInlineEdit: React.ForwardRefExoticComponent<JimboInlineEditProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ export interface JimboListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
+ /** Visual + a11y active state. Renders the selected/hover palette. */
4
+ active?: boolean;
5
+ }
6
+ /**
7
+ * Clickable list row. Use anywhere a vertical stack of selectable rows is
8
+ * needed — filter browsers, seed lists, saved-search picker, etc. Composes
9
+ * `.j-list-item` from jimbo.css; pass extra layout inside via children.
10
+ */
11
+ export declare const JimboListItem: React.ForwardRefExoticComponent<JimboListItemProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,8 @@
1
+ export interface JimboPanelSplitterProps {
2
+ "aria-label"?: string;
3
+ className?: string;
4
+ onDrag: (delta: number) => void;
5
+ onKeyAdjust?: (delta: number) => void;
6
+ orientation?: "vertical" | "horizontal";
7
+ }
8
+ export declare function JimboPanelSplitter({ orientation, onDrag, onKeyAdjust, className, "aria-label": ariaLabel, }: JimboPanelSplitterProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { default as React } from 'react';
2
+ export interface JimboSeedCopyChipProps {
3
+ value: string;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ copiedLabel?: string;
7
+ copiedDurationMs?: number;
8
+ onCopy?: (value: string) => void;
9
+ className?: string;
10
+ style?: React.CSSProperties;
11
+ }
12
+ /**
13
+ * Harvested from WeeJoker SeedCard — tap-to-copy seed row with icon + value.
14
+ * Idle: copy icon + seed. Copied: green check + brief confirmation label.
15
+ */
16
+ export declare function JimboSeedCopyChip({ value, placeholder, disabled, copiedLabel, copiedDurationMs, onCopy, className, style, }: JimboSeedCopyChipProps): import("react/jsx-runtime").JSX.Element;
@@ -5,14 +5,26 @@ export interface JimboSelectOption {
5
5
  value: string;
6
6
  }
7
7
  export interface JimboSelectProps {
8
- "aria-label"?: string;
8
+ 'aria-label'?: string;
9
9
  disabled?: boolean;
10
10
  fullWidth?: boolean;
11
11
  onChange: (value: string) => void;
12
12
  options: JimboSelectOption[] | string[];
13
13
  placeholder?: string;
14
- size?: "sm" | "md";
14
+ size?: 'sm' | 'md';
15
15
  style?: React.CSSProperties;
16
16
  value: string;
17
+ label?: string;
17
18
  }
18
- export declare function JimboSelect({ value, options, onChange, placeholder, disabled, fullWidth, size, style, "aria-label": ariaLabel, }: JimboSelectProps): import("react/jsx-runtime").JSX.Element;
19
+ /**
20
+ * Select one of N values. There is no native `<select>` in the Balatro UI
21
+ * language — options are cycled with arrows, not via an OS dropdown. So
22
+ * JimboSelect is a `< value >` JimboSpinner that cycles through the options
23
+ * list. If `value` isn't in `options`, we land on the first option.
24
+ *
25
+ * Was previously a raw `<select>` element with hand-painted gold-arrow CSS
26
+ * gradients. That was off-brand on every axis (native control, gold control
27
+ * affordance, no JimboButton). Rewritten to compose JimboSpinner so it
28
+ * behaves like every other selector in the design system.
29
+ */
30
+ export declare function JimboSelect({ value, options, onChange, disabled, label, 'aria-label': ariaLabel, style, }: JimboSelectProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,11 @@
1
+ export interface JimboSliderProps {
2
+ value: number;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ label?: string;
7
+ onChange?: (value: number) => void;
8
+ className?: string;
9
+ id?: string;
10
+ }
11
+ export declare function JimboSlider({ value, min, max, step, label, onChange, className, id, }: JimboSliderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ export interface JimboSpinnerProps {
2
+ value: string | number;
3
+ label?: string;
4
+ onPrev?: () => void;
5
+ onNext?: () => void;
6
+ canPrev?: boolean;
7
+ canNext?: boolean;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * Two-state-or-more value spinner: red `<` button + value chip + red `>` button.
12
+ * This is the canonical Balatro on/off control (IMG_3678: `Shadows < OFF >`)
13
+ * AND the canonical option-cycler (deck/stake/aesthetic picker).
14
+ *
15
+ * Note: previously named JimboStepper — that was a misnomer. A stepper is a
16
+ * page-dot indicator (see JimboStepper). This is a spinner.
17
+ */
18
+ export declare function JimboSpinner({ value, label, onPrev, onNext, canPrev, canNext, className, }: JimboSpinnerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ export interface JimboStepperProps {
3
+ /** Total number of pages/steps. */
4
+ count: number;
5
+ /** Current page index (0-based). */
6
+ index: number;
7
+ /** Optional click handler to jump to a specific page. */
8
+ onIndexChange?: (i: number) => void;
9
+ /** Label for screen readers describing what the dots track. */
10
+ ariaLabel?: string;
11
+ className?: string;
12
+ style?: React.CSSProperties;
13
+ }
14
+ /**
15
+ * Page-dot indicator. A row of small circles: the active page is a filled
16
+ * white dot; the others are dim grey dots. Matches Balatro's carousel page
17
+ * indicator pattern (deck picker, blind preview, etc.).
18
+ *
19
+ * For the value spinner pattern (`< OFF >`) see JimboSpinner — different
20
+ * component, despite the previous misnomer.
21
+ */
22
+ export declare function JimboStepper({ count, index, onIndexChange, ariaLabel, className, style, }: JimboStepperProps): import("react/jsx-runtime").JSX.Element | null;
@@ -7,5 +7,6 @@ export interface JimboToggleListProps {
7
7
  items: ToggleItem[];
8
8
  onToggle: (id: string) => void;
9
9
  title?: string;
10
+ variant?: 'row' | 'stacked';
10
11
  }
11
- export declare function JimboToggleList({ items, onToggle, title }: JimboToggleListProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function JimboToggleList({ items, onToggle, title, variant }: JimboToggleListProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { default as React } from 'react';
2
+ export interface JimboValueBadgeProps {
3
+ value: number;
4
+ onChange?: (value: number) => void;
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ unit?: string;
9
+ /** Disables click-to-edit; renders as a read-only display badge. */
10
+ readOnly?: boolean;
11
+ className?: string;
12
+ style?: React.CSSProperties;
13
+ }
14
+ /**
15
+ * Red pill displaying a numeric value. Click to edit inline; Enter or blur
16
+ * commits (clamped to [min, max] and snapped to `step`); Escape cancels.
17
+ * Used as the thumb on JimboSlider and as a standalone numeric chip in panels.
18
+ */
19
+ export declare function JimboValueBadge({ value, onChange, min, max, step, unit, readOnly, className, style, }: JimboValueBadgeProps): import("react/jsx-runtime").JSX.Element;
@@ -4,4 +4,8 @@ export interface JimboCodeBlockProps {
4
4
  filename?: string;
5
5
  className?: string;
6
6
  }
7
+ /**
8
+ * Code block with filename + language chip + JimboCopyButton. Pure Jimbo
9
+ * primitives end to end; the copy logic lives in JimboCopyButton.
10
+ */
7
11
  export declare function JimboCodeBlock({ code, language, filename, className }: JimboCodeBlockProps): import("react/jsx-runtime").JSX.Element;
@@ -12,11 +12,36 @@ export declare function useDelayedVisibility(open: boolean, delay: number): {
12
12
  visible: boolean;
13
13
  opacity: number;
14
14
  };
15
+ export type JimboBackgroundColor = string | [number, number, number];
16
+ export interface JimboBackgroundConfig {
17
+ /** Three palette colors. Each can be `#RRGGBB` / `#RGB` or `[r,g,b]` in 0..1. */
18
+ primary?: JimboBackgroundColor;
19
+ secondary?: JimboBackgroundColor;
20
+ dark?: JimboBackgroundColor;
21
+ /** Animation speed multiplier on top of the base SPIN_SPEED. Default 1. */
22
+ speed?: number;
23
+ /** Twirl rotation seed (default -2). */
24
+ spinRotation?: number;
25
+ /** How much the twirl warps the field, 0..1ish (default 0.35). */
26
+ spinAmount?: number;
27
+ /** Pixelation. Higher = finer pixels (default ~244 = 740*0.33). */
28
+ pixelFilter?: number;
29
+ /** Contrast multiplier (default 4.5). */
30
+ contrast?: number;
31
+ /** Bright lighting amount (default 0.5). */
32
+ lighting?: number;
33
+ /** Color transition duration in ms when palette changes. Default 800. */
34
+ transitionMs?: number;
35
+ }
15
36
  /**
16
37
  * Hook for the Balatro hypnotic swirl background.
17
38
  * Manages WebGL context, shader compilation, and animation loop.
39
+ *
40
+ * All shader constants are exposed as uniforms so they can be tuned at
41
+ * runtime. Palette and scalar uniforms (pixelFilter, contrast, spin, etc.)
42
+ * interpolate over `transitionMs` so control changes fade smoothly.
18
43
  */
19
- export declare function useBalatroBackground(): React.RefObject<HTMLCanvasElement | null>;
44
+ export declare function useBalatroBackground(config?: JimboBackgroundConfig): React.RefObject<HTMLCanvasElement | null>;
20
45
  export type JimboTooltipMode = 'snap' | 'mouse';
21
46
  export type JimboTooltipPlacement = 'top' | 'bottom' | 'auto';
22
47
  /**
@@ -87,8 +112,10 @@ export declare function useJamlIdeDrag(filter: JamlVisualFilter, onChange: (filt
87
112
  onDragStart: (e: React.MouseEvent | React.TouchEvent, clause: JamlVisualClause, fromZone: JamlZone) => void;
88
113
  };
89
114
  /**
90
- * Provides a magnetic 3D tilt effect for DOM elements, replicating the 'juice' of Balatro cards.
91
- * Ensures the hit-detection area remains stable by separating container events from the transformed style.
115
+ * Provides a magnetic 3D tilt effect for DOM elements, replicating the hover-follow
116
+ * of Balatro cards. Continuous (tracks the cursor). For 3D/spring-physics button
117
+ * juice prefer `JimboButton3D` from `jaml-ui/r3f`, which uses the canonical
118
+ * pmndrs stack (@react-three/fiber + @react-spring/three).
92
119
  */
93
120
  export declare function useDOMMagneticTilt(enabled?: boolean): {
94
121
  handlers: {