koin.js 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -20
- package/dist/index.d.mts +248 -1
- package/dist/index.d.ts +248 -1
- package/dist/index.js +1444 -506
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1169 -236
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +56 -10
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Play, Pause, RotateCcw, Rewind, Save, Download, Camera, Video, Circle, Monitor, ChevronDown, RefreshCw, HelpCircle, Maximize, Gamepad2, Joystick, Code, Power, Square, Keyboard, X, Palette, ChevronUp, Gauge, VolumeX, Volume1, Volume2, Loader2, Trophy, AlertTriangle, List,
|
|
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';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { Nostalgist } from 'nostalgist';
|
|
5
5
|
|
|
@@ -33,20 +33,23 @@ var ControlButton = memo(function ControlButton2({
|
|
|
33
33
|
onTouchEnd,
|
|
34
34
|
disabled,
|
|
35
35
|
className: `
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
flex flex-col items-center justify-center gap-1.5
|
|
37
|
+
px-3 py-2 rounded-lg
|
|
38
|
+
transition-all duration-200
|
|
39
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
40
|
+
hover:bg-white/10 active:bg-white/20
|
|
41
|
+
${active ? "bg-white/10 ring-1 ring-inset" : ""}
|
|
42
|
+
${danger ? "hover:bg-red-500/20 text-red-400" : "text-gray-400 hover:text-white"}
|
|
43
|
+
${className}
|
|
44
|
+
`,
|
|
42
45
|
style: active ? {
|
|
43
46
|
backgroundColor: `${systemColor}20`,
|
|
44
47
|
color: systemColor
|
|
45
48
|
} : {},
|
|
46
49
|
title: label,
|
|
47
50
|
children: [
|
|
48
|
-
/* @__PURE__ */ jsx(Icon, { size: iconSize, className:
|
|
49
|
-
/* @__PURE__ */ jsx("span", { className: "text-[9px] font-bold uppercase tracking-wider
|
|
51
|
+
/* @__PURE__ */ jsx(Icon, { size: iconSize, className: `transition-transform duration-200 ${active ? "scale-110" : "group-hover:scale-110"}`, style: active ? { color: systemColor } : void 0 }),
|
|
52
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] font-bold uppercase tracking-wider whitespace-nowrap", style: { color: active ? systemColor : void 0 }, children: label })
|
|
50
53
|
]
|
|
51
54
|
}
|
|
52
55
|
);
|
|
@@ -54,10 +57,21 @@ var ControlButton = memo(function ControlButton2({
|
|
|
54
57
|
var SPEED_OPTIONS = [1, 2];
|
|
55
58
|
function SpeedMenu({ speed, onSpeedChange, disabled = false }) {
|
|
56
59
|
const [showMenu, setShowMenu] = useState(false);
|
|
60
|
+
const buttonRef = React2.useRef(null);
|
|
61
|
+
const getMenuPosition = () => {
|
|
62
|
+
if (!buttonRef.current) return {};
|
|
63
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
64
|
+
return {
|
|
65
|
+
bottom: `${window.innerHeight - rect.top + 8}px`,
|
|
66
|
+
left: `${rect.left + rect.width / 2}px`,
|
|
67
|
+
transform: "translateX(-50%)"
|
|
68
|
+
};
|
|
69
|
+
};
|
|
57
70
|
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
58
71
|
/* @__PURE__ */ jsxs(
|
|
59
72
|
"button",
|
|
60
73
|
{
|
|
74
|
+
ref: buttonRef,
|
|
61
75
|
onClick: () => setShowMenu(!showMenu),
|
|
62
76
|
disabled,
|
|
63
77
|
className: `
|
|
@@ -84,25 +98,32 @@ function SpeedMenu({ speed, onSpeedChange, disabled = false }) {
|
|
|
84
98
|
}
|
|
85
99
|
),
|
|
86
100
|
showMenu && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
87
|
-
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-
|
|
88
|
-
/* @__PURE__ */ jsx(
|
|
89
|
-
"
|
|
101
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9998]", onClick: () => setShowMenu(false) }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"div",
|
|
90
104
|
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
className: "fixed z-[9999] bg-black/90 backdrop-blur-md border border-white/20 rounded-lg p-1.5 shadow-xl flex flex-col gap-1 min-w-[80px]",
|
|
106
|
+
style: getMenuPosition(),
|
|
107
|
+
children: SPEED_OPTIONS.map((s) => /* @__PURE__ */ jsxs(
|
|
108
|
+
"button",
|
|
109
|
+
{
|
|
110
|
+
onClick: () => {
|
|
111
|
+
onSpeedChange(s);
|
|
112
|
+
setShowMenu(false);
|
|
113
|
+
},
|
|
114
|
+
className: `
|
|
96
115
|
px-3 py-2 rounded text-xs font-mono font-bold text-center transition-colors
|
|
97
116
|
${speed === s ? "bg-white/20 text-white" : "hover:bg-white/10 text-gray-400 hover:text-white"}
|
|
98
117
|
`,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
children: [
|
|
119
|
+
s,
|
|
120
|
+
"x"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
s
|
|
124
|
+
))
|
|
125
|
+
}
|
|
126
|
+
)
|
|
106
127
|
] })
|
|
107
128
|
] });
|
|
108
129
|
}
|
|
@@ -171,6 +192,260 @@ function HardcoreTooltip({ show, message = "Disabled in Hardcore mode", children
|
|
|
171
192
|
/* @__PURE__ */ jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-amber-500/90 text-black text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50", children: message })
|
|
172
193
|
] });
|
|
173
194
|
}
|
|
195
|
+
|
|
196
|
+
// src/locales/en.ts
|
|
197
|
+
var en = {
|
|
198
|
+
controls: {
|
|
199
|
+
play: "Play",
|
|
200
|
+
pause: "Pause",
|
|
201
|
+
reset: "Reset",
|
|
202
|
+
rewind: "Rewind",
|
|
203
|
+
save: "Save",
|
|
204
|
+
load: "Load",
|
|
205
|
+
snap: "Snap",
|
|
206
|
+
rec: "Rec",
|
|
207
|
+
stopRec: "Stop",
|
|
208
|
+
startRecord: "Start recording",
|
|
209
|
+
stopRecord: "Stop recording",
|
|
210
|
+
mute: "Mute",
|
|
211
|
+
unmute: "Unmute",
|
|
212
|
+
help: "Help",
|
|
213
|
+
full: "Full",
|
|
214
|
+
keys: "Keys",
|
|
215
|
+
menuOpen: "Open menu",
|
|
216
|
+
menuClose: "Close menu",
|
|
217
|
+
gamepadConnected: "{{count}} controller{{plural}} connected - click to configure",
|
|
218
|
+
noGamepad: "No controller detected - press any button on your gamepad to connect",
|
|
219
|
+
press: "Press",
|
|
220
|
+
startBtn: "START",
|
|
221
|
+
selectBtn: "SEL"
|
|
222
|
+
},
|
|
223
|
+
common: {
|
|
224
|
+
disabledInHardcore: "Disabled in Hardcore mode",
|
|
225
|
+
notSupported: "Not supported on this console",
|
|
226
|
+
playToEnableRewind: "Play for a few seconds to enable rewind"
|
|
227
|
+
},
|
|
228
|
+
settings: {
|
|
229
|
+
title: "Settings",
|
|
230
|
+
general: "General",
|
|
231
|
+
audio: "Audio",
|
|
232
|
+
video: "Video",
|
|
233
|
+
input: "Input",
|
|
234
|
+
advanced: "Advanced",
|
|
235
|
+
fullscreen: "Fullscreen",
|
|
236
|
+
controls: "Controls",
|
|
237
|
+
gamepad: "Gamepad",
|
|
238
|
+
cheats: "Cheats",
|
|
239
|
+
retroAchievements: "RetroAchievements",
|
|
240
|
+
shortcuts: "Shortcuts",
|
|
241
|
+
exit: "Exit",
|
|
242
|
+
language: "Language",
|
|
243
|
+
selectLanguage: "Select Language"
|
|
244
|
+
},
|
|
245
|
+
overlay: {
|
|
246
|
+
play: "PLAY",
|
|
247
|
+
systemFirmware: "SYSTEM FIRMWARE",
|
|
248
|
+
loading: "Loading {{system}}",
|
|
249
|
+
initializing: "Initializing emulator",
|
|
250
|
+
loadingSave: "Loading Save",
|
|
251
|
+
preparingSlot: "Preparing slot {{num}}",
|
|
252
|
+
systemError: "System Error",
|
|
253
|
+
failedInit: "Failed to initialize emulator",
|
|
254
|
+
retry: "Retry",
|
|
255
|
+
slotReady: "Slot {{num}} ready",
|
|
256
|
+
paused: "Paused"
|
|
257
|
+
},
|
|
258
|
+
notifications: {
|
|
259
|
+
saved: "State saved",
|
|
260
|
+
loaded: "State loaded",
|
|
261
|
+
error: "Error",
|
|
262
|
+
recordingStarted: "Recording started",
|
|
263
|
+
recordingSaved: "Recording saved",
|
|
264
|
+
downloaded: "State downloaded",
|
|
265
|
+
loadedFile: "State loaded from file",
|
|
266
|
+
savedSlot: "Saved to slot {{num}}",
|
|
267
|
+
loadedSlot: "Loaded from slot {{num}}",
|
|
268
|
+
deletedSlot: "Deleted slot {{num}}",
|
|
269
|
+
emptySlot: "Empty slot",
|
|
270
|
+
noSaveFound: "No save found",
|
|
271
|
+
failedSave: "Failed to save",
|
|
272
|
+
failedLoad: "Failed to load",
|
|
273
|
+
failedDelete: "Failed to delete",
|
|
274
|
+
failedFetch: "Failed to load save slots",
|
|
275
|
+
controllerConnected: "Controller Connected",
|
|
276
|
+
controllerDisconnected: "Controller Lost",
|
|
277
|
+
controllerReady: "Controller ready to use",
|
|
278
|
+
insertCoin: "Press SHIFT to insert coin",
|
|
279
|
+
insertCoinTitle: "\u{1FA99} Insert Coin",
|
|
280
|
+
controlsSaved: "Controls saved",
|
|
281
|
+
controlsReset: "Controls reset to defaults"
|
|
282
|
+
},
|
|
283
|
+
modals: {
|
|
284
|
+
shortcuts: {
|
|
285
|
+
title: "Keyboard Shortcuts",
|
|
286
|
+
playerShortcuts: "Player Shortcuts",
|
|
287
|
+
overlays: "Overlays",
|
|
288
|
+
recording: "Recording",
|
|
289
|
+
showHelp: "Show Help",
|
|
290
|
+
perfOverlay: "Performance Overlay",
|
|
291
|
+
inputDisplay: "Input Display",
|
|
292
|
+
toggleRec: "Toggle Recording",
|
|
293
|
+
toggleMute: "Toggle Mute",
|
|
294
|
+
pressEsc: "Press ESC to close"
|
|
295
|
+
},
|
|
296
|
+
controls: {
|
|
297
|
+
title: "Controls",
|
|
298
|
+
keyboard: "Keyboard Mapping",
|
|
299
|
+
description: "Click a button and press a key to remap",
|
|
300
|
+
pressKey: "Press...",
|
|
301
|
+
reset: "Reset to Default",
|
|
302
|
+
save: "Save Controls"
|
|
303
|
+
},
|
|
304
|
+
gamepad: {
|
|
305
|
+
title: "Gamepad Settings",
|
|
306
|
+
noGamepad: "No gamepad detected",
|
|
307
|
+
connected: "{{count}} controller{{s}} connected",
|
|
308
|
+
none: "No controllers detected",
|
|
309
|
+
player: "Player:",
|
|
310
|
+
noController: "No controller detected",
|
|
311
|
+
pressAny: "Press any button on your gamepad to connect",
|
|
312
|
+
waiting: "Waiting for input...",
|
|
313
|
+
pressButton: "Press a button on your controller for {{button}}",
|
|
314
|
+
pressEsc: "Press Escape to cancel",
|
|
315
|
+
reset: "Reset to Default",
|
|
316
|
+
save: "Save Settings"
|
|
317
|
+
},
|
|
318
|
+
cheats: {
|
|
319
|
+
title: "Cheats",
|
|
320
|
+
addCheat: "Add Cheat",
|
|
321
|
+
available: "{{count}} cheat{{s}} available",
|
|
322
|
+
emptyTitle: "No cheats available",
|
|
323
|
+
emptyDesc: "No cheat codes found for this game",
|
|
324
|
+
copy: "Copy code",
|
|
325
|
+
active: "{{count}} cheat{{s}} active",
|
|
326
|
+
toggleHint: "Click a cheat to toggle it on/off"
|
|
327
|
+
},
|
|
328
|
+
saveSlots: {
|
|
329
|
+
title: "Save States",
|
|
330
|
+
saveTitle: "Save Game",
|
|
331
|
+
loadTitle: "Load Game",
|
|
332
|
+
emptySlot: "Empty Slot",
|
|
333
|
+
subtitleSave: "Choose a slot to save your progress",
|
|
334
|
+
subtitleLoad: "Select a save to restore",
|
|
335
|
+
loading: "Loading saves...",
|
|
336
|
+
locked: "Slot {{num}} Locked",
|
|
337
|
+
upgrade: "Upgrade to unlock more save slots",
|
|
338
|
+
autoSave: "Auto-Save Slot",
|
|
339
|
+
autoSaveDesc: "Reserved for automatic saves",
|
|
340
|
+
noData: "No save data",
|
|
341
|
+
slot: "Slot {{num}}",
|
|
342
|
+
footerSave: "Saves are stored in the cloud and sync across devices",
|
|
343
|
+
footerLoad: "Your progress will be restored to the selected save point"
|
|
344
|
+
},
|
|
345
|
+
bios: {
|
|
346
|
+
title: "BIOS Selection",
|
|
347
|
+
description: "Select a BIOS file to use for this game",
|
|
348
|
+
warningTitle: "Note:",
|
|
349
|
+
warning: "Changing BIOS requires the emulator to restart. Your unsaved progress will be lost.",
|
|
350
|
+
systemDefault: "System Default",
|
|
351
|
+
active: "Active",
|
|
352
|
+
defaultDesc: "Use the emulator's built-in or default BIOS",
|
|
353
|
+
emptyTitle: "No BIOS files found for this console.",
|
|
354
|
+
emptyDesc: "Upload BIOS files in your Dashboard Library.",
|
|
355
|
+
footer: "System Firmware Settings"
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
retroAchievements: {
|
|
359
|
+
title: "RetroAchievements",
|
|
360
|
+
login: "Login",
|
|
361
|
+
logout: "Logout",
|
|
362
|
+
username: "Username",
|
|
363
|
+
password: "Password",
|
|
364
|
+
hardcore: "Hardcore Mode",
|
|
365
|
+
achievements: "Achievements",
|
|
366
|
+
locked: "Locked",
|
|
367
|
+
unlocked: "Unlocked",
|
|
368
|
+
mastered: "Mastered",
|
|
369
|
+
identifying: "Identifying game...",
|
|
370
|
+
achievementsAvailable: "{{count}} achievements available",
|
|
371
|
+
gameNotSupported: "Connected - Game not in RA database",
|
|
372
|
+
connect: "Connect RetroAchievements",
|
|
373
|
+
connected: "RetroAchievements (connected)",
|
|
374
|
+
createAccount: "Create an account",
|
|
375
|
+
privacy: "Privacy:",
|
|
376
|
+
privacyText: "Your password is only used to authenticate with RetroAchievements. It is never stored.",
|
|
377
|
+
connecting: "Connecting...",
|
|
378
|
+
connectAccount: "Connect your account to track achievements",
|
|
379
|
+
poweredBy: "Powered by",
|
|
380
|
+
connectedStatus: "Connected",
|
|
381
|
+
yourUsername: "Your RA username",
|
|
382
|
+
yourPassword: "Your RA password",
|
|
383
|
+
usernameRequired: "Username and Password are required",
|
|
384
|
+
noGame: "No game loaded",
|
|
385
|
+
loadGame: "Load a game to see achievements",
|
|
386
|
+
noAchievements: "No achievements found",
|
|
387
|
+
notSupported: "This game may not be supported",
|
|
388
|
+
ptsRemaining: "{{count}} pts remaining",
|
|
389
|
+
filters: {
|
|
390
|
+
all: "All",
|
|
391
|
+
locked: "Locked",
|
|
392
|
+
unlocked: "Unlocked"
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
overlays: {
|
|
396
|
+
performance: {
|
|
397
|
+
title: "Stats",
|
|
398
|
+
fps: "FPS",
|
|
399
|
+
frameTime: "FT",
|
|
400
|
+
memory: "MEM",
|
|
401
|
+
core: "Core",
|
|
402
|
+
input: "INPUT",
|
|
403
|
+
active: "ACTIVE"
|
|
404
|
+
},
|
|
405
|
+
toast: {
|
|
406
|
+
saved: "Game Saved",
|
|
407
|
+
loaded: "Game Loaded",
|
|
408
|
+
error: "Error"
|
|
409
|
+
},
|
|
410
|
+
recording: {
|
|
411
|
+
started: "Recording Started",
|
|
412
|
+
stopped: "Recording Stopped",
|
|
413
|
+
saved: "Recording Saved",
|
|
414
|
+
paused: "PAUSED",
|
|
415
|
+
recording: "RECORDING",
|
|
416
|
+
resume: "Resume recording",
|
|
417
|
+
pause: "Pause recording",
|
|
418
|
+
stop: "Stop and save recording",
|
|
419
|
+
hover: "Hover for controls"
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var KoinI18nContext = createContext(en);
|
|
424
|
+
function deepMerge(target, source) {
|
|
425
|
+
const result = { ...target };
|
|
426
|
+
for (const key in source) {
|
|
427
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
428
|
+
const sourceValue = source[key];
|
|
429
|
+
const targetValue = result[key];
|
|
430
|
+
if (sourceValue && typeof sourceValue === "object" && targetValue && typeof targetValue === "object" && !Array.isArray(sourceValue)) {
|
|
431
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
432
|
+
} else if (sourceValue !== void 0) {
|
|
433
|
+
result[key] = sourceValue;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
var KoinI18nProvider = ({ children, translations }) => {
|
|
440
|
+
const value = useMemo(() => {
|
|
441
|
+
if (!translations) return en;
|
|
442
|
+
return deepMerge(en, translations);
|
|
443
|
+
}, [translations]);
|
|
444
|
+
return /* @__PURE__ */ jsx(KoinI18nContext.Provider, { value, children });
|
|
445
|
+
};
|
|
446
|
+
function useKoinTranslation() {
|
|
447
|
+
return useContext(KoinI18nContext);
|
|
448
|
+
}
|
|
174
449
|
var PlaybackControls = memo(function PlaybackControls2({
|
|
175
450
|
isPaused,
|
|
176
451
|
isRunning,
|
|
@@ -190,20 +465,30 @@ var PlaybackControls = memo(function PlaybackControls2({
|
|
|
190
465
|
systemColor = "#00FF41",
|
|
191
466
|
hardcoreRestrictions
|
|
192
467
|
}) {
|
|
468
|
+
const t = useKoinTranslation();
|
|
193
469
|
const hasRewindHistory = rewindBufferSize > 0;
|
|
194
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 flex-shrink-0", children: [
|
|
470
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-4 w-full sm:w-auto sm:flex-nowrap sm:gap-3 flex-shrink-0", children: [
|
|
195
471
|
/* @__PURE__ */ jsx(
|
|
196
472
|
ControlButton,
|
|
197
473
|
{
|
|
198
474
|
onClick: onPauseToggle,
|
|
199
475
|
icon: !isRunning || isPaused ? Play : Pause,
|
|
200
|
-
label: !isRunning || isPaused ?
|
|
476
|
+
label: !isRunning || isPaused ? t.controls.play : t.controls.pause,
|
|
201
477
|
active: isPaused,
|
|
202
478
|
disabled,
|
|
203
479
|
systemColor
|
|
204
480
|
}
|
|
205
481
|
),
|
|
206
|
-
/* @__PURE__ */ jsx(
|
|
482
|
+
/* @__PURE__ */ jsx(
|
|
483
|
+
ControlButton,
|
|
484
|
+
{
|
|
485
|
+
onClick: onRestart,
|
|
486
|
+
icon: RotateCcw,
|
|
487
|
+
label: t.controls.reset,
|
|
488
|
+
disabled,
|
|
489
|
+
systemColor
|
|
490
|
+
}
|
|
491
|
+
),
|
|
207
492
|
/* @__PURE__ */ jsx(SpeedMenu, { speed, onSpeedChange, disabled }),
|
|
208
493
|
/* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
209
494
|
/* @__PURE__ */ jsx(
|
|
@@ -215,7 +500,7 @@ var PlaybackControls = memo(function PlaybackControls2({
|
|
|
215
500
|
onTouchStart: hasRewindHistory && hardcoreRestrictions?.canUseRewind !== false ? onRewindStart : void 0,
|
|
216
501
|
onTouchEnd: hasRewindHistory && hardcoreRestrictions?.canUseRewind !== false ? onRewindStop : void 0,
|
|
217
502
|
icon: Rewind,
|
|
218
|
-
label:
|
|
503
|
+
label: t.controls.rewind,
|
|
219
504
|
active: isRewinding,
|
|
220
505
|
disabled: disabled || !hasRewindHistory || hardcoreRestrictions?.canUseRewind === false,
|
|
221
506
|
systemColor
|
|
@@ -226,10 +511,10 @@ var PlaybackControls = memo(function PlaybackControls2({
|
|
|
226
511
|
HardcoreTooltip,
|
|
227
512
|
{
|
|
228
513
|
show: hardcoreRestrictions?.canUseRewind === false,
|
|
229
|
-
message: hardcoreRestrictions?.isHardcore ?
|
|
514
|
+
message: hardcoreRestrictions?.isHardcore ? t.common.disabledInHardcore : t.common.notSupported
|
|
230
515
|
}
|
|
231
516
|
),
|
|
232
|
-
hardcoreRestrictions?.canUseRewind !== false && !hasRewindHistory && /* @__PURE__ */ jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-black/90 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50", children:
|
|
517
|
+
hardcoreRestrictions?.canUseRewind !== false && !hasRewindHistory && /* @__PURE__ */ jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-black/90 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50", children: t.common.playToEnableRewind })
|
|
233
518
|
] }),
|
|
234
519
|
/* @__PURE__ */ jsx("div", { className: "w-px h-8 bg-white/10 mx-1" }),
|
|
235
520
|
/* @__PURE__ */ jsx(
|
|
@@ -432,7 +717,8 @@ var SaveLoadControls = memo(function SaveLoadControls2({
|
|
|
432
717
|
autoSavePaused = false,
|
|
433
718
|
onAutoSaveToggle
|
|
434
719
|
}) {
|
|
435
|
-
|
|
720
|
+
const t = useKoinTranslation();
|
|
721
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-4 w-full sm:w-auto sm:flex-nowrap sm:gap-3 px-3 sm:px-4 sm:border-x sm:border-white/10 flex-shrink-0", children: [
|
|
436
722
|
autoSaveEnabled && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
437
723
|
AutoSaveIndicator,
|
|
438
724
|
{
|
|
@@ -449,12 +735,18 @@ var SaveLoadControls = memo(function SaveLoadControls2({
|
|
|
449
735
|
{
|
|
450
736
|
onClick: hardcoreRestrictions?.canUseSaveStates === false ? void 0 : onSave,
|
|
451
737
|
icon: Save,
|
|
452
|
-
label:
|
|
738
|
+
label: t.controls.save,
|
|
453
739
|
disabled: disabled || saveDisabled || hardcoreRestrictions?.canUseSaveStates === false,
|
|
454
740
|
systemColor
|
|
455
741
|
}
|
|
456
742
|
),
|
|
457
|
-
/* @__PURE__ */ jsx(
|
|
743
|
+
/* @__PURE__ */ jsx(
|
|
744
|
+
HardcoreTooltip,
|
|
745
|
+
{
|
|
746
|
+
show: hardcoreRestrictions?.canUseSaveStates === false,
|
|
747
|
+
message: hardcoreRestrictions?.isHardcore ? t.common.disabledInHardcore : t.common.notSupported
|
|
748
|
+
}
|
|
749
|
+
)
|
|
458
750
|
] }),
|
|
459
751
|
/* @__PURE__ */ jsxs("div", { className: "relative group flex-shrink-0", children: [
|
|
460
752
|
/* @__PURE__ */ jsx(
|
|
@@ -462,19 +754,25 @@ var SaveLoadControls = memo(function SaveLoadControls2({
|
|
|
462
754
|
{
|
|
463
755
|
onClick: hardcoreRestrictions?.canUseSaveStates === false ? void 0 : onLoad,
|
|
464
756
|
icon: Download,
|
|
465
|
-
label:
|
|
757
|
+
label: t.controls.load,
|
|
466
758
|
disabled: disabled || loadDisabled || hardcoreRestrictions?.canUseSaveStates === false,
|
|
467
759
|
systemColor
|
|
468
760
|
}
|
|
469
761
|
),
|
|
470
|
-
/* @__PURE__ */ jsx(
|
|
762
|
+
/* @__PURE__ */ jsx(
|
|
763
|
+
HardcoreTooltip,
|
|
764
|
+
{
|
|
765
|
+
show: hardcoreRestrictions?.canUseSaveStates === false,
|
|
766
|
+
message: hardcoreRestrictions?.isHardcore ? t.common.disabledInHardcore : t.common.notSupported
|
|
767
|
+
}
|
|
768
|
+
)
|
|
471
769
|
] }),
|
|
472
770
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
473
771
|
ControlButton,
|
|
474
772
|
{
|
|
475
773
|
onClick: onScreenshot,
|
|
476
774
|
icon: Camera,
|
|
477
|
-
label:
|
|
775
|
+
label: t.controls.snap,
|
|
478
776
|
disabled: disabled || saveDisabled,
|
|
479
777
|
systemColor
|
|
480
778
|
}
|
|
@@ -485,7 +783,7 @@ var SaveLoadControls = memo(function SaveLoadControls2({
|
|
|
485
783
|
onClick: onRecordToggle,
|
|
486
784
|
disabled,
|
|
487
785
|
className: `flex flex-col items-center gap-1 px-2 sm:px-3 py-2 rounded-lg transition-all duration-200 hover:bg-white/10 ${disabled ? "opacity-50 cursor-not-allowed" : ""}`,
|
|
488
|
-
title: isRecording ?
|
|
786
|
+
title: isRecording ? t.controls.stopRecord : t.controls.startRecord,
|
|
489
787
|
children: [
|
|
490
788
|
/* @__PURE__ */ jsx("div", { className: "relative", children: isRecording ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
491
789
|
/* @__PURE__ */ jsx(Video, { size: 20, className: "text-red-500" }),
|
|
@@ -496,7 +794,7 @@ var SaveLoadControls = memo(function SaveLoadControls2({
|
|
|
496
794
|
{
|
|
497
795
|
className: "text-[10px] font-bold uppercase tracking-wider",
|
|
498
796
|
style: { color: isRecording ? "#FF3333" : void 0 },
|
|
499
|
-
children:
|
|
797
|
+
children: t.controls.rec
|
|
500
798
|
}
|
|
501
799
|
)
|
|
502
800
|
]
|
|
@@ -629,6 +927,9 @@ function RAButton({
|
|
|
629
927
|
achievementCount,
|
|
630
928
|
className = ""
|
|
631
929
|
}) {
|
|
930
|
+
const t = useKoinTranslation();
|
|
931
|
+
const title = isGameFound ? `${t.retroAchievements.title} (${achievementCount} achievements)` : isConnected ? t.retroAchievements.connected : t.retroAchievements.title;
|
|
932
|
+
const tooltip = isIdentifying ? t.retroAchievements.identifying : isGameFound ? t.retroAchievements.achievementsAvailable.replace("{{count}}", achievementCount.toString()) : isConnected ? t.retroAchievements.gameNotSupported : t.retroAchievements.connect;
|
|
632
933
|
return /* @__PURE__ */ jsxs("div", { className: `relative group ${className}`, children: [
|
|
633
934
|
/* @__PURE__ */ jsxs(
|
|
634
935
|
"button",
|
|
@@ -641,7 +942,7 @@ function RAButton({
|
|
|
641
942
|
select-none
|
|
642
943
|
${isGameFound ? "bg-gradient-to-b from-yellow-500/30 to-orange-500/20 text-yellow-400 ring-1 ring-yellow-500/50 shadow-[0_0_12px_rgba(234,179,8,0.3)]" : isConnected ? "bg-yellow-500/10 text-yellow-400/70 ring-1 ring-yellow-500/30" : "hover:bg-white/10 text-gray-400 hover:text-white"}
|
|
643
944
|
`,
|
|
644
|
-
title
|
|
945
|
+
title,
|
|
645
946
|
children: [
|
|
646
947
|
isIdentifying ? /* @__PURE__ */ jsx(Loader2, { size: 20, className: "animate-spin text-yellow-400" }) : /* @__PURE__ */ jsx(
|
|
647
948
|
Trophy,
|
|
@@ -662,13 +963,14 @@ function RAButton({
|
|
|
662
963
|
absolute -top-1 -right-1 w-3 h-3 rounded-full border-2 border-black
|
|
663
964
|
${isGameFound ? "bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.8)]" : "bg-yellow-500 shadow-[0_0_6px_rgba(234,179,8,0.6)]"}
|
|
664
965
|
`, children: isGameFound && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-green-400 rounded-full animate-ping opacity-50" }) }),
|
|
665
|
-
/* @__PURE__ */ jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900/95 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 border border-white/10", children:
|
|
966
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900/95 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 border border-white/10", children: tooltip })
|
|
666
967
|
] });
|
|
667
968
|
}
|
|
668
969
|
var SettingsControls = memo(function SettingsControls2({
|
|
669
970
|
onFullscreen,
|
|
670
971
|
onControls,
|
|
671
972
|
onGamepadSettings,
|
|
973
|
+
onSettings,
|
|
672
974
|
onCheats,
|
|
673
975
|
onRetroAchievements,
|
|
674
976
|
onShowShortcuts,
|
|
@@ -685,8 +987,10 @@ var SettingsControls = memo(function SettingsControls2({
|
|
|
685
987
|
raAchievementCount = 0,
|
|
686
988
|
raIsIdentifying = false
|
|
687
989
|
}) {
|
|
990
|
+
const t = useKoinTranslation();
|
|
688
991
|
const gamepadIndicatorText = gamepadCount > 0 ? Array.from({ length: gamepadCount }, (_, i) => `P${i + 1}`).join(" ") : "";
|
|
689
|
-
|
|
992
|
+
const gamepadConnectedTitle = t.controls.gamepadConnected.replace("{{count}}", gamepadCount.toString()).replace("{{plural}}", gamepadCount > 1 ? "s" : "");
|
|
993
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-4 w-full sm:w-auto sm:flex-nowrap sm:gap-3 flex-shrink-0", children: [
|
|
690
994
|
/* @__PURE__ */ jsx(
|
|
691
995
|
ShaderDropdown_default,
|
|
692
996
|
{
|
|
@@ -697,15 +1001,15 @@ var SettingsControls = memo(function SettingsControls2({
|
|
|
697
1001
|
disabled
|
|
698
1002
|
}
|
|
699
1003
|
),
|
|
700
|
-
onShowShortcuts && /* @__PURE__ */ jsx(ControlButton, { onClick: onShowShortcuts, icon: HelpCircle, label:
|
|
701
|
-
/* @__PURE__ */ jsx(ControlButton, { onClick: onFullscreen, icon: Maximize, label:
|
|
702
|
-
/* @__PURE__ */ jsx(ControlButton, { onClick: onControls, icon: Gamepad2, label:
|
|
1004
|
+
onShowShortcuts && /* @__PURE__ */ jsx(ControlButton, { onClick: onShowShortcuts, icon: HelpCircle, label: t.controls.help, disabled, className: "hidden sm:flex", systemColor }),
|
|
1005
|
+
/* @__PURE__ */ jsx(ControlButton, { onClick: onFullscreen, icon: Maximize, label: t.controls.full, disabled, className: "hidden sm:flex", systemColor }),
|
|
1006
|
+
/* @__PURE__ */ jsx(ControlButton, { onClick: onControls, icon: Gamepad2, label: t.controls.keys, disabled, className: "hidden sm:flex", systemColor }),
|
|
703
1007
|
gamepadCount > 0 ? /* @__PURE__ */ jsxs(
|
|
704
1008
|
"button",
|
|
705
1009
|
{
|
|
706
1010
|
onClick: onGamepadSettings,
|
|
707
1011
|
className: "relative group flex flex-col items-center gap-1 px-2 sm:px-3 py-2 rounded-lg transition-all duration-200 hover:bg-white/10 flex-shrink-0 hidden sm:flex",
|
|
708
|
-
title:
|
|
1012
|
+
title: gamepadConnectedTitle,
|
|
709
1013
|
children: [
|
|
710
1014
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
711
1015
|
/* @__PURE__ */ jsx(Joystick, { size: 20, style: { color: systemColor }, className: "transition-transform group-hover:scale-110" }),
|
|
@@ -726,15 +1030,15 @@ var SettingsControls = memo(function SettingsControls2({
|
|
|
726
1030
|
"button",
|
|
727
1031
|
{
|
|
728
1032
|
onClick: onGamepadSettings,
|
|
729
|
-
className: "relative group flex-col items-center gap-1 px-3 py-2 transition-all duration-200 flex-shrink-0
|
|
730
|
-
title:
|
|
1033
|
+
className: "relative group flex-col items-center gap-1 px-3 py-2 transition-all duration-200 flex-shrink-0",
|
|
1034
|
+
title: t.controls.noGamepad,
|
|
731
1035
|
style: {
|
|
732
1036
|
border: "2px dashed #6b7280",
|
|
733
1037
|
backgroundColor: "transparent"
|
|
734
1038
|
},
|
|
735
1039
|
children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center gap-2", children: [
|
|
736
1040
|
/* @__PURE__ */ jsx(Gamepad2, { size: 18, className: "text-gray-400 transition-transform group-hover:scale-110 group-hover:text-white" }),
|
|
737
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-gray-400 group-hover:text-white whitespace-nowrap", children:
|
|
1041
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-gray-400 group-hover:text-white whitespace-nowrap", children: t.controls.press }),
|
|
738
1042
|
/* @__PURE__ */ jsx("div", { className: "w-5 h-5 rounded-full border-2 border-gray-400 group-hover:border-white flex items-center justify-center animate-pulse", children: /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-gray-400 group-hover:bg-white" }) })
|
|
739
1043
|
] })
|
|
740
1044
|
}
|
|
@@ -746,12 +1050,18 @@ var SettingsControls = memo(function SettingsControls2({
|
|
|
746
1050
|
{
|
|
747
1051
|
onClick: hardcoreRestrictions?.canUseCheats === false ? void 0 : onCheats,
|
|
748
1052
|
icon: Code,
|
|
749
|
-
label:
|
|
1053
|
+
label: t.settings.cheats,
|
|
750
1054
|
disabled: disabled || hardcoreRestrictions?.canUseCheats === false,
|
|
751
1055
|
systemColor
|
|
752
1056
|
}
|
|
753
1057
|
),
|
|
754
|
-
/* @__PURE__ */ jsx(
|
|
1058
|
+
/* @__PURE__ */ jsx(
|
|
1059
|
+
HardcoreTooltip,
|
|
1060
|
+
{
|
|
1061
|
+
show: hardcoreRestrictions?.canUseCheats === false,
|
|
1062
|
+
message: hardcoreRestrictions?.isHardcore ? t.common.disabledInHardcore : t.common.notSupported
|
|
1063
|
+
}
|
|
1064
|
+
)
|
|
755
1065
|
] }),
|
|
756
1066
|
/* @__PURE__ */ jsx(
|
|
757
1067
|
RAButton,
|
|
@@ -765,14 +1075,26 @@ var SettingsControls = memo(function SettingsControls2({
|
|
|
765
1075
|
className: "hidden sm:flex"
|
|
766
1076
|
}
|
|
767
1077
|
),
|
|
1078
|
+
onSettings && /* @__PURE__ */ jsx(
|
|
1079
|
+
ControlButton,
|
|
1080
|
+
{
|
|
1081
|
+
onClick: onSettings,
|
|
1082
|
+
icon: Settings,
|
|
1083
|
+
label: t.settings.title,
|
|
1084
|
+
disabled,
|
|
1085
|
+
systemColor,
|
|
1086
|
+
className: "hidden sm:flex"
|
|
1087
|
+
}
|
|
1088
|
+
),
|
|
768
1089
|
/* @__PURE__ */ jsx("div", { className: "w-px h-8 bg-white/10 mx-2 hidden sm:block" }),
|
|
769
|
-
/* @__PURE__ */ jsx(ControlButton, { onClick: onExit, icon: Power, label:
|
|
1090
|
+
/* @__PURE__ */ jsx(ControlButton, { onClick: onExit, icon: Power, label: t.settings.exit, danger: true, disabled, systemColor })
|
|
770
1091
|
] });
|
|
771
1092
|
});
|
|
772
1093
|
var MobileControlDrawer = memo(function MobileControlDrawer2({
|
|
773
1094
|
children,
|
|
774
1095
|
systemColor = "#00FF41"
|
|
775
1096
|
}) {
|
|
1097
|
+
const t = useKoinTranslation();
|
|
776
1098
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
777
1099
|
useEffect(() => {
|
|
778
1100
|
if (isExpanded) {
|
|
@@ -807,7 +1129,7 @@ var MobileControlDrawer = memo(function MobileControlDrawer2({
|
|
|
807
1129
|
boxShadow: isExpanded ? `0 0 20px ${systemColor}60, 0 4px 20px rgba(0,0,0,0.5)` : "0 4px 20px rgba(0,0,0,0.5)",
|
|
808
1130
|
borderColor: isExpanded ? systemColor : void 0
|
|
809
1131
|
},
|
|
810
|
-
"aria-label": isExpanded ?
|
|
1132
|
+
"aria-label": isExpanded ? t.controls.menuClose : t.controls.menuOpen,
|
|
811
1133
|
children: /* @__PURE__ */ jsxs(
|
|
812
1134
|
"svg",
|
|
813
1135
|
{
|
|
@@ -848,7 +1170,7 @@ var MobileControlDrawer = memo(function MobileControlDrawer2({
|
|
|
848
1170
|
/* @__PURE__ */ jsx(
|
|
849
1171
|
"div",
|
|
850
1172
|
{
|
|
851
|
-
className: "relative flex flex-
|
|
1173
|
+
className: "relative flex flex-col items-center px-4 py-6 pb-20",
|
|
852
1174
|
style: { paddingBottom: "calc(env(safe-area-inset-bottom, 20px) + 80px)" },
|
|
853
1175
|
children
|
|
854
1176
|
}
|
|
@@ -896,6 +1218,7 @@ var PlayerControls = memo(function PlayerControls2({
|
|
|
896
1218
|
systemColor = "#00FF41",
|
|
897
1219
|
gamepadCount = 0,
|
|
898
1220
|
onGamepadSettings,
|
|
1221
|
+
onSettings,
|
|
899
1222
|
volume = 100,
|
|
900
1223
|
isMuted = false,
|
|
901
1224
|
onVolumeChange,
|
|
@@ -928,6 +1251,7 @@ var PlayerControls = memo(function PlayerControls2({
|
|
|
928
1251
|
hardcoreRestrictions
|
|
929
1252
|
}
|
|
930
1253
|
),
|
|
1254
|
+
/* @__PURE__ */ jsx("div", { className: "w-full h-px bg-white/10 sm:hidden my-4" }),
|
|
931
1255
|
/* @__PURE__ */ jsx(
|
|
932
1256
|
SaveLoadControls,
|
|
933
1257
|
{
|
|
@@ -948,12 +1272,14 @@ var PlayerControls = memo(function PlayerControls2({
|
|
|
948
1272
|
onAutoSaveToggle
|
|
949
1273
|
}
|
|
950
1274
|
),
|
|
1275
|
+
/* @__PURE__ */ jsx("div", { className: "w-full h-px bg-white/10 sm:hidden my-4" }),
|
|
951
1276
|
/* @__PURE__ */ jsx(
|
|
952
1277
|
SettingsControls,
|
|
953
1278
|
{
|
|
954
1279
|
onFullscreen,
|
|
955
1280
|
onControls,
|
|
956
1281
|
onGamepadSettings,
|
|
1282
|
+
onSettings,
|
|
957
1283
|
onCheats,
|
|
958
1284
|
onRetroAchievements,
|
|
959
1285
|
onShowShortcuts,
|
|
@@ -974,7 +1300,7 @@ var PlayerControls = memo(function PlayerControls2({
|
|
|
974
1300
|
] });
|
|
975
1301
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
976
1302
|
/* @__PURE__ */ jsx(MobileControlDrawer_default, { systemColor, children: controlsContent }),
|
|
977
|
-
/* @__PURE__ */ jsx("div", { className: "hidden sm:flex w-full
|
|
1303
|
+
/* @__PURE__ */ jsx("div", { className: "hidden sm:flex w-full bg-black/90 backdrop-blur-md border-t border-white/10 shrink-0 z-50 overflow-x-auto no-scrollbar", children: /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-max mx-auto gap-4 px-8 py-4", children: controlsContent }) })
|
|
978
1304
|
] });
|
|
979
1305
|
});
|
|
980
1306
|
var PlayerControls_default = PlayerControls;
|
|
@@ -1137,6 +1463,7 @@ var PerformanceOverlay = memo(function PerformanceOverlay2({
|
|
|
1137
1463
|
coreName = "Unknown",
|
|
1138
1464
|
systemColor = "#00FF41"
|
|
1139
1465
|
}) {
|
|
1466
|
+
const t = useKoinTranslation();
|
|
1140
1467
|
const [fps, setFps] = useState(0);
|
|
1141
1468
|
const [frameTime, setFrameTime] = useState(0);
|
|
1142
1469
|
const frameTimesRef = useRef([]);
|
|
@@ -1188,12 +1515,12 @@ var PerformanceOverlay = memo(function PerformanceOverlay2({
|
|
|
1188
1515
|
},
|
|
1189
1516
|
children: [
|
|
1190
1517
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1191
|
-
/* @__PURE__ */ jsx("span", { className: "opacity-60", children:
|
|
1518
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-60 text-[10px] uppercase", children: t.overlays.performance.fps }),
|
|
1192
1519
|
/* @__PURE__ */ jsx("span", { className: "font-bold", children: fps })
|
|
1193
1520
|
] }),
|
|
1194
1521
|
/* @__PURE__ */ jsx("div", { className: "w-px h-3 bg-white/20" }),
|
|
1195
1522
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1196
|
-
/* @__PURE__ */ jsx("span", { className: "opacity-60", children:
|
|
1523
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-60 text-[10px] uppercase", children: t.overlays.performance.frameTime }),
|
|
1197
1524
|
/* @__PURE__ */ jsxs("span", { className: "font-bold", children: [
|
|
1198
1525
|
frameTime,
|
|
1199
1526
|
"ms"
|
|
@@ -1201,7 +1528,7 @@ var PerformanceOverlay = memo(function PerformanceOverlay2({
|
|
|
1201
1528
|
] }),
|
|
1202
1529
|
/* @__PURE__ */ jsx("div", { className: "w-px h-3 bg-white/20" }),
|
|
1203
1530
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1204
|
-
/* @__PURE__ */ jsx("span", { className: "opacity-60", children:
|
|
1531
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-60 text-[10px] uppercase", children: t.overlays.performance.core }),
|
|
1205
1532
|
/* @__PURE__ */ jsx("span", { className: "font-bold text-white/80", children: coreName })
|
|
1206
1533
|
] })
|
|
1207
1534
|
]
|
|
@@ -1375,6 +1702,7 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1375
1702
|
onStop,
|
|
1376
1703
|
systemColor = "#FF3333"
|
|
1377
1704
|
}) {
|
|
1705
|
+
const t = useKoinTranslation();
|
|
1378
1706
|
const [isHovered, setIsHovered] = useState(false);
|
|
1379
1707
|
if (!isRecording) return null;
|
|
1380
1708
|
const minutes = Math.floor(duration / 60);
|
|
@@ -1422,7 +1750,7 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1422
1750
|
{
|
|
1423
1751
|
className: "text-xs font-bold",
|
|
1424
1752
|
style: { color: isPaused ? "#FFA500" : "#FF3333" },
|
|
1425
|
-
children: isPaused ?
|
|
1753
|
+
children: isPaused ? t.overlays.recording.paused : t.overlays.recording.recording
|
|
1426
1754
|
}
|
|
1427
1755
|
),
|
|
1428
1756
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/60", children: timeString })
|
|
@@ -1433,7 +1761,7 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1433
1761
|
{
|
|
1434
1762
|
onClick: onResume,
|
|
1435
1763
|
className: "p-1.5 rounded hover:bg-white/20 transition-colors",
|
|
1436
|
-
title:
|
|
1764
|
+
title: t.overlays.recording.resume,
|
|
1437
1765
|
children: /* @__PURE__ */ jsx(Play, { size: 14, className: "text-orange-400", fill: "#FFA500" })
|
|
1438
1766
|
}
|
|
1439
1767
|
) : /* @__PURE__ */ jsx(
|
|
@@ -1441,7 +1769,7 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1441
1769
|
{
|
|
1442
1770
|
onClick: onPause,
|
|
1443
1771
|
className: "p-1.5 rounded hover:bg-white/20 transition-colors",
|
|
1444
|
-
title:
|
|
1772
|
+
title: t.overlays.recording.pause,
|
|
1445
1773
|
children: /* @__PURE__ */ jsx(Pause, { size: 14, className: "text-white/80" })
|
|
1446
1774
|
}
|
|
1447
1775
|
),
|
|
@@ -1450,7 +1778,7 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1450
1778
|
{
|
|
1451
1779
|
onClick: onStop,
|
|
1452
1780
|
className: "p-1.5 rounded hover:bg-red-500/30 transition-colors flex items-center gap-1",
|
|
1453
|
-
title:
|
|
1781
|
+
title: t.overlays.recording.stop,
|
|
1454
1782
|
children: [
|
|
1455
1783
|
/* @__PURE__ */ jsx(Square, { size: 12, fill: "#FF3333", className: "text-red-500" }),
|
|
1456
1784
|
/* @__PURE__ */ jsx(Download, { size: 12, className: "text-white/60" })
|
|
@@ -1461,39 +1789,40 @@ var RecordingIndicator = memo(function RecordingIndicator2({
|
|
|
1461
1789
|
]
|
|
1462
1790
|
}
|
|
1463
1791
|
),
|
|
1464
|
-
!isHovered && /* @__PURE__ */ jsx("div", { className: "text-[9px] text-white/40 text-center mt-1", children:
|
|
1792
|
+
!isHovered && /* @__PURE__ */ jsx("div", { className: "text-[9px] text-white/40 text-center mt-1", children: t.overlays.recording.hover })
|
|
1465
1793
|
]
|
|
1466
1794
|
}
|
|
1467
1795
|
);
|
|
1468
1796
|
});
|
|
1469
1797
|
var RecordingIndicator_default = RecordingIndicator;
|
|
1470
|
-
var SHORTCUTS = [
|
|
1471
|
-
{
|
|
1472
|
-
section: "Overlays",
|
|
1473
|
-
items: [
|
|
1474
|
-
{ key: "F1", description: "Show this help" },
|
|
1475
|
-
{ key: "F3", description: "Performance Overlay (FPS)" },
|
|
1476
|
-
{ key: "F4", description: "Input Display" }
|
|
1477
|
-
]
|
|
1478
|
-
},
|
|
1479
|
-
{
|
|
1480
|
-
section: "Recording",
|
|
1481
|
-
items: [
|
|
1482
|
-
{ key: "F5", description: "Start/Stop Recording" }
|
|
1483
|
-
]
|
|
1484
|
-
},
|
|
1485
|
-
{
|
|
1486
|
-
section: "Audio",
|
|
1487
|
-
items: [
|
|
1488
|
-
{ key: "F9", description: "Toggle Mute" }
|
|
1489
|
-
]
|
|
1490
|
-
}
|
|
1491
|
-
];
|
|
1492
1798
|
var ShortcutsModal = memo(function ShortcutsModal2({
|
|
1493
1799
|
isOpen,
|
|
1494
1800
|
onClose,
|
|
1495
1801
|
systemColor = "#00FF41"
|
|
1496
1802
|
}) {
|
|
1803
|
+
const t = useKoinTranslation();
|
|
1804
|
+
const shortcuts = useMemo(() => [
|
|
1805
|
+
{
|
|
1806
|
+
section: t.modals.shortcuts.overlays,
|
|
1807
|
+
items: [
|
|
1808
|
+
{ key: "F1", description: t.modals.shortcuts.showHelp },
|
|
1809
|
+
{ key: "F3", description: t.modals.shortcuts.perfOverlay },
|
|
1810
|
+
{ key: "F4", description: t.modals.shortcuts.inputDisplay }
|
|
1811
|
+
]
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
section: t.modals.shortcuts.recording,
|
|
1815
|
+
items: [
|
|
1816
|
+
{ key: "F5", description: t.modals.shortcuts.toggleRec }
|
|
1817
|
+
]
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
section: t.settings.audio,
|
|
1821
|
+
items: [
|
|
1822
|
+
{ key: "F9", description: t.modals.shortcuts.toggleMute }
|
|
1823
|
+
]
|
|
1824
|
+
}
|
|
1825
|
+
], [t]);
|
|
1497
1826
|
if (!isOpen) return null;
|
|
1498
1827
|
return /* @__PURE__ */ jsx(
|
|
1499
1828
|
"div",
|
|
@@ -1515,7 +1844,7 @@ var ShortcutsModal = memo(function ShortcutsModal2({
|
|
|
1515
1844
|
children: [
|
|
1516
1845
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1517
1846
|
/* @__PURE__ */ jsx(Keyboard, { size: 18, style: { color: systemColor } }),
|
|
1518
|
-
/* @__PURE__ */ jsx("span", { className: "font-bold text-white", children:
|
|
1847
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold text-white", children: t.modals.shortcuts.playerShortcuts })
|
|
1519
1848
|
] }),
|
|
1520
1849
|
/* @__PURE__ */ jsx(
|
|
1521
1850
|
"button",
|
|
@@ -1529,7 +1858,7 @@ var ShortcutsModal = memo(function ShortcutsModal2({
|
|
|
1529
1858
|
}
|
|
1530
1859
|
),
|
|
1531
1860
|
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
|
|
1532
|
-
|
|
1861
|
+
shortcuts.map(({ section, items }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
1533
1862
|
/* @__PURE__ */ jsx(
|
|
1534
1863
|
"h3",
|
|
1535
1864
|
{
|
|
@@ -1563,20 +1892,16 @@ var ShortcutsModal = memo(function ShortcutsModal2({
|
|
|
1563
1892
|
] }, section)),
|
|
1564
1893
|
/* @__PURE__ */ jsxs("div", { className: "pt-2 border-t border-white/10 text-xs text-white/40", children: [
|
|
1565
1894
|
"Game controls can be configured in ",
|
|
1566
|
-
/* @__PURE__ */ jsx("strong", { className: "text-white/60", children:
|
|
1895
|
+
/* @__PURE__ */ jsx("strong", { className: "text-white/60", children: t.controls.keys }),
|
|
1567
1896
|
" settings."
|
|
1568
1897
|
] })
|
|
1569
1898
|
] }),
|
|
1570
|
-
/* @__PURE__ */
|
|
1899
|
+
/* @__PURE__ */ jsx(
|
|
1571
1900
|
"div",
|
|
1572
1901
|
{
|
|
1573
1902
|
className: "px-4 py-2 text-center text-xs text-white/40 border-t",
|
|
1574
1903
|
style: { borderColor: `${systemColor}20` },
|
|
1575
|
-
children:
|
|
1576
|
-
"Press ",
|
|
1577
|
-
/* @__PURE__ */ jsx("kbd", { className: "px-1 bg-white/10 rounded font-mono", children: "ESC" }),
|
|
1578
|
-
" to close"
|
|
1579
|
-
]
|
|
1904
|
+
children: t.modals.shortcuts.pressEsc
|
|
1580
1905
|
}
|
|
1581
1906
|
)
|
|
1582
1907
|
]
|
|
@@ -2037,7 +2362,7 @@ function getButtonStyles(buttonType, isPressed) {
|
|
|
2037
2362
|
};
|
|
2038
2363
|
}
|
|
2039
2364
|
}
|
|
2040
|
-
var VirtualButton =
|
|
2365
|
+
var VirtualButton = React2.memo(function VirtualButton2({
|
|
2041
2366
|
config,
|
|
2042
2367
|
isPressed,
|
|
2043
2368
|
onPress,
|
|
@@ -2051,10 +2376,14 @@ var VirtualButton = React5.memo(function VirtualButton2({
|
|
|
2051
2376
|
systemColor = "#00FF41"
|
|
2052
2377
|
// Default retro green
|
|
2053
2378
|
}) {
|
|
2379
|
+
const t = useKoinTranslation();
|
|
2054
2380
|
const buttonRef = useRef(null);
|
|
2055
2381
|
const isSystemButton = config.type === "start" || config.type === "select";
|
|
2056
2382
|
const displayX = customPosition ? customPosition.x : config.x;
|
|
2057
2383
|
const displayY = customPosition ? customPosition.y : config.y;
|
|
2384
|
+
let label = config.label;
|
|
2385
|
+
if (config.type === "start") label = t.controls.startBtn;
|
|
2386
|
+
if (config.type === "select") label = t.controls.selectBtn;
|
|
2058
2387
|
const {
|
|
2059
2388
|
handleTouchStart,
|
|
2060
2389
|
handleTouchMove,
|
|
@@ -2132,9 +2461,9 @@ var VirtualButton = React5.memo(function VirtualButton2({
|
|
|
2132
2461
|
userSelect: "none",
|
|
2133
2462
|
...pressedStyle
|
|
2134
2463
|
},
|
|
2135
|
-
"aria-label":
|
|
2464
|
+
"aria-label": label,
|
|
2136
2465
|
onContextMenu: (e) => e.preventDefault(),
|
|
2137
|
-
children:
|
|
2466
|
+
children: label
|
|
2138
2467
|
}
|
|
2139
2468
|
);
|
|
2140
2469
|
});
|
|
@@ -2751,7 +3080,7 @@ function dispatchKeyboardEvent(type, code) {
|
|
|
2751
3080
|
canvas.dispatchEvent(event);
|
|
2752
3081
|
return true;
|
|
2753
3082
|
}
|
|
2754
|
-
var Dpad =
|
|
3083
|
+
var Dpad = React2.memo(function Dpad2({
|
|
2755
3084
|
size,
|
|
2756
3085
|
x,
|
|
2757
3086
|
y,
|
|
@@ -3263,9 +3592,10 @@ function GameOverlay({
|
|
|
3263
3592
|
isLoadingSave,
|
|
3264
3593
|
onSelectBios
|
|
3265
3594
|
}) {
|
|
3595
|
+
const t = useKoinTranslation();
|
|
3266
3596
|
const isLoading = status === "loading" || status === "ready" && isLoadingSave;
|
|
3267
3597
|
if (isLoading) {
|
|
3268
|
-
const message = status === "loading" ? { title:
|
|
3598
|
+
const message = status === "loading" ? { title: t.overlay.loading.replace("{{system}}", system || ""), subtitle: t.overlay.initializing } : { title: t.overlay.loadingSave, subtitle: t.overlay.preparingSlot.replace("{{num}}", String(pendingSlot || "")) };
|
|
3269
3599
|
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black z-20", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
3270
3600
|
/* @__PURE__ */ jsx(LoadingSpinner, { color: systemColor, size: "lg" }),
|
|
3271
3601
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
@@ -3303,12 +3633,11 @@ function GameOverlay({
|
|
|
3303
3633
|
children: /* @__PURE__ */ jsx(Play, { className: "w-8 h-8 ml-1", style: { color: systemColor }, fill: systemColor })
|
|
3304
3634
|
}
|
|
3305
3635
|
),
|
|
3306
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-lg uppercase tracking-wider", style: { color: systemColor }, children:
|
|
3636
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-lg uppercase tracking-wider", style: { color: systemColor }, children: t.overlay.play }),
|
|
3307
3637
|
hasPendingSave && /* @__PURE__ */ jsxs("span", { className: "text-gray-400 text-xs flex items-center gap-1", children: [
|
|
3308
3638
|
/* @__PURE__ */ jsx(Save, { size: 12 }),
|
|
3309
|
-
"
|
|
3310
|
-
pendingSlot
|
|
3311
|
-
" ready"
|
|
3639
|
+
" ",
|
|
3640
|
+
t.overlay.slotReady.replace("{{num}}", String(pendingSlot))
|
|
3312
3641
|
] })
|
|
3313
3642
|
]
|
|
3314
3643
|
}
|
|
@@ -3320,7 +3649,7 @@ function GameOverlay({
|
|
|
3320
3649
|
className: "mt-6 flex items-center gap-2 px-4 py-2 rounded-lg text-sm text-gray-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
3321
3650
|
children: [
|
|
3322
3651
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full", style: { backgroundColor: systemColor } }),
|
|
3323
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono uppercase tracking-wider text-xs", children:
|
|
3652
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono uppercase tracking-wider text-xs", children: t.overlay.systemFirmware })
|
|
3324
3653
|
]
|
|
3325
3654
|
}
|
|
3326
3655
|
)
|
|
@@ -3330,8 +3659,8 @@ function GameOverlay({
|
|
|
3330
3659
|
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/90 z-20", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
3331
3660
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-12 h-12 text-red-500" }),
|
|
3332
3661
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
3333
|
-
/* @__PURE__ */ jsx("p", { className: "text-red-400 font-mono uppercase tracking-widest text-sm", children:
|
|
3334
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-500 text-xs mt-1 max-w-xs", children: error ||
|
|
3662
|
+
/* @__PURE__ */ jsx("p", { className: "text-red-400 font-mono uppercase tracking-widest text-sm", children: t.overlay.systemError }),
|
|
3663
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-500 text-xs mt-1 max-w-xs", children: error || t.overlay.failedInit })
|
|
3335
3664
|
] }),
|
|
3336
3665
|
/* @__PURE__ */ jsx(
|
|
3337
3666
|
"button",
|
|
@@ -3339,7 +3668,7 @@ function GameOverlay({
|
|
|
3339
3668
|
onClick: onStart,
|
|
3340
3669
|
className: "mt-2 px-4 py-2 text-sm font-bold rounded-lg transition-colors",
|
|
3341
3670
|
style: { backgroundColor: systemColor, color: "#000" },
|
|
3342
|
-
children:
|
|
3671
|
+
children: t.overlay.retry
|
|
3343
3672
|
}
|
|
3344
3673
|
)
|
|
3345
3674
|
] }) });
|
|
@@ -3357,7 +3686,7 @@ function GameOverlay({
|
|
|
3357
3686
|
] })
|
|
3358
3687
|
}
|
|
3359
3688
|
),
|
|
3360
|
-
/* @__PURE__ */ jsx("p", { className: "font-mono uppercase tracking-wider text-sm", style: { color: systemColor }, children:
|
|
3689
|
+
/* @__PURE__ */ jsx("p", { className: "font-mono uppercase tracking-wider text-sm", style: { color: systemColor }, children: t.overlay.paused })
|
|
3361
3690
|
] }) });
|
|
3362
3691
|
}
|
|
3363
3692
|
return null;
|
|
@@ -3466,6 +3795,7 @@ function ControlMapper({
|
|
|
3466
3795
|
onClose,
|
|
3467
3796
|
system
|
|
3468
3797
|
}) {
|
|
3798
|
+
const t = useKoinTranslation();
|
|
3469
3799
|
const [localControls, setLocalControls] = useState(controls);
|
|
3470
3800
|
const [listeningFor, setListeningFor] = useState(null);
|
|
3471
3801
|
const activeButtons = useMemo(() => {
|
|
@@ -3525,8 +3855,8 @@ function ControlMapper({
|
|
|
3525
3855
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3526
3856
|
/* @__PURE__ */ jsx(Gamepad2, { className: "text-retro-primary", size: 24 }),
|
|
3527
3857
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3528
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children:
|
|
3529
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children:
|
|
3858
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: t.modals.controls.title }),
|
|
3859
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: t.modals.controls.description })
|
|
3530
3860
|
] })
|
|
3531
3861
|
] }),
|
|
3532
3862
|
/* @__PURE__ */ jsx(
|
|
@@ -3557,7 +3887,7 @@ function ControlMapper({
|
|
|
3557
3887
|
px-2 py-1 rounded text-xs font-mono
|
|
3558
3888
|
${listeningFor === btn ? "bg-retro-primary/30 text-retro-primary animate-pulse" : "bg-black/50 text-white"}
|
|
3559
3889
|
`,
|
|
3560
|
-
children: listeningFor === btn ?
|
|
3890
|
+
children: listeningFor === btn ? t.modals.controls.pressKey : formatKeyCode(localControls[btn] || "")
|
|
3561
3891
|
}
|
|
3562
3892
|
)
|
|
3563
3893
|
]
|
|
@@ -3573,7 +3903,7 @@ function ControlMapper({
|
|
|
3573
3903
|
className: "flex items-center gap-2 px-4 py-2 rounded-lg text-sm text-gray-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
3574
3904
|
children: [
|
|
3575
3905
|
/* @__PURE__ */ jsx(RotateCcw, { size: 16 }),
|
|
3576
|
-
|
|
3906
|
+
t.modals.controls.reset
|
|
3577
3907
|
]
|
|
3578
3908
|
}
|
|
3579
3909
|
),
|
|
@@ -3584,7 +3914,7 @@ function ControlMapper({
|
|
|
3584
3914
|
className: "flex items-center gap-2 px-6 py-2 rounded-lg bg-retro-primary text-black font-bold text-sm hover:bg-retro-primary/90 transition-colors",
|
|
3585
3915
|
children: [
|
|
3586
3916
|
/* @__PURE__ */ jsx(Check, { size: 16 }),
|
|
3587
|
-
|
|
3917
|
+
t.modals.controls.save
|
|
3588
3918
|
]
|
|
3589
3919
|
}
|
|
3590
3920
|
)
|
|
@@ -3764,6 +4094,7 @@ function GamepadMapper({
|
|
|
3764
4094
|
onSave,
|
|
3765
4095
|
systemColor = "#00FF41"
|
|
3766
4096
|
}) {
|
|
4097
|
+
const t = useKoinTranslation();
|
|
3767
4098
|
const [selectedPlayer, setSelectedPlayer] = useState(1);
|
|
3768
4099
|
const [bindings, setBindings] = useState({});
|
|
3769
4100
|
const [listeningFor, setListeningFor] = useState(null);
|
|
@@ -3860,8 +4191,8 @@ function GamepadMapper({
|
|
|
3860
4191
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3861
4192
|
/* @__PURE__ */ jsx(Joystick, { className: "text-retro-primary", size: 24, style: { color: systemColor } }),
|
|
3862
4193
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3863
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children:
|
|
3864
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: gamepads.length > 0 ?
|
|
4194
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: t.modals.gamepad.title }),
|
|
4195
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: gamepads.length > 0 ? t.modals.gamepad.connected.replace("{{count}}", gamepads.length.toString()) : t.modals.gamepad.none })
|
|
3865
4196
|
] })
|
|
3866
4197
|
] }),
|
|
3867
4198
|
/* @__PURE__ */ jsx(
|
|
@@ -3875,7 +4206,7 @@ function GamepadMapper({
|
|
|
3875
4206
|
] }),
|
|
3876
4207
|
gamepads.length > 1 && /* @__PURE__ */ jsxs("div", { className: "px-6 py-3 border-b border-white/10 bg-black/30", children: [
|
|
3877
4208
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3878
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 font-medium", children:
|
|
4209
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 font-medium", children: t.modals.gamepad.player }),
|
|
3879
4210
|
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: gamepads.map((gp) => /* @__PURE__ */ jsxs(
|
|
3880
4211
|
"button",
|
|
3881
4212
|
{
|
|
@@ -3908,10 +4239,10 @@ function GamepadMapper({
|
|
|
3908
4239
|
/* @__PURE__ */ jsx(Joystick, { size: 56, className: "text-gray-600 animate-pulse" }),
|
|
3909
4240
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 -m-2 rounded-full border-2 border-dashed border-gray-600 animate-spin", style: { animationDuration: "8s" } })
|
|
3910
4241
|
] }),
|
|
3911
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-300 font-medium mb-2", children:
|
|
3912
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mb-4", children:
|
|
4242
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-300 font-medium mb-2", children: t.modals.gamepad.noController }),
|
|
4243
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mb-4", children: t.modals.gamepad.pressAny }),
|
|
3913
4244
|
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-white/5 border border-white/10", children: [
|
|
3914
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children:
|
|
4245
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: t.modals.gamepad.waiting }),
|
|
3915
4246
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
3916
4247
|
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-gray-500 animate-bounce", style: { animationDelay: "0ms" } }),
|
|
3917
4248
|
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-gray-500 animate-bounce", style: { animationDelay: "150ms" } }),
|
|
@@ -3921,11 +4252,8 @@ function GamepadMapper({
|
|
|
3921
4252
|
] }),
|
|
3922
4253
|
gamepads.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-6 max-h-[400px] overflow-y-auto", children: [
|
|
3923
4254
|
listeningFor && /* @__PURE__ */ jsxs("div", { className: "p-4 rounded-lg bg-black/50 border border-retro-primary/50 text-center animate-pulse", style: { borderColor: `${systemColor}50` }, children: [
|
|
3924
|
-
/* @__PURE__ */
|
|
3925
|
-
|
|
3926
|
-
/* @__PURE__ */ jsx("strong", { children: BUTTON_LABELS[listeningFor] })
|
|
3927
|
-
] }),
|
|
3928
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: "Press Escape to cancel" })
|
|
4255
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-white mb-1", children: t.modals.gamepad.pressButton.replace("{{button}}", BUTTON_LABELS[listeningFor]) }),
|
|
4256
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: t.modals.gamepad.pressEsc })
|
|
3929
4257
|
] }),
|
|
3930
4258
|
BUTTON_GROUPS.map((group) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3931
4259
|
/* @__PURE__ */ jsx("h3", { className: "text-xs font-bold text-gray-500 uppercase tracking-wider mb-2", children: group.label }),
|
|
@@ -3957,7 +4285,7 @@ function GamepadMapper({
|
|
|
3957
4285
|
backgroundColor: `${systemColor}30`,
|
|
3958
4286
|
color: systemColor
|
|
3959
4287
|
} : {},
|
|
3960
|
-
children: listeningFor === btn ?
|
|
4288
|
+
children: listeningFor === btn ? t.controls.press : formatGamepadButton(currentBindings[btn])
|
|
3961
4289
|
}
|
|
3962
4290
|
)
|
|
3963
4291
|
]
|
|
@@ -3975,7 +4303,7 @@ function GamepadMapper({
|
|
|
3975
4303
|
className: "flex items-center gap-2 px-4 py-2 rounded-lg text-sm text-gray-400 hover:text-white hover:bg-white/10 transition-colors disabled:opacity-50",
|
|
3976
4304
|
children: [
|
|
3977
4305
|
/* @__PURE__ */ jsx(RotateCcw, { size: 16 }),
|
|
3978
|
-
|
|
4306
|
+
t.modals.gamepad.reset
|
|
3979
4307
|
]
|
|
3980
4308
|
}
|
|
3981
4309
|
),
|
|
@@ -3988,7 +4316,7 @@ function GamepadMapper({
|
|
|
3988
4316
|
style: { backgroundColor: systemColor },
|
|
3989
4317
|
children: [
|
|
3990
4318
|
/* @__PURE__ */ jsx(Check, { size: 16 }),
|
|
3991
|
-
|
|
4319
|
+
t.modals.gamepad.save
|
|
3992
4320
|
]
|
|
3993
4321
|
}
|
|
3994
4322
|
)
|
|
@@ -4003,7 +4331,8 @@ function CheatModal({
|
|
|
4003
4331
|
onToggle,
|
|
4004
4332
|
onClose
|
|
4005
4333
|
}) {
|
|
4006
|
-
const
|
|
4334
|
+
const t = useKoinTranslation();
|
|
4335
|
+
const [copiedId, setCopiedId] = React2.useState(null);
|
|
4007
4336
|
if (!isOpen) return null;
|
|
4008
4337
|
const handleCopy = async (code, id) => {
|
|
4009
4338
|
await navigator.clipboard.writeText(code);
|
|
@@ -4023,13 +4352,8 @@ function CheatModal({
|
|
|
4023
4352
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4024
4353
|
/* @__PURE__ */ jsx(Code, { className: "text-purple-400", size: 24 }),
|
|
4025
4354
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4026
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children:
|
|
4027
|
-
/* @__PURE__ */
|
|
4028
|
-
cheats.length,
|
|
4029
|
-
" cheat",
|
|
4030
|
-
cheats.length !== 1 ? "s" : "",
|
|
4031
|
-
" available"
|
|
4032
|
-
] })
|
|
4355
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: t.modals.cheats.title }),
|
|
4356
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: t.modals.cheats.available.replace("{{count}}", cheats.length.toString()) })
|
|
4033
4357
|
] })
|
|
4034
4358
|
] }),
|
|
4035
4359
|
/* @__PURE__ */ jsx(
|
|
@@ -4043,8 +4367,8 @@ function CheatModal({
|
|
|
4043
4367
|
] }),
|
|
4044
4368
|
/* @__PURE__ */ jsx("div", { className: "p-4 space-y-2 max-h-[400px] overflow-y-auto", children: cheats.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-12 text-gray-500", children: [
|
|
4045
4369
|
/* @__PURE__ */ jsx(Code, { size: 48, className: "mx-auto mb-3 opacity-50" }),
|
|
4046
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium", children:
|
|
4047
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm mt-1", children:
|
|
4370
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t.modals.cheats.emptyTitle }),
|
|
4371
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-1", children: t.modals.cheats.emptyDesc })
|
|
4048
4372
|
] }) : cheats.map((cheat) => {
|
|
4049
4373
|
const isActive = activeCheats.has(cheat.id);
|
|
4050
4374
|
return /* @__PURE__ */ jsxs(
|
|
@@ -4078,7 +4402,7 @@ function CheatModal({
|
|
|
4078
4402
|
handleCopy(cheat.code, cheat.id);
|
|
4079
4403
|
},
|
|
4080
4404
|
className: "p-1.5 rounded hover:bg-white/10 text-gray-500 hover:text-white transition-colors",
|
|
4081
|
-
title:
|
|
4405
|
+
title: t.modals.cheats.copy,
|
|
4082
4406
|
children: copiedId === cheat.id ? /* @__PURE__ */ jsx(Check, { size: 14, className: "text-green-400" }) : /* @__PURE__ */ jsx(Copy, { size: 14 })
|
|
4083
4407
|
}
|
|
4084
4408
|
)
|
|
@@ -4089,7 +4413,7 @@ function CheatModal({
|
|
|
4089
4413
|
cheat.id
|
|
4090
4414
|
);
|
|
4091
4415
|
}) }),
|
|
4092
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 text-center", children: activeCheats.size > 0 ?
|
|
4416
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 text-center", children: activeCheats.size > 0 ? t.modals.cheats.active.replace("{{count}}", activeCheats.size.toString()) : t.modals.cheats.toggleHint }) })
|
|
4093
4417
|
] })
|
|
4094
4418
|
] });
|
|
4095
4419
|
}
|
|
@@ -4102,18 +4426,22 @@ function formatBytes(bytes) {
|
|
|
4102
4426
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
|
|
4103
4427
|
}
|
|
4104
4428
|
function formatTimestamp(timestamp) {
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4429
|
+
try {
|
|
4430
|
+
const date = new Date(timestamp);
|
|
4431
|
+
const now = /* @__PURE__ */ new Date();
|
|
4432
|
+
const diff = now.getTime() - date.getTime();
|
|
4433
|
+
if (diff < 6e4) return "Just now";
|
|
4434
|
+
if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
|
|
4435
|
+
if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
|
|
4436
|
+
return date.toLocaleDateString("en-US", {
|
|
4437
|
+
month: "short",
|
|
4438
|
+
day: "numeric",
|
|
4439
|
+
hour: "2-digit",
|
|
4440
|
+
minute: "2-digit"
|
|
4441
|
+
});
|
|
4442
|
+
} catch {
|
|
4443
|
+
return "Unknown";
|
|
4444
|
+
}
|
|
4117
4445
|
}
|
|
4118
4446
|
function SaveSlotModal({
|
|
4119
4447
|
isOpen,
|
|
@@ -4127,6 +4455,7 @@ function SaveSlotModal({
|
|
|
4127
4455
|
maxSlots = 5,
|
|
4128
4456
|
onUpgrade
|
|
4129
4457
|
}) {
|
|
4458
|
+
const t = useKoinTranslation();
|
|
4130
4459
|
if (!isOpen) return null;
|
|
4131
4460
|
const isSaveMode = mode === "save";
|
|
4132
4461
|
const allSlots = [1, 2, 3, 4, 5];
|
|
@@ -4155,8 +4484,8 @@ function SaveSlotModal({
|
|
|
4155
4484
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4156
4485
|
isSaveMode ? /* @__PURE__ */ jsx(Save, { className: "text-retro-primary", size: 24 }) : /* @__PURE__ */ jsx(Download, { className: "text-retro-primary", size: 24 }),
|
|
4157
4486
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4158
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: isSaveMode ?
|
|
4159
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: isSaveMode ?
|
|
4487
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: isSaveMode ? t.modals.saveSlots.saveTitle : t.modals.saveSlots.loadTitle }),
|
|
4488
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: isSaveMode ? t.modals.saveSlots.subtitleSave : t.modals.saveSlots.subtitleLoad })
|
|
4160
4489
|
] })
|
|
4161
4490
|
] }),
|
|
4162
4491
|
/* @__PURE__ */ jsx(
|
|
@@ -4170,7 +4499,7 @@ function SaveSlotModal({
|
|
|
4170
4499
|
] }),
|
|
4171
4500
|
/* @__PURE__ */ jsx("div", { className: "p-4 space-y-2 max-h-[400px] overflow-y-auto", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-gray-400", children: [
|
|
4172
4501
|
/* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin mb-3" }),
|
|
4173
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm", children:
|
|
4502
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: t.modals.saveSlots.loading })
|
|
4174
4503
|
] }) : displaySlots.map((slotNum) => {
|
|
4175
4504
|
const slotData = getSlotData(slotNum);
|
|
4176
4505
|
const isEmpty = !slotData;
|
|
@@ -4187,14 +4516,10 @@ function SaveSlotModal({
|
|
|
4187
4516
|
children: [
|
|
4188
4517
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-12 h-12 rounded-lg flex items-center justify-center font-bold text-xl bg-gray-700 text-gray-500", children: /* @__PURE__ */ jsx(Lock, { size: 20 }) }),
|
|
4189
4518
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
4190
|
-
/* @__PURE__ */
|
|
4191
|
-
"Slot ",
|
|
4192
|
-
slotNum,
|
|
4193
|
-
" Locked"
|
|
4194
|
-
] }),
|
|
4519
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-400 group-hover:text-white transition-colors", children: t.modals.saveSlots.locked.replace("{{num}}", slotNum.toString()) }),
|
|
4195
4520
|
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 group-hover:text-retro-primary transition-colors flex items-center gap-1", children: [
|
|
4196
4521
|
/* @__PURE__ */ jsx(Zap, { size: 10 }),
|
|
4197
|
-
|
|
4522
|
+
t.modals.saveSlots.upgrade
|
|
4198
4523
|
] })
|
|
4199
4524
|
] })
|
|
4200
4525
|
]
|
|
@@ -4218,8 +4543,8 @@ function SaveSlotModal({
|
|
|
4218
4543
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: isAutoSaveSlot ? (
|
|
4219
4544
|
// Auto-save slot - special display
|
|
4220
4545
|
isEmpty ? /* @__PURE__ */ jsxs("div", { className: "text-retro-primary/70", children: [
|
|
4221
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium", children:
|
|
4222
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-retro-primary/50", children:
|
|
4546
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t.modals.saveSlots.autoSave }),
|
|
4547
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-retro-primary/50", children: t.modals.saveSlots.autoSaveDesc })
|
|
4223
4548
|
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4224
4549
|
slotData.screenshot && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-16 h-12 rounded border border-retro-primary/30 overflow-hidden bg-black", children: /* @__PURE__ */ jsx(
|
|
4225
4550
|
"img",
|
|
@@ -4233,7 +4558,7 @@ function SaveSlotModal({
|
|
|
4233
4558
|
}
|
|
4234
4559
|
) }),
|
|
4235
4560
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
4236
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-retro-primary truncate", children:
|
|
4561
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-retro-primary truncate", children: t.modals.saveSlots.autoSave }),
|
|
4237
4562
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-retro-primary/60 mt-1", children: [
|
|
4238
4563
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
4239
4564
|
/* @__PURE__ */ jsx(Clock, { size: 12 }),
|
|
@@ -4247,8 +4572,8 @@ function SaveSlotModal({
|
|
|
4247
4572
|
] })
|
|
4248
4573
|
] })
|
|
4249
4574
|
) : isEmpty ? /* @__PURE__ */ jsxs("div", { className: "text-gray-500", children: [
|
|
4250
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium", children:
|
|
4251
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs", children:
|
|
4575
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t.modals.saveSlots.emptySlot }),
|
|
4576
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs", children: t.modals.saveSlots.noData })
|
|
4252
4577
|
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4253
4578
|
slotData.screenshot && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-16 h-12 rounded border border-white/10 overflow-hidden bg-black", children: /* @__PURE__ */ jsx(
|
|
4254
4579
|
"img",
|
|
@@ -4262,10 +4587,7 @@ function SaveSlotModal({
|
|
|
4262
4587
|
}
|
|
4263
4588
|
) }),
|
|
4264
4589
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
4265
|
-
/* @__PURE__ */
|
|
4266
|
-
"Slot ",
|
|
4267
|
-
slotNum
|
|
4268
|
-
] }),
|
|
4590
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-white truncate", children: t.modals.saveSlots.slot.replace("{{num}}", slotNum.toString()) }),
|
|
4269
4591
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-gray-400 mt-1", children: [
|
|
4270
4592
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
4271
4593
|
/* @__PURE__ */ jsx(Clock, { size: 12 }),
|
|
@@ -4295,7 +4617,7 @@ function SaveSlotModal({
|
|
|
4295
4617
|
slotNum
|
|
4296
4618
|
);
|
|
4297
4619
|
}) }),
|
|
4298
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 text-center", children: isSaveMode ?
|
|
4620
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 text-center", children: isSaveMode ? t.modals.saveSlots.footerSave : t.modals.saveSlots.footerLoad }) })
|
|
4299
4621
|
] })
|
|
4300
4622
|
] });
|
|
4301
4623
|
}
|
|
@@ -4307,6 +4629,7 @@ function BiosSelectionModal({
|
|
|
4307
4629
|
onSelectBios,
|
|
4308
4630
|
systemColor = "#00FF41"
|
|
4309
4631
|
}) {
|
|
4632
|
+
const t = useKoinTranslation();
|
|
4310
4633
|
if (!isOpen) return null;
|
|
4311
4634
|
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
4312
4635
|
/* @__PURE__ */ jsx(
|
|
@@ -4326,8 +4649,8 @@ function BiosSelectionModal({
|
|
|
4326
4649
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4327
4650
|
/* @__PURE__ */ jsx(Cpu, { size: 24, style: { color: systemColor } }),
|
|
4328
4651
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4329
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white uppercase tracking-wide", children:
|
|
4330
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children:
|
|
4652
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white uppercase tracking-wide", children: t.modals.bios.title }),
|
|
4653
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: t.modals.bios.description })
|
|
4331
4654
|
] })
|
|
4332
4655
|
] }),
|
|
4333
4656
|
/* @__PURE__ */ jsx(
|
|
@@ -4343,8 +4666,9 @@ function BiosSelectionModal({
|
|
|
4343
4666
|
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 rounded bg-yellow-500/10 border border-yellow-500/20 flex items-start gap-3", children: [
|
|
4344
4667
|
/* @__PURE__ */ jsx(AlertCircle, { className: "shrink-0 text-yellow-500 mt-0.5", size: 16 }),
|
|
4345
4668
|
/* @__PURE__ */ jsxs("div", { className: "text-xs text-yellow-200/80", children: [
|
|
4346
|
-
/* @__PURE__ */ jsx("strong", { children:
|
|
4347
|
-
"
|
|
4669
|
+
/* @__PURE__ */ jsx("strong", { children: t.modals.bios.warningTitle }),
|
|
4670
|
+
" ",
|
|
4671
|
+
t.modals.bios.warning
|
|
4348
4672
|
] })
|
|
4349
4673
|
] }),
|
|
4350
4674
|
/* @__PURE__ */ jsxs(
|
|
@@ -4374,10 +4698,10 @@ function BiosSelectionModal({
|
|
|
4374
4698
|
),
|
|
4375
4699
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
4376
4700
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4377
|
-
/* @__PURE__ */ jsx("span", { className: `font-mono font-bold truncate ${!currentBiosId ? "text-white" : "text-gray-300"}`, children:
|
|
4378
|
-
!currentBiosId && /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 rounded text-[10px] font-bold bg-white/20 text-white uppercase tracking-wider", children:
|
|
4701
|
+
/* @__PURE__ */ jsx("span", { className: `font-mono font-bold truncate ${!currentBiosId ? "text-white" : "text-gray-300"}`, children: t.modals.bios.systemDefault }),
|
|
4702
|
+
!currentBiosId && /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 rounded text-[10px] font-bold bg-white/20 text-white uppercase tracking-wider", children: t.modals.bios.active })
|
|
4379
4703
|
] }),
|
|
4380
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children:
|
|
4704
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: t.modals.bios.defaultDesc })
|
|
4381
4705
|
] }),
|
|
4382
4706
|
!currentBiosId && /* @__PURE__ */ jsx(Check, { size: 20, style: { color: systemColor } })
|
|
4383
4707
|
]
|
|
@@ -4385,8 +4709,8 @@ function BiosSelectionModal({
|
|
|
4385
4709
|
),
|
|
4386
4710
|
biosOptions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8 text-gray-500", children: [
|
|
4387
4711
|
/* @__PURE__ */ jsx(FileCode, { size: 48, className: "mx-auto mb-3 opacity-30" }),
|
|
4388
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm", children:
|
|
4389
|
-
/* @__PURE__ */ jsx("p", { className: "text-[10px] mt-1 text-gray-600", children:
|
|
4712
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: t.modals.bios.emptyTitle }),
|
|
4713
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] mt-1 text-gray-600", children: t.modals.bios.emptyDesc })
|
|
4390
4714
|
] }) : biosOptions.map((bios) => {
|
|
4391
4715
|
const isSelected = bios.id === currentBiosId;
|
|
4392
4716
|
return /* @__PURE__ */ jsxs(
|
|
@@ -4417,7 +4741,7 @@ function BiosSelectionModal({
|
|
|
4417
4741
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
4418
4742
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4419
4743
|
/* @__PURE__ */ jsx("span", { className: `font-mono font-bold truncate ${isSelected ? "text-white" : "text-gray-300"}`, children: bios.name }),
|
|
4420
|
-
isSelected && /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 rounded text-[10px] font-bold bg-white/20 text-white uppercase tracking-wider", children:
|
|
4744
|
+
isSelected && /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 rounded text-[10px] font-bold bg-white/20 text-white uppercase tracking-wider", children: t.modals.bios.active })
|
|
4421
4745
|
] }),
|
|
4422
4746
|
bios.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: bios.description })
|
|
4423
4747
|
] }),
|
|
@@ -4428,12 +4752,84 @@ function BiosSelectionModal({
|
|
|
4428
4752
|
);
|
|
4429
4753
|
})
|
|
4430
4754
|
] }),
|
|
4431
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-500 uppercase tracking-widest", children:
|
|
4755
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-black/30 border-t border-white/10 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-500 uppercase tracking-widest", children: t.modals.bios.footer }) })
|
|
4432
4756
|
]
|
|
4433
4757
|
}
|
|
4434
4758
|
)
|
|
4435
4759
|
] });
|
|
4436
4760
|
}
|
|
4761
|
+
function SettingsModal({
|
|
4762
|
+
isOpen,
|
|
4763
|
+
onClose,
|
|
4764
|
+
currentLanguage,
|
|
4765
|
+
onLanguageChange,
|
|
4766
|
+
systemColor = "#00FF41"
|
|
4767
|
+
}) {
|
|
4768
|
+
const t = useKoinTranslation();
|
|
4769
|
+
if (!isOpen) return null;
|
|
4770
|
+
const languages = [
|
|
4771
|
+
{ code: "en", name: "English" },
|
|
4772
|
+
{ code: "es", name: "Espa\xF1ol" },
|
|
4773
|
+
{ code: "fr", name: "Fran\xE7ais" }
|
|
4774
|
+
];
|
|
4775
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
4776
|
+
/* @__PURE__ */ jsx(
|
|
4777
|
+
"div",
|
|
4778
|
+
{
|
|
4779
|
+
className: "absolute inset-0 bg-black/80 backdrop-blur-sm",
|
|
4780
|
+
onClick: onClose
|
|
4781
|
+
}
|
|
4782
|
+
),
|
|
4783
|
+
/* @__PURE__ */ jsxs("div", { className: "relative bg-gray-900 border border-white/10 rounded-xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden animate-in fade-in zoom-in-95 duration-200", children: [
|
|
4784
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-white/10 bg-black/50", children: [
|
|
4785
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4786
|
+
/* @__PURE__ */ jsx(Settings, { className: "text-white", size: 20 }),
|
|
4787
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-white", children: t.settings.title })
|
|
4788
|
+
] }),
|
|
4789
|
+
/* @__PURE__ */ jsx(
|
|
4790
|
+
"button",
|
|
4791
|
+
{
|
|
4792
|
+
onClick: onClose,
|
|
4793
|
+
className: "p-2 rounded-lg hover:bg-white/10 text-gray-400 hover:text-white transition-colors",
|
|
4794
|
+
children: /* @__PURE__ */ jsx(X, { size: 20 })
|
|
4795
|
+
}
|
|
4796
|
+
)
|
|
4797
|
+
] }),
|
|
4798
|
+
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-6", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4799
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-400", children: [
|
|
4800
|
+
/* @__PURE__ */ jsx(Globe, { size: 16 }),
|
|
4801
|
+
/* @__PURE__ */ jsx("span", { children: t.settings.language })
|
|
4802
|
+
] }),
|
|
4803
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-2", children: languages.map((lang) => {
|
|
4804
|
+
const isActive = currentLanguage === lang.code;
|
|
4805
|
+
return /* @__PURE__ */ jsxs(
|
|
4806
|
+
"button",
|
|
4807
|
+
{
|
|
4808
|
+
onClick: () => onLanguageChange(lang.code),
|
|
4809
|
+
className: `
|
|
4810
|
+
flex items-center justify-between px-4 py-3 rounded-lg border transition-all
|
|
4811
|
+
${isActive ? "bg-white/10 border-white/20 text-white" : "bg-black/20 border-transparent text-gray-400 hover:bg-white/5 hover:text-white"}
|
|
4812
|
+
`,
|
|
4813
|
+
children: [
|
|
4814
|
+
/* @__PURE__ */ jsx("span", { children: lang.name }),
|
|
4815
|
+
isActive && /* @__PURE__ */ jsx(Check, { size: 16, style: { color: systemColor } })
|
|
4816
|
+
]
|
|
4817
|
+
},
|
|
4818
|
+
lang.code
|
|
4819
|
+
);
|
|
4820
|
+
}) })
|
|
4821
|
+
] }) }),
|
|
4822
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4 bg-black/30 border-t border-white/10 text-center", children: /* @__PURE__ */ jsx(
|
|
4823
|
+
"button",
|
|
4824
|
+
{
|
|
4825
|
+
onClick: onClose,
|
|
4826
|
+
className: "text-sm text-gray-500 hover:text-white transition-colors",
|
|
4827
|
+
children: t.modals.shortcuts.pressEsc
|
|
4828
|
+
}
|
|
4829
|
+
) })
|
|
4830
|
+
] })
|
|
4831
|
+
] });
|
|
4832
|
+
}
|
|
4437
4833
|
function GameModals({
|
|
4438
4834
|
controlsModalOpen,
|
|
4439
4835
|
setControlsModalOpen,
|
|
@@ -4465,7 +4861,11 @@ function GameModals({
|
|
|
4465
4861
|
setBiosModalOpen,
|
|
4466
4862
|
availableBios,
|
|
4467
4863
|
currentBiosId,
|
|
4468
|
-
onSelectBios
|
|
4864
|
+
onSelectBios,
|
|
4865
|
+
settingsModalOpen,
|
|
4866
|
+
setSettingsModalOpen,
|
|
4867
|
+
currentLanguage,
|
|
4868
|
+
onLanguageChange
|
|
4469
4869
|
}) {
|
|
4470
4870
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4471
4871
|
/* @__PURE__ */ jsx(
|
|
@@ -4541,6 +4941,19 @@ function GameModals({
|
|
|
4541
4941
|
},
|
|
4542
4942
|
systemColor
|
|
4543
4943
|
}
|
|
4944
|
+
),
|
|
4945
|
+
/* @__PURE__ */ jsx(
|
|
4946
|
+
SettingsModal,
|
|
4947
|
+
{
|
|
4948
|
+
isOpen: settingsModalOpen,
|
|
4949
|
+
onClose: () => {
|
|
4950
|
+
setSettingsModalOpen(false);
|
|
4951
|
+
onResume();
|
|
4952
|
+
},
|
|
4953
|
+
currentLanguage,
|
|
4954
|
+
onLanguageChange,
|
|
4955
|
+
systemColor
|
|
4956
|
+
}
|
|
4544
4957
|
)
|
|
4545
4958
|
] });
|
|
4546
4959
|
}
|
|
@@ -4555,10 +4968,11 @@ function LoginForm({
|
|
|
4555
4968
|
error,
|
|
4556
4969
|
onSubmit
|
|
4557
4970
|
}) {
|
|
4971
|
+
const t = useKoinTranslation();
|
|
4558
4972
|
return /* @__PURE__ */ jsxs("form", { onSubmit, className: "p-4 space-y-3", children: [
|
|
4559
4973
|
/* @__PURE__ */ jsxs("div", { className: "text-center mb-4", children: [
|
|
4560
4974
|
/* @__PURE__ */ jsx("div", { className: "w-16 h-16 mx-auto mb-3 rounded-full bg-gradient-to-br from-yellow-500/20 to-orange-500/20 border border-yellow-500/30 flex items-center justify-center", children: /* @__PURE__ */ jsx(Trophy, { className: "text-yellow-400", size: 32 }) }),
|
|
4561
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-400 text-xs", children:
|
|
4975
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-400 text-xs", children: t.retroAchievements.connectAccount }),
|
|
4562
4976
|
/* @__PURE__ */ jsxs(
|
|
4563
4977
|
"a",
|
|
4564
4978
|
{
|
|
@@ -4567,14 +4981,14 @@ function LoginForm({
|
|
|
4567
4981
|
rel: "noopener noreferrer",
|
|
4568
4982
|
className: "text-yellow-400 hover:text-yellow-300 text-xs inline-flex items-center gap-1 mt-1",
|
|
4569
4983
|
children: [
|
|
4570
|
-
|
|
4984
|
+
t.retroAchievements.createAccount,
|
|
4571
4985
|
/* @__PURE__ */ jsx(ExternalLink, { size: 10 })
|
|
4572
4986
|
]
|
|
4573
4987
|
}
|
|
4574
4988
|
)
|
|
4575
4989
|
] }),
|
|
4576
4990
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4577
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] text-gray-500 mb-1 uppercase tracking-wider", children:
|
|
4991
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] text-gray-500 mb-1 uppercase tracking-wider", children: t.retroAchievements.username }),
|
|
4578
4992
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4579
4993
|
/* @__PURE__ */ jsx(User, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-500", size: 14 }),
|
|
4580
4994
|
/* @__PURE__ */ jsx(
|
|
@@ -4583,7 +4997,7 @@ function LoginForm({
|
|
|
4583
4997
|
type: "text",
|
|
4584
4998
|
value: username,
|
|
4585
4999
|
onChange: (e) => setUsername(e.target.value),
|
|
4586
|
-
placeholder:
|
|
5000
|
+
placeholder: t.retroAchievements.yourUsername,
|
|
4587
5001
|
className: "w-full pl-8 pr-3 py-2 text-sm bg-black/50 border border-white/20 rounded-lg text-white placeholder-gray-500 focus:border-yellow-500/50 focus:outline-none transition-colors",
|
|
4588
5002
|
disabled: isLoading
|
|
4589
5003
|
}
|
|
@@ -4591,7 +5005,7 @@ function LoginForm({
|
|
|
4591
5005
|
] })
|
|
4592
5006
|
] }),
|
|
4593
5007
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4594
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] text-gray-500 mb-1 uppercase tracking-wider", children:
|
|
5008
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] text-gray-500 mb-1 uppercase tracking-wider", children: t.retroAchievements.password }),
|
|
4595
5009
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4596
5010
|
/* @__PURE__ */ jsx(Lock, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-500", size: 14 }),
|
|
4597
5011
|
/* @__PURE__ */ jsx(
|
|
@@ -4600,7 +5014,7 @@ function LoginForm({
|
|
|
4600
5014
|
type: showPassword ? "text" : "password",
|
|
4601
5015
|
value: password,
|
|
4602
5016
|
onChange: (e) => setPassword(e.target.value),
|
|
4603
|
-
placeholder:
|
|
5017
|
+
placeholder: t.retroAchievements.yourPassword,
|
|
4604
5018
|
className: "w-full pl-8 pr-9 py-2 text-sm bg-black/50 border border-white/20 rounded-lg text-white placeholder-gray-500 focus:border-yellow-500/50 focus:outline-none transition-colors",
|
|
4605
5019
|
disabled: isLoading
|
|
4606
5020
|
}
|
|
@@ -4628,16 +5042,20 @@ function LoginForm({
|
|
|
4628
5042
|
className: "w-full flex items-center justify-center gap-2 px-3 py-2.5 bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-400 hover:to-orange-400 text-black text-sm font-bold rounded-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4629
5043
|
children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4630
5044
|
/* @__PURE__ */ jsx(Loader2, { className: "animate-spin", size: 14 }),
|
|
4631
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
5045
|
+
/* @__PURE__ */ jsx("span", { children: t.retroAchievements.connecting })
|
|
4632
5046
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4633
5047
|
/* @__PURE__ */ jsx(Trophy, { size: 14 }),
|
|
4634
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
5048
|
+
/* @__PURE__ */ jsx("span", { children: t.retroAchievements.login })
|
|
4635
5049
|
] })
|
|
4636
5050
|
}
|
|
4637
5051
|
),
|
|
4638
5052
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-500/10 border border-blue-500/20 rounded-lg", children: /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-blue-300 leading-relaxed", children: [
|
|
4639
|
-
/* @__PURE__ */
|
|
4640
|
-
|
|
5053
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
5054
|
+
"\u{1F512} ",
|
|
5055
|
+
t.retroAchievements.privacy
|
|
5056
|
+
] }),
|
|
5057
|
+
" ",
|
|
5058
|
+
t.retroAchievements.privacyText
|
|
4641
5059
|
] }) })
|
|
4642
5060
|
] });
|
|
4643
5061
|
}
|
|
@@ -4661,6 +5079,7 @@ function SettingsTab({
|
|
|
4661
5079
|
progress,
|
|
4662
5080
|
onLogout
|
|
4663
5081
|
}) {
|
|
5082
|
+
const t = useKoinTranslation();
|
|
4664
5083
|
return /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-3", children: [
|
|
4665
5084
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-3 bg-black/30 rounded-lg border border-yellow-500/20", children: [
|
|
4666
5085
|
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-lg bg-gray-800 overflow-hidden border-2 border-yellow-500/50 flex-shrink-0", children: credentials.avatarUrl ? /* @__PURE__ */ jsx(
|
|
@@ -4730,7 +5149,7 @@ function SettingsTab({
|
|
|
4730
5149
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4731
5150
|
/* @__PURE__ */ jsx(Shield, { className: hardcoreEnabled ? "text-red-500" : "text-gray-500", size: 16 }),
|
|
4732
5151
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4733
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-white", children:
|
|
5152
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-white", children: t.retroAchievements.hardcore }),
|
|
4734
5153
|
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-500", children: "2x points, no savestates" })
|
|
4735
5154
|
] })
|
|
4736
5155
|
] }),
|
|
@@ -4748,11 +5167,11 @@ function SettingsTab({
|
|
|
4748
5167
|
}
|
|
4749
5168
|
)
|
|
4750
5169
|
] }),
|
|
4751
|
-
hardcoreEnabled && /* @__PURE__ */ jsx("div", { className: "mt-2 p-2 bg-red-500/10 border border-red-500/20 rounded text-[10px] text-red-300", children:
|
|
5170
|
+
hardcoreEnabled && /* @__PURE__ */ jsx("div", { className: "mt-2 p-2 bg-red-500/10 border border-red-500/20 rounded text-[10px] text-red-300", children: t.common.disabledInHardcore })
|
|
4752
5171
|
] }),
|
|
4753
5172
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs px-1", children: [
|
|
4754
5173
|
/* @__PURE__ */ jsx(CheckCircle, { className: "text-green-500", size: 12 }),
|
|
4755
|
-
/* @__PURE__ */ jsx("span", { className: "text-green-400", children:
|
|
5174
|
+
/* @__PURE__ */ jsx("span", { className: "text-green-400", children: t.retroAchievements.connectedStatus })
|
|
4756
5175
|
] }),
|
|
4757
5176
|
/* @__PURE__ */ jsxs(
|
|
4758
5177
|
"button",
|
|
@@ -4761,7 +5180,7 @@ function SettingsTab({
|
|
|
4761
5180
|
className: "w-full flex items-center justify-center gap-2 px-3 py-2 bg-gray-800 hover:bg-gray-700 text-white text-xs rounded-lg transition-colors border border-white/10",
|
|
4762
5181
|
children: [
|
|
4763
5182
|
/* @__PURE__ */ jsx(LogOut, { size: 14 }),
|
|
4764
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
5183
|
+
/* @__PURE__ */ jsx("span", { children: t.retroAchievements.logout })
|
|
4765
5184
|
]
|
|
4766
5185
|
}
|
|
4767
5186
|
)
|
|
@@ -4778,18 +5197,19 @@ function AchievementsTab({
|
|
|
4778
5197
|
earnedPoints,
|
|
4779
5198
|
totalPoints
|
|
4780
5199
|
}) {
|
|
5200
|
+
const t = useKoinTranslation();
|
|
4781
5201
|
if (!currentGame) {
|
|
4782
5202
|
return /* @__PURE__ */ jsxs("div", { className: "p-6 text-center", children: [
|
|
4783
5203
|
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 mx-auto mb-3 rounded-full bg-gray-800 flex items-center justify-center", children: /* @__PURE__ */ jsx(Trophy, { className: "text-gray-600", size: 24 }) }),
|
|
4784
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400", children:
|
|
4785
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children:
|
|
5204
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400", children: t.retroAchievements.noGame }),
|
|
5205
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children: t.retroAchievements.loadGame })
|
|
4786
5206
|
] });
|
|
4787
5207
|
}
|
|
4788
5208
|
if (achievements.length === 0) {
|
|
4789
5209
|
return /* @__PURE__ */ jsxs("div", { className: "p-6 text-center", children: [
|
|
4790
5210
|
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 mx-auto mb-3 rounded-full bg-gray-800 flex items-center justify-center", children: /* @__PURE__ */ jsx(Trophy, { className: "text-gray-600", size: 24 }) }),
|
|
4791
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400", children:
|
|
4792
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children:
|
|
5211
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400", children: t.retroAchievements.noAchievements }),
|
|
5212
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children: t.retroAchievements.notSupported })
|
|
4793
5213
|
] });
|
|
4794
5214
|
}
|
|
4795
5215
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -4842,7 +5262,7 @@ function AchievementsTab({
|
|
|
4842
5262
|
{
|
|
4843
5263
|
onClick: () => setFilter(f),
|
|
4844
5264
|
className: `px-2 py-0.5 text-[10px] rounded-full border transition-colors ${filter === f ? "bg-yellow-500/20 border-yellow-500/50 text-yellow-400" : "border-gray-700 text-gray-500 hover:text-gray-300"}`,
|
|
4845
|
-
children:
|
|
5265
|
+
children: t.retroAchievements.filters[f]
|
|
4846
5266
|
},
|
|
4847
5267
|
f
|
|
4848
5268
|
)) })
|
|
@@ -4881,8 +5301,7 @@ function AchievementsTab({
|
|
|
4881
5301
|
/* @__PURE__ */ jsxs("div", { className: "p-2 text-[10px] text-gray-500 text-center border-t border-gray-800/50", children: [
|
|
4882
5302
|
progress,
|
|
4883
5303
|
"% complete \u2022 ",
|
|
4884
|
-
totalPoints - earnedPoints
|
|
4885
|
-
" pts remaining"
|
|
5304
|
+
t.retroAchievements.ptsRemaining.replace("{{count}}", (totalPoints - earnedPoints).toString())
|
|
4886
5305
|
] })
|
|
4887
5306
|
] });
|
|
4888
5307
|
}
|
|
@@ -4901,6 +5320,7 @@ function RASidebar({
|
|
|
4901
5320
|
achievements,
|
|
4902
5321
|
unlockedIds
|
|
4903
5322
|
}) {
|
|
5323
|
+
const t = useKoinTranslation();
|
|
4904
5324
|
const defaultTab = isLoggedIn && currentGame && achievements.length > 0 ? "achievements" : "settings";
|
|
4905
5325
|
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
4906
5326
|
const [filter, setFilter] = useState("all");
|
|
@@ -4926,7 +5346,7 @@ function RASidebar({
|
|
|
4926
5346
|
e.preventDefault();
|
|
4927
5347
|
setLocalError(null);
|
|
4928
5348
|
if (!username.trim() || !password.trim()) {
|
|
4929
|
-
setLocalError(
|
|
5349
|
+
setLocalError(t.retroAchievements.usernameRequired);
|
|
4930
5350
|
return;
|
|
4931
5351
|
}
|
|
4932
5352
|
const success = await onLogin(username.trim(), password.trim());
|
|
@@ -4969,7 +5389,7 @@ function RASidebar({
|
|
|
4969
5389
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
4970
5390
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4971
5391
|
/* @__PURE__ */ jsx(Trophy, { className: "text-yellow-400", size: 18 }),
|
|
4972
|
-
/* @__PURE__ */ jsx("span", { className: "font-heading text-sm text-white", children:
|
|
5392
|
+
/* @__PURE__ */ jsx("span", { className: "font-heading text-sm text-white", children: t.retroAchievements.title })
|
|
4973
5393
|
] }),
|
|
4974
5394
|
/* @__PURE__ */ jsx(
|
|
4975
5395
|
"button",
|
|
@@ -4988,7 +5408,7 @@ function RASidebar({
|
|
|
4988
5408
|
className: `flex-1 flex items-center justify-center gap-1.5 px-2 py-1.5 text-xs rounded-md transition-colors ${activeTab === "achievements" ? "bg-yellow-500/20 text-yellow-400" : "text-gray-400 hover:text-white"}`,
|
|
4989
5409
|
children: [
|
|
4990
5410
|
/* @__PURE__ */ jsx(List, { size: 12 }),
|
|
4991
|
-
|
|
5411
|
+
t.retroAchievements.achievements
|
|
4992
5412
|
]
|
|
4993
5413
|
}
|
|
4994
5414
|
),
|
|
@@ -4999,7 +5419,7 @@ function RASidebar({
|
|
|
4999
5419
|
className: `flex-1 flex items-center justify-center gap-1.5 px-2 py-1.5 text-xs rounded-md transition-colors ${activeTab === "settings" ? "bg-yellow-500/20 text-yellow-400" : "text-gray-400 hover:text-white"}`,
|
|
5000
5420
|
children: [
|
|
5001
5421
|
/* @__PURE__ */ jsx(Settings, { size: 12 }),
|
|
5002
|
-
|
|
5422
|
+
t.settings.title
|
|
5003
5423
|
]
|
|
5004
5424
|
}
|
|
5005
5425
|
)
|
|
@@ -5046,7 +5466,7 @@ function RASidebar({
|
|
|
5046
5466
|
}
|
|
5047
5467
|
) }),
|
|
5048
5468
|
/* @__PURE__ */ jsxs("div", { className: "p-2 border-t border-gray-800 bg-gray-900/80 text-[10px] text-gray-500 text-center flex-shrink-0", children: [
|
|
5049
|
-
|
|
5469
|
+
t.retroAchievements.poweredBy,
|
|
5050
5470
|
" ",
|
|
5051
5471
|
/* @__PURE__ */ jsx(
|
|
5052
5472
|
"a",
|
|
@@ -6650,6 +7070,7 @@ function useVolume({
|
|
|
6650
7070
|
};
|
|
6651
7071
|
}
|
|
6652
7072
|
function useControls(system, onNotify) {
|
|
7073
|
+
const t = useKoinTranslation();
|
|
6653
7074
|
const defaultControls = getConsoleKeyboardDefaults(system || "SNES");
|
|
6654
7075
|
const [controls, setControls] = useState(() => {
|
|
6655
7076
|
if (typeof window !== "undefined") {
|
|
@@ -6664,13 +7085,13 @@ function useControls(system, onNotify) {
|
|
|
6664
7085
|
const saveControls = useCallback((newControls) => {
|
|
6665
7086
|
setControls(newControls);
|
|
6666
7087
|
saveKeyboardMapping(newControls, system);
|
|
6667
|
-
onNotify?.(
|
|
6668
|
-
}, [system, onNotify]);
|
|
7088
|
+
onNotify?.(t.notifications.controlsSaved, "success");
|
|
7089
|
+
}, [system, onNotify, t]);
|
|
6669
7090
|
const resetToDefaults = useCallback(() => {
|
|
6670
7091
|
setControls(defaultControls);
|
|
6671
7092
|
saveKeyboardMapping(defaultControls, system);
|
|
6672
|
-
onNotify?.(
|
|
6673
|
-
}, [defaultControls, system, onNotify]);
|
|
7093
|
+
onNotify?.(t.notifications.controlsReset, "info");
|
|
7094
|
+
}, [defaultControls, system, onNotify, t]);
|
|
6674
7095
|
return {
|
|
6675
7096
|
controls,
|
|
6676
7097
|
saveControls,
|
|
@@ -6696,16 +7117,17 @@ function useGameSession(props) {
|
|
|
6696
7117
|
canvasRef,
|
|
6697
7118
|
showToast
|
|
6698
7119
|
} = props;
|
|
7120
|
+
const t = useKoinTranslation();
|
|
6699
7121
|
const { controls, saveControls } = useControls(system, showToast);
|
|
6700
7122
|
const [gamepadModalOpen, setGamepadModalOpen] = useState(false);
|
|
6701
7123
|
const [controlsModalOpen, setControlsModalOpen] = useState(false);
|
|
6702
7124
|
const { gamepads, connectedCount } = useGamepad({
|
|
6703
7125
|
onConnect: (gamepad) => {
|
|
6704
7126
|
showToast(
|
|
6705
|
-
gamepad.name ||
|
|
7127
|
+
gamepad.name || t.notifications.controllerReady,
|
|
6706
7128
|
"gamepad",
|
|
6707
7129
|
{
|
|
6708
|
-
title:
|
|
7130
|
+
title: t.notifications.controllerConnected,
|
|
6709
7131
|
duration: 4e3,
|
|
6710
7132
|
action: {
|
|
6711
7133
|
label: "Configure",
|
|
@@ -6716,10 +7138,11 @@ function useGameSession(props) {
|
|
|
6716
7138
|
},
|
|
6717
7139
|
onDisconnect: () => {
|
|
6718
7140
|
showToast(
|
|
6719
|
-
|
|
7141
|
+
t.notifications.controllerDisconnected,
|
|
6720
7142
|
"warning",
|
|
6721
7143
|
{
|
|
6722
|
-
title:
|
|
7144
|
+
title: t.notifications.controllerDisconnected,
|
|
7145
|
+
// Title repeats or generic? Using same for now
|
|
6723
7146
|
duration: 3e3
|
|
6724
7147
|
}
|
|
6725
7148
|
);
|
|
@@ -6749,10 +7172,10 @@ function useGameSession(props) {
|
|
|
6749
7172
|
if (arcadeSystems.includes(system.toLowerCase())) {
|
|
6750
7173
|
setTimeout(() => {
|
|
6751
7174
|
showToast(
|
|
6752
|
-
|
|
7175
|
+
t.notifications.insertCoin,
|
|
6753
7176
|
"info",
|
|
6754
7177
|
{
|
|
6755
|
-
title:
|
|
7178
|
+
title: t.notifications.insertCoinTitle,
|
|
6756
7179
|
duration: 5e3
|
|
6757
7180
|
}
|
|
6758
7181
|
);
|
|
@@ -6990,6 +7413,7 @@ function useGameSaves({
|
|
|
6990
7413
|
onDeleteSaveState,
|
|
6991
7414
|
autoSaveInterval
|
|
6992
7415
|
}) {
|
|
7416
|
+
const t = useKoinTranslation();
|
|
6993
7417
|
const [saveModalOpen, setSaveModalOpen] = useState(false);
|
|
6994
7418
|
const [saveModalMode, setSaveModalMode] = useState("save");
|
|
6995
7419
|
const [saveSlots, setSaveSlots] = useState([]);
|
|
@@ -7016,7 +7440,7 @@ function useGameSaves({
|
|
|
7016
7440
|
setSaveSlots(slots);
|
|
7017
7441
|
} catch (err) {
|
|
7018
7442
|
console.error("Failed to fetch save slots:", err);
|
|
7019
|
-
showToast("
|
|
7443
|
+
showToast(t.notifications.failedFetch, "error", { title: t.overlays.toast.error });
|
|
7020
7444
|
} finally {
|
|
7021
7445
|
setIsSlotLoading(false);
|
|
7022
7446
|
}
|
|
@@ -7033,7 +7457,7 @@ function useGameSaves({
|
|
|
7033
7457
|
const result = await nostalgist.saveStateWithBlob();
|
|
7034
7458
|
if (result) {
|
|
7035
7459
|
await onSaveState(0, result.blob, void 0);
|
|
7036
|
-
showToast(
|
|
7460
|
+
showToast(t.notifications.saved, "success", { title: t.overlays.toast.saved });
|
|
7037
7461
|
}
|
|
7038
7462
|
});
|
|
7039
7463
|
} else {
|
|
@@ -7047,7 +7471,7 @@ function useGameSaves({
|
|
|
7047
7471
|
a.download = `${fileName}.state`;
|
|
7048
7472
|
a.click();
|
|
7049
7473
|
URL.revokeObjectURL(url);
|
|
7050
|
-
showToast(
|
|
7474
|
+
showToast(t.notifications.downloaded, "success", { title: t.overlays.toast.saved });
|
|
7051
7475
|
}
|
|
7052
7476
|
});
|
|
7053
7477
|
}
|
|
@@ -7066,9 +7490,9 @@ function useGameSaves({
|
|
|
7066
7490
|
await queueRef.current.add(async () => {
|
|
7067
7491
|
await nostalgist.loadState(new Uint8Array(buffer));
|
|
7068
7492
|
});
|
|
7069
|
-
showToast(
|
|
7493
|
+
showToast(t.notifications.loaded, "success", { title: t.overlays.toast.loaded });
|
|
7070
7494
|
} else {
|
|
7071
|
-
showToast(
|
|
7495
|
+
showToast(t.notifications.noSaveFound, "error", { title: t.overlays.toast.error });
|
|
7072
7496
|
}
|
|
7073
7497
|
} else {
|
|
7074
7498
|
const input = document.createElement("input");
|
|
@@ -7081,7 +7505,7 @@ function useGameSaves({
|
|
|
7081
7505
|
await queueRef.current.add(async () => {
|
|
7082
7506
|
await nostalgist.loadState(new Uint8Array(buffer));
|
|
7083
7507
|
});
|
|
7084
|
-
showToast("
|
|
7508
|
+
showToast(t.notifications.loadedFile, "success", { title: t.overlays.toast.loaded });
|
|
7085
7509
|
}
|
|
7086
7510
|
};
|
|
7087
7511
|
input.click();
|
|
@@ -7106,14 +7530,14 @@ function useGameSaves({
|
|
|
7106
7530
|
console.warn("Screenshot failed", e);
|
|
7107
7531
|
}
|
|
7108
7532
|
await onSaveState(slot, result.blob, screen);
|
|
7109
|
-
showToast(
|
|
7533
|
+
showToast(t.notifications.savedSlot.replace("{{num}}", slot.toString()), "success", { title: t.overlays.toast.saved });
|
|
7110
7534
|
setSaveModalOpen(false);
|
|
7111
7535
|
resume();
|
|
7112
7536
|
}
|
|
7113
7537
|
});
|
|
7114
7538
|
} catch (err) {
|
|
7115
7539
|
console.error("Save failed:", err);
|
|
7116
|
-
showToast(
|
|
7540
|
+
showToast(t.notifications.failedSave, "error", { title: t.overlays.toast.error });
|
|
7117
7541
|
} finally {
|
|
7118
7542
|
setActioningSlot(null);
|
|
7119
7543
|
}
|
|
@@ -7127,15 +7551,15 @@ function useGameSaves({
|
|
|
7127
7551
|
await queueRef.current.add(async () => {
|
|
7128
7552
|
await nostalgist.loadState(new Uint8Array(buffer));
|
|
7129
7553
|
});
|
|
7130
|
-
showToast(
|
|
7554
|
+
showToast(t.notifications.loadedSlot.replace("{{num}}", slot.toString()), "success", { title: t.overlays.toast.loaded });
|
|
7131
7555
|
setSaveModalOpen(false);
|
|
7132
7556
|
resume();
|
|
7133
7557
|
} else {
|
|
7134
|
-
showToast(
|
|
7558
|
+
showToast(t.notifications.emptySlot, "error", { title: t.overlays.toast.error });
|
|
7135
7559
|
}
|
|
7136
7560
|
} catch (err) {
|
|
7137
7561
|
console.error("Load failed:", err);
|
|
7138
|
-
showToast(
|
|
7562
|
+
showToast(t.notifications.failedLoad, "error", { title: t.overlays.toast.error });
|
|
7139
7563
|
} finally {
|
|
7140
7564
|
setActioningSlot(null);
|
|
7141
7565
|
}
|
|
@@ -7147,11 +7571,11 @@ function useGameSaves({
|
|
|
7147
7571
|
setActioningSlot(slot);
|
|
7148
7572
|
try {
|
|
7149
7573
|
await onDeleteSaveState(slot);
|
|
7150
|
-
showToast(
|
|
7574
|
+
showToast(t.notifications.deletedSlot.replace("{{num}}", slot.toString()), "success", { title: t.overlays.toast.saved });
|
|
7151
7575
|
refreshSlots();
|
|
7152
7576
|
} catch (err) {
|
|
7153
7577
|
console.error("Delete failed:", err);
|
|
7154
|
-
showToast(
|
|
7578
|
+
showToast(t.notifications.failedDelete, "error", { title: t.overlays.toast.error });
|
|
7155
7579
|
} finally {
|
|
7156
7580
|
setActioningSlot(null);
|
|
7157
7581
|
}
|
|
@@ -7529,7 +7953,485 @@ var sendTelemetry = (eventName, params = {}) => {
|
|
|
7529
7953
|
} catch (e) {
|
|
7530
7954
|
}
|
|
7531
7955
|
};
|
|
7532
|
-
|
|
7956
|
+
|
|
7957
|
+
// src/locales/es.ts
|
|
7958
|
+
var es = {
|
|
7959
|
+
controls: {
|
|
7960
|
+
play: "Jugar",
|
|
7961
|
+
pause: "Pausar",
|
|
7962
|
+
reset: "Reiniciar",
|
|
7963
|
+
rewind: "Rebobinar",
|
|
7964
|
+
save: "Guardar",
|
|
7965
|
+
load: "Cargar",
|
|
7966
|
+
snap: "Capturar",
|
|
7967
|
+
rec: "Grabar",
|
|
7968
|
+
stopRec: "Detener",
|
|
7969
|
+
startRecord: "Comenzar grabaci\xF3n",
|
|
7970
|
+
stopRecord: "Detener grabaci\xF3n",
|
|
7971
|
+
mute: "Silenciar",
|
|
7972
|
+
unmute: "Activar sonido",
|
|
7973
|
+
help: "Ayuda",
|
|
7974
|
+
full: "Pantalla comp.",
|
|
7975
|
+
// Abbreviated
|
|
7976
|
+
keys: "Teclas",
|
|
7977
|
+
menuOpen: "Abrir men\xFA",
|
|
7978
|
+
menuClose: "Cerrar men\xFA",
|
|
7979
|
+
gamepadConnected: "{{count}} mando{{plural}} conectado(s) - haz clic para configurar",
|
|
7980
|
+
noGamepad: "No hay mando detectado - presiona cualquier bot\xF3n para conectar",
|
|
7981
|
+
press: "Pulsa",
|
|
7982
|
+
startBtn: "START",
|
|
7983
|
+
selectBtn: "SELECT"
|
|
7984
|
+
},
|
|
7985
|
+
common: {
|
|
7986
|
+
disabledInHardcore: "Desactivado en modo Hardcore",
|
|
7987
|
+
notSupported: "No compatible con esta consola",
|
|
7988
|
+
playToEnableRewind: "Juega unos segundos para activar el rebobinado"
|
|
7989
|
+
},
|
|
7990
|
+
settings: {
|
|
7991
|
+
title: "Ajustes",
|
|
7992
|
+
general: "General",
|
|
7993
|
+
audio: "Sonido",
|
|
7994
|
+
video: "V\xEDdeo",
|
|
7995
|
+
input: "Entrada",
|
|
7996
|
+
advanced: "Avanzado",
|
|
7997
|
+
fullscreen: "Pantalla completa",
|
|
7998
|
+
controls: "Controles",
|
|
7999
|
+
gamepad: "Mando",
|
|
8000
|
+
cheats: "Trucos",
|
|
8001
|
+
retroAchievements: "RetroAchievements",
|
|
8002
|
+
shortcuts: "Atajos",
|
|
8003
|
+
exit: "Salir",
|
|
8004
|
+
language: "Idioma",
|
|
8005
|
+
selectLanguage: "Seleccionar idioma"
|
|
8006
|
+
},
|
|
8007
|
+
overlay: {
|
|
8008
|
+
play: "JUGAR",
|
|
8009
|
+
systemFirmware: "FIRMWARE DEL SISTEMA",
|
|
8010
|
+
loading: "Cargando {{system}}",
|
|
8011
|
+
initializing: "Inicializando emulador",
|
|
8012
|
+
loadingSave: "Cargando partida",
|
|
8013
|
+
preparingSlot: "Preparando ranura {{num}}",
|
|
8014
|
+
systemError: "Error del sistema",
|
|
8015
|
+
failedInit: "Error al inicializar el emulador",
|
|
8016
|
+
retry: "Reintentar",
|
|
8017
|
+
slotReady: "Ranura {{num}} lista",
|
|
8018
|
+
paused: "Pausado"
|
|
8019
|
+
},
|
|
8020
|
+
notifications: {
|
|
8021
|
+
saved: "Estado guardado",
|
|
8022
|
+
loaded: "Estado cargado",
|
|
8023
|
+
error: "Error",
|
|
8024
|
+
recordingStarted: "Grabaci\xF3n iniciada",
|
|
8025
|
+
recordingSaved: "Grabaci\xF3n guardada",
|
|
8026
|
+
downloaded: "Estado descargado",
|
|
8027
|
+
loadedFile: "Estado cargado desde archivo",
|
|
8028
|
+
savedSlot: "Guardado en ranura {{num}}",
|
|
8029
|
+
loadedSlot: "Cargado desde ranura {{num}}",
|
|
8030
|
+
deletedSlot: "Ranura {{num}} eliminada",
|
|
8031
|
+
emptySlot: "Ranura vac\xEDa",
|
|
8032
|
+
noSaveFound: "No se encontr\xF3 partida guardada",
|
|
8033
|
+
failedSave: "Error al guardar",
|
|
8034
|
+
failedLoad: "Error al cargar",
|
|
8035
|
+
failedDelete: "Error al eliminar",
|
|
8036
|
+
failedFetch: "Error al cargar ranuras",
|
|
8037
|
+
controllerConnected: "Mando conectado",
|
|
8038
|
+
controllerDisconnected: "Mando desconectado",
|
|
8039
|
+
controllerReady: "Mando listo para usar",
|
|
8040
|
+
insertCoin: "Pulsa SHIFT para insertar moneda",
|
|
8041
|
+
insertCoinTitle: "\u{1FA99} Insertar Moneda",
|
|
8042
|
+
controlsSaved: "Controles guardados",
|
|
8043
|
+
controlsReset: "Controles restablecidos"
|
|
8044
|
+
},
|
|
8045
|
+
modals: {
|
|
8046
|
+
shortcuts: {
|
|
8047
|
+
title: "Atajos de teclado",
|
|
8048
|
+
playerShortcuts: "Atajos del reproductor",
|
|
8049
|
+
overlays: "Superposiciones",
|
|
8050
|
+
recording: "Grabaci\xF3n",
|
|
8051
|
+
showHelp: "Mostrar ayuda",
|
|
8052
|
+
perfOverlay: "Superposici\xF3n de rendimiento",
|
|
8053
|
+
inputDisplay: "Mostrar entrada",
|
|
8054
|
+
toggleRec: "Alternar grabaci\xF3n",
|
|
8055
|
+
toggleMute: "Alternar silencio",
|
|
8056
|
+
pressEsc: "Pulsa ESC para cerrar"
|
|
8057
|
+
},
|
|
8058
|
+
controls: {
|
|
8059
|
+
title: "Controles",
|
|
8060
|
+
keyboard: "Asignaci\xF3n de teclado",
|
|
8061
|
+
description: "Haz clic en un bot\xF3n y pulsa una tecla",
|
|
8062
|
+
pressKey: "Pulsa una tecla...",
|
|
8063
|
+
reset: "Restablecer",
|
|
8064
|
+
save: "Guardar controles"
|
|
8065
|
+
},
|
|
8066
|
+
gamepad: {
|
|
8067
|
+
title: "Ajustes de mando",
|
|
8068
|
+
noGamepad: "No se detecta mando",
|
|
8069
|
+
connected: "{{count}} mando{{s}} conectado(s)",
|
|
8070
|
+
none: "Ning\xFAn mando detectado",
|
|
8071
|
+
player: "Jugador:",
|
|
8072
|
+
noController: "Sin mando",
|
|
8073
|
+
pressAny: "Pulsa cualquier bot\xF3n para conectar",
|
|
8074
|
+
waiting: "Esperando entrada...",
|
|
8075
|
+
pressButton: "Pulsa bot\xF3n para {{button}}",
|
|
8076
|
+
pressEsc: "Pulsa Escape para cancelar",
|
|
8077
|
+
reset: "Restablecer",
|
|
8078
|
+
save: "Guardar ajustes"
|
|
8079
|
+
},
|
|
8080
|
+
cheats: {
|
|
8081
|
+
title: "Trucos",
|
|
8082
|
+
addCheat: "A\xF1adir truco",
|
|
8083
|
+
available: "{{count}} truco{{s}} disponible(s)",
|
|
8084
|
+
emptyTitle: "No hay trucos disponibles",
|
|
8085
|
+
emptyDesc: "No se encontraron c\xF3digos para este juego",
|
|
8086
|
+
copy: "Copiar c\xF3digo",
|
|
8087
|
+
active: "{{count}} truco{{s}} activo(s)",
|
|
8088
|
+
toggleHint: "Haz clic para activar/desactivar"
|
|
8089
|
+
},
|
|
8090
|
+
saveSlots: {
|
|
8091
|
+
title: "Guardar partida",
|
|
8092
|
+
saveTitle: "Guardar",
|
|
8093
|
+
loadTitle: "Cargar",
|
|
8094
|
+
emptySlot: "Ranura vac\xEDa",
|
|
8095
|
+
subtitleSave: "Elige una ranura para guardar",
|
|
8096
|
+
subtitleLoad: "Elige una ranura para cargar",
|
|
8097
|
+
loading: "Cargando partidas...",
|
|
8098
|
+
locked: "Ranura {{num}} bloqueada",
|
|
8099
|
+
upgrade: "Mejora para desbloquear m\xE1s ranuras",
|
|
8100
|
+
autoSave: "Autoguardado",
|
|
8101
|
+
autoSaveDesc: "Reservado para guardado autom\xE1tico",
|
|
8102
|
+
noData: "Sin datos",
|
|
8103
|
+
slot: "Ranura {{num}}",
|
|
8104
|
+
footerSave: "Las partidas se guardan en la nube y se sincronizan",
|
|
8105
|
+
footerLoad: "Tu progreso se restaurar\xE1 al punto seleccionado"
|
|
8106
|
+
},
|
|
8107
|
+
bios: {
|
|
8108
|
+
title: "Selecci\xF3n de BIOS",
|
|
8109
|
+
description: "Selecciona una BIOS para este juego",
|
|
8110
|
+
warningTitle: "Nota:",
|
|
8111
|
+
warning: "Cambiar la BIOS reiniciar\xE1 el emulador. Se perder\xE1 el progreso no guardado.",
|
|
8112
|
+
systemDefault: "Por defecto",
|
|
8113
|
+
active: "Activo",
|
|
8114
|
+
defaultDesc: "Usar BIOS interna del emulador",
|
|
8115
|
+
emptyTitle: "No se encontraron archivos de BIOS.",
|
|
8116
|
+
emptyDesc: "Sube archivos BIOS en tu biblioteca.",
|
|
8117
|
+
footer: "Firmware del sistema"
|
|
8118
|
+
}
|
|
8119
|
+
},
|
|
8120
|
+
retroAchievements: {
|
|
8121
|
+
title: "RetroAchievements",
|
|
8122
|
+
login: "Iniciar sesi\xF3n",
|
|
8123
|
+
logout: "Cerrar sesi\xF3n",
|
|
8124
|
+
username: "Usuario",
|
|
8125
|
+
password: "Password",
|
|
8126
|
+
hardcore: "Modo Hardcore",
|
|
8127
|
+
achievements: "Logros",
|
|
8128
|
+
locked: "Bloqueado",
|
|
8129
|
+
unlocked: "Desbloqueado",
|
|
8130
|
+
mastered: "Dominado",
|
|
8131
|
+
identifying: "Identificando juego...",
|
|
8132
|
+
achievementsAvailable: "{{count}} logros disponibles",
|
|
8133
|
+
gameNotSupported: "Conectado - Juego no en base de datos",
|
|
8134
|
+
connect: "Conectar RetroAchievements",
|
|
8135
|
+
connected: "RetroAchievements (conectado)",
|
|
8136
|
+
createAccount: "Crear cuenta",
|
|
8137
|
+
privacy: "Privacidad:",
|
|
8138
|
+
privacyText: "Tu contrase\xF1a solo se usa para autenticaci\xF3n con RA. No se almacena.",
|
|
8139
|
+
connecting: "Conectando...",
|
|
8140
|
+
connectAccount: "Conecta tu cuenta para rastrear logros",
|
|
8141
|
+
poweredBy: "Con la tecnolog\xEDa de",
|
|
8142
|
+
connectedStatus: "Conectado",
|
|
8143
|
+
yourUsername: "Tu usuario RA",
|
|
8144
|
+
yourPassword: "Tu contrase\xF1a RA",
|
|
8145
|
+
usernameRequired: "Usuario y contrase\xF1a requeridos",
|
|
8146
|
+
noGame: "Sin juego cargado",
|
|
8147
|
+
loadGame: "Carga un juego para ver los logros",
|
|
8148
|
+
noAchievements: "No se encontraron logros",
|
|
8149
|
+
notSupported: "Este juego puede no ser compatible",
|
|
8150
|
+
ptsRemaining: "{{count}} pts restantes",
|
|
8151
|
+
filters: {
|
|
8152
|
+
all: "Todos",
|
|
8153
|
+
locked: "Bloqueado",
|
|
8154
|
+
unlocked: "Desbloqueado"
|
|
8155
|
+
}
|
|
8156
|
+
},
|
|
8157
|
+
overlays: {
|
|
8158
|
+
performance: {
|
|
8159
|
+
title: "Estad\xEDsticas",
|
|
8160
|
+
fps: "FPS",
|
|
8161
|
+
frameTime: "FT",
|
|
8162
|
+
memory: "MEM",
|
|
8163
|
+
core: "Core",
|
|
8164
|
+
input: "ENTRADA",
|
|
8165
|
+
active: "ACTIVO"
|
|
8166
|
+
},
|
|
8167
|
+
toast: {
|
|
8168
|
+
saved: "Juego guardado",
|
|
8169
|
+
loaded: "Juego cargado",
|
|
8170
|
+
error: "Error"
|
|
8171
|
+
},
|
|
8172
|
+
recording: {
|
|
8173
|
+
started: "Grabaci\xF3n iniciada",
|
|
8174
|
+
stopped: "Grabaci\xF3n detenida",
|
|
8175
|
+
saved: "Grabaci\xF3n guardada",
|
|
8176
|
+
paused: "PAUSA",
|
|
8177
|
+
recording: "GRABANDO",
|
|
8178
|
+
resume: "Reanudar grabaci\xF3n",
|
|
8179
|
+
pause: "Pausar grabaci\xF3n",
|
|
8180
|
+
stop: "Detener y guardar",
|
|
8181
|
+
hover: "Controles"
|
|
8182
|
+
}
|
|
8183
|
+
}
|
|
8184
|
+
};
|
|
8185
|
+
|
|
8186
|
+
// src/locales/fr.ts
|
|
8187
|
+
var fr = {
|
|
8188
|
+
controls: {
|
|
8189
|
+
play: "Jouer",
|
|
8190
|
+
pause: "Pause",
|
|
8191
|
+
reset: "R\xE9initialiser",
|
|
8192
|
+
rewind: "Rembobiner",
|
|
8193
|
+
save: "Sauver",
|
|
8194
|
+
load: "Charger",
|
|
8195
|
+
snap: "Photo",
|
|
8196
|
+
rec: "Enr.",
|
|
8197
|
+
stopRec: "Arr\xEAter",
|
|
8198
|
+
startRecord: "D\xE9marrer l'enregistrement",
|
|
8199
|
+
stopRecord: "Arr\xEAter l'enregistrement",
|
|
8200
|
+
mute: "Muet",
|
|
8201
|
+
unmute: "Son",
|
|
8202
|
+
help: "Aide",
|
|
8203
|
+
full: "Plein \xE9cran",
|
|
8204
|
+
keys: "Touches",
|
|
8205
|
+
menuOpen: "Ouvrir menu",
|
|
8206
|
+
menuClose: "Fermer menu",
|
|
8207
|
+
gamepadConnected: "{{count}} manette{{plural}} connect\xE9e(s)",
|
|
8208
|
+
noGamepad: "Aucune manette d\xE9tect\xE9e",
|
|
8209
|
+
press: "Appuyez",
|
|
8210
|
+
startBtn: "START",
|
|
8211
|
+
selectBtn: "SELECT"
|
|
8212
|
+
},
|
|
8213
|
+
common: {
|
|
8214
|
+
disabledInHardcore: "D\xE9sactiv\xE9 en mode Hardcore",
|
|
8215
|
+
notSupported: "Non support\xE9 sur cette console",
|
|
8216
|
+
playToEnableRewind: "Jouez quelques secondes pour activer le rembobinage"
|
|
8217
|
+
},
|
|
8218
|
+
settings: {
|
|
8219
|
+
title: "Param\xE8tres",
|
|
8220
|
+
general: "G\xE9n\xE9ral",
|
|
8221
|
+
audio: "Audio",
|
|
8222
|
+
video: "Vid\xE9o",
|
|
8223
|
+
input: "Entr\xE9e",
|
|
8224
|
+
advanced: "Avanc\xE9",
|
|
8225
|
+
fullscreen: "Plein \xE9cran",
|
|
8226
|
+
controls: "Contr\xF4les",
|
|
8227
|
+
gamepad: "Manette",
|
|
8228
|
+
cheats: "Codes",
|
|
8229
|
+
retroAchievements: "Succ\xE8s",
|
|
8230
|
+
shortcuts: "Raccourcis",
|
|
8231
|
+
exit: "Quitter",
|
|
8232
|
+
language: "Langue",
|
|
8233
|
+
selectLanguage: "Choisir la langue"
|
|
8234
|
+
},
|
|
8235
|
+
overlay: {
|
|
8236
|
+
play: "JOUER",
|
|
8237
|
+
systemFirmware: "MICROLOGICIEL SYST\xC8ME",
|
|
8238
|
+
loading: "Chargement {{system}}",
|
|
8239
|
+
initializing: "Initialisation de l'\xE9mulateur",
|
|
8240
|
+
loadingSave: "Chargement de la sauvegarde",
|
|
8241
|
+
preparingSlot: "Pr\xE9paration de l'emplacement {{num}}",
|
|
8242
|
+
systemError: "Erreur syst\xE8me",
|
|
8243
|
+
failedInit: "\xC9chec de l'initialisation",
|
|
8244
|
+
retry: "R\xE9essayer",
|
|
8245
|
+
slotReady: "Emplacement {{num}} pr\xEAt",
|
|
8246
|
+
paused: "Pause"
|
|
8247
|
+
},
|
|
8248
|
+
notifications: {
|
|
8249
|
+
saved: "\xC9tat sauvegard\xE9",
|
|
8250
|
+
loaded: "\xC9tat charg\xE9",
|
|
8251
|
+
error: "Erreur",
|
|
8252
|
+
recordingStarted: "Enregistrement d\xE9marr\xE9",
|
|
8253
|
+
recordingSaved: "Enregistrement sauvegard\xE9",
|
|
8254
|
+
downloaded: "\xC9tat t\xE9l\xE9charg\xE9",
|
|
8255
|
+
loadedFile: "\xC9tat charg\xE9 depuis fichier",
|
|
8256
|
+
savedSlot: "Sauvegard\xE9 sur l'emplacement {{num}}",
|
|
8257
|
+
loadedSlot: "Charg\xE9 depuis l'emplacement {{num}}",
|
|
8258
|
+
deletedSlot: "Emplacement {{num}} supprim\xE9",
|
|
8259
|
+
emptySlot: "Emplacement vide",
|
|
8260
|
+
noSaveFound: "Aucune sauvegarde trouv\xE9e",
|
|
8261
|
+
failedSave: "\xC9chec de la sauvegarde",
|
|
8262
|
+
failedLoad: "\xC9chec du chargement",
|
|
8263
|
+
failedDelete: "\xC9chec de la suppression",
|
|
8264
|
+
failedFetch: "\xC9chec du chargement des emplacements",
|
|
8265
|
+
controllerConnected: "Manette connect\xE9e",
|
|
8266
|
+
controllerDisconnected: "Manette d\xE9connect\xE9e",
|
|
8267
|
+
controllerReady: "Manette pr\xEAte",
|
|
8268
|
+
insertCoin: "Appuyez sur SHIFT pour ins\xE9rer une pi\xE8ce",
|
|
8269
|
+
insertCoinTitle: "\u{1FA99} Ins\xE9rer Pi\xE8ce",
|
|
8270
|
+
controlsSaved: "Contr\xF4les sauvegard\xE9s",
|
|
8271
|
+
controlsReset: "Contr\xF4les r\xE9initialis\xE9s"
|
|
8272
|
+
},
|
|
8273
|
+
modals: {
|
|
8274
|
+
shortcuts: {
|
|
8275
|
+
title: "Raccourcis clavier",
|
|
8276
|
+
playerShortcuts: "Raccourcis du lecteur",
|
|
8277
|
+
overlays: "Superpositions",
|
|
8278
|
+
recording: "Enregistrement",
|
|
8279
|
+
showHelp: "Afficher l'aide",
|
|
8280
|
+
perfOverlay: "Overlay de performance",
|
|
8281
|
+
inputDisplay: "Afficher les entr\xE9es",
|
|
8282
|
+
toggleRec: "Basculer l'enregistrement",
|
|
8283
|
+
toggleMute: "Basculer le son",
|
|
8284
|
+
pressEsc: "Appuyez sur ESC pour fermer"
|
|
8285
|
+
},
|
|
8286
|
+
controls: {
|
|
8287
|
+
title: "Contr\xF4les",
|
|
8288
|
+
keyboard: "Configuration clavier",
|
|
8289
|
+
description: "Cliquez sur un bouton et appuyez sur une touche",
|
|
8290
|
+
pressKey: "Appuyez...",
|
|
8291
|
+
reset: "R\xE9initialiser",
|
|
8292
|
+
save: "Sauvegarder"
|
|
8293
|
+
},
|
|
8294
|
+
gamepad: {
|
|
8295
|
+
title: "Param\xE8tres manette",
|
|
8296
|
+
noGamepad: "Aucune manette",
|
|
8297
|
+
connected: "{{count}} manette{{s}} connect\xE9e(s)",
|
|
8298
|
+
none: "Aucune manette d\xE9tect\xE9e",
|
|
8299
|
+
player: "Joueur:",
|
|
8300
|
+
noController: "Aucune manette",
|
|
8301
|
+
pressAny: "Appuyez sur un bouton pour connecter",
|
|
8302
|
+
waiting: "En attente...",
|
|
8303
|
+
pressButton: "Appuyez pour {{button}}",
|
|
8304
|
+
pressEsc: "Echap pour annuler",
|
|
8305
|
+
reset: "R\xE9initialiser",
|
|
8306
|
+
save: "Sauvegarder"
|
|
8307
|
+
},
|
|
8308
|
+
cheats: {
|
|
8309
|
+
title: "Codes de triche",
|
|
8310
|
+
addCheat: "Ajouter un code",
|
|
8311
|
+
available: "{{count}} code{{s}} disponible(s)",
|
|
8312
|
+
emptyTitle: "Aucun code disponible",
|
|
8313
|
+
emptyDesc: "Aucun code trouv\xE9 pour ce jeu",
|
|
8314
|
+
copy: "Copier",
|
|
8315
|
+
active: "{{count}} actif(s)",
|
|
8316
|
+
toggleHint: "Cliquez pour activer/d\xE9sactiver"
|
|
8317
|
+
},
|
|
8318
|
+
saveSlots: {
|
|
8319
|
+
title: "Sauvegardes",
|
|
8320
|
+
saveTitle: "Sauvegarder",
|
|
8321
|
+
loadTitle: "Charger",
|
|
8322
|
+
emptySlot: "Vide",
|
|
8323
|
+
subtitleSave: "Choisir un emplacement",
|
|
8324
|
+
subtitleLoad: "Choisir une sauvegarde",
|
|
8325
|
+
loading: "Chargement...",
|
|
8326
|
+
locked: "Emplacement {{num}} verrouill\xE9",
|
|
8327
|
+
upgrade: "Am\xE9liorer pour plus d'emplacements",
|
|
8328
|
+
autoSave: "Auto-Sauvegarde",
|
|
8329
|
+
autoSaveDesc: "R\xE9serv\xE9 \xE0 la sauvegarde automatique",
|
|
8330
|
+
noData: "Aucune donn\xE9e",
|
|
8331
|
+
slot: "Emplacement {{num}}",
|
|
8332
|
+
footerSave: "Les sauvegardes sont synchronis\xE9es dans le cloud",
|
|
8333
|
+
footerLoad: "Votre progression sera restaur\xE9e"
|
|
8334
|
+
},
|
|
8335
|
+
bios: {
|
|
8336
|
+
title: "S\xE9lection BIOS",
|
|
8337
|
+
description: "S\xE9lectionnez un BIOS",
|
|
8338
|
+
warningTitle: "Note:",
|
|
8339
|
+
warning: "Changer le BIOS red\xE9marre l'\xE9mulateur. Progression non sauvegard\xE9e sera perdue.",
|
|
8340
|
+
systemDefault: "Par d\xE9faut",
|
|
8341
|
+
active: "Actif",
|
|
8342
|
+
defaultDesc: "Utiliser le BIOS par d\xE9faut",
|
|
8343
|
+
emptyTitle: "Aucun BIOS trouv\xE9.",
|
|
8344
|
+
emptyDesc: "T\xE9l\xE9versez des fichiers BIOS dans votre biblioth\xE8que.",
|
|
8345
|
+
footer: "Firmware syst\xE8me"
|
|
8346
|
+
}
|
|
8347
|
+
},
|
|
8348
|
+
retroAchievements: {
|
|
8349
|
+
title: "RetroAchievements",
|
|
8350
|
+
login: "Connexion",
|
|
8351
|
+
logout: "D\xE9connexion",
|
|
8352
|
+
username: "Utilisateur",
|
|
8353
|
+
password: "Mot de passe",
|
|
8354
|
+
hardcore: "Mode Hardcore",
|
|
8355
|
+
achievements: "Succ\xE8s",
|
|
8356
|
+
locked: "Verrouill\xE9",
|
|
8357
|
+
unlocked: "D\xE9verrouill\xE9",
|
|
8358
|
+
mastered: "Ma\xEEtris\xE9",
|
|
8359
|
+
identifying: "Identification...",
|
|
8360
|
+
achievementsAvailable: "{{count}} succ\xE8s disponibles",
|
|
8361
|
+
gameNotSupported: "Jeu non support\xE9 par RA",
|
|
8362
|
+
connect: "Connecter RetroAchievements",
|
|
8363
|
+
connected: "RetroAchievements (connect\xE9)",
|
|
8364
|
+
createAccount: "Cr\xE9er un compte",
|
|
8365
|
+
privacy: "Confidentialit\xE9:",
|
|
8366
|
+
privacyText: "Votre mot de passe n'est pas stock\xE9.",
|
|
8367
|
+
connecting: "Connexion...",
|
|
8368
|
+
connectAccount: "Connectez votre compte",
|
|
8369
|
+
poweredBy: "Propuls\xE9 par",
|
|
8370
|
+
connectedStatus: "Connect\xE9",
|
|
8371
|
+
yourUsername: "Votre utilisateur RA",
|
|
8372
|
+
yourPassword: "Votre mot de passe RA",
|
|
8373
|
+
usernameRequired: "Utilisateur et mot de passe requis",
|
|
8374
|
+
noGame: "Aucun jeu",
|
|
8375
|
+
loadGame: "Chargez un jeu pour voir les succ\xE8s",
|
|
8376
|
+
noAchievements: "Aucun succ\xE8s trouv\xE9",
|
|
8377
|
+
notSupported: "Ce jeu n'est peut-\xEAtre pas support\xE9",
|
|
8378
|
+
ptsRemaining: "{{count}} pts restants",
|
|
8379
|
+
filters: {
|
|
8380
|
+
all: "Tous",
|
|
8381
|
+
locked: "Verrouill\xE9s",
|
|
8382
|
+
unlocked: "D\xE9verrouill\xE9s"
|
|
8383
|
+
}
|
|
8384
|
+
},
|
|
8385
|
+
overlays: {
|
|
8386
|
+
performance: {
|
|
8387
|
+
title: "Stats",
|
|
8388
|
+
fps: "FPS",
|
|
8389
|
+
frameTime: "FT",
|
|
8390
|
+
memory: "MEM",
|
|
8391
|
+
core: "Core",
|
|
8392
|
+
input: "ENTR\xC9E",
|
|
8393
|
+
active: "ACTIF"
|
|
8394
|
+
},
|
|
8395
|
+
toast: {
|
|
8396
|
+
saved: "Sauvegard\xE9",
|
|
8397
|
+
loaded: "Charg\xE9",
|
|
8398
|
+
error: "Erreur"
|
|
8399
|
+
},
|
|
8400
|
+
recording: {
|
|
8401
|
+
started: "Enregistrement d\xE9marr\xE9",
|
|
8402
|
+
stopped: "Enregistrement arr\xEAt\xE9",
|
|
8403
|
+
saved: "Enregistrement sauvegard\xE9",
|
|
8404
|
+
paused: "PAUSE",
|
|
8405
|
+
recording: "ENR.",
|
|
8406
|
+
resume: "Reprendre",
|
|
8407
|
+
pause: "Pause",
|
|
8408
|
+
stop: "Arr\xEAter et sauver",
|
|
8409
|
+
hover: "Contr\xF4les"
|
|
8410
|
+
}
|
|
8411
|
+
}
|
|
8412
|
+
};
|
|
8413
|
+
|
|
8414
|
+
// src/lib/common-utils.ts
|
|
8415
|
+
function deepMerge2(target, source) {
|
|
8416
|
+
const isObject = (obj) => obj && typeof obj === "object";
|
|
8417
|
+
if (!isObject(target) || !isObject(source)) {
|
|
8418
|
+
return source;
|
|
8419
|
+
}
|
|
8420
|
+
const output = { ...target };
|
|
8421
|
+
Object.keys(source).forEach((key) => {
|
|
8422
|
+
const targetValue = output[key];
|
|
8423
|
+
const sourceValue = source[key];
|
|
8424
|
+
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
|
|
8425
|
+
output[key] = sourceValue;
|
|
8426
|
+
} else if (isObject(targetValue) && isObject(sourceValue)) {
|
|
8427
|
+
output[key] = deepMerge2(targetValue, sourceValue);
|
|
8428
|
+
} else {
|
|
8429
|
+
output[key] = sourceValue;
|
|
8430
|
+
}
|
|
8431
|
+
});
|
|
8432
|
+
return output;
|
|
8433
|
+
}
|
|
8434
|
+
var GamePlayerInner = memo(function GamePlayerInner2(props) {
|
|
7533
8435
|
const { settings, updateSettings, isLoaded: settingsLoaded } = usePlayerPersistence();
|
|
7534
8436
|
useEffect(() => {
|
|
7535
8437
|
sendTelemetry("game_start", {
|
|
@@ -7540,6 +8442,7 @@ var GamePlayer = memo(function GamePlayer2(props) {
|
|
|
7540
8442
|
}, [props.system, props.core, props.title]);
|
|
7541
8443
|
const [biosModalOpen, setBiosModalOpen] = useState(false);
|
|
7542
8444
|
const [showShortcutsModal, setShowShortcutsModal] = useState(false);
|
|
8445
|
+
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
|
7543
8446
|
const effectiveShader = props.shader !== void 0 ? props.shader : settings.shader;
|
|
7544
8447
|
const {
|
|
7545
8448
|
// Refs
|
|
@@ -7674,6 +8577,10 @@ var GamePlayer = memo(function GamePlayer2(props) {
|
|
|
7674
8577
|
pause();
|
|
7675
8578
|
setGamepadModalOpen(true);
|
|
7676
8579
|
}, [pause, setGamepadModalOpen]);
|
|
8580
|
+
const handleShowSettings = useCallback(() => {
|
|
8581
|
+
pause();
|
|
8582
|
+
setSettingsModalOpen(true);
|
|
8583
|
+
}, [pause, setSettingsModalOpen]);
|
|
7677
8584
|
const handleExitClick = useCallback(() => {
|
|
7678
8585
|
onExit?.();
|
|
7679
8586
|
}, [onExit]);
|
|
@@ -7885,6 +8792,7 @@ var GamePlayer = memo(function GamePlayer2(props) {
|
|
|
7885
8792
|
systemColor,
|
|
7886
8793
|
gamepadCount: connectedCount,
|
|
7887
8794
|
onGamepadSettings: handleShowGamepadSettings,
|
|
8795
|
+
onSettings: handleShowSettings,
|
|
7888
8796
|
volume,
|
|
7889
8797
|
isMuted: muted,
|
|
7890
8798
|
onVolumeChange: handleVolumeChange,
|
|
@@ -7950,7 +8858,11 @@ var GamePlayer = memo(function GamePlayer2(props) {
|
|
|
7950
8858
|
setBiosModalOpen,
|
|
7951
8859
|
availableBios: props.availableBios,
|
|
7952
8860
|
currentBiosId: props.currentBiosId,
|
|
7953
|
-
onSelectBios: props.onSelectBios
|
|
8861
|
+
onSelectBios: props.onSelectBios,
|
|
8862
|
+
settingsModalOpen,
|
|
8863
|
+
setSettingsModalOpen,
|
|
8864
|
+
currentLanguage: props.currentLanguage,
|
|
8865
|
+
onLanguageChange: props.onLanguageChange
|
|
7954
8866
|
}
|
|
7955
8867
|
),
|
|
7956
8868
|
!isMobile && /* @__PURE__ */ jsx(
|
|
@@ -7978,6 +8890,27 @@ var GamePlayer = memo(function GamePlayer2(props) {
|
|
|
7978
8890
|
}
|
|
7979
8891
|
) });
|
|
7980
8892
|
});
|
|
8893
|
+
var GamePlayer = memo(function GamePlayer2(props) {
|
|
8894
|
+
const [currentLanguage, setCurrentLanguage] = useState(props.initialLanguage || "en");
|
|
8895
|
+
const effectiveTranslations = useMemo(() => {
|
|
8896
|
+
const base = currentLanguage === "es" ? es : currentLanguage === "fr" ? fr : en;
|
|
8897
|
+
if (props.translations) {
|
|
8898
|
+
return deepMerge2(base, props.translations);
|
|
8899
|
+
}
|
|
8900
|
+
return base;
|
|
8901
|
+
}, [currentLanguage, props.translations]);
|
|
8902
|
+
const handleLanguageChange = useCallback((lang) => {
|
|
8903
|
+
setCurrentLanguage(lang);
|
|
8904
|
+
}, []);
|
|
8905
|
+
return /* @__PURE__ */ jsx(KoinI18nProvider, { translations: effectiveTranslations, children: /* @__PURE__ */ jsx(
|
|
8906
|
+
GamePlayerInner,
|
|
8907
|
+
{
|
|
8908
|
+
...props,
|
|
8909
|
+
currentLanguage,
|
|
8910
|
+
onLanguageChange: handleLanguageChange
|
|
8911
|
+
}
|
|
8912
|
+
) });
|
|
8913
|
+
});
|
|
7981
8914
|
var GamePlayer_default = GamePlayer;
|
|
7982
8915
|
function AchievementPopup({
|
|
7983
8916
|
achievement,
|
|
@@ -8139,7 +9072,7 @@ var ShaderSelector = memo(function ShaderSelector2({
|
|
|
8139
9072
|
] });
|
|
8140
9073
|
});
|
|
8141
9074
|
var ShaderSelector_default = ShaderSelector;
|
|
8142
|
-
var
|
|
9075
|
+
var SHORTCUTS = [
|
|
8143
9076
|
{ key: "F1", description: "Help" },
|
|
8144
9077
|
{ key: "F3", description: "FPS Overlay" },
|
|
8145
9078
|
{ key: "F4", description: "Input Display" },
|
|
@@ -8174,7 +9107,7 @@ var ShortcutsReference = memo(function ShortcutsReference2({
|
|
|
8174
9107
|
]
|
|
8175
9108
|
}
|
|
8176
9109
|
),
|
|
8177
|
-
isExpanded && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 pt-1 space-y-1.5 border-t border-white/10", children:
|
|
9110
|
+
isExpanded && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 pt-1 space-y-1.5 border-t border-white/10", children: SHORTCUTS.map(({ key, description }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
8178
9111
|
/* @__PURE__ */ jsx("span", { className: "text-white/60", children: description }),
|
|
8179
9112
|
/* @__PURE__ */ jsx(
|
|
8180
9113
|
"kbd",
|
|
@@ -8195,6 +9128,6 @@ var ShortcutsReference = memo(function ShortcutsReference2({
|
|
|
8195
9128
|
});
|
|
8196
9129
|
var ShortcutsReference_default = ShortcutsReference;
|
|
8197
9130
|
|
|
8198
|
-
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, 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, fetchAndCacheRom, formatGamepadButton, formatKeyCode, 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, useNostalgist, useToast };
|
|
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 };
|
|
8199
9132
|
//# sourceMappingURL=index.mjs.map
|
|
8200
9133
|
//# sourceMappingURL=index.mjs.map
|