Skip to content

Commit c6a1af5

Browse files
authored
Adds the ability to scale the fontsize globally (#21)
1 parent 04be7ad commit c6a1af5

File tree

15 files changed

+455
-41
lines changed

15 files changed

+455
-41
lines changed

src/app/overlayManager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ export class OverlayManager {
155155
const browserWindow = new BrowserWindow({
156156
title: `iRacing Dashies - Settings`,
157157
frame: true,
158+
width: 800,
159+
height: 700,
158160
autoHideMenuBar: true,
159161
webPreferences: {
160162
preload: path.join(__dirname, 'preload.js'),

src/app/storage/dashboards.spec.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
updateDashboardWidget,
88
} from './dashboards';
99
import { defaultDashboard } from './defaultDashboard';
10+
import { DashboardLayout } from '@irdashies/types';
1011

1112
const mockReadData = vi.hoisted(() => vi.fn());
1213
const mockWriteData = vi.hoisted(() => vi.fn());
@@ -84,7 +85,7 @@ describe('dashboards', () => {
8485

8586
describe('saveDashboard', () => {
8687
it('should save a new dashboard', () => {
87-
const newDashboard = { widgets: [] };
88+
const newDashboard: DashboardLayout = { widgets: [], generalSettings: { fontSize: 'sm' }};
8889
mockReadData.mockReturnValue(null);
8990

9091
saveDashboard('newDashboard', newDashboard);
@@ -96,7 +97,7 @@ describe('dashboards', () => {
9697

9798
it('should update an existing dashboard', () => {
9899
const existingDashboards = { default: defaultDashboard };
99-
const updatedDashboard = { widgets: [] };
100+
const updatedDashboard: DashboardLayout = { widgets: [], generalSettings: { fontSize: 'lg' }};
100101
mockReadData.mockReturnValue(existingDashboards);
101102

102103
saveDashboard('default', updatedDashboard);
@@ -133,15 +134,15 @@ describe('dashboards', () => {
133134
enabled: false,
134135
layout: { x: 100, y: 100, width: 600, height: 120 },
135136
};
136-
const existingDashboard = { widgets: [existingWidget] };
137+
const existingDashboard: DashboardLayout = { widgets: [existingWidget], generalSettings: { fontSize: 'sm' } };
137138
mockReadData.mockReturnValue({
138139
default: existingDashboard,
139140
});
140141

141142
updateDashboardWidget(updatedWidget);
142143

143144
expect(mockWriteData).toHaveBeenCalledWith('dashboards', {
144-
default: { widgets: [updatedWidget] },
145+
default: { widgets: [updatedWidget], generalSettings: { fontSize: 'sm' } },
145146
});
146147
});
147148

@@ -156,15 +157,15 @@ describe('dashboards', () => {
156157
enabled: true,
157158
layout: { x: 100, y: 100, width: 600, height: 120 },
158159
};
159-
const existingDashboard = { widgets: [existingWidget] };
160+
const existingDashboard: DashboardLayout = { widgets: [existingWidget], generalSettings: { fontSize: 'sm' } };
160161
mockReadData.mockReturnValue({
161162
custom: existingDashboard,
162163
});
163164

164165
updateDashboardWidget(updatedWidget, 'custom');
165166

166167
expect(mockWriteData).toHaveBeenCalledWith('dashboards', {
167-
custom: { widgets: [updatedWidget] },
168+
custom: { widgets: [updatedWidget], generalSettings: { fontSize: 'sm' } },
168169
});
169170
});
170171

@@ -174,7 +175,7 @@ describe('dashboards', () => {
174175
enabled: true,
175176
layout: { x: 100, y: 100, width: 600, height: 120 },
176177
};
177-
const existingDashboard = { widgets: [] };
178+
const existingDashboard: DashboardLayout = { widgets: [], generalSettings: { fontSize: 'sm' } };
178179
mockReadData.mockReturnValue({
179180
default: existingDashboard,
180181
});
@@ -209,6 +210,7 @@ describe('dashboards', () => {
209210

210211
it('should add missing widgets to the default dashboard if some widgets are missing', () => {
211212
const incompleteDashboard = {
213+
generalSettings: { fontSize: 'sm' },
212214
widgets: defaultDashboard.widgets.slice(0, 1),
213215
};
214216
mockReadData.mockReturnValue({
@@ -235,4 +237,28 @@ describe('dashboards', () => {
235237
expect(mockWriteData).not.toHaveBeenCalled();
236238
});
237239
});
240+
241+
describe('generalSettings', () => {
242+
it('should add general settings from the default dashboard if none exist', () => {
243+
const dashboard: DashboardLayout = { widgets: [] };
244+
mockReadData.mockReturnValue({
245+
default: dashboard,
246+
});
247+
248+
const updatedDashboard = getOrCreateDefaultDashboard();
249+
250+
expect(updatedDashboard.generalSettings).toEqual({ fontSize: 'sm' });
251+
});
252+
253+
it('should preserve general settings from the existing dashboard', () => {
254+
const dashboard: DashboardLayout = { widgets: [], generalSettings: { fontSize: 'lg' } };
255+
mockReadData.mockReturnValue({
256+
default: dashboard,
257+
});
258+
259+
const updatedDashboard = getOrCreateDefaultDashboard();
260+
261+
expect(updatedDashboard.generalSettings).toEqual({ fontSize: 'lg' });
262+
});
263+
});
238264
});

src/app/storage/dashboards.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ const DASHBOARDS_KEY = 'dashboards';
77

88
const isDashboardChanged = (oldDashboard: DashboardLayout | undefined, newDashboard: DashboardLayout): boolean => {
99
if (!oldDashboard) return true;
10-
10+
11+
// Compare generalSettings
12+
if (JSON.stringify(oldDashboard.generalSettings) !== JSON.stringify(newDashboard.generalSettings)) {
13+
return true;
14+
}
15+
1116
// Compare widgets length
1217
if (oldDashboard.widgets.length !== newDashboard.widgets.length) return true;
13-
18+
1419
// Compare each widget
1520
return oldDashboard.widgets.some((oldWidget, index) => {
1621
const newWidget = newDashboard.widgets[index];
@@ -29,10 +34,16 @@ export const getOrCreateDefaultDashboard = () => {
2934

3035
// add missing widgets and save
3136
const updatedDashboard = {
37+
...dashboard,
38+
generalSettings: {
39+
...defaultDashboard.generalSettings,
40+
...dashboard.generalSettings,
41+
},
3242
widgets: [...dashboard.widgets, ...missingWidgets].map((widget) => {
3343
// add missing default widget config
34-
if (!widget.config) {
35-
return { ...widget, config: defaultDashboard.widgets.find((w) => w.id === widget.id)?.config };
44+
const defaultWidget = defaultDashboard.widgets.find((w) => w.id === widget.id);
45+
if (!widget.config && defaultWidget?.config) {
46+
return { ...widget, config: defaultWidget.config };
3647
}
3748
return widget;
3849
}),
@@ -85,11 +96,22 @@ export const saveDashboard = (
8596
) => {
8697
const dashboards = listDashboards();
8798
const existingDashboard = dashboards[id];
88-
99+
100+
// Merge the existing dashboard with the new value to preserve structure
101+
const mergedDashboard: DashboardLayout = {
102+
...existingDashboard,
103+
...value,
104+
widgets: value.widgets || existingDashboard?.widgets || [],
105+
generalSettings: {
106+
...existingDashboard?.generalSettings,
107+
...value.generalSettings,
108+
}
109+
};
110+
89111
// Only save and emit if there are actual changes
90-
if (isDashboardChanged(existingDashboard, value)) {
91-
dashboards[id] = value;
112+
if (isDashboardChanged(existingDashboard, mergedDashboard)) {
113+
dashboards[id] = mergedDashboard;
92114
writeData(DASHBOARDS_KEY, dashboards);
93-
emitDashboardUpdated(value);
115+
emitDashboardUpdated(mergedDashboard);
94116
}
95117
};

src/app/storage/defaultDashboard.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,7 @@ export const defaultDashboard: DashboardLayout = {
8080
},
8181
}
8282
],
83+
generalSettings: {
84+
fontSize: 'sm',
85+
},
8386
};

src/frontend/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Weather } from './components/Weather';
1616
import { TrackMap } from './components/TrackMap/TrackMap';
1717
import { FasterCarsFromBehind } from './components/FasterCarsFromBehind/FasterCarsFromBehind';
1818
import { EditMode } from './components/EditMode/EditMode';
19+
import { ThemeManager } from './components/ThemeManager/ThemeManager';
1920

2021
// TODO: type this better, right now the config comes from settings
2122
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -32,6 +33,7 @@ const WIDGET_MAP: Record<string, (config: any) => React.JSX.Element> = {
3233
const AppRoutes = () => {
3334
const { currentDashboard } = useDashboard();
3435
const { running } = useRunningState();
36+
3537
return (
3638
<Routes>
3739
{currentDashboard?.widgets.map((widget) => {
@@ -60,7 +62,9 @@ const App = () => (
6062
<TelemetryProvider bridge={window.irsdkBridge} />
6163
<HashRouter>
6264
<EditMode>
63-
<AppRoutes />
65+
<ThemeManager>
66+
<AppRoutes />
67+
</ThemeManager>
6468
</EditMode>
6569
</HashRouter>
6670
</RunningStateProvider>

src/frontend/components/Settings/SettingsLayout.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GearIcon, LockIcon, LockOpenIcon, PresentationChartIcon } from '@phosphor-icons/react';
2-
import { Link, Route, Routes, useLocation } from 'react-router-dom';
2+
import { Link, Route, Routes, useLocation, Navigate } from 'react-router-dom';
33
import { StandingsSettings } from './sections/StandingsSettings';
44
import { RelativeSettings } from './sections/RelativeSettings';
55
import { WeatherSettings } from './sections/WeatherSettings';
@@ -8,12 +8,13 @@ import { AdvancedSettings } from './sections/AdvancedSettings';
88
import { InputSettings } from './sections/InputSettings';
99
import { AboutSettings } from './sections/AboutSettings';
1010
import { FasterCarsFromBehindSettings } from './sections/FasterCarsFromBehindSettings';
11+
import { GeneralSettings } from './sections/GeneralSettings';
1112
import { useDashboard } from '@irdashies/context';
1213
import { useState } from 'react';
1314

1415
export const SettingsLayout = () => {
1516
const location = useLocation();
16-
const { bridge, editMode, isDemoMode, toggleDemoMode } = useDashboard();
17+
const { bridge, editMode, isDemoMode, toggleDemoMode, currentDashboard } = useDashboard();
1718
const [isLocked, setIsLocked] = useState(!editMode);
1819

1920
const isActive = (path: string) => {
@@ -30,6 +31,10 @@ export const SettingsLayout = () => {
3031
setIsLocked(locked);
3132
};
3233

34+
if (!currentDashboard) {
35+
return <>Loading...</>;
36+
}
37+
3338
return (
3439
<div className="flex flex-col gap-4 bg-slate-700 p-4 rounded-md w-full h-full">
3540
<div className="flex flex-row gap-4 items-center justify-between">
@@ -76,6 +81,11 @@ export const SettingsLayout = () => {
7681
{/* Left Column - Widget Menu */}
7782
<div className="w-1/3 bg-slate-800 p-4 rounded-md flex flex-col overflow-y-auto">
7883
<ul className="flex flex-col gap-2 flex-1">
84+
<li>
85+
<Link to="/settings/general" className={menuItemClass('/general')}>
86+
General
87+
</Link>
88+
</li>
7989
<li>
8090
<Link to="/settings/input" className={menuItemClass('/input')}>
8191
Input Traces
@@ -114,7 +124,7 @@ export const SettingsLayout = () => {
114124
</Link>
115125
</li>
116126
<li>
117-
<Link to="/settings/map" className={menuItemClass('/track-map')}>
127+
<Link to="/settings/map" className={menuItemClass('/map')}>
118128
<div className="flex flex-row gap-2 items-center">
119129
Track Map
120130
<span className="text-xs bg-yellow-600 text-yellow-100 px-2 py-0.5 rounded-full flex flex-row gap-1 items-center">
@@ -144,6 +154,8 @@ export const SettingsLayout = () => {
144154
{/* Right Column - Widget Settings */}
145155
<div className="w-2/3 bg-slate-800 p-4 rounded-md flex flex-col overflow-hidden">
146156
<Routes>
157+
<Route path="/" element={<Navigate to="/settings/general" replace />} />
158+
<Route path="general" element={<GeneralSettings />} />
147159
<Route path="standings" element={<StandingsSettings />} />
148160
<Route path="relative" element={<RelativeSettings />} />
149161
<Route path="weather" element={<WeatherSettings />} />
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { useState } from 'react';
2+
import { useDashboard } from '@irdashies/context';
3+
import { GeneralSettingsType } from '@irdashies/types';
4+
5+
const FONT_SIZE_PRESETS = {
6+
xs: 'Extra Small',
7+
sm: 'Small',
8+
lg: 'Large',
9+
xl: 'Extra Large',
10+
};
11+
12+
export const GeneralSettings = () => {
13+
const { currentDashboard, onDashboardUpdated } = useDashboard();
14+
const [settings, setSettings] = useState<GeneralSettingsType>({
15+
fontSize: currentDashboard?.generalSettings?.fontSize ?? 'sm',
16+
});
17+
18+
if (!currentDashboard || !onDashboardUpdated) {
19+
return <>Loading...</>;
20+
}
21+
22+
const updateDashboard = (newSettings: GeneralSettingsType) => {
23+
const updatedDashboard = {
24+
...currentDashboard,
25+
generalSettings: newSettings,
26+
};
27+
onDashboardUpdated(updatedDashboard);
28+
};
29+
30+
const handleFontSizeChange = (newSize: 'xs' | 'sm' | 'lg' | 'xl') => {
31+
const newSettings = { ...settings, fontSize: newSize };
32+
setSettings(newSettings);
33+
updateDashboard(newSettings);
34+
};
35+
36+
return (
37+
<div className="flex flex-col h-full space-y-6">
38+
<div>
39+
<h2 className="text-xl mb-4">General Settings</h2>
40+
<p className="text-slate-400 mb-4">Configure general application settings and preferences.</p>
41+
</div>
42+
43+
{/* Font Size Settings */}
44+
<div className="space-y-4">
45+
<div className="flex items-center justify-between">
46+
<h3 className="text-lg font-medium text-slate-200">Font Size</h3>
47+
<div className="flex items-center gap-2">
48+
<span className="text-sm text-slate-300">{FONT_SIZE_PRESETS[settings.fontSize ?? 'sm']}</span>
49+
</div>
50+
</div>
51+
52+
{/* Font Size Presets */}
53+
<div className="flex gap-2 mt-4">
54+
<button
55+
onClick={() => handleFontSizeChange('xs')}
56+
className={`px-3 py-1 rounded text-sm ${
57+
settings.fontSize === 'xs'
58+
? 'bg-blue-500 text-white'
59+
: 'bg-slate-700 text-slate-300 hover:bg-slate-600'
60+
}`}
61+
>
62+
{FONT_SIZE_PRESETS.xs}
63+
</button>
64+
<button
65+
onClick={() => handleFontSizeChange('sm')}
66+
className={`px-3 py-1 rounded text-sm ${
67+
settings.fontSize === 'sm'
68+
? 'bg-blue-500 text-white'
69+
: 'bg-slate-700 text-slate-300 hover:bg-slate-600'
70+
}`}
71+
>
72+
{FONT_SIZE_PRESETS.sm}
73+
</button>
74+
<button
75+
onClick={() => handleFontSizeChange('lg')}
76+
className={`px-3 py-1 rounded text-sm ${
77+
settings.fontSize === 'lg'
78+
? 'bg-blue-500 text-white'
79+
: 'bg-slate-700 text-slate-300 hover:bg-slate-600'
80+
}`}
81+
>
82+
{FONT_SIZE_PRESETS.lg}
83+
</button>
84+
<button
85+
onClick={() => handleFontSizeChange('xl')}
86+
className={`px-3 py-1 rounded text-sm ${
87+
settings.fontSize === 'xl'
88+
? 'bg-blue-500 text-white'
89+
: 'bg-slate-700 text-slate-300 hover:bg-slate-600'
90+
}`}
91+
>
92+
{FONT_SIZE_PRESETS.xl}
93+
</button>
94+
</div>
95+
</div>
96+
</div>
97+
);
98+
};

src/frontend/components/Standings/DriverRatingBadge/DriverRatingBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const DriverRatingBadge = ({
2525

2626
return (
2727
<div
28-
className={`text-center text-white w-16 border-solid rounded-md text-xs m-0 px-1 border-2 ${color}`}
28+
className={`text-center text-white text-nowrap border-solid rounded-md text-xs m-0 px-1 border-2 ${color}`}
2929
>
3030
{formattedLicense} {simplifiedRating}k
3131
</div>

0 commit comments

Comments
 (0)