polish(ui): refine pause settings menu

This commit is contained in:
Tom Boullay
2026-05-31 21:42:59 +02:00
parent f11ed67452
commit efcbf9e972
3 changed files with 106 additions and 55 deletions
+71 -34
View File
@@ -3,9 +3,11 @@ import type { ReactNode } from "react";
import {
Captions,
Gauge,
LogOut,
Hand,
Laptop,
Music2,
RotateCcw,
Server,
Volume2,
X,
} from "lucide-react";
@@ -14,25 +16,19 @@ import {
GRAPHICS_PRESETS,
type GraphicsPreset,
} from "@/data/world/graphicsConfig";
import { useDebugStore } from "@/hooks/debug/useDebugStore";
import { useGameStore } from "@/managers/stores/useGameStore";
import { useSettingsStore } from "@/managers/stores/useSettingsStore";
import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore";
import type { HandTrackingSource } from "@/types/handTracking/handTracking";
import type { SubtitleLanguage } from "@/types/settings/settings";
import { isDebugEnabled } from "@/utils/debug/isDebugEnabled";
import { clearSiteVisited } from "@/utils/cookies/siteVisitCookie";
import { Debug } from "@/utils/debug/Debug";
function formatPercent(value: number): string {
return `${Math.round(value * 100)}%`;
}
function clearCookies(): void {
document.cookie.split(";").forEach((cookie) => {
const cookieName = cookie.split("=")[0]?.trim();
if (!cookieName) return;
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
});
}
interface VolumeSliderProps {
id: string;
label: string;
@@ -74,6 +70,26 @@ function formatChunkDistance(distance: number): string {
return `${distance}m`;
}
const HAND_TRACKING_OPTIONS = [
{
description: "Calcul local",
icon: <Laptop size={14} aria-hidden="true" />,
label: "Sur cet ordi",
source: "browser",
},
{
description: "Soulage l'ordi",
icon: <Server size={14} aria-hidden="true" />,
label: "Mode assisté",
source: "backend",
},
] as const satisfies readonly {
description: string;
icon: ReactNode;
label: string;
source: HandTrackingSource;
}[];
interface GraphicsPresetButtonProps {
active: boolean;
preset: GraphicsPreset;
@@ -108,6 +124,9 @@ function GraphicsPresetButton({
export function GameSettingsMenu(): React.JSX.Element | null {
const resetGame = useGameStore((state) => state.resetGame);
const handTrackingSource = useDebugStore((debug) =>
debug.getHandTrackingSource(),
);
const graphicsPreset = useWorldSettingsStore(
(state) => state.graphics.preset,
);
@@ -148,18 +167,17 @@ export function GameSettingsMenu(): React.JSX.Element | null {
if (!isSettingsMenuOpen) return null;
const handleQuit = (): void => {
clearCookies();
const handleRestart = (): void => {
resetGame();
clearSiteVisited();
setSettingsMenuOpen(false);
window.location.assign("/");
};
const handleRestart = (): void => {
resetGame();
window.location.reload();
const handleHandTrackingSourceChange = (source: HandTrackingSource): void => {
Debug.getInstance().setHandTrackingSource(source);
};
const showDebugRestart = isDebugEnabled();
return (
<div className="game-settings-menu" role="dialog" aria-modal="true">
<div className="game-settings-menu__panel">
@@ -185,7 +203,7 @@ export function GameSettingsMenu(): React.JSX.Element | null {
>
<div className="game-settings-menu__section-title">
<Gauge size={16} aria-hidden="true" />
<h3 id="graphics-settings-heading">Performance</h3>
<h3 id="graphics-settings-heading">Graphisme</h3>
</div>
<div
className="game-settings-menu__choice-group game-settings-menu__choice-group--presets"
@@ -269,26 +287,45 @@ export function GameSettingsMenu(): React.JSX.Element | null {
))}
</div>
</section>
<section
className="game-settings-menu__section game-settings-menu__section--right"
aria-labelledby="hand-tracking-settings-heading"
>
<div className="game-settings-menu__section-title">
<Hand size={16} aria-hidden="true" />
<h3 id="hand-tracking-settings-heading">Détection des mains</h3>
</div>
<div
className="game-settings-menu__choice-group game-settings-menu__choice-group--hand-tracking"
aria-label="Mode de détection des mains"
>
{HAND_TRACKING_OPTIONS.map((option) => (
<button
key={option.source}
type="button"
className={
handTrackingSource === option.source ? "active" : undefined
}
onClick={() => handleHandTrackingSourceChange(option.source)}
aria-pressed={handTrackingSource === option.source}
>
{option.icon}
<span>{option.label}</span>
<small>{option.description}</small>
</button>
))}
</div>
</section>
</div>
{showDebugRestart ? (
<button
className="game-settings-menu__restart"
type="button"
onClick={handleRestart}
>
<RotateCcw size={14} aria-hidden="true" />
Recommencer
</button>
) : null}
<button
className="game-settings-menu__quit"
className="game-settings-menu__restart"
type="button"
onClick={handleQuit}
onClick={handleRestart}
>
<LogOut size={14} aria-hidden="true" />
Quitter
<RotateCcw size={14} aria-hidden="true" />
Recommencer
</button>
</div>
</div>
+30 -21
View File
@@ -1220,8 +1220,8 @@ canvas {
place-items: center;
padding: clamp(18px, 4vw, 42px);
background:
linear-gradient(180deg, rgba(4, 10, 18, 0.64), rgba(2, 6, 12, 0.86)),
rgba(0, 0, 0, 0.72);
linear-gradient(180deg, rgba(4, 10, 18, 0.72), rgba(2, 6, 12, 0.9)),
rgba(0, 0, 0, 0.82);
color: #ffffff;
pointer-events: auto;
backdrop-filter: blur(14px);
@@ -1236,8 +1236,8 @@ canvas {
border: 1px solid rgba(125, 211, 252, 0.38);
border-radius: 8px;
background:
linear-gradient(180deg, rgba(8, 20, 34, 0.94), rgba(3, 8, 14, 0.96)),
rgba(4, 8, 12, 0.96);
linear-gradient(180deg, rgba(8, 20, 34, 0.98), rgba(3, 8, 14, 0.99)),
rgba(4, 8, 12, 0.99);
box-shadow:
0 28px 90px rgba(0, 0, 0, 0.58),
inset 0 0 0 1px rgba(255, 255, 255, 0.05),
@@ -1327,13 +1327,17 @@ canvas {
padding: 14px;
border: 1px solid rgba(125, 211, 252, 0.2);
border-radius: 8px;
background: rgba(5, 14, 24, 0.64);
background: rgba(5, 14, 24, 0.78);
}
.game-settings-menu__section--wide {
grid-column: 1 / -1;
}
.game-settings-menu__section--right {
grid-column: 2;
}
.game-settings-menu__section-title {
display: flex;
align-items: center;
@@ -1408,8 +1412,7 @@ canvas {
}
.game-settings-menu__choice-group button,
.game-settings-menu__restart,
.game-settings-menu__quit {
.game-settings-menu__restart {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -1419,7 +1422,7 @@ canvas {
padding: 11px 12px;
border: 1px solid rgba(125, 211, 252, 0.2);
border-radius: 8px;
background: rgba(6, 18, 30, 0.72);
background: rgba(6, 18, 30, 0.84);
color: #edfaff;
cursor: pointer;
font-size: 0.88rem;
@@ -1451,12 +1454,21 @@ canvas {
line-height: 1.35;
}
.game-settings-menu__choice-group--hand-tracking button {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
justify-content: start;
text-align: left;
}
.game-settings-menu__choice-group--hand-tracking button small {
grid-column: 2;
}
.game-settings-menu__choice-group button:hover,
.game-settings-menu__choice-group button:focus-visible,
.game-settings-menu__restart:hover,
.game-settings-menu__restart:focus-visible,
.game-settings-menu__quit:hover,
.game-settings-menu__quit:focus-visible {
.game-settings-menu__restart:focus-visible {
border-color: rgba(255, 255, 255, 0.64);
background: rgba(125, 211, 252, 0.16);
color: #ffffff;
@@ -1464,8 +1476,7 @@ canvas {
}
.game-settings-menu__choice-group button:active,
.game-settings-menu__restart:active,
.game-settings-menu__quit:active {
.game-settings-menu__restart:active {
transform: translateY(1px);
}
@@ -1488,14 +1499,8 @@ canvas {
.game-settings-menu__restart {
margin-top: 12px;
border-color: rgba(96, 165, 250, 0.35);
color: #bfdbfe;
}
.game-settings-menu__quit {
margin-top: 8px;
border-color: rgba(248, 113, 113, 0.35);
color: #fecaca;
border-color: rgba(125, 211, 252, 0.35);
color: #dff7ff;
}
@media (max-width: 720px) {
@@ -1512,6 +1517,10 @@ canvas {
.game-settings-menu__choice-group--presets {
grid-template-columns: 1fr;
}
.game-settings-menu__section--right {
grid-column: auto;
}
}
/* Debug overlay panels */
+5
View File
@@ -244,6 +244,11 @@ export class Debug {
return this.controls.handTrackingSource;
}
setHandTrackingSource(value: HandTrackingSource): void {
this.controls.handTrackingSource = value;
this.emit();
}
getFogEnabled(): boolean {
return this.controls.fogEnabled;
}