Compare commits

...

60 Commits

Author SHA1 Message Date
gorbit99
61065eba7f Apply round of suggestions 2025-09-30 22:48:51 +02:00
gorbit99
0e750fd1de Merge branch 'main' into tracker-graph 2025-09-30 01:30:32 +02:00
gorbit99
7aabdbe724 Add tracker graph to tracker settings page 2025-09-30 01:25:44 +02:00
Eiren Rain
de16acbcf4 New Pontoon translations (#1512) 2025-09-30 00:24:22 +03:00
Erimel
486cb295ac Pontoon: Update French (fr) localization of GUI
Co-Authored-By: Erimel <loukalemire@gmail.com>
Co-Authored-By: SlimePuppy <minouviolet2007+github@gmail.com>
2025-09-23 21:00:09 +00:00
nekomona
dd3fe24294 Pontoon: Update Simplified Chinese (zh-Hans) localization of GUI
Co-authored-by: nekomona <nekomona@163.com>
2025-09-23 21:00:09 +00:00
Alejandro
59bdedecbb Pontoon: Update Latinamerican Spanish (es-419) localization of GUI
Co-authored-by: Alejandro <moctezumaalejandro25@gmail.com>
2025-09-23 21:00:09 +00:00
YumeTomo
fc3d049019 Pontoon: Update Thai (th) localization of GUI
Co-Authored-By: YumeTomo <Sodnoobe@gmail.com>
Co-Authored-By: Cusmo84 <saopob@gmail.com>
Co-Authored-By: Kaiera huzu <patsakorn9090@gmail.com>
2025-09-23 21:00:09 +00:00
ReDoX
de150e7349 Pontoon: Update Polish (pl) localization of GUI
Co-authored-by: ReDoX <redox01@o2.pl>
2025-09-23 21:00:09 +00:00
Meow Wei
06b3f61d37 Pontoon: Update Traditional Chinese (zh-Hant) localization of GUI
Co-authored-by: Meow Wei <medicalwei@gmail.com>
2025-09-23 21:00:09 +00:00
Bader
82b96d5ebe Pontoon: Update Arabic (ar) localization of GUI
Co-Authored-By: Bader <baq100@gmail.com>
Co-Authored-By: FennT <0094falcon@gmail.com>
2025-09-23 21:00:09 +00:00
Aed
f2b4d468c2 Added Done button to end of mounting calibration (#1557) 2025-09-23 22:59:22 +02:00
Eiren Rain
1c0f5c381b Update CODEOWNERS (#1558) 2025-09-23 23:54:01 +03:00
lucas lelievre
fb25421ab0 OOPS 2025-09-23 20:23:51 +00:00
lucas lelievre
4890b4a71c Update CODEOWNERS 2025-09-23 22:17:57 +02:00
lucas lelievre
e38732a81c Remove secondary colors usage + improve spacing (#1554) 2025-09-22 21:27:07 +02:00
Eiren Rain
2a78354b17 Disable update prompt on Android (#1547) 2025-09-19 19:55:27 +03:00
Eiren Rain
3fb4347277 Fix Stagered fw update detection (#1544) 2025-09-19 19:55:17 +03:00
Eiren Rain
081e88ead4 Update gui/src/hooks/firmware-update.ts
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2025-09-18 15:34:56 +03:00
Uriel
73f41f8fc6 Disable update prompt on Android 2025-09-17 19:32:30 -03:00
loucass003
f233f59079 Fix Stagered fw update detection 2025-09-16 23:24:53 +02:00
Eiren Rain
9644b00690 fix: use transaction id in RPC reponses (#1527) 2025-09-16 18:54:12 +03:00
Eiren Rain
543e319c25 HID support on Android (#1532) 2025-09-16 14:57:24 +03:00
Eiren Rain
b7ef70f5c6 Bump actions/setup-java from 4 to 5 (#1522) 2025-09-16 14:25:42 +03:00
Eiren Rain
af171f8812 Fix Android serial support (#1533) 2025-09-16 14:25:11 +03:00
dependabot[bot]
8ec3cade06 Bump actions/setup-java from 4 to 5
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-16 11:23:12 +00:00
Eiren Rain
bec8be46ca Add folder icon option for the file input (#1536) 2025-09-16 14:22:47 +03:00
Eiren Rain
ac7f809132 Bump actions/setup-node from 4 to 5 (#1540) 2025-09-16 14:22:10 +03:00
Eiren Rain
fcb241fab8 Bump actions/labeler from 5 to 6 (#1541) 2025-09-16 14:21:55 +03:00
Eiren Rain
fbdcf2fa2d Race condition patch for constraints (#1535) 2025-09-16 14:21:13 +03:00
Butterscotch!
73c821e07b Merge remote-tracking branch 'upstream/main' into bscotch/android-serial-fix 2025-09-11 16:07:57 -04:00
Butterscotch!
422ddd7ee8 Write serial directly on Android 2025-09-11 16:07:49 -04:00
Butterscotch!
943ad974ec Limit Android serial buffer to 1024 chars 2025-09-11 16:03:57 -04:00
dependabot[bot]
12a5d59e89 Bump actions/labeler from 5 to 6
Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 14:22:53 +00:00
dependabot[bot]
85286651dd Bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 14:22:49 +00:00
Uriel
e9b3efe3d5 Add folder icon option for the file input 2025-09-05 14:13:54 -04:00
Butterscotch!
75fa1aa1e2 Messy race condition patch for constraints 2025-09-04 06:50:34 -04:00
Butterscotch!
7d642a21f5 Use a read buffer for Android serial
The serial reader on Android does not have a big enough buffer for our serial output, causing issues when parsing it. We can buffer until we reach a newline character to make parsing work.
2025-09-04 06:35:30 -04:00
Butterscotch!
4e1421180c Ensure serial port closure 2025-09-04 04:33:39 -04:00
Butterscotch!
f0a72645d7 Clear request more & update usbReceiver comment 2025-09-04 04:27:20 -04:00
Butterscotch!
86b8e0a904 Fix serial staying open after disconnect 2025-09-04 04:18:52 -04:00
Butterscotch!
789e6a6962 Make ACTION_USB_PERMISSION const 2025-09-04 02:37:52 -04:00
Butterscotch!
f03b300d72 Use val for usbReceiver 2025-09-04 02:26:36 -04:00
Butterscotch!
6b0822c0f6 Rename HID "Service" to "Manager" 2025-09-04 02:26:08 -04:00
Butterscotch!
d9774cab87 Extra requestingPermission handling for new device 2025-09-04 02:24:21 -04:00
Butterscotch!
f07f9f3718 Add comments for USB attach/detach events 2025-09-04 02:22:23 -04:00
Butterscotch!
486be0973b Use USB events for serial & re-request permission 2025-09-04 02:17:28 -04:00
Butterscotch!
9246dd00d3 Additional comments 2025-09-04 01:41:01 -04:00
Butterscotch!
baf515791d Fix HID service comments 2025-09-04 01:37:15 -04:00
Butterscotch!
876450d764 Make Android USB host mode optional 2025-09-04 01:34:22 -04:00
Butterscotch!
44e90e255b Rename HID classes for clarity 2025-09-04 01:34:06 -04:00
Butterscotch!
41026ab851 Move deviceIdLookup to HIDCommon 2025-09-04 01:28:26 -04:00
Butterscotch!
d8509c431d Remove device reattach handling
Events should be good enough :3
2025-09-04 01:05:35 -04:00
Butterscotch!
66df65eb80 Use Android USB events for HID instead of polling 2025-09-04 00:31:43 -04:00
Butterscotch!
30641a5809 Reconnect HID 2025-09-04 00:31:43 -04:00
Butterscotch!
574523daec Persist UsbDeviceConnection 2025-09-04 00:31:43 -04:00
Butterscotch!
1913605d16 De-duplicate HID code 2025-09-04 00:31:42 -04:00
Butterscotch!
8aeecab51f Implement awful reading 2025-09-04 00:29:45 -04:00
Butterscotch!
9fd5d6a187 Initial Android USB HID support 2025-09-04 00:29:45 -04:00
peelz
0c09c22306 fix: use transaction id in RPC reponses 2025-09-01 13:46:48 -04:00
108 changed files with 2038 additions and 1198 deletions

32
.github/CODEOWNERS vendored
View File

@@ -2,23 +2,23 @@
* @Eirenliel
# Make everyone be able to approve SolarXR submodule changes
/solarxr-protocol @ButterscotchV @Erimelowo @ImUrX @loucass003
/solarxr-protocol @ButterscotchV @Erimelowo @loucass003
# Make Loucas and Uriel the owners of all GUI stuff
/gui/ @ImUrX @loucass003
/pnpm-lock.yaml @ImUrX @loucass003
/pnpm-workspace.yaml @ImUrX @loucass003
# Make Loucass the owner of all GUI stuff
/gui/ @loucass003
/pnpm-lock.yaml @loucass003
/pnpm-workspace.yaml @loucass003
# Uriel and Erimel responsible for i18n
/gui/public/i18n/ @ImUrX @Erimelowo
/gui/src/i18n/ @ImUrX @Erimelowo
/l10n.toml @ImUrX @Erimelowo
# loucass003 and Erimel responsible for i18n
/gui/public/i18n/ @loucass003 @Erimelowo
/gui/src/i18n/ @loucass003 @Erimelowo
/l10n.toml @loucass003 @Erimelowo
/gui/src/components/settings/ @Erimelowo @ImUrX @loucass003
/gui/src/components/settings/ @Erimelowo @loucass003
# Rust part of the GUI
/gui/src-tauri/ @ImUrX
/Cargo.lock @ImUrX
/gui/src-tauri/ @loucass003
/Cargo.lock @loucass003
# Some server code~
/server/ @ButterscotchV @Eirenliel @Erimelowo
@@ -32,7 +32,7 @@
/server/src/main/java/dev/slimevr/filtering/ @Erimelowo
# Linux files
*.nix @ImUrX
/flake.lock @ImUrX
/dev.slimevr.SlimeVR.metainfo.xml @ImUrX
/.envrc @ImUrX
*.nix @loucass003
/flake.lock @loucass003
/dev.slimevr.SlimeVR.metainfo.xml @loucass003
/.envrc @loucass003

View File

@@ -27,7 +27,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -85,7 +85,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: '22.x'

View File

@@ -27,7 +27,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -56,7 +56,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -95,7 +95,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -105,7 +105,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -190,7 +190,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -266,7 +266,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -351,7 +351,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -17,6 +17,6 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v6
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -26,6 +26,7 @@
"@tweenjs/tween.js": "^25.0.0",
"@twemoji/svg": "^15.0.0",
"browser-fs-access": "^0.35.0",
"chart.js": "^4.5.0",
"classnames": "^2.5.1",
"flatbuffers": "22.10.26",
"intl-pluralrules": "^2.0.1",
@@ -33,6 +34,7 @@
"jotai": "^2.12.2",
"prompts": "^2.4.2",
"react": "^18.3.1",
"react-chartjs-2": "^5.3.0",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
"react-helmet": "^6.1.0",

View File

@@ -497,8 +497,6 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = يمكن أن ي
settings-general-fk_settings-leg_tweak-toe_snap-description = الانجذاب إلى أصابع القدم يحاول تخمين دوران قدميك إذا لم تكن أجهزة تعقب القدم قيد الاستخدام.
settings-general-fk_settings-leg_tweak-foot_plant-description = تثبيت اصبع القدم يحاول تخمين دوران قدميك إذا لم تكن أجهزة تعقب القدم قيد الاستخدام.
settings-general-fk_settings-leg_fk = تعقب الساق
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = تمكين إعادة ضبط تركيب القدمين عن طريق المشي على رؤوس الأصابع.
settings-general-fk_settings-leg_fk-reset_mounting_feet = إعادة تعيين تركيب القدمين
settings-general-fk_settings-enforce_joint_constraints = حدود الهيكل العظمي
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = فرض القيود
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = منع المفاصل من الدوران إلى ما بعد الحد الأقصى
@@ -637,6 +635,14 @@ settings-general-interface-discord_presence-message =
[many] كثيرة
*[other] أخرى
}
settings-interface-behavior-error_tracking = جمع الأخطاء عبر Sentry.io
settings-interface-behavior-error_tracking-description_v2 =
<h1>هل توافق على جمع بيانات الخطأ مجهولة المصدر؟</h1>
<b>نحن لا نجمع معلومات شخصية</b> مثل عنوان IP الخاص بك أو بيانات الاعتماد اللاسلكية. يقدر SlimeVR خصوصيتك!
لتوفير أفضل تجربة للمستخدم، نقوم بجمع تقارير الأخطاء ومقاييس الأداء ومعلومات نظام التشغيل مجهولة المصدر. يساعدنا هذا في اكتشاف الأخطاء والمشكلات المتعلقة ب SlimeVR. يتم جمع هذه المقاييس عبر Sentry.io.
settings-interface-behavior-error_tracking-label = إرسال الأخطاء إلى المطورين
## Serial settings
@@ -659,6 +665,8 @@ settings-serial-get_infos = احصل على معلومات
settings-serial-serial_select = اختر منفذ تسلسلي
settings-serial-auto_dropdown_item = تلقائي
settings-serial-get_wifi_scan = احصل على فحص WiFi
settings-serial-file_type = نص عادي
settings-serial-save_logs = حفظ في ملف
## OSC router settings
@@ -688,9 +696,21 @@ settings-osc-router-network-address-placeholder = عنوان آي بي في 4
## OSC VRChat settings
settings-osc-vrchat = أجهزة تعقب "في ار تشات أوه أس سي"
# This cares about multilines
settings-osc-vrchat-description-v1 =
تغيير الإعدادات الخاصة بمعيار أجهزة تعقب OSC المستخدم لإرسال
بيانات التعقب إلى التطبيقات التي لا تحتوي على SteamVR (مثل Quest المستقل).
تأكد من تمكين OSC في VRChat عبر قائمة الإجراءات ضمن OSC > ممكن.
settings-osc-vrchat-enable = تمكين
settings-osc-vrchat-enable-description = بتبديل إرسال واستقبال البيانات.
settings-osc-vrchat-enable-label = تمكين
settings-osc-vrchat-oscqueryEnabled = تمكين OSCQuery
settings-osc-vrchat-oscqueryEnabled-description =
يكتشف OSCQuery تلقائيا مثيلات VRChat قيد التشغيل ويرسل البيانات إليها.
يمكنه أيضا الإعلان عن نفسه لهم من أجل تلقي بيانات HMD ووحدة التحكم.
للسماح بتلقي بيانات HMD ووحدة التحكم من VRChat ، انتقل إلى إعدادات القائمة الرئيسية
ضمن "التتبع و IK (الحركة العكسية)" وتمكين "السماح بإرسال بيانات OSC لتتبع الرأس والمعصم".
settings-osc-vrchat-oscqueryEnabled-label = تمكين OSCQuery
settings-osc-vrchat-network = منافذ الشبكة
settings-osc-vrchat-network-port_in =
.label = منفذ الدخول

View File

@@ -390,6 +390,10 @@ tracker-settings-update-blocked = Update not available. No other releases availa
tracker-settings-update-available = { $versionName } is now available
tracker-settings-update = Update now
tracker-settings-update-title = Firmware version
tracker-settings-graph-acceleration-title = Tracker Acceleration
tracker-settings-graph-position-title = Tracker Position
tracker-settings-graph-show-title = Show Tracker Graph
tracker-settings-graph-hide-title = Hide Tracker Graph
## Tracker part card info
tracker-part_card-no_name = No name
@@ -1125,6 +1129,7 @@ onboarding-automatic_mounting-preparation-v2-step-2 = 3. Hold the position until
onboarding-automatic_mounting-put_trackers_on-title = Put on your trackers
onboarding-automatic_mounting-put_trackers_on-description = To calibrate mounting orientations, we're gonna use the trackers you just assigned. Put on all your trackers, you can see which are which in the figure to the right.
onboarding-automatic_mounting-put_trackers_on-next = I have all my trackers on
onboarding-automatic_mounting-return-home = Done
## Tracker manual proportions setupa
onboarding-manual_proportions-back = Go Back to Reset tutorial

View File

@@ -7,8 +7,8 @@
## Websocket (server) status
websocket-connecting = Conectando al servidor
websocket-connection_lost = Conexión al servidor perdida. Intentando reconectar...
websocket-connecting = Cargando...
websocket-connection_lost = ¡El servidor falló!
websocket-connection_lost-desc = Parece que el servidor de SlimeVR ha dejado de funcionar. Revise los registros y reinicie el programa.
websocket-timedout = No se ha podido conectar al servidor.
websocket-timedout-desc = Parece que el servidor de SlimeVR ha dejado de funcionar o se agotó el tiempo de espera de la conexión. Revise los registros y reinicie el programa
@@ -609,8 +609,6 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = El clip del suel
settings-general-fk_settings-leg_tweak-toe_snap-description = El encajado de dedos intenta adivinar la rotación de los pies si sus respectivos trackers no están en uso.
settings-general-fk_settings-leg_tweak-foot_plant-description = El plantado del pie rota los pies para que sean paralelos con el suelo al entrar en contacto.
settings-general-fk_settings-leg_fk = Tracking de piernas
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = Activar reinicio de montura para el pie mediante el pararse de puntillas.
settings-general-fk_settings-leg_fk-reset_mounting_feet = Reinicio de montura de pies
settings-general-fk_settings-enforce_joint_constraints = Límites esqueléticos
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = Imponer restricciones
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = Evita que las articulaciones giren más allá de su límite
@@ -1018,7 +1016,6 @@ onboarding-connect_tracker-next = He conectado todos mis sensores
onboarding-calibration_tutorial = Tutorial de calibración de IMU
onboarding-calibration_tutorial-subtitle = ¡Esto te ayudara a reducir la desviación del tracker!
onboarding-calibration_tutorial-description = Cada vez que enciendes tus trackers, van a necesitar descansar un ratito en una superficie plana para calibrarse. Tratemos de hacer lo mismo presionando el botón «{ onboarding-calibration_tutorial-calibrate }», <b>¡No los muevas!</b>
onboarding-calibration_tutorial-calibrate = Puse los sensores en una mesa.
onboarding-calibration_tutorial-status-waiting = Esperando por ti
onboarding-calibration_tutorial-status-calibrating = Calibrando

View File

@@ -244,6 +244,8 @@ reset-reset_all_warning_default-v2 =
Êtes-vous sûr de vouloir faire cela ?
reset-full = Réinitialisation complète
reset-mounting = Réinitialiser l'alignement
reset-mounting-feet = Réinitialiser l'alignement des pieds
reset-mounting-fingers = Réinitialiser l'alignement des doigts
reset-yaw = Réinitialisation horizontale
## Serial detection stuff
@@ -611,8 +613,8 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = Le limitage au s
settings-general-fk_settings-leg_tweak-toe_snap-description = La correction des orteils estime l'orientation de vos pieds si vous ne portez pas de capteurs sur ses derniers.
settings-general-fk_settings-leg_tweak-foot_plant-description = La correction des pieds oriente vos pieds pour qu'ils soient parallèles au sol lorsqu'ils le touche.
settings-general-fk_settings-leg_fk = Capture des jambes
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = Activer la réinitialisation de l'alignement des pieds en allant sur la pointe des pieds.
settings-general-fk_settings-leg_fk-reset_mounting_feet = Réinitialisation de l'alignement des pieds
settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1 = Forcer la réinitialisation de l'alignement des pieds pendant la réinitialisation d'alignement générale.
settings-general-fk_settings-leg_fk-reset_mounting_feet-v1 = Forcer la réinitialisation de l'alignement des pieds
settings-general-fk_settings-enforce_joint_constraints = Limites squelettiques
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = Appliquer les contraintes
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = Empêche les articulations de tourner au-delà de leur limite

View File

@@ -243,6 +243,8 @@ reset-reset_all_warning_default-v2 =
Czy na pewno chcesz to zrobić?
reset-full = Pełny Reset
reset-mounting = Zresetuj położenie
reset-mounting-feet = Zresetuj mocowanie stóp
reset-mounting-fingers = Zresetuj mocowanie palców
reset-yaw = Reset odchylenia
## Serial detection stuff
@@ -610,8 +612,6 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = Floor-clip może
settings-general-fk_settings-leg_tweak-toe_snap-description = Toe-snap próbuje odgadnąć obrót twoich stóp, jeśli trackery stóp nie są używane.
settings-general-fk_settings-leg_tweak-foot_plant-description = Foot-plant obraca stopy, aby były równoległe do podłoża podczas kontaktu.
settings-general-fk_settings-leg_fk = Śledzenie nóg
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = Włącz resetowanie montażu stóp, chodząc na palcach.
settings-general-fk_settings-leg_fk-reset_mounting_feet = Reset mocowania stóp
settings-general-fk_settings-enforce_joint_constraints = Limity szkieletowe
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = Wymuszanie ograniczeń
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = Zapobiega obracaniu się stawów poza ich limit

View File

@@ -89,7 +89,7 @@ body_part-RIGHT_LITTLE_DISTAL = ปลายนิ้วก้อยขวา
board_type-UNKNOWN = ไม่ทราบ
board_type-NODEMCU = NodeMCU
board_type-CUSTOM = บอร์ดคัสตอม
board_type-CUSTOM = บอร์ดสั่งทำ
board_type-WROOM32 = WROOM32
board_type-WEMOSD1MINI = Wemos D1 Mini
board_type-TTGO_TBASE = TTGO T-Base
@@ -187,7 +187,7 @@ skeleton_bone-SKELETON_OFFSET-desc =
ปรับค่านี้เพื่อเลื่อนตำแหน่งแทร็กเกอร์ทั้งหมดไปด้านหน้าหรือด้านหลัง
เพื่อช่วยในการปรับเทียบสำหรับเกมหรือแอปพลิเคชันบางตัว
ที่อาจต้องการให้แทร็กเกอร์อยู่ด้านหน้ามากกว่าปกติ
skeleton_bone-SHOULDERS_DISTANCE = ระยะไหล่
skeleton_bone-SHOULDERS_DISTANCE = ระยะความกว้างไหล่
skeleton_bone-SHOULDERS_DISTANCE-desc =
นี่คือระยะทางในแนวตั้งจากฐานคอถึงไหล่ของคุณ
ปรับโดยเริ่มจากตั้งความยาวแขนส่วนบนเป็น 0 และปรับค่านี้จนกว่าแทร็กเกอร์ข้อศอกเสมือน
@@ -243,7 +243,9 @@ reset-reset_all_warning_default-v2 =
คุณต้องการดำเนินการต่อหรือไม่?
reset-full = รีเซ็ตแทร็กเกอร์ทั้งหมด
reset-mounting = รีเซ็ตการติดตั้ง
reset-yaw = รีเซ็ตแกนแนวตั้ง
reset-mounting-feet = รีเซ็ตทิศทางติดตั้งเท้า
reset-mounting-fingers = รีเซ็ตการติดตั้งนิ้วมือ
reset-yaw = รีเซ็ตแกนหมุน
## Serial detection stuff
@@ -260,25 +262,26 @@ serial_detection-close = ปิด
navbar-home = หน้าหลัก
navbar-body_proportions = สัดส่วนร่างกาย
navbar-trackers_assign = กำหนดแทร็กเกอร์
navbar-mounting = ปรับเทียบการติดตั้ง
navbar-mounting = ปรับเทียบทิศทางการติดตั้ง
navbar-onboarding = ตัวช่วยการตั้งค่า
navbar-settings = ตั้งค่า
## Biovision hierarchy recording
bvh-start_recording = อัด BVH
bvh-recording = กำลังอัดบันทึก
bvh-start_recording = บันทึก BVH
bvh-recording = กำลังบันทึก...
bvh-save_title = บันทึก การบันทึก BVH
## Tracking pause
tracking-unpaused = หยุดการติดตามชั่วคราว
tracking-paused = ยกเลิกการหยุดการติดตาม
tracking-paused = ยกเลิกหยุดการติดตาม
## Widget: Overlay settings
widget-overlay = โอเวอร์เลย์
widget-overlay-is_visible_label = แสดงโอเวอร์เลย์ ใน SteamVR
widget-overlay-is_mirrored_label = แสดงโอเวอร์เลย์เป็นกระจก
widget-overlay-is_mirrored_label = สะท้อนการแสดงโอเวอร์เลย์
## Widget: Drift compensation
@@ -286,7 +289,7 @@ widget-drift_compensation-clear = ล้างการชดเชยค่า
## Widget: Clear Reset Mounting
widget-clear_mounting = คืนค่าการติดตั้ง
widget-clear_mounting = ล้างค้าการติดตั้ง
## Widget: Developer settings
@@ -321,9 +324,9 @@ tracker-status-none = ไม่มีสถานะ
tracker-status-busy = ไม่ว่าง
tracker-status-error = มีปัญหา
tracker-status-disconnected = ขาดการเชื่อมต่อ
tracker-status-occluded = มีการติดขัด
tracker-status-occluded = ติดขัด
tracker-status-ok = OK
tracker-status-timed_out = หมดเวลาการเชื่อมต่อ
tracker-status-timed_out = หมดเวลาเชื่อมต่อ
## Tracker status columns
@@ -350,7 +353,7 @@ tracker-rotation-back = หลัง
tracker-rotation-back_left = หลังซ้าย
tracker-rotation-back_right = หลังขวา
tracker-rotation-custom = กำหนดเอง
tracker-rotation-overriden = (ถูกแทนที่ด้วยการ รีเซ็ตการติดตั้ง)
tracker-rotation-overriden = (ถูกแทนที่ด้วยการรีเซ็ตการติดตั้ง)
## Tracker information
@@ -403,7 +406,8 @@ tracker-settings-forget-description = ลบตัวแทร็กเกอร
tracker-settings-forget-label = ลืมแทร็กเกอร์นี้
tracker-settings-update-unavailable = ไม่สามารถอัพเดทได้ (DIY)
tracker-settings-update-low-battery = ไม่สามารถอัพเดทได้ แบตเตอรรี่ต่ำกว่า 50%
tracker-settings-update-up_to_date = อัพเดทล่าสุด
tracker-settings-update-up_to_date = อัพเดทล่าสุดแล้ว
tracker-settings-update-blocked = ไม่สามารถอัพเดทได้ ไม่มีเวอร์ชั่นอื่นที่พร้อมอัพเดท
tracker-settings-update-available = { $versionName } พร้อมใช้งานแล้ว
tracker-settings-update = อัพเดตเดี๋ยวนี้
tracker-settings-update-title = เวอร์ชั่นเฟิร์มแวร์
@@ -471,7 +475,7 @@ mounting_selection_menu-close = ปิด
## Sidebar settings
settings-sidebar-title = การตั้งค่า
settings-sidebar-general = ทั่วไป
settings-sidebar-general = การตั้งค่าทั่วไป
settings-sidebar-tracker_mechanics = การทำงานแทร็กเกอร์
settings-sidebar-stay_aligned = Stay Aligned
settings-sidebar-fk_settings = ตั้งค่าการจับตำแหน่ง
@@ -560,9 +564,9 @@ settings-general-tracker_mechanics-drift_compensation-amount-label = เปอ
settings-general-tracker_mechanics-drift_compensation-max_resets-label = ใช้ค่าจากการรีเซ็ต X ครั้ง
settings-general-tracker_mechanics-save_mounting_reset = บันทึกค่าการปรับเทียบตำแหน่งแบบอัตโนมัติ
settings-general-tracker_mechanics-save_mounting_reset-description =
บันทึกการปรับเทียบตำแหน่งแทร็กเกอร์ระหว่างการรีสตาร์ท มีประโยชน์กับ
ผู้ที่สวมชุดที่แทร็กเกอร์ไม่เคลื่อนในการใช้แต่ละครั้ง <b>ไม่แนะนำสำหรับผู้ใช้ทั่วไป!</b>
settings-general-tracker_mechanics-save_mounting_reset-enabled-label = บันทึกการรีเซ็ตตำแหน่
บันทึกการปรับเทียบการติดตั้งแทร็กเกอร์ระหว่างการรีสตาร์ท มีประโยชน์กับ
ผู้ที่แทร็กเกอร์ไม่เคลื่อนในการใช้แต่ละครั้ง <b>ไม่แนะนำสำหรับผู้ใช้ทั่วไป!</b>
settings-general-tracker_mechanics-save_mounting_reset-enabled-label = บันทึกการรีเซ็ตการติดตั้
settings-general-tracker_mechanics-use_mag_on_all_trackers = ใช้ตัววัดสนามแม่เหล็กบนแทร็กเกอร์ที่ IMU รองรับ
settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
ใช้ตัววัดสนามแม่เหล็กกับแทร็กเกอร์ทุกตัวที่เฟิร์มแวร์รองรับ ลดค่าดริฟท์ในพื้นที่เล่นที่สนามแม่เหล็กเสถียร์
@@ -573,6 +577,7 @@ settings-stay_aligned-description = Stay Aligned ลดอัตตาค่า
settings-stay_aligned-setup-label = ตั้งค่า Stay Aligned
settings-stay_aligned-setup-description = คุณต้องทำการ "ตั้งค่า Stay Aligned" ถึงจะเปิดใช้งาน Stay Aligned ได้
settings-stay_aligned-warnings-drift_compensation = ⚠ กรุณาปิดใช้งาน การชดเชยค่าดริฟท์!! เพราะมันจะขัดกับการตั้งค่า Stay Aligned
settings-stay_aligned-enabled-label = ปรับแทร็กเกอร์ของคุณ
settings-stay_aligned-hide_yaw_correction-label = ซ่อนการปรับแต่ง(เพื่อเปรียบเทียบกับไม่ได้เปิดใช้ Stay Aligned)
settings-stay_aligned-general-label = การตั้งค่าทั่วไป
settings-stay_aligned-relaxed_poses-label = ท่าผ่อนคลาย
@@ -609,21 +614,28 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = เปิดก
settings-general-fk_settings-leg_tweak-toe_snap-description = เปิดเดาการหมุนของเท้าเพื่อคาดเดาการหมุนของเท้าในกรณีที่คุณไม่ได้ใส่แทร็กเกอร์ที่เท้า
settings-general-fk_settings-leg_tweak-foot_plant-description = เปิดปรับสมดุลเท้าเพื่อให้เท้าคุณอยู่ในระนาบเดียวกับพื้นเมื่อเท้าอยู่บนพื้น
settings-general-fk_settings-leg_fk = การจับตำแหน่งขา
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = เปิดให้รีเซ็ตตำแหน่งแทร็กเกอร์เท้าเมื่อเขย่งได้
settings-general-fk_settings-leg_fk-reset_mounting_feet = รีเซ็ตตำแหน่งแทร็กเกอร์เท้า
settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1 = บังคับใช้การรีเซ็ตการติดตั้งของแทร็กเกอร์เท้าเมื่อทำการรีเซ็ตการติดตั้ง
settings-general-fk_settings-leg_fk-reset_mounting_feet-v1 = บังคับใช้รีเซ็ตตำแหน่งที่เท้า
settings-general-fk_settings-enforce_joint_constraints = จำกัดองศากระดูก
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = จำกัดองศาที่จะหมุนได้
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = ป้องกันไม่ให้ข้อต่อหมุนเกินขีดจำกัด
settings-general-fk_settings-enforce_joint_constraints-correct_constraints = แก้ไขด้วยข้อจำกัด
settings-general-fk_settings-enforce_joint_constraints-correct_constraints-description = แก้การหมุนของข้อต่อเมื่อหมุนเกินขีดจำกัด
settings-general-fk_settings-arm_fk = การจับตำแหน่งแขน
settings-general-fk_settings-arm_fk-description = บังคับให้ใช้การจับตำแหน่งแขนจากแว่น VR แม้ว่าจะมีตำแหน่งแขนอยู่แล้ว
settings-general-fk_settings-arm_fk-force_arms = บังคับใช้ตำแหน่งแขนจากแว่น
settings-general-fk_settings-reset_settings = ตั้งค่าการรีเซ็ต
settings-general-fk_settings-reset_settings-reset_hmd_pitch-description = รีเซ็ตแกนตั้งของแว่น(HMD) เมื่อใช้การรีเซ็ตแทร็กเกอร์ทั้งหมด เหมาะกับผู้ที่ใส่แว่นบนหน้าผากสำหรับ Vtubing หรือ mocap ไม่ควรใช้สำหรับเล่น VR
settings-general-fk_settings-reset_settings-reset_hmd_pitch = รีเซ็ตแกนตั้งแว่น HMD
settings-general-fk_settings-arm_fk-reset_mode-description = เปลี่ยนท่าทางการไว้แขนในการรีเซ็ตตำแหน่งแทร็กเกอร์
settings-general-fk_settings-arm_fk-reset_mode-description = เปลี่ยนท่าทางการไว้แขนเมื่อทำการรีเซ็ตการติดตั้งแทร็กเกอร์
settings-general-fk_settings-arm_fk-back = หลัง
settings-general-fk_settings-arm_fk-back-description = เป็นท่าเริ่มต้น โดยแขนส่วนบนไปด้านหลัง และแขนส่วนล่างไปด้านหน้า
settings-general-fk_settings-arm_fk-tpose_up = ที-โพส (ขึ้น)
settings-general-fk_settings-arm_fk-tpose_up-description = ให้แขนอยู๋ข้างลำตัวเมื่อทำการรีเซ็ตทั้งหมด และอยู่ในมุม 90 องศา เมื่อทำการรีเซ็ตการติดตั้ง
settings-general-fk_settings-arm_fk-tpose_down = ที-โพส (ล่าง)
settings-general-fk_settings-arm_fk-tpose_down-description = ให้แขนอยู๋ในมุม 90 องศาข้างลำตัวเมื่อทำการรีเซ็ตทั้งหมด และอยู่แนบลำตัวเมื่อทำการรีเซ็ตการติดตั้ง
settings-general-fk_settings-arm_fk-forward = ไปข้างหน้า
settings-general-fk_settings-arm_fk-forward-description = ให้ยกแขนคุณ 90 องศาในด้านหน้า เหมาะสำหรับผู้ที่ Vtubing
settings-general-fk_settings-skeleton_settings-toggles = ตั้งค่าโครงกระดูก
settings-general-fk_settings-skeleton_settings-description = ตั้งค่าให้แสดงหรือไม่แสดงโครงกระดูก เราแนะนำเปิดไว้
settings-general-fk_settings-skeleton_settings-extended_spine_model = โมเดลกระดูกสันหลังขั้นสูง
@@ -645,7 +657,7 @@ settings-general-fk_settings-self_localization-description = โหมด Mocap
settings-general-gesture_control = ควบคุมผ่านท่าทาง
settings-general-gesture_control-subtitle = การรีเซ็ตตามการแตะ
settings-general-gesture_control-description = ใช้การแตะเพื่อรีเซ็ตแทรกเกอร์โดย แทร็กเกอร์ที่อยู่บนสุดบนตัวจะใช้เพื่อรีเซ็ตแกนตั้ง แทร็กเกอร์ที่อยู่บนสุดของเท้าซ้ายจะใช้เพื่อรีเซ็ทแทร็กเกอร์ทั้งหมด และแทร็กเกอร์ที่อยู่บนสุดของขาขวาจะใช้เพื่อรีเซ็ตตำแหน่งแทร็กเกอร์ โดยการแตะจะต้องทำในระยะเวลา 0.3 วินาทีเพื่อให้โปรแกรมรับทราบ
settings-general-gesture_control-description = ใช้งานการแตะเพื่อรีเซ็ตแทรกเกอร์ โดยให้แทร็กเกอร์ที่อยู่บนสุดบนตัวในการรีเซ็ตแกนหมุน แทร็กเกอร์ที่อยู่บนสุดของเท้าซ้ายจะใช้เพื่อรีเซ็ทแทร็กเกอร์ทั้งหมด และแทร็กเกอร์ที่อยู่บนสุดของขาขวาจะใช้เพื่อรีเซ็ตตำแหน่งแทร็กเกอร์ โดยการแตะจะต้องทำในระยะเวลา 0.3 วินาทีเพื่อให้โปรแกรมรับทราบ
# This is a unit: 3 taps, 2 taps, 1 tap
# $amount (Number) - Amount of taps (touches to the tracker's case)
settings-general-gesture_control-taps = แตะ{ $amount }ครั้ง
@@ -729,6 +741,9 @@ settings-interface-behavior-error_tracking-description_v2 =
เพื่อมอบประสบการณ์การใช้งานที่ดีที่สุด เรานั้นเก็บรายงานข้อผิดพลาดแบบไม่ระบุตัวตน เมตริกประสิทธิภาพ และข้อมูลระบบปฏิบัติการเพื่อให้เราตรวจพบข้อบกพร่องและปัญหาใน SlimeVR โดยข้อมูลเหล่านี้ถูกเก็บผ่าน Sentry.io
settings-interface-behavior-error_tracking-label = อนุญาติการส่งข้อมูลให้นักพัฒนา
settings-interface-behavior-bvh_directory = ตำแหน่งที่คุณจะบันทึกค่า BVH
settings-interface-behavior-bvh_directory-description = เลือกตำแหน่งที่คุณต้องการบันทึกค่า BVH แทนที่จะต้องกำหนดเองทุกครั้งเมื่อบันทึก
settings-interface-behavior-bvh_directory-label = เปิดโฟลเดอร์ไปยังไฟล์ BVH
## Serial settings
@@ -992,6 +1007,7 @@ onboarding-connect_tracker-next = ฉันเชื่อมต่อแทร
onboarding-calibration_tutorial = บทเรียนการปรับเทียบ IMU
onboarding-calibration_tutorial-subtitle = นี่จะเป็นการช่วยลดค่าดริฟท์ของแทร็กเกอร์
onboarding-calibration_tutorial-description-v1 = หลังจากเปิดแทร็กเกอร์ของคุณแล้ว วางมันลงบนพื้นที่เรียบสักครู่ แล้วให้แทร็กเกอร์ปรับเทียบตัวเอง การปรับเทียบสามารถทำได้ตลอดเวลาหลังจากเปิดแทร็กเกอร์ขึ้นมา — หน้านี้เป็นเพียงแค่ให้คำแนะนำเท่านั้น หากจะเริ่มต้น คลิกที่ปุ่ม "{ onboarding-calibration_tutorial-calibrate }" แล้ว <b>อย่าขยับแทร็กเกอร์ของคุณ!</b>
onboarding-calibration_tutorial-calibrate = ฉันได้วางแทร็กเกอร์ไว้บนโต๊ะแล้ว
onboarding-calibration_tutorial-status-waiting = เรารอคุณอยู่
onboarding-calibration_tutorial-status-calibrating = กำลังปรับเทียบ
@@ -1074,6 +1090,38 @@ onboarding-assign_trackers-warning-LEFT_LOWER_LEG =
[2] ข้อเท้าซ้าย ถูกกำหนดแล้ว แต่คุณต้องกำหนด ต้นขาซ้าย ด้วย
*[unknown] ข้อเท้าซ้าย ถูกกำหนดแล้ว แต่คุณต้องกำหนด ??? ???? ของร่างกายด้วย
}
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-RIGHT_LOWER_LEG =
{ $unassigned ->
[0] ข้อเท้าขวา ถูกกำหนดแล้ว แต่คุณต้องกำหนด ต้นขาขวา และอย่างใดอย่างหนึ่งระหว่าง ส่วนอก สะโพก หรือ เอว ด้วย
[1] ข้อเท้าขวา ถูกกำหนดแล้ว แต่คุณต้องกำหนด อย่างใดอย่างหนึ่งระหว่าง ส่วนอก สะโพก หรือ เอว ด้วย
[2] ข้อเท้าขวา ถูกกำหนดแล้ว แต่คุณต้องกำหนด ต้นขาขวา ด้วย
*[unknown] ข้อเท้าขวา ถูกกำหนดแล้ว แต่คุณต้องกำหนด ??? ของร่างกายด้วย
}
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-LEFT_UPPER_LEG =
{ $unassigned ->
[0] ต้นขาซ้าย ถูกกำหนดแล้ว แต่คุณต้องกำหนด อย่างใดอย่างหนึ่งระหว่าง ส่วนอก สะโพก หรือ เอว ด้วย
*[unknown] ต้าขาซ้าย ถูกกำหนดแล้ว แแต่คุณต้องกำหนด ??? ??? ของร่างกายด้วย
}
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-RIGHT_UPPER_LEG =
{ $unassigned ->
[0] ต้นขาขวา ถูกกำหนดแล้ว แต่คุณต้องกำหนด อย่างใดอย่างหนึ่งระหว่าง ส่วนอก สะโพก หรือ เอว ด้วย
*[unknown] ต้าขาขวา ถูกกำหนดแล้ว แแต่คุณต้องกำหนด ??? ??? ของร่างกายด้วย
}
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-HIP =
{ $unassigned ->
[0] สะโพก ถูกกำหนดแล้ว แต่คุณต้องกำหนด ส่วนอกด้วย
*[unknown] สะโพก ถูกกำหนดแล้ว แต่คุณต้องกำหนด ??? ??? ของร่างกาย ด้วย
}
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-WAIST =
{ $unassigned ->
[0] เอว ถูกกำหนดแล้ว แต่คุณต้องกำหนด ส่วนอกด้วย
*[unknown] เอว ถูกกำหนดแล้ว แต่คุณต้องกำหนด ??? ??? ของร่างกาย ด้วย
}
## Tracker mounting method choose
@@ -1100,12 +1148,15 @@ onboarding-choose_mounting-manual_modal-cancel = ยกเลิก
onboarding-manual_mounting-back = กลับไปยังเข้าสู่ VR
onboarding-manual_mounting = ปรับเทียบตำแหน่งแบบกำหนดเอง
onboarding-manual_mounting-description = คลิกบนแทร็กเกอร์ทุกตัว และดูทิศทางของแทร็กเกอร์ที่ถูกติดตั้ง
onboarding-manual_mounting-auto_mounting = ปรับเทียบตำแหน่งแบบอัตโนมัติ
onboarding-manual_mounting-next = ขั้นตอนถัดไป
## Tracker automatic mounting setup
onboarding-automatic_mounting-back = กลับไปยังเข้าสู่ VR
onboarding-automatic_mounting-title = ปรับเทียบตำแหน่งแทร็กเกอร์
onboarding-automatic_mounting-description = เพื่อให้ SlimeVR ทำงานได้ เราจะต้องทำการตั้งค่าการติดตั้งของแทร็กเกอร์ของคุณให้ตรงกับการติดตั้งบนร่างกายจริง
onboarding-automatic_mounting-manual_mounting = ปรับเทียบตำแหน่งแบบกำหนดเอง
onboarding-automatic_mounting-next = ขั้นตอนถัดไป
onboarding-automatic_mounting-prev_step = ขั้นตอนก่อนหน้า
@@ -1113,6 +1164,7 @@ onboarding-automatic_mounting-done-title = ตำแหน่งแทร็ก
onboarding-automatic_mounting-done-description = ปรับเทียบตำแหน่งแทร็กเกอร์ของคุณสำเร็จแล้ว!
onboarding-automatic_mounting-done-restart = ลองอีกครั้ง
onboarding-automatic_mounting-mounting_reset-title = รีเซ็ตตำแหน่งแทร็กเกอร์
onboarding-automatic_mounting-mounting_reset-step-0 = 1. ก้มตัวลงอยู่ในท่าที่ "กำลังเล่นสกี" โดยให้ขางอ ลำตัวด้านบนเอียงไปด้านหน้า และงอแขนของคุณ
onboarding-automatic_mounting-mounting_reset-step-1 = 2. กดไปที่ปุ่ม "รีเซ็ตำตำแหน่งแทร็กเกอร์" แล้วรอประมาณ 3 วินาที ก่อนที่ตำแหน่งของแทร็กเกอร์จะถูกรีเซ็ต
onboarding-automatic_mounting-preparation-title = จัดเตรียมพร้อม
onboarding-automatic_mounting-preparation-v2-step-0 = 1. กดที่ปุ่ม "รีเซ็ตทั้งหมด"
@@ -1141,7 +1193,7 @@ onboarding-manual_proportions-estimated_height = ความสูงของ
onboarding-automatic_proportions-back = กลับไปยังการปรับสัดส่วนด้วยตัวเอง
onboarding-automatic_proportions-title = วัดสัดส่วนร่างกายของคุณ
onboarding-automatic_proportions-description = เพื่อให้ SlimeVR แทร็กเกอร์ทำงานได้ เราจะต้องรู้ความยาวของกระดูกของคุณ การ Calibration อย่างไวนี้จะวัดค่ามันให้คุณ
onboarding-automatic_proportions-description = เพื่อให้ SlimeVR แทร็กเกอร์ทำงานได้ เราจะต้องรู้ความยาวของกระดูกของคุณ การปรับเทียบอย่างไวนี้จะวัดค่ามันให้คุณ
onboarding-automatic_proportions-manual = ปรับสัดส่วนด้วยตัวเอง
onboarding-automatic_proportions-prev_step = ขั้นตอนก่อนหน้า
onboarding-automatic_proportions-put_trackers_on-title = ใส่แทร็กเกอร์ของคุณ
@@ -1150,10 +1202,10 @@ onboarding-automatic_proportions-put_trackers_on-next = ฉันใส่แท
onboarding-automatic_proportions-requirements-title = สิ่งที่ต้องการ
# Each line of text is a different list item
onboarding-automatic_proportions-requirements-descriptionv2 =
คุณมีแทร็เกอร์ พอที่จะจับการเคลื่อนไหวเท้าของคุณ (โดยทั่วไป 5 ตัว)
คุณมีแทร็เกอร์พอที่จะจับการเคลื่อนไหวเท้าของคุณ (โดยทั่วไป 5 ตัว)
คุณกำลังใส่แว่น และแทร็กเกอร์อยู่
แทร็กเกอร์ของคุณได้เชื่อมต่อเข้ากับ SlimeVR Server และทำงานเป็นปรกติ (เช่น ไม่มีการกระตุก หลุดการเชื่อมต่อ หรืออื่นๆ)
แว่นของคุณได้ส่งข้อมูลตำแหน่งของคุณเข้ากับ SlimeVR server (ปรกติแล้วหมายถึงว่า SlimeVR ทำงานอยู่ และเชื่อมต่อเข้ากับ SlimeVR ด้วย SlimeVR's SteamVR driver)
แว่นของคุณได้ส่งข้อมูลตำแหน่งของคุณเข้ากับ SlimeVR server (ปรกติแล้วหมายถึงว่า SlimeVR ทำงานอยู่ และเชื่อมต่อเข้ากับ SlimeVR ด้วย ไดร์เวอร์ของ SteamVR ผ่าน SlimeVR
แทร็กเกอร์ของคุณนั้นทำงานปรกติ และแสดงการเคลื่อนไหวของคุณถูกต้อง (ตัวอย่างเช่น คุณได้ทำการ Full reset และตัวแทร็กเกอร์ขยับไปในทิศทางที่ถูกเมื่อขยับตัว ก้มลง นั่งลง หรือขยับขา เป็นต้น)
onboarding-automatic_proportions-requirements-next = ฉันได้อ่านสิ่งที่ต้องการแล้ว
onboarding-automatic_proportions-check_height-title-v3 = วัดความสูงของแว่นของคุณ
@@ -1266,9 +1318,12 @@ onboarding-stay_aligned-relaxed_poses-sitting-title = ท่าผ่อนค
onboarding-stay_aligned-relaxed_poses-sitting-step-0 = 1. นั่งในท่าที่สบายๆ
onboarding-stay_aligned-relaxed_poses-sitting-step-1-v2 = 2. กดไปที่ "บันทึกท่าโพส"
onboarding-stay_aligned-relaxed_poses-flat-title = ท่าผ่อนคลายในขณะที่นั่งบนพื้น
onboarding-stay_aligned-relaxed_poses-flat-step-0 = 1. นั่งลงบนพื้นและยื่นขาไปทางด้านหน้า ไม่ต้องเกร็งนะ
onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. กดไปที่ "บันทึกท่าโพส"
onboarding-stay_aligned-relaxed_poses-skip_step = ข้าม
onboarding-stay_aligned-done-title = เปิดใช้งาน Stay Aligned แล้ว!
onboarding-stay_aligned-done-description = การตั้งค่า Stay Aligned ของคุณสำเร็จแล้ว!
onboarding-stay_aligned-done-description-2 = การตั้งค่าเสร็จสิ้นแล้ว คุณสามารถจะเริ่มต้นการตั้งค่าใหม่หากคุณต้องการจะปรับค่าของท่าโพสคุณ
onboarding-stay_aligned-previous_step = ก่อนหน้า
onboarding-stay_aligned-next_step = ต่อไป
onboarding-stay_aligned-restart = เริ่มใหม่
@@ -1276,6 +1331,7 @@ onboarding-stay_aligned-done = เสร็จแล้ว
## Home
home-no_trackers = ไม่พบแทร็กเกอร์หรือยังไม่ได้ถูกกำหนด
## Trackers Still On notification
@@ -1284,10 +1340,19 @@ trackers_still_on-modal-description =
มีแทร็กเกอร์เปิดค้างไว้อยู่
คุณแน่ใจที่จะปิด SlimeVR?
trackers_still_on-modal-confirm = ออกจาก SlimeVR
trackers_still_on-modal-cancel = ใจเย็นๆก่อน....
## Status system
status_system-StatusTrackerReset = เราแนะนำให้คุณทำการรีเซ็ตทั้งหมดเพราะว่าแทร็กเกอร์หนึ่งตัวหรือมากกว่านั้นยังไม่ได้ถูกปรับ
status_system-StatusSteamVRDisconnected =
{ $type ->
[steamvr_feeder] ขณะนี้ยังไม่มีการเชื่อมต่อกับแอป SlimeVR Feeder
*[steamvr] ขณะนี้ยังไม่มีการเชื่อมต่อกับ SteamVR ผ่านไดรเวอร์ SlimeVR
}
status_system-StatusTrackerError = แทร็กเกอร์ { $trackerName } เกิดข้อผิดพลาด
status_system-StatusUnassignedHMD = แว่น VR ของคุณควรจะถูกกำหนดเป็นแทร็กเกอร์ส่วนหัว
status_system-StatusPublicNetwork = โปรไฟล์เครือข่ายของคุณตอนนี้ถูกตั้งค่าเป็นสาธารณะ ({ $adapters }) ซึ่งไม่แนะนำสำหรับการทำงานของ SlimeVR <PublicFixLink>ดูวิธีการแก้ไขให้เหมาะสมที่นี่</PublicFixLink>
## Firmware tool globals
@@ -1299,7 +1364,16 @@ firmware_tool-loading = กำลังโหลด...
## Firmware tool Steps
firmware_tool = เครื่องมือเฟิร์มแวร์ DIY
firmware_tool-description = ให้คุณตั้งค่าและแฟลชเฟิร์มแวร์แทร็กเกอร์ DIY ของคุณ
firmware_tool-not_available = ไอ่หย๊า เครื่องมือเฟิร์มแวร์ยังไม่พร้อมใช้งานในขณะนี้ โปรดกลับมาใหม่ทีหลัง!
firmware_tool-not_compatible = Firmware นี้เข้ากันไม่ได้กับเซิร์ฟเวอร์เวอร์ชั่นนี้ โปรดอัปเดตเซิร์ฟเวอร์ของคุณ!
firmware_tool-board_step = เลือกบอร์ดของคุณ
firmware_tool-board_step-description = เลือกบอร์ดหนึ่งตัวที่แสดงอยู่ด้านล่าง
firmware_tool-board_pins_step = ตรวจสอบ Pin ของคุณ
firmware_tool-board_pins_step-description =
โปรดตรวจสอบว่า Pin ที่คุณเลือกนั้นถูกต้อง
ค่าเริ่มต้นมักจะถูกแล้วหากคุณทำตามเอกสารประกอบของ SlimeVR
firmware_tool-board_pins_step-enable_led = เปิดใช้งาน LED
firmware_tool-board_pins_step-led_pin =
.label = พิน LED
@@ -1315,6 +1389,16 @@ firmware_tool-board_pins_step-battery_sensor_pin =
firmware_tool-board_pins_step-battery_resistor =
.label = ตัวต้านทานของแบตเตอรี่ (โอห์ม)
.placeholder = ใส่ค่าต้านทานของตัวต้านทานแบตเตอรี่
firmware_tool-board_pins_step-battery_shield_resistor-0 =
.label = ค่าความต้านทาน R1 ของ Battery Shield (โอห์ม)
.placeholder = กรุณากรอกค่าความต้านทาน R1 ของ Battery Shield
firmware_tool-board_pins_step-battery_shield_resistor-1 =
.label = ค่าความต้านทาน R1 ของ Battery Shield (โอห์ม)
.placeholder = กรุณากรอกค่าความต้านทาน R1 ของ Battery Shield
firmware_tool-add_imus_step = เลือก IMU ที่คุณใช้
firmware_tool-add_imus_step-description =
กรุณาใส่ชนิดและจำนวนของ IMU ที่แทร็กเกอร์ของคุณใช้
ค่าเริ่มต้นมักจะถูกแล้วหากคุณทำตามเอกสารประกอบของ SlimeVR
firmware_tool-add_imus_step-imu_type-label = ประเภท IMU
firmware_tool-add_imus_step-imu_type-placeholder = เลือกประเภทของ IMU
firmware_tool-add_imus_step-imu_rotation =
@@ -1458,14 +1542,25 @@ vrc_config-mute-btn = ปิดคำเตือน
vrc_config-unmute-btn = เลิกแจ้งเตือน
vrc_config-legacy_mode = การตั้งศูนย์แบบเก่า
vrc_config-disable_shoulder_tracking = ปิดใช้งานการจับตำแหน่งไหล
vrc_config-shoulder_width_compensation = การชดเชยความกว้างของไหล่
vrc_config-spine_mode = โหมดกระดูกสันหลัง FBT
vrc_config-tracker_model = โมเดลตัวติดตาม FBT
vrc_config-avatar_measurement_type = การวัดของอวาตาร์
vrc_config-calibration_range = ช่วงการตั้งศูนย์
vrc_config-calibration_visuals = แสดงภาพการตั้งศูนย์
vrc_config-user_height = ความสูงจริงของผู้ใช้
vrc_config-spine_mode-UNKNOWN = ไม่ทราบ
vrc_config-spine_mode-LOCK_BOTH = ล็อคทั้งคู่
vrc_config-spine_mode-LOCK_HEAD = ล็อคศีรษะ
vrc_config-spine_mode-LOCK_HIP = ล็อคสะโพก
vrc_config-tracker_model-UNKNOWN = ไม่ทราบ
vrc_config-tracker_model-AXIS = แกนหมุน
vrc_config-tracker_model-BOX = กล่อง
vrc_config-tracker_model-SPHERE = ทรงกลม
vrc_config-tracker_model-SYSTEM = ระบบ
vrc_config-avatar_measurement_type-UNKNOWN = ไม่ทราบ
vrc_config-avatar_measurement_type-HEIGHT = ความสูง:
vrc_config-avatar_measurement_type-ARM_SPAN = ช่วงแขน
## Error collection consent modal

View File

@@ -602,8 +602,8 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = 地板限制可
settings-general-fk_settings-leg_tweak-toe_snap-description = 脚趾着地可以在没有脚部追踪器的情况下尝试猜测脚部的俯仰。
settings-general-fk_settings-leg_tweak-foot_plant-description = 脚掌着地会在脚与地面接触时保持脚掌与地板平行。
settings-general-fk_settings-leg_fk = 腿部追踪
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = 开启脚部佩戴重置。(佩戴重置时需要踮起脚尖)
settings-general-fk_settings-leg_fk-reset_mounting_feet = 脚部佩戴重置
settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1 = 在进行普通佩戴重置时强制进行脚部佩戴重置。
settings-general-fk_settings-leg_fk-reset_mounting_feet-v1 = 强制脚部佩戴重置
settings-general-fk_settings-enforce_joint_constraints = 骨骼限制
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = 强制约束
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = 避免关节旋转超过人体骨骼角度限制

View File

@@ -239,6 +239,8 @@ reset-reset_all_warning_default-v2 =
確定要繼續嗎?
reset-full = 完整重置
reset-mounting = 配戴重置
reset-mounting-feet = 重置腳部配戴
reset-mounting-fingers = 重置手指配戴
reset-yaw = 左右偏擺重置
## Serial detection stuff
@@ -264,6 +266,7 @@ navbar-settings = 詳細設定
bvh-start_recording = 錄製 BVH 檔案
bvh-recording = 錄製中…
bvh-save_title = 儲存 BVH 紀錄
## Tracking pause
@@ -401,6 +404,7 @@ tracker-settings-forget-label = 忘記追蹤器
tracker-settings-update-unavailable = 無法更新 (DIY)
tracker-settings-update-low-battery = 無法更新,電池電量低於 50%
tracker-settings-update-up_to_date = 已為最新版本
tracker-settings-update-blocked = 無法更新,沒有其他可用版本。
tracker-settings-update-available = 版本 { $versionName } 可供更新
tracker-settings-update = 立即更新
tracker-settings-update-title = 韌體版本
@@ -604,8 +608,8 @@ settings-general-fk_settings-leg_tweak-floor_clip-description = 地板限制功
settings-general-fk_settings-leg_tweak-toe_snap-description = 腳趾跟地功能在沒有腳部的追蹤器時,會嘗試猜測腳掌的旋轉角度。
settings-general-fk_settings-leg_tweak-foot_plant-description = 腳底貼地功能會在腳底與地面接觸時,將腳部旋轉成與地板平行。
settings-general-fk_settings-leg_fk = 腿部追蹤
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = 開啟腳部配戴重置,進行配戴重置時需要踮起腳尖
settings-general-fk_settings-leg_fk-reset_mounting_feet = 腳部配戴重置
settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1 = 使用普通的重置配戴時,一併重置腳部配戴
settings-general-fk_settings-leg_fk-reset_mounting_feet-v1 = 強制重置腳部配戴
settings-general-fk_settings-enforce_joint_constraints = 骨架限制
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = 約束關節旋轉
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = 避免關節旋轉超出極限
@@ -734,6 +738,9 @@ settings-interface-behavior-error_tracking-description_v2 =
為了提供最佳的使用者體驗,我們會蒐集匿名化的錯誤報告、性能指標和作業系統資訊,這會對我們檢測 SlimeVR 的錯誤和問題有所幫助。我們會透過 Sentry.io 來蒐集這些指標。
settings-interface-behavior-error_tracking-label = 向開發者傳送錯誤資訊
settings-interface-behavior-bvh_directory = BVH 紀錄儲存目錄
settings-interface-behavior-bvh_directory-description = 選擇儲存 BVH 紀錄文件的目錄,如此每次錄製 BVH 時不需要選擇儲存位置。
settings-interface-behavior-bvh_directory-label = 存放 BVH 紀錄的目錄
## Serial settings
@@ -879,8 +886,8 @@ settings-utils-advanced-reset_warning-cancel = 取消
settings-utils-advanced-open_data-v1 = 設定資料夾
settings-utils-advanced-open_data-description-v1 = 在檔案管理器中開啟 SlimeVR 的設定資料夾,該資料夾包含程式的設定。
settings-utils-advanced-open_data-label = 打開資料夾
settings-utils-advanced-open_logs = 錄檔資料夾
settings-utils-advanced-open_logs-description = 在檔案管理器中開啟 SlimeVR 的錄檔資料夾,該資料夾包含程式的錄檔。
settings-utils-advanced-open_logs = 錄檔資料夾
settings-utils-advanced-open_logs-description = 在檔案管理器中開啟 SlimeVR 的錄檔資料夾,該資料夾包含程式的錄檔。
settings-utils-advanced-open_logs-label = 打開資料夾
## Setup/onboarding menu
@@ -1000,7 +1007,7 @@ onboarding-connect_tracker-next = 所有的追蹤器都連接好了
onboarding-calibration_tutorial = IMU 校正教學
onboarding-calibration_tutorial-subtitle = 進行這項操作可以有效減少追蹤器發生飄移的機會
onboarding-calibration_tutorial-description = 每次在打開追蹤器開關時,需要將追蹤器平置一下來進行自動校正。你也可以透過按下「{ onboarding-calibration_tutorial-calibrate }」按鈕來進行手動校正,<b>校正過程中請勿移動追蹤器</b>
onboarding-calibration_tutorial-description-v1 = 開啟追蹤器開關後,將其放置在穩定的平面上一段時間以便進行校正。本頁僅提供操作教學——追蹤器電源開啟後即可隨時進行校正,無須回到本頁進行。首先請點選「{ onboarding-calibration_tutorial-calibrate }」按鈕,然後<b>不要移動追蹤器</b>
onboarding-calibration_tutorial-calibrate = 追蹤器已經放置在桌上了
onboarding-calibration_tutorial-status-waiting = 正在等待你完成動作
onboarding-calibration_tutorial-status-calibrating = 校正中

View File

@@ -61,6 +61,16 @@ import { FirmwareUpdate } from './components/firmware-update/FirmwareUpdate';
import { ConnectionLost } from './components/onboarding/pages/ConnectionLost';
import { VRCWarningsPage } from './components/vrc/VRCWarningsPage';
import { StayAlignedSetup } from './components/onboarding/pages/stay-aligned/StayAlignedSetup';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
CategoryScale,
LinearScale,
PointElement,
LineElement,
} from 'chart.js';
export const GH_REPO = 'SlimeVR/SlimeVR-Server';
export const VersionContext = createContext('');
@@ -73,6 +83,16 @@ function Layout() {
const { isMobile } = useBreakpoint('mobile');
useDiscordPresence();
ChartJS.register(
Title,
Tooltip,
Legend,
CategoryScale,
LinearScale,
PointElement,
LineElement
);
return (
<>
<SerialDetectionModal></SerialDetectionModal>
@@ -206,6 +226,11 @@ export default function App() {
}, []);
useEffect(() => {
// don't show update stuff when on android
if (window.__ANDROID__?.isThere()) {
setUpdateFound('');
return;
}
async function fetchReleases() {
const releases = await fetch(
`https://api.github.com/repos/${GH_REPO}/releases`

View File

@@ -66,9 +66,7 @@ export function WidgetsComponent() {
bodyPartsToReset="fingers"
></ResetButton>
<ClearMountingButton></ClearMountingButton>
{(typeof __ANDROID__ === 'undefined' || !__ANDROID__?.isThere()) && (
<BVHButton></BVHButton>
)}
{!window.__ANDROID__?.isThere() && <BVHButton></BVHButton>}
<TrackingPauseButton></TrackingPauseButton>
</div>
<div className="w-full">

View File

@@ -28,7 +28,7 @@ export function ArrowLink({
};
return classNames(
variantsMap[variant],
'flex gap-2 hover:fill-background-10 hover:text-background-10 fill-background-30 text-background-30'
'flex gap-2 hover:fill-background-10 fill-background-30 text-background-10'
);
}, [variant]);

View File

@@ -53,7 +53,7 @@ export function CheckBox({
className={classNames(
{
'rounded-lg': outlined,
'text-background-30': !outlined || disabled,
'text-background-10': !outlined || disabled,
'bg-background-60': outlined && color === 'primary',
'bg-background-70': outlined && color === 'secondary',
'bg-background-50': outlined && color === 'tertiary',

View File

@@ -12,6 +12,8 @@ import { FileIcon } from './icon/FileIcon';
import { UploadFileIcon } from './icon/UploadFileIcon';
import { Typography } from './Typography';
import { CloseIcon } from './icon/CloseIcon';
import { FolderIcon } from './icon/FolderIcon';
import { UploadFolderIcon } from './icon/UploadFolderIcon';
interface InputProps {
variant?: 'primary' | 'secondary';
@@ -22,9 +24,11 @@ interface InputProps {
export const FileInputContentBlank = ({
isDragging,
label,
directory = false,
}: {
isDragging: boolean;
label: string;
directory?: boolean;
}) => {
return (
<div
@@ -36,7 +40,11 @@ export const FileInputContentBlank = ({
)}
>
<span className="flex items-center space-x-2 pointer-events-none">
<UploadFileIcon isDragging={isDragging} />
{directory ? (
<UploadFolderIcon isDragging={isDragging} />
) : (
<UploadFileIcon isDragging={isDragging} />
)}
<div>
<Localized
id={label}
@@ -57,10 +65,12 @@ export const FileInputContentBlank = ({
export const FileInputContentFile = ({
importedFileName,
directory = false,
onClearPicker,
}: {
importedFileName: string;
onClearPicker: () => any;
directory?: boolean;
}) => {
return (
<div
@@ -72,7 +82,7 @@ export const FileInputContentFile = ({
>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2 px-4">
<FileIcon />
{directory ? <FolderIcon /> : <FileIcon />}
<span>{importedFileName}</span>
</div>
<span className="flex-grow"></span>

View File

@@ -47,19 +47,19 @@ export const InputInside = forwardRef<
const classes = useMemo(() => {
const variantsMap = {
primary: classNames({
'placeholder:text-background-30 bg-background-60 border-background-60':
'placeholder:text-background-10 placeholder:italic bg-background-60 border-background-60':
!disabled,
'text-background-30 placeholder:text-background-30 border-background-70 bg-background-70':
disabled,
}),
secondary: classNames({
'placeholder:text-background-30 bg-background-50 border-background-50':
'placeholder:text-background-10 placeholder:italic bg-background-50 border-background-50':
!disabled,
'text-background-40 placeholder:text-background-40 border-background-70 bg-background-70':
disabled,
}),
tertiary: classNames({
'placeholder:text-background-30 bg-background-40 border-background-40':
'placeholder:text-background-10 placeholder:italic bg-background-40 border-background-40':
!disabled,
'text-background-30 placeholder:text-background-30 border-background-70 bg-background-70':
disabled,

View File

@@ -51,9 +51,7 @@ export function Radio({
<div className="flex flex-col gap-2 pointer-events-none">
{children ? children : <Typography bold>{label}</Typography>}
{description && (
<Typography variant="standard" color="secondary">
{description}
</Typography>
<Typography variant="standard">{description}</Typography>
)}
</div>
</label>

View File

@@ -24,10 +24,11 @@ export function InnerTauriFileInput({
<div ref={ref} onClick={async () => onChange(await open({ directory }))}>
{value !== null
? FileInputContentFile({
directory,
importedFileName: value,
onClearPicker: () => onChange(null),
})
: FileInputContentBlank({ isDragging: false, label })}
: FileInputContentBlank({ isDragging: false, label, directory })}
</div>
);
}

View File

@@ -0,0 +1,12 @@
export function FolderIcon({ width = 24 }: { width?: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
width={width}
>
<path d="M19.5 21a3 3 0 0 0 3-3v-4.5a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3V18a3 3 0 0 0 3 3h15ZM1.5 10.146V6a3 3 0 0 1 3-3h5.379a2.25 2.25 0 0 1 1.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 0 1 3 3v1.146A4.483 4.483 0 0 0 19.5 9h-15a4.483 4.483 0 0 0-3 1.146Z" />
</svg>
);
}

View File

@@ -0,0 +1,25 @@
import classNames from 'classnames';
export function UploadFolderIcon({
width = 24,
isDragging = false,
}: {
width?: number;
isDragging?: boolean;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 5.5499997 4.7600007"
width={width}
fill="currentColor"
className={classNames('transition-transform', isDragging && 'scale-150')}
>
<path
fill-rule="evenodd"
d="m 4.76,4.76 c 0.44,0 0.79,-0.36 0.79,-0.79 v -2.39 c 0,-0.44 -0.36,-0.79 -0.79,-0.79 H 3.34 c -0.05,0 -0.1,-0.02 -0.14,-0.06 L 2.64,0.17 C 2.53,0.06 2.38,0 2.22,0 H 0.79 C 0.35,0 0,0.36 0,0.79 V 3.97 C 0,4.41 0.36,4.76 0.79,4.76 Z M 2.58,3.71 c 0,0.26 0.4,0.26 0.4,0 V 2.6 L 3.44,3.06 C 3.63,3.23 3.89,2.97 3.72,2.78 L 2.93,1.99 c -0.08,-0.08 -0.2,-0.08 -0.28,0 L 1.86,2.78 C 1.65,2.97 1.95,3.26 2.14,3.06 L 2.6,2.6 Z"
clip-rule="evenodd"
/>
</svg>
);
}

View File

@@ -240,7 +240,7 @@ export function AddImusStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-add_imus_step-description')}
</Typography>
</div>
@@ -297,7 +297,7 @@ export function AddImusStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -53,7 +53,7 @@ export function BoardPinsStep({
<>
<div className="flex flex-col w-full justify-between text-background-10">
<div className="flex flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-board_pins_step-description')}
</Typography>
</div>
@@ -172,7 +172,7 @@ export function BoardPinsStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -68,7 +68,7 @@ export function BuildStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-build_step-description')}
</Typography>
</div>
@@ -82,7 +82,7 @@ export function BuildStep({
: SlimeState.SAD
}
></LoaderIcon>
<Typography variant="section-title" color="secondary">
<Typography variant="section-title">
{l10n.getString('firmware_tool-build-' + buildStatus.status)}
</Typography>
</div>
@@ -91,7 +91,7 @@ export function BuildStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -85,9 +85,7 @@ function FirmwareToolContent() {
.getString('firmware_tool-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>

View File

@@ -22,7 +22,7 @@ export function FlashBtnStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flashbtn_step-description')}
</Typography>
{defaultConfig?.boardConfig.type ===

View File

@@ -160,7 +160,7 @@ function SerialDevicesList({
</Localized>
{Object.keys(devices).length === 0 ? (
<Localized id="firmware_tool-flash_method_serial-no_devices">
<Typography variant="standard" color="secondary"></Typography>
<Typography variant="standard"></Typography>
</Localized>
) : (
<Dropdown
@@ -274,7 +274,7 @@ function OTADevicesList({
</Localized>
{devices.length === 0 && (
<Localized id="firmware_tool-flash_method_ota-no_devices">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
)}
<div className="grid xs-settings:grid-cols-2 mobile-settings:grid-cols-1 gap-2">
@@ -347,7 +347,7 @@ export function FlashingMethodStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flash_method_step-description')}
</Typography>
</div>
@@ -416,7 +416,7 @@ export function FlashingMethodStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -164,7 +164,7 @@ export function FlashingStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flashing_step-description')}
</Typography>
</div>

View File

@@ -27,7 +27,7 @@ export function SelectBoardStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-board_step-description')}
</Typography>
</div>
@@ -91,7 +91,7 @@ export function SelectBoardStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -40,7 +40,7 @@ export function SelectFirmwareStep({
<>
<div className="flex flex-col w-full">
<div className="flex justify-between items-center mobile:flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-select_firmware_step-description')}
</Typography>
<div>
@@ -109,7 +109,7 @@ export function SelectFirmwareStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -320,7 +320,7 @@ export function FirmwareUpdate() {
<Typography variant="section-title"></Typography>
</Localized>
<Localized id="firmware_update-devices-description">
<Typography variant="standard" color="secondary"></Typography>
<Typography variant="standard"></Typography>
</Localized>
<div className="flex flex-col gap-4 overflow-y-auto xs:max-h-[530px]">
{devices.length === 0 &&

View File

@@ -28,8 +28,8 @@ export function SkipSetupButton({
<button
type="button"
className={classNames(
'text-background-40 hover:text-background-30',
'stroke-background-40 hover:stroke-background-30',
'text-background-10 hover:text-background-20',
'stroke-background-10 hover:stroke-background-20',
'absolute xs:-top-10 xs:right-4 mobile:top-0 mobile:right-0'
)}
onClick={onClick}

View File

@@ -132,9 +132,7 @@ export function CalibrationTutorialPage() {
id="onboarding-calibration_tutorial-description-v1"
elems={{ b: <b></b> }}
>
<Typography color="secondary">
Description on calibration of IMU
</Typography>
<Typography>Description on calibration of IMU</Typography>
</Localized>
<div>
<div className="xs:hidden flex flex-row justify-center">

View File

@@ -221,10 +221,10 @@ export function ConnectTrackersPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-connect_tracker-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-connect_tracker-description-p0-v1')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-connect_tracker-description-p1-v1')}
</Typography>
<div className="flex flex-col gap-2 py-5">
@@ -281,7 +281,7 @@ export function ConnectTrackersPage() {
{l10n.getString('onboarding-connect_tracker-usb')}
</Typography>
<div className="flex fill-background-10 gap-1">
<Typography color="secondary">
<Typography>
{l10n.getString(statusLabelMap[provisioningStatus])}
</Typography>
</div>
@@ -319,7 +319,7 @@ export function ConnectTrackersPage() {
</div>
</div>
<div style={{ gridArea: 't' }} className="flex items-center px-5">
<Typography color="secondary" bold>
<Typography bold>
{l10n.getString('onboarding-connect_tracker-connected_trackers', {
amount: connectedIMUTrackers.length,
})}

View File

@@ -20,7 +20,7 @@ export function DonePage() {
{l10n.getString('onboarding-done-title')}
</Typography>
<div className="flex flex-col items-center">
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-done-description')}
</Typography>
</div>

View File

@@ -22,7 +22,7 @@ export function EnterVRPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-enter_vr-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-enter_vr-description')}
</Typography>
</div>

View File

@@ -125,7 +125,7 @@ export function ResetTutorialPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-reset_tutorial')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-reset_tutorial-explanation')}
</Typography>
<div className="flex">
@@ -165,7 +165,7 @@ export function ResetTutorialPage() {
curIndex >= order.length && 'hidden'
)}
>
<Typography whitespace="whitespace-pre-line" color="secondary">
<Typography whitespace="whitespace-pre-line">
{l10n.getString(`onboarding-reset_tutorial-${curIndex}`, {
taps: tapSettings[curIndex],
})}
@@ -184,7 +184,7 @@ export function ResetTutorialPage() {
curIndex >= order.length && 'hidden'
)}
>
<Typography whitespace="whitespace-pre-line" color="secondary">
<Typography whitespace="whitespace-pre-line">
{l10n.getString(`onboarding-reset_tutorial-${curIndex}`, {
taps: tapSettings[curIndex],
})}

View File

@@ -35,9 +35,7 @@ export function WifiCredsPage() {
.getString('onboarding-wifi_creds-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
{!state.alonePage && (

View File

@@ -32,7 +32,7 @@ export function AutomaticProportionsPage() {
{l10n.getString('onboarding-automatic_proportions-title')}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-description'
)}

View File

@@ -52,7 +52,7 @@ export function ScaledProportionsPage() {
{l10n.getString('onboarding-scaled_proportions-title')}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-scaled_proportions-description')}
</Typography>
</div>

View File

@@ -72,7 +72,7 @@ export function CheckFloorHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-check_floor_height-description'
)}
@@ -81,7 +81,7 @@ export function CheckFloorHeightStep({
id="onboarding-automatic_proportions-check_floor_height-calculation_warning-v2"
elems={{ u: <span className="underline"></span> }}
>
<Typography color="secondary" bold>
<Typography bold>
Press the button to get your height!
</Typography>
</Localized>

View File

@@ -60,7 +60,7 @@ export function CheckHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-check_height-description-v2'
)}
@@ -69,7 +69,7 @@ export function CheckHeightStep({
id="onboarding-automatic_proportions-check_height-calculation_warning-v3"
elems={{ u: <span className="underline"></span> }}
>
<Typography color="secondary" bold>
<Typography bold>
Press the button to get your height!
</Typography>
</Localized>

View File

@@ -12,7 +12,7 @@ export function DoneStep({ variant }: { variant: 'onboarding' | 'alone' }) {
<Typography variant="section-title">
{l10n.getString('onboarding-automatic_proportions-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_proportions-done-description')}
</Typography>
</div>

View File

@@ -26,13 +26,13 @@ export function PreparationStep({
</Typography>
<div>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
</div>

View File

@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-put_trackers_on-description'
)}

View File

@@ -97,7 +97,7 @@ export function Recording({
'onboarding-automatic_proportions-recording-description-p0'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-recording-description-p1'
)}
@@ -110,7 +110,7 @@ export function Recording({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>
@@ -137,7 +137,7 @@ export function Recording({
)
.otherwise(() => undefined)}
></ProgressBar>
<Typography color="secondary">
<Typography>
{match([hasCalibration, hasRecording])
.returnType<ReactNode>()
.with([ProcessStatus.PENDING, ProcessStatus.FULFILLED], () =>

View File

@@ -31,7 +31,7 @@ export function RequirementsStep({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>

View File

@@ -63,7 +63,7 @@ export function StartRecording({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-start_recording-description'
)}
@@ -76,7 +76,7 @@ export function StartRecording({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>

View File

@@ -42,7 +42,7 @@ export function VerifyResultsStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-verify_results-description'
)}
@@ -65,7 +65,7 @@ export function VerifyResultsStep({
>
{bodyParts?.map(({ bone, label, value }) => (
<div className="flex justify-between" key={bone}>
<Typography color="secondary">{label}</Typography>
<Typography>{label}</Typography>
<Typography bold sentryMask>
{(value * 100).toFixed(2)} CM
</Typography>

View File

@@ -12,7 +12,7 @@ export function DoneStep({ variant }: { variant: 'onboarding' | 'alone' }) {
<Typography variant="section-title">
{l10n.getString('onboarding-scaled_proportions-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-scaled_proportions-done-description')}
</Typography>
</div>

View File

@@ -90,7 +90,7 @@ export function ManualHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-scaled_proportions-manual_height-description-v2'
)}

View File

@@ -26,7 +26,7 @@ export function ResetProportionsStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-scaled_proportions-reset_proportion-description'
)}

View File

@@ -33,7 +33,7 @@ export function AutomaticMountingPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-automatic_mounting-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_mounting-description')}
</Typography>
</div>

View File

@@ -99,7 +99,7 @@ export function ManualMountingPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-manual_mounting')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-manual_mounting-description')}
</Typography>
<TipBox>{l10n.getString('tips-find_tracker')}</TipBox>

View File

@@ -17,16 +17,12 @@ export function MountingChoose() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto px-4 pb-4">
<div className="flex flex-col gap-4 justify-center">
<div className="flex flex-col gap-8 justify-center">
<div className="xs:w-10/12 xs:max-w-[666px]">
<Typography variant="main-title">
{l10n.getString('onboarding-choose_mounting')}
</Typography>
<Typography
variant="standard"
color="secondary"
whitespace="whitespace-pre-line"
>
<Typography variant="standard" whitespace="whitespace-pre-line">
{l10n.getString('onboarding-choose_mounting-description')}
</Typography>
</div>
@@ -37,12 +33,19 @@ export function MountingChoose() {
>
<div
className={classNames(
'rounded-lg p-4 flex',
'rounded-lg p-4 flex relative',
!state.alonePage && 'bg-background-70',
state.alonePage && 'bg-background-60'
)}
>
<div className="flex flex-col gap-4">
<div className="bg-accent-background-30 absolute -left-4 -top-5 p-1.5 rounded-lg">
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-label-v2'
)}
</Typography>
</div>
<div className="flex flex-col gap-4 ">
<div className="flex flex-grow flex-col gap-4 max-w-sm">
<div>
<Typography variant="main-title" bold>
@@ -50,14 +53,9 @@ export function MountingChoose() {
'onboarding-choose_mounting-auto_mounting'
)}
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-label-v2'
)}
</Typography>
</div>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-description'
)}
@@ -98,14 +96,9 @@ export function MountingChoose() {
'onboarding-choose_mounting-manual_mounting'
)}
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-manual_mounting-label-v2'
)}
</Typography>
</div>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-choose_mounting-manual_mounting-description'
)}

View File

@@ -20,7 +20,7 @@ export function DoneStep({
<Typography variant="section-title">
{l10n.getString('onboarding-automatic_mounting-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_mounting-done-description')}
</Typography>
</div>
@@ -38,6 +38,11 @@ export function DoneStep({
{l10n.getString('onboarding-automatic_mounting-next')}
</Button>
)}
{variant === 'alone' && (
<Button className="flex gap-3" variant="primary" to="/">
{l10n.getString('onboarding-automatic_mounting-return-home')}
</Button>
)}
</div>
<SkeletonVisualizerWidget />

View File

@@ -27,12 +27,12 @@ export function MountingResetStep({
)}
</Typography>
<div className="flex flex-col gap-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-mounting_reset-step-0'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-mounting_reset-step-1'
)}

View File

@@ -26,13 +26,13 @@ export function PreparationStep({
</Typography>
<div>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
</div>

View File

@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-put_trackers_on-description'
)}

View File

@@ -136,7 +136,7 @@ export function StayAlignedSetup() {
<Typography variant="main-title">
{l10n.getString('onboarding-stay_aligned-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-stay_aligned-description')}
</Typography>
</div>

View File

@@ -11,7 +11,7 @@ export function DoneStep({ goTo }: VerticalStepComponentProps) {
<Typography variant="main-title"></Typography>
</Localized>
<Localized id="onboarding-stay_aligned-done-description-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<div className="flex gap-3 justify-between">

View File

@@ -16,13 +16,13 @@ export function PreparationStep({
<div className="flex flex-col flex-grow justify-between py-2 gap-2">
<div className="flex flex-col gap-1">
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<Localized id="onboarding-stay_aligned-preparation-tip">

View File

@@ -19,7 +19,7 @@ export function PutTrackersOnStep({ nextStep }: VerticalStepComponentProps) {
<div className="flex flex-grow flex-col gap-4">
<div>
<Localized id="onboarding-stay_aligned-put_trackers_on-description">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<div className="flex">

View File

@@ -30,9 +30,7 @@ function PosePage({
<div className="flex flex-col py-2">
<div className="flex flex-col gap-2">
{descriptionKeys.map((descriptionKey) => (
<Typography color="secondary">
{l10n.getString(descriptionKey)}
</Typography>
<Typography>{l10n.getString(descriptionKey)}</Typography>
))}
</div>
<div className="flex pt-1 items-center fill-background-50 justify-center px-12">

View File

@@ -11,16 +11,16 @@ export function VerifyMountingStep({
<div className="flex flex-grow flex-col gap-4 py-2">
<div className="flex flex-col gap-2">
<Localized id="onboarding-stay_aligned-verify_mounting-step-0">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-1">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-2">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-3">
<Typography color="secondary" />
<Typography />
</Localized>
</div>
<div className="flex gap-3 justify-between">

View File

@@ -43,7 +43,7 @@ const ItemContent = ({
mode,
})}
</Typography>
<Typography variant="standard" color="secondary">
<Typography variant="standard">
{l10n.getString('onboarding-assign_trackers-option-description', {
mode,
})}

View File

@@ -284,11 +284,11 @@ export function TrackersAssignPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-assign_trackers-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-assign_trackers-description')}
</Typography>
<div className="flex gap-1">
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-assign_trackers-assigned', {
assigned: assignedTrackers.length,
trackers: trackers.length,

View File

@@ -63,11 +63,11 @@ export function AdvancedSettings() {
<div className="grid gap-4">
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-gui')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-gui-description'
)}
@@ -95,11 +95,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-server')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-server-description'
)}
@@ -132,11 +132,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-all')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-all-description'
)}
@@ -168,11 +168,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-open_data-v1')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-open_data-description-v1'
)}
@@ -188,11 +188,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-open_logs')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-open_logs-description'
)}

View File

@@ -459,7 +459,7 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-steamvr')}
</Typography>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-steamvr-subtitle')}
</Typography>
<div className="flex flex-col py-2">
@@ -467,13 +467,11 @@ export function GeneralSettings() {
.getString('settings-general-steamvr-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<div className="flex flex-col pt-4"></div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-steamvr-trackers-tracker_toggling'
)}
@@ -485,9 +483,7 @@ export function GeneralSettings() {
)
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<CheckBox
@@ -608,7 +604,7 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-tracker_mechanics')}
</Typography>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-tracker_mechanics-filtering')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
@@ -618,9 +614,7 @@ export function GeneralSettings() {
)
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<Typography>
@@ -690,7 +684,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-5 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-tracker_mechanics-save_mounting_reset'
)}
@@ -699,7 +693,7 @@ export function GeneralSettings() {
id="settings-general-tracker_mechanics-save_mounting_reset-description"
elems={{ b: <b></b> }}
>
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<CheckBox
@@ -725,19 +719,19 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-fk_settings')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
<Typography bold>
<div className="flex flex-col pt-2 pb-4 gap-2">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-leg_tweak-skating_correction'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-skating_correction-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-4">
<div className="grid sm:grid-cols-1 gap-2 pb-4">
<CheckBox
variant="toggle"
outlined
@@ -761,18 +755,18 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-leg_fk')}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-floor_clip-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<div className="grid sm:grid-cols-1 gap-2 pb-3">
<CheckBox
variant="toggle"
outlined
@@ -784,7 +778,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-foot_plant-description'
)}
@@ -802,7 +796,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-toe_snap-description'
)}
@@ -821,10 +815,10 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-arm_fk')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-arm_fk-description'
)}
@@ -843,110 +837,114 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-reset_settings')}
</Typography>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetHmdPitch"
label={l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch'
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-v1'
)}
/>
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col gap-2">
<Typography>
{l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch-description'
)}
</Typography>
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetHmdPitch"
label={l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch'
)}
/>
</div>
<div className="flex flex-col gap-2 justify-end">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1'
)}
</Typography>
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-v1'
)}
/>
</div>
</div>
</div>
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-arm_fk-reset_mode-description'
)}
</Typography>
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2 pb-3">
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-back'
<div>
<Typography>
{l10n.getString(
'settings-general-fk_settings-arm_fk-reset_mode-description'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-back-description'
)}
value={'0'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-forward'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-forward-description'
)}
value={'1'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up-description'
)}
value={'2'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down-description'
)}
value={'3'}
></Radio>
</Typography>
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2 pb-3">
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-back'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-back-description'
)}
value={'0'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-forward'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-forward-description'
)}
value={'1'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up-description'
)}
value={'2'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down-description'
)}
value={'3'}
></Radio>
</div>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<div className="flex flex-col pt-2 pb-1">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints'
)}
</Typography>
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description'
)}
</Typography>
<div className="pt-2">
<Typography>
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description'
)}
</Typography>
</div>
</div>
<div className="grid sm:grid-cols-1 pb-3">
<CheckBox
@@ -963,12 +961,12 @@ export function GeneralSettings() {
{config?.debug && (
<>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-toggles'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-description'
)}
@@ -1003,14 +1001,14 @@ export function GeneralSettings() {
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<div className="flex flex-col">
<div className="flex flex-col pt-2 pb-3 gap-2">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-ratios'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-ratios-description'
)}
@@ -1112,12 +1110,12 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-self_localization-title'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-self_localization-description'
)}
@@ -1147,11 +1145,12 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-gesture_control')}
</Typography>
<Typography bold>
{l10n.getString('settings-general-gesture_control-subtitle')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
<Typography color="secondary">
<div className="flex flex-col pt-2 pb-4 gap-2">
<Typography variant="section-title">
{l10n.getString('settings-general-gesture_control-subtitle')}
</Typography>
<Typography>
{l10n.getString('settings-general-gesture_control-description')}
</Typography>
</div>
@@ -1267,12 +1266,12 @@ export function GeneralSettings() {
/>
</div>
<div className="grid sm:grid-cols-1 gap-2 pt-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-gesture_control-numberTrackersOverThreshold'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-gesture_control-numberTrackersOverThreshold-description'
)}

View File

@@ -130,11 +130,14 @@ export function InterfaceSettings() {
{l10n.getString('settings-interface-notifications')}
</Typography>
<Typography bold>
{l10n.getString('settings-general-interface-serial_detection')}
</Typography>
<div className="pt-2">
<Typography variant="section-title">
{l10n.getString('settings-general-interface-serial_detection')}
</Typography>
</div>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-serial_detection-description'
)}
@@ -152,11 +155,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-feedback_sound')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-feedback_sound-description'
)}
@@ -187,13 +190,13 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-connected_trackers_warning'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-connected_trackers_warning-description'
)}
@@ -221,130 +224,134 @@ export function InterfaceSettings() {
<Typography variant="main-title">
{l10n.getString('settings-interface-behavior')}
</Typography>
<div className="pt-2">
{isTrayAvailable && (
<>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-use_tray')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-use_tray-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.useTray"
label={l10n.getString(
'settings-general-interface-use_tray-label'
)}
/>
</div>
</>
)}
{isTrayAvailable && (
<>
<Typography bold>
{l10n.getString('settings-general-interface-use_tray')}
<Typography variant="section-title">
{l10n.getString('settings-general-interface-discord_presence')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-discord_presence-description'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.discordPresence"
label={l10n.getString(
'settings-general-interface-discord_presence-label'
)}
/>
</div>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-dev_mode')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-dev_mode-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.devmode"
label={l10n.getString(
'settings-general-interface-dev_mode-label'
)}
/>
</div>
<Typography variant="section-title">
{l10n.getString('settings-interface-behavior-error_tracking')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={
'settings-interface-behavior-error_tracking-description_v2'
}
elems={{
b: <b></b>,
}}
>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.errorTracking"
label={l10n.getString(
'settings-interface-behavior-error_tracking-label'
)}
/>
</div>
{isTauri() && (
<>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-use_tray-description'
'settings-interface-behavior-bvh_directory'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.useTray"
label={l10n.getString(
'settings-general-interface-use_tray-label'
)}
/>
</div>
</>
)}
<Typography bold>
{l10n.getString('settings-general-interface-discord_presence')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
{l10n.getString(
'settings-general-interface-discord_presence-description'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={
'settings-interface-behavior-bvh_directory-description'
}
>
<Typography></Typography>
</Localized>
</div>
<div className="grid gap-3 pb-5">
<TauriFileInput
name="behavior.bvhDirectory"
rules={{
required: false,
}}
control={control}
label="settings-interface-behavior-bvh_directory-label"
directory
/>
</div>
</>
)}
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.discordPresence"
label={l10n.getString(
'settings-general-interface-discord_presence-label'
)}
/>
</div>
<Typography bold>
{l10n.getString('settings-general-interface-dev_mode')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
{l10n.getString(
'settings-general-interface-dev_mode-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.devmode"
label={l10n.getString(
'settings-general-interface-dev_mode-label'
)}
/>
</div>
<Typography bold>
{l10n.getString('settings-interface-behavior-error_tracking')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={'settings-interface-behavior-error_tracking-description_v2'}
elems={{
b: <b></b>,
}}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
</Localized>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.errorTracking"
label={l10n.getString(
'settings-interface-behavior-error_tracking-label'
)}
/>
</div>
{isTauri() && (
<>
<Typography bold>
{l10n.getString('settings-interface-behavior-bvh_directory')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={'settings-interface-behavior-bvh_directory-description'}
>
<Typography color="secondary"></Typography>
</Localized>
</div>
<div className="grid gap-3 pb-5">
<TauriFileInput
name="behavior.bvhDirectory"
rules={{
required: false,
}}
control={control}
label="settings-interface-behavior-bvh_directory-label"
directory
/>
</div>
</>
)}
</>
</SettingsPagePaneLayout>
@@ -356,11 +363,13 @@ export function InterfaceSettings() {
<Typography variant="main-title">
{l10n.getString('settings-interface-appearance')}
</Typography>
<Typography bold>
{l10n.getString('settings-interface-appearance-decorations')}
</Typography>
<div className="pt-2">
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-decorations')}
</Typography>
</div>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-decorations-description'
)}
@@ -379,7 +388,7 @@ export function InterfaceSettings() {
</div>
<div className="pb-4">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-theme')}
</Typography>
<div className="flex flex-wrap gap-3 pt-2">
@@ -440,13 +449,13 @@ export function InterfaceSettings() {
</div>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-show-navbar-onboarding'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-show-navbar-onboarding-description'
)}
@@ -464,11 +473,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-font')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-font-description'
)}
@@ -513,11 +522,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-font_size')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-font_size-description'
)}
@@ -541,11 +550,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-lang')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-general-interface-lang-description')}
</Typography>
</div>

View File

@@ -95,7 +95,7 @@ export function MagnetometerToggleSetting({
return settingType === 'general' ? (
<>
<div className="flex flex-col pt-5 pb-3" id={id}>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-tracker_mechanics-use_mag_on_all_trackers'
)}
@@ -104,10 +104,7 @@ export function MagnetometerToggleSetting({
id="settings-general-tracker_mechanics-use_mag_on_all_trackers-description"
elems={{ b: <b></b> }}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
</div>
<CheckBox
@@ -139,10 +136,7 @@ export function MagnetometerToggleSetting({
),
}}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
<div className="flex">
<CheckBox

View File

@@ -110,17 +110,15 @@ export function OSCRouterSettings() {
.getString('settings-osc-router-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-router-enable-description')}
</Typography>
</div>
@@ -133,7 +131,7 @@ export function OSCRouterSettings() {
label={l10n.getString('settings-osc-router-enable-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-network')}
</Typography>
<div className="flex flex-col pb-2">
@@ -142,9 +140,7 @@ export function OSCRouterSettings() {
.getString('settings-osc-router-network-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
@@ -176,11 +172,11 @@ export function OSCRouterSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-router-network-address-description'
)}

View File

@@ -240,9 +240,7 @@ export function Serial() {
.getString('settings-serial-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>

View File

@@ -137,17 +137,15 @@ export function VMCSettings() {
.getString('settings-osc-vmc-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-enable-description')}
</Typography>
</div>
@@ -160,7 +158,7 @@ export function VMCSettings() {
label={l10n.getString('settings-osc-vmc-enable-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-network')}
</Typography>
<div className="flex flex-col pb-2">
@@ -169,9 +167,7 @@ export function VMCSettings() {
.getString('settings-osc-vmc-network-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
@@ -203,11 +199,11 @@ export function VMCSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-network-address-description')}
</Typography>
</div>
@@ -227,11 +223,11 @@ export function VMCSettings() {
label=""
></Input>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-vrm')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-vrm-description')}
</Typography>
</div>
@@ -254,11 +250,11 @@ export function VMCSettings() {
></FileInput>
{/* For some reason, linux (GNOME) is detecting the VRM file is a VRML */}
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-anchor_hip')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-anchor_hip-description')}
</Typography>
</div>
@@ -271,11 +267,11 @@ export function VMCSettings() {
label={l10n.getString('settings-osc-vmc-anchor_hip-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-mirror_tracking')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-mirror_tracking-description')}
</Typography>
</div>

View File

@@ -140,17 +140,15 @@ export function VRCOSCSettings() {
.getString('settings-osc-vrchat-description-v1')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vrchat-enable-description')}
</Typography>
</div>
@@ -164,18 +162,16 @@ export function VRCOSCSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-oscqueryEnabled')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n
.getString('settings-osc-vrchat-oscqueryEnabled-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</Typography>
</div>
@@ -191,11 +187,11 @@ export function VRCOSCSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vrchat-network-description-v1')}
</Typography>
</div>
@@ -227,11 +223,11 @@ export function VRCOSCSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-vrchat-network-address-description-v1'
)}
@@ -253,11 +249,11 @@ export function VRCOSCSettings() {
label=""
></Input>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network-trackers')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-vrchat-network-trackers-description'
)}

View File

@@ -40,9 +40,7 @@ function StaAlignedPoseModal({
</div>
<div className="flex flex-col gap-1">
{descriptionKeys.map((descriptionKey) => (
<Typography color="secondary">
{l10n.getString(descriptionKey)}
</Typography>
<Typography>{l10n.getString(descriptionKey)}</Typography>
))}
</div>
<div className="flex pt-1 items-center fill-background-50 justify-center px-12">

View File

@@ -182,10 +182,10 @@ export function StayAlignedSettings({
{l10n.getString('settings-stay_aligned')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-description')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-setup-description')}
</Typography>
<div className="flex mt-2">
@@ -199,7 +199,7 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-general-label')}
</Typography>
<div className="grid sm:grid-cols-2 gap-3 mt-2">
@@ -224,11 +224,11 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-relaxed_poses-label')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-stay_aligned-relaxed_poses-description'
)}
@@ -305,11 +305,11 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-debug-label')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-debug-description')}
</Typography>
</div>

View File

@@ -49,7 +49,7 @@ export function SingleTrackerBodyAssignmentMenu({
<Typography variant="mobile-title" bold>
{l10n.getString('body_assignment_menu')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('body_assignment_menu-description')}
</Typography>
<div className="flex">

View File

@@ -7,7 +7,7 @@ export function TrackerBattery({
value,
voltage,
disabled,
textColor = 'secondary',
textColor = 'primary',
}: {
/**
* a [0, 1] value range is expected

View File

@@ -0,0 +1,177 @@
import { useLocalization } from '@fluent/react';
import { useEffect, useState } from 'react';
import { Line } from 'react-chartjs-2';
import { TrackerDataT } from 'solarxr-protocol';
import { Button } from '@/components/commons/Button';
import { useConfig } from '@/hooks/config';
export function TrackerGraph({ tracker }: { tracker: TrackerDataT }) {
const { l10n } = useLocalization();
const { config } = useConfig();
type AxisData = {
x: number;
y: number;
time: number;
};
type ChartData = {
x: AxisData[];
y: AxisData[];
z: AxisData[];
};
const [chartData, setChartData] = useState<ChartData>({
x: [],
y: [],
z: [],
});
const [showTrackerGraph, setShowTrackerGraph] = useState(false);
const secondDuration = 60;
useEffect(() => {
if (!showTrackerGraph) {
return;
}
const newValue = tracker.info?.isImu
? tracker.linearAcceleration
: tracker.position;
if (!newValue) {
return;
}
const currentTime = new Date().getTime() / 1000;
const startTime = currentTime - secondDuration;
const updateData = (data: AxisData[], newSample: number) => {
const remapped = data
.filter((value) => value.time >= startTime)
.map((value) => ({ ...value, x: value.time - startTime }));
remapped.push({
time: currentTime,
x: secondDuration,
y: newSample,
});
return remapped;
};
const newData = {
x: updateData(chartData.x, newValue.x),
y: updateData(chartData.y, newValue.y),
z: updateData(chartData.z, newValue.z),
};
setChartData(newData);
}, [tracker]);
useEffect(() => {
if (!showTrackerGraph) {
setChartData({ x: [], y: [], z: [] });
}
}, [showTrackerGraph]);
return (
<>
<Button
variant="tertiary"
className="self-start"
onClick={() => setShowTrackerGraph(!showTrackerGraph)}
>
{l10n.getString(
showTrackerGraph
? 'tracker-settings-graph-hide-title'
: 'tracker-settings-graph-show-title'
)}
</Button>
{showTrackerGraph && (
<div className="h-96">
<Line
options={{
responsive: true,
animation: false,
font: {
family: config?.fonts.map((font) => `"${font}"`).join(','),
size: config?.textSize,
},
plugins: {
title: {
display: true,
text: l10n.getString(
tracker?.info?.isImu
? 'tracker-settings-graph-acceleration-title'
: 'tracker-settings-graph-position-title'
),
color: 'white',
},
tooltip: {
mode: 'index',
intersect: false,
animation: false,
callbacks: {
title: () => '',
},
},
legend: {
labels: {
color: 'white',
},
},
},
scales: {
x: {
type: 'linear',
min: 0,
max: secondDuration,
ticks: {
color: 'white',
},
},
y: {
min: -4,
max: 4,
ticks: {
color: 'white',
},
},
},
elements: {
point: {
radius: 0,
},
},
parsing: false,
normalized: true,
maintainAspectRatio: false,
}}
data={{
labels: ['X', 'Y', 'Z'],
datasets: [
{
label: 'X',
data: chartData.x,
borderColor: 'rgb(200, 50, 50)',
backgroundColor: 'rgb(200, 100, 100)',
},
{
label: 'Y',
data: chartData.y,
borderColor: 'rgb(50, 200, 50)',
backgroundColor: 'rgb(100, 200, 100)',
},
{
label: 'Z',
data: chartData.z,
borderColor: 'rgb(50, 50, 200)',
backgroundColor: 'rgb(100, 100, 200)',
},
],
}}
id="tracker-graph"
/>
</div>
)}
</>
);
}

View File

@@ -99,7 +99,7 @@ export function TrackerPartCard({
<WarningIcon></WarningIcon>
</div>
)}
<Typography color="secondary">
<Typography variant="section-title">
{l10n.getString('body_part-' + BodyPart[role])}
</Typography>
{td?.map(({ tracker }, index) => (
@@ -110,7 +110,7 @@ export function TrackerPartCard({
/>
))}
{!td && (
<Typography>
<Typography color="text-background-30">
{l10n.getString('tracker-part_card-unassigned')}
</Typography>
)}

View File

@@ -40,6 +40,7 @@ import semver from 'semver';
import { useSetAtom } from 'jotai';
import { ignoredTrackersAtom } from '@/store/app-store';
import { checkForUpdate } from '@/hooks/firmware-update';
import { TrackerGraph } from './TrackerGraph';
const rotationsLabels: [Quaternion, string][] = [
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
@@ -211,10 +212,10 @@ export function TrackerSettingsPage() {
</Typography>
</Localized>
<div className="flex gap-2">
<Typography color="secondary">
<Typography>
v{tracker?.device?.hardwareInfo?.firmwareVersion}
</Typography>
<Typography color="secondary">-</Typography>
<Typography>-</Typography>
{updateUnavailable && (
<Localized id="tracker-settings-update-unavailable">
<Typography>Cannot be updated (DIY)</Typography>
@@ -272,7 +273,7 @@ export function TrackerSettingsPage() {
<div className="flex flex-col bg-background-70 p-3 rounded-lg gap-2 overflow-x-auto">
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-manufacturer')}
</Typography>
<Typography>
@@ -280,13 +281,13 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-display_name')}
</Typography>
<Typography>{tracker?.tracker.info?.displayName}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-custom_name')}
</Typography>
<Typography sentry-mask>
@@ -294,9 +295,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-url')}
</Typography>
<Typography>{l10n.getString('tracker-infos-url')}</Typography>
<Typography>
udp://
{IPv4.fromNumber(
@@ -305,7 +304,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-hardware_identifier')}
</Typography>
<Typography>
@@ -313,7 +312,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-data_support')}
</Typography>
<Typography>
@@ -323,9 +322,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-imu')}
</Typography>
<Typography>{l10n.getString('tracker-infos-imu')}</Typography>
<Typography>
{tracker?.tracker.info?.imuType
? ImuType[tracker?.tracker.info?.imuType]
@@ -333,13 +330,13 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-board_type')}
</Typography>
<Typography>{boardType}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-magnetometer')}
</Typography>
<Typography>
@@ -352,7 +349,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-network_version')}
</Typography>
<Typography>
@@ -377,7 +374,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-assignment_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'tracker-settings-assignment_section-description'
)}
@@ -419,7 +416,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-mounting_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'tracker-settings-mounting_section-description'
)}
@@ -468,7 +465,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-name_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-settings-name_section-description')}
</Typography>
<Input
@@ -488,7 +485,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-forget')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-settings-forget-description')}
</Typography>
<Button
@@ -509,6 +506,7 @@ export function TrackerSettingsPage() {
</Button>
</div>
)}
{tracker && <TrackerGraph tracker={tracker.tracker} />}
</div>
</div>
</form>

View File

@@ -35,7 +35,7 @@ export function TrackerStatus({ status }: { status: number }) {
<div className="flex flex-col justify-center">
<div className={classNames('w-2 h-2 rounded-full', statusClass)}></div>
</div>
<Typography color="secondary" whitespace="whitespace-nowrap">
<Typography whitespace="whitespace-nowrap">
{l10n.getString(statusLabel)}
</Typography>
</div>

View File

@@ -130,7 +130,7 @@ export function VRCWarningsPage() {
<Typography variant="main-title" />
</Localized>
<Localized id={'vrc_config-page-desc'}>
<Typography variant="standard" color="secondary" />
<Typography variant="standard" />
</Localized>
</div>
<div className="w-full mt-4 gap-2 flex flex-col">
@@ -142,7 +142,7 @@ export function VRCWarningsPage() {
<Typography variant="section-title" />
</Localized>
<Localized id="vrc_config-page-big_menu-desc">
<Typography color="secondary" />
<Typography />
</Localized>
<Table>
<SettingRow
@@ -248,7 +248,7 @@ export function VRCWarningsPage() {
<Typography variant="section-title" />
</Localized>
<Localized id="vrc_config-page-wrist_menu-desc">
<Typography color="secondary" />
<Typography />
</Localized>
<Table>
<SettingRow
@@ -304,7 +304,7 @@ export function VRCWarningsPage() {
a: <A href="https://docs.slimevr.dev/tools/vrchat-config.html"></A>,
}}
>
<Typography color="secondary" />
<Typography />
</Localized>
</div>
</div>

View File

@@ -75,9 +75,7 @@ export function DeveloperModeWidget() {
return (
<form className="bg-background-60 flex flex-col w-full rounded-md px-2">
<div className="mt-2 px-1">
<Typography color="secondary">
{l10n.getString('widget-developer_mode')}
</Typography>
<Typography>{l10n.getString('widget-developer_mode')}</Typography>
</div>
{Object.entries(toggles).map(makeToggle)}
</form>

View File

@@ -128,7 +128,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.position && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-position')}
</Typography>
<Typography>{formatVector3(tracker.position, 2)}</Typography>
@@ -136,14 +136,14 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
)}
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-rotation_raw')}
</Typography>
<Typography>{formatVector3(rotationRaw, 2)}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-rotation_preview')}
</Typography>
<Typography>{formatVector3(rotationIdent, 2)}</Typography>
@@ -151,7 +151,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.linearAcceleration && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-acceleration')}
</Typography>
<Typography>
@@ -162,7 +162,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.rawMagneticVector && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-magnetometer')}
</Typography>
<Typography>{formatVector3(tracker.rawMagneticVector, 1)}</Typography>
@@ -171,7 +171,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{!!tracker.stayAligned && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-stay_aligned')}
</Typography>
<StayAlignedInfo color="primary" tracker={tracker} />

View File

@@ -68,9 +68,7 @@ export function OverlayWidget() {
return !loading ? (
<form className="bg-background-60 flex flex-col w-full rounded-md px-2">
<div className="mt-2 px-1">
<Typography color="secondary">
{l10n.getString('widget-overlay')}
</Typography>
<Typography>{l10n.getString('widget-overlay')}</Typography>
</div>
<CheckBox
control={control}

View File

@@ -27,6 +27,14 @@ const hash = (str: string) => {
const firstAsset = (assets: any[], name: string) =>
assets.find((asset: any) => asset.name === name && asset.browser_download_url);
const todaysRange = (deployData: [number, Date][]): number => {
let maxRange = 0;
for (const [range, date] of deployData) {
if (Date.now() >= date.getTime()) maxRange = range;
}
return maxRange;
};
const checkUserCanUpdate = async (url: string, fwVersion: string) => {
if (!url) return true;
const deployDataJson = JSON.parse(
@@ -57,11 +65,7 @@ const checkUserCanUpdate = async (url: string, fwVersion: string) => {
)
return false; // Dates in the wrong order / cancel
const todayUpdateRange = deployData.find(([, date], index) => {
if (index === 0 && Date.now() < date.getTime()) return true;
return Date.now() >= date.getTime();
})?.[0];
const todayUpdateRange = todaysRange(deployData);
if (!todayUpdateRange) return false;
const uniqueUserKey = `${await hostname()}-${await locale()}-${platform()}-${version()}`;

10
gui/src/vite-env.d.ts vendored
View File

@@ -4,14 +4,14 @@
declare const __COMMIT_HASH__: string;
declare const __VERSION_TAG__: string;
declare const __GIT_CLEAN__: boolean;
declare const __ANDROID__:
| {
isThere: () => boolean;
}
| undefined;
interface Window {
readonly isTauri: boolean;
readonly __ANDROID__:
| {
isThere: () => boolean;
}
| undefined;
}
declare module 'tailwind-gradient-mask-image';

31
pnpm-lock.yaml generated
View File

@@ -83,6 +83,9 @@ importers:
browser-fs-access:
specifier: ^0.35.0
version: 0.35.0
chart.js:
specifier: ^4.5.0
version: 4.5.0
classnames:
specifier: ^2.5.1
version: 2.5.1
@@ -104,6 +107,9 @@ importers:
react:
specifier: ^18.3.1
version: 18.3.1
react-chartjs-2:
specifier: ^5.3.0
version: 5.3.0(chart.js@4.5.0)(react@18.3.1)
react-dom:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
@@ -720,6 +726,9 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@kurkle/color@0.3.4':
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
'@mediapipe/tasks-vision@0.10.8':
resolution: {integrity: sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==}
@@ -1780,6 +1789,10 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
chart.js@4.5.0:
resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
engines: {pnpm: '>=8'}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -2470,6 +2483,7 @@ packages:
got-fetch@5.1.10:
resolution: {integrity: sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==}
engines: {node: '>=14.0.0'}
deprecated: please use built-in fetch in nodejs
peerDependencies:
got: ^12.0.0
@@ -3542,6 +3556,12 @@ packages:
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
engines: {node: '>=10'}
react-chartjs-2@5.3.0:
resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==}
peerDependencies:
chart.js: ^4.1.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-composer@5.0.3:
resolution: {integrity: sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==}
peerDependencies:
@@ -4959,6 +4979,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@kurkle/color@0.3.4': {}
'@mediapipe/tasks-vision@0.10.8': {}
'@mgit-at/typescript-flatbuffers-codegen@0.1.3':
@@ -6072,6 +6094,10 @@ snapshots:
character-reference-invalid@2.0.1: {}
chart.js@4.5.0:
dependencies:
'@kurkle/color': 0.3.4
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -8159,6 +8185,11 @@ snapshots:
quick-lru@5.1.1: {}
react-chartjs-2@5.3.0(chart.js@4.5.0)(react@18.3.1):
dependencies:
chart.js: 4.5.0
react: 18.3.1
react-composer@5.0.3(react@18.3.1):
dependencies:
prop-types: 15.8.1

View File

@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
@@ -26,7 +28,11 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
<service

View File

@@ -8,6 +8,8 @@ import androidx.appcompat.app.AppCompatActivity
import dev.slimevr.Keybinding
import dev.slimevr.VRServer
import dev.slimevr.android.serial.AndroidSerialHandler
import dev.slimevr.android.tracking.trackers.hid.AndroidHIDManager
import dev.slimevr.tracking.trackers.Tracker
import io.eiren.util.logging.LogManager
import io.ktor.http.CacheControl
import io.ktor.http.CacheControl.Visibility
@@ -60,6 +62,15 @@ fun main(activity: AppCompatActivity) {
},
)
vrServer.start()
// Start service for USB HID trackers
val androidHidManager = AndroidHIDManager(
"Sensors HID service",
{ tracker: Tracker -> vrServer.registerTracker(tracker) },
activity,
)
androidHidManager.start()
Keybinding(vrServer)
vrServer.join()
LogManager.closeLogger()

View File

@@ -1,10 +1,13 @@
package dev.slimevr.android.serial
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -15,10 +18,8 @@ import io.eiren.util.logging.LogManager
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.stream.Stream
import kotlin.concurrent.timerTask
import kotlin.streams.asSequence
import kotlin.streams.asStream
import dev.slimevr.serial.SerialPort as SlimeSerialPort
@@ -43,12 +44,11 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
private var usbIoManager: SerialInputOutputManager? = null
private val listeners: MutableList<SerialListener> = CopyOnWriteArrayList()
private val getDevicesTimer = Timer("GetDevicesTimer")
private var watchingNewDevices = false
private var lastKnownPorts = setOf<SerialPortWrapper>()
private val manager = activity.getSystemService(Context.USB_SERVICE) as UsbManager
private var currentPort: SerialPortWrapper? = null
private var requestingPermission: String = ""
private var readBuffer: StringBuilder = StringBuilder(1024)
override val isConnected: Boolean
get() = currentPort?.port?.isOpen ?: false
@@ -60,37 +60,70 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
.filter { isKnownBoard(it) }
.asStream()
val usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_ATTACHED, UsbManager.ACTION_USB_DEVICE_DETACHED -> {
// Use device from `UsbManager.EXTRA_DEVICE` if this is a problem
detectNewPorts()
}
ACTION_USB_PERMISSION -> {
// TODO: We can probably receive this event in the server to avoid
// polling, but for now we can just ignore it. (Note: This event is
// not currently registered, so it will never fire.)
}
}
}
}
init {
startWatchingNewDevices()
val intentFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
// Listen for USB device attach/detach
ContextCompat.registerReceiver(
activity,
usbReceiver,
intentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED,
)
// Detect initial serial ports
detectNewPorts()
}
private fun getPorts(): List<UsbSerialDriver> = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
private fun startWatchingNewDevices() {
if (watchingNewDevices) return
watchingNewDevices = true
getDevicesTimer.scheduleAtFixedRate(
timerTask {
try {
detectNewPorts()
} catch (t: Throwable) {
LogManager.severe(
"[SerialHandler] Error while watching for new devices, cancelling the \"getDevicesTimer\".",
t,
)
getDevicesTimer.cancel()
}
},
0,
3000,
)
}
private fun onNewDevice(port: SerialPortWrapper) {
// If we missed clearing this on disconnect/close, clear it on discovery
if (requestingPermission == port.portLocation) {
requestingPermission = ""
}
LogManager.info("[SerialHandler] Device added: ${port.descriptivePortName}")
listeners.forEach { it.onNewSerialDevice(port) }
}
private fun onDeviceDel(port: SerialPortWrapper) {
// Remove permission request on disconnect so reconnecting re-requests
if (requestingPermission == port.portLocation) {
requestingPermission = ""
}
// If we're currently using this port, close it
currentPort?.portLocation.let { currentPortLocation ->
if (currentPortLocation == port.portLocation) {
closeSerial()
}
}
// If this port is still open for whatever reason, close it
if (port.port.isOpen) {
port.port.close()
}
LogManager.info("[SerialHandler] Device removed: ${port.descriptivePortName}")
listeners.forEach { it.onSerialDeviceDeleted(port) }
}
@@ -154,9 +187,11 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
flags,
)
if (requestingPermission != newPort.portLocation) {
println("Requesting permission for ${newPort.portLocation}")
LogManager.info("[SerialHandler] Requesting permission for ${newPort.portLocation}")
manager.requestPermission(newPort.port.device, usbPermissionIntent)
requestingPermission = newPort.portLocation
} else {
LogManager.info("[SerialHandler] Already requested permission for ${newPort.portLocation}, skipping")
}
LogManager.warning(
"[SerialHandler] Can't open serial port ${newPort.descriptivePortName}, invalid permissions",
@@ -164,11 +199,13 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
return false
}
// If we have permission, we aren't requesting anymore
requestingPermission = ""
val connection = manager.openDevice(newPort.port.device)
if (connection == null) {
LogManager.warning(
"[SerialHandler] Can't open serial port ${newPort.descriptivePortName}, connection failed",
)
return false
}
@@ -186,7 +223,7 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
@Synchronized
private fun writeSerial(serialText: String, print: Boolean = false) {
try {
usbIoManager?.writeAsync("${serialText}\n".toByteArray())
currentPort?.port?.write("${serialText}\n".toByteArray(), 0)
if (print) {
addLog("-> $serialText\n")
}
@@ -224,6 +261,8 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
usbIoManager?.stop()
usbIoManager = null
currentPort = null
requestingPermission = ""
readBuffer.clear()
} catch (e: Exception) {
LogManager.warning(
"[SerialHandler] Error closing port ${currentPort?.descriptivePortName}",
@@ -233,7 +272,7 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
}
override fun write(buff: ByteArray) {
usbIoManager?.writeAsync(buff)
currentPort?.port?.write(buff, 0)
}
@Synchronized
@@ -251,14 +290,21 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
override fun onNewData(data: ByteArray?) {
if (data != null) {
val s = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(data)).toString()
addLog(s, false)
// Collect serial in a buffer until newline (or character limit)
// This is somewhat of a workaround for Android serial buffer being smaller
// than on desktop, so we don't read full lines and it causes parsing issues
readBuffer.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(data)))
if (readBuffer.contains('\n') || readBuffer.length >= 1024) {
addLog(readBuffer.toString(), false)
readBuffer.clear()
}
}
}
override fun onRunError(e: java.lang.Exception?) {}
companion object {
private val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
private const val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
}
}

View File

@@ -0,0 +1,93 @@
package dev.slimevr.android.tracking.trackers.hid
import android.hardware.usb.UsbConstants
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint
import android.hardware.usb.UsbInterface
import android.hardware.usb.UsbManager
import java.io.Closeable
/**
* A wrapper over Android's [UsbDevice] for HID devices.
*/
class AndroidHIDDevice(hidDevice: UsbDevice, usbManager: UsbManager) : Closeable {
val deviceName = hidDevice.deviceName
val serialNumber = hidDevice.serialNumber
val manufacturerName = hidDevice.manufacturerName
val productName = hidDevice.productName
val hidInterface: UsbInterface
val endpointIn: UsbEndpoint
val endpointOut: UsbEndpoint?
val deviceConnection: UsbDeviceConnection
init {
hidInterface = findHidInterface(hidDevice)!!
val (endpointIn, endpointOut) = findHidIO(hidInterface)
this.endpointIn = endpointIn!!
this.endpointOut = endpointOut
deviceConnection = usbManager.openDevice(hidDevice)!!
deviceConnection.claimInterface(hidInterface, true)
}
override fun close() {
deviceConnection.releaseInterface(hidInterface)
deviceConnection.close()
}
companion object {
/**
* Find the HID interface.
*
* @return
* Return the HID interface if found, otherwise null.
*/
private fun findHidInterface(usbDevice: UsbDevice): UsbInterface? {
val interfaceCount: Int = usbDevice.interfaceCount
for (interfaceIndex in 0 until interfaceCount) {
val usbInterface = usbDevice.getInterface(interfaceIndex)
if (usbInterface.interfaceClass == UsbConstants.USB_CLASS_HID) {
return usbInterface
}
}
return null
}
/**
* Find the HID endpoints.
*
* @return
* Return the HID endpoints if found, otherwise null.
*/
private fun findHidIO(usbInterface: UsbInterface): Pair<UsbEndpoint?, UsbEndpoint?> {
val endpointCount: Int = usbInterface.endpointCount
var usbEndpointIn: UsbEndpoint? = null
var usbEndpointOut: UsbEndpoint? = null
for (endpointIndex in 0 until endpointCount) {
val usbEndpoint = usbInterface.getEndpoint(endpointIndex)
if (usbEndpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT) {
if (usbEndpoint.direction == UsbConstants.USB_DIR_OUT) {
usbEndpointOut = usbEndpoint
} else {
usbEndpointIn = usbEndpoint
}
}
}
return Pair(usbEndpointIn, usbEndpointOut)
}
}
}

View File

@@ -0,0 +1,254 @@
package dev.slimevr.android.tracking.trackers.hid
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import androidx.core.content.ContextCompat
import dev.slimevr.tracking.trackers.Device
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerStatus
import dev.slimevr.tracking.trackers.hid.HIDCommon
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE
import dev.slimevr.tracking.trackers.hid.HIDDevice
import io.eiren.util.logging.LogManager
import java.nio.ByteBuffer
import java.util.function.Consumer
const val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
/**
* Handles Android USB Host HID dongles and receives tracker data from them.
*/
class AndroidHIDManager(
name: String,
private val trackersConsumer: Consumer<Tracker>,
private val context: Context,
) : Thread(name) {
private val devices: MutableList<HIDDevice> = mutableListOf()
private val devicesBySerial: MutableMap<String, MutableList<Int>> = HashMap()
private val devicesByHID: MutableMap<UsbDevice, MutableList<Int>> = HashMap()
private val connByHID: MutableMap<UsbDevice, AndroidHIDDevice> = HashMap()
private val lastDataByHID: MutableMap<UsbDevice, Int> = HashMap()
private val usbManager: UsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
val usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
(intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) as UsbDevice?)?.let {
checkConfigureDevice(it, requestPermission = true)
}
}
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
(intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) as UsbDevice?)?.let {
removeDevice(it)
}
}
ACTION_USB_PERMISSION -> {
deviceEnumerate(false)
}
}
}
}
private fun proceedWithDeviceConfiguration(hidDevice: UsbDevice) {
// This is the original logic from checkConfigureDevice after permission is confirmed
LogManager.info("[TrackerServer] USB Permission granted for ${hidDevice.deviceName}. Proceeding with configuration.")
// Close any existing connection (do we still have one?)
this.connByHID[hidDevice]?.close()
// Open new HID connection with USB device
this.connByHID[hidDevice] = AndroidHIDDevice(hidDevice, usbManager)
val serial = hidDevice.serialNumber ?: "Unknown USB Device ${hidDevice.deviceId}"
this.devicesBySerial[serial]?.let {
this.devicesByHID[hidDevice] = it
synchronized(this.devices) {
for (id in it) {
val device = this.devices[id]
for (value in device.trackers.values) {
if (value.status == TrackerStatus.DISCONNECTED) value.status = TrackerStatus.OK
}
}
}
LogManager.info("[TrackerServer] Linked HID device reattached: $serial")
return
}
val list: MutableList<Int> = mutableListOf()
this.devicesBySerial[serial] = list
this.devicesByHID[hidDevice] = list
this.lastDataByHID[hidDevice] = 0 // initialize last data received
LogManager.info("[TrackerServer] (Probably) Compatible HID device detected: $serial")
}
fun checkConfigureDevice(usbDevice: UsbDevice, requestPermission: Boolean = false) {
if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && usbDevice.productId == HID_TRACKER_RECEIVER_PID) {
if (usbManager.hasPermission(usbDevice)) {
LogManager.info("[TrackerServer] Already have permission for ${usbDevice.deviceName}")
proceedWithDeviceConfiguration(usbDevice)
} else if (requestPermission) {
LogManager.info("[TrackerServer] Requesting permission for ${usbDevice.deviceName}")
val permissionIntent = PendingIntent.getBroadcast(
context,
0,
Intent(ACTION_USB_PERMISSION).apply { setPackage(context.packageName) }, // Explicitly set package
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
)
usbManager.requestPermission(usbDevice, permissionIntent)
}
}
}
private fun removeDevice(hidDevice: UsbDevice) {
this.devicesByHID[hidDevice]?.let {
synchronized(this.devices) {
for (id in it) {
val device = this.devices[id]
for (value in device.trackers.values) {
if (value.status == TrackerStatus.OK) {
value.status =
TrackerStatus.DISCONNECTED
}
}
}
}
this.devicesByHID.remove(hidDevice)
val oldConn = this.connByHID.remove(hidDevice)
val serial = oldConn?.serialNumber ?: "Unknown"
oldConn?.close()
LogManager.info("[TrackerServer] Linked HID device removed: $serial")
}
}
private fun dataRead() {
synchronized(devicesByHID) {
var devicesPresent = false
var devicesDataReceived = false
val q = intArrayOf(0, 0, 0, 0)
val a = intArrayOf(0, 0, 0)
val m = intArrayOf(0, 0, 0)
for ((hidDevice, deviceList) in devicesByHID) {
val dataReceived = ByteArray(64)
val conn = connByHID[hidDevice]!!
val dataRead = conn.deviceConnection.bulkTransfer(conn.endpointIn, dataReceived, dataReceived.size, 0)
// LogManager.info("[TrackerServer] HID data read ($dataRead bytes): ${dataReceived.contentToString()}")
devicesPresent = true // Even if the device has no data
if (dataRead > 0) {
// Process data
// The data is always received as 64 bytes, this check no longer works
if (dataRead % PACKET_SIZE != 0) {
LogManager.info("[TrackerServer] Malformed HID packet, ignoring")
continue // Don't continue with this data
}
devicesDataReceived = true // Data is received and is valid (not malformed)
lastDataByHID[hidDevice] = 0 // reset last data received
val packetCount = dataRead / PACKET_SIZE
var i = 0
while (i < packetCount * PACKET_SIZE) {
// Common packet data
val packetType = dataReceived[i].toUByte().toInt()
val id = dataReceived[i + 1].toUByte().toInt()
val deviceId = id
// Register device
if (packetType == 255) { // device register packet from receiver
val buffer = ByteBuffer.wrap(dataReceived, i + 2, 8)
buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
val addr = buffer.getLong() and 0xFFFFFFFFFFFF
val deviceName = String.format("%012X", addr)
HIDCommon.deviceIdLookup(devices, hidDevice.serialNumber, deviceId, deviceName, deviceList) // register device
// server wants tracker to be unique, so use combination of hid serial and full id
i += PACKET_SIZE
continue
}
val device: HIDDevice? = HIDCommon.deviceIdLookup(devices, hidDevice.serialNumber, deviceId, null, deviceList)
if (device == null) { // not registered yet
i += PACKET_SIZE
continue
}
HIDCommon.processPacket(dataReceived, i, packetType, device, q, a, m, trackersConsumer)
i += PACKET_SIZE
}
// LogManager.info("[TrackerServer] HID received $packetCount tracker packets")
} else {
lastDataByHID[hidDevice] = lastDataByHID[hidDevice]!! + 1 // increment last data received
}
}
if (!devicesPresent) {
sleep(10) // No hid device, "empty loop" so sleep to save the poor cpu
} else if (!devicesDataReceived) {
sleep(1) // read has no timeout, no data also causes an "empty loop"
}
}
}
private fun deviceEnumerate(requestPermission: Boolean = false) {
val hidDeviceList: MutableList<UsbDevice> = usbManager.deviceList.values.filter {
it.vendorId == HID_TRACKER_RECEIVER_VID && it.productId == HID_TRACKER_RECEIVER_PID
}.toMutableList()
synchronized(devicesByHID) {
// Work on devicesByHid and add/remove as necessary
val removeList: MutableList<UsbDevice> = devicesByHID.keys.toMutableList()
removeList.removeAll(hidDeviceList)
for (device in removeList) {
removeDevice(device)
}
hidDeviceList.removeAll(devicesByHID.keys) // addList
for (device in hidDeviceList) {
// This will handle permission check/request
checkConfigureDevice(device, requestPermission)
}
}
}
override fun run() {
val intentFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
intentFilter.addAction(ACTION_USB_PERMISSION)
// Listen for USB device attach/detach
ContextCompat.registerReceiver(
context,
usbReceiver,
intentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED,
)
// Enumerate existing devices
deviceEnumerate(true)
// Data read loop
while (true) {
try {
sleep(0) // Possible performance impact
} catch (e: InterruptedException) {
currentThread().interrupt()
break
}
dataRead() // not in try catch?
}
}
fun getDevices(): List<Device> = devices
companion object {
private const val resetSourceName = "TrackerServer"
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="4617" product-id="30352" />
</resources>

View File

@@ -34,7 +34,6 @@ import solarxr_protocol.rpc.*
import kotlin.io.path.Path
class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeader>() {
private var currTransactionId: Long = 0
private val mainScope = CoroutineScope(SupervisorJob())
init {
@@ -238,7 +237,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val localIp = getLocalIp() ?: return
val response = ServerInfosResponse
.createServerInfosResponse(fbb, fbb.createString(localIp))
val outbound = this.createRPCMessage(fbb, RpcMessage.ServerInfosResponse, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.ServerInfosResponse, response, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -251,7 +250,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val config = api.server.configManager.vrConfig.overlay
val response = OverlayDisplayModeResponse
.createOverlayDisplayModeResponse(fbb, config.isVisible, config.isMirrored)
val outbound = this.createRPCMessage(fbb, RpcMessage.OverlayDisplayModeResponse, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.OverlayDisplayModeResponse, response, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -283,7 +282,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
// might not be a good idea maybe let the client ask again
val fbb = FlatBufferBuilder(300)
val config = createSkeletonConfig(fbb, api.server.humanPoseManager)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -297,7 +296,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(300)
val config = createSkeletonConfig(fbb, api.server.humanPoseManager)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -332,7 +331,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(40)
val status = RecordBVHStatus
.createRecordBVHStatus(fbb, api.server.bvhRecorder.isRecording)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -343,7 +342,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(40)
val status = RecordBVHStatus
.createRecordBVHStatus(fbb, api.server.bvhRecorder.isRecording)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -495,13 +494,16 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
}
}
fun createRPCMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int): Int {
@JvmOverloads
fun createRPCMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int, respondTo: RpcMessageHeader? = null): Int {
val data = IntArray(1)
RpcMessageHeader.startRpcMessageHeader(fbb)
RpcMessageHeader.addMessage(fbb, messageOffset)
RpcMessageHeader.addMessageType(fbb, messageType)
RpcMessageHeader.addTxId(fbb, TransactionId.createTransactionId(fbb, currTransactionId++))
respondTo?.txId()?.let { txId ->
RpcMessageHeader.addTxId(fbb, TransactionId.createTransactionId(fbb, txId.id()))
}
data[0] = RpcMessageHeader.endRpcMessageHeader(fbb)
val messages = MessageBundle.createRpcMsgsVector(fbb, data)
@@ -525,7 +527,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val response = StatusSystemResponseT()
response.currentStatuses = statuses
val offset = StatusSystemResponse.pack(fbb, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.StatusSystemResponse, offset)
val outbound = this.createRPCMessage(fbb, RpcMessage.StatusSystemResponse, offset, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -557,7 +559,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0f,
)
}
fbb.finish(createRPCMessage(fbb, RpcMessage.HeightResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.HeightResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
@@ -572,7 +574,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0,
api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
return
}
@@ -584,7 +586,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
tracker.config.shouldHaveMagEnabled == true,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
@@ -604,7 +606,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0,
api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
return
@@ -623,7 +625,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
state,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
return
}
@@ -640,7 +642,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
state,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
}
@@ -660,7 +662,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
configManager.saveConfig()
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
private fun onDetectStayAlignedRelaxedPoseRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
@@ -692,7 +694,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
LogManager.info("[detectStayAlignedRelaxedPose] pose=$pose $relaxedPose")
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
private fun onResetStayAlignedRelaxedPoseRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
@@ -720,13 +722,13 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
LogManager.info("[resetStayAlignedRelaxedPose] pose=$pose")
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
fun sendSettingsChangedResponse(conn: GenericConnection) {
fun sendSettingsChangedResponse(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
val fbb = FlatBufferBuilder(32)
val settings = RPCSettingsBuilder.createSettingsResponse(fbb, api.server)
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings)
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}

Some files were not shown because too many files have changed in this diff Show More