mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
67 Commits
height-con
...
rest-calib
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0155a461da | ||
|
|
de16acbcf4 | ||
|
|
486cb295ac | ||
|
|
dd3fe24294 | ||
|
|
59bdedecbb | ||
|
|
fc3d049019 | ||
|
|
de150e7349 | ||
|
|
06b3f61d37 | ||
|
|
82b96d5ebe | ||
|
|
f2b4d468c2 | ||
|
|
1c0f5c381b | ||
|
|
fb25421ab0 | ||
|
|
4890b4a71c | ||
|
|
e38732a81c | ||
|
|
2a78354b17 | ||
|
|
3fb4347277 | ||
|
|
081e88ead4 | ||
|
|
73f41f8fc6 | ||
|
|
f233f59079 | ||
|
|
9644b00690 | ||
|
|
543e319c25 | ||
|
|
b7ef70f5c6 | ||
|
|
af171f8812 | ||
|
|
8ec3cade06 | ||
|
|
bec8be46ca | ||
|
|
ac7f809132 | ||
|
|
fcb241fab8 | ||
|
|
fbdcf2fa2d | ||
|
|
73c821e07b | ||
|
|
422ddd7ee8 | ||
|
|
943ad974ec | ||
|
|
12a5d59e89 | ||
|
|
85286651dd | ||
|
|
e9b3efe3d5 | ||
|
|
0f06ac0253 | ||
|
|
75fa1aa1e2 | ||
|
|
7d642a21f5 | ||
|
|
4e1421180c | ||
|
|
f0a72645d7 | ||
|
|
86b8e0a904 | ||
|
|
789e6a6962 | ||
|
|
f03b300d72 | ||
|
|
6b0822c0f6 | ||
|
|
d9774cab87 | ||
|
|
f07f9f3718 | ||
|
|
486be0973b | ||
|
|
9246dd00d3 | ||
|
|
baf515791d | ||
|
|
876450d764 | ||
|
|
44e90e255b | ||
|
|
41026ab851 | ||
|
|
d8509c431d | ||
|
|
66df65eb80 | ||
|
|
30641a5809 | ||
|
|
574523daec | ||
|
|
1913605d16 | ||
|
|
8aeecab51f | ||
|
|
9fd5d6a187 | ||
|
|
4568ebb41a | ||
|
|
2777d8af89 | ||
|
|
0c09c22306 | ||
|
|
3f9b997ffa | ||
|
|
df379ee234 | ||
|
|
acd628637e | ||
|
|
155dbfbff1 | ||
|
|
eda3d74c54 | ||
|
|
4b9f393cee |
32
.github/CODEOWNERS
vendored
32
.github/CODEOWNERS
vendored
@@ -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
|
||||
|
||||
10
.github/workflows/build-gui.yml
vendored
10
.github/workflows/build-gui.yml
vendored
@@ -21,13 +21,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- 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'
|
||||
@@ -59,13 +59,13 @@ jobs:
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'aarch64' || 'amd64' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Set up Linux dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.3
|
||||
with:
|
||||
packages: libgtk-3-dev webkit2gtk-4.1 libappindicator3-dev librsvg2-dev patchelf
|
||||
# Increment to invalidate the cache
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
34
.github/workflows/gradle.yaml
vendored
34
.github/workflows/gradle.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -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'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -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'
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
bundle-android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -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'
|
||||
@@ -154,17 +154,17 @@ jobs:
|
||||
env:
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'aarch64' || 'amd64' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
|
||||
- name: Set up Linux dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.3
|
||||
with:
|
||||
packages: |
|
||||
build-essential curl wget file libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev
|
||||
@@ -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'
|
||||
@@ -252,11 +252,11 @@ jobs:
|
||||
needs: [build, test]
|
||||
if: contains(fromJSON('["workflow_dispatch", "create"]'), github.event_name)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
@@ -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'
|
||||
@@ -332,11 +332,11 @@ jobs:
|
||||
env:
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'win-aarch64' || 'win64' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
@@ -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'
|
||||
|
||||
2
.github/workflows/label.yml
vendored
2
.github/workflows/label.yml
vendored
@@ -17,6 +17,6 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v6
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
2
.github/workflows/pontoon-pr.yml
vendored
2
.github/workflows/pontoon-pr.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: repo-sync/pull-request@v2
|
||||
|
||||
2
.github/workflows/rebase.yml
vendored
2
.github/workflows/rebase.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: pontoon
|
||||
submodules: recursive
|
||||
|
||||
@@ -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 = منفذ الدخول
|
||||
|
||||
@@ -971,6 +971,7 @@ onboarding-connect_tracker-next = I connected all my trackers
|
||||
onboarding-calibration_tutorial = IMU Calibration Tutorial
|
||||
onboarding-calibration_tutorial-subtitle = This will help reduce tracker drifting!
|
||||
onboarding-calibration_tutorial-description-v1 = After turning on your trackers, place them on a stable surface for a moment to allow for calibration. Calibration can be performed at any time after the trackers are powered on—this page simply provides a tutorial. To begin, click the "{ onboarding-calibration_tutorial-calibrate }" button, then <b>do not move your trackers!</b>
|
||||
onboarding-calibration_tutorial-description-no-ready-v1 = After turning on your trackers, place them on a stable surface for a couple seconds to allow for calibration. Calibration can be performed at any time after the trackers are powered on—this page simply provides a tutorial. To begin, put down your trackers, then <b>do not move them!</b>
|
||||
onboarding-calibration_tutorial-calibrate = I placed my trackers on a table
|
||||
onboarding-calibration_tutorial-status-waiting = Waiting for you
|
||||
onboarding-calibration_tutorial-status-calibrating = Calibrating
|
||||
@@ -1125,6 +1126,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = 避免关节旋转超过人体骨骼角度限制
|
||||
|
||||
@@ -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 = 校正中
|
||||
|
||||
@@ -65,6 +65,10 @@ work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
</provides>
|
||||
|
||||
<releases>
|
||||
<release version="0.16.2" date="2025-08-01"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.2</url></release>
|
||||
<release version="0.16.1" date="2025-07-27"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.1</url></release>
|
||||
<release version="0.16.1~rc.2" type="development" date="2025-07-17"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.1-rc.2</url></release>
|
||||
<release version="0.16.1~rc.1" type="development" date="2025-07-04"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.1-rc.1</url></release>
|
||||
<release version="0.16.0" date="2025-07-01"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0</url></release>
|
||||
<release version="0.16.0~rc.2" type="development" date="2025-06-20"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0-rc.2</url></release>
|
||||
<release version="0.16.0~rc.1" type="development" date="2025-05-27"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0-rc.1</url></release>
|
||||
|
||||
@@ -206,6 +206,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`
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
12
gui/src/components/commons/icon/FolderIcon.tsx
Normal file
12
gui/src/components/commons/icon/FolderIcon.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
25
gui/src/components/commons/icon/UploadFolderIcon.tsx
Normal file
25
gui/src/components/commons/icon/UploadFolderIcon.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 ===
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Vector3 } from 'three';
|
||||
import { useTimeout } from '@/hooks/timeout';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { connectedIMUTrackersAtom } from '@/store/app-store';
|
||||
import { RestCalibrationStatus } from 'solarxr-protocol';
|
||||
|
||||
export enum CalibrationStatus {
|
||||
SUCCESS,
|
||||
@@ -40,9 +41,25 @@ export function CalibrationTutorialPage() {
|
||||
const connectedIMUTrackers = useAtomValue(connectedIMUTrackersAtom);
|
||||
const restCalibrationTrackers =
|
||||
useRestCalibrationTrackers(connectedIMUTrackers);
|
||||
|
||||
const useRestCalibrationFlag = useMemo(
|
||||
() =>
|
||||
restCalibrationTrackers.length > 0 &&
|
||||
restCalibrationTrackers.every(
|
||||
(tracker) =>
|
||||
tracker.tracker.info?.restCalibrationStatus !==
|
||||
RestCalibrationStatus.NOT_SUPPORTED
|
||||
),
|
||||
[restCalibrationTrackers]
|
||||
);
|
||||
|
||||
const [rested, setRested] = useState(false);
|
||||
const lastValueMap = useRef(new Map<number, Vector3[]>());
|
||||
useEffect(() => {
|
||||
if (useRestCalibrationFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
const accelLength = restCalibrationTrackers.every((x) => {
|
||||
if (
|
||||
x.tracker.trackerId?.trackerNum === undefined ||
|
||||
@@ -71,6 +88,29 @@ export function CalibrationTutorialPage() {
|
||||
}, [restCalibrationTrackers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!useRestCalibrationFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
restCalibrationTrackers.some(
|
||||
(tracker) =>
|
||||
tracker.tracker.info?.restCalibrationStatus !==
|
||||
RestCalibrationStatus.CALIBRATED
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCalibrationStatus(CalibrationStatus.SUCCESS);
|
||||
abortCountdown();
|
||||
}, [restCalibrationTrackers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (useRestCalibrationFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (calibrationStatus === CalibrationStatus.CALIBRATING && !rested) {
|
||||
setCalibrationStatus(CalibrationStatus.ERROR);
|
||||
abortCountdown();
|
||||
@@ -129,12 +169,14 @@ export function CalibrationTutorialPage() {
|
||||
</Typography>
|
||||
</div>
|
||||
<Localized
|
||||
id="onboarding-calibration_tutorial-description-v1"
|
||||
id={
|
||||
useRestCalibrationFlag
|
||||
? 'onboarding-calibration_tutorial-description-no-ready-v1'
|
||||
: '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">
|
||||
@@ -179,7 +221,9 @@ export function CalibrationTutorialPage() {
|
||||
disabled={isCounting || !rested}
|
||||
className={classNames(
|
||||
'xs:ml-auto',
|
||||
CalibrationStatus.SUCCESS === calibrationStatus && 'hidden'
|
||||
(CalibrationStatus.SUCCESS === calibrationStatus ||
|
||||
useRestCalibrationFlag) &&
|
||||
'hidden'
|
||||
)}
|
||||
>
|
||||
{l10n.getString('onboarding-calibration_tutorial-calibrate')}
|
||||
@@ -189,8 +233,14 @@ export function CalibrationTutorialPage() {
|
||||
to="/onboarding/assign-tutorial"
|
||||
className={classNames(
|
||||
'xs:ml-auto',
|
||||
CalibrationStatus.SUCCESS !== calibrationStatus && 'hidden'
|
||||
CalibrationStatus.SUCCESS !== calibrationStatus &&
|
||||
!useRestCalibrationFlag &&
|
||||
'hidden'
|
||||
)}
|
||||
disabled={
|
||||
useRestCalibrationFlag &&
|
||||
calibrationStatus !== CalibrationStatus.SUCCESS
|
||||
}
|
||||
>
|
||||
{l10n.getString('onboarding-continue')}
|
||||
</Button>
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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],
|
||||
})}
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
|
||||
)}
|
||||
</Typography>
|
||||
<div>
|
||||
<Typography color="secondary">
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-automatic_proportions-put_trackers_on-description'
|
||||
)}
|
||||
|
||||
@@ -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], () =>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function RequirementsStep({
|
||||
.split('\n')
|
||||
.map((line, i) => (
|
||||
<li key={i}>
|
||||
<Typography color="secondary">{line}</Typography>
|
||||
<Typography>{line}</Typography>
|
||||
</li>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -90,7 +90,7 @@ export function ManualHeightStep({
|
||||
)}
|
||||
</Typography>
|
||||
<div>
|
||||
<Typography color="secondary">
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-scaled_proportions-manual_height-description-v2'
|
||||
)}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function ResetProportionsStep({
|
||||
)}
|
||||
</Typography>
|
||||
<div>
|
||||
<Typography color="secondary">
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-scaled_proportions-reset_proportion-description'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
|
||||
)}
|
||||
</Typography>
|
||||
<div>
|
||||
<Typography color="secondary">
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-automatic_mounting-put_trackers_on-description'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -43,7 +43,7 @@ const ItemContent = ({
|
||||
mode,
|
||||
})}
|
||||
</Typography>
|
||||
<Typography variant="standard" color="secondary">
|
||||
<Typography variant="standard">
|
||||
{l10n.getString('onboarding-assign_trackers-option-description', {
|
||||
mode,
|
||||
})}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
)}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -7,7 +7,7 @@ export function TrackerBattery({
|
||||
value,
|
||||
voltage,
|
||||
disabled,
|
||||
textColor = 'secondary',
|
||||
textColor = 'primary',
|
||||
}: {
|
||||
/**
|
||||
* a [0, 1] value range is expected
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -211,10 +211,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 +272,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 +280,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 +294,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 +303,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 +311,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 +321,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 +329,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 +348,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 +373,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 +415,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 +464,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 +484,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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
10
gui/src/vite-env.d.ts
vendored
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
4
server/android/src/main/res/xml/device_filter.xml
Normal file
4
server/android/src/main/res/xml/device_filter.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<usb-device vendor-id="4617" product-id="30352" />
|
||||
</resources>
|
||||
@@ -153,6 +153,12 @@ public class DataFeedBuilder {
|
||||
|
||||
TrackerInfo.addDataSupport(fbb, tracker.getTrackerDataType().getSolarType());
|
||||
|
||||
TrackerInfo
|
||||
.addRestCalibrationStatus(
|
||||
fbb,
|
||||
tracker.getRestCalibrationStatus().getSolarType()
|
||||
);
|
||||
|
||||
return TrackerInfo.endTrackerInfo(fbb);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user