Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8c7aabd
Started work on implementing keybind recorders in frontend
Jan 14, 2026
5e92f87
Remove unecessary code
Jan 14, 2026
121aa28
Working on rpc packet for keybind settings
HannahPadd Jan 15, 2026
523134b
Working on rpc packet for keybind settings
HannahPadd Jan 15, 2026
7670350
Updated Keybindsconfig
Jan 15, 2026
007bb25
Working on keybindhandler
HannahPadd Jan 16, 2026
ef2dde0
Keybind Request and Response are working
HannahPadd Jan 16, 2026
420eef2
Started updating frontend to receive keybind info from backend
HannahPadd Jan 16, 2026
807ecb6
beep boop need changes on laptop
HannahPadd Jan 19, 2026
4368b62
Update keybind component
HannahPadd Jan 20, 2026
75fa324
Keybinds now save on server
HannahPadd Jan 21, 2026
38a556e
Working on bug in server
HannahPadd Jan 21, 2026
407c060
Save and loading from config should work now
HannahPadd Jan 21, 2026
dbb529a
Styling update
HannahPadd Jan 21, 2026
2836f66
Refactor keybindinput to keybindrow and move the input to it's own Ke…
HannahPadd Jan 22, 2026
659afee
Added resetfield to keybindrow
HannahPadd Jan 22, 2026
f35a316
Added Reset Icon
HannahPadd Jan 22, 2026
1efce29
Added Reset Icon
HannahPadd Jan 22, 2026
210f931
Styling update and fixed recording text updating
HannahPadd Jan 22, 2026
37f58fb
Styling update
HannahPadd Jan 22, 2026
4cb335c
Remove clear button
HannahPadd Jan 23, 2026
8fccbec
Merge remote-tracking branch 'origin/main' into keybinds
Jan 26, 2026
5d628b2
Added feet
Jan 26, 2026
d059c1d
Added english text
Jan 26, 2026
be424ed
Update keybindSettings
Jan 27, 2026
9257874
update solarxr submodule
Jan 27, 2026
44fa1ad
lint
Jan 27, 2026
bd4dd11
Merge branch 'SlimeVR:main' into keybinds
HannahPadd Feb 2, 2026
5c81076
Added platform check
Feb 2, 2026
a787fc7
Merge branch 'keybinds' of https://github.com/HannahPadd/SlimeVR-Serv…
Feb 2, 2026
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
58 changes: 58 additions & 0 deletions SlimeVR-Server.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solarxr-protocol", "solarxr-protocol", "{D2BA29CC-4E32-9914-C788-2487335F485F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{14112BA2-6B61-185E-FA3B-BBA04EC69D71}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "flatbuffers", "flatbuffers", "{8338A523-5303-4D5D-E1DD-291792CC5800}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net", "net", "{3696A2D9-9D4F-20AD-9FBF-1E3A042EF026}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FlatBuffers", "FlatBuffers", "{383CA28D-60BC-1E09-8533-A4EC3A4E9D27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.FlatBuffers", "solarxr-protocol\lib\flatbuffers\net\FlatBuffers\Google.FlatBuffers.csproj", "{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DB17BF73-048E-8E2E-34E2-1366CF5405FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatBuffers.Benchmarks", "solarxr-protocol\lib\flatbuffers\tests\FlatBuffers.Benchmarks\FlatBuffers.Benchmarks.csproj", "{D5FED821-E13D-072F-201B-A18CCAB7AC54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatBuffers.Test", "solarxr-protocol\lib\flatbuffers\tests\FlatBuffers.Test\FlatBuffers.Test.csproj", "{ECE36BCD-852C-448A-8EFC-5928050F9D1A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D}.Release|Any CPU.Build.0 = Release|Any CPU
{D5FED821-E13D-072F-201B-A18CCAB7AC54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5FED821-E13D-072F-201B-A18CCAB7AC54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5FED821-E13D-072F-201B-A18CCAB7AC54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5FED821-E13D-072F-201B-A18CCAB7AC54}.Release|Any CPU.Build.0 = Release|Any CPU
{ECE36BCD-852C-448A-8EFC-5928050F9D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECE36BCD-852C-448A-8EFC-5928050F9D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECE36BCD-852C-448A-8EFC-5928050F9D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECE36BCD-852C-448A-8EFC-5928050F9D1A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{14112BA2-6B61-185E-FA3B-BBA04EC69D71} = {D2BA29CC-4E32-9914-C788-2487335F485F}
{8338A523-5303-4D5D-E1DD-291792CC5800} = {14112BA2-6B61-185E-FA3B-BBA04EC69D71}
{3696A2D9-9D4F-20AD-9FBF-1E3A042EF026} = {8338A523-5303-4D5D-E1DD-291792CC5800}
{383CA28D-60BC-1E09-8533-A4EC3A4E9D27} = {3696A2D9-9D4F-20AD-9FBF-1E3A042EF026}
{7DCCF1EE-FF55-7593-D206-A2BB4FEFCC2D} = {383CA28D-60BC-1E09-8533-A4EC3A4E9D27}
{DB17BF73-048E-8E2E-34E2-1366CF5405FE} = {8338A523-5303-4D5D-E1DD-291792CC5800}
{D5FED821-E13D-072F-201B-A18CCAB7AC54} = {DB17BF73-048E-8E2E-34E2-1366CF5405FE}
{ECE36BCD-852C-448A-8EFC-5928050F9D1A} = {DB17BF73-048E-8E2E-34E2-1366CF5405FE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B95917B1-9E9F-44B1-AC9C-8FF2D103B85B}
EndGlobalSection
EndGlobal
3 changes: 3 additions & 0 deletions gui/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,9 @@ settings-stay_aligned-debug-label = Debugging
settings-stay_aligned-debug-description = Please include your settings when reporting problems about Stay Aligned.
settings-stay_aligned-debug-copy-label = Copy settings to clipboard

settings-keybinds = Keybind settings
settings-keybinds-description = Change keybinds for various shortcuts
settings-sidebar-keybinds = Keybinds
## FK/Tracking settings
settings-general-fk_settings = Tracking settings

Expand Down
2 changes: 2 additions & 0 deletions gui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { StayAlignedSetup } from './components/onboarding/pages/stay-aligned/Sta
import { TrackingChecklistProvider } from './components/tracking-checklist/TrackingChecklistProvider';
import { HomeScreenSettings } from './components/settings/pages/HomeScreenSettings';
import { ChecklistPage } from './components/tracking-checklist/TrackingChecklist';
import { KeybindSettings } from './components/settings/pages/KeybindSettings';

export const GH_REPO = 'SlimeVR/SlimeVR-Server';
export const VersionContext = createContext('');
Expand Down Expand Up @@ -142,6 +143,7 @@ function Layout() {
<Route path="interface" element={<InterfaceSettings />} />
<Route path="interface/home" element={<HomeScreenSettings />} />
<Route path="advanced" element={<AdvancedSettings />} />
<Route path="keybinds" element={<KeybindSettings />} />
</Route>
<Route
path="/onboarding"
Expand Down
53 changes: 53 additions & 0 deletions gui/src/components/commons/KeybindRecorder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useState, forwardRef } from 'react';

export const KeybindRecorder = forwardRef<
HTMLInputElement,
{
keys: string[];
onKeysChange: (v: string[]) => void;
}
>(function KeybindRecorder({ keys, onKeysChange }, ref) {
const [isRecording, setIsRecording] = useState(false);
const [oldKeys, setOldKeys] = useState<string[]>([]);

return (
<div className="relative w-full">
<input
ref={ref}
className="opacity-0 absolute inset-0 cursor-pointer"
onFocus={() => {
setOldKeys(keys);
onKeysChange([]);
setIsRecording(true);
}}
onBlur={() => {
setIsRecording(false);
if (keys.length < 4) onKeysChange(oldKeys);
}}
onKeyDown={(e) => {
const key = e.key.toUpperCase();
if (!keys.includes(key) && keys.length < 4) {
onKeysChange([...keys, key]);
}
}}
/>

<div className="flex gap-2 min-h-[42px] items-center px-3 rounded-lg bg-background-80">
<div className="flex flex-grow gap-2 flex-wrap">
{keys.map((key, i) => (
<div
key={i}
className="bg-accent-background-50 px-3 py-1 rounded-md text-sm"
>
{key}
</div>
))}
</div>

<div className="text-accent-background-10 text-sm font-medium">
{keys.length < 4 && isRecording ? 'Recording…' : 'Click to record'}
</div>
</div>
</div>
);
});
73 changes: 73 additions & 0 deletions gui/src/components/commons/KeybindRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Controller, Control, UseFormResetField } from 'react-hook-form';
import { Button } from './Button';
import { NumberSelector } from './NumberSelector';
import { KeybindRecorder } from './KeybindRecorder';
import { useLocaleConfig } from '@/i18n/config';

export function KeybindRow({
label,
control,
resetField,
bindingName,
delayName,
}: {
label: string;
control: Control<any>;
resetField: UseFormResetField<any>;
bindingName: string;
delayName: string;
}) {
const { currentLocales } = useLocaleConfig();
const secondsFormat = new Intl.NumberFormat(currentLocales, {
style: 'unit',
unit: 'second',
unitDisplay: 'narrow',
maximumFractionDigits: 2,
});
return (
<tr className="border-b border-background-60 h-20">
<td className="px-6 py-4 pr-4">
<label className="text-sm font-medium text-background-10">
{label}
</label>
</td>
<td className="px-4">
<Controller
control={control}
name={bindingName}
render={({ field }) => (
<KeybindRecorder
keys={field.value ?? []}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
/>
</td>
<td className="px-4">
<NumberSelector
control={control}
name={delayName}
valueLabelFormat={(value) => secondsFormat.format(value)}
min={0}
max={10}
step={0.2}
/>
</td>

<td className="px-2">
<div className="flex gap-2 justify-center px-4">
<Button
variant="primary"
onClick={() => {
resetField(bindingName);
resetField(delayName);
}}
>
Reset
</Button>
</div>
</td>
</tr>
);
}
13 changes: 13 additions & 0 deletions gui/src/components/commons/icon/ResetSettingIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export function ResetSettingIcon({ size = 24 }: { size?: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height={size}
viewBox="0 -960 960 960"
width={size}
fill="#00000"
>
<path d="M520-330v-60h160v60H520Zm60 210v-50h-60v-60h60v-50h60v160h-60Zm100-50v-60h160v60H680Zm40-110v-160h60v50h60v60h-60v50h-60Zm111-280h-83q-26-88-99-144t-169-56q-117 0-198.5 81.5T200-480q0 72 32.5 132t87.5 98v-110h80v240H160v-80h94q-62-50-98-122.5T120-480q0-75 28.5-140.5t77-114q48.5-48.5 114-77T480-840q129 0 226.5 79.5T831-560Z" />
</svg>
);
}
10 changes: 10 additions & 0 deletions gui/src/components/settings/SettingsSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMemo } from 'react';
import { NavLink, useLocation, useMatch } from 'react-router-dom';
import { Typography } from '@/components/commons/Typography';
import { useVRCConfig } from '@/hooks/vrc-config';
import { platform } from '@tauri-apps/plugin-os';

export function SettingsLink({
to,
Expand Down Expand Up @@ -41,6 +42,7 @@ export function SettingsLink({

export function SettingsSidebar() {
const { state: vrcConfigState } = useVRCConfig();
const currentPlatform = platform();

return (
<div className="flex flex-col px-5 py-5 gap-3 overflow-y-auto bg-background-70 rounded-lg h-full">
Expand Down Expand Up @@ -73,6 +75,14 @@ export function SettingsSidebar() {
scrollTo="gestureControl"
id="settings-sidebar-gesture_control"
/>
{
currentPlatform == "windows" ?
<SettingsLink
to="/settings/keybinds"
scrollTo="keybinds"
id="settings-sidebar-keybinds"
/> : <></>
}
</div>
</div>
<div className="flex flex-col gap-3">
Expand Down
Loading