diff --git a/flake.nix b/flake.nix index 4dd0c939eb..7499283e88 100644 --- a/flake.nix +++ b/flake.nix @@ -95,6 +95,7 @@ freetype expat libayatana-appindicator + libusb1 ]) ++ lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.apple_sdk.frameworks.Security diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index 8b789381a3..245c220946 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -19,6 +19,7 @@ tips-find_tracker = Not sure which tracker is which? Shake a tracker and it will tips-do_not_move_heels = Ensure your heels do not move during recording! tips-file_select = Drag & drop files to use, or browse. tips-tap_setup = You can slowly tap 2 times your tracker to choose it instead of selecting it from the menu. +tips-turn_on_tracker = Using official SlimeVR trackers? Remember to turn on your tracker after connecting it to the PC! ## Body parts body_part-NONE = Unassigned @@ -652,6 +653,7 @@ onboarding-assign_trackers-assigned = { $assigned } of { $trackers -> } assigned onboarding-assign_trackers-advanced = Show advanced assign locations onboarding-assign_trackers-next = I assigned all the trackers +onboarding-assign_trackers-mirror_view = Mirror view ## Tracker assignment warnings # Note for devs, number is used for representing boolean states per bit. @@ -761,8 +763,9 @@ onboarding-automatic_mounting-put_trackers_on-next = I have all my trackers on ## Tracker proportions method choose onboarding-choose_proportions = What proportion calibration method to use? # Multiline string -onboarding-choose_proportions-description = Body proportions are used to know the measurements of your body. They're required to calculate the trackers' positions. +onboarding-choose_proportions-description-v1 = Body proportions are used to know the measurements of your body. They're required to calculate the trackers' positions. When proportions of your body don't match the ones saved, your tracking precision will be worse and you will notice things like skating or sliding, or your body not matching your avatar well. + You only need to measure your body once! Unless they are wrong or your body has changed, then you don't need to do them again. onboarding-choose_proportions-auto_proportions = Automatic proportions # Italized text onboarding-choose_proportions-auto_proportions-subtitle = Recommended diff --git a/gui/public/images/autobone-poster.webp b/gui/public/images/autobone-poster.webp new file mode 100644 index 0000000000..b9d646f68a Binary files /dev/null and b/gui/public/images/autobone-poster.webp differ diff --git a/gui/public/videos/autobone.webm b/gui/public/videos/autobone.webm new file mode 100644 index 0000000000..e3020eb96b Binary files /dev/null and b/gui/public/videos/autobone.webm differ diff --git a/gui/src/components/Preload.tsx b/gui/src/components/Preload.tsx index b58830a1ee..fa1d9eece8 100644 --- a/gui/src/components/Preload.tsx +++ b/gui/src/components/Preload.tsx @@ -8,6 +8,8 @@ export function Preload() { + + void; highlightedRoles: BodyPart[]; + mirror: boolean; }) { const { isMobile } = useBreakpoint('mobile'); @@ -166,7 +168,7 @@ export function BodyInteractions({ variant === 'tracker-select' && 'mobile:mx-0 xs:mx-10' )} > - + {slotsButtonsPos.map( ({ top, left, height, width, id, hidden, buttonOffset }) => (
{type === 'password' && (
- +
)} {error?.message && ( diff --git a/gui/src/components/commons/PersonFrontIcon.tsx b/gui/src/components/commons/PersonFrontIcon.tsx index 51fe2f3715..78849998e7 100644 --- a/gui/src/components/commons/PersonFrontIcon.tsx +++ b/gui/src/components/commons/PersonFrontIcon.tsx @@ -1,7 +1,36 @@ import { BodyPart } from 'solarxr-protocol'; -export function PersonFrontIcon({ width }: { width?: number }) { +export const SIDES = [ + { + shoulder: BodyPart.LEFT_SHOULDER, + upperArm: BodyPart.LEFT_UPPER_ARM, + lowerArm: BodyPart.LEFT_LOWER_ARM, + hand: BodyPart.LEFT_HAND, + upperLeg: BodyPart.LEFT_UPPER_LEG, + lowerLeg: BodyPart.LEFT_LOWER_LEG, + foot: BodyPart.LEFT_FOOT, + }, + { + shoulder: BodyPart.RIGHT_SHOULDER, + upperArm: BodyPart.RIGHT_UPPER_ARM, + lowerArm: BodyPart.RIGHT_LOWER_ARM, + hand: BodyPart.RIGHT_HAND, + upperLeg: BodyPart.RIGHT_UPPER_LEG, + lowerLeg: BodyPart.RIGHT_LOWER_LEG, + foot: BodyPart.RIGHT_FOOT, + }, +]; + +export function PersonFrontIcon({ + width, + mirror = true, +}: { + width?: number; + mirror?: boolean; +}) { const CIRCLE_RADIUS = 0.0001; + const left = +!mirror; + const right = +mirror; return ( ); diff --git a/gui/src/components/commons/icon/EyeIcon.tsx b/gui/src/components/commons/icon/EyeIcon.tsx index f4ebad00a9..3e6903a09f 100644 --- a/gui/src/components/commons/icon/EyeIcon.tsx +++ b/gui/src/components/commons/icon/EyeIcon.tsx @@ -1,12 +1,36 @@ -export function EyeIcon() { - return ( +export function EyeIcon({ + width = 14, + closed = false, +}: { + width?: number; + closed?: boolean; +}) { + return closed ? ( - + + + + + ) : ( + + + ); } diff --git a/gui/src/components/commons/icon/PlayIcon.tsx b/gui/src/components/commons/icon/PlayIcon.tsx index 6b8a89a09d..14c2366c97 100644 --- a/gui/src/components/commons/icon/PlayIcon.tsx +++ b/gui/src/components/commons/icon/PlayIcon.tsx @@ -5,3 +5,20 @@ export function PlayIcon({ width = 33 }: { width?: number }) { ); } + +export function PlayCircleIcon({ width = 24 }: { width?: number }) { + return ( + + + + ); +} diff --git a/gui/src/components/onboarding/BodyAssignment.tsx b/gui/src/components/onboarding/BodyAssignment.tsx index bfa8fa4b8d..90af2f731c 100644 --- a/gui/src/components/onboarding/BodyAssignment.tsx +++ b/gui/src/components/onboarding/BodyAssignment.tsx @@ -5,7 +5,16 @@ import { useTrackers } from '@/hooks/tracker'; import { BodyInteractions } from '@/components/commons/BodyInteractions'; import { TrackerPartCard } from '@/components/tracker/TrackerPartCard'; import { BodyPartError } from './pages/trackers-assign/TrackerAssignment'; +import { SIDES } from '@/components/commons/PersonFrontIcon'; +export const LOWER_BODY = new Set([ + BodyPart.LEFT_FOOT, + BodyPart.RIGHT_FOOT, + BodyPart.LEFT_LOWER_LEG, + BodyPart.RIGHT_LOWER_LEG, + BodyPart.LEFT_UPPER_LEG, + BodyPart.RIGHT_UPPER_LEG, +]); export const SPINE_PARTS = [ BodyPart.UPPER_CHEST, BodyPart.CHEST, @@ -37,6 +46,7 @@ export const ASSIGNMENT_RULES: Partial< export function BodyAssignment({ advanced, + mirror, onRoleSelected, rolesWithErrors = {}, highlightedRoles = [], @@ -44,6 +54,7 @@ export function BodyAssignment({ width, }: { advanced: boolean; + mirror: boolean; onlyAssigned?: boolean; rolesWithErrors?: Partial>; highlightedRoles?: BodyPart[]; @@ -80,10 +91,14 @@ export function BodyAssignment({ [assignedTrackers] ); + const left = +!mirror; + const right = +mirror; + return ( <> onRoleSelected(BodyPart.LEFT_SHOULDER)} + roleError={rolesWithErrors[SIDES[left].shoulder]?.label} + td={trackerPartGrouped[SIDES[left].shoulder]} + role={SIDES[left].shoulder} + onClick={() => onRoleSelected(SIDES[left].shoulder)} direction="right" /> )} onRoleSelected(BodyPart.LEFT_UPPER_ARM)} + roleError={rolesWithErrors[SIDES[left].upperArm]?.label} + td={trackerPartGrouped[SIDES[left].upperArm]} + role={SIDES[left].upperArm} + onClick={() => onRoleSelected(SIDES[left].upperArm)} direction="right" />
onRoleSelected(BodyPart.LEFT_LOWER_ARM)} + roleError={rolesWithErrors[SIDES[left].lowerArm]?.label} + td={trackerPartGrouped[SIDES[left].lowerArm]} + role={SIDES[left].lowerArm} + onClick={() => onRoleSelected(SIDES[left].lowerArm)} direction="right" /> {advanced && ( onRoleSelected(BodyPart.LEFT_HAND)} + roleError={rolesWithErrors[SIDES[left].hand]?.label} + td={trackerPartGrouped[SIDES[left].hand]} + role={SIDES[left].hand} + onClick={() => onRoleSelected(SIDES[left].hand)} direction="right" /> )} @@ -156,27 +171,27 @@ export function BodyAssignment({
onRoleSelected(BodyPart.LEFT_UPPER_LEG)} + roleError={rolesWithErrors[SIDES[left].upperLeg]?.label} + td={trackerPartGrouped[SIDES[left].upperLeg]} + role={SIDES[left].upperLeg} + onClick={() => onRoleSelected(SIDES[left].upperLeg)} direction="right" /> onRoleSelected(BodyPart.LEFT_LOWER_LEG)} + roleError={rolesWithErrors[SIDES[left].lowerLeg]?.label} + td={trackerPartGrouped[SIDES[left].lowerLeg]} + role={SIDES[left].lowerLeg} + onClick={() => onRoleSelected(SIDES[left].lowerLeg)} direction="right" /> onRoleSelected(BodyPart.LEFT_FOOT)} + roleError={rolesWithErrors[SIDES[left].foot]?.label} + td={trackerPartGrouped[SIDES[left].foot]} + role={SIDES[left].foot} + onClick={() => onRoleSelected(SIDES[left].foot)} direction="right" />
@@ -208,20 +223,20 @@ export function BodyAssignment({ {advanced && ( onRoleSelected(BodyPart.RIGHT_SHOULDER)} + roleError={rolesWithErrors[SIDES[right].shoulder]?.label} + td={trackerPartGrouped[SIDES[right].shoulder]} + role={SIDES[right].shoulder} + onClick={() => onRoleSelected(SIDES[right].shoulder)} direction="left" /> )} onRoleSelected(BodyPart.RIGHT_UPPER_ARM)} + roleError={rolesWithErrors[SIDES[right].upperArm]?.label} + td={trackerPartGrouped[SIDES[right].upperArm]} + role={SIDES[right].upperArm} + onClick={() => onRoleSelected(SIDES[right].upperArm)} direction="left" />
@@ -248,19 +263,19 @@ export function BodyAssignment({
onRoleSelected(BodyPart.RIGHT_LOWER_ARM)} + roleError={rolesWithErrors[SIDES[right].lowerArm]?.label} + td={trackerPartGrouped[SIDES[right].lowerArm]} + role={SIDES[right].lowerArm} + onClick={() => onRoleSelected(SIDES[right].lowerArm)} direction="left" /> {advanced && ( onRoleSelected(BodyPart.RIGHT_HAND)} - role={BodyPart.RIGHT_HAND} + roleError={rolesWithErrors[SIDES[right].hand]?.label} + td={trackerPartGrouped[SIDES[right].hand]} + onClick={() => onRoleSelected(SIDES[right].hand)} + role={SIDES[right].hand} direction="left" /> )} @@ -269,27 +284,27 @@ export function BodyAssignment({
onRoleSelected(BodyPart.RIGHT_UPPER_LEG)} + roleError={rolesWithErrors[SIDES[right].upperLeg]?.label} + td={trackerPartGrouped[SIDES[right].upperLeg]} + role={SIDES[right].upperLeg} + onClick={() => onRoleSelected(SIDES[right].upperLeg)} direction="left" /> onRoleSelected(BodyPart.RIGHT_LOWER_LEG)} + roleError={rolesWithErrors[SIDES[right].lowerLeg]?.label} + td={trackerPartGrouped[SIDES[right].lowerLeg]} + role={SIDES[right].lowerLeg} + onClick={() => onRoleSelected(SIDES[right].lowerLeg)} direction="left" /> onRoleSelected(BodyPart.RIGHT_FOOT)} + roleError={rolesWithErrors[SIDES[right].foot]?.label} + td={trackerPartGrouped[SIDES[right].foot]} + role={SIDES[right].foot} + onClick={() => onRoleSelected(SIDES[right].foot)} direction="left" />
diff --git a/gui/src/components/onboarding/StepperSlider.tsx b/gui/src/components/onboarding/StepperSlider.tsx index db86d60e1d..8778bfc58b 100644 --- a/gui/src/components/onboarding/StepperSlider.tsx +++ b/gui/src/components/onboarding/StepperSlider.tsx @@ -17,6 +17,7 @@ type StepComponentType = FC<{ prevStep: () => void; resetSteps: () => void; variant: 'alone' | 'onboarding'; + active: boolean; }>; export type Step = { type: 'numbered' | 'fullsize'; @@ -160,6 +161,7 @@ export function StepperSlider({ nextStep={nextStep} prevStep={prevStep} resetSteps={resetSteps} + active={index === step} /> ))} diff --git a/gui/src/components/onboarding/pages/ConnectTracker.tsx b/gui/src/components/onboarding/pages/ConnectTracker.tsx index f00ec39f00..db547fc186 100644 --- a/gui/src/components/onboarding/pages/ConnectTracker.tsx +++ b/gui/src/components/onboarding/pages/ConnectTracker.tsx @@ -1,4 +1,4 @@ -import { useLocalization } from '@fluent/react'; +import { Localized, useLocalization } from '@fluent/react'; import classNames from 'classnames'; import { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -126,6 +126,14 @@ export function ConnectTrackersPage() { } }, [provisioningStatus]); + const currentTip = useMemo( + () => + connectedIMUTrackers.length > 0 + ? 'tips-find_tracker' + : 'tips-turn_on_tracker', + [connectedIMUTrackers.length] + ); + return (
@@ -156,7 +164,12 @@ export function ConnectTrackersPage() { {l10n.getString('onboarding-connect_tracker-issue-serial')}
- {l10n.getString('tips-find_tracker')} + , b: }} + > + Conditional tip +
+ v === undefined || + v.length === 0 || + new Blob([v]).size >= 8, + }, + }} name="password" type="password" label="Password" diff --git a/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx b/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx index e1147a9f1a..d142cb78ba 100644 --- a/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx +++ b/gui/src/components/onboarding/pages/body-proportions/ProportionsChoose.tsx @@ -159,13 +159,18 @@ export function ProportionsChoose() { {l10n.getString('onboarding-choose_proportions')}
- }} > - {l10n.getString('onboarding-choose_proportions-description')} - + + How to measure your body! + +
void; resetSteps: () => void; + active: boolean; }) { const { l10n } = useLocalization(); const { progress, hasCalibration, hasRecording, eta } = useAutobone(); @@ -35,8 +38,33 @@ export function Recording({ } }, [progress, hasCalibration, hasRecording]); + const videoRef = useRef(null); + const [paused, setPaused] = useState(true); + + function toggleVideo() { + if (!videoRef.current) return; + if (videoRef.current.paused) { + videoRef.current.play(); + } else { + videoRef.current.pause(); + videoRef.current.fastSeek(0); + } + setPaused(videoRef.current.paused); + } + + useEffect(() => { + if (!active && !paused) { + toggleVideo(); + return; + } + if (active && paused) { + toggleVideo(); + return; + } + }, [active]); + return ( -
+
{ @@ -44,75 +72,99 @@ export function Recording({ resetSteps(); }} > -
-
-
- - {l10n.getString('onboarding-automatic_proportions-recording-title')} +
+
+
+
+ + {l10n.getString( + 'onboarding-automatic_proportions-recording-title' + )} + +
+ + {l10n.getString( + 'onboarding-automatic_proportions-recording-description-p0' + )} + + + {l10n.getString( + 'onboarding-automatic_proportions-recording-description-p1' + )}
- - {l10n.getString( - 'onboarding-automatic_proportions-recording-description-p0' - )} - - - {l10n.getString( - 'onboarding-automatic_proportions-recording-description-p1' - )} - -
-
    - <> - {l10n - .getString('onboarding-automatic_proportions-recording-steps') - .split('\n') - .map((line, i) => ( -
  1. - {line} -
  2. - ))} - -
-
- {l10n.getString('tips-do_not_move_heels')} -
-
- () - .with( - P.union( - [ProcessStatus.REJECTED, P._], - [P._, ProcessStatus.REJECTED] - ), - () => 'bg-status-critical' - ) - .with( - [ProcessStatus.FULFILLED, ProcessStatus.FULFILLED], - () => 'bg-status-success' - ) - .otherwise(() => undefined)} - > - - {match([hasCalibration, hasRecording]) - .returnType() - .with([ProcessStatus.PENDING, ProcessStatus.FULFILLED], () => - l10n.getString( - 'onboarding-automatic_proportions-recording-processing' +
    + <> + {l10n + .getString('onboarding-automatic_proportions-recording-steps') + .split('\n') + .map((line, i) => ( +
  1. + {line} +
  2. + ))} + +
+
+ {l10n.getString('tips-do_not_move_heels')} +
+
+ () + .with( + P.union( + [ProcessStatus.REJECTED, P._], + [P._, ProcessStatus.REJECTED] + ), + () => 'bg-status-critical' + ) + .with( + [ProcessStatus.FULFILLED, ProcessStatus.FULFILLED], + () => 'bg-status-success' + ) + .otherwise(() => undefined)} + > + + {match([hasCalibration, hasRecording]) + .returnType() + .with([ProcessStatus.PENDING, ProcessStatus.FULFILLED], () => + l10n.getString( + 'onboarding-automatic_proportions-recording-processing' + ) ) - ) - .with([ProcessStatus.PENDING, ProcessStatus.PENDING], () => - l10n.getString( - 'onboarding-automatic_proportions-recording-timer', - { time: Math.round(eta) } + .with([ProcessStatus.PENDING, ProcessStatus.PENDING], () => + l10n.getString( + 'onboarding-automatic_proportions-recording-timer', + { time: Math.round(eta) } + ) ) - ) - .otherwise(() => '')} - + .otherwise(() => '')} + +
+
); } diff --git a/gui/src/components/onboarding/pages/body-proportions/autobone-steps/StartRecording.tsx b/gui/src/components/onboarding/pages/body-proportions/autobone-steps/StartRecording.tsx index 3e8ccd1f24..2a5a77020e 100644 --- a/gui/src/components/onboarding/pages/body-proportions/autobone-steps/StartRecording.tsx +++ b/gui/src/components/onboarding/pages/body-proportions/autobone-steps/StartRecording.tsx @@ -3,18 +3,39 @@ import { Button } from '@/components/commons/Button'; import { TipBox } from '@/components/commons/TipBox'; import { Typography } from '@/components/commons/Typography'; import { useLocalization } from '@fluent/react'; +import { useEffect, useRef, useState } from 'react'; +import { PlayCircleIcon } from '@/components/commons/icon/PlayIcon'; export function StartRecording({ nextStep, prevStep, variant, + active, }: { nextStep: () => void; prevStep: () => void; variant: 'onboarding' | 'alone'; + active: boolean; }) { const { l10n } = useLocalization(); const { startRecording } = useAutobone(); + const videoRef = useRef(null); + const [paused, setPaused] = useState(true); + + function toggleVideo() { + if (!videoRef.current) return; + if (videoRef.current.paused) { + videoRef.current.play(); + } else { + videoRef.current.pause(); + videoRef.current.fastSeek(0); + } + setPaused(videoRef.current.paused); + } + + useEffect(() => { + if (!active && !paused) toggleVideo(); + }, [active]); const start = () => { nextStep(); @@ -24,34 +45,59 @@ export function StartRecording({ return ( <>
-
- - {l10n.getString( - 'onboarding-automatic_proportions-start_recording-title' - )} - -
- +
+
+ {l10n.getString( - 'onboarding-automatic_proportions-start_recording-description' + 'onboarding-automatic_proportions-start_recording-title' )} +
+ + {l10n.getString( + 'onboarding-automatic_proportions-start_recording-description' + )} + +
+
    + <> + {l10n + .getString('onboarding-automatic_proportions-recording-steps') + .split('\n') + .map((line, i) => ( +
  1. + {line} +
  2. + ))} + +
+
+ {l10n.getString('tips-do_not_move_heels')} +
-
    - <> - {l10n - .getString('onboarding-automatic_proportions-recording-steps') - .split('\n') - .map((line, i) => ( -
  1. - {line} -
  2. - ))} - -
-
- {l10n.getString('tips-do_not_move_heels')} -
+
diff --git a/gui/src/components/onboarding/pages/mounting/ManualMounting.tsx b/gui/src/components/onboarding/pages/mounting/ManualMounting.tsx index 8b6ac657bc..78287a7676 100644 --- a/gui/src/components/onboarding/pages/mounting/ManualMounting.tsx +++ b/gui/src/components/onboarding/pages/mounting/ManualMounting.tsx @@ -13,14 +13,14 @@ import { MountingSelectionMenu } from './MountingSelectionMenu'; import { useLocalization } from '@fluent/react'; import { useBreakpoint } from '@/hooks/breakpoint'; import { Quaternion } from 'three'; -import { useConfig } from '@/hooks/config'; +import { defaultConfig, useConfig } from '@/hooks/config'; export function ManualMountingPage() { const { isMobile } = useBreakpoint('mobile'); const { l10n } = useLocalization(); const { applyProgress, state } = useOnboarding(); const { sendRPCPacket } = useWebsocketAPI(); - const { setConfig } = useConfig(); + const { setConfig, config } = useConfig(); const [selectedRole, setSelectRole] = useState(BodyPart.NONE); @@ -102,6 +102,7 @@ export function ManualMountingPage() {
({ - defaultValues: { advanced: config?.advancedAssign ?? false }, + const { control, watch } = useForm<{ + advanced: boolean; + mirrorView: boolean; + }>({ + defaultValues: { + advanced: config?.advancedAssign ?? false, + mirrorView: config?.mirrorView ?? true, + }, }); - const { advanced } = watch(); + const { advanced, mirrorView } = watch(); const [selectedRole, setSelectRole] = useState(BodyPart.NONE); const assignedTrackers = useAssignedTrackers(); useEffect(() => { - setConfig({ advancedAssign: advanced }); - }, [advanced]); + setConfig({ advancedAssign: advanced, mirrorView }); + }, [advanced, mirrorView]); const [tapDetectionSettings, setTapDetectionSettings] = useState LOWER_BODY.has(t)) + ) { + return; + } + if (unassignedRoles.every(([, state]) => state)) return; return { @@ -277,12 +292,22 @@ export function TrackersAssignPage() {
{l10n.getString('tips-find_tracker')} - +
+ + +
{!!firstError && (
@@ -320,6 +345,7 @@ export function TrackersAssignPage() { highlightedRoles={firstError?.affectedRoles || []} rolesWithErrors={rolesWithErrors} advanced={advanced} + mirror={mirrorView} onRoleSelected={tryOpenChokerWarning} >
diff --git a/gui/src/components/tracker/SingleTrackerBodyAssignmentMenu.tsx b/gui/src/components/tracker/SingleTrackerBodyAssignmentMenu.tsx index e90734bdd2..3a6cd374cb 100644 --- a/gui/src/components/tracker/SingleTrackerBodyAssignmentMenu.tsx +++ b/gui/src/components/tracker/SingleTrackerBodyAssignmentMenu.tsx @@ -10,6 +10,7 @@ import { useLocalization } from '@fluent/react'; import { NeckWarningModal } from '@/components/onboarding/NeckWarningModal'; import { useChokerWarning } from '@/hooks/choker-warning'; import { useBreakpoint } from '@/hooks/breakpoint'; +import { defaultConfig, useConfig } from '@/hooks/config'; export function SingleTrackerBodyAssignmentMenu({ isOpen, @@ -22,6 +23,7 @@ export function SingleTrackerBodyAssignmentMenu({ }) { const { isMobile } = useBreakpoint('mobile'); const { l10n } = useLocalization(); + const { config } = useConfig(); const { control, watch } = useForm<{ advanced: boolean }>({ defaultValues: { advanced: false }, }); @@ -75,6 +77,7 @@ export function SingleTrackerBodyAssignmentMenu({
{ + const state = localStorage.getItem('skeletonModelPreview'); + if (state) setEnabled(state === 'true'); + }, []); + return ( <> {!enabled && ( @@ -96,7 +101,7 @@ export function ToggleableSkeletonVisualizerWidget( className="w-full" onClick={() => { setEnabled(true); - localStorage.setItem('modelPreview', 'true'); + localStorage.setItem('skeletonModelPreview', 'true'); }} > {l10n.getString('widget-skeleton_visualizer-preview')} @@ -109,7 +114,7 @@ export function ToggleableSkeletonVisualizerWidget( variant="secondary" onClick={() => { setEnabled(false); - localStorage.setItem('modelPreview', 'false'); + localStorage.setItem('skeletonModelPreview', 'false'); }} > {l10n.getString('widget-skeleton_visualizer-hide')} diff --git a/gui/src/hooks/config.ts b/gui/src/hooks/config.ts index 58008e627f..63246d4df1 100644 --- a/gui/src/hooks/config.ts +++ b/gui/src/hooks/config.ts @@ -28,6 +28,7 @@ export interface Config { advancedAssign: boolean; useTray: boolean | null; doneManualMounting: boolean; + mirrorView: boolean; } export interface ConfigContext { @@ -51,6 +52,7 @@ export const defaultConfig: Omit = { advancedAssign: false, useTray: null, doneManualMounting: false, + mirrorView: true, }; function fallbackToDefaults(loadedConfig: any): Config { diff --git a/gui/src/hooks/wifi-form.tsx b/gui/src/hooks/wifi-form.tsx index f1f616be64..28a24f9426 100644 --- a/gui/src/hooks/wifi-form.tsx +++ b/gui/src/hooks/wifi-form.tsx @@ -5,7 +5,7 @@ import { useOnboarding } from './onboarding'; export interface WifiFormData { ssid: string; - password: string; + password?: string; } export function useWifiForm() { @@ -27,7 +27,7 @@ export function useWifiForm() { }, []); const submitWifiCreds = (value: WifiFormData) => { - setWifiCredentials(value.ssid, value.password); + setWifiCredentials(value.ssid, value.password ?? ''); navigate('/onboarding/connect-trackers', { state: { alonePage: state.alonePage }, }); diff --git a/server/core/src/main/java/io/eiren/util/OperatingSystem.kt b/server/core/src/main/java/io/eiren/util/OperatingSystem.kt index 0b0a66fd70..b93d8edac1 100644 --- a/server/core/src/main/java/io/eiren/util/OperatingSystem.kt +++ b/server/core/src/main/java/io/eiren/util/OperatingSystem.kt @@ -52,8 +52,8 @@ enum class OperatingSystem( } fun resolveLogDirectory(identifier: String): Path? = when (currentPlatform) { - LINUX -> System.getenv("XDG_CONFIG_HOME")?.let { Path(it, identifier, "logs") } - ?: System.getenv("HOME")?.let { Path(it, ".config", identifier, "logs") } + LINUX -> System.getenv("XDG_DATA_HOME")?.let { Path(it, identifier, "logs") } + ?: System.getenv("HOME")?.let { Path(it, ".local", "share", identifier, "logs") } WINDOWS -> System.getenv("AppData")?.let { Path(it, identifier, "logs") } OSX -> System.getenv("HOME")?.let { Path(it, "Library", "Logs", identifier) } UNKNOWN -> null