koin.js 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +600 -251
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +601 -252
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +138 -58
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React2, { memo, createContext, useState, useRef, useEffect, useMemo, useCallback, useContext } from 'react';
|
|
2
|
-
import { Play, Pause, RotateCcw, Rewind, Save, Download, Camera, Video, Circle, Monitor, ChevronDown, RefreshCw, HelpCircle, Maximize, Gamepad2, Joystick, Code, Settings, Power, Square, Keyboard, X, Palette, ChevronUp, Gauge, VolumeX, Volume1, Volume2, Loader2, Trophy, AlertTriangle, List, PauseCircle, Check, Clock, User, Copy, Lock, Zap, HardDrive, Trash2, Cpu, AlertCircle, FileCode, Globe, ExternalLink, EyeOff, Eye, Shield, CheckCircle, LogOut, Info, XCircle } from 'lucide-react';
|
|
2
|
+
import { Play, Pause, RotateCcw, Rewind, Save, Download, Camera, Video, Circle, Monitor, ChevronDown, RefreshCw, HelpCircle, Maximize, Gamepad2, Joystick, Code, Settings, Power, Square, Keyboard, X, Palette, ChevronUp, Gauge, VolumeX, Volume1, Volume2, Loader2, Trophy, AlertTriangle, Minimize2, List, PauseCircle, Check, Clock, Move, User, Copy, Lock, Zap, HardDrive, Trash2, Cpu, AlertCircle, FileCode, Globe, ExternalLink, EyeOff, Eye, Shield, CheckCircle, LogOut, Info, XCircle } from 'lucide-react';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { Nostalgist } from 'nostalgist';
|
|
5
5
|
|
|
@@ -2078,92 +2078,175 @@ function useMobile() {
|
|
|
2078
2078
|
|
|
2079
2079
|
// src/components/VirtualController/layouts.ts
|
|
2080
2080
|
var BUTTON_SMALL = 44;
|
|
2081
|
-
var BUTTON_MEDIUM =
|
|
2082
|
-
var BUTTON_LARGE =
|
|
2083
|
-
var
|
|
2084
|
-
|
|
2081
|
+
var BUTTON_MEDIUM = 56;
|
|
2082
|
+
var BUTTON_LARGE = 68;
|
|
2083
|
+
var START_SELECT_Y = 8;
|
|
2084
|
+
var SELECT_X = 38;
|
|
2085
|
+
var START_X = 62;
|
|
2086
|
+
var DPAD_BUTTONS = [
|
|
2087
|
+
{ type: "up", label: "\u2191", x: 14, y: 55, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2088
|
+
{ type: "down", label: "\u2193", x: 14, y: 70, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2089
|
+
{ type: "left", label: "\u2190", x: 6, y: 62.5, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2090
|
+
{ type: "right", label: "\u2192", x: 22, y: 62.5, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true }
|
|
2091
|
+
];
|
|
2092
|
+
var TWO_BUTTON_LAYOUT = {
|
|
2093
|
+
console: "2BUTTON",
|
|
2085
2094
|
buttons: [
|
|
2086
|
-
|
|
2087
|
-
{ type: "
|
|
2088
|
-
{ type: "
|
|
2089
|
-
{ type: "
|
|
2090
|
-
{ type: "
|
|
2091
|
-
// Action buttons
|
|
2092
|
-
{ type: "b", label: "B", x: 80, y: 64, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2093
|
-
{ type: "a", label: "A", x: 92, y: 56, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2094
|
-
// System buttons - top center
|
|
2095
|
-
{ type: "select", label: "SEL", x: 38, y: 8, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true },
|
|
2096
|
-
{ type: "start", label: "START", x: 62, y: 8, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true }
|
|
2095
|
+
...DPAD_BUTTONS,
|
|
2096
|
+
{ type: "b", label: "B", x: 78, y: 66, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2097
|
+
{ type: "a", label: "A", x: 90, y: 56, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2098
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2099
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2097
2100
|
]
|
|
2098
2101
|
};
|
|
2099
2102
|
var SNES_LAYOUT = {
|
|
2100
2103
|
console: "SNES",
|
|
2101
2104
|
buttons: [
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
{ type: "
|
|
2105
|
-
{ type: "
|
|
2106
|
-
{ type: "
|
|
2107
|
-
{ type: "
|
|
2108
|
-
|
|
2109
|
-
{ type: "
|
|
2110
|
-
{ type: "
|
|
2111
|
-
{ type: "
|
|
2112
|
-
{ type: "
|
|
2113
|
-
{ type: "start", label: "START", x: 62, y: 8, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true }
|
|
2105
|
+
...DPAD_BUTTONS,
|
|
2106
|
+
// Diamond: Y-X-B-A (left-top-bottom-right)
|
|
2107
|
+
{ type: "y", label: "Y", x: 74, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2108
|
+
{ type: "x", label: "X", x: 84, y: 46, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2109
|
+
{ type: "b", label: "B", x: 84, y: 70, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2110
|
+
{ type: "a", label: "A", x: 94, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2111
|
+
// Shoulders
|
|
2112
|
+
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2113
|
+
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2114
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2115
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2114
2116
|
]
|
|
2115
2117
|
};
|
|
2116
|
-
var
|
|
2117
|
-
console: "
|
|
2118
|
+
var GBA_LAYOUT = {
|
|
2119
|
+
console: "GBA",
|
|
2118
2120
|
buttons: [
|
|
2119
|
-
|
|
2120
|
-
{ type: "
|
|
2121
|
-
{ type: "
|
|
2122
|
-
{ type: "
|
|
2123
|
-
{ type: "
|
|
2124
|
-
{ type: "
|
|
2125
|
-
{ type: "
|
|
2126
|
-
{ type: "start", label: "START", x: 62, y: 8, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true }
|
|
2121
|
+
...DPAD_BUTTONS,
|
|
2122
|
+
{ type: "b", label: "B", x: 80, y: 62, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2123
|
+
{ type: "a", label: "A", x: 92, y: 52, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2124
|
+
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2125
|
+
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2126
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2127
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2127
2128
|
]
|
|
2128
2129
|
};
|
|
2129
|
-
var
|
|
2130
|
-
console: "
|
|
2130
|
+
var SIX_BUTTON_LAYOUT = {
|
|
2131
|
+
console: "6BUTTON",
|
|
2131
2132
|
buttons: [
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
{ type: "
|
|
2135
|
-
{ type: "
|
|
2136
|
-
{ type: "
|
|
2137
|
-
|
|
2138
|
-
{ type: "
|
|
2139
|
-
{ type: "
|
|
2140
|
-
{ type: "
|
|
2141
|
-
{ type: "start", label: "START", x:
|
|
2133
|
+
...DPAD_BUTTONS,
|
|
2134
|
+
// Top row (X, Y, Z) → mapped to L, X, R
|
|
2135
|
+
{ type: "l", label: "X", x: 74, y: 48, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2136
|
+
{ type: "x", label: "Y", x: 84, y: 44, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2137
|
+
{ type: "r", label: "Z", x: 94, y: 40, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2138
|
+
// Bottom row (A, B, C) → mapped to Y, B, A
|
|
2139
|
+
{ type: "y", label: "A", x: 72, y: 64, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2140
|
+
{ type: "b", label: "B", x: 82, y: 60, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2141
|
+
{ type: "a", label: "C", x: 92, y: 56, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2142
|
+
{ type: "start", label: "START", x: 50, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2142
2143
|
]
|
|
2143
2144
|
};
|
|
2144
|
-
var
|
|
2145
|
-
console: "
|
|
2145
|
+
var SATURN_LAYOUT = {
|
|
2146
|
+
console: "SATURN",
|
|
2146
2147
|
buttons: [
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
{ type: "
|
|
2151
|
-
{ type: "
|
|
2152
|
-
{ type: "
|
|
2153
|
-
|
|
2154
|
-
{ type: "
|
|
2148
|
+
...DPAD_BUTTONS,
|
|
2149
|
+
// Face buttons (same as 6-button but uses standard buttons)
|
|
2150
|
+
// X/Y/Z on top
|
|
2151
|
+
{ type: "x", label: "X", x: 74, y: 48, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2152
|
+
{ type: "y", label: "Y", x: 84, y: 44, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2153
|
+
{ type: "l", label: "Z", x: 94, y: 40, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2154
|
+
// A/B/C on bottom
|
|
2155
|
+
{ type: "b", label: "A", x: 72, y: 64, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2156
|
+
{ type: "a", label: "B", x: 82, y: 60, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2157
|
+
{ type: "r", label: "C", x: 92, y: 56, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2158
|
+
// Triggers (L2/R2 for Saturn L/R)
|
|
2159
|
+
{ type: "l2", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2160
|
+
{ type: "r2", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2161
|
+
{ type: "start", label: "START", x: 50, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2162
|
+
]
|
|
2163
|
+
};
|
|
2164
|
+
var NEOGEO_LAYOUT = {
|
|
2165
|
+
console: "NEOGEO",
|
|
2166
|
+
buttons: [
|
|
2167
|
+
...DPAD_BUTTONS,
|
|
2168
|
+
// Curved A-B-C-D layout (maps to b-a-y-x for RetroPad)
|
|
2169
|
+
{ type: "b", label: "A", x: 68, y: 68, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2170
|
+
{ type: "a", label: "B", x: 78, y: 62, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2171
|
+
{ type: "y", label: "C", x: 88, y: 56, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2172
|
+
{ type: "x", label: "D", x: 96, y: 48, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2173
|
+
{ type: "select", label: "COIN", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2174
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2175
|
+
]
|
|
2176
|
+
};
|
|
2177
|
+
var PSX_LAYOUT = {
|
|
2178
|
+
console: "PSX",
|
|
2179
|
+
buttons: [
|
|
2180
|
+
...DPAD_BUTTONS,
|
|
2181
|
+
// PS symbols: △◯✕□ → y, b, a, x (triangle-circle-cross-square)
|
|
2182
|
+
{ type: "y", label: "\u25B3", x: 84, y: 46, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2183
|
+
{ type: "b", label: "\u25CB", x: 94, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2184
|
+
{ type: "a", label: "\u2715", x: 84, y: 70, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2185
|
+
{ type: "x", label: "\u25A1", x: 74, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2186
|
+
// Shoulders
|
|
2187
|
+
{ type: "l", label: "L1", x: 8, y: 25, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2188
|
+
{ type: "r", label: "R1", x: 92, y: 25, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2189
|
+
{ type: "l2", label: "L2", x: 8, y: 15, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2190
|
+
{ type: "r2", label: "R2", x: 92, y: 15, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2191
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2192
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2193
|
+
]
|
|
2194
|
+
};
|
|
2195
|
+
var N64_LAYOUT = {
|
|
2196
|
+
console: "N64",
|
|
2197
|
+
buttons: [
|
|
2198
|
+
...DPAD_BUTTONS,
|
|
2199
|
+
// A/B
|
|
2200
|
+
{ type: "a", label: "A", x: 80, y: 68, size: BUTTON_LARGE, showInPortrait: true, showInLandscape: true },
|
|
2201
|
+
{ type: "b", label: "B", x: 72, y: 76, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2202
|
+
// C-buttons (use x/y as C-left/C-up, l2/r2 as C-down/C-right)
|
|
2203
|
+
{ type: "y", label: "C\u2191", x: 92, y: 50, size: 36, showInPortrait: true, showInLandscape: true },
|
|
2204
|
+
{ type: "x", label: "C\u2190", x: 86, y: 56, size: 36, showInPortrait: true, showInLandscape: true },
|
|
2205
|
+
{ type: "l2", label: "C\u2193", x: 92, y: 62, size: 36, showInPortrait: true, showInLandscape: true },
|
|
2206
|
+
{ type: "r2", label: "C\u2192", x: 98, y: 56, size: 36, showInPortrait: true, showInLandscape: true },
|
|
2207
|
+
// Shoulders
|
|
2208
|
+
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2209
|
+
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2210
|
+
// Z trigger (use select as workaround since it's a unique button)
|
|
2211
|
+
{ type: "select", label: "Z", x: 8, y: 35, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2212
|
+
{ type: "start", label: "START", x: 50, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2213
|
+
]
|
|
2214
|
+
};
|
|
2215
|
+
var DREAMCAST_LAYOUT = {
|
|
2216
|
+
console: "DREAMCAST",
|
|
2217
|
+
buttons: [
|
|
2218
|
+
...DPAD_BUTTONS,
|
|
2219
|
+
// Standard diamond
|
|
2220
|
+
{ type: "y", label: "Y", x: 74, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2221
|
+
{ type: "x", label: "X", x: 84, y: 46, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2222
|
+
{ type: "b", label: "B", x: 84, y: 70, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2223
|
+
{ type: "a", label: "A", x: 94, y: 58, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2224
|
+
// Triggers
|
|
2225
|
+
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2226
|
+
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2227
|
+
{ type: "start", label: "START", x: 50, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2155
2228
|
]
|
|
2156
2229
|
};
|
|
2157
2230
|
function getLayoutForSystem(system) {
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2160
|
-
if (
|
|
2161
|
-
if (
|
|
2162
|
-
if (
|
|
2163
|
-
if (
|
|
2164
|
-
return
|
|
2231
|
+
const s = system.toUpperCase();
|
|
2232
|
+
if (s === "PS1" || s === "PSX" || s === "PSP") return PSX_LAYOUT;
|
|
2233
|
+
if (s === "N64") return N64_LAYOUT;
|
|
2234
|
+
if (s === "SNES" || s === "NDS") return SNES_LAYOUT;
|
|
2235
|
+
if (s === "GBA") return GBA_LAYOUT;
|
|
2236
|
+
if (s === "NES" || s === "GB" || s === "GBC" || s === "VIRTUAL_BOY") return TWO_BUTTON_LAYOUT;
|
|
2237
|
+
if (s === "DREAMCAST") return DREAMCAST_LAYOUT;
|
|
2238
|
+
if (s === "SATURN") return SATURN_LAYOUT;
|
|
2239
|
+
if (s === "GENESIS") return SIX_BUTTON_LAYOUT;
|
|
2240
|
+
if (s === "MASTER_SYSTEM" || s === "GAME_GEAR") return TWO_BUTTON_LAYOUT;
|
|
2241
|
+
if (s === "NEOGEO") return NEOGEO_LAYOUT;
|
|
2242
|
+
if (s.includes("NEOGEO_POCKET")) return TWO_BUTTON_LAYOUT;
|
|
2243
|
+
if (s === "ARCADE") return SIX_BUTTON_LAYOUT;
|
|
2244
|
+
if (s === "PC_ENGINE" || s === "TURBOGRAFX") return TWO_BUTTON_LAYOUT;
|
|
2245
|
+
if (s.includes("WONDERSWAN")) return TWO_BUTTON_LAYOUT;
|
|
2246
|
+
if (s.includes("ATARI")) return TWO_BUTTON_LAYOUT;
|
|
2247
|
+
return TWO_BUTTON_LAYOUT;
|
|
2165
2248
|
}
|
|
2166
|
-
var DRAG_HOLD_DELAY =
|
|
2249
|
+
var DRAG_HOLD_DELAY = 350;
|
|
2167
2250
|
var DRAG_MOVE_THRESHOLD = 10;
|
|
2168
2251
|
var DRAG_CENTER_THRESHOLD = 0.4;
|
|
2169
2252
|
function useTouchHandlers({
|
|
@@ -2196,6 +2279,9 @@ function useTouchHandlers({
|
|
|
2196
2279
|
x: touchX - displayX / 100 * containerWidth,
|
|
2197
2280
|
y: touchY - displayY / 100 * containerHeight
|
|
2198
2281
|
};
|
|
2282
|
+
if (navigator.vibrate) {
|
|
2283
|
+
navigator.vibrate([10, 30, 10]);
|
|
2284
|
+
}
|
|
2199
2285
|
if (!isSystemButton) {
|
|
2200
2286
|
onRelease(buttonType);
|
|
2201
2287
|
}
|
|
@@ -2311,55 +2397,119 @@ function useTouchHandlers({
|
|
|
2311
2397
|
}
|
|
2312
2398
|
|
|
2313
2399
|
// src/components/VirtualController/utils/buttonStyles.ts
|
|
2314
|
-
|
|
2400
|
+
var DEFAULT_FACE = {
|
|
2401
|
+
bg: "bg-white/10 backdrop-blur-md",
|
|
2402
|
+
text: "text-white",
|
|
2403
|
+
border: "border-white/30",
|
|
2404
|
+
shadow: "shadow-lg",
|
|
2405
|
+
transform: ""
|
|
2406
|
+
};
|
|
2407
|
+
var DEFAULT_SHOULDER = {
|
|
2408
|
+
bg: "bg-white/20 backdrop-blur-md",
|
|
2409
|
+
text: "text-white",
|
|
2410
|
+
border: "border-white/30",
|
|
2411
|
+
shadow: "shadow-sm",
|
|
2412
|
+
transform: ""
|
|
2413
|
+
};
|
|
2414
|
+
var DEFAULT_SYSTEM = {
|
|
2415
|
+
bg: "bg-black/60 backdrop-blur-md",
|
|
2416
|
+
text: "text-white/80",
|
|
2417
|
+
border: "border-white/20",
|
|
2418
|
+
shadow: "shadow-sm",
|
|
2419
|
+
transform: ""
|
|
2420
|
+
};
|
|
2421
|
+
var PRESSED_STYLE = {
|
|
2422
|
+
bg: "bg-white/90",
|
|
2423
|
+
text: "text-black",
|
|
2424
|
+
border: "border-white",
|
|
2425
|
+
shadow: "shadow-none",
|
|
2426
|
+
transform: "scale(0.95)"
|
|
2427
|
+
};
|
|
2428
|
+
function coloredButton(bgColor, textColor = "text-white") {
|
|
2429
|
+
return {
|
|
2430
|
+
bg: bgColor,
|
|
2431
|
+
text: textColor,
|
|
2432
|
+
border: "border-white/20",
|
|
2433
|
+
shadow: "shadow-lg",
|
|
2434
|
+
transform: ""
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
function psButton(symbolColor) {
|
|
2438
|
+
return {
|
|
2439
|
+
bg: "bg-black/80 backdrop-blur-md",
|
|
2440
|
+
text: symbolColor,
|
|
2441
|
+
border: "border-white/30",
|
|
2442
|
+
shadow: "shadow-lg",
|
|
2443
|
+
transform: ""
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
function getButtonStyles(buttonType, isPressed, consoleName = "") {
|
|
2315
2447
|
if (isPressed) {
|
|
2316
|
-
return
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2448
|
+
return PRESSED_STYLE;
|
|
2449
|
+
}
|
|
2450
|
+
const c = consoleName.toUpperCase();
|
|
2451
|
+
if (c === "NEOGEO") {
|
|
2452
|
+
switch (buttonType) {
|
|
2453
|
+
case "b":
|
|
2454
|
+
return coloredButton("bg-[#E60012]");
|
|
2455
|
+
// A = Red
|
|
2456
|
+
case "a":
|
|
2457
|
+
return coloredButton("bg-[#FFD600]", "text-black");
|
|
2458
|
+
// B = Yellow
|
|
2459
|
+
case "y":
|
|
2460
|
+
return coloredButton("bg-[#009944]");
|
|
2461
|
+
// C = Green
|
|
2462
|
+
case "x":
|
|
2463
|
+
return coloredButton("bg-[#0068B7]");
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
if (c === "PSX" || c === "PS1" || c === "PSP") {
|
|
2467
|
+
switch (buttonType) {
|
|
2468
|
+
case "y":
|
|
2469
|
+
return psButton("text-[#25D998]");
|
|
2470
|
+
// △ = Green
|
|
2471
|
+
case "b":
|
|
2472
|
+
return psButton("text-[#FF5555]");
|
|
2473
|
+
// ○ = Red
|
|
2474
|
+
case "a":
|
|
2475
|
+
return psButton("text-[#5599FF]");
|
|
2476
|
+
// ✕ = Blue
|
|
2477
|
+
case "x":
|
|
2478
|
+
return psButton("text-[#E889DD]");
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (c === "SNES" || c === "NDS") {
|
|
2482
|
+
switch (buttonType) {
|
|
2483
|
+
case "a":
|
|
2484
|
+
return coloredButton("bg-[#CC0000]");
|
|
2485
|
+
// A = Red
|
|
2486
|
+
case "b":
|
|
2487
|
+
return coloredButton("bg-[#CCCC00]", "text-black");
|
|
2488
|
+
// B = Yellow
|
|
2489
|
+
case "x":
|
|
2490
|
+
return coloredButton("bg-[#0033CC]");
|
|
2491
|
+
// X = Blue
|
|
2492
|
+
case "y":
|
|
2493
|
+
return coloredButton("bg-[#006600]");
|
|
2494
|
+
}
|
|
2323
2495
|
}
|
|
2324
2496
|
switch (buttonType) {
|
|
2325
2497
|
case "a":
|
|
2326
2498
|
case "b":
|
|
2327
|
-
case "c":
|
|
2328
|
-
return {
|
|
2329
|
-
bg: "bg-red-600",
|
|
2330
|
-
text: "text-white",
|
|
2331
|
-
border: "border-white",
|
|
2332
|
-
shadow: "shadow-hard",
|
|
2333
|
-
transform: ""
|
|
2334
|
-
};
|
|
2335
2499
|
case "x":
|
|
2336
2500
|
case "y":
|
|
2337
|
-
return
|
|
2338
|
-
bg: "bg-blue-600",
|
|
2339
|
-
text: "text-white",
|
|
2340
|
-
border: "border-white",
|
|
2341
|
-
shadow: "shadow-hard",
|
|
2342
|
-
transform: ""
|
|
2343
|
-
};
|
|
2501
|
+
return DEFAULT_FACE;
|
|
2344
2502
|
case "l":
|
|
2345
2503
|
case "r":
|
|
2504
|
+
case "l2":
|
|
2505
|
+
case "r2":
|
|
2506
|
+
return DEFAULT_SHOULDER;
|
|
2346
2507
|
case "start":
|
|
2347
2508
|
case "select":
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
text: "text-white",
|
|
2351
|
-
border: "border-white",
|
|
2352
|
-
shadow: "shadow-hard",
|
|
2353
|
-
transform: ""
|
|
2354
|
-
};
|
|
2509
|
+
case "menu":
|
|
2510
|
+
return DEFAULT_SYSTEM;
|
|
2355
2511
|
default:
|
|
2356
|
-
return
|
|
2357
|
-
bg: "bg-black",
|
|
2358
|
-
text: "text-white",
|
|
2359
|
-
border: "border-white",
|
|
2360
|
-
shadow: "shadow-hard",
|
|
2361
|
-
transform: ""
|
|
2362
|
-
};
|
|
2512
|
+
return DEFAULT_FACE;
|
|
2363
2513
|
}
|
|
2364
2514
|
}
|
|
2365
2515
|
var VirtualButton = React2.memo(function VirtualButton2({
|
|
@@ -2373,12 +2523,11 @@ var VirtualButton = React2.memo(function VirtualButton2({
|
|
|
2373
2523
|
customPosition,
|
|
2374
2524
|
onPositionChange,
|
|
2375
2525
|
isLandscape = false,
|
|
2376
|
-
|
|
2377
|
-
// Default retro green
|
|
2526
|
+
console: console2 = ""
|
|
2378
2527
|
}) {
|
|
2379
2528
|
const t = useKoinTranslation();
|
|
2380
2529
|
const buttonRef = useRef(null);
|
|
2381
|
-
const isSystemButton = config.type === "start" || config.type === "select";
|
|
2530
|
+
const isSystemButton = config.type === "start" || config.type === "select" || config.type === "menu";
|
|
2382
2531
|
const displayX = customPosition ? customPosition.x : config.x;
|
|
2383
2532
|
const displayY = customPosition ? customPosition.y : config.y;
|
|
2384
2533
|
let label = config.label;
|
|
@@ -2421,63 +2570,78 @@ var VirtualButton = React2.memo(function VirtualButton2({
|
|
|
2421
2570
|
const leftPercent = displayX / 100 * containerWidth - config.size / 2;
|
|
2422
2571
|
const topPercent = displayY / 100 * containerHeight - config.size / 2;
|
|
2423
2572
|
const transform = `translate3d(${leftPercent.toFixed(1)}px, ${topPercent.toFixed(1)}px, 0)`;
|
|
2424
|
-
const styles = getButtonStyles(config.type, isPressed);
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2573
|
+
const styles = getButtonStyles(config.type, isPressed, console2);
|
|
2574
|
+
let borderRadius = "50%";
|
|
2575
|
+
let width = `${config.size}px`;
|
|
2576
|
+
if (config.shape === "pill") {
|
|
2577
|
+
borderRadius = "20px";
|
|
2578
|
+
width = `${config.size * 1.8}px`;
|
|
2579
|
+
} else if (config.shape === "rect") {
|
|
2580
|
+
borderRadius = "8px";
|
|
2581
|
+
width = `${config.size * 2.5}px`;
|
|
2582
|
+
} else if (config.shape === "square") {
|
|
2583
|
+
borderRadius = "12px";
|
|
2584
|
+
} else {
|
|
2585
|
+
if (["l", "r", "l2", "r2"].includes(config.type)) {
|
|
2586
|
+
borderRadius = "10px";
|
|
2587
|
+
width = `${config.size * 2}px`;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2432
2590
|
return /* @__PURE__ */ jsx(
|
|
2433
2591
|
"button",
|
|
2434
2592
|
{
|
|
2435
2593
|
ref: buttonRef,
|
|
2436
2594
|
className: `
|
|
2437
|
-
absolute
|
|
2438
|
-
|
|
2595
|
+
absolute flex items-center justify-center
|
|
2596
|
+
font-heading font-bold uppercase tracking-wider
|
|
2597
|
+
transition-all duration-75 select-none
|
|
2439
2598
|
pointer-events-auto touch-manipulation
|
|
2440
|
-
|
|
2441
|
-
|
|
2599
|
+
backdrop-blur-sm
|
|
2600
|
+
${isPressed ? "" : `${styles.bg} ${styles.border} ${styles.shadow}`}
|
|
2601
|
+
${styles.text}
|
|
2602
|
+
active:shadow-none
|
|
2442
2603
|
`,
|
|
2443
2604
|
style: {
|
|
2444
|
-
// Remove left/top and use transform instead for high performance (compositor only)
|
|
2445
2605
|
top: 0,
|
|
2446
2606
|
left: 0,
|
|
2447
|
-
transform,
|
|
2607
|
+
transform: transform + (isPressed ? " scale(0.95)" : ""),
|
|
2448
2608
|
willChange: "transform",
|
|
2449
|
-
width
|
|
2609
|
+
width,
|
|
2450
2610
|
height: `${config.size}px`,
|
|
2611
|
+
// Height stays consistent
|
|
2451
2612
|
minWidth: `${config.size}px`,
|
|
2452
|
-
|
|
2453
|
-
fontSize: config.size < 50 ? "10px" : "12px",
|
|
2613
|
+
fontSize: config.size < 50 ? "11px" : "16px",
|
|
2454
2614
|
borderRadius,
|
|
2615
|
+
borderWidth: isPressed ? "0px" : "1.5px",
|
|
2616
|
+
// slightly thicker border
|
|
2455
2617
|
lineHeight: "1",
|
|
2456
2618
|
// Semi-transparent in landscape mode
|
|
2457
2619
|
opacity: isLandscape ? 0.85 : 1,
|
|
2458
|
-
// Prevent context menu on long-press
|
|
2459
2620
|
WebkitTouchCallout: "none",
|
|
2460
2621
|
WebkitUserSelect: "none",
|
|
2461
2622
|
userSelect: "none",
|
|
2462
|
-
|
|
2623
|
+
// Direct style overrides if needed from getButtonStyles (for exact hex colors)
|
|
2624
|
+
backgroundColor: isPressed ? styles.bg.startsWith("bg-") ? void 0 : styles.bg : styles.bg.startsWith("bg-") ? void 0 : styles.bg,
|
|
2625
|
+
borderColor: isPressed ? void 0 : styles.border.startsWith("border-") ? void 0 : styles.border,
|
|
2626
|
+
color: styles.text.startsWith("text-") ? void 0 : styles.text
|
|
2463
2627
|
},
|
|
2464
2628
|
"aria-label": label,
|
|
2465
2629
|
onContextMenu: (e) => e.preventDefault(),
|
|
2466
|
-
children: label
|
|
2630
|
+
children: /* @__PURE__ */ jsx("span", { className: "drop-shadow-md", children: label })
|
|
2467
2631
|
}
|
|
2468
2632
|
);
|
|
2469
2633
|
});
|
|
2470
2634
|
var VirtualButton_default = VirtualButton;
|
|
2471
2635
|
|
|
2472
2636
|
// src/lib/controls/types.ts
|
|
2473
|
-
var
|
|
2637
|
+
var DPAD_BUTTONS2 = ["up", "down", "left", "right"];
|
|
2474
2638
|
var FACE_BUTTONS = ["a", "b", "x", "y"];
|
|
2475
2639
|
var SHOULDER_BUTTONS = ["l", "r"];
|
|
2476
2640
|
var TRIGGER_BUTTONS = ["l2", "r2"];
|
|
2477
2641
|
var STICK_BUTTONS = ["l3", "r3"];
|
|
2478
2642
|
var SYSTEM_BUTTONS = ["start", "select"];
|
|
2479
2643
|
var ALL_BUTTONS = [
|
|
2480
|
-
...
|
|
2644
|
+
...DPAD_BUTTONS2,
|
|
2481
2645
|
...FACE_BUTTONS,
|
|
2482
2646
|
...SHOULDER_BUTTONS,
|
|
2483
2647
|
...TRIGGER_BUTTONS,
|
|
@@ -2635,91 +2799,91 @@ var CONSOLE_CAPABILITIES = {
|
|
|
2635
2799
|
// ============ Nintendo ============
|
|
2636
2800
|
NES: {
|
|
2637
2801
|
console: "NES",
|
|
2638
|
-
buttons: [...
|
|
2802
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2639
2803
|
},
|
|
2640
2804
|
SNES: {
|
|
2641
2805
|
console: "SNES",
|
|
2642
|
-
buttons: [...
|
|
2806
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "start", "select"]
|
|
2643
2807
|
},
|
|
2644
2808
|
N64: {
|
|
2645
2809
|
console: "N64",
|
|
2646
|
-
buttons: [...
|
|
2810
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "l", "r", "start"]
|
|
2647
2811
|
// No select, has Z trigger (mapped to l2)
|
|
2648
2812
|
},
|
|
2649
2813
|
GB: {
|
|
2650
2814
|
console: "GB",
|
|
2651
|
-
buttons: [...
|
|
2815
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2652
2816
|
},
|
|
2653
2817
|
GBC: {
|
|
2654
2818
|
console: "GBC",
|
|
2655
|
-
buttons: [...
|
|
2819
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2656
2820
|
},
|
|
2657
2821
|
GBA: {
|
|
2658
2822
|
console: "GBA",
|
|
2659
|
-
buttons: [...
|
|
2823
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "l", "r", "start", "select"]
|
|
2660
2824
|
},
|
|
2661
2825
|
// ============ Sega ============
|
|
2662
2826
|
SMS: {
|
|
2663
2827
|
console: "SMS",
|
|
2664
|
-
buttons: [...
|
|
2828
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start"]
|
|
2665
2829
|
// 1, 2, Pause
|
|
2666
2830
|
},
|
|
2667
2831
|
GENESIS: {
|
|
2668
2832
|
console: "GENESIS",
|
|
2669
|
-
buttons: [...
|
|
2833
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "start"]
|
|
2670
2834
|
// 6-button: A/B/C + X/Y/Z
|
|
2671
2835
|
},
|
|
2672
2836
|
GG: {
|
|
2673
2837
|
console: "GG",
|
|
2674
|
-
buttons: [...
|
|
2838
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start"]
|
|
2675
2839
|
},
|
|
2676
2840
|
SATURN: {
|
|
2677
2841
|
console: "SATURN",
|
|
2678
|
-
buttons: [...
|
|
2842
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "l2", "r2", "start"]
|
|
2679
2843
|
// Full 8-button
|
|
2680
2844
|
},
|
|
2681
2845
|
// ============ Sony ============
|
|
2682
2846
|
PS1: {
|
|
2683
2847
|
console: "PS1",
|
|
2684
|
-
buttons: [...
|
|
2848
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "l2", "r2", "l3", "r3", "start", "select"]
|
|
2685
2849
|
},
|
|
2686
2850
|
// ============ NEC ============
|
|
2687
2851
|
PCE: {
|
|
2688
2852
|
console: "PCE",
|
|
2689
2853
|
// TurboGrafx-16
|
|
2690
|
-
buttons: [...
|
|
2854
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2691
2855
|
// I, II, Run, Select
|
|
2692
2856
|
},
|
|
2693
2857
|
// ============ Atari ============
|
|
2694
2858
|
ATARI2600: {
|
|
2695
2859
|
console: "ATARI2600",
|
|
2696
|
-
buttons: [...
|
|
2860
|
+
buttons: [...DPAD_BUTTONS2, "a", "select"]
|
|
2697
2861
|
// Fire, Select/Reset
|
|
2698
2862
|
},
|
|
2699
2863
|
ATARI7800: {
|
|
2700
2864
|
console: "ATARI7800",
|
|
2701
|
-
buttons: [...
|
|
2865
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "select"]
|
|
2702
2866
|
},
|
|
2703
2867
|
LYNX: {
|
|
2704
2868
|
console: "LYNX",
|
|
2705
|
-
buttons: [...
|
|
2869
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2706
2870
|
// A, B, Option 1, Option 2
|
|
2707
2871
|
},
|
|
2708
2872
|
// ============ SNK ============
|
|
2709
2873
|
NGPC: {
|
|
2710
2874
|
console: "NGPC",
|
|
2711
2875
|
// Neo Geo Pocket
|
|
2712
|
-
buttons: [...
|
|
2876
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "start", "select"]
|
|
2713
2877
|
},
|
|
2714
2878
|
// ============ Other ============
|
|
2715
2879
|
WSC: {
|
|
2716
2880
|
console: "WSC",
|
|
2717
2881
|
// WonderSwan
|
|
2718
|
-
buttons: [...
|
|
2882
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "start"]
|
|
2719
2883
|
},
|
|
2720
2884
|
ARCADE: {
|
|
2721
2885
|
console: "ARCADE",
|
|
2722
|
-
buttons: [...
|
|
2886
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "start", "select"]
|
|
2723
2887
|
// Generic 6-button
|
|
2724
2888
|
}
|
|
2725
2889
|
};
|
|
@@ -2760,7 +2924,7 @@ function getConsoleCapabilities(system) {
|
|
|
2760
2924
|
const normalized = system.toUpperCase();
|
|
2761
2925
|
return CONSOLE_CAPABILITIES[normalized] ?? {
|
|
2762
2926
|
console: normalized,
|
|
2763
|
-
buttons: [...
|
|
2927
|
+
buttons: [...DPAD_BUTTONS2, "a", "b", "x", "y", "l", "r", "start", "select"]
|
|
2764
2928
|
};
|
|
2765
2929
|
}
|
|
2766
2930
|
function getConsoleButtons(system) {
|
|
@@ -3048,13 +3212,11 @@ var DEFAULT_CONTROLS = DEFAULT_KEYBOARD;
|
|
|
3048
3212
|
|
|
3049
3213
|
// src/components/VirtualController/utils/keyboardEvents.ts
|
|
3050
3214
|
function getKeyboardCode(buttonType, controls) {
|
|
3051
|
-
const
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
if (key in controlMapping) {
|
|
3055
|
-
return controlMapping[key] ?? null;
|
|
3215
|
+
const key = buttonType;
|
|
3216
|
+
if (controls && key in controls && controls[key]) {
|
|
3217
|
+
return controls[key];
|
|
3056
3218
|
}
|
|
3057
|
-
return null;
|
|
3219
|
+
return DEFAULT_CONTROLS[key] ?? null;
|
|
3058
3220
|
}
|
|
3059
3221
|
function getKeyName(code) {
|
|
3060
3222
|
if (code.startsWith("Key")) return code.slice(3).toLowerCase();
|
|
@@ -3080,23 +3242,34 @@ function dispatchKeyboardEvent(type, code) {
|
|
|
3080
3242
|
canvas.dispatchEvent(event);
|
|
3081
3243
|
return true;
|
|
3082
3244
|
}
|
|
3245
|
+
var DRAG_HOLD_DELAY2 = 350;
|
|
3246
|
+
var CENTER_TOUCH_RADIUS = 0.25;
|
|
3083
3247
|
var Dpad = React2.memo(function Dpad2({
|
|
3084
|
-
size,
|
|
3248
|
+
size = 180,
|
|
3085
3249
|
x,
|
|
3086
3250
|
y,
|
|
3087
3251
|
containerWidth,
|
|
3088
3252
|
containerHeight,
|
|
3089
3253
|
controls,
|
|
3090
3254
|
systemColor = "#00FF41",
|
|
3091
|
-
isLandscape = false
|
|
3255
|
+
isLandscape = false,
|
|
3256
|
+
customPosition,
|
|
3257
|
+
onPositionChange
|
|
3092
3258
|
}) {
|
|
3093
3259
|
const dpadRef = useRef(null);
|
|
3094
3260
|
const activeTouchRef = useRef(null);
|
|
3095
3261
|
const activeDirectionsRef = useRef(/* @__PURE__ */ new Set());
|
|
3096
|
-
const
|
|
3097
|
-
const
|
|
3098
|
-
const
|
|
3099
|
-
const
|
|
3262
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
3263
|
+
const dragTimerRef = useRef(null);
|
|
3264
|
+
const dragStartRef = useRef({ x: 0, y: 0, touchX: 0, touchY: 0 });
|
|
3265
|
+
const touchStartPosRef = useRef({ x: 0, y: 0, time: 0 });
|
|
3266
|
+
const upPathRef = useRef(null);
|
|
3267
|
+
const downPathRef = useRef(null);
|
|
3268
|
+
const leftPathRef = useRef(null);
|
|
3269
|
+
const rightPathRef = useRef(null);
|
|
3270
|
+
const centerCircleRef = useRef(null);
|
|
3271
|
+
const displayX = customPosition ? customPosition.x : x;
|
|
3272
|
+
const displayY = customPosition ? customPosition.y : y;
|
|
3100
3273
|
const getKeyCode = useCallback((direction) => {
|
|
3101
3274
|
if (!controls) {
|
|
3102
3275
|
const defaults = {
|
|
@@ -3114,24 +3287,41 @@ var Dpad = React2.memo(function Dpad2({
|
|
|
3114
3287
|
const centerY = rect.top + rect.height / 2;
|
|
3115
3288
|
const dx = touchX - centerX;
|
|
3116
3289
|
const dy = touchY - centerY;
|
|
3117
|
-
const deadZone = rect.width * 0.12;
|
|
3118
3290
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
3291
|
+
const deadZone = rect.width / 2 * 0.15;
|
|
3119
3292
|
if (distance < deadZone) return /* @__PURE__ */ new Set();
|
|
3120
3293
|
const directions = /* @__PURE__ */ new Set();
|
|
3121
3294
|
const angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
3122
|
-
if (angle >= -
|
|
3123
|
-
if (angle >=
|
|
3124
|
-
if (angle >=
|
|
3125
|
-
if (angle >= -
|
|
3295
|
+
if (angle >= -150 && angle <= -30) directions.add("up");
|
|
3296
|
+
if (angle >= 30 && angle <= 150) directions.add("down");
|
|
3297
|
+
if (angle >= 120 || angle <= -120) directions.add("left");
|
|
3298
|
+
if (angle >= -60 && angle <= 60) directions.add("right");
|
|
3126
3299
|
return directions;
|
|
3127
3300
|
}, []);
|
|
3128
3301
|
const updateVisuals = useCallback((directions) => {
|
|
3129
|
-
const
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3302
|
+
const activeFill = `${systemColor}80`;
|
|
3303
|
+
const inactiveFill = "rgba(255, 255, 255, 0.05)";
|
|
3304
|
+
const activeStroke = systemColor;
|
|
3305
|
+
const inactiveStroke = "rgba(255, 255, 255, 0.2)";
|
|
3306
|
+
const glow = `0 0 15px ${systemColor}`;
|
|
3307
|
+
const updatePart = (ref, isActive) => {
|
|
3308
|
+
if (ref.current) {
|
|
3309
|
+
ref.current.style.fill = isActive ? activeFill : inactiveFill;
|
|
3310
|
+
ref.current.style.stroke = isActive ? activeStroke : inactiveStroke;
|
|
3311
|
+
ref.current.style.filter = isActive ? `drop-shadow(${glow})` : "none";
|
|
3312
|
+
ref.current.style.transform = isActive ? "scale(0.98)" : "scale(1)";
|
|
3313
|
+
ref.current.style.transformOrigin = "center";
|
|
3314
|
+
}
|
|
3315
|
+
};
|
|
3316
|
+
updatePart(upPathRef, directions.has("up"));
|
|
3317
|
+
updatePart(downPathRef, directions.has("down"));
|
|
3318
|
+
updatePart(leftPathRef, directions.has("left"));
|
|
3319
|
+
updatePart(rightPathRef, directions.has("right"));
|
|
3320
|
+
if (centerCircleRef.current) {
|
|
3321
|
+
const isAny = directions.size > 0;
|
|
3322
|
+
centerCircleRef.current.style.fill = isAny ? systemColor : "rgba(0,0,0,0.5)";
|
|
3323
|
+
centerCircleRef.current.style.stroke = isAny ? "#fff" : "rgba(255,255,255,0.3)";
|
|
3324
|
+
}
|
|
3135
3325
|
}, [systemColor]);
|
|
3136
3326
|
const updateDirections = useCallback((newDirections) => {
|
|
3137
3327
|
const prev = activeDirectionsRef.current;
|
|
@@ -3153,48 +3343,107 @@ var Dpad = React2.memo(function Dpad2({
|
|
|
3153
3343
|
activeDirectionsRef.current = newDirections;
|
|
3154
3344
|
updateVisuals(newDirections);
|
|
3155
3345
|
}, [getKeyCode, updateVisuals]);
|
|
3346
|
+
const clearDragTimer = useCallback(() => {
|
|
3347
|
+
if (dragTimerRef.current) {
|
|
3348
|
+
clearTimeout(dragTimerRef.current);
|
|
3349
|
+
dragTimerRef.current = null;
|
|
3350
|
+
}
|
|
3351
|
+
}, []);
|
|
3352
|
+
const startDragging = useCallback((touchX, touchY) => {
|
|
3353
|
+
setIsDragging(true);
|
|
3354
|
+
dragStartRef.current = {
|
|
3355
|
+
x: displayX,
|
|
3356
|
+
y: displayY,
|
|
3357
|
+
touchX,
|
|
3358
|
+
touchY
|
|
3359
|
+
};
|
|
3360
|
+
activeDirectionsRef.current.forEach((dir) => {
|
|
3361
|
+
const keyCode = getKeyCode(dir);
|
|
3362
|
+
if (keyCode) dispatchKeyboardEvent("keyup", keyCode);
|
|
3363
|
+
});
|
|
3364
|
+
activeDirectionsRef.current = /* @__PURE__ */ new Set();
|
|
3365
|
+
updateVisuals(/* @__PURE__ */ new Set());
|
|
3366
|
+
if (navigator.vibrate) navigator.vibrate([10, 30, 10]);
|
|
3367
|
+
}, [displayX, displayY, getKeyCode, updateVisuals]);
|
|
3156
3368
|
const handleTouchStart = useCallback((e) => {
|
|
3157
3369
|
e.preventDefault();
|
|
3158
|
-
|
|
3159
|
-
const touch = e.
|
|
3370
|
+
if (activeTouchRef.current !== null) return;
|
|
3371
|
+
const touch = e.changedTouches[0];
|
|
3160
3372
|
activeTouchRef.current = touch.identifier;
|
|
3373
|
+
touchStartPosRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() };
|
|
3161
3374
|
const rect = dpadRef.current?.getBoundingClientRect();
|
|
3162
3375
|
if (!rect) return;
|
|
3163
|
-
|
|
3164
|
-
|
|
3376
|
+
const centerX = rect.left + rect.width / 2;
|
|
3377
|
+
const centerY = rect.top + rect.height / 2;
|
|
3378
|
+
const distFromCenter = Math.sqrt(
|
|
3379
|
+
Math.pow(touch.clientX - centerX, 2) + Math.pow(touch.clientY - centerY, 2)
|
|
3380
|
+
);
|
|
3381
|
+
const centerRadius = size * CENTER_TOUCH_RADIUS;
|
|
3382
|
+
if (distFromCenter < centerRadius && onPositionChange) {
|
|
3383
|
+
dragTimerRef.current = setTimeout(() => {
|
|
3384
|
+
startDragging(touch.clientX, touch.clientY);
|
|
3385
|
+
}, DRAG_HOLD_DELAY2);
|
|
3386
|
+
}
|
|
3387
|
+
if (!isDragging) {
|
|
3388
|
+
updateDirections(getDirectionsFromTouch(touch.clientX, touch.clientY, rect));
|
|
3389
|
+
}
|
|
3390
|
+
}, [getDirectionsFromTouch, updateDirections, isDragging, size, onPositionChange, startDragging]);
|
|
3165
3391
|
const handleTouchMove = useCallback((e) => {
|
|
3166
3392
|
e.preventDefault();
|
|
3167
3393
|
let touch = null;
|
|
3168
|
-
for (let i = 0; i < e.
|
|
3169
|
-
if (e.
|
|
3170
|
-
touch = e.
|
|
3394
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
3395
|
+
if (e.changedTouches[i].identifier === activeTouchRef.current) {
|
|
3396
|
+
touch = e.changedTouches[i];
|
|
3171
3397
|
break;
|
|
3172
3398
|
}
|
|
3173
3399
|
}
|
|
3174
3400
|
if (!touch) return;
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3401
|
+
if (isDragging && onPositionChange) {
|
|
3402
|
+
const deltaX = touch.clientX - dragStartRef.current.touchX;
|
|
3403
|
+
const deltaY = touch.clientY - dragStartRef.current.touchY;
|
|
3404
|
+
const newXPercent = dragStartRef.current.x + deltaX / containerWidth * 100;
|
|
3405
|
+
const newYPercent = dragStartRef.current.y + deltaY / containerHeight * 100;
|
|
3406
|
+
const margin = size / 2 / Math.min(containerWidth, containerHeight) * 100;
|
|
3407
|
+
const constrainedX = Math.max(margin, Math.min(100 - margin, newXPercent));
|
|
3408
|
+
const constrainedY = Math.max(margin, Math.min(100 - margin, newYPercent));
|
|
3409
|
+
onPositionChange(constrainedX, constrainedY);
|
|
3410
|
+
} else {
|
|
3411
|
+
const moveDistance = Math.sqrt(
|
|
3412
|
+
Math.pow(touch.clientX - touchStartPosRef.current.x, 2) + Math.pow(touch.clientY - touchStartPosRef.current.y, 2)
|
|
3413
|
+
);
|
|
3414
|
+
if (moveDistance > 15) {
|
|
3415
|
+
clearDragTimer();
|
|
3416
|
+
}
|
|
3417
|
+
const rect = dpadRef.current?.getBoundingClientRect();
|
|
3418
|
+
if (rect) {
|
|
3419
|
+
updateDirections(getDirectionsFromTouch(touch.clientX, touch.clientY, rect));
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}, [isDragging, onPositionChange, containerWidth, containerHeight, size, getDirectionsFromTouch, updateDirections, clearDragTimer]);
|
|
3179
3423
|
const handleTouchEnd = useCallback((e) => {
|
|
3180
3424
|
e.preventDefault();
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3425
|
+
clearDragTimer();
|
|
3426
|
+
let touchEnded = false;
|
|
3427
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
3428
|
+
if (e.changedTouches[i].identifier === activeTouchRef.current) {
|
|
3429
|
+
touchEnded = true;
|
|
3185
3430
|
break;
|
|
3186
3431
|
}
|
|
3187
3432
|
}
|
|
3188
3433
|
if (touchEnded) {
|
|
3189
3434
|
activeTouchRef.current = null;
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3435
|
+
if (isDragging) {
|
|
3436
|
+
setIsDragging(false);
|
|
3437
|
+
} else {
|
|
3438
|
+
activeDirectionsRef.current.forEach((dir) => {
|
|
3439
|
+
const keyCode = getKeyCode(dir);
|
|
3440
|
+
if (keyCode) dispatchKeyboardEvent("keyup", keyCode);
|
|
3441
|
+
});
|
|
3442
|
+
activeDirectionsRef.current = /* @__PURE__ */ new Set();
|
|
3443
|
+
updateVisuals(/* @__PURE__ */ new Set());
|
|
3444
|
+
}
|
|
3196
3445
|
}
|
|
3197
|
-
}, [getKeyCode, updateVisuals]);
|
|
3446
|
+
}, [getKeyCode, updateVisuals, isDragging, clearDragTimer]);
|
|
3198
3447
|
useEffect(() => {
|
|
3199
3448
|
const dpad = dpadRef.current;
|
|
3200
3449
|
if (!dpad) return;
|
|
@@ -3207,37 +3456,57 @@ var Dpad = React2.memo(function Dpad2({
|
|
|
3207
3456
|
dpad.removeEventListener("touchmove", handleTouchMove);
|
|
3208
3457
|
dpad.removeEventListener("touchend", handleTouchEnd);
|
|
3209
3458
|
dpad.removeEventListener("touchcancel", handleTouchEnd);
|
|
3459
|
+
clearDragTimer();
|
|
3210
3460
|
};
|
|
3211
|
-
}, [handleTouchStart, handleTouchMove, handleTouchEnd]);
|
|
3212
|
-
const leftPx =
|
|
3213
|
-
const topPx =
|
|
3214
|
-
|
|
3461
|
+
}, [handleTouchStart, handleTouchMove, handleTouchEnd, clearDragTimer]);
|
|
3462
|
+
const leftPx = displayX / 100 * containerWidth - size / 2;
|
|
3463
|
+
const topPx = displayY / 100 * containerHeight - size / 2;
|
|
3464
|
+
const dUp = "M 35,5 L 65,5 L 65,35 L 50,50 L 35,35 Z";
|
|
3465
|
+
const dRight = "M 65,35 L 95,35 L 95,65 L 65,65 L 50,50 Z";
|
|
3466
|
+
const dDown = "M 65,65 L 65,95 L 35,95 L 35,65 L 50,50 Z";
|
|
3467
|
+
const dLeft = "M 35,65 L 5,65 L 5,35 L 35,35 L 50,50 Z";
|
|
3468
|
+
return /* @__PURE__ */ jsxs(
|
|
3215
3469
|
"div",
|
|
3216
3470
|
{
|
|
3217
3471
|
ref: dpadRef,
|
|
3218
|
-
className:
|
|
3472
|
+
className: `absolute pointer-events-auto touch-manipulation select-none ${isDragging ? "opacity-60" : ""}`,
|
|
3219
3473
|
style: {
|
|
3220
3474
|
top: 0,
|
|
3221
3475
|
left: 0,
|
|
3222
|
-
transform: `translate3d(${leftPx}px, ${topPx}px, 0)`,
|
|
3476
|
+
transform: `translate3d(${leftPx}px, ${topPx}px, 0)${isDragging ? " scale(1.05)" : ""}`,
|
|
3223
3477
|
width: size,
|
|
3224
3478
|
height: size,
|
|
3225
|
-
opacity: isLandscape ? 0.
|
|
3479
|
+
opacity: isLandscape ? 0.75 : 0.9,
|
|
3226
3480
|
WebkitTouchCallout: "none",
|
|
3227
|
-
|
|
3481
|
+
WebkitUserSelect: "none",
|
|
3482
|
+
touchAction: "none",
|
|
3483
|
+
transition: isDragging ? "none" : "transform 0.1s ease-out"
|
|
3228
3484
|
},
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
/* @__PURE__ */
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3485
|
+
children: [
|
|
3486
|
+
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 rounded-full bg-black/40 backdrop-blur-md border shadow-lg ${isDragging ? "border-white/50 ring-2 ring-white/30" : "border-white/10"}` }),
|
|
3487
|
+
/* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", viewBox: "0 0 100 100", className: "drop-shadow-xl relative z-10", children: [
|
|
3488
|
+
/* @__PURE__ */ jsx("path", { ref: upPathRef, d: dUp, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
3489
|
+
/* @__PURE__ */ jsx("path", { ref: rightPathRef, d: dRight, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
3490
|
+
/* @__PURE__ */ jsx("path", { ref: downPathRef, d: dDown, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
3491
|
+
/* @__PURE__ */ jsx("path", { ref: leftPathRef, d: dLeft, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
3492
|
+
/* @__PURE__ */ jsx(
|
|
3493
|
+
"circle",
|
|
3494
|
+
{
|
|
3495
|
+
ref: centerCircleRef,
|
|
3496
|
+
cx: "50",
|
|
3497
|
+
cy: "50",
|
|
3498
|
+
r: "12",
|
|
3499
|
+
fill: isDragging ? systemColor : "rgba(0,0,0,0.5)",
|
|
3500
|
+
stroke: isDragging ? "#fff" : "rgba(255,255,255,0.3)",
|
|
3501
|
+
strokeWidth: isDragging ? 2 : 1
|
|
3502
|
+
}
|
|
3503
|
+
),
|
|
3504
|
+
/* @__PURE__ */ jsx("path", { d: "M 50,15 L 50,25 M 45,20 L 50,15 L 55,20", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.8", pointerEvents: "none" }),
|
|
3505
|
+
/* @__PURE__ */ jsx("path", { d: "M 50,85 L 50,75 M 45,80 L 50,85 L 55,80", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.8", pointerEvents: "none" }),
|
|
3506
|
+
/* @__PURE__ */ jsx("path", { d: "M 15,50 L 25,50 M 20,45 L 15,50 L 20,55", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.8", pointerEvents: "none" }),
|
|
3507
|
+
/* @__PURE__ */ jsx("path", { d: "M 85,50 L 75,50 M 80,45 L 85,50 L 80,55", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.8", pointerEvents: "none" })
|
|
3508
|
+
] })
|
|
3509
|
+
]
|
|
3241
3510
|
}
|
|
3242
3511
|
);
|
|
3243
3512
|
});
|
|
@@ -3323,6 +3592,72 @@ function useButtonPositions() {
|
|
|
3323
3592
|
resetPositions
|
|
3324
3593
|
};
|
|
3325
3594
|
}
|
|
3595
|
+
var STORAGE_KEY2 = "koin-controls-hint-shown";
|
|
3596
|
+
function ControlsHint({ isVisible }) {
|
|
3597
|
+
const [show, setShow] = useState(false);
|
|
3598
|
+
useEffect(() => {
|
|
3599
|
+
if (!isVisible) return;
|
|
3600
|
+
try {
|
|
3601
|
+
const wasShown = sessionStorage.getItem(STORAGE_KEY2);
|
|
3602
|
+
if (!wasShown) {
|
|
3603
|
+
const timer = setTimeout(() => setShow(true), 1500);
|
|
3604
|
+
return () => clearTimeout(timer);
|
|
3605
|
+
}
|
|
3606
|
+
} catch {
|
|
3607
|
+
}
|
|
3608
|
+
}, [isVisible]);
|
|
3609
|
+
const handleDismiss = () => {
|
|
3610
|
+
setShow(false);
|
|
3611
|
+
try {
|
|
3612
|
+
sessionStorage.setItem(STORAGE_KEY2, "true");
|
|
3613
|
+
} catch {
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
if (!show) return null;
|
|
3617
|
+
return /* @__PURE__ */ jsx(
|
|
3618
|
+
"div",
|
|
3619
|
+
{
|
|
3620
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm",
|
|
3621
|
+
onClick: handleDismiss,
|
|
3622
|
+
onTouchEnd: handleDismiss,
|
|
3623
|
+
children: /* @__PURE__ */ jsxs(
|
|
3624
|
+
"div",
|
|
3625
|
+
{
|
|
3626
|
+
className: "bg-black/90 border border-white/30 rounded-2xl p-6 mx-4 max-w-sm text-center shadow-2xl pointer-events-auto",
|
|
3627
|
+
onClick: (e) => e.stopPropagation(),
|
|
3628
|
+
onTouchEnd: (e) => e.stopPropagation(),
|
|
3629
|
+
children: [
|
|
3630
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-full bg-green-500/20 border-2 border-green-400 flex items-center justify-center", children: /* @__PURE__ */ jsx(Move, { size: 32, className: "text-green-400" }) }) }),
|
|
3631
|
+
/* @__PURE__ */ jsx("h3", { className: "text-white text-lg font-bold mb-2", children: "Customize Your Controls" }),
|
|
3632
|
+
/* @__PURE__ */ jsxs("p", { className: "text-white/70 text-sm mb-4", children: [
|
|
3633
|
+
/* @__PURE__ */ jsx("strong", { className: "text-white", children: "Long-press" }),
|
|
3634
|
+
" any button or the ",
|
|
3635
|
+
/* @__PURE__ */ jsx("strong", { className: "text-white", children: "D-pad center" }),
|
|
3636
|
+
" to drag and reposition it."
|
|
3637
|
+
] }),
|
|
3638
|
+
/* @__PURE__ */ jsx("p", { className: "text-white/50 text-xs mb-4", children: "Your layout is saved separately for portrait and landscape modes." }),
|
|
3639
|
+
/* @__PURE__ */ jsx(
|
|
3640
|
+
"button",
|
|
3641
|
+
{
|
|
3642
|
+
type: "button",
|
|
3643
|
+
onClick: (e) => {
|
|
3644
|
+
e.stopPropagation();
|
|
3645
|
+
handleDismiss();
|
|
3646
|
+
},
|
|
3647
|
+
onTouchEnd: (e) => {
|
|
3648
|
+
e.stopPropagation();
|
|
3649
|
+
handleDismiss();
|
|
3650
|
+
},
|
|
3651
|
+
className: "w-full py-3 px-4 bg-green-500 hover:bg-green-400 active:bg-green-600 text-black font-bold rounded-xl transition-colors flex items-center justify-center gap-2 cursor-pointer touch-manipulation",
|
|
3652
|
+
children: "Got it!"
|
|
3653
|
+
}
|
|
3654
|
+
)
|
|
3655
|
+
]
|
|
3656
|
+
}
|
|
3657
|
+
)
|
|
3658
|
+
}
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3326
3661
|
function VirtualController({
|
|
3327
3662
|
system,
|
|
3328
3663
|
isRunning,
|
|
@@ -3343,7 +3678,6 @@ function VirtualController({
|
|
|
3343
3678
|
return btn.showInLandscape;
|
|
3344
3679
|
});
|
|
3345
3680
|
const DPAD_TYPES = ["up", "down", "left", "right"];
|
|
3346
|
-
const dpadButtons = visibleButtons.filter((btn) => DPAD_TYPES.includes(btn.type));
|
|
3347
3681
|
useEffect(() => {
|
|
3348
3682
|
const updateSize = () => {
|
|
3349
3683
|
const { width, height } = getViewportSize();
|
|
@@ -3393,7 +3727,7 @@ function VirtualController({
|
|
|
3393
3727
|
},
|
|
3394
3728
|
[controls]
|
|
3395
3729
|
);
|
|
3396
|
-
const SYSTEM_BUTTONS2 = ["start", "select"];
|
|
3730
|
+
const SYSTEM_BUTTONS2 = ["start", "select", "menu"];
|
|
3397
3731
|
const handlePress = useCallback(
|
|
3398
3732
|
(buttonType) => {
|
|
3399
3733
|
const isSystemButton = SYSTEM_BUTTONS2.includes(buttonType);
|
|
@@ -3481,26 +3815,28 @@ function VirtualController({
|
|
|
3481
3815
|
if (!isMobile) {
|
|
3482
3816
|
return null;
|
|
3483
3817
|
}
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3818
|
+
const dpadSize = containerSize.width > containerSize.height ? 160 : 180;
|
|
3819
|
+
const dpadY = containerSize.width > containerSize.height ? 55 : 62;
|
|
3820
|
+
const finalDpadY = system.toUpperCase() === "NEOGEO" ? dpadY - 5 : dpadY;
|
|
3487
3821
|
return /* @__PURE__ */ jsxs(
|
|
3488
3822
|
"div",
|
|
3489
3823
|
{
|
|
3490
3824
|
className: "fixed inset-0 z-30 pointer-events-none",
|
|
3491
3825
|
style: { touchAction: "none" },
|
|
3492
3826
|
children: [
|
|
3493
|
-
|
|
3827
|
+
/* @__PURE__ */ jsx(
|
|
3494
3828
|
Dpad_default,
|
|
3495
3829
|
{
|
|
3496
|
-
size:
|
|
3497
|
-
x:
|
|
3498
|
-
y:
|
|
3830
|
+
size: dpadSize,
|
|
3831
|
+
x: 14,
|
|
3832
|
+
y: finalDpadY,
|
|
3499
3833
|
containerWidth: containerSize.width || window.innerWidth,
|
|
3500
3834
|
containerHeight: containerSize.height || window.innerHeight,
|
|
3501
3835
|
controls,
|
|
3502
3836
|
systemColor,
|
|
3503
|
-
isLandscape
|
|
3837
|
+
isLandscape,
|
|
3838
|
+
customPosition: getPosition("up", isLandscape),
|
|
3839
|
+
onPositionChange: (x, y) => savePosition("up", x, y, isLandscape)
|
|
3504
3840
|
}
|
|
3505
3841
|
),
|
|
3506
3842
|
memoizedButtonElements.filter(({ buttonConfig }) => !DPAD_TYPES.includes(buttonConfig.type)).map(({ buttonConfig, adjustedConfig, customPosition, width, height }) => /* @__PURE__ */ jsx(
|
|
@@ -3516,64 +3852,77 @@ function VirtualController({
|
|
|
3516
3852
|
customPosition,
|
|
3517
3853
|
onPositionChange: (x, y) => savePosition(buttonConfig.type, x, y, isLandscape),
|
|
3518
3854
|
isLandscape,
|
|
3519
|
-
|
|
3855
|
+
console: layout.console
|
|
3520
3856
|
},
|
|
3521
3857
|
buttonConfig.type
|
|
3522
|
-
))
|
|
3858
|
+
)),
|
|
3859
|
+
/* @__PURE__ */ jsx(ControlsHint, { isVisible: isRunning })
|
|
3523
3860
|
]
|
|
3524
3861
|
}
|
|
3525
3862
|
);
|
|
3526
3863
|
}
|
|
3527
3864
|
function FloatingExitButton({ onClick, disabled = false }) {
|
|
3528
|
-
return /* @__PURE__ */
|
|
3865
|
+
return /* @__PURE__ */ jsxs(
|
|
3529
3866
|
"button",
|
|
3530
3867
|
{
|
|
3531
3868
|
onClick,
|
|
3532
3869
|
disabled,
|
|
3533
3870
|
className: `
|
|
3534
|
-
fixed top-3
|
|
3535
|
-
|
|
3536
|
-
bg-black/
|
|
3537
|
-
border-2 border-
|
|
3538
|
-
shadow-
|
|
3539
|
-
flex items-center
|
|
3540
|
-
transition-all duration-
|
|
3871
|
+
fixed top-3 right-3 z-50
|
|
3872
|
+
px-3 py-2 rounded-xl
|
|
3873
|
+
bg-black/80 backdrop-blur-md
|
|
3874
|
+
border-2 border-red-400/60
|
|
3875
|
+
shadow-xl
|
|
3876
|
+
flex items-center gap-2
|
|
3877
|
+
transition-all duration-300
|
|
3541
3878
|
hover:bg-red-600/30 hover:border-red-400 hover:scale-105
|
|
3542
3879
|
active:scale-95
|
|
3543
3880
|
disabled:opacity-40 disabled:cursor-not-allowed
|
|
3544
3881
|
touch-manipulation
|
|
3545
3882
|
`,
|
|
3546
3883
|
style: {
|
|
3547
|
-
paddingTop: "env(safe-area-inset-top, 0px)"
|
|
3884
|
+
paddingTop: "max(env(safe-area-inset-top, 0px), 8px)"
|
|
3548
3885
|
},
|
|
3549
3886
|
"aria-label": "Exit fullscreen",
|
|
3550
|
-
|
|
3551
|
-
|
|
3887
|
+
children: [
|
|
3888
|
+
/* @__PURE__ */ jsx(X, { size: 16, className: "text-red-400" }),
|
|
3889
|
+
/* @__PURE__ */ jsx("span", { className: "text-white text-xs font-bold uppercase tracking-wider", children: "Exit" }),
|
|
3890
|
+
/* @__PURE__ */ jsx(Minimize2, { size: 14, className: "text-white/60" })
|
|
3891
|
+
]
|
|
3552
3892
|
}
|
|
3553
3893
|
);
|
|
3554
3894
|
}
|
|
3555
3895
|
function FloatingFullscreenButton({ onClick, disabled = false }) {
|
|
3556
|
-
|
|
3896
|
+
const [pulse, setPulse] = useState(true);
|
|
3897
|
+
useEffect(() => {
|
|
3898
|
+
const timer = setTimeout(() => setPulse(false), 5e3);
|
|
3899
|
+
return () => clearTimeout(timer);
|
|
3900
|
+
}, []);
|
|
3901
|
+
return /* @__PURE__ */ jsxs(
|
|
3557
3902
|
"button",
|
|
3558
3903
|
{
|
|
3559
3904
|
onClick,
|
|
3560
3905
|
disabled,
|
|
3561
3906
|
className: `
|
|
3562
3907
|
absolute top-3 left-3 z-50
|
|
3563
|
-
|
|
3564
|
-
bg-black/
|
|
3565
|
-
border-2 border-white/
|
|
3566
|
-
shadow-
|
|
3567
|
-
flex items-center
|
|
3568
|
-
transition-all duration-
|
|
3569
|
-
hover:bg-white/20 hover:border-white
|
|
3908
|
+
px-3 py-2 rounded-xl
|
|
3909
|
+
bg-black/80 backdrop-blur-md
|
|
3910
|
+
border-2 border-white/60
|
|
3911
|
+
shadow-xl
|
|
3912
|
+
flex items-center gap-2
|
|
3913
|
+
transition-all duration-300
|
|
3914
|
+
hover:bg-white/20 hover:border-white hover:scale-105
|
|
3570
3915
|
active:scale-95
|
|
3571
3916
|
disabled:opacity-40 disabled:cursor-not-allowed
|
|
3572
3917
|
touch-manipulation
|
|
3918
|
+
${pulse ? "animate-pulse border-green-400/80" : ""}
|
|
3573
3919
|
`,
|
|
3574
|
-
"aria-label": "
|
|
3575
|
-
|
|
3576
|
-
|
|
3920
|
+
"aria-label": "Tap for fullscreen controls",
|
|
3921
|
+
children: [
|
|
3922
|
+
/* @__PURE__ */ jsx(Gamepad2, { size: 18, className: "text-green-400" }),
|
|
3923
|
+
/* @__PURE__ */ jsx("span", { className: "text-white text-xs font-bold uppercase tracking-wider whitespace-nowrap", children: "Tap for Controls" }),
|
|
3924
|
+
/* @__PURE__ */ jsx(Maximize, { size: 14, className: "text-white/60" })
|
|
3925
|
+
]
|
|
3577
3926
|
}
|
|
3578
3927
|
);
|
|
3579
3928
|
}
|
|
@@ -7889,7 +8238,7 @@ function useGamePlayer(props) {
|
|
|
7889
8238
|
recordingSupported
|
|
7890
8239
|
};
|
|
7891
8240
|
}
|
|
7892
|
-
var
|
|
8241
|
+
var STORAGE_KEY3 = "koin-player-settings";
|
|
7893
8242
|
var DEFAULT_SETTINGS = {
|
|
7894
8243
|
volume: 1,
|
|
7895
8244
|
muted: false,
|
|
@@ -7902,7 +8251,7 @@ function usePlayerPersistence(onSettingsChange) {
|
|
|
7902
8251
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
7903
8252
|
useEffect(() => {
|
|
7904
8253
|
try {
|
|
7905
|
-
const stored = localStorage.getItem(
|
|
8254
|
+
const stored = localStorage.getItem(STORAGE_KEY3);
|
|
7906
8255
|
if (stored) {
|
|
7907
8256
|
const parsed = JSON.parse(stored);
|
|
7908
8257
|
setSettings((prev) => ({ ...prev, ...parsed }));
|
|
@@ -7916,7 +8265,7 @@ function usePlayerPersistence(onSettingsChange) {
|
|
|
7916
8265
|
setSettings((prev) => {
|
|
7917
8266
|
const next = { ...prev, ...updates };
|
|
7918
8267
|
try {
|
|
7919
|
-
localStorage.setItem(
|
|
8268
|
+
localStorage.setItem(STORAGE_KEY3, JSON.stringify(next));
|
|
7920
8269
|
} catch (e) {
|
|
7921
8270
|
console.error("Failed to save player settings", e);
|
|
7922
8271
|
}
|
|
@@ -9128,6 +9477,6 @@ var ShortcutsReference = memo(function ShortcutsReference2({
|
|
|
9128
9477
|
});
|
|
9129
9478
|
var ShortcutsReference_default = ShortcutsReference;
|
|
9130
9479
|
|
|
9131
|
-
export { ALL_BUTTONS, AchievementPopup, BUTTON_GROUPS, BUTTON_LABELS, CONSOLE_CAPABILITIES, CONSOLE_KEYBOARD_OVERRIDES, DEFAULT_CONTROLS, DEFAULT_GAMEPAD, DEFAULT_KEYBOARD, DPAD_BUTTONS, FACE_BUTTONS, GamePlayer_default as GamePlayer, KoinI18nProvider, PERFORMANCE_TIER_1_SYSTEMS, PERFORMANCE_TIER_2_SYSTEMS, RASidebar, RA_MEDIA_BASE, SHADER_PRESETS, SHOULDER_BUTTONS, STANDARD_AXIS_MAP, STANDARD_GAMEPAD_BUTTONS, STICK_BUTTONS, SUPPORTED_EXTENSIONS, SYSTEMS, SYSTEM_BUTTONS, ShaderSelector_default as ShaderSelector, ShortcutsReference_default as ShortcutsReference, TRIGGER_BUTTONS, ToastContainer, buildRetroArchConfig, clearAllControls, consoleHasButton, detectControllerBrand, detectSystem, en, es, fetchAndCacheRom, formatGamepadButton, formatKeyCode, fr, gamepadToRetroArchConfig, getAchievementBadgeUrl, getCachedRom, getConsoleButtons, getConsoleCapabilities, getConsoleKeyboardDefaults, getCore, getDBSystemNames, getFullGamepadMapping, getFullKeyboardMapping, getSupportedExtensions, getSystem, getSystemByDbName, getSystemByKey, getSystemFromExtension, getSystemsList, getUserAvatarUrl, isSystemSupported, keyboardToRetroArchConfig, loadAllGamepadMappings, loadGamepadMapping, loadKeyboardMapping, normalizeSystemKey, saveGamepadMapping, saveKeyboardMapping, systemsMatch, useGameRecording, useGamepad, useKoinTranslation, useNostalgist, useToast };
|
|
9480
|
+
export { ALL_BUTTONS, AchievementPopup, BUTTON_GROUPS, BUTTON_LABELS, CONSOLE_CAPABILITIES, CONSOLE_KEYBOARD_OVERRIDES, DEFAULT_CONTROLS, DEFAULT_GAMEPAD, DEFAULT_KEYBOARD, DPAD_BUTTONS2 as DPAD_BUTTONS, FACE_BUTTONS, GamePlayer_default as GamePlayer, KoinI18nProvider, PERFORMANCE_TIER_1_SYSTEMS, PERFORMANCE_TIER_2_SYSTEMS, RASidebar, RA_MEDIA_BASE, SHADER_PRESETS, SHOULDER_BUTTONS, STANDARD_AXIS_MAP, STANDARD_GAMEPAD_BUTTONS, STICK_BUTTONS, SUPPORTED_EXTENSIONS, SYSTEMS, SYSTEM_BUTTONS, ShaderSelector_default as ShaderSelector, ShortcutsReference_default as ShortcutsReference, TRIGGER_BUTTONS, ToastContainer, buildRetroArchConfig, clearAllControls, consoleHasButton, detectControllerBrand, detectSystem, en, es, fetchAndCacheRom, formatGamepadButton, formatKeyCode, fr, gamepadToRetroArchConfig, getAchievementBadgeUrl, getCachedRom, getConsoleButtons, getConsoleCapabilities, getConsoleKeyboardDefaults, getCore, getDBSystemNames, getFullGamepadMapping, getFullKeyboardMapping, getSupportedExtensions, getSystem, getSystemByDbName, getSystemByKey, getSystemFromExtension, getSystemsList, getUserAvatarUrl, isSystemSupported, keyboardToRetroArchConfig, loadAllGamepadMappings, loadGamepadMapping, loadKeyboardMapping, normalizeSystemKey, saveGamepadMapping, saveKeyboardMapping, systemsMatch, useGameRecording, useGamepad, useKoinTranslation, useNostalgist, useToast };
|
|
9132
9481
|
//# sourceMappingURL=index.mjs.map
|
|
9133
9482
|
//# sourceMappingURL=index.mjs.map
|