jaml-ui 0.24.20 → 0.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -13
- package/assets/WeeJokerExampleDAilyGame.png +0 -0
- package/assets/balatro-stake-chips.png +0 -0
- package/dist/assets.d.ts +1 -2
- package/dist/chunks/Layer-BBPJFHfs.js +17 -0
- package/dist/chunks/Layer-BBPJFHfs.js.map +1 -0
- package/dist/chunks/assets-RWUiFSTc.js +37 -0
- package/dist/chunks/assets-RWUiFSTc.js.map +1 -0
- package/dist/chunks/motelyItemDecoder-CueyZ0XD.js +6039 -0
- package/dist/chunks/motelyItemDecoder-CueyZ0XD.js.map +1 -0
- package/dist/chunks/spriteMapper-CFjN0_TV.js +2415 -0
- package/dist/chunks/spriteMapper-CFjN0_TV.js.map +1 -0
- package/dist/chunks/tokens-B65Fzble.js +57 -0
- package/dist/chunks/tokens-B65Fzble.js.map +1 -0
- package/dist/chunks/ui-5cBy3zAm.js +1387 -0
- package/dist/chunks/ui-5cBy3zAm.js.map +1 -0
- package/dist/components/AnalyzerExplorer.d.ts +1 -1
- package/dist/components/CardFan.d.ts +1 -1
- package/dist/components/CardList.d.ts +1 -1
- package/dist/components/DeckSprite.d.ts +1 -1
- package/dist/components/JamlAestheticSelector.d.ts +1 -1
- package/dist/components/JamlAnalyzerFullscreen.d.ts +5 -5
- package/dist/components/JamlCurator.d.ts +1 -6
- package/dist/components/JamlIde.d.ts +5 -5
- package/dist/components/JamlSeedInput.d.ts +1 -1
- package/dist/components/JamlSpeedometer.d.ts +1 -1
- package/dist/components/MotelyVersionBadge.d.ts +1 -1
- package/dist/components/Standardcard.d.ts +1 -1
- package/dist/components/jamlMap/CategoryPicker.d.ts +3 -3
- package/dist/components/jamlMap/JamlMapEditor.d.ts +1 -1
- package/dist/components/jamlMap/JokerPicker.d.ts +1 -1
- package/dist/components/jamlMap/MysterySlot.d.ts +2 -2
- package/dist/components/jamlMap/index.d.ts +4 -4
- package/dist/core.d.ts +5 -5
- package/dist/core.js +27 -5
- package/dist/core.js.map +1 -0
- package/dist/decode/motelyItemDecoder.d.ts +0 -10
- package/dist/decode/motelySprite.d.ts +1 -1
- package/dist/fonts/m6x11plus.otf +0 -0
- package/dist/hooks/analyzerStreamRegistry.d.ts +2 -2
- package/dist/hooks/useAnalyzer.d.ts +3 -3
- package/dist/hooks/useSearch.d.ts +2 -2
- package/dist/index.d.ts +29 -31
- package/dist/index.js +16721 -34
- package/dist/index.js.map +1 -0
- package/dist/lib/const.d.ts +2 -2
- package/dist/lib/hooks/useDragScroll.d.ts +1 -1
- package/dist/lib/hooks/useJamlFilter.d.ts +2 -2
- package/dist/lib/hooks/useSeedAnalyzer.d.ts +2 -2
- package/dist/lib/utils.d.ts +1 -1
- package/dist/motely.d.ts +4 -3
- package/dist/motely.js +65 -3
- package/dist/motely.js.map +1 -0
- package/dist/motelyBoot.d.ts +2 -0
- package/dist/motelyDisplay.d.ts +0 -2
- package/dist/r3f/Card3D.d.ts +2 -2
- package/dist/r3f/JimboBillboard.d.ts +1 -1
- package/dist/r3f.js +235 -3
- package/dist/r3f.js.map +1 -0
- package/dist/render/CanvasRenderer.d.ts +1 -1
- package/dist/sprites/spriteData.d.ts +1 -6
- package/dist/sprites/spriteMapper.d.ts +1 -1
- package/dist/ui/JimboBadge.d.ts +1 -1
- package/dist/ui/JimboFloating.d.ts +1 -1
- package/dist/ui/JimboIconButton.d.ts +1 -1
- package/dist/ui/JimboSelect.d.ts +1 -1
- package/dist/ui/footer.d.ts +2 -3
- package/dist/ui/hooks.d.ts +1 -1
- package/dist/ui/ide/DeckSprite.d.ts +1 -1
- package/dist/ui/jimbo.css +2 -1856
- package/dist/ui/jimboApp.d.ts +1 -1
- package/dist/ui/jimboFilterBar.d.ts +1 -1
- package/dist/ui/jimboFlankNav.d.ts +1 -1
- package/dist/ui/jimboInfoCard.d.ts +1 -1
- package/dist/ui/jimboInset.d.ts +1 -1
- package/dist/ui/jimboStatGrid.d.ts +1 -1
- package/dist/ui/jimboText.d.ts +1 -1
- package/dist/ui/jimboTooltip.d.ts +2 -2
- package/dist/ui/mascot/SeedMascot.d.ts +2 -2
- package/dist/ui/panel.d.ts +1 -1
- package/dist/ui/radial/RadialBadge.d.ts +1 -2
- package/dist/ui/radial/RadialButton.d.ts +1 -2
- package/dist/ui/radial/RadialMenu.d.ts +2 -2
- package/dist/ui/radial/RadialPill.d.ts +1 -2
- package/dist/ui/radial/index.d.ts +16 -16
- package/dist/ui/radial/radialMenuStore.d.ts +1 -1
- package/dist/ui/showcase.d.ts +1 -1
- package/dist/ui/sprites.d.ts +2 -2
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +3 -36
- package/dist/utils/gameCardUtils.d.ts +1 -1
- package/dist/utils/jamlVisualFilter.d.ts +1 -7
- package/package.json +13 -25
- package/dist/assets.js +0 -48
- package/dist/components/AnalyzerExplorer.js +0 -391
- package/dist/components/CardFan.js +0 -80
- package/dist/components/CardList.js +0 -5
- package/dist/components/DeckSprite.js +0 -75
- package/dist/components/GameCard.js +0 -355
- package/dist/components/JamlAestheticSelector.js +0 -22
- package/dist/components/JamlAnalyzerFullscreen.js +0 -263
- package/dist/components/JamlCodeEditor.js +0 -137
- package/dist/components/JamlCurator.js +0 -64
- package/dist/components/JamlCurator.stories.d.ts +0 -6
- package/dist/components/JamlCurator.stories.js +0 -14
- package/dist/components/JamlIde.js +0 -193
- package/dist/components/JamlIdeToolbar.js +0 -23
- package/dist/components/JamlIdeVisual.js +0 -218
- package/dist/components/JamlMapPreview.js +0 -121
- package/dist/components/JamlSeedInput.js +0 -26
- package/dist/components/JamlSpeedometer.js +0 -70
- package/dist/components/Jimbolate.js +0 -17
- package/dist/components/MotelyVersionBadge.js +0 -19
- package/dist/components/PaginatedFilterBrowser.js +0 -54
- package/dist/components/RunConfigModal.js +0 -59
- package/dist/components/Standardcard.js +0 -80
- package/dist/components/jamlMap/CategoryPicker.js +0 -135
- package/dist/components/jamlMap/JamlMapEditor.js +0 -304
- package/dist/components/jamlMap/JamlMapEditor.stories.d.ts +0 -7
- package/dist/components/jamlMap/JamlMapEditor.stories.js +0 -26
- package/dist/components/jamlMap/JamlMapEditorDemo.d.ts +0 -8
- package/dist/components/jamlMap/JamlMapEditorDemo.js +0 -323
- package/dist/components/jamlMap/JokerPicker.js +0 -113
- package/dist/components/jamlMap/MysterySlot.js +0 -128
- package/dist/components/jamlMap/MysterySlot.stories.d.ts +0 -7
- package/dist/components/jamlMap/MysterySlot.stories.js +0 -31
- package/dist/components/jamlMap/index.js +0 -4
- package/dist/decode/motelyItemDecoder.js +0 -164
- package/dist/decode/motelySprite.js +0 -84
- package/dist/hooks/analyzerStreamRegistry.js +0 -96
- package/dist/hooks/searchWorker.d.ts +0 -1
- package/dist/hooks/searchWorker.js +0 -119
- package/dist/hooks/searchWorkerCode.d.ts +0 -1
- package/dist/hooks/searchWorkerCode.js +0 -85
- package/dist/hooks/useAnalyzer.js +0 -91
- package/dist/hooks/useIntersectionObserver.js +0 -52
- package/dist/hooks/useSearch.js +0 -161
- package/dist/hooks/useShopStream.js +0 -85
- package/dist/lib/SpriteMapper.js +0 -48
- package/dist/lib/cardParser.js +0 -67
- package/dist/lib/classes/BuyMetaData.js +0 -1
- package/dist/lib/config.js +0 -15
- package/dist/lib/const.js +0 -521
- package/dist/lib/data/constants.js +0 -14
- package/dist/lib/hooks/useDragScroll.js +0 -48
- package/dist/lib/hooks/useJamlFilter.js +0 -219
- package/dist/lib/hooks/useSeedAnalyzer.js +0 -50
- package/dist/lib/jaml/jamlCompletion.js +0 -13
- package/dist/lib/jaml/jamlData.js +0 -6
- package/dist/lib/jaml/jamlObjectives.js +0 -97
- package/dist/lib/jaml/jamlParser.js +0 -47
- package/dist/lib/jaml/jamlPresets.js +0 -61
- package/dist/lib/jaml/jamlSchema.js +0 -91
- package/dist/lib/parseDailyRitual.js +0 -70
- package/dist/lib/tts/getRevealPos.js +0 -16
- package/dist/lib/tts/splitTtsDisplay.js +0 -35
- package/dist/lib/types.js +0 -1
- package/dist/lib/utils.js +0 -5
- package/dist/motelyDisplay.js +0 -59
- package/dist/r3f/Card3D.js +0 -72
- package/dist/r3f/JimboBillboard.js +0 -32
- package/dist/r3f/JimboText3D.js +0 -8
- package/dist/render/CanvasRenderer.js +0 -11
- package/dist/render/Layer.js +0 -18
- package/dist/sprites/spriteData.js +0 -100
- package/dist/sprites/spriteMapper.js +0 -75
- package/dist/stories/Button.d.ts +0 -15
- package/dist/stories/Button.js +0 -7
- package/dist/stories/Button.stories.d.ts +0 -24
- package/dist/stories/Button.stories.js +0 -50
- package/dist/stories/Header.d.ts +0 -12
- package/dist/stories/Header.js +0 -4
- package/dist/stories/Header.stories.d.ts +0 -18
- package/dist/stories/Header.stories.js +0 -26
- package/dist/stories/Page.d.ts +0 -3
- package/dist/stories/Page.js +0 -8
- package/dist/stories/Page.stories.d.ts +0 -12
- package/dist/stories/Page.stories.js +0 -24
- package/dist/ui/Jimbo.stories.d.ts +0 -7
- package/dist/ui/Jimbo.stories.js +0 -28
- package/dist/ui/JimboBadge.js +0 -8
- package/dist/ui/JimboFloating.js +0 -17
- package/dist/ui/JimboIconButton.js +0 -28
- package/dist/ui/JimboInputModal.js +0 -66
- package/dist/ui/JimboSelect.js +0 -43
- package/dist/ui/JimboToggleList.js +0 -5
- package/dist/ui/PanelSplitter.js +0 -78
- package/dist/ui/codeBlock.js +0 -14
- package/dist/ui/footer.js +0 -20
- package/dist/ui/hooks.js +0 -634
- package/dist/ui/ide/AgnosticSeedCard.d.ts +0 -19
- package/dist/ui/ide/AgnosticSeedCard.js +0 -48
- package/dist/ui/ide/DeckSprite.js +0 -2
- package/dist/ui/ide/JamlBuilder.d.ts +0 -1
- package/dist/ui/ide/JamlBuilder.js +0 -112
- package/dist/ui/ide/JamlEditor.js +0 -486
- package/dist/ui/ide/JamlEditorMonaco.d.ts +0 -8
- package/dist/ui/ide/JamlEditorMonaco.js +0 -78
- package/dist/ui/ide/WasmStatus.d.ts +0 -1
- package/dist/ui/ide/WasmStatus.js +0 -42
- package/dist/ui/jimboApp.js +0 -15
- package/dist/ui/jimboBackground.js +0 -27
- package/dist/ui/jimboCopyRow.js +0 -18
- package/dist/ui/jimboFilterBar.js +0 -16
- package/dist/ui/jimboFlankNav.js +0 -18
- package/dist/ui/jimboInfoCard.js +0 -26
- package/dist/ui/jimboInset.js +0 -9
- package/dist/ui/jimboSectionHeader.js +0 -9
- package/dist/ui/jimboStatGrid.js +0 -9
- package/dist/ui/jimboTabs.js +0 -22
- package/dist/ui/jimboText.js +0 -33
- package/dist/ui/jimboTooltip.js +0 -39
- package/dist/ui/jimboWordmark.js +0 -9
- package/dist/ui/mascot/JammySpeechBox.js +0 -30
- package/dist/ui/mascot/SeedMascot.js +0 -17
- package/dist/ui/mascot/index.js +0 -3
- package/dist/ui/mascot/menuConfig.js +0 -12
- package/dist/ui/panel.js +0 -24
- package/dist/ui/radial/RadialBadge.js +0 -43
- package/dist/ui/radial/RadialBreadcrumb.js +0 -18
- package/dist/ui/radial/RadialButton.js +0 -102
- package/dist/ui/radial/RadialMenu.js +0 -168
- package/dist/ui/radial/RadialPill.js +0 -15
- package/dist/ui/radial/index.js +0 -18
- package/dist/ui/radial/radialMenuStore.js +0 -122
- package/dist/ui/radial/radialMenuViewport.js +0 -59
- package/dist/ui/radial/useRadialMenu.js +0 -107
- package/dist/ui/showcase.js +0 -20
- package/dist/ui/sprites.js +0 -77
- package/dist/ui/tokens.js +0 -64
- package/dist/utils/gameCardUtils.js +0 -15
- package/dist/utils/jamlMapPreview.js +0 -106
- package/dist/utils/jamlVisualFilter.js +0 -210
package/dist/ui/hooks.js
DELETED
|
@@ -1,634 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { JIMBO_ANIMATIONS } from './tokens.js';
|
|
3
|
-
import { SPRITE_SHEETS } from '../sprites/spriteData.js';
|
|
4
|
-
function loadImage(url) {
|
|
5
|
-
return new Promise((resolve) => {
|
|
6
|
-
const image = new window.Image();
|
|
7
|
-
image.addEventListener('load', () => resolve(image));
|
|
8
|
-
image.addEventListener('error', () => resolve(null));
|
|
9
|
-
image.src = url;
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
function renderImage(canvas, context, image, layer, timestamp) {
|
|
13
|
-
if (!image || !layer || !layer?.pos)
|
|
14
|
-
return 0;
|
|
15
|
-
const cardWidth = image.width / layer.columns;
|
|
16
|
-
const cardHeight = image.height / layer.rows;
|
|
17
|
-
const canvasStyle = canvas.style;
|
|
18
|
-
if (layer.order === 0) {
|
|
19
|
-
canvas.width = cardWidth;
|
|
20
|
-
canvas.height = cardHeight;
|
|
21
|
-
canvasStyle.width = `${cardWidth}px`;
|
|
22
|
-
canvasStyle.height = `${cardHeight}px`;
|
|
23
|
-
}
|
|
24
|
-
canvasStyle.imageRendering = 'pixelated';
|
|
25
|
-
context.imageSmoothingEnabled = true;
|
|
26
|
-
context.save();
|
|
27
|
-
if (layer.animated && timestamp) {
|
|
28
|
-
const elapsed = timestamp;
|
|
29
|
-
const yOffset = Math.sin(elapsed / 1000) * 3;
|
|
30
|
-
const xOffset = Math.sin(elapsed / 1500) * 1.5;
|
|
31
|
-
context.globalAlpha = 0.65 + (Math.sin(elapsed / 2000) + 1) * 0.075;
|
|
32
|
-
context.translate(xOffset, yOffset);
|
|
33
|
-
}
|
|
34
|
-
context.drawImage(image, layer.pos.x * cardWidth, layer.pos.y * cardHeight, cardWidth, cardHeight, 0, 0, canvas.width, canvas.height);
|
|
35
|
-
context.restore();
|
|
36
|
-
return cardWidth / cardHeight;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Sway animation for Balatro-style UI elements.
|
|
40
|
-
*/
|
|
41
|
-
export function useSway(active) {
|
|
42
|
-
const ref = useRef(null);
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (!active || !ref.current)
|
|
45
|
-
return;
|
|
46
|
-
let frame;
|
|
47
|
-
const start = Date.now();
|
|
48
|
-
const el = ref.current;
|
|
49
|
-
const tick = () => {
|
|
50
|
-
const t = ((Date.now() - start) % JIMBO_ANIMATIONS.SWAY_DURATION) / JIMBO_ANIMATIONS.SWAY_DURATION * Math.PI * 2;
|
|
51
|
-
el.style.transform = `translate(${Math.sin(t) * JIMBO_ANIMATIONS.SWAY_AMOUNT * 0.3}px, ${Math.sin(t * 0.8) * JIMBO_ANIMATIONS.SWAY_AMOUNT}px)`;
|
|
52
|
-
frame = requestAnimationFrame(tick);
|
|
53
|
-
};
|
|
54
|
-
frame = requestAnimationFrame(tick);
|
|
55
|
-
return () => {
|
|
56
|
-
cancelAnimationFrame(frame);
|
|
57
|
-
if (el)
|
|
58
|
-
el.style.transform = '';
|
|
59
|
-
};
|
|
60
|
-
}, [active]);
|
|
61
|
-
return ref;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Handles delayed visibility for transitions (e.g. modals).
|
|
65
|
-
*/
|
|
66
|
-
export function useDelayedVisibility(open, delay) {
|
|
67
|
-
const [visible, setVisible] = useState(open);
|
|
68
|
-
const [opacity, setOpacity] = useState(open ? 1 : 0);
|
|
69
|
-
const [prevOpen, setPrevOpen] = useState(open);
|
|
70
|
-
if (open !== prevOpen) {
|
|
71
|
-
setPrevOpen(open);
|
|
72
|
-
if (open) {
|
|
73
|
-
setVisible(true);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
setOpacity(0);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
if (open) {
|
|
81
|
-
const frame = requestAnimationFrame(() => setOpacity(1));
|
|
82
|
-
return () => cancelAnimationFrame(frame);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const t = setTimeout(() => setVisible(false), delay);
|
|
86
|
-
return () => clearTimeout(t);
|
|
87
|
-
}
|
|
88
|
-
}, [open, delay]);
|
|
89
|
-
return { visible, opacity };
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Hook for the Balatro hypnotic swirl background.
|
|
93
|
-
* Manages WebGL context, shader compilation, and animation loop.
|
|
94
|
-
*/
|
|
95
|
-
export function useBalatroBackground() {
|
|
96
|
-
const canvasRef = useRef(null);
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
const canvas = canvasRef.current;
|
|
99
|
-
if (!canvas)
|
|
100
|
-
return;
|
|
101
|
-
const gl = canvas.getContext('webgl');
|
|
102
|
-
if (!gl)
|
|
103
|
-
return;
|
|
104
|
-
const vsSource = `
|
|
105
|
-
attribute vec2 position;
|
|
106
|
-
void main() {
|
|
107
|
-
gl_Position = vec4(position, 0.0, 1.0);
|
|
108
|
-
}
|
|
109
|
-
`;
|
|
110
|
-
const fsSource = `
|
|
111
|
-
precision mediump float;
|
|
112
|
-
|
|
113
|
-
uniform float u_time;
|
|
114
|
-
uniform vec2 u_resolution;
|
|
115
|
-
|
|
116
|
-
const float SPIN_ROTATION = -2.0;
|
|
117
|
-
const float SPIN_SPEED = 4.5;
|
|
118
|
-
const vec4 COLOUR_1 = vec4(1.0, 0.2, 0.2, 1.0);
|
|
119
|
-
const vec4 COLOUR_2 = vec4(0.0, 0.5, 1.0, 1.0);
|
|
120
|
-
const vec4 COLOUR_3 = vec4(0.05, 0.08, 0.1, 1.0);
|
|
121
|
-
const float CONTRAST = 4.5;
|
|
122
|
-
const float LIGTHING = 0.5;
|
|
123
|
-
const float SPIN_AMOUNT = 0.35;
|
|
124
|
-
const float PIXEL_FILTER = 1024.0;
|
|
125
|
-
const float PI = 3.14159265359;
|
|
126
|
-
|
|
127
|
-
void main() {
|
|
128
|
-
vec2 screenSize = u_resolution;
|
|
129
|
-
float pixel_size = length(screenSize.xy) / PIXEL_FILTER;
|
|
130
|
-
vec2 uv = (floor(gl_FragCoord.xy*(1.0/pixel_size))*pixel_size - 0.5*screenSize.xy)/length(screenSize.xy);
|
|
131
|
-
float uv_len = length(uv);
|
|
132
|
-
|
|
133
|
-
float speed = (SPIN_ROTATION * 0.2) + 302.2;
|
|
134
|
-
float new_pixel_angle = atan(uv.y, uv.x) + speed - 20.0*(1.0*SPIN_AMOUNT*uv_len + (1.0 - 1.0*SPIN_AMOUNT));
|
|
135
|
-
|
|
136
|
-
vec2 mid = (screenSize.xy/length(screenSize.xy))/2.0;
|
|
137
|
-
uv = (vec2((uv_len * cos(new_pixel_angle) + mid.x), (uv_len * sin(new_pixel_angle) + mid.y)) - mid);
|
|
138
|
-
|
|
139
|
-
uv *= 30.0;
|
|
140
|
-
speed = u_time * SPIN_SPEED;
|
|
141
|
-
vec2 uv2 = vec2(uv.x, uv.y);
|
|
142
|
-
|
|
143
|
-
for(int i=0; i < 5; i++) {
|
|
144
|
-
uv2 += sin(max(uv.x, uv.y)) + uv;
|
|
145
|
-
uv += 0.5*vec2(cos(5.1123314 + 0.353*uv2.y + speed*0.131121), sin(uv2.x - 0.113*speed));
|
|
146
|
-
uv -= 1.0*cos(uv.x + uv.y) - 1.0*sin(uv.x*0.711 - uv.y);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
float contrast_mod = (0.25*CONTRAST + 0.5*SPIN_AMOUNT + 1.2);
|
|
150
|
-
float paint_res = min(2.0, max(0.0, length(uv)*(0.035)*contrast_mod));
|
|
151
|
-
float c1p = max(0.0, 1.0 - contrast_mod*abs(1.0 - paint_res));
|
|
152
|
-
float c2p = max(0.0, 1.0 - contrast_mod*abs(paint_res));
|
|
153
|
-
float c3p = 1.0 - min(1.0, c1p + c2p);
|
|
154
|
-
float light = (LIGTHING - 0.2)*max(c1p*5.0 - 4.0, 0.0) + LIGTHING*max(c2p*5.0 - 4.0, 0.0);
|
|
155
|
-
|
|
156
|
-
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;
|
|
157
|
-
|
|
158
|
-
gl_FragColor = finalCol;
|
|
159
|
-
}
|
|
160
|
-
`;
|
|
161
|
-
const createShader = (type, source) => {
|
|
162
|
-
const shader = gl.createShader(type);
|
|
163
|
-
if (!shader)
|
|
164
|
-
return null;
|
|
165
|
-
gl.shaderSource(shader, source);
|
|
166
|
-
gl.compileShader(shader);
|
|
167
|
-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
168
|
-
console.error('[JimboBackground] shader compile error:', gl.getShaderInfoLog(shader));
|
|
169
|
-
gl.deleteShader(shader);
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
return shader;
|
|
173
|
-
};
|
|
174
|
-
const vertexShader = createShader(gl.VERTEX_SHADER, vsSource);
|
|
175
|
-
const fragmentShader = createShader(gl.FRAGMENT_SHADER, fsSource);
|
|
176
|
-
if (!vertexShader || !fragmentShader)
|
|
177
|
-
return;
|
|
178
|
-
const program = gl.createProgram();
|
|
179
|
-
if (!program)
|
|
180
|
-
return;
|
|
181
|
-
gl.attachShader(program, vertexShader);
|
|
182
|
-
gl.attachShader(program, fragmentShader);
|
|
183
|
-
gl.linkProgram(program);
|
|
184
|
-
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
185
|
-
console.error('[JimboBackground] program link error:', gl.getProgramInfoLog(program));
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
gl.useProgram(program);
|
|
189
|
-
const positionBuffer = gl.createBuffer();
|
|
190
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
191
|
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
192
|
-
-1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
|
|
193
|
-
-1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
|
|
194
|
-
]), gl.STATIC_DRAW);
|
|
195
|
-
const positionLocation = gl.getAttribLocation(program, 'position');
|
|
196
|
-
gl.enableVertexAttribArray(positionLocation);
|
|
197
|
-
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
198
|
-
const timeLocation = gl.getUniformLocation(program, 'u_time');
|
|
199
|
-
const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
|
|
200
|
-
const startTime = Date.now();
|
|
201
|
-
let animationFrameId;
|
|
202
|
-
const render = () => {
|
|
203
|
-
const displayWidth = canvas.clientWidth;
|
|
204
|
-
const displayHeight = canvas.clientHeight;
|
|
205
|
-
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
|
|
206
|
-
canvas.width = displayWidth;
|
|
207
|
-
canvas.height = displayHeight;
|
|
208
|
-
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
209
|
-
}
|
|
210
|
-
const currentTime = (Date.now() - startTime) / 1000.0;
|
|
211
|
-
gl.uniform1f(timeLocation, currentTime);
|
|
212
|
-
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
213
|
-
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
214
|
-
animationFrameId = requestAnimationFrame(render);
|
|
215
|
-
};
|
|
216
|
-
render();
|
|
217
|
-
return () => {
|
|
218
|
-
cancelAnimationFrame(animationFrameId);
|
|
219
|
-
gl.deleteProgram(program);
|
|
220
|
-
gl.deleteShader(vertexShader);
|
|
221
|
-
gl.deleteShader(fragmentShader);
|
|
222
|
-
gl.deleteBuffer(positionBuffer);
|
|
223
|
-
};
|
|
224
|
-
}, []);
|
|
225
|
-
return canvasRef;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Hook for managing JimboTooltip state and positioning.
|
|
229
|
-
*/
|
|
230
|
-
export function useJimboTooltip({ mode = 'snap', placement = 'auto', delay = 80, disabled = false, }) {
|
|
231
|
-
const [visible, setVisible] = useState(false);
|
|
232
|
-
const [pos, setPos] = useState(null);
|
|
233
|
-
const targetRef = useRef(null);
|
|
234
|
-
const tooltipRef = useRef(null);
|
|
235
|
-
const delayTimerRef = useRef(null);
|
|
236
|
-
const show = useCallback(() => {
|
|
237
|
-
if (disabled)
|
|
238
|
-
return;
|
|
239
|
-
if (delayTimerRef.current)
|
|
240
|
-
clearTimeout(delayTimerRef.current);
|
|
241
|
-
delayTimerRef.current = setTimeout(() => setVisible(true), delay);
|
|
242
|
-
}, [disabled, delay]);
|
|
243
|
-
const hide = useCallback(() => {
|
|
244
|
-
if (delayTimerRef.current)
|
|
245
|
-
clearTimeout(delayTimerRef.current);
|
|
246
|
-
setVisible(false);
|
|
247
|
-
setPos(null);
|
|
248
|
-
}, []);
|
|
249
|
-
const computeSnapPos = useCallback(() => {
|
|
250
|
-
const el = targetRef.current;
|
|
251
|
-
const tip = tooltipRef.current;
|
|
252
|
-
if (!el || !tip)
|
|
253
|
-
return;
|
|
254
|
-
const rect = el.getBoundingClientRect();
|
|
255
|
-
const tipRect = tip.getBoundingClientRect();
|
|
256
|
-
const roomAbove = rect.top;
|
|
257
|
-
const align = placement === 'top' ? 'top'
|
|
258
|
-
: placement === 'bottom' ? 'bottom'
|
|
259
|
-
: roomAbove >= tipRect.height + 12 ? 'top' : 'bottom';
|
|
260
|
-
const left = rect.left + rect.width / 2 - tipRect.width / 2;
|
|
261
|
-
const top = align === 'top' ? rect.top - tipRect.height - 8 : rect.bottom + 8;
|
|
262
|
-
setPos({
|
|
263
|
-
left: Math.max(8, Math.min(window.innerWidth - tipRect.width - 8, left)),
|
|
264
|
-
top,
|
|
265
|
-
align
|
|
266
|
-
});
|
|
267
|
-
}, [placement]);
|
|
268
|
-
useEffect(() => {
|
|
269
|
-
if (!visible || mode !== 'snap')
|
|
270
|
-
return;
|
|
271
|
-
const raf = requestAnimationFrame(computeSnapPos);
|
|
272
|
-
window.addEventListener('resize', computeSnapPos);
|
|
273
|
-
window.addEventListener('scroll', computeSnapPos, true);
|
|
274
|
-
return () => {
|
|
275
|
-
cancelAnimationFrame(raf);
|
|
276
|
-
window.removeEventListener('resize', computeSnapPos);
|
|
277
|
-
window.removeEventListener('scroll', computeSnapPos, true);
|
|
278
|
-
};
|
|
279
|
-
}, [visible, mode, computeSnapPos]);
|
|
280
|
-
useEffect(() => () => {
|
|
281
|
-
if (delayTimerRef.current)
|
|
282
|
-
clearTimeout(delayTimerRef.current);
|
|
283
|
-
}, []);
|
|
284
|
-
const handleMouseMove = useCallback((e) => {
|
|
285
|
-
if (mode !== 'mouse')
|
|
286
|
-
return;
|
|
287
|
-
setPos({ left: e.clientX + 12, top: e.clientY + 16, align: 'bottom' });
|
|
288
|
-
}, [mode]);
|
|
289
|
-
return {
|
|
290
|
-
visible,
|
|
291
|
-
pos,
|
|
292
|
-
targetRef,
|
|
293
|
-
tooltipRef,
|
|
294
|
-
show,
|
|
295
|
-
hide,
|
|
296
|
-
handleMouseMove,
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Hook for managing the JamlCardRenderer logic.
|
|
301
|
-
*/
|
|
302
|
-
export function useJamlCardRenderer({ layers, invert = false, hoverTilt = false, }) {
|
|
303
|
-
const canvasRef = useRef(null);
|
|
304
|
-
const imageCacheRef = useRef(new Map());
|
|
305
|
-
const [ratio, setRatio] = useState(3 / 4);
|
|
306
|
-
const [, forceUpdate] = useState(0);
|
|
307
|
-
const animationFrameRef = useRef(null);
|
|
308
|
-
const [elapsed, setElapsed] = useState(0);
|
|
309
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
310
|
-
const [transform, setTransform] = useState('none');
|
|
311
|
-
const hasAnimatedLayer = layers?.some((layer) => layer.animated);
|
|
312
|
-
// Preload all known sheets once
|
|
313
|
-
useEffect(() => {
|
|
314
|
-
let cancelled = false;
|
|
315
|
-
const imageCache = imageCacheRef.current;
|
|
316
|
-
const preload = async () => {
|
|
317
|
-
const urls = Array.from(new Set(Object.values(SPRITE_SHEETS).map((sheet) => sheet.src)));
|
|
318
|
-
const images = await Promise.all(urls.map((url) => loadImage(url)));
|
|
319
|
-
if (cancelled)
|
|
320
|
-
return;
|
|
321
|
-
images.forEach((image, index) => {
|
|
322
|
-
if (image)
|
|
323
|
-
imageCache.set(urls[index], image);
|
|
324
|
-
});
|
|
325
|
-
forceUpdate((prev) => prev + 1);
|
|
326
|
-
};
|
|
327
|
-
preload().catch((err) => console.error('[JamlCardRenderer]', err));
|
|
328
|
-
return () => {
|
|
329
|
-
cancelled = true;
|
|
330
|
-
imageCache.clear();
|
|
331
|
-
};
|
|
332
|
-
}, []);
|
|
333
|
-
// Animation loop for animated layers
|
|
334
|
-
useEffect(() => {
|
|
335
|
-
if (!hasAnimatedLayer)
|
|
336
|
-
return;
|
|
337
|
-
let startTime;
|
|
338
|
-
const animate = (timestamp) => {
|
|
339
|
-
if (!startTime)
|
|
340
|
-
startTime = timestamp;
|
|
341
|
-
const now = timestamp - startTime;
|
|
342
|
-
if (!animationFrameRef.current || timestamp - 100 > animationFrameRef.current) {
|
|
343
|
-
animationFrameRef.current = timestamp;
|
|
344
|
-
setElapsed(now);
|
|
345
|
-
}
|
|
346
|
-
animationFrameRef.current = requestAnimationFrame(animate);
|
|
347
|
-
};
|
|
348
|
-
animationFrameRef.current = requestAnimationFrame(animate);
|
|
349
|
-
return () => {
|
|
350
|
-
if (animationFrameRef.current)
|
|
351
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
352
|
-
};
|
|
353
|
-
}, [hasAnimatedLayer]);
|
|
354
|
-
// Core drawing logic
|
|
355
|
-
useEffect(() => {
|
|
356
|
-
const canvas = canvasRef.current;
|
|
357
|
-
if (!canvas || !layers || layers.length === 0)
|
|
358
|
-
return;
|
|
359
|
-
const context = canvas.getContext('2d');
|
|
360
|
-
if (!context)
|
|
361
|
-
return;
|
|
362
|
-
let cancelled = false;
|
|
363
|
-
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
364
|
-
[...layers]
|
|
365
|
-
.sort((a, b) => a.order - b.order)
|
|
366
|
-
.forEach((layer) => {
|
|
367
|
-
if (imageCacheRef.current.has(layer.source)) {
|
|
368
|
-
const image = imageCacheRef.current.get(layer.source);
|
|
369
|
-
if (!image)
|
|
370
|
-
return;
|
|
371
|
-
const imageRatio = renderImage(canvas, context, image, layer, hasAnimatedLayer ? elapsed : undefined);
|
|
372
|
-
if (layer.order === 0)
|
|
373
|
-
setRatio(imageRatio);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
loadImage(layer.source).then((img) => {
|
|
377
|
-
if (cancelled || !img)
|
|
378
|
-
return;
|
|
379
|
-
const imageRatio = renderImage(canvas, context, img, layer, hasAnimatedLayer ? elapsed : undefined);
|
|
380
|
-
imageCacheRef.current.set(layer.source, img);
|
|
381
|
-
if (layer.order === 0)
|
|
382
|
-
setRatio(imageRatio);
|
|
383
|
-
forceUpdate((prev) => prev + 1);
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
canvas.style.filter = invert ? 'invert(0.94)' : 'none';
|
|
387
|
-
return () => { cancelled = true; };
|
|
388
|
-
}, [layers, elapsed, invert, hasAnimatedLayer]);
|
|
389
|
-
const onPointerEnter = (event) => {
|
|
390
|
-
if (!hoverTilt || event.pointerType === 'touch')
|
|
391
|
-
return;
|
|
392
|
-
setIsHovered(true);
|
|
393
|
-
};
|
|
394
|
-
const onPointerLeave = () => {
|
|
395
|
-
if (!hoverTilt)
|
|
396
|
-
return;
|
|
397
|
-
setIsHovered(false);
|
|
398
|
-
setTransform('none');
|
|
399
|
-
};
|
|
400
|
-
const onPointerMove = (event) => {
|
|
401
|
-
if (!hoverTilt || event.pointerType === 'touch')
|
|
402
|
-
return;
|
|
403
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
404
|
-
const x = event.clientX - rect.left;
|
|
405
|
-
const y = event.clientY - rect.top;
|
|
406
|
-
const rotateY = (x / rect.width) * 12 - 6;
|
|
407
|
-
const rotateX = (y / rect.height) * -16 + 8;
|
|
408
|
-
const juiceScale = 1.05;
|
|
409
|
-
const juiceY = -2; // slight move up
|
|
410
|
-
setTransform(`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${juiceScale}) translateY(${juiceY}px)`);
|
|
411
|
-
};
|
|
412
|
-
const containerStyle = {
|
|
413
|
-
aspectRatio: String(ratio),
|
|
414
|
-
width: '100%',
|
|
415
|
-
display: 'flex',
|
|
416
|
-
perspective: hoverTilt ? '1000px' : undefined,
|
|
417
|
-
userSelect: 'none',
|
|
418
|
-
WebkitUserSelect: 'none',
|
|
419
|
-
};
|
|
420
|
-
const canvasStyle = {
|
|
421
|
-
transition: hoverTilt && !isHovered ? 'transform 0.4s ease, box-shadow 0.4s ease-out' : 'transform 0.1s ease-out',
|
|
422
|
-
transform: hoverTilt ? (isHovered ? transform : 'none') : undefined,
|
|
423
|
-
transformStyle: hoverTilt ? 'preserve-3d' : undefined,
|
|
424
|
-
transformOrigin: hoverTilt ? 'center center' : undefined,
|
|
425
|
-
borderRadius: '6px',
|
|
426
|
-
boxShadow: hoverTilt && isHovered ? '0 2px 12px rgba(0,0,0,0.3)' : '0 2px 8px rgba(0,0,0,0.2)',
|
|
427
|
-
imageRendering: 'pixelated',
|
|
428
|
-
pointerEvents: 'none',
|
|
429
|
-
};
|
|
430
|
-
return {
|
|
431
|
-
canvasRef,
|
|
432
|
-
containerStyle,
|
|
433
|
-
canvasStyle,
|
|
434
|
-
handlers: {
|
|
435
|
-
onPointerEnter: hoverTilt ? onPointerEnter : undefined,
|
|
436
|
-
onPointerLeave: hoverTilt ? onPointerLeave : undefined,
|
|
437
|
-
onPointerMove: hoverTilt ? onPointerMove : undefined,
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* Tracks which 'ante' section is currently most visible in a scrollable container.
|
|
443
|
-
* Used in JamlAnalyzerFullscreen and AnalyzerExplorer.
|
|
444
|
-
*/
|
|
445
|
-
export function useAnteTracker(antes, options = {}) {
|
|
446
|
-
const [currentAnte, setCurrentAnte] = useState(antes[0]?.ante ?? 0);
|
|
447
|
-
const [prevFirstAnte, setPrevFirstAnte] = useState(antes[0]?.ante);
|
|
448
|
-
const scrollRef = useRef(null);
|
|
449
|
-
const anteRefs = useRef(new Map());
|
|
450
|
-
if (antes[0]?.ante !== prevFirstAnte) {
|
|
451
|
-
setPrevFirstAnte(antes[0]?.ante);
|
|
452
|
-
if (antes.length > 0) {
|
|
453
|
-
setCurrentAnte(antes[0].ante);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
useEffect(() => {
|
|
457
|
-
const root = scrollRef.current;
|
|
458
|
-
if (!root || antes.length === 0)
|
|
459
|
-
return;
|
|
460
|
-
const observer = new IntersectionObserver((entries) => {
|
|
461
|
-
// Find the entry with the highest intersection ratio
|
|
462
|
-
const mostVisible = entries
|
|
463
|
-
.filter((e) => e.isIntersecting)
|
|
464
|
-
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
|
|
465
|
-
if (mostVisible) {
|
|
466
|
-
const ante = Number(mostVisible.target.dataset.ante);
|
|
467
|
-
if (!Number.isNaN(ante)) {
|
|
468
|
-
setCurrentAnte(ante);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}, {
|
|
472
|
-
root,
|
|
473
|
-
threshold: options.threshold ?? [0.4, 0.6, 0.8],
|
|
474
|
-
});
|
|
475
|
-
anteRefs.current.forEach((el) => observer.observe(el));
|
|
476
|
-
return () => observer.disconnect();
|
|
477
|
-
}, [antes, options.threshold]);
|
|
478
|
-
const scrollToAnte = useCallback((ante) => {
|
|
479
|
-
const el = anteRefs.current.get(ante);
|
|
480
|
-
if (el) {
|
|
481
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
482
|
-
}
|
|
483
|
-
}, []);
|
|
484
|
-
const registerAnteRef = useCallback((ante, el) => {
|
|
485
|
-
if (el) {
|
|
486
|
-
anteRefs.current.set(ante, el);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
anteRefs.current.delete(ante);
|
|
490
|
-
}
|
|
491
|
-
}, []);
|
|
492
|
-
return {
|
|
493
|
-
currentAnte,
|
|
494
|
-
scrollRef,
|
|
495
|
-
scrollToAnte,
|
|
496
|
-
registerAnteRef,
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Manages drag-and-drop state for the Jaml IDE visual filter editor.
|
|
501
|
-
*/
|
|
502
|
-
export function useJamlIdeDrag(filter, onChange, rootRef) {
|
|
503
|
-
const [drag, setDrag] = useState(null);
|
|
504
|
-
const [pendingDrag, setPendingDrag] = useState(null);
|
|
505
|
-
const [hoverZone, setHoverZone] = useState(null);
|
|
506
|
-
const onDragStart = useCallback((e, clause, fromZone) => {
|
|
507
|
-
const t = 'touches' in e ? e.touches[0] : e;
|
|
508
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
509
|
-
setPendingDrag({
|
|
510
|
-
clause,
|
|
511
|
-
fromZone,
|
|
512
|
-
x: t.clientX,
|
|
513
|
-
y: t.clientY,
|
|
514
|
-
offX: t.clientX - rect.left,
|
|
515
|
-
offY: t.clientY - rect.top,
|
|
516
|
-
});
|
|
517
|
-
setHoverZone(null);
|
|
518
|
-
}, []);
|
|
519
|
-
useEffect(() => {
|
|
520
|
-
if (!pendingDrag && !drag)
|
|
521
|
-
return;
|
|
522
|
-
const move = (e) => {
|
|
523
|
-
const touchEvent = 'touches' in e ? e : null;
|
|
524
|
-
const t = touchEvent ? touchEvent.touches[0] : e;
|
|
525
|
-
if (!t)
|
|
526
|
-
return;
|
|
527
|
-
let activeDrag = drag;
|
|
528
|
-
if (!activeDrag && pendingDrag) {
|
|
529
|
-
const dx = t.clientX - pendingDrag.x;
|
|
530
|
-
const dy = t.clientY - pendingDrag.y;
|
|
531
|
-
if (Math.hypot(dx, dy) < 8)
|
|
532
|
-
return;
|
|
533
|
-
activeDrag = {
|
|
534
|
-
...pendingDrag,
|
|
535
|
-
x: t.clientX,
|
|
536
|
-
y: t.clientY,
|
|
537
|
-
};
|
|
538
|
-
setPendingDrag(null);
|
|
539
|
-
setDrag(activeDrag);
|
|
540
|
-
}
|
|
541
|
-
else if (activeDrag) {
|
|
542
|
-
activeDrag = { ...activeDrag, x: t.clientX, y: t.clientY };
|
|
543
|
-
setDrag(activeDrag);
|
|
544
|
-
}
|
|
545
|
-
if (!activeDrag)
|
|
546
|
-
return;
|
|
547
|
-
if (touchEvent?.cancelable)
|
|
548
|
-
touchEvent.preventDefault();
|
|
549
|
-
const rails = rootRef.current?.querySelectorAll('[data-zone]') ?? [];
|
|
550
|
-
let found = null;
|
|
551
|
-
for (const r of rails) {
|
|
552
|
-
const rc = r.getBoundingClientRect();
|
|
553
|
-
if (t.clientX >= rc.left && t.clientX <= rc.right && t.clientY >= rc.top && t.clientY <= rc.bottom) {
|
|
554
|
-
found = r.getAttribute('data-zone');
|
|
555
|
-
break;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
setHoverZone(found);
|
|
559
|
-
};
|
|
560
|
-
const up = () => {
|
|
561
|
-
if (drag && hoverZone && hoverZone !== drag.fromZone) {
|
|
562
|
-
const to = hoverZone;
|
|
563
|
-
onChange({
|
|
564
|
-
...filter,
|
|
565
|
-
[drag.fromZone]: filter[drag.fromZone].filter((c) => c.id !== drag.clause.id),
|
|
566
|
-
[to]: [...filter[to], { ...drag.clause }],
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
setPendingDrag(null);
|
|
570
|
-
setDrag(null);
|
|
571
|
-
setHoverZone(null);
|
|
572
|
-
};
|
|
573
|
-
window.addEventListener('mousemove', move);
|
|
574
|
-
window.addEventListener('mouseup', up);
|
|
575
|
-
window.addEventListener('touchmove', move, { passive: false });
|
|
576
|
-
window.addEventListener('touchend', up);
|
|
577
|
-
return () => {
|
|
578
|
-
window.removeEventListener('mousemove', move);
|
|
579
|
-
window.removeEventListener('mouseup', up);
|
|
580
|
-
window.removeEventListener('touchmove', move);
|
|
581
|
-
window.removeEventListener('touchend', up);
|
|
582
|
-
};
|
|
583
|
-
}, [pendingDrag, drag, hoverZone, filter, onChange, rootRef]);
|
|
584
|
-
return {
|
|
585
|
-
drag,
|
|
586
|
-
hoverZone,
|
|
587
|
-
onDragStart,
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* Provides a magnetic 3D tilt effect for DOM elements, replicating the 'juice' of Balatro cards.
|
|
592
|
-
* Ensures the hit-detection area remains stable by separating container events from the transformed style.
|
|
593
|
-
*/
|
|
594
|
-
export function useDOMMagneticTilt(enabled = true) {
|
|
595
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
596
|
-
const [transform, setTransform] = useState('none');
|
|
597
|
-
const onPointerEnter = (event) => {
|
|
598
|
-
if (!enabled || event.pointerType === 'touch')
|
|
599
|
-
return;
|
|
600
|
-
setIsHovered(true);
|
|
601
|
-
};
|
|
602
|
-
const onPointerLeave = () => {
|
|
603
|
-
if (!enabled)
|
|
604
|
-
return;
|
|
605
|
-
setIsHovered(false);
|
|
606
|
-
setTransform('none');
|
|
607
|
-
};
|
|
608
|
-
const onPointerMove = (event) => {
|
|
609
|
-
if (!enabled || event.pointerType === 'touch')
|
|
610
|
-
return;
|
|
611
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
612
|
-
const x = event.clientX - rect.left;
|
|
613
|
-
const y = event.clientY - rect.top;
|
|
614
|
-
const rotateY = (x / rect.width) * 12 - 6;
|
|
615
|
-
const rotateX = (y / rect.height) * -16 + 8;
|
|
616
|
-
const juiceScale = 1.05;
|
|
617
|
-
const juiceY = -2; // slight move up
|
|
618
|
-
setTransform(`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${juiceScale}) translateY(${juiceY}px)`);
|
|
619
|
-
};
|
|
620
|
-
const handlers = {
|
|
621
|
-
onPointerEnter: enabled ? onPointerEnter : undefined,
|
|
622
|
-
onPointerLeave: enabled ? onPointerLeave : undefined,
|
|
623
|
-
onPointerMove: enabled ? onPointerMove : undefined,
|
|
624
|
-
};
|
|
625
|
-
const tiltStyle = {
|
|
626
|
-
transition: enabled && !isHovered ? 'transform 0.4s ease, box-shadow 0.4s ease-out' : 'transform 0.1s ease-out',
|
|
627
|
-
transform: enabled ? (isHovered ? transform : 'none') : undefined,
|
|
628
|
-
transformStyle: enabled ? 'preserve-3d' : undefined,
|
|
629
|
-
transformOrigin: enabled ? 'center center' : undefined,
|
|
630
|
-
willChange: enabled ? 'transform' : undefined,
|
|
631
|
-
pointerEvents: enabled ? 'none' : undefined,
|
|
632
|
-
};
|
|
633
|
-
return { handlers, tiltStyle, isHovered };
|
|
634
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
interface AgnosticSeedCardProps {
|
|
2
|
-
seed: string;
|
|
3
|
-
deckSlug?: string;
|
|
4
|
-
stakeSlug?: string;
|
|
5
|
-
className?: string;
|
|
6
|
-
onClick?: () => void;
|
|
7
|
-
analysis?: any;
|
|
8
|
-
result?: any;
|
|
9
|
-
dayNumber?: number;
|
|
10
|
-
ritualId?: string;
|
|
11
|
-
jamlConfig?: string | null;
|
|
12
|
-
isLocked?: boolean;
|
|
13
|
-
onShowHowTo?: () => void;
|
|
14
|
-
onOpenSubmit?: () => void;
|
|
15
|
-
canSubmit?: boolean;
|
|
16
|
-
filter?: any;
|
|
17
|
-
}
|
|
18
|
-
export declare function AgnosticSeedCard({ seed, deckSlug, stakeSlug, isLocked, dayNumber, className, onClick, analysis: propAnalysis, result: propResult, filter }: AgnosticSeedCardProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
-
export {};
|