Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Persisted cross-game race results with qualifying, podium, fastest-lap, pit, strategy, and position-timeline summaries, plus idempotent historical backfill
- Configure driver-profile AI output tokens with provider-advertised limits
- Use simulator-independent semantic telemetry for live dashboards while keeping native packet inspection in the development panel and recording bytes unchanged
- Toggle ACC and AC Evo reference racing lines alongside other Analyse overlays in both 2D and 3D views

- Detect imported file contents before accepting ZIP/BIN session data and reject unrelated archives
### Fixes
Expand Down
12 changes: 6 additions & 6 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion client/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -696,10 +696,11 @@
"track_detail_no_laps_match_filters": "Keine Runden entsprechen den gewählten Filtern",
"overlay_follow": "Folgen",
"overlay_fixed": "Fest",
"overlay_overlay": "Overlay",
"overlay_overlay": "Overlays",
"overlay_inputs": "Eingaben",
"overlay_segments": "Segmente",
"overlay_sectors": "Sektoren",
"overlay_racing_line": "Ideallinie",
"track_detail_class": "Klasse",
"track_detail_lap_num": "Runde #",
"track_detail_all_cars": "Alle Fahrzeuge",
Expand Down Expand Up @@ -904,6 +905,7 @@
"carwire_hidden": "Versteckt",
"carwire_solid": "Solide",
"carwire_wire": "Gitter",
"carwire_view": "Ansicht",
"carwire_springs": "Federn",
"carwire_inputs": "Eingaben",
"carwire_grid": "Raster",
Expand Down
4 changes: 3 additions & 1 deletion client/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -696,10 +696,11 @@
"track_detail_no_laps_match_filters": "No laps match the selected filters",
"overlay_follow": "Follow",
"overlay_fixed": "Fixed",
"overlay_overlay": "Overlay",
"overlay_overlay": "Overlays",
"overlay_inputs": "Inputs",
"overlay_segments": "Segments",
"overlay_sectors": "Sectors",
"overlay_racing_line": "Racing line",
"track_detail_class": "Class",
"track_detail_lap_num": "Lap #",
"track_detail_all_cars": "All cars",
Expand Down Expand Up @@ -904,6 +905,7 @@
"carwire_hidden": "Hidden",
"carwire_solid": "Solid",
"carwire_wire": "Wire",
"carwire_view": "View",
"carwire_springs": "Springs",
"carwire_inputs": "Inputs",
"carwire_grid": "Grid",
Expand Down
56 changes: 41 additions & 15 deletions client/src/components/CarWireframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { useGLTF } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import { tryGetGame } from "@shared/games/registry";
import { flipBoundaries, needsTrackFlip } from "@shared/racing/tracks/coords";
import { ChevronDownIcon } from "lucide-react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { m } from "@/paraglide/messages";
import type { GameId } from "../../../shared/games/ids";
import type { TrackMapBoundaries } from "./analyse/track-map/types";
import { type CarModelEnrichment, DEMO_CAR, F1_CAR, getCarModel, loadCarModelConfigs } from "../data/car-models";
import { useSettings } from "../hooks/settings";
import { useLocalStorage } from "../hooks/useLocalStorage";
Expand All @@ -16,6 +18,7 @@ import { DEFAULT_TOGGLES, VIEW_PRESETS, type ViewPreset, type ViewToggles } from
import { useGameId } from "../stores/game";
import type { SemanticAnalysisFrame } from "./analyse/track-map/types";
import { Button } from "./ui/button";
import { DropdownMenu } from "./ui/DropdownMenu";
import { CarScene } from "./wireframe/CarScene";
import { ToggleButton } from "./wireframe/ToggleButton";

Expand All @@ -42,7 +45,7 @@ export const CarWireframe = React.memo(function CarWireframe({
telemetry: SemanticAnalysisFrame[];
cursorIdx: number;
outline: { x: number; z: number }[] | null;
boundaries?: { leftEdge: { x: number; z: number }[]; rightEdge: { x: number; z: number }[] } | null;
boundaries?: Pick<TrackMapBoundaries, "leftEdge" | "rightEdge" | "raceLine"> | null;
carOrdinal?: number;
carModel?: CarModelEnrichment & { hasModel: boolean };
tempLabel?: string;
Expand Down Expand Up @@ -107,8 +110,25 @@ export const CarWireframe = React.memo(function CarWireframe({
if (!boundaries) return null;
return needsTrackFlip(gameId) ? flipBoundaries(boundaries) : boundaries;
}, [boundaries, gameId]);

const toggle = (key: keyof ViewToggles) => setToggles((prev) => ({ ...prev, [key]: !prev[key] }));
const viewToggleItems = [
{ key: "springs" as const, label: m.carwire_springs(), available: true },
{ key: "trails" as const, label: m.carwire_trails(), available: true },
{ key: "inputs" as const, label: m.carwire_inputs(), available: true },
{ key: "track" as const, label: m.carwire_track(), available: true },
{ key: "racingLine" as const, label: m.overlay_racing_line(), available: Array.isArray(flippedBoundaries?.raceLine) && flippedBoundaries.raceLine.length > 1 },
{ key: "grid" as const, label: m.carwire_grid(), available: true },
{ key: "drivetrain" as const, label: m.carwire_drive(), available: true },
{ key: "wheelInfo" as const, label: m.carwire_tire_info(), available: true },
]
.filter((item) => item.available)
.map((item) => ({
type: "checkbox" as const,
key: item.key,
label: item.label,
checked: toggles[item.key],
onCheckedChange: (checked: boolean) => setToggles((previous) => ({ ...previous, [item.key]: checked })),
}));
const anyViewToggle = viewToggleItems.some((item) => item.checked);

const fpsRef = useRef<HTMLSpanElement>(null);
const fpsFrames = useRef(0);
Expand Down Expand Up @@ -237,7 +257,7 @@ export const CarWireframe = React.memo(function CarWireframe({

{/* View toggles */}
{!hideControls && (
<div className="absolute top-2 left-2 flex flex-wrap gap-1 max-w-[65%]">
<div className="absolute top-2 left-2 flex max-w-[65%] flex-wrap gap-1">
<ToggleButton
label={toggles.solid === "solid" ? m.carwire_solid() : toggles.solid === "hidden" ? m.carwire_hidden() : m.carwire_wire()}
active={toggles.solid !== "wire"}
Expand All @@ -248,17 +268,23 @@ export const CarWireframe = React.memo(function CarWireframe({
}))
}
/>
{!minimal && <ToggleButton label={m.carwire_springs()} active={toggles.springs} onClick={() => toggle("springs")} />}
{!minimal && <ToggleButton label={m.carwire_trails()} active={toggles.trails} onClick={() => toggle("trails")} />}
{!minimal && <ToggleButton label={m.carwire_inputs()} active={toggles.inputs} onClick={() => toggle("inputs")} />}
{!minimal && <ToggleButton label={m.carwire_track()} active={toggles.track} onClick={() => toggle("track")} />}
{!minimal && <ToggleButton label={m.carwire_grid()} active={toggles.grid} onClick={() => toggle("grid")} />}
{!minimal && <ToggleButton label={m.carwire_drive()} active={toggles.drivetrain} onClick={() => toggle("drivetrain")} />}
{!minimal && <ToggleButton label={m.carwire_tire_info()} active={toggles.wheelInfo} onClick={() => toggle("wheelInfo")} />}
{/* Camber toggle intentionally not rendered: ACC is the only game
with camber in telemetry and Kunos currently ships camberRAD[4]
as a zeroed stub. Re-enable when the game writes real values. */}
{minimal && <ToggleButton label={m.carwire_dims()} active={toggles.dimensions} onClick={() => toggle("dimensions")} />}
{!minimal && (
<DropdownMenu
align="left"
trigger={
<Button
className={`px-2 py-1 text-app-micro uppercase tracking-wider font-semibold rounded border transition-colors ${
anyViewToggle ? "bg-app-accent/15 border-app-accent/40 text-app-accent" : "bg-app-surface-alt/80 border-app-border-input text-app-text-muted hover:text-app-text"
}`}
>
{m.carwire_view()}
<ChevronDownIcon data-icon="inline-end" />
</Button>
}
items={viewToggleItems}
/>
)}
{minimal && <ToggleButton label={m.carwire_dims()} active={toggles.dimensions} onClick={() => setToggles((previous) => ({ ...previous, dimensions: !previous.dimensions }))} />}
</div>
)}

Expand Down
Loading
Loading