diff --git a/gui/package.json b/gui/package.json
index 1657a509c..58a1e4d6e 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -17,6 +17,7 @@
},
"scripts": {
"start": "vite --force",
+ "dev:clean": "rm -rf node_modules/.vite && pnpm run gui",
"gui": "electron-vite dev --config electron.vite.config.ts --watch",
"build": "electron-vite build --config electron.vite.config.ts",
"package": "electron-builder",
diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl
index a16b62035..156507c32 100644
--- a/gui/public/i18n/en/translation.ftl
+++ b/gui/public/i18n/en/translation.ftl
@@ -605,9 +605,22 @@ settings-stay_aligned-debug-copy-label = Copy settings to clipboard
settings-keybinds = Keybind settings
settings-keybinds-description = Change keybinds for various shortcuts
+keybind_config-keybind_name = Keybind
+keybind_config-keybind_value = Combination
+keybind_config-keybind_delay = Delay before trigger (s)
+settings-keybinds_full-reset = Full Reset
+settings-keybinds_yaw-reset = Yaw Reset
+settings-keybinds_mounting-reset = Mounting Reset
+settings-keybinds_feet-mounting-reset = Feet Mounting Reset
+settings-keybinds_pause-tracking = Pause Tracking
+settings-keybinds_record-keybind = Click to record
+settings-keybinds_now-recording = Recording…
+settings-keybinds_reset-button = Reset
+settings-keybinds_reset-all-button = Reset all
+settings-keybinds-wayland-description = You appear to be using wayland, Please change your shortcuts in your system settings.
+settings-keybinds-wayland-open-system-settings-button = Open system settings
settings-sidebar-keybinds = Keybinds
-settings-sidebar_keybinds_record-keybind = Click to record
-settings-sidebar_keybinds_now-recording = Recording…
+
## FK/Tracking settings
settings-general-fk_settings = Tracking settings
diff --git a/gui/src/components/commons/KeybindRecorder.tsx b/gui/src/components/commons/KeybindRecorder.tsx
index c047f224d..dd5ef1b58 100644
--- a/gui/src/components/commons/KeybindRecorder.tsx
+++ b/gui/src/components/commons/KeybindRecorder.tsx
@@ -14,12 +14,24 @@ export const KeybindRecorder = forwardRef<
return (
+
{
setOldKeys(keys);
- onKeysChange([]);
+ onKeysChange(['CTRL', 'ALT']);
setIsRecording(true);
}}
onBlur={() => {
@@ -33,21 +45,29 @@ export const KeybindRecorder = forwardRef<
}
}}
/>
-
-
- {keys.map((key, i) => (
-
- {key}
-
- ))}
+
+ {Array.from({ length: 4 }).map((_, i) => {
+ const key = keys[i];
+ const isActive = isRecording && i === keys.length;
+ return (
+
+ {key ?? ''}
+
+ );
+ })}
-
-
- {keys.length < 4 && isRecording ? l10n.getString('settings-sidebar_keybinds_now-recording') : l10n.getString('settings-sidebar_keybinds_record-keybind') }
+
+ {keys.length < 4 && isRecording
+ ? l10n.getString('settings-keybinds_now-recording')
+ : l10n.getString('settings-keybinds_record-keybind')}
diff --git a/gui/src/components/commons/KeybindRow.scss b/gui/src/components/commons/KeybindRow.scss
new file mode 100644
index 000000000..2f81f72dd
--- /dev/null
+++ b/gui/src/components/commons/KeybindRow.scss
@@ -0,0 +1,16 @@
+:root {
+ --row-h: 80px;
+}
+
+.keybind-row {
+ display: grid;
+ grid-column: 1 / -1;
+ grid-template-columns: subgrid;
+ height: var(--row-h);
+ align-items: center;
+ gap: 20px;
+}
+
+ .number-selector::after {
+ gap: 0;
+ }
diff --git a/gui/src/components/commons/KeybindRow.tsx b/gui/src/components/commons/KeybindRow.tsx
index 5a094a457..345cab8e0 100644
--- a/gui/src/components/commons/KeybindRow.tsx
+++ b/gui/src/components/commons/KeybindRow.tsx
@@ -3,19 +3,22 @@ import { Button } from './Button';
import { NumberSelector } from './NumberSelector';
import { KeybindRecorder } from './KeybindRecorder';
import { useLocaleConfig } from '@/i18n/config';
+import { Typography } from './Typography';
+import './KeybindRow.scss';
export function KeybindRow({
- label,
+ id,
control,
resetField,
- bindingName,
- delayName,
+ name,
+ delay,
}: {
- label: string;
+ id?: string;
+ label?: string;
control: Control
;
resetField: UseFormResetField;
- bindingName: string;
- delayName: string;
+ name: string;
+ delay: string;
}) {
const { currentLocales } = useLocaleConfig();
const secondsFormat = new Intl.NumberFormat(currentLocales, {
@@ -24,50 +27,41 @@ export function KeybindRow({
unitDisplay: 'narrow',
maximumFractionDigits: 2,
});
+
return (
-
- |
-
- |
-
- (
-
- )}
- />
- |
-
+
+
+ (
+
+ )}
+ />
secondsFormat.format(value)}
min={0}
max={10}
step={0.2}
/>
- |
-
-
-
-
-
- |
-
+
+
+
);
}
diff --git a/gui/src/components/onboarding/UdevRulesModal.tsx b/gui/src/components/onboarding/UdevRulesModal.tsx
index b89af1e73..634acb4d7 100644
--- a/gui/src/components/onboarding/UdevRulesModal.tsx
+++ b/gui/src/components/onboarding/UdevRulesModal.tsx
@@ -8,17 +8,17 @@ import { useWebsocketAPI } from '@/hooks/websocket-api';
import { RpcMessage, InstalledInfoResponseT } from 'solarxr-protocol';
import { useConfig } from '@/hooks/config';
import { useLocalization } from '@fluent/react';
+import { useAppContext } from '@/hooks/app';
export function UdevRulesModal() {
const { config, setConfig } = useConfig();
- const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const electron = useElectron();
const [udevContent, setUdevContent] = useState('');
- const [isUdevInstalledResponse, setIsUdevInstalledResponse] = useState(true);
const [showUdevWarning, setShowUdevWarning] = useState(false);
const [dontShowThisSession, setDontShowThisSession] = useState(false);
const [dontShowAgain, setDontShowAgain] = useState(false);
const { l10n } = useLocalization();
+ const { installInfo } = useAppContext();
const handleUdevContent = async () => {
if (electron.isElectron) {
@@ -38,28 +38,15 @@ export function UdevRulesModal() {
if (!config) throw 'Invalid state!';
if (electron.isElectron) {
const isLinux = electron.data().os.type === 'linux';
- const udevMissing = !isUdevInstalledResponse;
+ const udevMissing = !installInfo?.isUdevInstalled;
const notHiddenGlobally = !config.dontShowUdevModal;
const notHiddenThisSession = !dontShowThisSession;
const shouldShow =
isLinux && udevMissing && notHiddenGlobally && notHiddenThisSession;
setShowUdevWarning(shouldShow);
}
- }, [config, isUdevInstalledResponse, dontShowThisSession]);
+ }, [config, dontShowThisSession]);
- useEffect(() => {
- sendRPCPacket(
- RpcMessage.InstalledInfoRequest,
- new InstalledInfoResponseT()
- );
- }, []);
-
- useRPCPacket(
- RpcMessage.InstalledInfoResponse,
- ({ isUdevInstalled }: InstalledInfoResponseT) => {
- setIsUdevInstalledResponse(isUdevInstalled);
- }
- );
const handleModalClose = () => {
if (!config) throw 'Invalid State!';
diff --git a/gui/src/components/settings/pages/KeybindSettings.scss b/gui/src/components/settings/pages/KeybindSettings.scss
new file mode 100644
index 000000000..442e48500
--- /dev/null
+++ b/gui/src/components/settings/pages/KeybindSettings.scss
@@ -0,0 +1,22 @@
+:root {
+ --keybindrow-w: 1fr;
+ --top-row-h: 10px;
+ --keybindrow-h: 10px;
+ --buttonrow-h: 10px;
+}
+
+.keybind-settings {
+ display: grid;
+
+ grid-template:
+ 'n v d e' auto
+ 'k k k k' auto
+ 'b . . .' auto
+ / 200px 1fr 120px auto;
+
+ grid-template-columns: subgrid;
+ grid-template-rows: subgrid;
+
+ place-items: center;
+
+}
diff --git a/gui/src/components/settings/pages/KeybindSettings.tsx b/gui/src/components/settings/pages/KeybindSettings.tsx
index 6bc33d7a4..6a514018e 100644
--- a/gui/src/components/settings/pages/KeybindSettings.tsx
+++ b/gui/src/components/settings/pages/KeybindSettings.tsx
@@ -4,8 +4,9 @@ import {
SettingsPagePaneLayout,
} from '@/components/settings/SettingsPageLayout';
import { Typography } from '@/components/commons/Typography';
-import { Localized, useLocalization } from '@fluent/react';
-import { useForm } from 'react-hook-form';
+import { useLocalization } from '@fluent/react';
+import { DefaultValues, useForm } from 'react-hook-form';
+import './KeybindSettings.scss';
import { ReactNode, useEffect } from 'react';
import { KeybindRow } from '@/components/commons/KeybindRow';
import { Button } from '@/components/commons/Button';
@@ -17,7 +18,30 @@ import {
KeybindT,
KeybindName,
ChangeKeybindRequestT,
+ OpenUriRequestT,
} from 'solarxr-protocol';
+import { useAppContext } from '@/hooks/app';
+
+function Table({ children }: { children: ReactNode }) {
+ return (
+
+
+
+ |
+
+ |
+
+
+ |
+
+
+ |
+
+
+ {children}
+
+ );
+}
export type KeybindsForm = {
names: {
@@ -67,39 +91,17 @@ const defaultValues: KeybindsForm = {
},
};
-export function useKeybindsForm() {
- const {
- register,
- reset,
- handleSubmit,
- formState,
- control,
- getValues,
- resetField,
- watch,
- } = useForm
({
- defaultValues,
- });
-
- return {
- control,
- register,
- reset,
- handleSubmit,
- formState,
- getValues,
- resetField,
- watch,
- };
-}
-
export function KeybindSettings() {
const { l10n } = useLocalization();
- const { control, reset, handleSubmit, watch, getValues, resetField } =
- useKeybindsForm();
+ const { control, reset, handleSubmit, watch, resetField, getValues } =
+ useForm({
+ defaultValues,
+ });
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
+ const { installInfo } = useAppContext();
const onSubmit = (values: KeybindsForm) => {
+ console.log('Onsubmit in KeybindSettings');
const keybinds = new ChangeKeybindRequestT();
const fullResetKeybind = new KeybindT();
@@ -134,7 +136,6 @@ export function KeybindSettings() {
feetResetKeybind.keybindDelay = values.delays.pauseTrackingDelay;
keybinds.keybind.push(feetResetKeybind);
- console.log(`Delay ${Number(fullResetKeybind.keybindDelay)}`);
sendRPCPacket(RpcMessage.ChangeKeybindRequest, keybinds);
};
@@ -150,162 +151,151 @@ export function KeybindSettings() {
useRPCPacket(RpcMessage.KeybindResponse, ({ keybind }: KeybindResponseT) => {
if (!keybind) return;
- const keybindValues: KeybindsForm = {
- names: {
- fullResetName: KeybindName.FULL_RESET,
- yawResetName: KeybindName.YAW_RESET,
- mountingResetName: KeybindName.MOUNTING_RESET,
- pauseTrackingName: KeybindName.PAUSE_TRACKING,
- feetResetName: KeybindName.FEET_MOUNTING_RESET,
+ reset(
+ {
+ names: {
+ fullResetName: KeybindName.FULL_RESET,
+ yawResetName: KeybindName.YAW_RESET,
+ mountingResetName: KeybindName.MOUNTING_RESET,
+ pauseTrackingName: KeybindName.PAUSE_TRACKING,
+ feetResetName: KeybindName.FEET_MOUNTING_RESET,
+ },
+ bindings: {
+ fullResetBinding:
+ (typeof keybind[KeybindName.FULL_RESET].keybindValue === 'string'
+ ? keybind[KeybindName.FULL_RESET].keybindValue
+ : ''
+ ).split('+') || defaultValues.bindings.fullResetBinding,
+ yawResetBinding:
+ (typeof keybind[KeybindName.YAW_RESET].keybindValue === 'string'
+ ? keybind[KeybindName.YAW_RESET].keybindValue
+ : ''
+ ).split('+') || defaultValues.bindings.yawResetBinding,
+ mountingResetBinding:
+ (typeof keybind[KeybindName.MOUNTING_RESET].keybindValue ===
+ 'string'
+ ? keybind[KeybindName.MOUNTING_RESET].keybindValue
+ : ''
+ ).split('+') || defaultValues.bindings.mountingResetBinding,
+ pauseTrackingBinding:
+ (typeof keybind[KeybindName.PAUSE_TRACKING].keybindValue ===
+ 'string'
+ ? keybind[KeybindName.PAUSE_TRACKING].keybindValue
+ : ''
+ ).split('+') || defaultValues.bindings.pauseTrackingBinding,
+ feetResetBinding:
+ (typeof keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue ===
+ 'string'
+ ? keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue
+ : ''
+ ).split('+') || defaultValues.bindings.feetResetBinding,
+ },
+ delays: {
+ fullResetDelay:
+ keybind[KeybindName.FULL_RESET].keybindDelay ||
+ defaultValues.delays.fullResetDelay,
+ yawResetDelay:
+ keybind[KeybindName.YAW_RESET].keybindDelay ||
+ defaultValues.delays.yawResetDelay,
+ mountingResetDelay:
+ keybind[KeybindName.MOUNTING_RESET].keybindDelay ||
+ defaultValues.delays.mountingResetDelay,
+ pauseTrackingDelay:
+ keybind[KeybindName.PAUSE_TRACKING].keybindDelay ||
+ defaultValues.delays.pauseTrackingDelay,
+ feetResetDelay:
+ keybind[KeybindName.FEET_MOUNTING_RESET].keybindDelay ||
+ defaultValues.delays.feetResetDelay,
+ },
},
- bindings: {
- fullResetBinding:
- (typeof keybind[KeybindName.FULL_RESET].keybindValue === 'string'
- ? keybind[KeybindName.FULL_RESET].keybindValue
- : ''
- ).split('+') || defaultValues.bindings.fullResetBinding,
-
- yawResetBinding:
- (typeof keybind[KeybindName.YAW_RESET].keybindValue === 'string'
- ? keybind[KeybindName.YAW_RESET].keybindValue
- : ''
- ).split('+') || defaultValues.bindings.yawResetBinding,
-
- mountingResetBinding:
- (typeof keybind[KeybindName.MOUNTING_RESET].keybindValue === 'string'
- ? keybind[KeybindName.MOUNTING_RESET].keybindValue
- : ''
- ).split('+') || defaultValues.bindings.mountingResetBinding,
-
- pauseTrackingBinding:
- (typeof keybind[KeybindName.PAUSE_TRACKING].keybindValue === 'string'
- ? keybind[KeybindName.PAUSE_TRACKING].keybindValue
- : ''
- ).split('+') || defaultValues.bindings.pauseTrackingBinding,
-
- feetResetBinding:
- (typeof keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue ===
- 'string'
- ? keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue
- : ''
- ).split('+') || defaultValues.bindings.feetResetBinding,
- },
- delays: {
- fullResetDelay:
- keybind[KeybindName.FULL_RESET].keybindDelay ||
- defaultValues.delays.fullResetDelay,
- yawResetDelay:
- keybind[KeybindName.YAW_RESET].keybindDelay ||
- defaultValues.delays.yawResetDelay,
- mountingResetDelay:
- keybind[KeybindName.MOUNTING_RESET].keybindDelay ||
- defaultValues.delays.mountingResetDelay,
- pauseTrackingDelay:
- keybind[KeybindName.PAUSE_TRACKING].keybindDelay ||
- defaultValues.delays.mountingResetDelay,
- feetResetDelay:
- keybind[KeybindName.FEET_MOUNTING_RESET].keybindDelay ||
- defaultValues.delays.feetResetDelay,
- },
- };
-
- reset({ ...getValues(), ...keybindValues });
+ {
+ keepDefaultValues: true,
+ }
+ );
});
- const handleResetButton = () => {
- reset(defaultValues);
+ const handleOpenSystemSettingsButton = () => {
+ sendRPCPacket(RpcMessage.OpenUriRequest, new OpenUriRequestT());
};
- function Table({ children }: { children: ReactNode }) {
- return (
-
-
-
- |
-
-
-
- Keybind
- |
-
-
-
-
- Combination
- |
-
-
-
-
- Delay before trigger (S)
- |
-
-
- {children}
-
- );
- }
+ const handleResetAllButton = () => {
+ reset(defaultValues);
+ };
return (