Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quick setting setup steps on onboarding #1218

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
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
6b8e4c9
start adding usage step on onboarding
ImUrX Oct 8, 2024
e530cb5
Merge branch 'main' into onboarding-usage-step
ImUrX Oct 14, 2024
af7f13e
Merge branch 'main' into onboarding-usage-step
ImUrX Oct 21, 2024
682300e
add vrusagechoose
ImUrX Oct 23, 2024
085ba25
add image
ImUrX Oct 23, 2024
bb5b9f3
add check and warning
ImUrX Oct 23, 2024
db80609
fix progress
ImUrX Oct 23, 2024
97fb9dd
fix bugs
ImUrX Oct 25, 2024
986ef8d
add standalone usage guide step
ImUrX Oct 25, 2024
583e335
fix status not working correctly
ImUrX Oct 28, 2024
0c9a016
Merge branch 'main' into onboarding-usage-step
ImUrX Oct 28, 2024
945b8b3
use voltage inside tracker settings page for showing charging
ImUrX Oct 28, 2024
c026915
add images for radio button
ImUrX Oct 29, 2024
957e81c
commit the code that uses it
ImUrX Oct 29, 2024
c341534
fix images again
ImUrX Oct 30, 2024
5f60e59
Merge branch 'main' into onboarding-usage-step
ImUrX Nov 4, 2024
1385403
improve translations
ImUrX Nov 4, 2024
fa10e2d
Merge branch 'main' into onboarding-usage-step
ImUrX Nov 19, 2024
8bdee03
add head tracking choose page
ImUrX Nov 20, 2024
6a8786b
Merge branch 'main' into onboarding-usage-step
ImUrX Nov 20, 2024
96fa13a
improve buttons
ImUrX Nov 21, 2024
92b7ca7
modify how it looks
ImUrX Nov 27, 2024
da133c0
Merge branch 'main' into onboarding-usage-step
ImUrX Dec 2, 2024
40b2da3
continue working in the data choose page
ImUrX Nov 27, 2024
b90da87
mocap data choose
ImUrX Nov 29, 2024
fa74a74
Start adding strings of stuff
ImUrX Dec 2, 2024
9c9c552
Merge branch 'main' into onboarding-usage-step
ImUrX Dec 12, 2024
c2fe954
add mocap vmc setting
ImUrX Dec 12, 2024
75cf328
keep improving on some pages
ImUrX Dec 17, 2024
a088145
Merge branch 'main' into onboarding-usage-step
ImUrX Jan 22, 2025
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
3 changes: 2 additions & 1 deletion gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@
"spdx-satisfies": "^5.0.1",
"tailwind-gradient-mask-image": "^1.2.0",
"tailwindcss": "^3.4.13",
"ts-xor": "^1.3.0",
"vite": "^5.4.8",
"typescript-eslint": "^8.8.0"
}
}
}
81 changes: 78 additions & 3 deletions gui/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -610,12 +610,14 @@ settings-osc-router-network-address-placeholder = IPV4 address
## OSC VRChat settings
settings-osc-vrchat = VRChat OSC Trackers
# This cares about multilines
settings-osc-vrchat-description-v1 =
settings-osc-vrchat-description-v2 =
Change settings specific to the OSC Trackers standard used for sending
tracking data to applications without SteamVR (ex. Quest standalone).
# This cares about multilines
settings-osc-vrchat-description-guide =
Make sure to enable OSC in VRChat via the Action Menu under OSC > Enabled.
To allow receiving HMD and controller data from VRChat, go in your main menu's
settings under Tracking & IK > Allow Sending Head and Wrist VR Tracking OSC Data.

To allow receiving HMD and controller data from VRChat, go in your main menu's settings under Tracking & IK > Allow Sending Head and Wrist VR Tracking OSC Data.
settings-osc-vrchat-enable = Enable
settings-osc-vrchat-enable-description = Toggle the sending and receiving of data.
settings-osc-vrchat-enable-label = Enable
Expand Down Expand Up @@ -807,6 +809,79 @@ onboarding-assignment_tutorial-second_step-v2 = 2. Attach the strap to your trac
onboarding-assignment_tutorial-second_step-continuation-v2 = The velcro side for the extension should be facing up like the following image:
onboarding-assignment_tutorial-done = I put stickers and straps!

## Usage reason choose
onboarding-usage-choose = What are you gonna use SlimeVR for?
onboarding-usage-choose-description = What are you gonna use SlimeVR for?
onboarding-usage-choose-option-title = { $mode ->
*[VR] Virtual Reality
[VTUBING] VTuber
[MOCAP] Motion Capture
}
onboarding-usage-choose-option-label = { $mode ->
*[VR] For use with games and applications that use a headset
[VTUBING] For use with VTubing programs that use the VMC protocol
[MOCAP] For recording a whole body with precise tracking.
}
onboarding-usage-choose-option-description = { $mode ->
*[VR] Users of SteamVR or VR programs that use OSC can select this option to get right to it.
[VTUBING] VTubing programs work with SlimeVR using VMC (Virtual Motion Capture), this is what to pick for that!
[MOCAP] Many 3D programs can record live mocap for use in animation, and BVH recording is also supported directly in the app.
}

## VR usage choose
onboarding-usage-vr-choose = Choose your VR setup
onboarding-usage-vr-choose-description = There are different ways to connect SlimeVR to your virtual reality setup! You can decide which you will use here.
onboarding-usage-vr-choose-steamvr = I use SteamVR
onboarding-usage-vr-choose-steamvr-label = For PCVR
# uses multiline
onboarding-usage-vr-choose-steamvr-description =
SlimeVR emulates SteamVR trackers using the rotational data of the trackers and a human skeleton model, so SteamVR games and programs can use it for full body tracking.

SteamVR must be installed and a headset or positional head tracker connected to the SlimeVR server to use this method.
onboarding-usage-vr-choose-steamvr-warning = The SteamVR driver is currently not connected, <b>please turn on SteamVR</b> or check <docs>the docs for more info</docs>.
onboarding-usage-vr-choose-standalone = I use standalone
onboarding-usage-vr-choose-standalone-label = For VRChat Quest/Pico users
onboarding-usage-vr-choose-standalone-description =
Standalone use connects through OSC instead of SteamVR to provide full body tracking with SlimeVR.
Any PC that can run SlimeVR server can function like this, as well as phones, which are the recommended ways for best ergonomics.
onboarding-usage-vr-standalone-title = Setting up VRChat
onboarding-usage-vr-standalone-next = Done!

## Mocap head usage choose
onboarding-usage-mocap-head_choose = What kind of head tracking do you want?
onboarding-usage-mocap-head_choose-description = You can use either a tracker or a headset for the head!

onboarding-usage-mocap-head_choose-standalone = SlimeVR head tracker
onboarding-usage-mocap-head_choose-standalone-label = Use an IMU tracker for tracking position
onboarding-usage-mocap-head_choose-standalone-description =
This enables head tracking using a head mounted SlimeVR tracker.

This is much less precise in the way that if you walk and return to your starting point, you won't be on the same place on the recording.
onboarding-usage-mocap-head_choose-standalone-button = Use IMU tracker

onboarding-usage-mocap-head_choose-steamvr = SteamVR head tracking
onboarding-usage-mocap-head_choose-steamvr-label = Use an HMD or a positional tracker for precision
onboarding-usage-mocap-head_choose-steamvr-description =
Most accurate way to track the head, using true positional data as reference.

This allows for the best quality motion capture recordings as well as movements that require both feet to leave the floor at the same time.
onboarding-usage-mocap-head_choose-steamvr-button = Use SteamVR

## Mocap data mode choose
onboarding-usage-mocap-data_choose = What kind of data format to use?
onboarding-usage-mocap-data_choose-description = description

onboarding-usage-mocap-data_choose-option-title = { $mode ->
*[BVH] BVH
[STEAMVR] SteamVR
[VMC] VMC
}
onboarding-usage-mocap-data_choose-option-label = { $mode ->
*[BVH] Natively supported on most animation programs
[STEAMVR] For programs that support OpenVR as a source of data
[VMC] Popular data protocol for VTubing
}

## Tracker assignment setup
onboarding-assign_trackers-back = Go Back to Wi-Fi Credentials
onboarding-assign_trackers-title = Assign trackers
Expand Down
Binary file added gui/public/images/nighty-vr-sitting.webp
Binary file not shown.
Binary file added gui/public/images/usage-mocap.webp
Binary file not shown.
Binary file added gui/public/images/usage-vr.webp
Binary file not shown.
Binary file added gui/public/images/usage-vtuber.webp
Binary file not shown.
Binary file added gui/public/images/vrslimes.webp
Binary file not shown.
Binary file added gui/public/videos/vrchatosc.webm
Binary file not shown.
23 changes: 23 additions & 0 deletions gui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ import { useDiscordPresence } from './hooks/discord-presence';
import { EmptyLayout } from './components/EmptyLayout';
import { AdvancedSettings } from './components/settings/pages/AdvancedSettings';
import { FirmwareUpdate } from './components/firmware-update/FirmwareUpdate';
import { UsageChoose } from './components/onboarding/pages/usage-reason/UsageChoose';
import { VRUsageChoose } from './components/onboarding/pages/usage-reason/VRUsageChoose';
import { StandaloneUsageSetup } from './components/onboarding/pages/usage-reason/StandaloneUsageSetup';
import { HeadTrackingChoose } from './components/onboarding/pages/usage-reason/HeadTrackingChoose';
import { MocapDataChoose } from './components/onboarding/pages/usage-reason/MocapDataChoose';
import { MocapVMCSetup } from './components/onboarding/pages/usage-reason/MocapVMCSetup';
import { MocapBVHSetup } from './components/onboarding/pages/usage-reason/MocapBVHSetup';
import { MocapSteamSetup } from './components/onboarding/pages/usage-reason/MocapSteamSetup';

export const GH_REPO = 'SlimeVR/SlimeVR-Server';
export const VersionContext = createContext('');
Expand Down Expand Up @@ -144,6 +152,21 @@ function Layout() {
path="assign-tutorial"
element={<AssignmentTutorialPage />}
/>

<Route path="usage">
<Route path="choose" element={<UsageChoose />} />
<Route path="vr/choose" element={<VRUsageChoose />} />
<Route path="vr/standalone" element={<StandaloneUsageSetup />} />
<Route path="mocap/data/choose" element={<MocapDataChoose />} />
<Route
path="mocap/head-choose"
element={<HeadTrackingChoose />}
/>
<Route path="mocap/data/vmc" element={<MocapVMCSetup />} />
<Route path="mocap/data/bvh" element={<MocapBVHSetup />} />
<Route path="mocap/data/steamvr" element={<MocapSteamSetup />} />
</Route>

<Route path="trackers-assign" element={<TrackersAssignPage />} />
<Route path="enter-vr" element={<EnterVRPage />} />
<Route path="mounting/choose" element={<MountingChoose />}></Route>
Expand Down
30 changes: 21 additions & 9 deletions gui/src/components/commons/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames';
import React, { ReactNode, useMemo } from 'react';
import { NavLink } from 'react-router-dom';
import { LoaderIcon, SlimeState } from './icon/LoaderIcon';
import { XOR } from 'ts-xor';

function ButtonContent({
loading,
Expand Down Expand Up @@ -36,6 +37,25 @@ function ButtonContent({
);
}

type ButtonBaseParams = {
children?: ReactNode;
icon?: ReactNode;
variant: 'primary' | 'secondary' | 'tertiary' | 'quaternary';
loading?: boolean;
rounded?: boolean;
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;

type ButtonNavigateParams = { to: string; state?: any } & ButtonBaseParams;
type ButtonScriptParams = {
onClick?: React.MouseEventHandler<HTMLButtonElement>;
} & ButtonBaseParams;
type ButtonSubmitParams = { type: 'submit' } & ButtonBaseParams;
type ButtonParams = XOR<
ButtonNavigateParams,
ButtonScriptParams,
ButtonSubmitParams
>;

export function Button({
children,
variant,
Expand All @@ -46,15 +66,7 @@ export function Button({
icon,
rounded = false,
...props
}: {
children?: ReactNode;
icon?: ReactNode;
variant: 'primary' | 'secondary' | 'tertiary' | 'quaternary';
to?: string;
loading?: boolean;
rounded?: boolean;
state?: any;
} & React.ButtonHTMLAttributes<HTMLButtonElement>) {
}: ButtonParams) {
const classes = useMemo(() => {
const variantsMap = {
primary: classNames({
Expand Down
70 changes: 70 additions & 0 deletions gui/src/components/commons/PausableVideo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useRef, useState } from 'react';
import { PlayCircleIcon } from './icon/PlayIcon';
import { useDebouncedEffect } from '@/hooks/timeout';
import classNames from 'classnames';

export function PausableVideo({
src,
poster,
restartOnPause = false,
autoPlay = false,
}: {
src?: string;
poster?: string;
restartOnPause?: boolean;
autoPlay?: boolean;
}) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [paused, setPaused] = useState(!autoPlay);
const [atStart, setAtStart] = useState(true);

function toggleVideo() {
if (!videoRef.current) return;
if (videoRef.current.paused) {
videoRef.current.play();
} else {
videoRef.current.pause();
if (restartOnPause) {
videoRef.current.currentTime = 0;
}
setAtStart(videoRef.current.currentTime === 0);
}
setPaused(videoRef.current.paused);
}

useDebouncedEffect(
() => {
if (paused) videoRef.current?.pause();
},
[paused],
250
);

return (
<button className="relative appearance-none" onClick={toggleVideo}>
<div
className={classNames(
'absolute w-[100px] h-[100px] top-0 bottom-0 left-0 right-0 m-auto',
'fill-background-20',
paused && !atStart && 'opacity-50'
)}
hidden={!paused}
>
<PlayCircleIcon width={100}></PlayCircleIcon>
</div>

<video
preload="auto"
ref={videoRef}
src={src}
poster={poster}
className="min-w-[12rem] w-[30rem]"
muted
loop
playsInline
autoPlay={autoPlay}
controls={false}
></video>
</button>
);
}
34 changes: 26 additions & 8 deletions gui/src/components/commons/Radio.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import classNames from 'classnames';
import { Control, Controller } from 'react-hook-form';
import { Typography } from './Typography';
import { ReactNode } from 'react';
import { ReactNode, useMemo } from 'react';

export function Radio({
control,
Expand All @@ -12,6 +12,7 @@ export function Radio({
children,
// input props
disabled,
variant = 'secondary',
...props
}: {
control: Control<any>;
Expand All @@ -20,19 +21,36 @@ export function Radio({
value: string;
description?: string | null;
children?: ReactNode;
variant?: 'secondary' | 'none';
} & React.HTMLProps<HTMLInputElement>) {
const variantClasses = useMemo(() => {
const variantsMap = {
secondary: classNames({
'bg-background-60 hover:bg-background-50': !disabled,
'bg-background-80': disabled,
}),
none: '',
};
return variantsMap[variant];
}, [variant, disabled]);

return (
<Controller
control={control}
name={name}
render={({ field: { onChange, ref, name, value: checked } }) => (
<label
className={classNames('w-full p-3 rounded-md flex gap-3 border-2', {
'border-accent-background-30': value == checked,
'border-transparent': value != checked,
'bg-background-60 cursor-pointer hover:bg-background-50': !disabled,
'bg-background-80 cursor-not-allowed': disabled,
})}
className={classNames(
'w-full rounded-md flex gap-3 border-2 group/radio',
variantClasses,
{
'border-accent-background-30': value == checked,
'border-transparent': value != checked,
'cursor-pointer': !disabled,
'cursor-not-allowed': disabled,
'p-3': variant !== 'none',
}
)}
>
<input
type="radio"
Expand All @@ -48,7 +66,7 @@ export function Radio({
checked={value == checked}
{...props}
/>
<div className="flex flex-col gap-2 pointer-events-none">
<div className="flex flex-col gap-2 pointer-events-none w-full">
{children ? children : <Typography bold>{label}</Typography>}
{description && (
<Typography variant="standard" color="secondary">
Expand Down
2 changes: 1 addition & 1 deletion gui/src/components/commons/Typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function Typography({
tag,
{
className: classNames([
'transition-colors',
'transition-colors hyphens-auto',
variant === 'mobile-title' &&
'xs:text-main-title mobile:text-section-title',
variant === 'main-title' && 'text-main-title',
Expand Down
2 changes: 1 addition & 1 deletion gui/src/components/onboarding/pages/EnterVR.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function EnterVRPage() {
</div>
<div className="w-full py-4 flex flex-row">
<div className="flex flex-grow">
<Button variant="secondary" to="/" onClick={skipSetup}>
<Button variant="secondary" onClick={skipSetup}>
{l10n.getString('onboarding-skip')}
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { useOnboarding } from '@/hooks/onboarding';
import { useLocalization } from '@fluent/react';
import { useState } from 'react';
import { SkipSetupWarningModal } from '@/components/onboarding/SkipSetupWarningModal';
import classNames from 'classnames';
import { Typography } from '@/components/commons/Typography';
import { Button } from '@/components/commons/Button';

export function MountingChoose() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const [animated, setAnimated] = useState(false);
const [showWarning, setShowWarning] = useState(false);

applyProgress(0.65);

Expand Down Expand Up @@ -137,11 +135,6 @@ export function MountingChoose() {
)}
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setShowWarning(false)}
isOpen={showWarning}
></SkipSetupWarningModal>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export function TrackersAssignPage() {
}
);

applyProgress(0.5);
applyProgress(0.55);

const { closeChokerWarning, tryOpenChokerWarning, shouldShowChokerWarn } =
useChokerWarning({
Expand Down
Loading
Loading