diff --git a/gui/package.json b/gui/package.json
index 8b8b20766..6e61ff09d 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -29,10 +29,11 @@
"react-modal": "3.15.1",
"react-responsive": "^9.0.2",
"react-router-dom": "^6.2.2",
- "semver": "^7.5.0",
+ "semver": "^7.5.3",
"solarxr-protocol": "file:../solarxr-protocol",
"three": "^0.148.0",
- "typescript": "^4.6.3"
+ "ts-pattern": "^5.0.1",
+ "typescript": "^5.1.6"
},
"scripts": {
"start": "vite --force",
@@ -64,30 +65,30 @@
]
},
"devDependencies": {
- "@tailwindcss/forms": "^0.5.0",
+ "@tailwindcss/forms": "^0.5.3",
"@tauri-apps/cli": "^1.4.0",
"@types/file-saver": "^2.0.5",
"@types/react": "18.0.25",
"@types/react-dom": "^18.0.5",
"@types/react-modal": "3.13.1",
"@types/three": "^0.148.0",
- "@typescript-eslint/eslint-plugin": "^5.59.6",
- "@typescript-eslint/parser": "^5.59.0",
+ "@typescript-eslint/eslint-plugin": "^5.60.1",
+ "@typescript-eslint/parser": "^5.60.1",
"autoprefixer": "^10.4.4",
"cross-env": "^7.0.3",
- "eslint": "^8.18.0",
+ "eslint": "^8.44.0",
"eslint-config-airbnb": "^19.0.4",
- "eslint-import-resolver-typescript": "^3.1.1",
- "eslint-plugin-import": "^2.26.0",
- "eslint-plugin-jsx-a11y": "^6.6.0",
- "eslint-plugin-react": "^7.30.1",
+ "eslint-import-resolver-typescript": "^3.5.5",
+ "eslint-plugin-import": "^2.27.5",
+ "eslint-plugin-jsx-a11y": "^6.7.1",
+ "eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
- "postcss": "^8.4.12",
- "prettier": "^2.7.1",
+ "postcss": "^8.4.24",
+ "prettier": "^2.8.8",
"pretty-quick": "^3.1.3",
"rollup-plugin-visualizer": "^5.9.2",
"tailwind-gradient-mask-image": "^1.0.0",
- "tailwindcss": "^3.3.1",
- "vite": "^4.0.3"
+ "tailwindcss": "^3.3.2",
+ "vite": "^4.3.9"
}
}
diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl
index 16e87eb18..b6caa1cd9 100644
--- a/gui/public/i18n/en/translation.ftl
+++ b/gui/public/i18n/en/translation.ftl
@@ -730,7 +730,10 @@ onboarding-choose_proportions-description = Body proportions are used to know th
onboarding-choose_proportions-auto_proportions = Automatic proportions
# Italized text
onboarding-choose_proportions-auto_proportions-subtitle = Recommended
-onboarding-choose_proportions-auto_proportions-description = This will guess your proportions by recording a sample of your movements and passing it through an algorithm
+onboarding-choose_proportions-auto_proportions-descriptionv2 =
+ This will guess your proportions by recording a sample of your movements and passing it through an algorithm.
+
+ It requires having your HMD connected to SlimeVR!
onboarding-choose_proportions-manual_proportions = Manual proportions
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = For small touches
@@ -764,6 +767,18 @@ onboarding-automatic_proportions-requirements-description =
Your trackers and headset are working properly within the SlimeVR server.
Your headset is reporting positional data to the SlimeVR server (this generally means having SteamVR running and connected to SlimeVR using SlimeVR's SteamVR driver).
onboarding-automatic_proportions-requirements-next = I have read the requirements
+onboarding-automatic_proportions-check_height-title = Check your height
+onboarding-automatic_proportions-check_height-description = We use your height as a basis of our measurements by using the HMD's height as an approximation of your actual height, but it's better to check if they are right yourself!
+# All the text is in bold!
+onboarding-automatic_proportions-check_height-calculation_warning = Please press the button while standing upright to calculate your height. You have 3 seconds after you press the button!
+onboarding-automatic_proportions-check_height-fetch_height = I'm standing!
+# Context is that the height is unknown
+onboarding-automatic_proportions-check_height-unknown = Unknown
+# Shows an element below it
+onboarding-automatic_proportions-check_height-height = Your height is
+# Shows an element below it
+onboarding-automatic_proportions-check_height-hmd_height = And HMD height is
+onboarding-automatic_proportions-check_height-next_step = They are fine
onboarding-automatic_proportions-start_recording-title = Get ready to move
onboarding-automatic_proportions-start_recording-description = We're now going to record some specific poses and moves. These will be prompted in the next screen. Be ready to start when the button is pressed!
onboarding-automatic_proportions-start_recording-next = Start Recording
@@ -792,6 +807,10 @@ onboarding-automatic_proportions-verify_results-redo = Redo recording
onboarding-automatic_proportions-verify_results-confirm = They're correct
onboarding-automatic_proportions-done-title = Body measured and saved.
onboarding-automatic_proportions-done-description = Your body proportions' calibration is complete!
+onboarding-automatic_proportions-error_modal =
+ Warning: An error was found while estimating proportions!
+ Please check the docs or join our Discord for help ^_^
+onboarding-automatic_proportions-error_modal-confirm = Understood!
## Home
home-no_trackers = No trackers detected or assigned
diff --git a/gui/src/App.tsx b/gui/src/App.tsx
index 71f02bae1..213d70f51 100644
--- a/gui/src/App.tsx
+++ b/gui/src/App.tsx
@@ -55,7 +55,8 @@ import { error, log } from './utils/logging';
export const GH_REPO = 'SlimeVR/SlimeVR-Server';
export const VersionContext = createContext('');
-export const DOCS_SITE = 'https://docs.slimevr.dev/';
+export const DOCS_SITE = 'https://docs.slimevr.dev';
+export const SLIMEVR_DISCORD = 'https://discord.gg/slimevr';
function Layout() {
const { loading } = useConfig();
diff --git a/gui/src/components/commons/A.tsx b/gui/src/components/commons/A.tsx
new file mode 100644
index 000000000..311887831
--- /dev/null
+++ b/gui/src/components/commons/A.tsx
@@ -0,0 +1,14 @@
+import { open } from '@tauri-apps/api/shell';
+import { ReactNode } from 'react';
+
+export function A({ href, children }: { href: string; children?: ReactNode }) {
+ return (
+ open(href).catch(() => window.open(href, '_blank'))}
+ className="underline"
+ >
+ {children}
+
+ );
+}
diff --git a/gui/src/components/commons/NumberSelector.tsx b/gui/src/components/commons/NumberSelector.tsx
index 4d93e6018..06d39869e 100644
--- a/gui/src/components/commons/NumberSelector.tsx
+++ b/gui/src/components/commons/NumberSelector.tsx
@@ -10,6 +10,7 @@ export function NumberSelector({
min,
max,
step,
+ disabled = false,
}: {
label: string;
valueLabelFormat?: (value: number) => string;
@@ -18,6 +19,7 @@ export function NumberSelector({
min: number;
max: number;
step: number | ((value: number, add: boolean) => number);
+ disabled?: boolean;
}) {
const stepFn =
typeof step === 'function'
@@ -38,7 +40,7 @@ export function NumberSelector({
variant="tertiary"
rounded
onClick={() => onChange(stepFn(value, false))}
- disabled={stepFn(value, false) < min}
+ disabled={stepFn(value, false) < min || disabled}
>
-
@@ -51,7 +53,7 @@ export function NumberSelector({
variant="tertiary"
rounded
onClick={() => onChange(stepFn(value, true))}
- disabled={stepFn(value, true) > max}
+ disabled={stepFn(value, true) > max || disabled}
>
+
diff --git a/gui/src/components/onboarding/pages/body-proportions/AutomaticProportions.tsx b/gui/src/components/onboarding/pages/body-proportions/AutomaticProportions.tsx
index 62b31ad33..84f1c11e9 100644
--- a/gui/src/components/onboarding/pages/body-proportions/AutomaticProportions.tsx
+++ b/gui/src/components/onboarding/pages/body-proportions/AutomaticProportions.tsx
@@ -16,6 +16,7 @@ import { Recording } from './autobone-steps/Recording';
import { StartRecording } from './autobone-steps/StartRecording';
import { VerifyResultsStep } from './autobone-steps/VerifyResults';
import { useCountdown } from '../../../../hooks/countdown';
+import { CheckHeight } from './autobone-steps/СheckHeight';
export function AutomaticProportionsPage() {
const { l10n } = useLocalization();
@@ -53,6 +54,7 @@ export function AutomaticProportionsPage() {
steps={[
{ type: 'numbered', component: PutTrackersOnStep },
{ type: 'numbered', component: RequirementsStep },
+ { type: 'numbered', component: CheckHeight },
{ type: 'numbered', component: StartRecording },
{ type: 'fullsize', component: Recording },
{ type: 'numbered', component: VerifyResultsStep },
diff --git a/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx b/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
index 6a52015f9..3a6d099aa 100644
--- a/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
+++ b/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
@@ -60,7 +60,7 @@ export function BodyProportions({
unit: 'centimeter',
maximumFractionDigits: 1,
});
- const percentageFormat = Intl.NumberFormat(currentLocales, {
+ const percentageFormat = new Intl.NumberFormat(currentLocales, {
style: 'percent',
maximumFractionDigits: 1,
});
diff --git a/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx b/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx
index 9e030058b..0b24616a9 100644
--- a/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx
+++ b/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx
@@ -1,6 +1,6 @@
import { useOnboarding } from '../../../../hooks/onboarding';
-import { useLocalization } from '@fluent/react';
-import { useState } from 'react';
+import { Localized, useLocalization } from '@fluent/react';
+import { useMemo, useState } from 'react';
import classNames from 'classnames';
import { Typography } from '../../../commons/Typography';
import { Button } from '../../../commons/Button';
@@ -14,14 +14,39 @@ import saveAs from 'file-saver';
import { save } from '@tauri-apps/api/dialog';
import { writeTextFile } from '@tauri-apps/api/fs';
import { useIsTauri } from '../../../../hooks/breakpoint';
+import { useAppContext } from '../../../../hooks/app';
import { error } from '../../../../utils/logging';
+export const MIN_HEIGHT = 0.4;
+export const MAX_HEIGHT = 4;
+export const DEFAULT_HEIGHT = 1.5;
+
export function ProportionsChoose() {
const isTauri = useIsTauri();
const { l10n } = useLocalization();
const { applyProgress, state } = useOnboarding();
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const [animated, setAnimated] = useState(false);
+ const { computedTrackers } = useAppContext();
+
+ const hmdTracker = useMemo(
+ () =>
+ computedTrackers.find(
+ (tracker) =>
+ tracker.tracker.trackerId?.trackerNum === 1 &&
+ tracker.tracker.trackerId.deviceId?.id === undefined
+ ),
+ [computedTrackers]
+ );
+
+ const beneathFloor = useMemo(
+ () =>
+ !(
+ hmdTracker?.tracker.position &&
+ hmdTracker.tracker.position.y >= MIN_HEIGHT
+ ),
+ [hmdTracker?.tracker.position?.y]
+ );
useRPCPacket(
RpcMessage.SkeletonConfigResponse,
@@ -145,15 +170,23 @@ export function ProportionsChoose() {
-
- {l10n.getString(
- 'onboarding-choose_proportions-auto_proportions-description'
- )}
-
+ }}
+ >
+
+ Description for autobone
+
+