// Copyright (C) 2017-2026 Smart code 203358507 import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useGamepad } from 'stremio/services'; import styles from './styles.less'; type ActiveButton = string | null; const CX = 400; const BTN = { L1: 'L1', L2: 'L2', R1: 'R1', R2: 'R2' }; const ARROW = { UP: '↑', DOWN: '↓', LEFT: '←', RIGHT: '→' }; const GamepadDiagram = () => { const { t } = useTranslation(); const gamepad = useGamepad(); const [active, setActive] = useState(null); useEffect(() => { let timeout: ReturnType; const flash = (button: string) => () => { setActive(button); clearTimeout(timeout); timeout = setTimeout(() => setActive(null), 400); }; gamepad?.on('buttonA', 'gamepad-diagram', flash('cross')); gamepad?.on('buttonB', 'gamepad-diagram', flash('circle')); gamepad?.on('buttonX', 'gamepad-diagram', flash('square')); gamepad?.on('buttonY', 'gamepad-diagram', flash('triangle')); gamepad?.on('buttonLT', 'gamepad-diagram', flash('l1')); gamepad?.on('buttonRT', 'gamepad-diagram', flash('r1')); gamepad?.on('analog', 'gamepad-diagram', (dir: string) => flash('stick-' + dir)()); gamepad?.on('analogRight', 'gamepad-diagram', (dir: string) => flash('rstick-' + dir)()); return () => { clearTimeout(timeout); gamepad?.off('buttonA', 'gamepad-diagram'); gamepad?.off('buttonB', 'gamepad-diagram'); gamepad?.off('buttonX', 'gamepad-diagram'); gamepad?.off('buttonY', 'gamepad-diagram'); gamepad?.off('buttonLT', 'gamepad-diagram'); gamepad?.off('buttonRT', 'gamepad-diagram'); gamepad?.off('analog', 'gamepad-diagram'); gamepad?.off('analogRight', 'gamepad-diagram'); }; }, [gamepad]); const glow = (id: string) => active === id ? '#7b5bf5' : undefined; const glowOp = (id: string) => active === id ? 1 : undefined; const SX = 130; const BX = 120; const STX = 75; const BY = 30; return ( {/* ===== L2 / R2 TRIGGERS (drawn first, behind everything) ===== */} {BTN.L2} {BTN.R2} {/* ===== CONTROLLER BODY ===== */} {/* ===== TOUCHPAD ===== */} {/* ===== L1 / R1 BUMPERS (on top of body, on shoulder edge) ===== */} {BTN.L1} {BTN.R1} {/* ===== FACE BUTTONS (right, centered at CX+BX) ===== */} {/* △ */} {/* ○ */} {/* ✕ */} {/* □ */} {/* ===== D-PAD (left, mirrored at CX-BX) — cosmetic only ===== */} {/* ===== LEFT STICK (CX-STX) ===== */} {/* ===== RIGHT STICK (CX+STX) ===== */} {ARROW.UP} {ARROW.DOWN} {ARROW.LEFT} {ARROW.RIGHT} {/* ============================= */} {/* ===== LABELS — LEFT ===== */} {/* ============================= */} {/* L1 */} {t('GAMEPAD_ACTION_PREV_TAB')} {/* Left stick */} {t('GAMEPAD_ACTION_NAVIGATE')} {/* □ Square */} {t('GAMEPAD_ACTION_GUIDE')} {/* ============================= */} {/* ===== LABELS — RIGHT ===== */} {/* ============================= */} {/* R1 */} {t('GAMEPAD_ACTION_NEXT_TAB')} {/* △ Triangle */} {t('GAMEPAD_ACTION_FULLSCREEN')} {/* ○ Circle */} {t('GAMEPAD_ACTION_BACK')} {/* ✕ Cross */} {t('GAMEPAD_ACTION_SELECT')} {t('GAMEPAD_LABEL_PLAY_PAUSE_PLAYER')} {/* Right stick */} {t('GAMEPAD_LABEL_SEEK_VOL')} {/* Compat note */} {t('GAMEPAD_LABEL_COMPAT')} ); }; export default GamepadDiagram;