From 19525bb5d288fda26d91b64971c6a032f5680ed0 Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Thu, 10 Jul 2025 19:29:24 +0200 Subject: [PATCH 1/6] spiral --- package.json | 3 + src/common/ColorPicker.tsx | 425 ++++++++++ src/common/index.ts | 1 + src/game/GamePage.tsx | 2 + src/game/components/GameHypnoSpiral.tsx | 120 +++ src/game/components/GameSettings.tsx | 25 + src/game/components/index.ts | 1 + src/settings/SettingsProvider.tsx | 23 + src/settings/SettingsSection.tsx | 2 + .../{HpynoSettings.tsx => HypnoSettings.tsx} | 0 .../components/HypnoSpiralSettings.tsx | 191 +++++ src/settings/components/index.ts | 3 +- src/types/gl-react.d.ts | 2 + yarn.lock | 751 +++++++++++++----- 14 files changed, 1347 insertions(+), 202 deletions(-) create mode 100644 src/common/ColorPicker.tsx create mode 100644 src/game/components/GameHypnoSpiral.tsx rename src/settings/components/{HpynoSettings.tsx => HypnoSettings.tsx} (100%) create mode 100644 src/settings/components/HypnoSpiralSettings.tsx create mode 100644 src/types/gl-react.d.ts diff --git a/package.json b/package.json index 368ee3c9..c52df762 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,10 @@ "@lit/react": "^1.0.8", "axios": "^1.6.8", "buttplug": "^3.2.2", + "colord": "^2.9.3", "framer-motion": "^11.1.9", + "gl-react": "^5.2.0", + "gl-react-dom": "^5.2.1", "idb": "^8.0.0", "lit": "^3.3.1", "lodash": "^4.17.21", diff --git a/src/common/ColorPicker.tsx b/src/common/ColorPicker.tsx new file mode 100644 index 00000000..834d8fbf --- /dev/null +++ b/src/common/ColorPicker.tsx @@ -0,0 +1,425 @@ +import React, { useEffect, useRef, useState } from 'react'; +import styled from 'styled-components'; + +export interface RGBA { + r: number; + g: number; + b: number; + a: number; +} + +const RelativeDiv = styled.div` + position: relative; +`; + +const Checkerboard = styled.div` + position: absolute; + inset: 0; + background-image: + linear-gradient(45deg, #ddd 25%, transparent 25%), + linear-gradient(-45deg, #ddd 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #ddd 75%), + linear-gradient(-45deg, transparent 75%, #ddd 75%); + background-size: 8px 8px; + background-position: + 0 0, + 0 4px, + 4px -4px, + -4px 0; + border-radius: 0.75rem; + z-index: 0; +`; + +const StyledColorTile = styled.button<{ $active: boolean }>` + grid-column: 1 / -1; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + background: var(--button-background); + color: var(--button-color); + border-radius: var(--border-radius); + border: none; + opacity: ${({ $active }) => ($active ? 1 : 0.3)}; + transition: + opacity 0.2s, + background 0.2s; + padding: 10px 14px; + margin: 10px 0; + cursor: pointer; + + &:hover { + background: var(--primary); + } +`; + +const TileContent = styled.div` + display: flex; + flex-direction: column; + text-align: start; +`; + +const ColorSwatchWrapper = styled.div` + position: relative; + width: 2.5rem; + height: 2.5rem; + border-radius: var(--border-radius); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +`; + +const VisibleColorSwatch = styled.div<{ color: string }>` + width: 100%; + height: 100%; + border-radius: var(--border-radius); + background-color: ${({ color }) => color}; + z-index: 1; +`; + +const Popover = styled.div` + position: absolute; + top: calc(100% + 8px); + right: 0; + z-index: 100; + background: var(--background); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: 15px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + flex-direction: column; + gap: 15px; + min-width: 220px; +`; + +const SliderContainer = styled.div` + display: flex; + flex-direction: column; + gap: 8px; +`; + +const SliderLabel = styled.span` + font-size: 0.8rem; + color: var(--text-color); + display: flex; + justify-content: space-between; +`; + +const Slider = styled.input` + width: 100%; + height: 8px; + border-radius: 4px; + outline: none; + -webkit-appearance: none; + + &::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: white; + border: 1px solid var(--border-color); + cursor: pointer; + } +`; + +const HueSlider = styled(Slider)` + background: linear-gradient( + to right, + #f00, + #ff0, + #0f0, + #0ff, + #00f, + #f0f, + #f00 + ); +`; + +const AlphaSlider = styled(Slider)<{ hue: number }>` + color: ${({ hue }) => `hsl(${hue * 360}, 100%, 50%)`}; + background: linear-gradient(to right, transparent, currentColor); +`; + +const SaturationValueArea = styled.div<{ hue: number }>` + position: relative; + width: 100%; + height: 150px; + border-radius: var(--border-radius); + background: linear-gradient( + to right, + white, + hsl(${({ hue }) => hue * 360}, 100%, 50%) + ); + overflow: hidden; + + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(to top, #000, transparent); + } +`; + +const PickerCursor = styled.div<{ x: number; y: number }>` + position: absolute; + width: 16px; + height: 16px; + border: 2px solid white; + border-radius: 50%; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.5); + transform: translate(-50%, -50%); + left: ${({ x }) => x * 100}%; + top: ${({ y }) => y * 100}%; + z-index: 2; + pointer-events: none; +`; + +const HexInput = styled.input` + width: 100%; + padding: 8px; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + background: var(--input-background); + color: var(--text-color); + font-family: monospace; + font-size: 0.9rem; +`; + +function rgbaToHex({ r, g, b, a }: RGBA): string { + return `#${[r, g, b, a].map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`; +} + +function rgbaToHsv({ r, g, b }: RGBA): [number, number, number] { + r /= 255; + g /= 255; + g /= 255; + const max = Math.max(r, g, b), + min = Math.min(r, g, b), + delta = max - min, + v = max, + s = max === 0 ? 0 : delta / max; + let h = 0; + if (delta !== 0) { + switch (max) { + case r: + h = (g - b) / delta + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / delta + 2; + break; + default: + h = (r - g) / delta + 4; + break; + } + h /= 6; + } + return [h, s, v]; +} + +function hsvToRgba(h: number, s: number, v: number, a: number): RGBA { + const i = Math.floor(h * 6), + f = h * 6 - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s); + let r = 0, + g = 0, + b = 0; + switch (i % 6) { + case 0: + [r, g, b] = [v, t, p]; + break; + case 1: + [r, g, b] = [q, v, p]; + break; + case 2: + [r, g, b] = [p, v, t]; + break; + case 3: + [r, g, b] = [p, q, v]; + break; + case 4: + [r, g, b] = [t, p, v]; + break; + default: + [r, g, b] = [v, p, q]; + break; + } + return { r: r * 255, g: g * 255, b: b * 255, a: a * 255 }; +} + +function rgbaToCss({ r, g, b, a }: RGBA): string { + return `rgba(${r}, ${g}, ${b}, ${a})`; +} + +function hexToRgba(hex: string): RGBA { + const clean = hex.replace(/^#/, ''), + r = parseInt(clean.slice(0, 2), 16), + g = parseInt(clean.slice(2, 4), 16), + b = parseInt(clean.slice(4, 6), 16), + a = clean.length >= 8 ? parseInt(clean.slice(6, 8), 16) : 255; + return { r, g, b, a }; +} + +interface ColorPickerTileProps { + label: string; + description?: string; + color: RGBA; + onChange: (color: RGBA) => void; +} + +export const ColorPicker: React.FC = ({ + label, + description = '', + color, + onChange, +}) => { + const [hue, setHue] = useState(0); + const [saturation, setSaturation] = useState(1); + const [value, setValue] = useState(0.5); + const [alpha, setAlpha] = useState(1); + const [hex, setHex] = useState('#00000000'); + const [showPicker, setShowPicker] = useState(false); + + const areaRef = useRef(null); + const containerRef = useRef(null); + const isInteracting = useRef(false); + + useEffect(() => { + const [h, s, v] = rgbaToHsv(color); + setHue(h); + setSaturation(s); + setValue(v); + setAlpha(color.a / 255); + setHex(rgbaToHex(color)); + }, [color]); + + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (!containerRef.current?.contains(e.target as Node)) + setShowPicker(false); + }; + if (showPicker) document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [showPicker]); + + const updateColor = (h: number, s: number, v: number, a: number) => { + const newColor = hsvToRgba(h, s, v, a); + setHex(rgbaToHex(newColor)); + onChange(newColor); + }; + + const handleAreaChange = (e: MouseEvent | React.MouseEvent) => { + if (!areaRef.current) return; + const rect = areaRef.current.getBoundingClientRect(); + const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); + const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height)); + setSaturation(x); + setValue(1 - y); + updateColor(hue, x, 1 - y, alpha); + }; + + const startInteraction = (e: React.MouseEvent) => { + isInteracting.current = true; + handleAreaChange(e); + + const move = (e: MouseEvent) => + isInteracting.current && handleAreaChange(e); + const up = () => { + isInteracting.current = false; + document.removeEventListener('mousemove', move); + document.removeEventListener('mouseup', up); + }; + document.addEventListener('mousemove', move); + document.addEventListener('mouseup', up); + }; + + const currentColor = hsvToRgba(hue, saturation, value, alpha); + const colorString = rgbaToCss(currentColor); + + return ( + + setShowPicker(!showPicker)} + > + + {label} +

{description}

+
+ + + + +
+ + {showPicker && ( + + + + + + + + Hue + + + updateColor( + parseFloat(e.target.value), + saturation, + value, + alpha + ) + } + /> + + + + + Opacity + {Math.round(alpha * 100)}% + + + updateColor(hue, saturation, value, parseFloat(e.target.value)) + } + /> + + + { + const val = e.target.value.replace(/^#/, ''); + if (/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(val)) { + const rgba = hexToRgba(`#${val}`); + setHex(`#${val}`); + onChange(rgba); + } else if (val.length <= 8) setHex(`#${val}`); + }} + spellCheck={false} + /> + + )} +
+ ); +}; diff --git a/src/common/index.ts b/src/common/index.ts index 77d1af48..45c7aa3c 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,5 +1,6 @@ export * from './ContentSection'; export * from './Fields'; +export * from './ColorPicker'; export * from './ImageDialog'; export * from './ImageGrid'; export * from './JoiImage'; diff --git a/src/game/GamePage.tsx b/src/game/GamePage.tsx index 3a245977..9e81c5c2 100644 --- a/src/game/GamePage.tsx +++ b/src/game/GamePage.tsx @@ -1,6 +1,7 @@ import styled from 'styled-components'; import { GameHypno, + GameHypnoSpiral, GameImages, GameMeter, GameIntensity, @@ -93,6 +94,7 @@ export const GamePage = () => { + diff --git a/src/game/components/GameHypnoSpiral.tsx b/src/game/components/GameHypnoSpiral.tsx new file mode 100644 index 00000000..1a0fdcfd --- /dev/null +++ b/src/game/components/GameHypnoSpiral.tsx @@ -0,0 +1,120 @@ +import { useEffect, useRef, useState } from 'react'; +import { Node, Shaders } from 'gl-react'; +import { Surface } from 'gl-react-dom'; +import styled from 'styled-components'; +import { colord } from 'colord'; + +import { useSetting } from '../../settings'; + +const shader = Shaders.create({ + spiral: { + frag: ` + precision highp float; + + varying vec2 uv; + + uniform vec4 spiralColor; + uniform vec4 bgColor; + uniform vec2 iRes; + uniform float iTime; + uniform float spinSpeed; + uniform float throbSpeed; + uniform float throbStrength; + uniform float zoom; + + void main() { + vec2 truPos = vec2(1.0, iRes.y / iRes.x) * (uv - vec2(0.5, 0.5)) * 2.0; + float angle = atan(truPos.y, truPos.x); + float dist = pow(length(truPos), .4 + sin((iTime + cos(iTime * .05) * 0.1) * throbSpeed) * 0.2 * throbStrength); + float spiFactor = clamp(pow(sin(angle + dist * 40. * zoom - iTime * 5. * spinSpeed) + 1.0, 50.), 0., 1.); + + gl_FragColor = mix(spiralColor, bgColor, spiFactor); + } + `, + }, +}); + +const StyledGameHypnoSpiral = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +`; + +export const GameHypnoSpiral = () => { + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + + const targetRef = useRef(null); + + const [spiralEnabled] = useSetting('hypnoSpiralEnabled'); + const [spinSpeed] = useSetting('hypnoSpiralSpinSpeed'); + const [throbSpeed] = useSetting('hypnoSpiralThrobSpeed'); + const [throbStrength] = useSetting('hypnoSpiralThrobStrength'); + const [zoom] = useSetting('hypnoSpiralZoom'); + const [primary] = useSetting('hypnoSpiralPrimary'); + const [secondary] = useSetting('hypnoSpiralSecondary'); + const secc = { ...secondary }; + const [rainbowColors] = useSetting('hypnoSpiralRainbowColors'); + const [rainbowSaturation] = useSetting('hypnoSpiralRainbowSaturation'); + const [rainbowLightness] = useSetting('hypnoSpiralRainbowLightness'); + const [rainbowHueSpeed] = useSetting('hypnoSpiralRainbowHueSpeed'); + + const iTime = performance.now() / 1000.0; + + if (rainbowColors) { + const newColor = colord({ + h: (iTime * 10.0 * rainbowHueSpeed) % 360, + s: rainbowSaturation, + l: rainbowLightness, + }).toRgb(); + secc.r = newColor.r; + secc.g = newColor.g; + secc.b = newColor.b; + } + + useEffect(() => { + const animFrame = () => { + if (targetRef.current != null) { + setDimensions({ + width: targetRef.current.offsetWidth, + height: targetRef.current.offsetHeight, + }); + requestAnimationFrame(animFrame); + } + }; + + requestAnimationFrame(animFrame); + }, [targetRef]); + + return ( + + {spiralEnabled && ( + + + + )} + + ); +}; diff --git a/src/game/components/GameSettings.tsx b/src/game/components/GameSettings.tsx index 02c88317..58583c4e 100644 --- a/src/game/components/GameSettings.tsx +++ b/src/game/components/GameSettings.tsx @@ -6,6 +6,7 @@ import { DurationSettings, EventSettings, HypnoSettings, + HypnoSpiralSettings, ImageSettings, PaceSettings, PlayerSettings, @@ -41,6 +42,30 @@ const StyledGameSettingsDialog = styled.div` grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); `; +const GameSettingsDialog: React.FC = props => { + return ( + + + + + + + + + + + + + } + /> + ); +}; + export const GameSettings = () => { const [open, setOpen] = useState(false); const [phase, setPhase] = useGameValue('phase'); diff --git a/src/game/components/index.ts b/src/game/components/index.ts index 4cb0ba1e..d69ffa1d 100644 --- a/src/game/components/index.ts +++ b/src/game/components/index.ts @@ -2,6 +2,7 @@ export * from './events'; export * from './GameEmergencyStop'; export * from './GameEvents'; export * from './GameHypno'; +export * from './GameHypnoSpiral'; export * from './GameImages'; export * from './GameInstructions'; export * from './GameIntensity'; diff --git a/src/settings/SettingsProvider.tsx b/src/settings/SettingsProvider.tsx index 390d7144..00f7888f 100644 --- a/src/settings/SettingsProvider.tsx +++ b/src/settings/SettingsProvider.tsx @@ -1,5 +1,6 @@ import { useCallback } from 'react'; import { GameEvent, GameHypnoType, PlayerBody, PlayerGender } from '../types'; +import { RGBA } from '../common'; import { createLocalStorageProvider, VibrationMode } from '../utils'; import { interpolateWith } from '../utils/translate'; @@ -14,6 +15,17 @@ export interface Settings { timeshift: number; events: GameEvent[]; hypno: GameHypnoType; + hypnoSpiralEnabled: boolean; + hypnoSpiralSpinSpeed: number; + hypnoSpiralThrobSpeed: number; + hypnoSpiralThrobStrength: number; + hypnoSpiralZoom: number; + hypnoSpiralPrimary: RGBA; + hypnoSpiralSecondary: RGBA; + hypnoSpiralRainbowColors: boolean; + hypnoSpiralRainbowSaturation: number; + hypnoSpiralRainbowLightness: number; + hypnoSpiralRainbowHueSpeed: number; gender: PlayerGender; body: PlayerBody; highRes: boolean; @@ -34,6 +46,17 @@ export const defaultSettings: Settings = { timeshift: 0.5, events: Object.values(GameEvent), hypno: GameHypnoType.joi, + hypnoSpiralEnabled: true, + hypnoSpiralSpinSpeed: 1, + hypnoSpiralThrobSpeed: 1, + hypnoSpiralThrobStrength: 1, + hypnoSpiralZoom: 1, + hypnoSpiralPrimary: { r: 65, g: 105, b: 225, a: 30 }, + hypnoSpiralSecondary: { r: 0, g: 0, b: 0, a: 30 }, + hypnoSpiralRainbowColors: false, + hypnoSpiralRainbowSaturation: 100, + hypnoSpiralRainbowLightness: 50, + hypnoSpiralRainbowHueSpeed: 1, gender: PlayerGender.male, body: PlayerBody.penis, highRes: false, diff --git a/src/settings/SettingsSection.tsx b/src/settings/SettingsSection.tsx index bb2cd43e..748fd6f0 100644 --- a/src/settings/SettingsSection.tsx +++ b/src/settings/SettingsSection.tsx @@ -4,6 +4,7 @@ import { DurationSettings, EventSettings, HypnoSettings, + HypnoSpiralSettings, PaceSettings, PlayerSettings, ServiceSettings, @@ -27,6 +28,7 @@ export const SettingsSection = () => { + diff --git a/src/settings/components/HpynoSettings.tsx b/src/settings/components/HypnoSettings.tsx similarity index 100% rename from src/settings/components/HpynoSettings.tsx rename to src/settings/components/HypnoSettings.tsx diff --git a/src/settings/components/HypnoSpiralSettings.tsx b/src/settings/components/HypnoSpiralSettings.tsx new file mode 100644 index 00000000..6930e424 --- /dev/null +++ b/src/settings/components/HypnoSpiralSettings.tsx @@ -0,0 +1,191 @@ +import { AnimatePresence, motion } from 'framer-motion'; +import styled from 'styled-components'; + +import { + SettingsTile, + SettingsDescription, + ToggleTile, + ToggleTileType, + SettingsLabel, + Slider, + Measure, + ColorPicker, + RGBA, +} from '../../common'; +import { useSetting } from '../SettingsProvider'; +import { defaultTransition } from '../../utils'; + +const SliderSettingContainer = styled.div` + display: grid; + grid-template-columns: auto auto auto; +`; + +export const HypnoSpiralSettings = () => { + const [spiralEnabled, setSpiralEnabled] = useSetting('hypnoSpiralEnabled'); + const [spinSpeed, setSpinSpeed] = useSetting('hypnoSpiralSpinSpeed'); + const [throbSpeed, setThrobSpeed] = useSetting('hypnoSpiralThrobSpeed'); + const [throbStrength, setThrobStrength] = useSetting( + 'hypnoSpiralThrobStrength' + ); + const [zoom, setZoom] = useSetting('hypnoSpiralZoom'); + const [primary, setPrimary] = useSetting('hypnoSpiralPrimary'); + const [secondary, setSecondary] = useSetting('hypnoSpiralSecondary'); + const [rainbowColors, setRainbowColors] = useSetting( + 'hypnoSpiralRainbowColors' + ); + const [rainbowSaturation, setRainbowSaturation] = useSetting( + 'hypnoSpiralRainbowSaturation' + ); + const [rainbowLightness, setRainbowLightness] = useSetting( + 'hypnoSpiralRainbowLightness' + ); + const [rainbowHueSpeed, setRainbowHueSpeed] = useSetting( + 'hypnoSpiralRainbowHueSpeed' + ); + + return ( + + Select your hypno spiral + setSpiralEnabled(!spiralEnabled)} + type={ToggleTileType.check} + > + Enable Spiral +

Display a hypno spiral in the game

+
+ + {spiralEnabled && ( + + + Spin Speed + setSpinSpeed(value)} + /> + + Throb Speed + setThrobSpeed(value)} + /> + + + Throb Strength + + setThrobStrength(value)} + /> + + Zoom Out + setZoom(value)} + /> + + + setPrimary(value)} + /> + setRainbowColors(!rainbowColors)} + type={ToggleTileType.check} + > + Rainbow Colors +

Use a rainbow cycle for the secondary color

+
+ {!rainbowColors && ( + + setSecondary(value)} + /> + + )} + {rainbowColors && ( + + + + Rainbow Saturation + + setRainbowSaturation(value)} + /> + + + Rainbow Lightness + + setRainbowLightness(value)} + /> + + + Rainbow Hue Speed + + setRainbowHueSpeed(value)} + /> + + + + )} +
+ )} +
+
+ ); +}; diff --git a/src/settings/components/index.ts b/src/settings/components/index.ts index bf45d051..90c964af 100644 --- a/src/settings/components/index.ts +++ b/src/settings/components/index.ts @@ -2,7 +2,8 @@ export * from './BoardSettings'; export * from './ClimaxSettings'; export * from './DurationSettings'; export * from './EventSettings'; -export * from './HpynoSettings'; +export * from './HypnoSettings'; +export * from './HypnoSpiralSettings'; export * from './ImageSettings'; export * from './PaceSettings'; export * from './PlayerSettings'; diff --git a/src/types/gl-react.d.ts b/src/types/gl-react.d.ts new file mode 100644 index 00000000..3cc6e552 --- /dev/null +++ b/src/types/gl-react.d.ts @@ -0,0 +1,2 @@ +declare module 'gl-react'; +declare module 'gl-react-dom'; diff --git a/yarn.lock b/yarn.lock index faf40768..3e9ed7bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,7 +40,28 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== -"@babel/core@^7.28.0", "@babel/core@^7.28.3": +"@babel/core@^7.27.4": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.0.tgz#55dad808d5bf3445a108eefc88ea3fdf034749a4" + integrity sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.0" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.27.3" + "@babel/helpers" "^7.27.6" + "@babel/parser" "^7.28.0" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.0" + "@babel/types" "^7.28.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/core@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.3.tgz#aceddde69c5d1def69b839d09efa3e3ff59c97cb" integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== @@ -61,6 +82,17 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/generator@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.0.tgz#9cc2f7bd6eb054d77dc66c2664148a0c5118acd2" + integrity sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg== + dependencies: + "@babel/parser" "^7.28.0" + "@babel/types" "^7.28.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/generator@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" @@ -124,6 +156,15 @@ "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" +"@babel/helper-module-transforms@^7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz#db0bbcfba5802f9ef7870705a7ef8788508ede02" + integrity sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.27.3" + "@babel/helper-module-transforms@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" @@ -177,6 +218,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== +"@babel/helpers@^7.27.6": + version "7.27.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c" + integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug== + dependencies: + "@babel/template" "^7.27.2" + "@babel/types" "^7.27.6" + "@babel/helpers@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.3.tgz#b83156c0a2232c133d1b535dd5d3452119c7e441" @@ -185,7 +234,14 @@ "@babel/template" "^7.27.2" "@babel/types" "^7.28.2" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e" + integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g== + dependencies: + "@babel/types" "^7.28.0" + +"@babel/parser@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== @@ -239,9 +295,9 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/runtime@^7.24.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.3.tgz#75c5034b55ba868121668be5d5bb31cc64e6e61a" - integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA== + version "7.27.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" + integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== "@babel/template@^7.27.2": version "7.27.2" @@ -252,7 +308,20 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": +"@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" + integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.0" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.0" + debug "^4.3.1" + +"@babel/traverse@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ== @@ -265,7 +334,15 @@ "@babel/types" "^7.28.2" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.6", "@babel/types@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.0.tgz#2fd0159a6dc7353933920c43136335a9b264d950" + integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@babel/types@^7.27.3", "@babel/types@^7.28.2": version "7.28.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== @@ -442,6 +519,13 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@floating-ui/core@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.2.tgz#3d1c35263950b314b6d5a72c8bfb9e3c1551aefd" + integrity sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw== + dependencies: + "@floating-ui/utils" "^0.2.10" + "@floating-ui/core@^1.7.3": version "1.7.3" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.3.tgz#462d722f001e23e46d86fd2bd0d21b7693ccb8b7" @@ -449,7 +533,7 @@ dependencies: "@floating-ui/utils" "^0.2.10" -"@floating-ui/dom@^1.6.13", "@floating-ui/dom@^1.7.4": +"@floating-ui/dom@^1.6.13": version "1.7.4" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.4.tgz#ee667549998745c9c3e3e84683b909c31d6c9a77" integrity sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA== @@ -457,12 +541,20 @@ "@floating-ui/core" "^1.7.3" "@floating-ui/utils" "^0.2.10" +"@floating-ui/dom@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.2.tgz#3540b051cf5ce0d4f4db5fb2507a76e8ea5b4a45" + integrity sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA== + dependencies: + "@floating-ui/core" "^1.7.2" + "@floating-ui/utils" "^0.2.10" + "@floating-ui/react-dom@^2.1.2": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.6.tgz#189f681043c1400561f62972f461b93f01bf2231" - integrity sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.4.tgz#a0689be8978352fff2be2dfdd718cf668c488ec3" + integrity sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw== dependencies: - "@floating-ui/dom" "^1.7.4" + "@floating-ui/dom" "^1.7.2" "@floating-ui/react@^0.26.13": version "0.26.28" @@ -512,9 +604,9 @@ "@fortawesome/fontawesome-common-types" "6.7.2" "@fortawesome/react-fontawesome@^0.2.0": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.6.tgz#b75d0c7710c646c9bb6b9bb61cb5b8548a9ef2f5" - integrity sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg== + version "0.2.2" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz#68b058f9132b46c8599875f6a636dad231af78d4" + integrity sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g== dependencies: prop-types "^15.8.1" @@ -538,9 +630,9 @@ integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + version "0.3.12" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b" + integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" @@ -551,14 +643,14 @@ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + version "1.5.4" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7" + integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.30" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" - integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + version "0.3.29" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz#a58d31eaadaf92c6695680b2e1d464a9b8fbf7fc" + integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -606,110 +698,110 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.23.0.tgz#35390d0e7779626c026b11376da6789eb8389242" integrity sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA== -"@rolldown/pluginutils@1.0.0-beta.27": - version "1.0.0-beta.27" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz#47d2bf4cef6d470b22f5831b420f8964e0bf755f" - integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA== - -"@rollup/rollup-android-arm-eabi@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz#13cccb90969f7ca3d1354129c859a3b05e90beed" - integrity sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw== - -"@rollup/rollup-android-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz#0d01925255bb27b56edd095ba1764c3f91f28048" - integrity sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ== - -"@rollup/rollup-darwin-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz#5b11bca1da78d68f26aa98754cecd1887b683689" - integrity sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A== - -"@rollup/rollup-darwin-x64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz#00039989c4cd27ead349c313dc5562c3897c0524" - integrity sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ== - -"@rollup/rollup-freebsd-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz#1e5aca23d8171313f408759c71342bbee7889f22" - integrity sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ== - -"@rollup/rollup-freebsd-x64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz#04af010d99ccba84db10d4498cadaac8529ee738" - integrity sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ== - -"@rollup/rollup-linux-arm-gnueabihf@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz#67747af83a2dd092144d643caf20b0d1eb817681" - integrity sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ== - -"@rollup/rollup-linux-arm-musleabihf@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz#b4bed820cfc5efec00a13190e3d53c0b5240f3b1" - integrity sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q== - -"@rollup/rollup-linux-arm64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz#6b4a7af7e53e7d95c8c1975945d8f8dc8f6d74a9" - integrity sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ== - -"@rollup/rollup-linux-arm64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz#dd08c8174cfb95d4fa90663dd6be8db27039ad51" - integrity sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ== - -"@rollup/rollup-linux-loongarch64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz#997248c983d2272d1b810e5817cc3c885ebde5ff" - integrity sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ== - -"@rollup/rollup-linux-ppc64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz#57d020583a314741d17b653419d1dbc3fe99bc52" - integrity sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ== - -"@rollup/rollup-linux-riscv64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz#bbd381e5f99658de9baa0193b89e286767c6e029" - integrity sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw== - -"@rollup/rollup-linux-riscv64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz#2866abbea1e702246900414f878203fea350ccee" - integrity sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA== - -"@rollup/rollup-linux-s390x-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz#02f6a1b0f207bf9b6c8f76fca3bd394ad1a5e800" - integrity sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ== - -"@rollup/rollup-linux-x64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz#867a70767a3e45c1a49b26310548e7861f980016" - integrity sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA== - -"@rollup/rollup-linux-x64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz#4b676c79d85c3ae58e706d44d4be3bc4eb6315cd" - integrity sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg== - -"@rollup/rollup-win32-arm64-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz#0106a573d0c7b82e95c6f57894b65f11bc0c7873" - integrity sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ== - -"@rollup/rollup-win32-ia32-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz#351eee360c21415c1efb01d402f53c2a1f5f1a53" - integrity sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ== - -"@rollup/rollup-win32-x64-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz#6821b48385af21ba55c7a5f9f64d19f38ea26014" - integrity sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg== +"@rolldown/pluginutils@1.0.0-beta.19": + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz#fc3b95145a8e7a3bf92754269d8e4f40eea8a244" + integrity sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA== + +"@rollup/rollup-android-arm-eabi@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz#6819b7f1e41a49af566f629a1556eaeea774d043" + integrity sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q== + +"@rollup/rollup-android-arm64@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz#7bd5591af68c64a75be1779e2b20f187878daba9" + integrity sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA== + +"@rollup/rollup-darwin-arm64@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz#e216c333e448c67973386e46dbfe8e381aafb055" + integrity sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA== + +"@rollup/rollup-darwin-x64@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz#202f80eea3acfe3f67496fedffa006a5f1ce7f5a" + integrity sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw== + +"@rollup/rollup-freebsd-arm64@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz#4880f9769f1a7eec436b9c146e1d714338c26567" + integrity sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg== + +"@rollup/rollup-freebsd-x64@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz#647d6e333349b1c0fb322c2827ba1a53a0f10301" + integrity sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA== + +"@rollup/rollup-linux-arm-gnueabihf@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz#7ba5c97a7224f49618861d093c4a7b40fa50867b" + integrity sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ== + +"@rollup/rollup-linux-arm-musleabihf@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz#f858dcf498299d6c625ec697a5191e0e41423905" + integrity sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA== + +"@rollup/rollup-linux-arm64-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz#c0f1fc20c50666c61f574536a00cdd486b6aaae1" + integrity sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A== + +"@rollup/rollup-linux-arm64-musl@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz#0214efc3e404ddf108e946ad5f7e4ee2792a155a" + integrity sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A== + +"@rollup/rollup-linux-loongarch64-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz#8303c4ea2ae7bcbb96b2c77cfb53527d964bfceb" + integrity sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g== + +"@rollup/rollup-linux-powerpc64le-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz#4197ffbc61809629094c0fccf825e43a40fbc0ca" + integrity sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw== + +"@rollup/rollup-linux-riscv64-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz#bcb99c9004c9b91e3704a6a70c892cb0599b1f42" + integrity sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg== + +"@rollup/rollup-linux-riscv64-musl@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz#3e943bae9b8b4637c573c1922392beb8a5e81acb" + integrity sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg== + +"@rollup/rollup-linux-s390x-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz#dc43fb467bff9547f5b9937f38668da07fa8fa9f" + integrity sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw== + +"@rollup/rollup-linux-x64-gnu@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz#0699c560fa6ce6b846581a7e6c30c85c22a3f0da" + integrity sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ== + +"@rollup/rollup-linux-x64-musl@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz#9fb1becedcdc9e227d4748576eb8ba2fad8d2e29" + integrity sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg== + +"@rollup/rollup-win32-arm64-msvc@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz#fcf3e62edd76c560252b819f69627685f65887d7" + integrity sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw== + +"@rollup/rollup-win32-ia32-msvc@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz#45a5304491d6da4666f6159be4f739d4d43a283f" + integrity sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q== + +"@rollup/rollup-win32-x64-msvc@4.44.2": + version "4.44.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz#660018c9696ad4f48abe8c5d56db53c81aadba25" + integrity sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA== "@shoelace-style/animations@^1.2.0": version "1.2.0" @@ -748,11 +840,11 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" - integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.7.tgz#968cdc2366ec3da159f61166428ee40f370e56c2" + integrity sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng== dependencies: - "@babel/types" "^7.28.2" + "@babel/types" "^7.20.7" "@types/d3-array@^3.0.3": version "3.2.1" @@ -838,16 +930,16 @@ "@types/react" "*" "@types/react@*": - version "19.1.11" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.11.tgz#a64d8ec1769fc861d22f54e6e9f360ed67b54dc8" - integrity sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ== + version "19.1.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" + integrity sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== dependencies: csstype "^3.0.2" "@types/react@^18.2.66": - version "18.3.24" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.24.tgz#f6a5a4c613242dfe3af0dcee2b4ec47b92d9b6bd" - integrity sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A== + version "18.3.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.23.tgz#86ae6f6b95a48c418fecdaccc8069e0fbb63696a" + integrity sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -954,14 +1046,14 @@ integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== "@vitejs/plugin-react@^4.2.1": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz#647af4e7bb75ad3add578e762ad984b90f4a24b9" - integrity sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA== + version "4.6.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz#2707b485f44806d42d41c63921883cff9c54dfaa" + integrity sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ== dependencies: - "@babel/core" "^7.28.0" + "@babel/core" "^7.27.4" "@babel/plugin-transform-react-jsx-self" "^7.27.1" "@babel/plugin-transform-react-jsx-source" "^7.27.1" - "@rolldown/pluginutils" "1.0.0-beta.27" + "@rolldown/pluginutils" "1.0.0-beta.19" "@types/babel__core" "^7.20.5" react-refresh "^0.17.0" @@ -975,6 +1067,13 @@ acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +add-line-numbers@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/add-line-numbers/-/add-line-numbers-1.0.1.tgz#48dbbdea47dbd234deafeac6c93cea6f70b4b7e3" + integrity sha512-w+2a1malCvWwACQFBpZ5/uwmHGaGYT+aGIxA8ONF5vlhe6X/gD3eR8qVoLWa+5nnWAOq2LuPbrqDYqj1pn0WMg== + dependencies: + pad-left "^1.0.2" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -998,9 +1097,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.0.tgz#2f302e7550431b1b7762705fffb52cf1ffa20447" - integrity sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg== + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^4.1.0: version "4.3.0" @@ -1029,13 +1128,18 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +atob-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-1.0.0.tgz#b88dca6006922b962094f7556826bab31c4a296b" + integrity sha512-ArXcmHR/vwSN37HLVap/Y5SKpz12CuEybxe1sIYl7th/S6SQPrVMNFt6rblJzCOAxn0SHbXpknUtqbAIeo3Aow== + axios@^1.6.8: - version "1.11.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.11.0.tgz#c2ec219e35e414c025b2095e8b8280278478fdb6" - integrity sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.10.0.tgz#af320aee8632eaf2a400b6a1979fa75856f38d54" + integrity sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw== dependencies: follow-redirects "^1.15.6" - form-data "^4.0.4" + form-data "^4.0.0" proxy-from-env "^1.1.0" balanced-match@^1.0.0: @@ -1043,6 +1147,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bit-twiddle@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bit-twiddle/-/bit-twiddle-1.0.2.tgz#0c6c1fabe2b23d17173d9a61b7b7093eb9e1769e" + integrity sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA== + brace-expansion@^1.1.7: version "1.1.12" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" @@ -1066,12 +1175,12 @@ braces@^3.0.3: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.25.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.3.tgz#9167c9cbb40473f15f75f85189290678b99b16c5" - integrity sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ== + version "4.25.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.1.tgz#ba9e8e6f298a1d86f829c9b975e07948967bb111" + integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw== dependencies: - caniuse-lite "^1.0.30001735" - electron-to-chromium "^1.5.204" + caniuse-lite "^1.0.30001726" + electron-to-chromium "^1.5.173" node-releases "^2.0.19" update-browserslist-db "^1.1.3" @@ -1103,10 +1212,10 @@ camelize@^1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== -caniuse-lite@^1.0.30001735: - version "1.0.30001737" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz#8292bb7591932ff09e9a765f12fdf5629a241ccc" - integrity sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw== +caniuse-lite@^1.0.30001726: + version "1.0.30001727" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz#22e9706422ad37aa50556af8c10e40e2d93a8b85" + integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q== chalk@^4.0.0: version "4.1.2" @@ -1117,9 +1226,9 @@ chalk@^4.0.0: supports-color "^7.1.0" chalk@^5.4.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.0.tgz#a1a8d294ea3526dbb77660f12649a08490e33ab8" - integrity sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ== + version "5.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== class-transformer@^0.5.1: version "0.5.1" @@ -1158,6 +1267,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" @@ -1195,6 +1309,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1223,6 +1342,13 @@ csstype@3.1.3, csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cwise-compiler@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cwise-compiler/-/cwise-compiler-1.1.3.tgz#f4d667410e850d3a313a7d2db7b1e505bb034cc5" + integrity sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ== + dependencies: + uniq "^1.0.0" + "d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: version "3.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" @@ -1352,10 +1478,15 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -electron-to-chromium@^1.5.204: - version "1.5.209" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz#403e7a84933b7206bb2e737d897042b2a6ef8d3e" - integrity sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A== +dup@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dup/-/dup-1.0.0.tgz#51fc5ac685f8196469df0b905e934b20af5b4029" + integrity sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA== + +electron-to-chromium@^1.5.173: + version "1.5.181" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.181.tgz#2f68dc900651858200a423acdf12c644f6b7f014" + integrity sha512-+ISMj8OIQ+0qEeDj14Rt8WwcTOiqHyAB+5bnK1K7xNNLjBJ4hRCQfUkw8RWtcLbfBzDwc15ZnKH0c7SNOfwiyA== emoji-regex@^10.3.0: version "10.4.0" @@ -1643,9 +1774,9 @@ flatted@^3.2.9: integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== follow-redirects@^1.15.6: - version "1.15.11" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" - integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== form-data@^4.0.4: version "4.0.4" @@ -1726,6 +1857,52 @@ get-stream@^8.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== +gl-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gl-constants/-/gl-constants-1.0.0.tgz#597a504e364750ff50253aa35f8dea7af4a5d233" + integrity sha512-3DNyoAUdb1c+o7jNk5Nm7eh6RSQFi9ZmMQIQb2xxsO27rUopE+IUhoh4xlUvZYBn1YPgUC8BlCnrVjXq/d2dQA== + +gl-format-compiler-error@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/gl-format-compiler-error/-/gl-format-compiler-error-1.0.3.tgz#0c79b1751899ce9732e86240f090aa41e98471a8" + integrity sha512-FtQaBYlsM/rnz7YhLkxG9dLcNDB+ExErIsFV2DXl0nk+YgIZ2i0jMob4BrhT9dNa179zFb0gZMWpNAokytK+Ug== + dependencies: + add-line-numbers "^1.0.1" + gl-constants "^1.0.0" + glsl-shader-name "^1.0.0" + sprintf-js "^1.0.3" + +gl-react-dom@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/gl-react-dom/-/gl-react-dom-5.2.1.tgz#a6dc93ea0ef747ecc0bebea90be38ab48d1ebcdf" + integrity sha512-RZxOovktk8czle/FiMbiEJJxHLICoHEzxU46GeQ9tqiWMYxj/wj01VbqGVlrn6wVZ+fx77bhRxzn5ptW2uuxbA== + dependencies: + invariant "^2.2.4" + prop-types "^15.7.2" + raf "^3.4.1" + webgltexture-loader-dom "1.0.0" + +gl-react@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/gl-react/-/gl-react-5.2.0.tgz#877aa3fdb3168bcde1d69da9a8a5a0b0937e859c" + integrity sha512-A/FxG65e8Zx2RYi9+kYpeAwfFb7qQN0T6BCRXcUiwevUC//i3cH53w0AQRGj1mm96NVvD412mYJatl52qlDuxw== + dependencies: + gl-shader "^4.2.1" + invariant "^2.2.4" + ndarray "^1.0.19" + prop-types "^15.7.2" + typedarray-pool "^1.2.0" + webgltexture-loader "1.0.0" + webgltexture-loader-ndarray "1.2.0" + +gl-shader@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/gl-shader/-/gl-shader-4.3.1.tgz#56094cf3c06e802ac6c286b3b2166abce901d882" + integrity sha512-xLoN6XtRLlg97SEqtuzfKc+pVWpVkQ3YjDI1kuCale8tF7+zMhiKlMfmG4IMQPMdKJZQbIc/Ny8ZusEpfh5U+w== + dependencies: + gl-format-compiler-error "^1.0.2" + weakmap-shim "^1.1.0" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1771,6 +1948,21 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +glsl-shader-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/glsl-shader-name/-/glsl-shader-name-1.0.0.tgz#a2c30b3ba73499befb0cc7184d7c7733dd4b487d" + integrity sha512-OtHon0dPCbJD+IrVA1vw9QDlp2cS/f9z8X/0y+W7Qy1oZ3U1iFAQUEco2v30V0SAlVLDG5rEfhjEfc3DKdGbFQ== + dependencies: + atob-lite "^1.0.0" + glsl-tokenizer "^2.0.2" + +glsl-tokenizer@^2.0.2: + version "2.1.5" + resolved "https://registry.yarnpkg.com/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz#1c2e78c16589933c274ba278d0a63b370c5fee1a" + integrity sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA== + dependencies: + through2 "^0.6.3" + glur@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/glur/-/glur-1.1.2.tgz#f20ea36db103bfc292343921f1f91e83c3467689" @@ -1851,7 +2043,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@~2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1861,6 +2053,23 @@ inherits@2: resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +iota-array@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" + integrity sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA== + +is-buffer@^1.0.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1900,6 +2109,11 @@ is-stream@^3.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2048,7 +2262,7 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -2181,6 +2395,21 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +ndarray-ops@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ndarray-ops/-/ndarray-ops-1.2.2.tgz#59e88d2c32a7eebcb1bc690fae141579557a614e" + integrity sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw== + dependencies: + cwise-compiler "^1.0.0" + +ndarray@^1.0.19: + version "1.0.19" + resolved "https://registry.yarnpkg.com/ndarray/-/ndarray-1.0.19.tgz#6785b5f5dfa58b83e31ae5b2a058cfd1ab3f694e" + integrity sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ== + dependencies: + iota-array "^1.0.0" + is-buffer "^1.0.2" + node-releases@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" @@ -2245,6 +2474,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +pad-left@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pad-left/-/pad-left-1.0.2.tgz#19e5735ea98395a26cedc6ab926ead10f3100d4c" + integrity sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg== + dependencies: + repeat-string "^1.3.0" + param-enveloper@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/param-enveloper/-/param-enveloper-0.3.0.tgz#cf8456ca2a91aeb4fa991132e51ec17d861707d4" @@ -2282,6 +2518,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + pica@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/pica/-/pica-9.0.1.tgz#9ba5a5e81fc09dca9800abef9fb8388434b18b2f" @@ -2340,7 +2581,7 @@ prettier@^3.2.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== -prop-types@^15.6.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -2369,6 +2610,13 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -2433,6 +2681,16 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" +"readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + recharts-scale@^0.4.4: version "0.4.5" resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9" @@ -2459,6 +2717,11 @@ reflect-metadata@^0.2.1: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== +repeat-string@^1.3.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -2490,32 +2753,32 @@ rimraf@^3.0.2: glob "^7.1.3" rollup@^4.20.0: - version "4.48.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.48.1.tgz#acd64b7e3f8734728c5daedd5db42f4a8ea57858" - integrity sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg== + version "4.44.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.44.2.tgz#faedb27cb2aa6742530c39668092eecbaf78c488" + integrity sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.48.1" - "@rollup/rollup-android-arm64" "4.48.1" - "@rollup/rollup-darwin-arm64" "4.48.1" - "@rollup/rollup-darwin-x64" "4.48.1" - "@rollup/rollup-freebsd-arm64" "4.48.1" - "@rollup/rollup-freebsd-x64" "4.48.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.48.1" - "@rollup/rollup-linux-arm-musleabihf" "4.48.1" - "@rollup/rollup-linux-arm64-gnu" "4.48.1" - "@rollup/rollup-linux-arm64-musl" "4.48.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.48.1" - "@rollup/rollup-linux-ppc64-gnu" "4.48.1" - "@rollup/rollup-linux-riscv64-gnu" "4.48.1" - "@rollup/rollup-linux-riscv64-musl" "4.48.1" - "@rollup/rollup-linux-s390x-gnu" "4.48.1" - "@rollup/rollup-linux-x64-gnu" "4.48.1" - "@rollup/rollup-linux-x64-musl" "4.48.1" - "@rollup/rollup-win32-arm64-msvc" "4.48.1" - "@rollup/rollup-win32-ia32-msvc" "4.48.1" - "@rollup/rollup-win32-x64-msvc" "4.48.1" + "@rollup/rollup-android-arm-eabi" "4.44.2" + "@rollup/rollup-android-arm64" "4.44.2" + "@rollup/rollup-darwin-arm64" "4.44.2" + "@rollup/rollup-darwin-x64" "4.44.2" + "@rollup/rollup-freebsd-arm64" "4.44.2" + "@rollup/rollup-freebsd-x64" "4.44.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.44.2" + "@rollup/rollup-linux-arm-musleabihf" "4.44.2" + "@rollup/rollup-linux-arm64-gnu" "4.44.2" + "@rollup/rollup-linux-arm64-musl" "4.44.2" + "@rollup/rollup-linux-loongarch64-gnu" "4.44.2" + "@rollup/rollup-linux-powerpc64le-gnu" "4.44.2" + "@rollup/rollup-linux-riscv64-gnu" "4.44.2" + "@rollup/rollup-linux-riscv64-musl" "4.44.2" + "@rollup/rollup-linux-s390x-gnu" "4.44.2" + "@rollup/rollup-linux-x64-gnu" "4.44.2" + "@rollup/rollup-linux-x64-musl" "4.44.2" + "@rollup/rollup-win32-arm64-msvc" "4.44.2" + "@rollup/rollup-win32-ia32-msvc" "4.44.2" + "@rollup/rollup-win32-x64-msvc" "4.44.2" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -2600,6 +2863,11 @@ spark-md5@^3.0.2: resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== +sprintf-js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + string-argv@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" @@ -2614,6 +2882,11 @@ string-width@^7.0.0: get-east-asian-width "^1.0.0" strip-ansi "^7.1.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2680,6 +2953,14 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +through2@^0.6.3: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + integrity sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg== + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + tiny-emitter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" @@ -2729,10 +3010,23 @@ typed-function@^4.1.1: resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.2.1.tgz#19aa51847aa2dea9ef5e7fb7641c060179a74426" integrity sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA== +typedarray-pool@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/typedarray-pool/-/typedarray-pool-1.2.0.tgz#e7e90720144ba02b9ed660438af6f3aacfe33ac3" + integrity sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ== + dependencies: + bit-twiddle "^1.0.0" + dup "^1.0.0" + typescript@^5.2.2: - version "5.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" - integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + +uniq@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== update-browserslist-db@^1.1.3: version "1.1.3" @@ -2792,6 +3086,56 @@ wasgen@^0.18.4: dependencies: param-enveloper "^0.3.0" +weakmap-shim@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/weakmap-shim/-/weakmap-shim-1.1.1.tgz#d65afd784109b2166e00ff571c33150ec2a40b49" + integrity sha512-/wNyG+1FpiHhnfQo+TuA/XAUpvOOkKVl0A4qpT+oGcj5SlZCLmM+M1Py/3Sj8sy+YrEauCVITOxCsZKo6sPbQg== + +webgltexture-loader-dom-canvas@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-dom-canvas/-/webgltexture-loader-dom-canvas-1.0.0.tgz#fc00051950f25a1339b0ef047fefe7c72b80450a" + integrity sha512-lRDoPVE4GXFyfsl/E5+sZd7m6304RmBRky0SmgHlqJZJySFrCbQMF/hMQkWNWM5iYks84kRUGlwugW/c6OgpDw== + dependencies: + webgltexture-loader "^1.0.0" + +webgltexture-loader-dom-image-url@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-dom-image-url/-/webgltexture-loader-dom-image-url-1.0.0.tgz#f3a3889122d912c2f77e273c011823b189e68638" + integrity sha512-KiQ16cLBTjoCQfdvfy1SQP3wMWEPW9ADqsODyhd+6nMg7qD6FmewF3OZs6chF4yfDtlVVXRYI/cDGF1K9MK5Mg== + dependencies: + webgltexture-loader "^1.0.0" + +webgltexture-loader-dom-video@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-dom-video/-/webgltexture-loader-dom-video-1.0.0.tgz#cb5d5423190ec7d6d21b154d548c7e9d2d44546f" + integrity sha512-o3zB2XQEMAqgHQeNavoy4q5/lwIIt1MjF6Qj+pUDju9VlovJXGJiCIZ/suv715YgS3k36/pg97847GhV3bHczQ== + dependencies: + webgltexture-loader "^1.0.0" + +webgltexture-loader-dom@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-dom/-/webgltexture-loader-dom-1.0.0.tgz#930bcfcc612db5bc5e648f8ad756289ab0a1fd6f" + integrity sha512-JavQ9Hvv3DIYx6WuqzVa76bn2YCcq1lazUdzf277VZWrmxVCln5QFjyTfWZozgYtT21lVeu6MdMOUIbY4xhjqA== + dependencies: + webgltexture-loader-dom-canvas "^1.0.0" + webgltexture-loader-dom-image-url "^1.0.0" + webgltexture-loader-dom-video "^1.0.0" + +webgltexture-loader-ndarray@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-ndarray/-/webgltexture-loader-ndarray-1.2.0.tgz#1eb09f21ebd26e5004ce545b61de15d88e666dfa" + integrity sha512-gWTIqCOYs1XK6Yp4G7XvFDhvCvr6wFjTLPRN/HGBouF4u0to2fAIh5+2cxzEs1WE7eSwsxg1y5aNi1jRXSX+zA== + dependencies: + ndarray "^1.0.19" + ndarray-ops "^1.2.2" + typedarray-pool "^1.2.0" + webgltexture-loader "^1.0.0" + +webgltexture-loader@1.0.0, webgltexture-loader@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader/-/webgltexture-loader-1.0.0.tgz#e3d5b687d79ac85bed29222a7931f1405b69da6a" + integrity sha512-TbQUdr0Bc3tcJ4pKIFoM9dKyLdYNf9Uxq05oZuIT+2e/vCcfaQS+WjNmW1FwYqaBrzcLr9CA/HBAmvyaS/REGw== + webworkify@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/webworkify/-/webworkify-1.5.0.tgz#734ad87a774de6ebdd546e1d3e027da5b8f4a42c" @@ -2828,15 +3172,20 @@ ws@^8.16.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +"xtend@>=4.0.0 <4.1.0-0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" - integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6" + integrity sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ== yocto-queue@^0.1.0: version "0.1.0" From b6c9e2160a172609aae9d4a56501819ebd19bfde Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Tue, 29 Jul 2025 13:15:47 +0200 Subject: [PATCH 2/6] picker-fix --- src/common/ColorPicker.tsx | 12 ++++-------- src/game/components/GameHypnoSpiral.tsx | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/common/ColorPicker.tsx b/src/common/ColorPicker.tsx index 834d8fbf..d7ea8df0 100644 --- a/src/common/ColorPicker.tsx +++ b/src/common/ColorPicker.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; +// 0-255 export interface RGBA { r: number; g: number; @@ -195,7 +196,7 @@ function rgbaToHex({ r, g, b, a }: RGBA): string { function rgbaToHsv({ r, g, b }: RGBA): [number, number, number] { r /= 255; g /= 255; - g /= 255; + b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b), delta = max - min, @@ -252,7 +253,7 @@ function hsvToRgba(h: number, s: number, v: number, a: number): RGBA { } function rgbaToCss({ r, g, b, a }: RGBA): string { - return `rgba(${r}, ${g}, ${b}, ${a})`; + return `rgba(${r}, ${g}, ${b}, ${a / 255})`; } function hexToRgba(hex: string): RGBA { @@ -308,7 +309,6 @@ export const ColorPicker: React.FC = ({ const updateColor = (h: number, s: number, v: number, a: number) => { const newColor = hsvToRgba(h, s, v, a); - setHex(rgbaToHex(newColor)); onChange(newColor); }; @@ -317,8 +317,6 @@ export const ColorPicker: React.FC = ({ const rect = areaRef.current.getBoundingClientRect(); const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height)); - setSaturation(x); - setValue(1 - y); updateColor(hue, x, 1 - y, alpha); }; @@ -411,9 +409,7 @@ export const ColorPicker: React.FC = ({ onChange={e => { const val = e.target.value.replace(/^#/, ''); if (/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(val)) { - const rgba = hexToRgba(`#${val}`); - setHex(`#${val}`); - onChange(rgba); + onChange(hexToRgba(`#${val}`)); } else if (val.length <= 8) setHex(`#${val}`); }} spellCheck={false} diff --git a/src/game/components/GameHypnoSpiral.tsx b/src/game/components/GameHypnoSpiral.tsx index 1a0fdcfd..44cafbd1 100644 --- a/src/game/components/GameHypnoSpiral.tsx +++ b/src/game/components/GameHypnoSpiral.tsx @@ -74,17 +74,22 @@ export const GameHypnoSpiral = () => { } useEffect(() => { - const animFrame = () => { - if (targetRef.current != null) { - setDimensions({ - width: targetRef.current.offsetWidth, - height: targetRef.current.offsetHeight, - }); - requestAnimationFrame(animFrame); - } + const nextFrame = () => { + // redraw + setDimensions(d => ({ ...d })); + requestAnimationFrame(nextFrame); }; + requestAnimationFrame(nextFrame); + }, []); - requestAnimationFrame(animFrame); + useEffect(() => { + if (!targetRef.current) return; + const observer = new ResizeObserver(([entry]) => { + const { width, height } = entry.contentRect; + setDimensions({ width, height }); + }); + observer.observe(targetRef.current); + return () => observer.disconnect(); }, [targetRef]); return ( From 18cdf33842bb6d3fc84ea01363b06f72b8cee187 Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Sun, 8 Feb 2026 17:28:22 +0000 Subject: [PATCH 3/6] full custom text mode --- src/game/GamePage.tsx | 4 +- src/game/components/GameHypno.tsx | 56 ------ src/game/components/GameHypnoSpiral.tsx | 24 +-- src/game/components/GameHypnoText.tsx | 98 ++++++++++ src/game/components/GameSettings.tsx | 6 +- src/game/components/index.ts | 2 +- src/home/HomePage.tsx | 2 + src/settings/SettingsProvider.tsx | 86 ++++++--- src/settings/SettingsSection.tsx | 4 - src/settings/components/index.ts | 2 - src/settings/hypno/HypnoSettingsSection.tsx | 31 +++ .../components/HypnoCustomTextSettings.tsx | 182 ++++++++++++++++++ .../components/HypnoSpiralSettings.tsx | 40 ++-- .../components/HypnoTextSettings.tsx} | 22 ++- src/settings/hypno/components/index.ts | 4 + src/settings/hypno/index.ts | 2 + src/settings/index.ts | 1 + src/types/hypno.ts | 4 + 18 files changed, 437 insertions(+), 133 deletions(-) delete mode 100644 src/game/components/GameHypno.tsx create mode 100644 src/game/components/GameHypnoText.tsx create mode 100644 src/settings/hypno/HypnoSettingsSection.tsx create mode 100644 src/settings/hypno/components/HypnoCustomTextSettings.tsx rename src/settings/{ => hypno}/components/HypnoSpiralSettings.tsx (84%) rename src/settings/{components/HypnoSettings.tsx => hypno/components/HypnoTextSettings.tsx} (75%) create mode 100644 src/settings/hypno/components/index.ts create mode 100644 src/settings/hypno/index.ts diff --git a/src/game/GamePage.tsx b/src/game/GamePage.tsx index 9e81c5c2..b6123adc 100644 --- a/src/game/GamePage.tsx +++ b/src/game/GamePage.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; import { - GameHypno, + GameHypnoText, GameHypnoSpiral, GameImages, GameMeter, @@ -101,7 +101,7 @@ export const GamePage = () => {
- + diff --git a/src/game/components/GameHypno.tsx b/src/game/components/GameHypno.tsx deleted file mode 100644 index 0017394c..00000000 --- a/src/game/components/GameHypno.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import styled from 'styled-components'; -import { useSetting, useTranslate } from '../../settings'; -import { GameHypnoType, HypnoPhrases } from '../../types'; -import { useLooping } from '../../utils'; -import { GamePhase, useGameValue } from '../GameProvider'; -import { useCallback, useMemo } from 'react'; -import { motion } from 'framer-motion'; - -const StyledGameHypno = motion(styled.div` - pointer-events: none; - font-size: 4rem; - font-weight: bold; - -webkit-text-stroke: black 1px; -`); - -export const GameHypno = () => { - const [hypno] = useSetting('hypno'); - const [current, setCurrent] = useGameValue('currentHypno'); - const [phase] = useGameValue('phase'); - const [intensity] = useGameValue('intensity'); - const translate = useTranslate(); - - const phrase = useMemo(() => { - const phrases = HypnoPhrases[hypno]; - if (phrases.length <= 0) return ''; - return translate(phrases[current % phrases.length]); - }, [current, hypno, translate]); - - const onTick = useCallback(() => { - setCurrent(Math.floor(Math.random() * HypnoPhrases[hypno].length)); - }, [hypno, setCurrent]); - - const delay = useMemo(() => 3000 - intensity * 29, [intensity]); - - const enabled = useMemo( - () => phase === GamePhase.active && hypno !== GameHypnoType.off, - [phase, hypno] - ); - - useLooping(onTick, delay, enabled); - - return ( - - {phrase} - - ); -}; diff --git a/src/game/components/GameHypnoSpiral.tsx b/src/game/components/GameHypnoSpiral.tsx index 44cafbd1..471b6a58 100644 --- a/src/game/components/GameHypnoSpiral.tsx +++ b/src/game/components/GameHypnoSpiral.tsx @@ -47,18 +47,20 @@ export const GameHypnoSpiral = () => { const targetRef = useRef(null); - const [spiralEnabled] = useSetting('hypnoSpiralEnabled'); - const [spinSpeed] = useSetting('hypnoSpiralSpinSpeed'); - const [throbSpeed] = useSetting('hypnoSpiralThrobSpeed'); - const [throbStrength] = useSetting('hypnoSpiralThrobStrength'); - const [zoom] = useSetting('hypnoSpiralZoom'); - const [primary] = useSetting('hypnoSpiralPrimary'); - const [secondary] = useSetting('hypnoSpiralSecondary'); + const [hypno] = useSetting('hypno'); + + const spiralEnabled = hypno.spiralEnabled; + const spinSpeed = hypno.spiralSpinSpeed; + const throbSpeed = hypno.spiralThrobSpeed; + const throbStrength = hypno.spiralThrobStrength; + const zoom = hypno.spiralZoom; + const primary = hypno.spiralPrimary; + const secondary = hypno.spiralSecondary; const secc = { ...secondary }; - const [rainbowColors] = useSetting('hypnoSpiralRainbowColors'); - const [rainbowSaturation] = useSetting('hypnoSpiralRainbowSaturation'); - const [rainbowLightness] = useSetting('hypnoSpiralRainbowLightness'); - const [rainbowHueSpeed] = useSetting('hypnoSpiralRainbowHueSpeed'); + const rainbowColors = hypno.spiralRainbowColors; + const rainbowSaturation = hypno.spiralRainbowSaturation; + const rainbowLightness = hypno.spiralRainbowLightness; + const rainbowHueSpeed = hypno.spiralRainbowHueSpeed; const iTime = performance.now() / 1000.0; diff --git a/src/game/components/GameHypnoText.tsx b/src/game/components/GameHypnoText.tsx new file mode 100644 index 00000000..3cc1e757 --- /dev/null +++ b/src/game/components/GameHypnoText.tsx @@ -0,0 +1,98 @@ +import styled from 'styled-components'; +import { useSetting, useTranslate } from '../../settings'; +import { GameHypnoType, HypnoPhrases } from '../../types'; +import { useLooping } from '../../utils'; +import { GamePhase, useGameValue } from '../GameProvider'; +import { useCallback, useMemo } from 'react'; +import { motion } from 'framer-motion'; + +const StyledGameHypno = motion(styled.div` + pointer-events: none; + font-size: 4rem; + font-weight: bold; + -webkit-text-stroke: black 1px; +`); + +// custom text mode vars +const FADE_TIME = 0.2 +let custom_opacity = 0 + +export const GameHypnoText = () => { + const [hypno] = useSetting('hypno') + const [current, setCurrent] = useGameValue('currentHypno'); + const [phase] = useGameValue('phase'); + const [intensity] = useGameValue('intensity'); + const translate = useTranslate(); + + const startTime = useMemo(() => Date.now(), []) + + const phrase = useMemo(() => { + if (hypno.textType !== GameHypnoType.custom) { + const phrases = HypnoPhrases[hypno.textType]; + if (phrases.length <= 0) return ''; + return translate(phrases[current % phrases.length]); + } + const s_el = (Date.now() - startTime) / 1000 + const msgs = hypno.textCustom.filter(el => el.start < s_el && (el.start + el.duration) > s_el).sort((a, b) => { + if (a.start != b.start) return a.start < b.start ? -1 : 1 + if (a.duration != b.duration) return a.duration < b.duration ? -1 : 1 + return a.id < b.id ? -1 : 1 + }) + + const earliestStart = Math.min(...msgs.map(el => el.start)) + const soonestEnd = Math.min(...msgs.map(el => el.start + el.duration)) + custom_opacity = Math.max(0.4, Math.min(1, (s_el - earliestStart) / FADE_TIME, (soonestEnd - s_el) / FADE_TIME)) + + return msgs.map(el => el.text).join('\n') + }, [current, hypno.textType, translate]); + + const onTick = useCallback(() => { + if (hypno.textType !== GameHypnoType.custom) setCurrent(Math.floor(Math.random() * HypnoPhrases[hypno.textType].length)) + else setCurrent(p => +!p) + }, [hypno.textType, setCurrent]); + + const delay = useMemo(() => { + if (hypno.textType !== GameHypnoType.custom) return 3000 - intensity * 29 + else return 100 + }, [intensity, hypno.textType]); + + const enabled = useMemo( + () => phase === GamePhase.active && hypno.textType !== GameHypnoType.off, + [phase, hypno.textType] + ); + + useLooping(onTick, delay, enabled); + + return ( + <> + {hypno.textType !== GameHypnoType.custom && ( + + {phrase} + + ) || ( + + {phrase} + + )} + + ) +} + diff --git a/src/game/components/GameSettings.tsx b/src/game/components/GameSettings.tsx index 58583c4e..ec75c765 100644 --- a/src/game/components/GameSettings.tsx +++ b/src/game/components/GameSettings.tsx @@ -5,8 +5,7 @@ import { ClimaxSettings, DurationSettings, EventSettings, - HypnoSettings, - HypnoSpiralSettings, + HypnoSettingsContent, ImageSettings, PaceSettings, PlayerSettings, @@ -54,12 +53,11 @@ const GameSettingsDialog: React.FC = props => { - - + } /> diff --git a/src/game/components/index.ts b/src/game/components/index.ts index d69ffa1d..2b3c8617 100644 --- a/src/game/components/index.ts +++ b/src/game/components/index.ts @@ -1,7 +1,7 @@ export * from './events'; export * from './GameEmergencyStop'; export * from './GameEvents'; -export * from './GameHypno'; +export * from './GameHypnoText'; export * from './GameHypnoSpiral'; export * from './GameImages'; export * from './GameInstructions'; diff --git a/src/home/HomePage.tsx b/src/home/HomePage.tsx index df13921d..e8981044 100644 --- a/src/home/HomePage.tsx +++ b/src/home/HomePage.tsx @@ -10,6 +10,7 @@ import { StartButton, } from './components'; import { SettingsSection } from '../settings'; +import { HypnoSettingsSection } from '../settings/hypno'; import { HomeProvider } from './HomeProvider'; const StyledHomePage = styled.div` @@ -34,6 +35,7 @@ export const HomePage = () => { + diff --git a/src/settings/SettingsProvider.tsx b/src/settings/SettingsProvider.tsx index 00f7888f..1f2972cb 100644 --- a/src/settings/SettingsProvider.tsx +++ b/src/settings/SettingsProvider.tsx @@ -4,6 +4,49 @@ import { RGBA } from '../common'; import { createLocalStorageProvider, VibrationMode } from '../utils'; import { interpolateWith } from '../utils/translate'; +export interface HypnoTextEntry { + id: number; + text: string; + start: number; + duration: number; +} + +export interface HypnoSettingsType { + textType: GameHypnoType; + textCustomMaster: string; + textCustomHands: string; + textCustom: HypnoTextEntry[]; + spiralEnabled: boolean; + spiralSpinSpeed: number; + spiralThrobSpeed: number; + spiralThrobStrength: number; + spiralZoom: number; + spiralPrimary: RGBA; + spiralSecondary: RGBA; + spiralRainbowColors: boolean; + spiralRainbowSaturation: number; + spiralRainbowLightness: number; + spiralRainbowHueSpeed: number; +} + +export const defaultHypnoSettings: HypnoSettingsType = { + textType: GameHypnoType.joi, + textCustomMaster: 'master', + textCustomHands: 'hands', + textCustom: [], + spiralEnabled: true, + spiralSpinSpeed: 1, + spiralThrobSpeed: 1, + spiralThrobStrength: 1, + spiralZoom: 1, + spiralPrimary: { r: 65, g: 105, b: 225, a: 30 }, + spiralSecondary: { r: 0, g: 0, b: 0, a: 30 }, + spiralRainbowColors: false, + spiralRainbowSaturation: 100, + spiralRainbowLightness: 50, + spiralRainbowHueSpeed: 1, +} + export interface Settings { gameDuration: number; warmupDuration: number; @@ -14,18 +57,7 @@ export interface Settings { steepness: number; timeshift: number; events: GameEvent[]; - hypno: GameHypnoType; - hypnoSpiralEnabled: boolean; - hypnoSpiralSpinSpeed: number; - hypnoSpiralThrobSpeed: number; - hypnoSpiralThrobStrength: number; - hypnoSpiralZoom: number; - hypnoSpiralPrimary: RGBA; - hypnoSpiralSecondary: RGBA; - hypnoSpiralRainbowColors: boolean; - hypnoSpiralRainbowSaturation: number; - hypnoSpiralRainbowLightness: number; - hypnoSpiralRainbowHueSpeed: number; + hypno: HypnoSettingsType; gender: PlayerGender; body: PlayerBody; highRes: boolean; @@ -45,18 +77,7 @@ export const defaultSettings: Settings = { steepness: 0.5, timeshift: 0.5, events: Object.values(GameEvent), - hypno: GameHypnoType.joi, - hypnoSpiralEnabled: true, - hypnoSpiralSpinSpeed: 1, - hypnoSpiralThrobSpeed: 1, - hypnoSpiralThrobStrength: 1, - hypnoSpiralZoom: 1, - hypnoSpiralPrimary: { r: 65, g: 105, b: 225, a: 30 }, - hypnoSpiralSecondary: { r: 0, g: 0, b: 0, a: 30 }, - hypnoSpiralRainbowColors: false, - hypnoSpiralRainbowSaturation: 100, - hypnoSpiralRainbowLightness: 50, - hypnoSpiralRainbowHueSpeed: 1, + hypno: defaultHypnoSettings, gender: PlayerGender.male, body: PlayerBody.penis, highRes: false, @@ -77,6 +98,17 @@ export const { defaultData: defaultSettings, }); +export function subsetting< + S extends Record, + K extends keyof S +>( + [state, setState]: readonly [S, React.Dispatch>], + key: K +) { + const set = (v: S[K] | ((prev: S[K]) => S[K])) => setState((p: any) => ({...p, [key]: v instanceof Function ? v(state[key]) : v })) + return [state[key], set] as const +} + const removeTrailingComma = (str: string): string => { return str.trim().replace(/,$/, ''); }; @@ -101,14 +133,16 @@ export const useTranslate = (): ((value: string) => string) => { breeding: '', maledom: 'master', femdom: 'mistress', - }[hypno], + custom: hypno.textCustomMaster, + }[hypno.textType], hands: { off: 'hands', joi: 'hands', breeding: 'hands', maledom: 'paws', femdom: 'paws', - }[hypno], + custom: hypno.textCustomHands, + }[hypno.textType], part: { penis: 'cock', vagina: 'pussy', diff --git a/src/settings/SettingsSection.tsx b/src/settings/SettingsSection.tsx index 748fd6f0..27dcfd45 100644 --- a/src/settings/SettingsSection.tsx +++ b/src/settings/SettingsSection.tsx @@ -3,8 +3,6 @@ import { ClimaxSettings, DurationSettings, EventSettings, - HypnoSettings, - HypnoSpiralSettings, PaceSettings, PlayerSettings, ServiceSettings, @@ -27,8 +25,6 @@ export const SettingsSection = () => { - - diff --git a/src/settings/components/index.ts b/src/settings/components/index.ts index 90c964af..ae8c5d45 100644 --- a/src/settings/components/index.ts +++ b/src/settings/components/index.ts @@ -2,8 +2,6 @@ export * from './BoardSettings'; export * from './ClimaxSettings'; export * from './DurationSettings'; export * from './EventSettings'; -export * from './HypnoSettings'; -export * from './HypnoSpiralSettings'; export * from './ImageSettings'; export * from './PaceSettings'; export * from './PlayerSettings'; diff --git a/src/settings/hypno/HypnoSettingsSection.tsx b/src/settings/hypno/HypnoSettingsSection.tsx new file mode 100644 index 00000000..582388c9 --- /dev/null +++ b/src/settings/hypno/HypnoSettingsSection.tsx @@ -0,0 +1,31 @@ +import styled from 'styled-components' +import { + HypnoTextSettings, + HypnoCustomTextSettings, + HypnoSpiralSettings, +} from './components' +import { ContentSection } from '../../common' + +export const HypnoSettingsContent = () => { + return ( + <> + + + + + ) +} + +const StyledHypnoSettingsSection = styled(ContentSection)` + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); +`; + +export const HypnoSettingsSection = () => { + return ( + + + + ) +} + diff --git a/src/settings/hypno/components/HypnoCustomTextSettings.tsx b/src/settings/hypno/components/HypnoCustomTextSettings.tsx new file mode 100644 index 00000000..8ff13b7e --- /dev/null +++ b/src/settings/hypno/components/HypnoCustomTextSettings.tsx @@ -0,0 +1,182 @@ +import { PropsWithChildren } from 'react' +import { AnimatePresence, motion, HTMLMotionProps } from 'framer-motion' +import styled from 'styled-components' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { + faMinus, + faPlus, +} from '@fortawesome/free-solid-svg-icons' +import { + SettingsLabel, + Button, + TextInput, + Space, +} from '../../../common' +import { GameHypnoType } from '../../../types' +import { defaultTransition } from '../../../utils' +import { useSetting, subsetting, HypnoTextEntry } from '../../SettingsProvider' + +// -------------------- SettingsTile from common using motion +interface MotionSettingsTileProps +extends PropsWithChildren, 'ref'>> { + label: React.ReactNode + grid?: boolean +} + +const MotionStyledSettingsTile = styled(motion.fieldset)<{ $grid?: boolean }>` + display: ${({ $grid }) => ($grid ? 'grid' : 'flex')}; + flex-direction: column; + grid-template-columns: auto 1fr auto; + grid-auto-rows: min-content; + + background: var(--section-background); + color: #b9bad6; + + font-size: 0.8rem; + + border: unset; + border-left: 2px solid var(--legend-background); + + margin: 15px; + padding: 5px 15px; + + position: relative; +` + +const StyledSettingsLabel = styled.legend` + width: fit-content; + padding: 4px 8px; + background: var(--legend-background); + color: var(--legend-color); + line-height: 100%; + font-size: 1rem; +` + +const MotionSettingsTile: React.FC = ({ + label, + children, + grid, + ...props +}) => { + return ( + + {label} + {children} + + ) +} +// -------------------- + +const StyledSettingsDescription = styled.p` + line-height: 1.1; + max-width: 100%; + text-wrap: wrap; + grid-column: 1 / -1; + margin: 10px 0px; +`; + +const HypnoRow = styled.div` + display: grid; + grid-template-columns: auto auto auto auto; + gap: 8px; + align-items: center; + padding: 4px; + border-radius: 4px; + background: var(--card-background); +` + +const Unit = styled.div` + position: relative; + pointer-events: none; + user-select-none: + color: #666; + right: 5px; + text-align: right; +` + +export const HypnoCustomTextSettings = () => { + const h = useSetting('hypno') + const [hypno] = h + const [customMaster, setCustomMaster] = subsetting(h, 'textCustomMaster') + const [customHands, setCustomHands] = subsetting(h, 'textCustomHands') + const [custom, setCustom] = subsetting(h, 'textCustom') + + const addRow = () => setCustom((p: HypnoTextEntry[]) => { + const rid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + if (p.length == 0) return [{ id: rid, text: '', start: 0, duration: 1 }] + return [...p, { id: rid, text: '', start: p[p.length - 1]!.start + p[p.length - 1]!.duration, duration: 1 }] + }) + function updateRow(ix: number, key: S, val: HypnoTextEntry[S]) { + setCustom((p: HypnoTextEntry[]) => p.map((v, i) => (i === ix ? { ...p[ix]!, [key]: val } : v))) + } + const removeRow = (ix: number) => setCustom((p: HypnoTextEntry[]) => [...p.slice(0, ix), ...p.slice(ix + 1)]) + + return ( + + {hypno.textType === GameHypnoType.custom && ( + + + Variables:
+ $part - selected genitalia, $stroke - genitalia touch word, $player - third pronoun, $master, $hands
+ Flavors:
+ $part - lowercase, $Part - capitalized, $PART - uppercase +
+ Custom Master + + + Custom Hands + + +
+ + {custom.map((el, ix) => { + return ( + + + + updateRow(ix, 'text', v)} /> + Start at + {if (!isNaN(parseFloat(v))) updateRow(ix, 'start', parseFloat(v))}} /> + s + Duration + {if (!isNaN(parseFloat(v))) updateRow(ix, 'duration', parseFloat(v))}} /> + s + + + + ) + })} + +
+ + +
+ )} +
+ ) +} + diff --git a/src/settings/components/HypnoSpiralSettings.tsx b/src/settings/hypno/components/HypnoSpiralSettings.tsx similarity index 84% rename from src/settings/components/HypnoSpiralSettings.tsx rename to src/settings/hypno/components/HypnoSpiralSettings.tsx index 6930e424..6ffe7aee 100644 --- a/src/settings/components/HypnoSpiralSettings.tsx +++ b/src/settings/hypno/components/HypnoSpiralSettings.tsx @@ -11,9 +11,9 @@ import { Measure, ColorPicker, RGBA, -} from '../../common'; -import { useSetting } from '../SettingsProvider'; -import { defaultTransition } from '../../utils'; +} from '../../../common'; +import { useSetting, subsetting } from '../../SettingsProvider'; +import { defaultTransition } from '../../../utils'; const SliderSettingContainer = styled.div` display: grid; @@ -21,27 +21,19 @@ const SliderSettingContainer = styled.div` `; export const HypnoSpiralSettings = () => { - const [spiralEnabled, setSpiralEnabled] = useSetting('hypnoSpiralEnabled'); - const [spinSpeed, setSpinSpeed] = useSetting('hypnoSpiralSpinSpeed'); - const [throbSpeed, setThrobSpeed] = useSetting('hypnoSpiralThrobSpeed'); - const [throbStrength, setThrobStrength] = useSetting( - 'hypnoSpiralThrobStrength' - ); - const [zoom, setZoom] = useSetting('hypnoSpiralZoom'); - const [primary, setPrimary] = useSetting('hypnoSpiralPrimary'); - const [secondary, setSecondary] = useSetting('hypnoSpiralSecondary'); - const [rainbowColors, setRainbowColors] = useSetting( - 'hypnoSpiralRainbowColors' - ); - const [rainbowSaturation, setRainbowSaturation] = useSetting( - 'hypnoSpiralRainbowSaturation' - ); - const [rainbowLightness, setRainbowLightness] = useSetting( - 'hypnoSpiralRainbowLightness' - ); - const [rainbowHueSpeed, setRainbowHueSpeed] = useSetting( - 'hypnoSpiralRainbowHueSpeed' - ); + const h = useSetting('hypno') + + const [spiralEnabled, setSpiralEnabled] = subsetting(h, 'spiralEnabled') + const [spinSpeed, setSpinSpeed] = subsetting(h, 'spiralSpinSpeed') + const [throbSpeed, setThrobSpeed] = subsetting(h, 'spiralThrobSpeed') + const [throbStrength, setThrobStrength] = subsetting(h, 'spiralThrobStrength') + const [zoom, setZoom] = subsetting(h, 'spiralZoom') + const [primary, setPrimary] = subsetting(h, 'spiralPrimary') + const [secondary, setSecondary] = subsetting(h, 'spiralSecondary') + const [rainbowColors, setRainbowColors] = subsetting(h, 'spiralRainbowColors') + const [rainbowSaturation, setRainbowSaturation] = subsetting(h, 'spiralRainbowSaturation') + const [rainbowLightness, setRainbowLightness] = subsetting(h, 'spiralRainbowLightness') + const [rainbowHueSpeed, setRainbowHueSpeed] = subsetting(h, 'spiralRainbowHueSpeed') return ( diff --git a/src/settings/components/HypnoSettings.tsx b/src/settings/hypno/components/HypnoTextSettings.tsx similarity index 75% rename from src/settings/components/HypnoSettings.tsx rename to src/settings/hypno/components/HypnoTextSettings.tsx index 2c3111c6..fe4843a0 100644 --- a/src/settings/components/HypnoSettings.tsx +++ b/src/settings/hypno/components/HypnoTextSettings.tsx @@ -12,13 +12,26 @@ import { faDog, faOtter, faPowerOff, + faGear, } from '@fortawesome/free-solid-svg-icons'; +import { + SettingsTile, + SettingsDescription, + ToggleTile, + ToggleTileType, +} from '../../../common'; +import { + GameHypnoType, + GameHypnoDescriptions, + GameHypnoLabels, +} from '../../../types'; +import { useSetting, subsetting } from '../../SettingsProvider'; -export const HypnoSettings = () => { - const [hypno, setHypno] = useSetting('hypno'); +export const HypnoTextSettings = () => { + const [hypno, setHypno] = subsetting(useSetting('hypno'), 'textType'); return ( - + Choose a hypno text set {Object.keys(GameHypnoType).map(key => { const current = GameHypnoType[key as keyof typeof GameHypnoType]; @@ -44,6 +57,8 @@ export const HypnoSettings = () => { return ; case GameHypnoType.femdom: return ; + case GameHypnoType.custom: + return ; } })()} @@ -53,3 +68,4 @@ export const HypnoSettings = () => { ); }; + diff --git a/src/settings/hypno/components/index.ts b/src/settings/hypno/components/index.ts new file mode 100644 index 00000000..139e84a7 --- /dev/null +++ b/src/settings/hypno/components/index.ts @@ -0,0 +1,4 @@ +export * from './HypnoTextSettings' +export * from './HypnoCustomTextSettings' +export * from './HypnoSpiralSettings' + diff --git a/src/settings/hypno/index.ts b/src/settings/hypno/index.ts new file mode 100644 index 00000000..05aae28f --- /dev/null +++ b/src/settings/hypno/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './HypnoSettingsSection'; diff --git a/src/settings/index.ts b/src/settings/index.ts index 9b79cb72..11449902 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -1,4 +1,5 @@ export * from './components'; +export * from './hypno'; export * from './ImageProvider'; export * from './SettingsProvider'; export * from './SettingsSection'; diff --git a/src/types/hypno.ts b/src/types/hypno.ts index f1f83930..99a5b0d7 100644 --- a/src/types/hypno.ts +++ b/src/types/hypno.ts @@ -4,6 +4,7 @@ export enum GameHypnoType { breeding = 'breeding', maledom = 'maledom', femdom = 'femdom', + custom = 'custom', } export const GameHypnoLabels: Record = { @@ -12,6 +13,7 @@ export const GameHypnoLabels: Record = { breeding: 'Breeding', maledom: 'Maledom Pet', femdom: 'Femdom Pet', + custom: 'Custom', }; export const GameHypnoDescriptions: Record = { @@ -20,6 +22,7 @@ export const GameHypnoDescriptions: Record = { breeding: 'Focus on breeding and primal urge', maledom: "Good pets get master's treats", femdom: "Good pets get mistress' treats", + custom: "Create your own on-screen text", }; export const HypnoPhrases: Record = { @@ -126,4 +129,5 @@ export const HypnoPhrases: Record = { "mistress' $part", "mistress' cum", ], + custom: [], }; From d687b63ed08b328d7066c5953bb14a313692dcc7 Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Sun, 8 Feb 2026 19:41:05 +0000 Subject: [PATCH 4/6] rebase fix --- src/common/ColorPicker.tsx | 3 +- src/game/components/GameSettings.tsx | 25 +- .../components/HypnoCustomTextSettings.tsx | 296 +++++++++++------- .../hypno/components/HypnoSpiralSettings.tsx | 182 ++++++----- .../hypno/components/HypnoTextSettings.tsx | 19 +- 5 files changed, 303 insertions(+), 222 deletions(-) diff --git a/src/common/ColorPicker.tsx b/src/common/ColorPicker.tsx index d7ea8df0..07daf646 100644 --- a/src/common/ColorPicker.tsx +++ b/src/common/ColorPicker.tsx @@ -32,12 +32,11 @@ const Checkerboard = styled.div` `; const StyledColorTile = styled.button<{ $active: boolean }>` - grid-column: 1 / -1; width: 100%; + height: 100%; display: flex; justify-content: space-between; align-items: center; - background: var(--button-background); color: var(--button-color); border-radius: var(--border-radius); border: none; diff --git a/src/game/components/GameSettings.tsx b/src/game/components/GameSettings.tsx index ec75c765..b01d38ce 100644 --- a/src/game/components/GameSettings.tsx +++ b/src/game/components/GameSettings.tsx @@ -41,29 +41,6 @@ const StyledGameSettingsDialog = styled.div` grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); `; -const GameSettingsDialog: React.FC = props => { - return ( - - - - - - - - - - - - } - /> - ); -}; - export const GameSettings = () => { const [open, setOpen] = useState(false); const [phase, setPhase] = useGameValue('phase'); @@ -147,11 +124,11 @@ export const GameSettings = () => { - + diff --git a/src/settings/hypno/components/HypnoCustomTextSettings.tsx b/src/settings/hypno/components/HypnoCustomTextSettings.tsx index 8ff13b7e..060859f0 100644 --- a/src/settings/hypno/components/HypnoCustomTextSettings.tsx +++ b/src/settings/hypno/components/HypnoCustomTextSettings.tsx @@ -1,71 +1,64 @@ -import { PropsWithChildren } from 'react' -import { AnimatePresence, motion, HTMLMotionProps } from 'framer-motion' -import styled from 'styled-components' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { - faMinus, - faPlus, -} from '@fortawesome/free-solid-svg-icons' +import { PropsWithChildren } from 'react'; +import { AnimatePresence, motion, HTMLMotionProps } from 'framer-motion'; +import styled from 'styled-components'; +import { WaInput, WaButton, WaIcon } from '@awesome.me/webawesome/dist/react'; import { SettingsLabel, - Button, - TextInput, Space, -} from '../../../common' -import { GameHypnoType } from '../../../types' -import { defaultTransition } from '../../../utils' -import { useSetting, subsetting, HypnoTextEntry } from '../../SettingsProvider' + SettingsGrid, + SettingsRow, +} from '../../../common'; +import { GameHypnoType } from '../../../types'; +import { defaultTransition } from '../../../utils'; +import { useSetting, subsetting, HypnoTextEntry } from '../../SettingsProvider'; -// -------------------- SettingsTile from common using motion -interface MotionSettingsTileProps -extends PropsWithChildren, 'ref'>> { - label: React.ReactNode - grid?: boolean +// --------------------- +// Fields as motion +interface FieldsProps + extends PropsWithChildren, 'ref'>> { + label?: React.ReactNode; } -const MotionStyledSettingsTile = styled(motion.fieldset)<{ $grid?: boolean }>` - display: ${({ $grid }) => ($grid ? 'grid' : 'flex')}; +const StyledFields = styled(motion.fieldset)` + display: flex; flex-direction: column; - grid-template-columns: auto 1fr auto; - grid-auto-rows: min-content; background: var(--section-background); color: #b9bad6; - font-size: 0.8rem; - border: unset; border-left: 2px solid var(--legend-background); + border-radius: unset; - margin: 15px; - padding: 5px 15px; + margin: var(--wa-space-m); + padding: var(--wa-space-s); position: relative; -` +`; -const StyledSettingsLabel = styled.legend` +const StyledFieldsLegend = styled.legend` width: fit-content; padding: 4px 8px; background: var(--legend-background); color: var(--legend-color); line-height: 100%; font-size: 1rem; -` +`; -const MotionSettingsTile: React.FC = ({ - label, +const MotionFields: React.FC = ({ + label: legend, children, - grid, ...props }) => { return ( - - {label} - {children} - - ) -} -// -------------------- + + {legend && {legend}} + {children} + + ); +}; + +// --------------------- const StyledSettingsDescription = styled.p` line-height: 1.1; @@ -83,7 +76,7 @@ const HypnoRow = styled.div` padding: 4px; border-radius: 4px; background: var(--card-background); -` +`; const Unit = styled.div` position: relative; @@ -92,30 +85,45 @@ const Unit = styled.div` color: #666; right: 5px; text-align: right; -` +`; export const HypnoCustomTextSettings = () => { - const h = useSetting('hypno') - const [hypno] = h - const [customMaster, setCustomMaster] = subsetting(h, 'textCustomMaster') - const [customHands, setCustomHands] = subsetting(h, 'textCustomHands') - const [custom, setCustom] = subsetting(h, 'textCustom') + const h = useSetting('hypno'); + const [hypno] = h; + const [customMaster, setCustomMaster] = subsetting(h, 'textCustomMaster'); + const [customHands, setCustomHands] = subsetting(h, 'textCustomHands'); + const [custom, setCustom] = subsetting(h, 'textCustom'); - const addRow = () => setCustom((p: HypnoTextEntry[]) => { - const rid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) - if (p.length == 0) return [{ id: rid, text: '', start: 0, duration: 1 }] - return [...p, { id: rid, text: '', start: p[p.length - 1]!.start + p[p.length - 1]!.duration, duration: 1 }] - }) - function updateRow(ix: number, key: S, val: HypnoTextEntry[S]) { - setCustom((p: HypnoTextEntry[]) => p.map((v, i) => (i === ix ? { ...p[ix]!, [key]: val } : v))) + const addRow = () => + setCustom((p: HypnoTextEntry[]) => { + const rid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + if (p.length == 0) return [{ id: rid, text: '', start: 0, duration: 1 }]; + return [ + ...p, + { + id: rid, + text: '', + start: p[p.length - 1]!.start + p[p.length - 1]!.duration, + duration: 1, + }, + ]; + }); + function updateRow( + ix: number, + key: S, + val: HypnoTextEntry[S] + ) { + setCustom((p: HypnoTextEntry[]) => + p.map((v, i) => (i === ix ? { ...p[ix]!, [key]: val } : v)) + ); } - const removeRow = (ix: number) => setCustom((p: HypnoTextEntry[]) => [...p.slice(0, ix), ...p.slice(ix + 1)]) + const removeRow = (ix: number) => + setCustom((p: HypnoTextEntry[]) => [...p.slice(0, ix), ...p.slice(ix + 1)]); return ( {hypno.textType === GameHypnoType.custom && ( - { transition={defaultTransition} > - Variables:
- $part - selected genitalia, $stroke - genitalia touch word, $player - third pronoun, $master, $hands
- Flavors:
- $part - lowercase, $Part - capitalized, $PART - uppercase + Variables: +
+ $part - selected genitalia, $stroke - genitalia touch + word, $player - third pronoun, $master, $hands +
+ Flavors: +
+ $part - lowercase, $Part - capitalized, $PART - + uppercase
- Custom Master - + + Custom Master + setCustomMaster(e.currentTarget.value ?? '')} + /> + - Custom Hands - + + Custom Hands + setCustomHands(e.currentTarget.value ?? '')} + /> + -
- - {custom.map((el, ix) => { - return ( - - - - updateRow(ix, 'text', v)} /> - Start at - {if (!isNaN(parseFloat(v))) updateRow(ix, 'start', parseFloat(v))}} /> - s - Duration - {if (!isNaN(parseFloat(v))) updateRow(ix, 'duration', parseFloat(v))}} /> - s - - - - ) - })} - -
- - -
+ + {custom.map((el, ix) => { + return ( + + + removeRow(ix)} + > + Delete + + + + updateRow(ix, 'text', e.currentTarget.value ?? '') + } + /> + + Start at + + { + if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) + updateRow( + ix, + 'start', + parseFloat(e.currentTarget.value ?? '') + ); + }} + /> + s + + Duration + + { + if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) + updateRow( + ix, + 'duration', + parseFloat(e.currentTarget.value ?? '') + ); + }} + /> + s + + + + ); + })} + + + Add entry + + + )}
- ) -} - + ); +}; diff --git a/src/settings/hypno/components/HypnoSpiralSettings.tsx b/src/settings/hypno/components/HypnoSpiralSettings.tsx index 6ffe7aee..1cd7c29a 100644 --- a/src/settings/hypno/components/HypnoSpiralSettings.tsx +++ b/src/settings/hypno/components/HypnoSpiralSettings.tsx @@ -1,117 +1,150 @@ import { AnimatePresence, motion } from 'framer-motion'; +import { WaSlider } from '@awesome.me/webawesome/dist/react'; import styled from 'styled-components'; - import { - SettingsTile, SettingsDescription, - ToggleTile, - ToggleTileType, + Fields, + JoiToggleTile, SettingsLabel, - Slider, Measure, ColorPicker, RGBA, + SettingsRow, } from '../../../common'; import { useSetting, subsetting } from '../../SettingsProvider'; import { defaultTransition } from '../../../utils'; -const SliderSettingContainer = styled.div` +// ---------------------------- +// settings grid but with motion +const StyledSettingsGrid = motion(styled.div` display: grid; - grid-template-columns: auto auto auto; -`; + grid-template-columns: auto 1fr auto; + align-items: center; + column-gap: 1rem; + + & > :not([data-settings-row='true']) { + grid-column: 1 / -1; + } +`); +// ---------------------------- export const HypnoSpiralSettings = () => { - const h = useSetting('hypno') + const h = useSetting('hypno'); - const [spiralEnabled, setSpiralEnabled] = subsetting(h, 'spiralEnabled') - const [spinSpeed, setSpinSpeed] = subsetting(h, 'spiralSpinSpeed') - const [throbSpeed, setThrobSpeed] = subsetting(h, 'spiralThrobSpeed') - const [throbStrength, setThrobStrength] = subsetting(h, 'spiralThrobStrength') - const [zoom, setZoom] = subsetting(h, 'spiralZoom') - const [primary, setPrimary] = subsetting(h, 'spiralPrimary') - const [secondary, setSecondary] = subsetting(h, 'spiralSecondary') - const [rainbowColors, setRainbowColors] = subsetting(h, 'spiralRainbowColors') - const [rainbowSaturation, setRainbowSaturation] = subsetting(h, 'spiralRainbowSaturation') - const [rainbowLightness, setRainbowLightness] = subsetting(h, 'spiralRainbowLightness') - const [rainbowHueSpeed, setRainbowHueSpeed] = subsetting(h, 'spiralRainbowHueSpeed') + const [spiralEnabled, setSpiralEnabled] = subsetting(h, 'spiralEnabled'); + const [spinSpeed, setSpinSpeed] = subsetting(h, 'spiralSpinSpeed'); + const [throbSpeed, setThrobSpeed] = subsetting(h, 'spiralThrobSpeed'); + const [throbStrength, setThrobStrength] = subsetting( + h, + 'spiralThrobStrength' + ); + const [zoom, setZoom] = subsetting(h, 'spiralZoom'); + const [primary, setPrimary] = subsetting(h, 'spiralPrimary'); + const [secondary, setSecondary] = subsetting(h, 'spiralSecondary'); + const [rainbowColors, setRainbowColors] = subsetting( + h, + 'spiralRainbowColors' + ); + const [rainbowSaturation, setRainbowSaturation] = subsetting( + h, + 'spiralRainbowSaturation' + ); + const [rainbowLightness, setRainbowLightness] = subsetting( + h, + 'spiralRainbowLightness' + ); + const [rainbowHueSpeed, setRainbowHueSpeed] = subsetting( + h, + 'spiralRainbowHueSpeed' + ); return ( - + Select your hypno spiral - setSpiralEnabled(!spiralEnabled)} - type={ToggleTileType.check} + type='check' > Enable Spiral

Display a hypno spiral in the game

-
+ {spiralEnabled && ( - - + Spin Speed - setSpinSpeed(value)} + min={0} + step={0.1} + max={5} + onInput={e => setSpinSpeed(e.currentTarget.value)} + style={{ width: '100%' }} /> + + Throb Speed - setThrobSpeed(value)} + min={0} + step={0.1} + max={5} + onInput={e => setThrobSpeed(e.currentTarget.value)} + style={{ width: '100%' }} /> + + Throb Strength - setThrobStrength(value)} + min={0} + step={0.1} + max={5} + onInput={e => setThrobStrength(e.currentTarget.value)} + style={{ width: '100%' }} /> + + Zoom Out - setZoom(value)} + min={0.1} + step={0.1} + max={5} + onInput={e => setZoom(e.currentTarget.value)} + style={{ width: '100%' }} /> - + setPrimary(value)} /> - setRainbowColors(!rainbowColors)} - type={ToggleTileType.check} + type='check' > Rainbow Colors

Use a rainbow cycle for the secondary color

-
+ {!rainbowColors && ( { )} {rainbowColors && ( - - + Rainbow Saturation - setRainbowSaturation(value)} + min={0} + step={10} + max={200} + onInput={e => setRainbowSaturation(e.currentTarget.value)} + style={{ width: '100%' }} /> + + Rainbow Lightness - setRainbowLightness(value)} + min={0} + step={5} + max={100} + onInput={e => setRainbowLightness(e.currentTarget.value)} + style={{ width: '100%' }} /> + + Rainbow Hue Speed - setRainbowHueSpeed(value)} + min={0} + step={0.1} + max={5} + onInput={e => setRainbowHueSpeed(e.currentTarget.value)} + style={{ width: '100%' }} /> - - + + )} -
+ )}
-
+
); }; diff --git a/src/settings/hypno/components/HypnoTextSettings.tsx b/src/settings/hypno/components/HypnoTextSettings.tsx index fe4843a0..6ed032e0 100644 --- a/src/settings/hypno/components/HypnoTextSettings.tsx +++ b/src/settings/hypno/components/HypnoTextSettings.tsx @@ -1,11 +1,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Fields, SettingsDescription, JoiToggleTile } from '../../common'; +import { Fields, SettingsDescription, JoiToggleTile } from '../../../common'; import { GameHypnoType, GameHypnoDescriptions, GameHypnoLabels, -} from '../../types'; -import { useSetting } from '../SettingsProvider'; +} from '../../../types'; +import { useSetting, subsetting } from '../../SettingsProvider'; import { faCat, faCow, @@ -14,18 +14,6 @@ import { faPowerOff, faGear, } from '@fortawesome/free-solid-svg-icons'; -import { - SettingsTile, - SettingsDescription, - ToggleTile, - ToggleTileType, -} from '../../../common'; -import { - GameHypnoType, - GameHypnoDescriptions, - GameHypnoLabels, -} from '../../../types'; -import { useSetting, subsetting } from '../../SettingsProvider'; export const HypnoTextSettings = () => { const [hypno, setHypno] = subsetting(useSetting('hypno'), 'textType'); @@ -68,4 +56,3 @@ export const HypnoTextSettings = () => { ); }; - From f1e726f9695c637d2c588b530a26a2c42516f0ad Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Sun, 8 Feb 2026 19:45:39 +0000 Subject: [PATCH 5/6] max alpha fix --- src/game/components/GameHypnoText.tsx | 61 ++++++++++++++++----------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/game/components/GameHypnoText.tsx b/src/game/components/GameHypnoText.tsx index 3cc1e757..af9d2016 100644 --- a/src/game/components/GameHypnoText.tsx +++ b/src/game/components/GameHypnoText.tsx @@ -14,17 +14,17 @@ const StyledGameHypno = motion(styled.div` `); // custom text mode vars -const FADE_TIME = 0.2 -let custom_opacity = 0 +const FADE_TIME = 0.2; +let custom_opacity = 0; export const GameHypnoText = () => { - const [hypno] = useSetting('hypno') + const [hypno] = useSetting('hypno'); const [current, setCurrent] = useGameValue('currentHypno'); const [phase] = useGameValue('phase'); const [intensity] = useGameValue('intensity'); const translate = useTranslate(); - const startTime = useMemo(() => Date.now(), []) + const startTime = useMemo(() => Date.now(), []); const phrase = useMemo(() => { if (hypno.textType !== GameHypnoType.custom) { @@ -32,28 +32,40 @@ export const GameHypnoText = () => { if (phrases.length <= 0) return ''; return translate(phrases[current % phrases.length]); } - const s_el = (Date.now() - startTime) / 1000 - const msgs = hypno.textCustom.filter(el => el.start < s_el && (el.start + el.duration) > s_el).sort((a, b) => { - if (a.start != b.start) return a.start < b.start ? -1 : 1 - if (a.duration != b.duration) return a.duration < b.duration ? -1 : 1 - return a.id < b.id ? -1 : 1 - }) + const s_el = (Date.now() - startTime) / 1000; + const msgs = hypno.textCustom + .filter(el => el.start < s_el && el.start + el.duration > s_el) + .sort((a, b) => { + if (a.start != b.start) return a.start < b.start ? -1 : 1; + if (a.duration != b.duration) return a.duration < b.duration ? -1 : 1; + return a.id < b.id ? -1 : 1; + }); - const earliestStart = Math.min(...msgs.map(el => el.start)) - const soonestEnd = Math.min(...msgs.map(el => el.start + el.duration)) - custom_opacity = Math.max(0.4, Math.min(1, (s_el - earliestStart) / FADE_TIME, (soonestEnd - s_el) / FADE_TIME)) + const earliestStart = Math.min(...msgs.map(el => el.start)); + const soonestEnd = Math.min(...msgs.map(el => el.start + el.duration)); + custom_opacity = Math.max( + 0, + Math.min( + 0.4, + (s_el - earliestStart) / FADE_TIME, + (soonestEnd - s_el) / FADE_TIME + ) + ); - return msgs.map(el => el.text).join('\n') - }, [current, hypno.textType, translate]); + return msgs.map(el => el.text).join('\n'); + }, [current, hypno.textType, hypno.textCustom, translate, startTime]); const onTick = useCallback(() => { - if (hypno.textType !== GameHypnoType.custom) setCurrent(Math.floor(Math.random() * HypnoPhrases[hypno.textType].length)) - else setCurrent(p => +!p) + if (hypno.textType !== GameHypnoType.custom) + setCurrent( + Math.floor(Math.random() * HypnoPhrases[hypno.textType].length) + ); + else setCurrent(p => +!p); }, [hypno.textType, setCurrent]); const delay = useMemo(() => { - if (hypno.textType !== GameHypnoType.custom) return 3000 - intensity * 29 - else return 100 + if (hypno.textType !== GameHypnoType.custom) return 3000 - intensity * 29; + else return 100; }, [intensity, hypno.textType]); const enabled = useMemo( @@ -65,7 +77,7 @@ export const GameHypnoText = () => { return ( <> - {hypno.textType !== GameHypnoType.custom && ( + {(hypno.textType !== GameHypnoType.custom && ( { > {phrase} - ) || ( + )) || ( { exit={{ opacity: 0 }} transition={{ ease: [0, 0, 1, 1], - duration: FADE_TIME + duration: FADE_TIME, }} > {phrase} )} - ) -} - + ); +}; From eaa2cf2629641e44d2409c0d90e947b11719cb4f Mon Sep 17 00:00:00 2001 From: Aonodensetsu Date: Sun, 8 Feb 2026 20:20:07 +0000 Subject: [PATCH 6/6] max height for custom text creator --- .../components/HypnoCustomTextSettings.tsx | 185 ++++++++++-------- 1 file changed, 103 insertions(+), 82 deletions(-) diff --git a/src/settings/hypno/components/HypnoCustomTextSettings.tsx b/src/settings/hypno/components/HypnoCustomTextSettings.tsx index 060859f0..b8e9f2a2 100644 --- a/src/settings/hypno/components/HypnoCustomTextSettings.tsx +++ b/src/settings/hypno/components/HypnoCustomTextSettings.tsx @@ -87,6 +87,25 @@ const Unit = styled.div` text-align: right; `; +const ScrollDiv = styled.div` + width: 100%; + max-height: 500px; + overflow-y: scroll; + scrollbar-width: thin !important; + scrollbar-color: var(--background) var(--card-background) !important; + &::-webkit-scrollbar { + background: var(--card-background) !important; + width: 12px !important; + } + &::-webkit-scrollbar-thumb { + border-radius: 6px; + background: var(--background) !important; + } + &::-webkit-scrollbar-thumb:hover { + filter: brightness(1.2); + } +`; + export const HypnoCustomTextSettings = () => { const h = useSetting('hypno'); const [hypno] = h; @@ -161,88 +180,90 @@ export const HypnoCustomTextSettings = () => { /> - - {custom.map((el, ix) => { - return ( - - - removeRow(ix)} - > - Delete - - - - updateRow(ix, 'text', e.currentTarget.value ?? '') - } - /> - - Start at - - { - if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) - updateRow( - ix, - 'start', - parseFloat(e.currentTarget.value ?? '') - ); - }} - /> - s - - Duration - - { - if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) - updateRow( - ix, - 'duration', - parseFloat(e.currentTarget.value ?? '') - ); - }} - /> - s - - - - ); - })} - + + + {custom.map((el, ix) => { + return ( + + + removeRow(ix)} + > + Delete + + + + updateRow(ix, 'text', e.currentTarget.value ?? '') + } + /> + + Start at + + { + if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) + updateRow( + ix, + 'start', + parseFloat(e.currentTarget.value ?? '') + ); + }} + /> + s + + Duration + + { + if (!isNaN(parseFloat(e.currentTarget.value ?? ''))) + updateRow( + ix, + 'duration', + parseFloat(e.currentTarget.value ?? '') + ); + }} + /> + s + + + + ); + })} + +