import { useLayoutEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Button } from '@shared/ui/Button' import { themeToCssVars } from '@features/clickgui/themeVars' import { parseArgb } from '@features/themes/types' import { useActiveTheme } from '@features/themes/useActiveTheme' import { VoxelScene } from '../../effects/VoxelScene' import { bounds, clamp, fitToField, type Layout, startLayout, type WidgetId } from './hud/layout' import { WidgetContent } from './hud/WidgetContent' import '@features/landing/styles/landing.css' import '@features/landing/styles/sections.css' const STEP = 8 type Drag = { id: WidgetId; px: number; py: number; ox: number; oy: number } /** * A full game screen with the mod's real HUD widgets on it. Holding a widget switches the * screen into "edit mode" (grid overlay, lifted widget), the same feedback the in-game editor gives. */ export function HudPlayground() { const { t } = useTranslation('landing') const { theme } = useActiveTheme() const { r, g, b } = parseArgb(theme.theme.accent) const fieldRef = useRef(null) const widgetRefs = useRef>>({}) const drag = useRef(null) const [pos, setPos] = useState(() => startLayout(null, {})) const [held, setHeld] = useState(null) useLayoutEffect(() => { setPos(startLayout(fieldRef.current, widgetRefs.current)) const field = fieldRef.current if (!field || typeof ResizeObserver === 'undefined') return const ro = new ResizeObserver(() => setPos((prev) => fitToField(prev, fieldRef.current, widgetRefs.current))) ro.observe(field) return () => ro.disconnect() }, []) const move = (id: WidgetId, x: number, y: number, el: HTMLElement) => { const { maxX, maxY } = bounds(fieldRef.current, el) setPos((prev) => ({ ...prev, [id]: { x: clamp(x, maxX), y: clamp(y, maxY) } })) } const onKeyDown = (id: WidgetId) => (e: React.KeyboardEvent) => { const deltas: Record = { ArrowLeft: [-STEP, 0], ArrowRight: [STEP, 0], ArrowUp: [0, -STEP], ArrowDown: [0, STEP], } const d = deltas[e.key] if (!d) return e.preventDefault() move(id, pos[id].x + d[0], pos[id].y + d[1], e.currentTarget) } const onPointerDown = (id: WidgetId) => (e: React.PointerEvent) => { e.currentTarget.setPointerCapture(e.pointerId) drag.current = { id, px: e.clientX, py: e.clientY, ox: pos[id].x, oy: pos[id].y } setHeld(id) } const onPointerMove = (e: React.PointerEvent) => { const d = drag.current if (d) move(d.id, d.ox + e.clientX - d.px, d.oy + e.clientY - d.py, e.currentTarget) } const onPointerUp = () => { drag.current = null setHeld(null) } const widgets: { id: WidgetId; label: string }[] = [ { id: 'fps', label: t('hud.widgetFps') }, { id: 'coords', label: t('hud.widgetCoords') }, { id: 'modules', label: t('hud.widgetModules') }, { id: 'keys', label: t('hud.widgetKeys') }, { id: 'ping', label: t('hud.widgetPing') }, ] return (

{t('hud.title')}

{t('hud.lead')}

) }