UI is functional now

This commit is contained in:
HannahPadd
2026-03-27 17:17:05 +01:00
parent 13e1265a51
commit cde42aa071
5 changed files with 457 additions and 8 deletions

View File

@@ -19,9 +19,6 @@ export const KeybindRecorder = forwardRef<
const [showError, setShowError] = useState<boolean>(false);
const [errorText, setErrorText] = useState<string>('');
const inputRef = useRef<HTMLInputElement>(null);
const { l10n } = useLocalization();
const displayKeys = isRecording ? localKeys : keys;
const activeIndex = isRecording ? displayKeys.length : -1;
@@ -98,9 +95,10 @@ export const KeybindRecorder = forwardRef<
const isActive = isRecording && i === activeIndex;
const isInvalid = invalidSlot === i;
return (
<div
key={i}
className={`
<div className="flex flex-row">
<div
key={i}
className={`
rounded-md min-w-[50px] min-h-[50px] text-lg flex items-center justify-center hover:ring-2 hover:ring-accent
${key ? 'bg-background-90' : 'bg-background-80'}
${
@@ -111,12 +109,17 @@ export const KeybindRecorder = forwardRef<
: 'ring-accent'
}
`}
>
{key ?? ''}
>
{key ?? ''}
</div>
<div className="flex justify-center items-center text-lg gap-2 pl-3">
{i < maxKeybindLength - 1 ? '+' : ''}
</div>
</div>
);
})}
</div>
{/*
<div className="w-40 flex-shrink-0 text-accent-background-10 text-right text-sm font-medium">
{displayKeys.length < maxKeybindLength && isRecording

View File

@@ -0,0 +1,72 @@
import { BaseModal } from './BaseModal';
import {
Controller,
Control,
UseFormResetField
} from 'react-hook-form';
import { KeybindRecorder } from './KeybindRecorder';
import { Typography } from './Typography';
import { Button } from './Button';
import { useLocalization } from '@fluent/react';
export function KeybindRecorderModal({
id,
control,
resetField,
name,
delay,
isVisisble,
onClose,
}: {
id?: string;
control: Control<any>;
resetField: UseFormResetField<any>;
name: string;
delay: string;
isVisisble: boolean;
onClose: () => void;
}) {
const { l10n } = useLocalization();
return (
<BaseModal
isOpen={isVisisble}
appendClasses="w-full max-w-xl h-full max-h-48"
>
<div className="flex-col gap-4 w-full">
<div className="flex flex-col w-full">
<div className="flex flex-col gap-3 w-full">
<Typography variant="section-title">
Create keybind for {l10n.getString(`settings-keybinds_${id}`)}
</Typography>
<Controller
control={control}
name={name}
render={({ field }) => (
<KeybindRecorder
keys={field.value ?? []}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
/>
<div className="flex flex-row justify-end gap-4">
<Button
id="settings-keybinds_reset-button"
variant="primary"
onClick={() => {
resetField(name);
resetField(delay);
}}
/>
<Button id="" variant="primary" onClick={onClose}>
Done
</Button>
</div>
</div>
</div>
</div>
</BaseModal>
);
}

View File

@@ -0,0 +1,45 @@
import { Typography } from './Typography';
import './KeybindRow.scss';
import { ReactNode } from 'react';
const createKeybindDisplay = (keybind: string[]): ReactNode | null => {
return keybind.map((key, i) => {
return (
<div className="flex flex-row">
<div
key={i}
className="flex rounded-xl min-w-[50px] min-h-[50px] text-lg justify-center items-center bg-background-70"
>
{key ?? ''}
</div>
<div className="flex justify-center items-center text-lg gap-2 pl-3">
{i < keybind.length - 1 ? '+' : ''}
</div>
</div>
);
});
};
export function NewKeybindsRow({
id,
keybind,
delay,
}: {
id?: string;
keybind?: string[];
delay?: number;
}) {
return (
<div className="keybind-row bg-background-60 rounded-xl hover:ring-2 hover:ring-accent">
<label className="text-sm font-medium text-background-10 p-2">
<Typography id={`settings-keybinds_${id}`} />
</label>
<div className="flex gap-2 min-h-[42px] items-center px-3 py-2 rounded-lg bg-background-80">
<div className="flex flex-grow gap-2 justify-center">
{keybind != null ? createKeybindDisplay(keybind) : ''}
</div>
</div>
<div>{delay}</div>
</div>
);
}

View File

@@ -0,0 +1,158 @@
import { KeybindRecorderModal } from '@/components/commons/KeybindRecorderModal';
import {
SettingsPageLayout,
SettingsPagePaneLayout,
} from '@/components/settings/SettingsPageLayout';
import { WrenchIcon } from '@/components/commons/icon/WrenchIcons';
import { Typography } from '@/components/commons/Typography';
import { useLocalization } from '@fluent/react';
import './KeybindSettings.scss';
import { Button } from '@/components/commons/Button';
import { NewKeybindsRow } from '@/components/commons/newKeybindsRow';
import { useWebsocketAPI } from '@/hooks/websocket-api';
import { ReactNode, useEffect, useState } from 'react';
import {
ChangeKeybindRequestT,
KeybindRequestT,
KeybindResponseT,
KeybindT,
RpcMessage,
} from 'solarxr-protocol';
import { useForm } from 'react-hook-form';
export type KeybindForm = {
id: number;
name: string;
binding: string[];
delay: number;
};
export function NewKeybindSettings() {
const { l10n } = useLocalization();
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const [isOpen, setIsOpen] = useState<boolean>(false);
const [keybinds, setKeybinds] = useState<KeybindT[]>();
const [selectedKeybind, setSelectedKeybind] = useState<KeybindT | null>(null);
const { control, resetField, reset, handleSubmit, watch } =
useForm<KeybindForm>({});
useEffect(() => {
const subscription = watch(() => handleSubmit(onSubmit)());
return () => subscription.unsubscribe();
}, []);
const onSubmit = (value: KeybindForm) => {
const changeKeybindRequest = new ChangeKeybindRequestT();
const keybind = new KeybindT();
keybind.keybindId = value.id;
keybind.keybindNameId = value.name;
keybind.keybindValue = value.binding.join('+');
keybind.keybindDelay = value.delay;
changeKeybindRequest.keybind = keybind;
console.log(`Onsubmit: ${keybind.keybindValue}`);
sendRPCPacket(RpcMessage.ChangeKeybindRequest, changeKeybindRequest);
};
useRPCPacket(RpcMessage.KeybindResponse, ({ keybind }: KeybindResponseT) => {
if (!keybind) return;
setKeybinds(keybind);
});
const handleOnClick = () => {
console.log('pressed');
};
const handleOpenRecorderModal = (index: number) => {
if (keybinds == null) return;
console.log('Handle open recorder modal');
const kb = keybinds[index];
setSelectedKeybind(kb);
setIsOpen(true);
reset({
id: kb.keybindId,
name: typeof kb.keybindNameId === 'string' ? kb.keybindNameId : '',
binding:
typeof kb.keybindValue === 'string' ? kb.keybindValue.split('+') : [],
delay: kb.keybindDelay,
});
};
const createKeybindRows = (): ReactNode => {
if (keybinds == null) return '';
return keybinds.map((key, i) => {
return (
<div className="keybind-row" onClick={() => handleOpenRecorderModal(i)}>
<NewKeybindsRow
key={i}
id={typeof key.keybindNameId === 'string' ? key.keybindNameId : ''}
keybind={
(typeof key.keybindValue === 'string'
? key.keybindValue
: ''
).split('+') || ''
}
delay={key.keybindDelay}
/>
</div>
);
});
};
useEffect(() => {
sendRPCPacket(RpcMessage.KeybindRequest, new KeybindRequestT());
}, [isOpen]);
return (
<SettingsPageLayout>
<SettingsPagePaneLayout icon={<WrenchIcon />} id="keybinds">
<Typography variant="main-title" id="settings-keybinds" />
<div className="flex flex-col pt-2 pb-4">
{l10n
.getString('settings-keybinds-description')
.split('\n')
.map((line, i) => (
<Typography key={i}>{line}</Typography>
))}
</div>
<div className="keybind-settings">
<Typography id="keybind_config-keybind_name" />
<Typography id="keybind_config-keybind_value" />
<Typography id="keybind_config-keybind_delay" />
{createKeybindRows()}
<Button
id="settings-keybinds_reset-all-button"
className="justify-self-start"
onClick={handleOnClick}
variant="primary"
/>
</div>
{selectedKeybind && (
<KeybindRecorderModal
id={
typeof selectedKeybind.keybindNameId === 'string'
? selectedKeybind.keybindNameId
: ''
}
control={control}
resetField={resetField}
name="binding"
delay={selectedKeybind.keybindDelay.toString()}
isVisisble={isOpen}
onClose={() => {
console.log('onclose');
setIsOpen(false);
setSelectedKeybind(null);
}}
/>
)}
</SettingsPagePaneLayout>
</SettingsPageLayout>
);
}

View File

@@ -0,0 +1,171 @@
---
server:
trackerPort: 6969
useMagnetometerOnAllTrackers: false
filters:
type: "prediction"
amount: 0.2
driftCompensation:
enabled: false
prediction: false
amount: 0.8
maxResets: 6
oscRouter:
enabled: false
portIn: 9002
portOut: 9000
address: "127.0.0.1"
vrcOSC:
enabled: false
portIn: 9001
portOut: 9000
address: "127.0.0.1"
trackers:
right_foot: true
left_foot: true
waist: true
oscqueryEnabled: true
vmc:
enabled: false
portIn: 39540
portOut: 39539
address: "127.0.0.1"
anchorHip: true
vrmJson: null
mirrorTracking: false
autoBone:
cursorIncrement: 2
minDataDistance: 1
maxDataDistance: 1
numEpochs: 50
printEveryNumEpochs: 25
initialAdjustRate: 10.0
adjustRateDecay: 1.0
slideErrorFactor: 1.0
offsetSlideErrorFactor: 0.0
footHeightOffsetErrorFactor: 0.0
bodyProportionErrorFactor: 0.05
heightErrorFactor: 0.0
positionErrorFactor: 0.0
positionOffsetErrorFactor: 0.0
calcInitError: false
randomizeFrameOrder: true
scaleEachStep: true
sampleCount: 1500
sampleRateMs: 20
saveRecordings: false
useSkeletonHeight: false
randSeed: 4
useFrameFiltering: false
maxFinalError: 0.03
keybindings:
keybinds:
"0":
id: 0
name: "full-reset"
binding: "CTRL+ALT+SHIFT+Y"
delay: 0.0
"1":
id: 1
name: "yaw-reset"
binding: "CTRL+ALT+SHIFT+U"
delay: 0.0
"2":
id: 2
name: "mounting-reset"
binding: "CTRL+ALT+SHIFT+I"
delay: 0.0
"4":
id: 4
name: "feet-mounting-reset"
binding: "CTRL+ALT+SHIFT+P"
delay: 0.0
"3":
id: 3
name: "pause-tracking"
binding: "CTRL+ALT+SHIFT+O"
delay: 0.0
"5":
id: 5
name: "test"
binding: "A+B+C+D"
delay: 2.0
skeleton:
toggles: {}
values: {}
offsets: {}
hmdHeight: 0.0
floorHeight: 0.0
legTweaks:
correctionStrength: 0.3
alwaysUseFloorclip: false
tapDetection:
yawResetDelay: 0.2
fullResetDelay: 1.0
mountingResetDelay: 1.0
yawResetEnabled: true
fullResetEnabled: true
mountingResetEnabled: true
setupMode: false
yawResetTaps: 2
fullResetTaps: 3
mountingResetTaps: 3
numberTrackersOverThreshold: 1
resetsConfig:
resetMountingFeet: false
mode: "BACK"
yawResetSmoothTime: 0.0
saveMountingReset: false
resetHmdPitch: false
lastMountingMethod: "AUTOMATIC"
yawResetDelay: 0.0
fullResetDelay: 3.0
mountingResetDelay: 3.0
stayAlignedConfig:
enabled: false
standingRelaxedPose:
enabled: false
upperLegAngleInDeg: 0.0
lowerLegAngleInDeg: 0.0
footAngleInDeg: 0.0
sittingRelaxedPose:
enabled: false
upperLegAngleInDeg: 0.0
lowerLegAngleInDeg: 0.0
footAngleInDeg: 0.0
flatRelaxedPose:
enabled: false
upperLegAngleInDeg: 0.0
lowerLegAngleInDeg: 0.0
footAngleInDeg: 0.0
setupComplete: false
hidConfig:
trackersOverHID: false
trackers: {}
bridges:
steamvr:
trackers:
right_knee: false
chest: false
right_foot: false
right_hand: false
left_foot: false
left_hand: false
waist: false
right_elbow: false
left_knee: false
hmd: false
left_elbow: false
automaticSharedTrackersToggling: true
steamvr_feeder:
trackers: {}
automaticSharedTrackersToggling: true
knownDevices: []
overlay:
mirrored: false
visible: false
trackingChecklist:
ignoredStepsIds: []
vrcConfig:
mutedWarnings: []
modelVersion: "15"