diff --git a/gui/.env b/gui/.env index 7ce15648f..8ecda1579 100644 --- a/gui/.env +++ b/gui/.env @@ -1,8 +1,8 @@ -VITE_FIRMWARE_TOOL_URL=https://fw-tool-api.slimevr.io -VITE_FIRMWARE_TOOL_S3_URL=https://fw-tool-bucket.slimevr.io -FIRMWARE_TOOL_SCHEMA_URL=https://fw-tool-api.slimevr.io/api-json +VITE_FIRMWARE_TOOL_URL=https://fw-tool-api-v2.slimevr.io +VITE_FIRMWARE_TOOL_S3_URL=https://fw-tool-bucket-v2.slimevr.io +FIRMWARE_TOOL_SCHEMA_URL=https://fw-tool-api-v2.slimevr.io/api-json # VITE_FIRMWARE_TOOL_URL=http://localhost:3000 -# VITE_FIRMWARE_TOOL_S3_URL=http://localhost:9000 +# VITE_FIRMWARE_TOOL_S3_URL=http://localhost:9099 # FIRMWARE_TOOL_SCHEMA_URL=http://localhost:3000/api-json diff --git a/gui/package.json b/gui/package.json index 9db664f6b..50f28709b 100644 --- a/gui/package.json +++ b/gui/package.json @@ -27,6 +27,7 @@ "@tauri-apps/plugin-store": "~2", "@tweenjs/tween.js": "^25.0.0", "@twemoji/svg": "^15.0.0", + "ajv": "^8.17.1", "browser-fs-access": "^0.35.0", "classnames": "^2.5.1", "flatbuffers": "22.10.26", diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index 9e2c10b74..c3954e9cd 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -89,6 +89,8 @@ board_type-WEMOSD1MINI = Wemos D1 Mini board_type-TTGO_TBASE = TTGO T-Base board_type-ESP01 = ESP-01 board_type-SLIMEVR = SlimeVR +board_type-SLIMEVR_DEV = SlimeVR Dev Board +board_type-SLIMEVR_V1_2 = SlimeVR v1.2 board_type-LOLIN_C3_MINI = Lolin C3 Mini board_type-BEETLE32C3 = Beetle ESP32-C3 board_type-ESP32C3DEVKITM1 = Espressif ESP32-C3 DevKitM-1 @@ -383,7 +385,8 @@ tracker-settings-name_section-label = Tracker name tracker-settings-forget = Forget tracker tracker-settings-forget-description = Removes the tracker from the SlimeVR Server and prevents it from connecting until the server is restarted. The configuration of the tracker won't be lost. tracker-settings-forget-label = Forget tracker -tracker-settings-update-unavailable = Cannot be updated (DIY) +tracker-settings-update-unavailable-v2 = No releases found +tracker-settings-update-incompatible = Cannot update. Incompatible board tracker-settings-update-low-battery = Cannot update. Battery lower than 50% tracker-settings-update-up_to_date = Up to date tracker-settings-update-blocked = Update not available. No other releases available @@ -1334,78 +1337,38 @@ firmware_tool-description = firmware_tool-not_available = Oops, the firmware tool is not available at the moment. Come back later! firmware_tool-not_compatible = The firmware tool is not compatible with this version of the server. Please update your server! -firmware_tool-board_step = Select your Board -firmware_tool-board_step-description = Select one of the boards listed below. +firmware_tool-select_source = Select the firmware to flash +firmware_tool-select_source-description = Select the firmware you want to flash on your board +firmware_tool-select_source-error = Unable to load Sources +firmware_tool-select_source-board_type = Board Type +firmware_tool-select_source-firmware = Firmware Source +firmware_tool-select_source-version = Firmware Version +firmware_tool-select_source-official = Official +firmware_tool-select_source-dev = Dev -firmware_tool-board_pins_step = Check the pins -firmware_tool-board_pins_step-description = - Please verify that the selected pins are correct. - If you followed the SlimeVR documentation, the default values should be correct. -firmware_tool-board_pins_step-enable_led = Enable LED -firmware_tool-board_pins_step-led_pin = - .label = LED Pin - .placeholder = Enter the pin address of the LED - -firmware_tool-board_pins_step-battery_type = Select the battery type -firmware_tool-board_pins_step-battery_type-BAT_EXTERNAL = External battery -firmware_tool-board_pins_step-battery_type-BAT_INTERNAL = Internal battery -firmware_tool-board_pins_step-battery_type-BAT_INTERNAL_MCP3021 = Internal MCP3021 -firmware_tool-board_pins_step-battery_type-BAT_MCP3021 = MCP3021 - - -firmware_tool-board_pins_step-battery_sensor_pin = - .label = Battery sensor Pin - .placeholder = Enter the pin address of battery sensor -firmware_tool-board_pins_step-battery_resistor = - .label = Battery Resistor (Ohms) - .placeholder = Enter the value of battery resistor -firmware_tool-board_pins_step-battery_shield_resistor-0 = - .label = Battery Shield R1 (Ohms) - .placeholder = Enter the value of Battery Shield R1 -firmware_tool-board_pins_step-battery_shield_resistor-1 = - .label = Battery Shield R2 (Ohms) - .placeholder = Enter the value of Battery Shield R2 - -firmware_tool-add_imus_step = Declare your IMUs -firmware_tool-add_imus_step-description = - Please add the IMUs that your tracker has. - If you followed the SlimeVR documentation, the default values should be correct. -firmware_tool-add_imus_step-imu_type-label = IMU type -firmware_tool-add_imus_step-imu_type-placeholder = Select the type of IMU -firmware_tool-add_imus_step-imu_rotation = - .label = IMU Rotation (deg) - .placeholder = Rotation angle of the IMU -firmware_tool-add_imus_step-scl_pin = - .label = SCL Pin - .placeholder = Pin address of SCL -firmware_tool-add_imus_step-sda_pin = - .label = SDA Pin - .placeholder = Pin address of SDA -firmware_tool-add_imus_step-int_pin = - .label = INT Pin - .placeholder = Pin address of INT -firmware_tool-add_imus_step-optional_tracker = - .label = Optional tracker -firmware_tool-add_imus_step-show_less = Show Less -firmware_tool-add_imus_step-show_more = Show More -firmware_tool-add_imus_step-add_more = Add more IMUs - -firmware_tool-select_firmware_step = Select the firmware version -firmware_tool-select_firmware_step-description = - Please choose what version of the firmware you want to use -firmware_tool-select_firmware_step-show-third-party = - .label = Show third party firmwares +firmware_tool-board_defaults = Configure your board +firmware_tool-board_defaults-description = Set the pins or settings relative to your hardware +firmware_tool-board_defaults-add = Add +firmware_tool-board_defaults-reset = Reset to Default +firmware_tool-board_defaults-error-required = Required field +firmware_tool-board_defaults-error-format = Invalid format +firmware_tool-board_defaults-error-format-number = Not a number firmware_tool-flash_method_step = Flashing Method firmware_tool-flash_method_step-description = Please select the flashing method you want to use -firmware_tool-flash_method_step-ota = - .label = OTA + +firmware_tool-flash_method_step-ota-v2 = + .label = Wi-Fi .description = Use the over-the-air method. Your tracker will use Wi-Fi to update its firmware. Only works on trackers that have been set up. -firmware_tool-flash_method_step-serial = - .label = Serial +firmware_tool-flash_method_step-ota-info = + We use your wifi credentials to flash the tracker and confirm that everything worked correctly. + We do not store your wifi credentials! +firmware_tool-flash_method_step-serial-v2 = + .label = USB .description = Use a USB cable to update your tracker. + firmware_tool-flashbtn_step = Press the boot btn firmware_tool-flashbtn_step-description = Before going to the next step, there are a few things you need to do @@ -1419,10 +1382,10 @@ firmware_tool-flashbtn_step-board_OTHER = Before flashing, you will probably nee If the flashing process times out at the start, it probably means that the tracker was not in bootloader mode. Refer to your board's flashing instructions to learn how to enter bootloader mode. - - +firmware_tool-flash_method_ota-title = Flashing over Wi-Fi firmware_tool-flash_method_ota-devices = Detected OTA Devices: firmware_tool-flash_method_ota-no_devices = There are no boards that can be updated using OTA, make sure you selected the correct board type +firmware_tool-flash_method_serial-title = Flashing over USB firmware_tool-flash_method_serial-wifi = Wi-Fi Credentials: firmware_tool-flash_method_serial-devices-label = Detected Serial Devices: firmware_tool-flash_method_serial-devices-placeholder = Select a serial device @@ -1440,10 +1403,10 @@ firmware_tool-flashing_step-flash_more = Flash more trackers firmware_tool-flashing_step-exit = Exit ## firmware tool build status +firmware_tool-build-QUEUED = Waiting to build.... firmware_tool-build-CREATING_BUILD_FOLDER = Creating the build folder -firmware_tool-build-DOWNLOADING_FIRMWARE = Downloading the firmware -firmware_tool-build-EXTRACTING_FIRMWARE = Extracting the firmware -firmware_tool-build-SETTING_UP_DEFINES = Configuring the defines +firmware_tool-build-DOWNLOADING_SOURCE = Downloading the source code +firmware_tool-build-EXTRACTING_SOURCE = Extracting the source code firmware_tool-build-BUILDING = Building the firmware firmware_tool-build-SAVING = Saving the build firmware_tool-build-DONE = Build Complete diff --git a/gui/src/components/commons/Checkbox.tsx b/gui/src/components/commons/Checkbox.tsx index c3adbd580..152cd8623 100644 --- a/gui/src/components/commons/Checkbox.tsx +++ b/gui/src/components/commons/Checkbox.tsx @@ -1,31 +1,36 @@ import classNames from 'classnames'; -import { useMemo } from 'react'; +import { forwardRef, useMemo } from 'react'; import { Control, Controller } from 'react-hook-form'; export const CHECKBOX_CLASSES = classNames( 'bg-background-50 border-background-50 rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent' ); -export function CheckBox({ - label, - variant = 'checkbox', - color = 'primary', - control, - outlined, - name, - loading, - // input props - disabled, - ...props -}: { - label: string; - control: Control; - name: string; - variant?: 'checkbox' | 'toggle'; - color?: 'primary' | 'secondary' | 'tertiary'; - outlined?: boolean; - loading?: boolean; -} & React.HTMLProps) { +export const CheckboxInternal = forwardRef< + HTMLInputElement, + { + disabled?: boolean; + variant?: 'checkbox' | 'toggle'; + color?: 'primary' | 'secondary' | 'tertiary'; + label?: string; + outlined?: boolean; + loading?: boolean; + name: string; + } & Partial> +>(function AppCheckbox( + { + variant = 'checkbox', + color = 'primary', + outlined = false, + loading = false, + disabled = false, + label, + onChange, + checked, + name, + }, + ref +) { const classes = useMemo(() => { const vriantsMap = { checkbox: { @@ -44,68 +49,102 @@ export function CheckBox({ return vriantsMap[variant]; }, [variant]); + return ( +
+ +
+ ); +}); + +export function CheckBox({ + label, + variant = 'checkbox', + color = 'primary', + control, + outlined, + name, + loading, + disabled, +}: { + label: string; + control: Control; + name: string; + variant?: 'checkbox' | 'toggle'; + color?: 'primary' | 'secondary' | 'tertiary'; + outlined?: boolean; + loading?: boolean; +} & React.HTMLProps) { return ( ( -
- -
+ )} /> ); diff --git a/gui/src/components/commons/Dropdown.tsx b/gui/src/components/commons/Dropdown.tsx index ec6c5e8d1..65dabbb7c 100644 --- a/gui/src/components/commons/Dropdown.tsx +++ b/gui/src/components/commons/Dropdown.tsx @@ -1,6 +1,11 @@ import classNames from 'classnames'; import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react'; -import { Control, Controller, UseControllerProps } from 'react-hook-form'; +import { + Control, + Controller, + FieldError, + UseControllerProps, +} from 'react-hook-form'; import { a11yClick } from '@/utils/a11y'; import { createPortal } from 'react-dom'; import { ArrowDownIcon } from './icon/ArrowIcons'; @@ -11,11 +16,10 @@ interface DropdownProps { alignment?: 'right' | 'left'; display?: 'fit' | 'block'; placeholder: string; - control: Control; name: string; items: DropdownItem[]; maxHeight?: string | number; - rules?: UseControllerProps['rules']; + error?: FieldError; } type DropdownItemsProps = Pick< @@ -129,18 +133,22 @@ export function DropdownItems({ ); } -export function Dropdown({ +export function DropdownInside({ direction = 'up', variant = 'primary', alignment = 'right', display = 'fit', maxHeight = '50vh', placeholder, - control, - rules, - name, items = [], -}: DropdownProps) { + value, + onChange, + error, +}: DropdownProps & { + name: string; + value: string; + onChange: (v: string) => void; +}) { const ref = useRef(null); const [isOpen, setOpenState] = useState(false); const [dropdownBounds, setDropdownBounds] = useState(); @@ -171,69 +179,93 @@ export function Dropdown({ updateBounds(); setOpenState(open); }; + return ( + <> +
+
setOpen((open) => !open)} + onKeyDown={(ev) => a11yClick(ev) && setOpen((open) => !open)} + tabIndex={0} + > +
+ {items.find((i) => i.value == value)?.component || + items.find((i) => i.value == value)?.label || + placeholder} +
+
+ +
+
+ {error?.message && ( +
+ {error.message} +
+ )} +
+ {isOpen && + dropdownBounds && + createPortal( + { + setOpen(false); + onChange(item.value); + }} + onBackdropClick={() => { + setOpen(false); + }} + >, + document.body + )} + + ); +} +export function Dropdown({ + name, + control, + rules, + ...props +}: DropdownProps & { + control: Control; + rules?: UseControllerProps['rules']; +}) { return ( ( - <> -
setOpen((open) => !open)} - onKeyDown={(ev) => a11yClick(ev) && setOpen((open) => !open)} - tabIndex={0} - > -
- {items.find((i) => i.value == value)?.component || - items.find((i) => i.value == value)?.label || - placeholder} -
-
- -
-
- {isOpen && - dropdownBounds && - createPortal( - { - setOpen(false); - onChange(item.value); - }} - onBackdropClick={() => { - setOpen(false); - }} - >, - document.body - )} - + )} /> ); diff --git a/gui/src/components/commons/Input.tsx b/gui/src/components/commons/Input.tsx index 1a33b3765..00637f064 100644 --- a/gui/src/components/commons/Input.tsx +++ b/gui/src/components/commons/Input.tsx @@ -7,6 +7,7 @@ import { UseControllerProps, } from 'react-hook-form'; import { EyeIcon } from './icon/EyeIcon'; +import { Typography } from './Typography'; interface InputProps { variant?: 'primary' | 'secondary' | 'tertiary'; @@ -20,8 +21,8 @@ export const InputInside = forwardRef< variant?: 'primary' | 'secondary' | 'tertiary'; label?: string; error?: FieldError; - onChange: () => void; - } & Partial + autocomplete?: boolean | string; + } & Partial> >(function AppInput( { type, @@ -70,7 +71,7 @@ export const InputInside = forwardRef< variantsMap[variant], 'w-full focus:ring-transparent focus:ring-offset-transparent min-h-[42px] z-10', 'focus:outline-transparent rounded-md focus:border-accent-background-40', - 'text-standard relative transition-colors', + 'text-standard text-background-10 relative transition-colors', error && 'border-status-critical border-1' ); }, [variant, disabled, error]); @@ -83,7 +84,7 @@ export const InputInside = forwardRef< return (