Compare commits
707 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8eba520090 | ||
|
|
05c421f51f | ||
|
|
629310f953 | ||
|
|
e7e0ee9d7d | ||
|
|
c9e82adda2 | ||
|
|
4b237d0b1b | ||
|
|
2dda66bc55 | ||
|
|
6a25a584b2 | ||
|
|
90fddbf66a | ||
|
|
7d0f2b0e08 | ||
|
|
3f022cd09e | ||
|
|
78f9780147 | ||
|
|
aa9d9486f3 | ||
|
|
aeae54d39e | ||
|
|
c972bb005d | ||
|
|
7913b2e839 | ||
|
|
51e9ecfd72 | ||
|
|
9b624f5d9a | ||
|
|
c3e2987e82 | ||
|
|
f7d34480de | ||
|
|
5247891ff0 | ||
|
|
e5c05fc864 | ||
|
|
b33d3cd41f | ||
|
|
e88357fb40 | ||
|
|
49994286fc | ||
|
|
cae2573a98 | ||
|
|
18aae19573 | ||
|
|
def3da6580 | ||
|
|
b5f12b46e0 | ||
|
|
5c59c4e385 | ||
|
|
7fd94c36f5 | ||
|
|
bb4861635d | ||
|
|
0a2d5674f7 | ||
|
|
1ac0a380db | ||
|
|
171877ee49 | ||
|
|
e45da2b979 | ||
|
|
46fedc2d3a | ||
|
|
6f8037f9f9 | ||
|
|
9d63191ff3 | ||
|
|
1bcf8467a6 | ||
|
|
eb55a3e1d4 | ||
|
|
57e220ac34 | ||
|
|
584f3b6afe | ||
|
|
b959ac56af | ||
|
|
32a3a010cf | ||
|
|
1624ed3282 | ||
|
|
1b27ea6940 | ||
|
|
70fb9b4a5a | ||
|
|
c0229e107b | ||
|
|
45d11ba176 | ||
|
|
d0b117ab36 | ||
|
|
3d3e6faad7 | ||
|
|
5b44eaddfa | ||
|
|
85ea2a0d06 | ||
|
|
ba146e0fb7 | ||
|
|
6dbe80050f | ||
|
|
1dd11ce579 | ||
|
|
317630628f | ||
|
|
d1f72c9280 | ||
|
|
e349440ce0 | ||
|
|
ae94de39b1 | ||
|
|
813c1340a8 | ||
|
|
d8a7fa978b | ||
|
|
d9988cc658 | ||
|
|
f38360e4f1 | ||
|
|
a6e17c96b8 | ||
|
|
b149b87fa7 | ||
|
|
79b1a96e8c | ||
|
|
cef818b430 | ||
|
|
dd8e9a623f | ||
|
|
fcad1142b0 | ||
|
|
af629007e1 | ||
|
|
25374a1d6a | ||
|
|
2f2f57a46f | ||
|
|
eed7dd98af | ||
|
|
660599b5e1 | ||
|
|
3d2aa988a0 | ||
|
|
f3d32da4cd | ||
|
|
9f9d45c450 | ||
|
|
85671f5497 | ||
|
|
081c2eb191 | ||
|
|
c3225c10c6 | ||
|
|
d5059ed48c | ||
|
|
8b1e4cb21a | ||
|
|
6242afda42 | ||
|
|
4f44e6c7c7 | ||
|
|
6fec8c6d07 | ||
|
|
1348f9f5ef | ||
|
|
1b5a4bf369 | ||
|
|
426f914f4d | ||
|
|
caa11f9dfc | ||
|
|
d1ed22d3d3 | ||
|
|
67ea5edda6 | ||
|
|
8c6d5e2369 | ||
|
|
c5e05d2940 | ||
|
|
8adf3fe591 | ||
|
|
101c403138 | ||
|
|
5d1dbb27e1 | ||
|
|
0e73480e8a | ||
|
|
58b0153cdc | ||
|
|
378cc12401 | ||
|
|
0146c5825c | ||
|
|
5cc2b391f8 | ||
|
|
80bced317c | ||
|
|
3f51170be7 | ||
|
|
ad7bd26577 | ||
|
|
820b8b21ec | ||
|
|
12fc9f0377 | ||
|
|
b475099b58 | ||
|
|
3ea3fbcd99 | ||
|
|
28fae6f8db | ||
|
|
d466871762 | ||
|
|
87850ee485 | ||
|
|
86cfa7ce4d | ||
|
|
0315e523b5 | ||
|
|
ad99dfb439 | ||
|
|
c7bf9a6d24 | ||
|
|
38ffa9bb6d | ||
|
|
1ac0191964 | ||
|
|
75fab32a32 | ||
|
|
cf3eccde64 | ||
|
|
5a0240a01a | ||
|
|
390d6f3b51 | ||
|
|
29121913fd | ||
|
|
7489d6ed34 | ||
|
|
5d288063ad | ||
|
|
efce9d3748 | ||
|
|
e9c0b79b73 | ||
|
|
29268da5c0 | ||
|
|
41a87b1c9b | ||
|
|
612d99ae70 | ||
|
|
d878789fcf | ||
|
|
2674da1185 | ||
|
|
8d90663799 | ||
|
|
bc3cc4ce14 | ||
|
|
b5159c9776 | ||
|
|
9ee09319d7 | ||
|
|
7818582f3f | ||
|
|
2ed0726b76 | ||
|
|
0b7f79340b | ||
|
|
c22b5ce421 | ||
|
|
078b140966 | ||
|
|
c3e4e86c4e | ||
|
|
d0b1a9c822 | ||
|
|
531503f789 | ||
|
|
3abf984d19 | ||
|
|
e875cbbcbc | ||
|
|
e0dff69d7c | ||
|
|
23141305c1 | ||
|
|
04b5bfac9d | ||
|
|
663170681a | ||
|
|
b1318567e3 | ||
|
|
d1a02c52a9 | ||
|
|
b3efcd19db | ||
|
|
66e4e4e0a0 | ||
|
|
7c37fdb24e | ||
|
|
ceb1cc77d8 | ||
|
|
8df4726d22 | ||
|
|
408df5273d | ||
|
|
7a4d087be2 | ||
|
|
f7249a883f | ||
|
|
b1ef647819 | ||
|
|
11a15ab5b1 | ||
|
|
f6ffd63b05 | ||
|
|
879065f420 | ||
|
|
1169f68eb2 | ||
|
|
0afe9d2ef8 | ||
|
|
738bf20dc6 | ||
|
|
c02b95346f | ||
|
|
db33c561ab | ||
|
|
df2cfa1e57 | ||
|
|
d40e2c2ce2 | ||
|
|
215f3b9f44 | ||
|
|
766cf9ae72 | ||
|
|
26d808deb7 | ||
|
|
4b887332ef | ||
|
|
f9259c976d | ||
|
|
39c3285a0a | ||
|
|
57f3624ee7 | ||
|
|
5a4b656a74 | ||
|
|
bf7e13d923 | ||
|
|
dedec668e1 | ||
|
|
1e4013be98 | ||
|
|
76c3210788 | ||
|
|
4a99eff854 | ||
|
|
237dabce17 | ||
|
|
6f36f7624a | ||
|
|
1dea626422 | ||
|
|
c74cc539c9 | ||
|
|
910e32bb3e | ||
|
|
6ad3c862ce | ||
|
|
05819baf97 | ||
|
|
d84ba2aef5 | ||
|
|
ec9e7d94bc | ||
|
|
a800749b87 | ||
|
|
675c69af60 | ||
|
|
76fe83452e | ||
|
|
6d940503af | ||
|
|
0ea5b58b50 | ||
|
|
94c079815f | ||
|
|
11ab7a6cb6 | ||
|
|
7f67135e69 | ||
|
|
a1717bbf7a | ||
|
|
c99375364d | ||
|
|
94460ed7ab | ||
|
|
413332462f | ||
|
|
18386d57d6 | ||
|
|
f72045438e | ||
|
|
17f74c693c | ||
|
|
fb3057b80a | ||
|
|
4eec83e5f4 | ||
|
|
f94850c120 | ||
|
|
3fb87a4688 | ||
|
|
488c3ec22c | ||
|
|
cccf57d37a | ||
|
|
8f1be283a4 | ||
|
|
049465fc41 | ||
|
|
00be864bbf | ||
|
|
494d362ec6 | ||
|
|
2c3ac89844 | ||
|
|
9205f6cab7 | ||
|
|
d0ee364fb9 | ||
|
|
3a80ad0632 | ||
|
|
93c353bbc6 | ||
|
|
9f883dccec | ||
|
|
ad3b697517 | ||
|
|
5f3097185e | ||
|
|
7ac7f7d424 | ||
|
|
ba58f4df6a | ||
|
|
297bfddf6c | ||
|
|
e4d0aca744 | ||
|
|
8b5ac226c3 | ||
|
|
cfc264fa33 | ||
|
|
9e12958ca6 | ||
|
|
8609fb9e97 | ||
|
|
9d6dda8b17 | ||
|
|
92b64f0f12 | ||
|
|
ffda9f6b2e | ||
|
|
ae881565e8 | ||
|
|
2b46032329 | ||
|
|
f4ee1ff73d | ||
|
|
a54e850de8 | ||
|
|
3e554c1b8a | ||
|
|
a7c442d785 | ||
|
|
677fa71ba1 | ||
|
|
9f3f34ce70 | ||
|
|
2d2b6588b3 | ||
|
|
f6e1cc7747 | ||
|
|
e04357c815 | ||
|
|
17d75cfee0 | ||
|
|
44fa266697 | ||
|
|
57f7ea6745 | ||
|
|
326c7e969a | ||
|
|
23f9b3b276 | ||
|
|
1e74deede3 | ||
|
|
4906d9551e | ||
|
|
83b40d3351 | ||
|
|
ce8929af04 | ||
|
|
c2a51e4d57 | ||
|
|
5fae72fb03 | ||
|
|
1b6b6e30ad | ||
|
|
451d687198 | ||
|
|
90144f13c8 | ||
|
|
9fc9b66745 | ||
|
|
2317ad94f5 | ||
|
|
58d1f2de96 | ||
|
|
de101b3576 | ||
|
|
9fbfc43655 | ||
|
|
e3e37023e8 | ||
|
|
63409b61ea | ||
|
|
c827e7725b | ||
|
|
0bcaed719b | ||
|
|
c77d73c460 | ||
|
|
9fc5d41e1b | ||
|
|
ac0cb9e9cb | ||
|
|
f961318035 | ||
|
|
52fa670293 | ||
|
|
d0c3e0ae8d | ||
|
|
0b2491ead5 | ||
|
|
1723d19882 | ||
|
|
416f96fe44 | ||
|
|
7b700b4a0c | ||
|
|
bde578fb9d | ||
|
|
c41dc490ed | ||
|
|
6d9e816d19 | ||
|
|
8f19afee88 | ||
|
|
784ec877d8 | ||
|
|
48c509ef54 | ||
|
|
0a5f06816d | ||
|
|
ecbeaf10ed | ||
|
|
c8e4918b4e | ||
|
|
4ab5609dbd | ||
|
|
cfe8eb62e7 | ||
|
|
f2ceb84969 | ||
|
|
039628d7e4 | ||
|
|
91fb4bc035 | ||
|
|
d89ccc3401 | ||
|
|
7ab9a37989 | ||
|
|
cf7054b6da | ||
|
|
758f9e8f2f | ||
|
|
fef21906f5 | ||
|
|
4ae37944d9 | ||
|
|
37ce6a69c1 | ||
|
|
aaed8fbd49 | ||
|
|
5fbac9d861 | ||
|
|
c54298709f | ||
|
|
b7d1637b18 | ||
|
|
56402526f7 | ||
|
|
015fa551b7 | ||
|
|
a681f0e5b3 | ||
|
|
281810dfbb | ||
|
|
9482e60dfa | ||
|
|
38b8e65d53 | ||
|
|
74ccaa6cf1 | ||
|
|
4d2083df27 | ||
|
|
98068232a6 | ||
|
|
60711df671 | ||
|
|
c6b7c11418 | ||
|
|
ece8811293 | ||
|
|
8c95c491b9 | ||
|
|
45091565b4 | ||
|
|
7c0222189a | ||
|
|
a84c735761 | ||
|
|
946c5d4527 | ||
|
|
2ac6208302 | ||
|
|
0cd65c825c | ||
|
|
43d97a7b97 | ||
|
|
9923556212 | ||
|
|
0a65783637 | ||
|
|
b5bd7e0a26 | ||
|
|
82779d101b | ||
|
|
9c18d349dd | ||
|
|
8c74071e30 | ||
|
|
256e5079cf | ||
|
|
4263e86189 | ||
|
|
dadb0bb378 | ||
|
|
8ba25ca840 | ||
|
|
09ac04331f | ||
|
|
c21caa76d5 | ||
|
|
4073d8fc32 | ||
|
|
f8b7be8572 | ||
|
|
d6ab811de0 | ||
|
|
9fa6722f2b | ||
|
|
c51204e9cd | ||
|
|
74794d8610 | ||
|
|
16978f5acd | ||
|
|
71e24d0cb9 | ||
|
|
9a45f99b0e | ||
|
|
7f829f56a3 | ||
|
|
347531f4fe | ||
|
|
a1c33a0852 | ||
|
|
9cd441654e | ||
|
|
88c866a735 | ||
|
|
b11492c3f3 | ||
|
|
52e30b6323 | ||
|
|
1ebad806f9 | ||
|
|
51129d3b5d | ||
|
|
379e1cdcf0 | ||
|
|
0a8f76cfd4 | ||
|
|
a382698c32 | ||
|
|
d43002952c | ||
|
|
718f1d02c6 | ||
|
|
542de22550 | ||
|
|
327d458f00 | ||
|
|
5f206dd12e | ||
|
|
e18ce338e9 | ||
|
|
51e6255e9d | ||
|
|
7fa7e6c2cc | ||
|
|
710d154817 | ||
|
|
f354a10a81 | ||
|
|
8bb8135f41 | ||
|
|
e7b9968519 | ||
|
|
bfc58d51f2 | ||
|
|
c158022da5 | ||
|
|
9e010b0026 | ||
|
|
a085b09e07 | ||
|
|
37da4ab7fe | ||
|
|
c5945d784b | ||
|
|
16ca08446b | ||
|
|
b487350714 | ||
|
|
753b12b49e | ||
|
|
0d90cf9c20 | ||
|
|
658fd2916d | ||
|
|
ed4ea675fb | ||
|
|
2746fd7a67 | ||
|
|
a6b92c60b0 | ||
|
|
d4d36a65ec | ||
|
|
97df8ee12f | ||
|
|
2ab637b4e8 | ||
|
|
9a821b051f | ||
|
|
e2f09fc93d | ||
|
|
891d8e0468 | ||
|
|
494e31e41f | ||
|
|
b369ae6a2a | ||
|
|
5c22ef0192 | ||
|
|
d99cbb9c85 | ||
|
|
4f14f01830 | ||
|
|
930b5c701a | ||
|
|
bd9e2c47a3 | ||
|
|
53ca2cf881 | ||
|
|
55e17e7625 | ||
|
|
13b37aa2a9 | ||
|
|
fe4dde69ea | ||
|
|
0268a5a3ec | ||
|
|
4bddb529d4 | ||
|
|
435f5d1751 | ||
|
|
af8ce60dbe | ||
|
|
25f53232cd | ||
|
|
012cb518b3 | ||
|
|
2d1ffbc5b0 | ||
|
|
c88a6802a9 | ||
|
|
f5d608ac6a | ||
|
|
5d49bbfb29 | ||
|
|
5ce520a316 | ||
|
|
98c2c6e202 | ||
|
|
a2fc809d71 | ||
|
|
eb302aaef1 | ||
|
|
3b354f103a | ||
|
|
03c24a5d39 | ||
|
|
a8f13bb570 | ||
|
|
f8e35e0a72 | ||
|
|
27c153f5d3 | ||
|
|
82fdedfa14 | ||
|
|
f5bfbb13e2 | ||
|
|
80de578334 | ||
|
|
3b0acbe406 | ||
|
|
1062361612 | ||
|
|
7d81fe6f92 | ||
|
|
0285eca613 | ||
|
|
b0aea9ba89 | ||
|
|
b98eafb66f | ||
|
|
566df6793c | ||
|
|
4949e0a7f3 | ||
|
|
572dcdf1bb | ||
|
|
80ce825494 | ||
|
|
bdc3b1971c | ||
|
|
cee400a4c6 | ||
|
|
e58706d212 | ||
|
|
ad03caa064 | ||
|
|
e2d6189547 | ||
|
|
7b15d242f7 | ||
|
|
b81458d034 | ||
|
|
0595422f69 | ||
|
|
8e58adb279 | ||
|
|
a5e4b4d8e2 | ||
|
|
be2c010b5a | ||
|
|
e107326fee | ||
|
|
4da54f6dec | ||
|
|
1a3a955e10 | ||
|
|
3f304f7275 | ||
|
|
0690d742c7 | ||
|
|
43bbd4b4dd | ||
|
|
77fa27a698 | ||
|
|
8991e4f9f8 | ||
|
|
c9740651ba | ||
|
|
473550ba07 | ||
|
|
a7cbe91e73 | ||
|
|
40281f68b9 | ||
|
|
d3049751ba | ||
|
|
68164756c2 | ||
|
|
91c0ddef28 | ||
|
|
0641ca1b7b | ||
|
|
e3d9eb6ac9 | ||
|
|
7eec89bd53 | ||
|
|
289a7f8313 | ||
|
|
f49b2556ae | ||
|
|
318c43077c | ||
|
|
5691b68166 | ||
|
|
0ea44f988c | ||
|
|
59e2f796eb | ||
|
|
f1a75a98d0 | ||
|
|
76ac3fcf55 | ||
|
|
6cc3c8e84b | ||
|
|
36907c3244 | ||
|
|
dfeb02c1a7 | ||
|
|
6adf5f4090 | ||
|
|
e44ce3fb0b | ||
|
|
76ab69e44e | ||
|
|
57d009df5c | ||
|
|
b4d07b0b7e | ||
|
|
da3afa6f8e | ||
|
|
ec1c491e93 | ||
|
|
baccb556e8 | ||
|
|
eedfa61d74 | ||
|
|
2a9225178f | ||
|
|
259190e478 | ||
|
|
24a0c3b136 | ||
|
|
f46f2bc913 | ||
|
|
77f048c48e | ||
|
|
4055d51758 | ||
|
|
21eff5e1ba | ||
|
|
34174b442f | ||
|
|
77d37ab2a7 | ||
|
|
350fdbce9d | ||
|
|
e19cec4d3e | ||
|
|
e56d7665ed | ||
|
|
a0e23bfbe9 | ||
|
|
b7dc33f79e | ||
|
|
d8c31eec81 | ||
|
|
e84ee760b1 | ||
|
|
6ba1cc6bdb | ||
|
|
1b5e534592 | ||
|
|
1a3e21007b | ||
|
|
55e11ffb5c | ||
|
|
957a040996 | ||
|
|
460a42b135 | ||
|
|
8c356b401c | ||
|
|
820d06f008 | ||
|
|
3fa22fa716 | ||
|
|
333daa9cfb | ||
|
|
0e61460cdb | ||
|
|
268da18bb5 | ||
|
|
3fec29cbc6 | ||
|
|
4f3e79a4ac | ||
|
|
8b3699024b | ||
|
|
dd071f2538 | ||
|
|
ebe74dac17 | ||
|
|
909a51892f | ||
|
|
35c26bec0f | ||
|
|
8f05284792 | ||
|
|
e1d3af0734 | ||
|
|
c3fc5607ba | ||
|
|
a326d76f6a | ||
|
|
472fcab821 | ||
|
|
e7f81eb1aa | ||
|
|
31375855a0 | ||
|
|
385890d0f0 | ||
|
|
c9b4b5f18f | ||
|
|
c49af7fb33 | ||
|
|
4f042de2f4 | ||
|
|
f3e2b2ca40 | ||
|
|
a690447391 | ||
|
|
01593352ab | ||
|
|
0e4618529d | ||
|
|
57c97cd5e1 | ||
|
|
8606c0daa3 | ||
|
|
e94551d4f7 | ||
|
|
ffcd4f32ed | ||
|
|
2248f577df | ||
|
|
8a57553986 | ||
|
|
bb01ce776b | ||
|
|
631870846c | ||
|
|
a45abb7992 | ||
|
|
c7aaffa5e6 | ||
|
|
7def0d0b4e | ||
|
|
c035135fb7 | ||
|
|
15ffdeeeb8 | ||
|
|
74f6902a1b | ||
|
|
b2ae71333a | ||
|
|
fc88269f2d | ||
|
|
a191fcf803 | ||
|
|
37b109bd73 | ||
|
|
27b2a77f48 | ||
|
|
0f34dd0967 | ||
|
|
10fc717500 | ||
|
|
250068c6c2 | ||
|
|
488838752b | ||
|
|
dd0f4deae3 | ||
|
|
2df4106c92 | ||
|
|
ed58076c68 | ||
|
|
a4b300198d | ||
|
|
6980023c5a | ||
|
|
9f4d956345 | ||
|
|
ce4a90dc55 | ||
|
|
82ba193bb4 | ||
|
|
a3a004536d | ||
|
|
bb1d7e06c2 | ||
|
|
3689e6723c | ||
|
|
ef504c40b6 | ||
|
|
5e4a128d25 | ||
|
|
67d93d87b5 | ||
|
|
56b8b58606 | ||
|
|
97bc9343c1 | ||
|
|
18cea30f72 | ||
|
|
d5c048600e | ||
|
|
6d103d4ff9 | ||
|
|
7008197760 | ||
|
|
da66f33edc | ||
|
|
4109d1c825 | ||
|
|
a300663a9e | ||
|
|
cb33dac3b9 | ||
|
|
582bac8050 | ||
|
|
5e1c45bc09 | ||
|
|
b3073e6938 | ||
|
|
63e259689f | ||
|
|
d92ea0a39e | ||
|
|
81bbb4008b | ||
|
|
45ad0698b1 | ||
|
|
bc542a7bb1 | ||
|
|
efb065f558 | ||
|
|
00e63db029 | ||
|
|
f6a2926033 | ||
|
|
5b0f8afa4e | ||
|
|
c5b4421eae | ||
|
|
4d3f04e227 | ||
|
|
75ad29a68d | ||
|
|
62e1e65dda | ||
|
|
02f64314b8 | ||
|
|
12d7f191ee | ||
|
|
37135e1c8e | ||
|
|
85a0c25d0e | ||
|
|
1f081392df | ||
|
|
c02f9b827d | ||
|
|
7e95c9f999 | ||
|
|
4836b025e9 | ||
|
|
9a76838602 | ||
|
|
6c27186ce9 | ||
|
|
74c25c2ca3 | ||
|
|
91ee6ff6c0 | ||
|
|
05ba866bef | ||
|
|
af3aab86dc | ||
|
|
4370defb69 | ||
|
|
a105879c9a | ||
|
|
9383be678c | ||
|
|
7c8a394147 | ||
|
|
ffc8a9dae4 | ||
|
|
bb4a65882d | ||
|
|
5ebbb907e7 | ||
|
|
2ba66d7f91 | ||
|
|
7f8fe9e4f4 | ||
|
|
12292070ce | ||
|
|
8bc2b72ab0 | ||
|
|
208ae6b6d6 | ||
|
|
ba8121a8a7 | ||
|
|
c8da0427f9 | ||
|
|
fed13e8fda | ||
|
|
e3b977c636 | ||
|
|
337912f3d0 | ||
|
|
3b61f88343 | ||
|
|
5f6a6ba1c5 | ||
|
|
bb29844101 | ||
|
|
5600d95684 | ||
|
|
45ba341ccf | ||
|
|
7992526d2d | ||
|
|
9a6cb23659 | ||
|
|
bc132b7757 | ||
|
|
b05d726ad0 | ||
|
|
a7a612aa9b | ||
|
|
32a29c8bc7 | ||
|
|
23a3babf33 | ||
|
|
3d90f0b284 | ||
|
|
1e6448c61f | ||
|
|
a1f709ca12 | ||
|
|
a8ca2fd6e6 | ||
|
|
f835eeecdd | ||
|
|
70f5228d1c | ||
|
|
89e2ea610a | ||
|
|
6b68a983a5 | ||
|
|
4a2878b92e | ||
|
|
4f8165c8e1 | ||
|
|
855d15cec5 | ||
|
|
e1d17f61c4 | ||
|
|
380ae27762 | ||
|
|
4775dcd57a | ||
|
|
807ccc69ce | ||
|
|
aaee64ce02 | ||
|
|
294141e223 | ||
|
|
e3b125f244 | ||
|
|
7fd3297fed | ||
|
|
a2f54f67a3 | ||
|
|
d77724a911 | ||
|
|
1dc05ba196 | ||
|
|
cd7d4d102b | ||
|
|
0ba2450152 | ||
|
|
eee7d67591 | ||
|
|
760dbfa5b9 | ||
|
|
a52384de2e | ||
|
|
0dab8f0c94 | ||
|
|
629984c792 | ||
|
|
707e4c6dde | ||
|
|
efbe409399 | ||
|
|
faf0be6c53 | ||
|
|
1a078993f3 | ||
|
|
e0ac3bb853 | ||
|
|
c6cd13d9cd | ||
|
|
ef88e2e4a9 | ||
|
|
d9bcc39ee6 | ||
|
|
84f4a47df1 | ||
|
|
1408a5c357 | ||
|
|
202b15e8a8 | ||
|
|
110554a180 | ||
|
|
90e3715426 | ||
|
|
644fee2d1f | ||
|
|
c163effe60 | ||
|
|
0a39c746a3 | ||
|
|
2f46b3ff58 | ||
|
|
d35760d3a2 | ||
|
|
19a1101b43 | ||
|
|
8b209eaf27 | ||
|
|
fc6f7d3004 | ||
|
|
1abab9f92d | ||
|
|
c3b50983e3 | ||
|
|
a0857090a0 | ||
|
|
1ce9be3ed3 | ||
|
|
11d461380d | ||
|
|
6c0eb07c0b | ||
|
|
fb9ae3e78c | ||
|
|
52f59fbfb3 | ||
|
|
4a59017269 | ||
|
|
5c6d02de30 | ||
|
|
83b0e78b9e | ||
|
|
ac192e2416 | ||
|
|
52932d63d3 | ||
|
|
6a45e5d32c | ||
|
|
6f09598243 | ||
|
|
467e79d1c0 | ||
|
|
fa66c94ec3 |
33
.classpath
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/Slime Java Commons"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
79
.github/workflows/build-gui.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Build GUI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/build-gui.yml
|
||||
- gui/**
|
||||
- package*.json
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/build-gui.yml
|
||||
- gui/**
|
||||
- package*.json
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
# Don't mark warnings as errors
|
||||
CI: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Set up Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
npm run tauri build
|
||||
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: target/release/slimevr-ui.exe
|
||||
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI-Deb
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: target/release/bundle/deb/slimevr-ui*.deb
|
||||
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI-AppImage
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: target/release/bundle/appimage/slimevr-ui*.AppImage
|
||||
61
.github/workflows/gradle.yaml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: SlimeVR Server
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
|
||||
- name: Check code formatting
|
||||
run: ./gradlew spotlessCheck
|
||||
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew test
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew shadowJar
|
||||
|
||||
- name: Upload the Server JAR as a Build Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
# Artifact name
|
||||
name: "SlimeVR-Server" # optional, default is artifact
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: server/build/libs/*
|
||||
48
.github/workflows/gradle.yml
vendored
@@ -1,48 +0,0 @@
|
||||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Build SlimeVR Server with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
|
||||
- name: Clone Slime Java Commons
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
repository: Eirenliel/slime-java-commons
|
||||
# Relative path under $GITHUB_WORKSPACE to place the repository
|
||||
path: Slime Java Commons
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2.1.0
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew clean test
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean serverJar
|
||||
|
||||
- name: Upload the Server JAR as a Build Artifact
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
# Artifact name
|
||||
name: "SlimeVR-Server" # optional, default is artifact
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: build/libs/*
|
||||
33
.gitignore
vendored
@@ -1,12 +1,33 @@
|
||||
# Ignore Gradle project-specific cache directory
|
||||
.gradle
|
||||
|
||||
# Ignore Gradle build output directory
|
||||
build
|
||||
|
||||
/bin/
|
||||
|
||||
# Syncthing ignore file
|
||||
.stignore
|
||||
|
||||
MagnetoLib.dll
|
||||
# Ignore .idea
|
||||
.idea
|
||||
|
||||
|
||||
# Ignore eclipse stuff
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
# VSCode stuff
|
||||
/.vscode/settings.json
|
||||
|
||||
# Ignore eclipse stuff
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
# Node Stuff
|
||||
/node_modules
|
||||
.husky
|
||||
|
||||
|
||||
# ignore gradle build folder
|
||||
build/
|
||||
|
||||
# Rust build artifacts
|
||||
/target
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "solarxr-protocol"]
|
||||
path = solarxr-protocol
|
||||
url = https://github.com/SlimeVR/SolarXR-Protocol.git
|
||||
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
18.12.1
|
||||
6
.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
23
.project
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>SlimeVR Server</name>
|
||||
<comment>SlimeVR Server</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,2 +0,0 @@
|
||||
connection.project.dir=
|
||||
eclipse.preferences.version=1
|
||||
@@ -1,2 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
@@ -1,2 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
line.separator=\r\n
|
||||
@@ -1,416 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
|
||||
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
|
||||
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
|
||||
org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
|
||||
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
|
||||
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
|
||||
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
|
||||
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
|
||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.APILeak=warning
|
||||
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deadCode=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.deprecation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.discouragedReference=info
|
||||
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
|
||||
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
|
||||
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
|
||||
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=info
|
||||
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
|
||||
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
|
||||
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
|
||||
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
|
||||
org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=error
|
||||
org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error
|
||||
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
|
||||
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
|
||||
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
|
||||
org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
|
||||
org.eclipse.jdt.core.compiler.problem.nullReference=error
|
||||
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
|
||||
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
|
||||
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
|
||||
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
|
||||
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
|
||||
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
|
||||
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
|
||||
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
|
||||
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=48
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=48
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=48
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_header=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_html=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_source_code=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.comment.line_length=80
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_empty_lines=true
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=false
|
||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.lineSplit=200
|
||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||
org.eclipse.jdt.core.formatter.tabulation.char=tab
|
||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||
org.eclipse.jdt.core.formatter.use_on_off_tags=true
|
||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
|
||||
@@ -1,3 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
formatter_profile=_Essentia
|
||||
formatter_settings_version=13
|
||||
15
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"richardwillis.vscode-spotless-gradle",
|
||||
"gaborv.flatbuffers",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"rust-lang.rust-analyzer",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
75
CONTRIBUTING.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Contributing to SlimeVR
|
||||
|
||||
This document describes essential knowledge for contributors to SlimeVR.
|
||||
|
||||
## How to get started
|
||||
|
||||
### Getting the code
|
||||
First, clone the codebase using `git`. If you don't have `git` installed, go install it.
|
||||
|
||||
```bash
|
||||
# Clone repositories
|
||||
git clone --recursive https://github.com/SlimeVR/SlimeVR-Server.git
|
||||
|
||||
# Enter the directory of the codebase
|
||||
cd SlimeVR-Server
|
||||
```
|
||||
|
||||
Now you can open the codebase in your favorite IDE or text editor.
|
||||
|
||||
### Building the code
|
||||
The code is built with `gradle`, a cli tool that manages java projects and their
|
||||
dependencies. You can build the code with `./gradlew build` and run it with
|
||||
`./gradlew run`.
|
||||
|
||||
|
||||
## Code Style
|
||||
Code is autoformatted with [spotless](https://github.com/diffplug/spotless/tree/main/plugin-gradle).
|
||||
Code is checked for autoformatting whenever you build, but you can also run
|
||||
`./gradlew spotlessCheck` if you prefer.
|
||||
|
||||
To autoformat your code from the command line, you can run `./gradlew spotlessApply`.
|
||||
We recommend installing support for spotless in your IDE of choice, and formatting
|
||||
whenever you save a file, to make things easy.
|
||||
|
||||
If you need to prevent autoformatting for a particular region of code, use
|
||||
`// @formatter:off` and `// @formatter:on`
|
||||
|
||||
### Setting up spotless in VSCode
|
||||
* Install the `richardwillis.vscode-spotless-gradle` extension
|
||||
* Add the following to your workspace settings, at `.vscode/settings.json`:
|
||||
```json
|
||||
"spotlessGradle.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"[java]": {
|
||||
"editor.defaultFormatter": "richardwillis.vscode-spotless-gradle"
|
||||
}
|
||||
```
|
||||
|
||||
### Setting up spotless for IntelliJ
|
||||
* Install https://plugins.jetbrains.com/plugin/18321-spotless-gradle.
|
||||
* Add a keyboard shortcut for `Code` > `Reformat Code with Spotless`
|
||||
* They are working on support to do this on save without a keybind
|
||||
[here](https://github.com/ragurney/spotless-intellij-gradle/issues/8)
|
||||
|
||||
### Setting up Eclipse autoformatting
|
||||
Import the formatting settings defined in `spotless.xml`, like this:
|
||||
* Go to `File > Properties`, then `Java Code Style > Formatter`
|
||||
* Check `Enable project specific settings`
|
||||
* Click `Import`, then open `spotless.xml`, then `Apply`
|
||||
* Go to `Java Editor > Save Actions`
|
||||
* Select `Enable project specific settings`, `Perform the selected actions on save`,
|
||||
`Format source code`, `Format all lines`
|
||||
|
||||
Eclipse will only do a subset of the checks in `spotless`, so you may still want to do
|
||||
`./gradlew spotlessApply` if you ever see an error from spotless.
|
||||
|
||||
## Code Licensing
|
||||
SlimeVR uses an MIT license, and some parts of the project use a dual MIT/Apache 2.0
|
||||
license. Be sure that any code that you reference, or dependencies you add, are
|
||||
compatible with these licenses. `GPL-v3` for example is not compatible because it
|
||||
requires any and all code that depends on it to *also* be licensed under `GPL-v3`.
|
||||
|
||||
## Discord
|
||||
We use discord *a lot* to coordinate and discuss development. Come join us at
|
||||
https://discord.gg/SlimeVR!
|
||||
3566
Cargo.lock
generated
Normal file
13
Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[workspace]
|
||||
# Use 2021 edition resolver, better resolves crate features.
|
||||
resolver = "2"
|
||||
|
||||
# A list of all rust crates in the workspace.
|
||||
members = ["gui/src-tauri"]
|
||||
|
||||
# These settings can be inherited by workspace members
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.65" # This version stabilized GATs and let-else
|
||||
repository = "https://github.com/SlimeVR/SlimeVR-Server"
|
||||
201
LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Eiren Rain and SlimeVR Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
21
LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Eiren Rain and SlimeVR Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
50
README.md
@@ -12,23 +12,47 @@ Integrations:
|
||||
* Integrations with other systems will be added later
|
||||
|
||||
## How to use
|
||||
It's recommended to download installer from here: https://github.com/SlimeVR/SlimeVR-Installer/releases/latest/download/slimevr_web_installer.exe
|
||||
|
||||
Latest instructions are currently [here](https://gist.github.com/Eirenliel/8c0eefcdbda1076d5c2e1bf634831d20). Will be updated and republished as time goes on.
|
||||
Latest instructions are [on our site](https://docs.slimevr.dev/server-setup/slimevr-setup.html).
|
||||
|
||||
## How to build
|
||||
## License Clarification
|
||||
**SlimeVR software** (including server, firmware, drivers, installator, documents, and others - see
|
||||
licence for each case specifically) **is distributed under the MIT License ([LICENSE-MIT])
|
||||
and in some cases dually licensed under Apache-2.0 License ([LICENSE-APACHE]). These
|
||||
licenses are the copyright of Eiren Rain and the SlimeVR contributors.**
|
||||
|
||||
You need to execute these commands in the folder where you want this project.
|
||||
**However, these Licenses have some limits, and if you wish to distribute software based
|
||||
on SlimeVR, you need to be aware of them:**
|
||||
|
||||
```bash
|
||||
# Clone repositories
|
||||
git clone https://github.com/SlimeVR/SlimeVR-Server.git
|
||||
git clone https://github.com/Eirenliel/slime-java-commons.git
|
||||
* When distributing any software that uses or is based on SlimeVR, you have to provide
|
||||
to the end-user at least one of the original, unmodified [LICENSE-MIT] or
|
||||
[LICENSE-APACHE] files from SlimeVR. This includes the `Copyright (c) 2022 Eiren Rain
|
||||
and SlimeVR Contributors` part of the license. It is insufficient to use a generic MIT
|
||||
or Apache-2.0 License, **it must be the original license file**.
|
||||
* This applies even if you distribute software without the source code. In this case,
|
||||
one way to provide it to the end-user is to have a menu in your application that lists
|
||||
all the open source licenses used, including SlimeVR's.
|
||||
|
||||
# Enter the directory and build the runnable server JAR
|
||||
cd SlimeVR-Server
|
||||
gradlew serverJar
|
||||
```
|
||||
Please refer to the [LICENSE-MIT] and [LICENSE-APACHE] files if you are at any point
|
||||
uncertain what the exact the requirements are.
|
||||
|
||||
Open Slime VR Server project in Eclipse or Intellij Idea
|
||||
## Contributions
|
||||
Any contributions submitted for inclusion in this repository will be dual-licensed under
|
||||
either:
|
||||
|
||||
run gradle command `serverJar` to build a runnable server JAR
|
||||
- MIT License ([LICENSE-MIT])
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE])
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for
|
||||
inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual
|
||||
licensed as above, without any additional terms or conditions.
|
||||
|
||||
You also certify that the code you have used is compatible with those licenses or is
|
||||
authored by you. If you're doing so on your work time, you certify that your employer is
|
||||
okay with this and that you are authorized to provide the above licenses.
|
||||
|
||||
For a how-to on contributing, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
[LICENSE-MIT]: LICENSE-MIT
|
||||
[LICENSE-APACHE]: LICENSE-APACHE
|
||||
|
||||
59
build.gradle
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This generated file contains a sample Java Library project to get you started.
|
||||
* For more details take a look at the Java Libraries chapter in the Gradle
|
||||
* User Manual available at https://docs.gradle.org/6.3/userguide/java_library_plugin.html
|
||||
*/
|
||||
|
||||
plugins {
|
||||
// Apply the java-library plugin to add support for Java Library
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use jcenter for resolving dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':Slime Java Commons')
|
||||
|
||||
// This dependency is exported to consumers, that is to say found on their compile classpath.
|
||||
compile 'org.apache.commons:commons-math3:3.6.1'
|
||||
compile 'org.yaml:snakeyaml:1.25'
|
||||
compile 'net.java.dev.jna:jna:5.6.0'
|
||||
compile 'net.java.dev.jna:jna-platform:5.6.0'
|
||||
compile 'com.illposed.osc:javaosc-core:0.8'
|
||||
compile 'com.fazecast:jSerialComm:[2.0.0,3.0.0)'
|
||||
|
||||
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
|
||||
implementation 'com.google.guava:guava:28.2-jre'
|
||||
|
||||
|
||||
// Use JUnit test framework
|
||||
testImplementation platform('org.junit:junit-bom:5.7.2')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
testImplementation 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
subprojects.each { subproject -> evaluationDependsOn(subproject.path) }
|
||||
task serverJar (type: Jar, dependsOn: subprojects.tasks['build']) {
|
||||
// Make the JAR runnable
|
||||
manifest {
|
||||
attributes 'Main-Class': 'io.eiren.vr.Main'
|
||||
}
|
||||
|
||||
// Pack all dependencies within the JAR
|
||||
from {
|
||||
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
|
||||
// Add this project's classes in the JAR
|
||||
with jar
|
||||
}
|
||||
6
gradle.properties
Normal file
@@ -0,0 +1,6 @@
|
||||
# Fixes bug with spotless. See https://github.com/diffplug/spotless/issues/834#issuecomment-819118761
|
||||
org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
269
gradlew
vendored
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,78 +17,113 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -105,79 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
22
gradlew.bat
vendored
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -54,7 +54,7 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
@@ -64,28 +64,14 @@ echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
40
gui/.eslintrc.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react-hooks", "@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/prop-types": "off",
|
||||
"spaced-comment": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"no-duplicate-imports": "error",
|
||||
"no-inline-styles": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
"prettier/prettier": "warn"
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"typescript": {}
|
||||
},
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
gui/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
29
gui/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# JS/TS dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# Build artifacts
|
||||
/build
|
||||
/target
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
*.log
|
||||
|
||||
|
||||
/dist
|
||||
6
gui/LICENSE.md
Normal file
@@ -0,0 +1,6 @@
|
||||
This directory and all subdirectories are dual-licensed under either
|
||||
|
||||
* MIT License ([/LICENSE-MIT](/LICENSE-MIT))
|
||||
* Apache License, Version 2.0 ([/LICENSE-APACHE](/LICENSE-APACHE))
|
||||
|
||||
at your option.
|
||||
30
gui/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# SlimeVR UI
|
||||
|
||||
This is the GUI of SlimeVR, it uses the SolarXR protocol to communicate with the server and is completely isolated from the server logic.
|
||||
|
||||
This project is written in Typescript + React for the frontend and uses Tauri + Rust as a small backend. This makes the application more lightweight than electron.
|
||||
|
||||
## Compiling
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org) 16 (We recommend the use of `nvm` instead of installing Node.js directly)
|
||||
- Windows Webview
|
||||
- SlimeVR server installed
|
||||
- [Rust](https://rustup.rs)
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Build for production
|
||||
|
||||
```
|
||||
npm run tauri build
|
||||
```
|
||||
|
||||
Launch in dev mode
|
||||
|
||||
```
|
||||
npm run tauri dev
|
||||
```
|
||||
20
gui/index.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
91
gui/package.json
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "slimevr-ui",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fontsource/poppins": "^4.5.8",
|
||||
"@tauri-apps/api": "^1.0.2",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"browserslist": "^4.18.1",
|
||||
"camelcase": "^6.2.1",
|
||||
"classnames": "^2.3.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"eslint-config-react-app": "^7.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"flatbuffers": "^22.10.26",
|
||||
"fs-extra": "^10.0.0",
|
||||
"i18next": "^22.0.6",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"ip-num": "^1.4.1",
|
||||
"postcss-flexbugs-fixes": "^5.0.2",
|
||||
"postcss-normalize": "^10.0.1",
|
||||
"postcss-preset-env": "^7.0.1",
|
||||
"prompts": "^2.4.2",
|
||||
"quaternion": "^1.4.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dev-utils": "^12.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-hook-form": "^7.29.0",
|
||||
"react-i18next": "^12.0.0",
|
||||
"react-modal": "3.15.1",
|
||||
"react-router-dom": "^6.2.2",
|
||||
"semver": "^7.3.5",
|
||||
"solarxr-protocol": "file:../solarxr-protocol",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite --force",
|
||||
"build": "vite build",
|
||||
"dev": "tauri dev",
|
||||
"tauri": "tauri",
|
||||
"lint": "eslint src/**/*.{js,jsx,ts,tsx,json}",
|
||||
"lint:fix": "eslint --fix src/**/*.{js,jsx,ts,tsx,json}",
|
||||
"format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md,json} --config ./.prettierrc",
|
||||
"preview-vite": "vite preview",
|
||||
"javaversion-build": "cd src-tauri/src/ && javac JavaVersion.java && jar cvfe JavaVersion.jar JavaVersion JavaVersion.class"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
||||
"@tailwindcss/forms": "^0.5.0",
|
||||
"@tauri-apps/cli": "^1.2.0",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/react-modal": "3.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
"@typescript-eslint/parser": "^5.43.0",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.0",
|
||||
"eslint-plugin-prettier": "^4.1.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"postcss": "^8.4.12",
|
||||
"prettier": "^2.7.1",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"vite": "^3.2.4"
|
||||
}
|
||||
}
|
||||
6
gui/postcss.config.cjs
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
BIN
gui/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
gui/public/images/mounting-reset-pose.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
gui/public/images/reset-pose.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
gui/public/images/slime-girl.png
Normal file
|
After Width: | Height: | Size: 948 KiB |
BIN
gui/public/images/slimes.png
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
gui/public/logo192.png
Normal file
BIN
gui/public/logo512.png
Normal file
25
gui/public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
gui/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
5
gui/src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
WixTools
|
||||
src/JavaVersion.class
|
||||
42
gui/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "slimevr_ui"
|
||||
version = "0.0.0"
|
||||
|
||||
description = "SlimeVR GUI Application"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/SlimeVR/SlimeVR-Serverk"
|
||||
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
default-run = "slimevr_ui"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
default = ["custom-protocol"]
|
||||
# this feature is used used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.2", features = [] }
|
||||
cfg_aliases = "0.1"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tauri = { version = "1.2", features = ["cli", "devtools", "dialog", "fs-all", "path-all", "shell-execute", "window-close", "window-maximize", "window-minimize", "window-set-resizable", "window-set-title", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
|
||||
pretty_env_logger = "0.4"
|
||||
log = "0.4"
|
||||
clap-verbosity-flag = "1"
|
||||
tauri-plugin-window-state = "0.1.0"
|
||||
rand = "0.8.5"
|
||||
tempfile = "3"
|
||||
which = "4.3.0"
|
||||
open = "3"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
win32job = "1"
|
||||
winreg = "0.10.1"
|
||||
9
gui/src-tauri/build.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
tauri_build::build();
|
||||
cfg_aliases! {
|
||||
mobile: { any(target_os = "ios", target_os = "android") },
|
||||
desktop: { not(any(target_os = "ios", target_os = "android")) }
|
||||
}
|
||||
}
|
||||
BIN
gui/src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
gui/src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
11
gui/src-tauri/run.bat.old
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
setlocal enableextensions
|
||||
|
||||
echo "TEST"
|
||||
|
||||
cd /d "C:\Program Files (x86)\SlimeVR Server"
|
||||
|
||||
jre\bin\java.exe -Xmx512M -jar slimevr.jar --no-gui
|
||||
if %errorlevel% NEQ 0 (
|
||||
pause
|
||||
)
|
||||
BIN
gui/src-tauri/src/JavaVersion.jar
Normal file
8
gui/src-tauri/src/JavaVersion.java
Normal file
@@ -0,0 +1,8 @@
|
||||
public class JavaVersion {
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
var version = Runtime.version().version().get(0);
|
||||
System.exit(version);
|
||||
}
|
||||
}
|
||||
308
gui/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
#![cfg_attr(all(not(debug_assertions), windows), windows_subsystem = "windows")]
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::Write;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::panic;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Stdio};
|
||||
|
||||
use clap::Parser;
|
||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use tauri::api::{clap, process::Command};
|
||||
use tauri::Manager;
|
||||
use tempfile::Builder;
|
||||
use which::which_all;
|
||||
|
||||
#[cfg(windows)]
|
||||
/// For Commands on Windows so they dont create terminals
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
/// It's an i32 because we check it through exit codes of the process
|
||||
const MINIMUM_JAVA_VERSION: i32 = 17;
|
||||
const JAVA_BIN: &str = if cfg!(windows) {
|
||||
"java.exe"
|
||||
} else {
|
||||
"java"
|
||||
};
|
||||
static POSSIBLE_TITLES: &[&str] = &[
|
||||
"Panicking situation",
|
||||
"looking for spatula",
|
||||
"never gonna give you up",
|
||||
"never gonna let you down",
|
||||
"uwu sowwy",
|
||||
];
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(version, about)]
|
||||
struct Cli {
|
||||
#[clap(short, long)]
|
||||
display_console: bool,
|
||||
#[clap(long)]
|
||||
launch_from_path: Option<PathBuf>,
|
||||
#[clap(flatten)]
|
||||
verbosity: Verbosity<InfoLevel>,
|
||||
}
|
||||
|
||||
fn is_valid_path(path: &PathBuf) -> bool {
|
||||
// Might need to be changed in the future, at least for linux
|
||||
let server_path = path.join("slimevr.jar");
|
||||
|
||||
return server_path.exists();
|
||||
}
|
||||
|
||||
fn get_launch_path(cli: Cli) -> Option<PathBuf> {
|
||||
if let Some(path) = cli.launch_from_path {
|
||||
if path.exists() && is_valid_path(&path) {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut path = env::current_dir().unwrap();
|
||||
if is_valid_path(&path) {
|
||||
return Some(path);
|
||||
}
|
||||
|
||||
path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
if is_valid_path(&path) {
|
||||
return Some(path);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn spawn_java(java: &OsStr, java_version: &OsStr) -> std::io::Result<Child> {
|
||||
let mut cmd = std::process::Command::new(java);
|
||||
|
||||
#[cfg(windows)]
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
|
||||
cmd.arg("-jar")
|
||||
.arg(java_version)
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.spawn()
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
fn show_error(text: &str) -> bool {
|
||||
use tauri::api::dialog::{
|
||||
blocking::MessageDialogBuilder, MessageDialogButtons, MessageDialogKind,
|
||||
};
|
||||
|
||||
MessageDialogBuilder::new(
|
||||
format!(
|
||||
"SlimeVR GUI crashed - {}",
|
||||
POSSIBLE_TITLES.choose(&mut thread_rng()).unwrap()
|
||||
),
|
||||
text,
|
||||
)
|
||||
.buttons(MessageDialogButtons::Ok)
|
||||
.kind(MessageDialogKind::Error)
|
||||
.show()
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
fn show_error(text: &str) -> bool {
|
||||
// needs to do native stuff on mobile
|
||||
false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Make an error dialog box when panicking
|
||||
panic::set_hook(Box::new(|panic_info| {
|
||||
println!("{}", panic_info);
|
||||
show_error(&panic_info.to_string());
|
||||
}));
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
// Set up loggers and global handlers
|
||||
{
|
||||
if std::env::var_os("RUST_LOG").is_none() {
|
||||
std::env::set_var("RUST_LOG", "info")
|
||||
}
|
||||
pretty_env_logger::init();
|
||||
}
|
||||
|
||||
// Ensure child processes die when spawned on windows
|
||||
// and then check for WebView2's existence
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use win32job::{ExtendedLimitInfo, Job};
|
||||
|
||||
let mut info = ExtendedLimitInfo::new();
|
||||
info.limit_kill_on_job_close();
|
||||
let job = Job::create_with_limit_info(&mut info).expect("Failed to create Job");
|
||||
job.assign_current_process()
|
||||
.expect("Failed to assign current process to Job");
|
||||
|
||||
// We don't do anything with the job anymore, but we shouldn't drop it because that would
|
||||
// terminate our process tree. So we intentionally leak it instead.
|
||||
std::mem::forget(job);
|
||||
|
||||
if !webview2_exists() {
|
||||
// This makes a dialog appear which let's you press Ok or Cancel
|
||||
// If you press Ok it will open the SlimeVR installer documentation
|
||||
use tauri::api::dialog::{
|
||||
blocking::MessageDialogBuilder, MessageDialogButtons, MessageDialogKind,
|
||||
};
|
||||
|
||||
let confirm = MessageDialogBuilder::new("SlimeVR", "Couldn't find WebView2 installed. You can install it with the SlimeVR installer")
|
||||
.buttons(MessageDialogButtons::OkCancel)
|
||||
.kind(MessageDialogKind::Error)
|
||||
.show();
|
||||
if confirm {
|
||||
open::that("https://docs.slimevr.dev/server-setup/installing-and-connecting.html#install-the-latest-slimevr-installer").unwrap();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn server process
|
||||
let run_path = get_launch_path(cli);
|
||||
|
||||
let stdout_recv = if let Some(p) = run_path {
|
||||
log::info!("Server found on path: {}", p.to_str().unwrap());
|
||||
|
||||
// Check if any Java already installed is compatible
|
||||
let jre = p.join("jre/bin").join(JAVA_BIN);
|
||||
let java_bin = jre
|
||||
.exists()
|
||||
.then(|| jre.into_os_string())
|
||||
.or_else(|| valid_java_paths().first().map(|x| x.0.to_owned()));
|
||||
let Some(java_bin) = java_bin else {
|
||||
show_error(&format!("Couldn't find a compatible Java version, please download Java {} or higher", MINIMUM_JAVA_VERSION));
|
||||
return;
|
||||
};
|
||||
|
||||
log::info!("Using Java binary: {:?}", java_bin);
|
||||
let (recv, _child) = Command::new(java_bin.to_str().unwrap())
|
||||
.current_dir(p)
|
||||
.args(["-Xmx512M", "-jar", "slimevr.jar", "--no-gui"])
|
||||
.spawn()
|
||||
.expect("Unable to start the server jar");
|
||||
Some(recv)
|
||||
} else {
|
||||
log::warn!("No server found. We will not start the server.");
|
||||
None
|
||||
};
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.setup(|app| {
|
||||
if let Some(mut recv) = stdout_recv {
|
||||
let app_handle = app.app_handle();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
use tauri::api::process::CommandEvent;
|
||||
|
||||
while let Some(cmd_event) = recv.recv().await {
|
||||
let emit_me = match cmd_event {
|
||||
CommandEvent::Stderr(s) => ("stderr", s),
|
||||
CommandEvent::Stdout(s) => ("stdout", s),
|
||||
CommandEvent::Error(s) => ("error", s),
|
||||
CommandEvent::Terminated(s) => {
|
||||
("terminated", format!("{s:?}"))
|
||||
}
|
||||
_ => ("other", "".to_string()),
|
||||
};
|
||||
app_handle
|
||||
.emit_all("server-status", emit_me)
|
||||
.expect("Failed to emit");
|
||||
}
|
||||
log::error!("Java server receiver died");
|
||||
app_handle
|
||||
.emit_all("server-status", ("other", "receiver cancelled"))
|
||||
.expect("Failed to emit");
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
//
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Check if WebView2 exists
|
||||
fn webview2_exists() -> bool {
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
|
||||
// First on the machine itself
|
||||
let machine: Option<String> = RegKey::predef(HKEY_LOCAL_MACHINE)
|
||||
.open_subkey(r"SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}")
|
||||
.map(|r| r.get_value("pv").ok()).ok().flatten();
|
||||
let mut exists = false;
|
||||
if let Some(version) = machine {
|
||||
exists = version.split('.').any(|x| x != "0");
|
||||
}
|
||||
// Then in the current user
|
||||
if !exists {
|
||||
let user: Option<String> = RegKey::predef(HKEY_CURRENT_USER)
|
||||
.open_subkey(
|
||||
r"Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}",
|
||||
)
|
||||
.map(|r| r.get_value("pv").ok())
|
||||
.ok()
|
||||
.flatten();
|
||||
if let Some(version) = user {
|
||||
exists = version.split('.').any(|x| x != "0");
|
||||
}
|
||||
}
|
||||
exists
|
||||
}
|
||||
|
||||
fn valid_java_paths() -> Vec<(OsString, i32)> {
|
||||
let mut file = Builder::new()
|
||||
.suffix(".jar")
|
||||
.tempfile()
|
||||
.expect("Couldn't generate .jar file");
|
||||
file.write_all(include_bytes!("JavaVersion.jar"))
|
||||
.expect("Couldn't write to .jar file");
|
||||
let java_version = file.into_temp_path();
|
||||
|
||||
// Check if main Java is a supported version
|
||||
let main_java = if let Ok(java_home) = std::env::var("JAVA_HOME") {
|
||||
PathBuf::from(java_home)
|
||||
.join("bin")
|
||||
.join(JAVA_BIN)
|
||||
.into_os_string()
|
||||
} else {
|
||||
JAVA_BIN.into()
|
||||
};
|
||||
if let Some(main_child) = spawn_java(&main_java, java_version.as_os_str())
|
||||
.expect("Couldn't spawn the main Java binary")
|
||||
.wait()
|
||||
.expect("Couldn't execute the main Java binary")
|
||||
.code()
|
||||
{
|
||||
if main_child >= MINIMUM_JAVA_VERSION {
|
||||
return vec![(main_java, main_child)];
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise check if anything else is a supported version
|
||||
let mut childs = vec![];
|
||||
for java in which_all(JAVA_BIN).unwrap() {
|
||||
let res = spawn_java(java.as_os_str(), java_version.as_os_str());
|
||||
|
||||
match res {
|
||||
Ok(child) => childs.push((java.into_os_string(), child)),
|
||||
Err(e) => println!("Error on trying to spawn a Java executable: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
childs
|
||||
.into_iter()
|
||||
.filter_map(|(p, mut c)| {
|
||||
c.wait()
|
||||
.expect("Failed on executing a Java executable")
|
||||
.code()
|
||||
.map(|code| (p, code))
|
||||
.filter(|(_p, code)| *code >= MINIMUM_JAVA_VERSION)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
86
gui/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "slimevr-ui",
|
||||
"version": "0.0.0"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
"devPath": "http://localhost:5173",
|
||||
"beforeDevCommand": "npm run start",
|
||||
"beforeBuildCommand": "npm run build"
|
||||
},
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "slimevr.dev",
|
||||
"icon": ["icons/icon.ico", "icons/icon.png"],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "",
|
||||
"category": "DeveloperTool",
|
||||
"shortDescription": "",
|
||||
"longDescription": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"macOS": {
|
||||
"frameworks": [],
|
||||
"exceptionDomain": "",
|
||||
"signingIdentity": null,
|
||||
"providerShortName": null,
|
||||
"entitlements": null
|
||||
},
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"allowlist": {
|
||||
"shell": {
|
||||
"all": false,
|
||||
"execute": true
|
||||
},
|
||||
"fs": {
|
||||
"scope": ["$APP/*", "$APP"],
|
||||
"all": true
|
||||
},
|
||||
"path": {
|
||||
"all": true
|
||||
},
|
||||
"window": {
|
||||
"setResizable": true,
|
||||
"setTitle": true,
|
||||
"maximize": true,
|
||||
"unmaximize": true,
|
||||
"minimize": true,
|
||||
"unminimize": true,
|
||||
"close": true,
|
||||
"startDragging": true
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "Slimevr UI",
|
||||
"width": 1289,
|
||||
"height": 709,
|
||||
"minWidth": 880,
|
||||
"minHeight": 740,
|
||||
"resizable": true,
|
||||
"fullscreen": false,
|
||||
"decorations": false,
|
||||
"transparent": false,
|
||||
"fileDropEnabled": false,
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"cli": {}
|
||||
}
|
||||
}
|
||||
9
gui/src/App.test.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
182
gui/src/App.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Outlet,
|
||||
Route,
|
||||
Routes
|
||||
} from 'react-router-dom';
|
||||
import { Home } from './components/home/Home';
|
||||
import { MainLayoutRoute } from './components/MainLayout';
|
||||
import { AppContextProvider } from './components/providers/AppContext';
|
||||
import { GeneralSettings } from './components/settings/pages/GeneralSettings';
|
||||
import { Serial } from './components/settings/pages/Serial';
|
||||
import { SettingsLayoutRoute } from './components/settings/SettingsLayout';
|
||||
import {
|
||||
useProvideWebsocketApi,
|
||||
WebSocketApiContext
|
||||
} from './hooks/websocket-api';
|
||||
|
||||
import { Event, listen } from '@tauri-apps/api/event';
|
||||
import { useTranslation, withTranslation } from 'react-i18next';
|
||||
import { OnboardingContextProvider } from './components/onboarding/OnboardingContextProvicer';
|
||||
import { OnboardingLayout } from './components/onboarding/OnboardingLayout';
|
||||
import { AutomaticProportionsPage } from './components/onboarding/pages/body-proportions/AutomaticProportions';
|
||||
import { ManualProportionsPage } from './components/onboarding/pages/body-proportions/ManualProportions';
|
||||
import { ConnectTrackersPage } from './components/onboarding/pages/ConnectTracker';
|
||||
import { DonePage } from './components/onboarding/pages/Done';
|
||||
import { EnterVRPage } from './components/onboarding/pages/EnterVR';
|
||||
import { HomePage } from './components/onboarding/pages/Home';
|
||||
import { AutomaticMountingPage } from './components/onboarding/pages/mounting/AutomaticMounting';
|
||||
import { ManualMountingPage } from './components/onboarding/pages/mounting/ManualMounting';
|
||||
import { ResetTutorialPage } from './components/onboarding/pages/ResetTutorial';
|
||||
import { TrackersAssignPage } from './components/onboarding/pages/trackers-assign/TrackerAssignment';
|
||||
import { WifiCredsPage } from './components/onboarding/pages/WifiCreds';
|
||||
import { ConfigContextProvider } from './components/providers/ConfigContext';
|
||||
import { SerialDetectionModal } from './components/SerialDetectionModal';
|
||||
import { OSCSettings } from './components/settings/pages/OSCSettings';
|
||||
import { TopBar } from './components/TopBar';
|
||||
import { TrackerSettingsPage } from './components/tracker/TrackerSettings';
|
||||
import { useConfig } from './hooks/config';
|
||||
|
||||
function Layout() {
|
||||
const { loading } = useConfig();
|
||||
if (loading) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SerialDetectionModal></SerialDetectionModal>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<MainLayoutRoute>
|
||||
<Home />
|
||||
</MainLayoutRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/tracker/:trackernum/:deviceid"
|
||||
element={
|
||||
<MainLayoutRoute background={false}>
|
||||
<TrackerSettingsPage />
|
||||
</MainLayoutRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<SettingsLayoutRoute>
|
||||
<Outlet></Outlet>
|
||||
</SettingsLayoutRoute>
|
||||
}
|
||||
>
|
||||
<Route path="trackers" element={<GeneralSettings />} />
|
||||
<Route path="serial" element={<Serial />} />
|
||||
<Route path="osc/vrchat" element={<OSCSettings />} />
|
||||
</Route>
|
||||
<Route
|
||||
path="/onboarding"
|
||||
element={
|
||||
<OnboardingLayout>
|
||||
<Outlet></Outlet>
|
||||
</OnboardingLayout>
|
||||
}
|
||||
>
|
||||
<Route path="home" element={<HomePage />} />
|
||||
<Route path="wifi-creds" element={<WifiCredsPage />} />
|
||||
<Route path="connect-trackers" element={<ConnectTrackersPage />} />
|
||||
<Route path="trackers-assign" element={<TrackersAssignPage />} />
|
||||
<Route path="enter-vr" element={<EnterVRPage />} />
|
||||
<Route path="mounting/auto" element={<AutomaticMountingPage />} />
|
||||
<Route path="mounting/manual" element={<ManualMountingPage />} />
|
||||
<Route path="reset-tutorial" element={<ResetTutorialPage />} />
|
||||
<Route
|
||||
path="body-proportions/auto"
|
||||
element={<AutomaticProportionsPage />}
|
||||
/>
|
||||
<Route
|
||||
path="body-proportions/manual"
|
||||
element={<ManualProportionsPage />}
|
||||
/>
|
||||
<Route path="done" element={<DonePage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<TopBar></TopBar>}></Route>
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const websocketAPI = useProvideWebsocketApi();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = listen(
|
||||
'server-status',
|
||||
(event: Event<[string, string]>) => {
|
||||
const [event_type, s] = event.payload;
|
||||
if ('stderr' === event_type) {
|
||||
// This strange invocation is what lets us lose the line information in the console
|
||||
// See more here: https://stackoverflow.com/a/48994308
|
||||
setTimeout(
|
||||
console.log.bind(
|
||||
console,
|
||||
`%c[SERVER] %c${s}`,
|
||||
'color:cyan',
|
||||
'color:red'
|
||||
)
|
||||
);
|
||||
} else if (event_type === 'stdout') {
|
||||
setTimeout(
|
||||
console.log.bind(
|
||||
console,
|
||||
`%c[SERVER] %c${s}`,
|
||||
'color:cyan',
|
||||
'color:green'
|
||||
)
|
||||
);
|
||||
} else if (event_type === 'error') {
|
||||
console.error('Error: %s', s);
|
||||
} else if (event_type === 'terminated') {
|
||||
console.error('Server Process Terminated: %s', s);
|
||||
} else if (event_type === 'other') {
|
||||
console.log('Other process event: %s', s);
|
||||
}
|
||||
}
|
||||
);
|
||||
return () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
unlisten.then(() => {});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ConfigContextProvider>
|
||||
<WebSocketApiContext.Provider value={websocketAPI}>
|
||||
<AppContextProvider>
|
||||
<OnboardingContextProvider>
|
||||
<div className="h-full w-full text-standard bg-background-80 text-background-10">
|
||||
<div className="flex-col h-full">
|
||||
{!websocketAPI.isConnected && (
|
||||
<>
|
||||
<TopBar></TopBar>
|
||||
<div className="flex w-full h-full justify-center items-center p-2">
|
||||
{websocketAPI.isFirstConnection
|
||||
? t('websocket.connecting')
|
||||
: t('websocket.connection-lost')}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{websocketAPI.isConnected && <Layout></Layout>}
|
||||
</div>
|
||||
</div>
|
||||
</OnboardingContextProvider>
|
||||
</AppContextProvider>
|
||||
</WebSocketApiContext.Provider>
|
||||
</ConfigContextProvider>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default withTranslation()(App);
|
||||
34
gui/src/components/BVHButton.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
RecordBVHRequestT,
|
||||
RecordBVHStatusT,
|
||||
RpcMessage
|
||||
} from 'solarxr-protocol';
|
||||
import { useWebsocketAPI } from '../hooks/websocket-api';
|
||||
import { BigButton } from './commons/BigButton';
|
||||
import { RecordIcon } from './commons/icon/RecordIcon';
|
||||
|
||||
export function BVHButton() {
|
||||
const { t } = useTranslation();
|
||||
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
|
||||
const [recording, setRecording] = useState(false);
|
||||
|
||||
const toggleBVH = () => {
|
||||
const record = new RecordBVHRequestT();
|
||||
record.stop = recording;
|
||||
sendRPCPacket(RpcMessage.RecordBVHRequest, record);
|
||||
};
|
||||
|
||||
useRPCPacket(RpcMessage.RecordBVHStatus, (data: RecordBVHStatusT) => {
|
||||
setRecording(data.recording);
|
||||
});
|
||||
|
||||
return (
|
||||
<BigButton
|
||||
text={t(recording ? 'bvh.recording' : 'bvh.start-recording')}
|
||||
icon={<RecordIcon width={20} />}
|
||||
onClick={toggleBVH}
|
||||
></BigButton>
|
||||
);
|
||||
}
|
||||
69
gui/src/components/MainLayout.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode } from 'react';
|
||||
import { ResetType } from 'solarxr-protocol';
|
||||
import { useLayout } from '../hooks/layout';
|
||||
import { BVHButton } from './BVHButton';
|
||||
import { ResetButton } from './home/ResetButton';
|
||||
import { Navbar } from './Navbar';
|
||||
import { TopBar } from './TopBar';
|
||||
import { OverlayWidget } from './widgets/OverlayWidget';
|
||||
|
||||
export function MainLayoutRoute({
|
||||
children,
|
||||
background = true,
|
||||
widgets = true,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
background?: boolean;
|
||||
widgets?: boolean;
|
||||
}) {
|
||||
const { layoutHeight, ref } = useLayout<HTMLDivElement>();
|
||||
const { layoutWidth, ref: refw } = useLayout<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar></TopBar>
|
||||
<div ref={ref} className="flex-grow" style={{ height: layoutHeight }}>
|
||||
<div className="flex h-full pb-3">
|
||||
<Navbar></Navbar>
|
||||
<div
|
||||
className="flex gap-2 pr-3 w-full"
|
||||
ref={refw}
|
||||
style={{ minWidth: layoutWidth }}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex flex-col rounded-xl w-full overflow-hidden',
|
||||
background && 'bg-background-70'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
{widgets && (
|
||||
<div className="flex flex-col px-2 min-w-[274px] w-[274px] gap-2 pt-2 rounded-xl overflow-y-auto bg-background-70">
|
||||
<div className="grid grid-cols-2 gap-2 w-full">
|
||||
<ResetButton
|
||||
type={ResetType.Quick}
|
||||
variant="big"
|
||||
></ResetButton>
|
||||
<ResetButton
|
||||
type={ResetType.Full}
|
||||
variant="big"
|
||||
></ResetButton>
|
||||
<ResetButton
|
||||
type={ResetType.Mounting}
|
||||
variant="big"
|
||||
></ResetButton>
|
||||
<BVHButton></BVHButton>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<OverlayWidget></OverlayWidget>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
105
gui/src/components/Navbar.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import classnames from 'classnames';
|
||||
import { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { NavLink, useMatch } from 'react-router-dom';
|
||||
import { CubeIcon } from './commons/icon/CubeIcon';
|
||||
import { GearIcon } from './commons/icon/GearIcon';
|
||||
|
||||
export function NavButton({
|
||||
to,
|
||||
children,
|
||||
match,
|
||||
state = {},
|
||||
icon,
|
||||
}: {
|
||||
to: string;
|
||||
children: ReactNode;
|
||||
match?: string;
|
||||
state?: any;
|
||||
icon: ReactNode;
|
||||
}) {
|
||||
const doesMatch = useMatch({
|
||||
path: match || to,
|
||||
});
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
to={to}
|
||||
state={state}
|
||||
className={classnames(
|
||||
'flex flex-col justify-center gap-4 w-[85px] h-[85px] rounded-md group select-text',
|
||||
{
|
||||
'bg-accent-background-50 fill-accent-background-20': doesMatch,
|
||||
'hover:bg-background-70': !doesMatch,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-around">
|
||||
<div
|
||||
className={classnames('scale-150', {
|
||||
'fill-accent-lighter': doesMatch,
|
||||
'fill-background-50': !doesMatch,
|
||||
})}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames('text-center', {
|
||||
'text-accent-background-10': doesMatch,
|
||||
'text-background-10': !doesMatch,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
|
||||
export function Navbar() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col px-2 pt-2">
|
||||
<div className="flex flex-col flex-grow gap-2">
|
||||
<NavButton to="/" icon={<CubeIcon></CubeIcon>}>
|
||||
{t('navbar.home')}
|
||||
</NavButton>
|
||||
<NavButton
|
||||
to="/onboarding/body-proportions/auto"
|
||||
match="/onboarding/body-proportions/*"
|
||||
state={{ alonePage: true }}
|
||||
icon={<GearIcon></GearIcon>}
|
||||
>
|
||||
{t('navbar.body-proportions')}
|
||||
</NavButton>
|
||||
<NavButton
|
||||
to="/onboarding/trackers-assign"
|
||||
state={{ alonePage: true }}
|
||||
icon={<GearIcon></GearIcon>}
|
||||
>
|
||||
{t('navbar.trackers-assign')}
|
||||
</NavButton>
|
||||
<NavButton
|
||||
to="/onboarding/mounting/auto"
|
||||
match="/onboarding/mounting/*"
|
||||
state={{ alonePage: true }}
|
||||
icon={<GearIcon></GearIcon>}
|
||||
>
|
||||
{t('navbar.mounting')}
|
||||
</NavButton>
|
||||
<NavButton to="/onboarding/home" icon={<GearIcon></GearIcon>}>
|
||||
{t('navbar.onboarding')}
|
||||
</NavButton>
|
||||
</div>
|
||||
<NavButton
|
||||
to="/settings/trackers"
|
||||
match="/settings/*"
|
||||
state={{ scrollTo: 'steamvr' }}
|
||||
icon={<GearIcon></GearIcon>}
|
||||
>
|
||||
{t('navbar.settings')}
|
||||
</NavButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
206
gui/src/components/SerialDetectionModal.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
RpcMessage,
|
||||
SerialDevicesRequestT,
|
||||
SerialDevicesResponseT,
|
||||
SerialDeviceT
|
||||
} from 'solarxr-protocol';
|
||||
import { useConfig } from '../hooks/config';
|
||||
import { usePrevious } from '../hooks/previous';
|
||||
import { useWebsocketAPI } from '../hooks/websocket-api';
|
||||
import { useWifiForm, WifiFormData } from '../hooks/wifi-form';
|
||||
import { BaseModal } from './commons/BaseModal';
|
||||
import { Button } from './commons/Button';
|
||||
import { BulbIcon } from './commons/icon/BulbIcon';
|
||||
import { USBIcon } from './commons/icon/UsbIcon';
|
||||
import { Typography } from './commons/Typography';
|
||||
|
||||
const mapItems = <T,>(items: T[], getKey: (item: T) => string) => {
|
||||
const map: any = {};
|
||||
for (const item of items) {
|
||||
const key = getKey(item);
|
||||
map[key] = item;
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
const detectChanges = <T,>(
|
||||
prevItems: T[],
|
||||
nextItems: T[],
|
||||
getKey: (item: T) => string,
|
||||
compareItems: (a: T, b: T) => boolean
|
||||
) => {
|
||||
const mappedItems = mapItems(prevItems, getKey);
|
||||
const addedItems = [];
|
||||
const updatedItems = [];
|
||||
const removedItems = [];
|
||||
const unchangedItems = [];
|
||||
for (const nextItem of nextItems) {
|
||||
const itemKey = getKey(nextItem);
|
||||
if (itemKey in mappedItems) {
|
||||
const prevItem = mappedItems[itemKey];
|
||||
if (delete mappedItems[itemKey] && compareItems(prevItem, nextItem)) {
|
||||
unchangedItems.push(nextItem);
|
||||
} else {
|
||||
updatedItems.push(nextItem);
|
||||
}
|
||||
} else {
|
||||
addedItems.push(nextItem);
|
||||
}
|
||||
}
|
||||
for (const itemKey in mappedItems) {
|
||||
if (itemKey in mappedItems) {
|
||||
removedItems.push(mappedItems[itemKey]);
|
||||
}
|
||||
}
|
||||
return { addedItems, updatedItems, removedItems, unchangedItems };
|
||||
};
|
||||
|
||||
export function SerialDetectionModal() {
|
||||
const { t } = useTranslation();
|
||||
const { config } = useConfig();
|
||||
const nav = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
|
||||
const [currentDevices, setCurrentDevices] = useState<SerialDeviceT[] | null>(
|
||||
null
|
||||
);
|
||||
const prevDevices = usePrevious<SerialDeviceT[] | null>(currentDevices);
|
||||
const [isOpen, setOpen] = useState<SerialDeviceT | null>(null);
|
||||
const [showWifiForm, setShowWifiForm] = useState(false);
|
||||
|
||||
const { WifiForm, handleSubmit, submitWifiCreds, formState, hasWifiCreds } =
|
||||
useWifiForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (prevDevices == null) return;
|
||||
|
||||
const changes = detectChanges(
|
||||
prevDevices || [],
|
||||
currentDevices || [],
|
||||
(item) => item.port?.toString() || 'error',
|
||||
(a, b) => a.port == b.port && a.name == b.name
|
||||
);
|
||||
if (changes.addedItems.length === 1) {
|
||||
setOpen(changes.addedItems[0]);
|
||||
}
|
||||
}, [prevDevices, currentDevices]);
|
||||
|
||||
useEffect(() => {
|
||||
let timerId: NodeJS.Timer;
|
||||
|
||||
if (
|
||||
config?.watchNewDevices &&
|
||||
!['/settings/serial', '/onboarding/connect-trackers'].includes(pathname)
|
||||
) {
|
||||
timerId = setInterval(() => {
|
||||
sendRPCPacket(
|
||||
RpcMessage.SerialDevicesRequest,
|
||||
new SerialDevicesRequestT()
|
||||
);
|
||||
}, 3000);
|
||||
}
|
||||
return () => {
|
||||
clearInterval(timerId);
|
||||
};
|
||||
}, [config, sendRPCPacket, pathname]);
|
||||
|
||||
const closeModal = () => {
|
||||
setOpen(null);
|
||||
setShowWifiForm(false);
|
||||
};
|
||||
|
||||
const openSerial = () => {
|
||||
nav('/settings/serial', { state: { serialPort: isOpen?.port } });
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const openWifi = () => {
|
||||
if (!hasWifiCreds) {
|
||||
setShowWifiForm(true);
|
||||
} else {
|
||||
closeModal();
|
||||
nav('/onboarding/connect-trackers', { state: { alonePage: true } });
|
||||
}
|
||||
};
|
||||
|
||||
const modalWifiSubmit = (form: WifiFormData) => {
|
||||
submitWifiCreds(form);
|
||||
closeModal();
|
||||
nav('/onboarding/connect-trackers', { state: { alonePage: true } });
|
||||
};
|
||||
|
||||
useRPCPacket(
|
||||
RpcMessage.SerialDevicesResponse,
|
||||
(val: SerialDevicesResponseT) => {
|
||||
setCurrentDevices(val.devices);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseModal isOpen={!!isOpen} onRequestClose={closeModal}>
|
||||
<div className="flex flex-col gap-3">
|
||||
{!showWifiForm && (
|
||||
<>
|
||||
<div className="flex flex-col items-center gap-3 fill-accent-background-20">
|
||||
<USBIcon></USBIcon>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Typography variant="main-title">
|
||||
{t('serial-detection.new-device.p0')}
|
||||
</Typography>
|
||||
<Typography variant="section-title">
|
||||
{isOpen?.name || 'unknown'}
|
||||
</Typography>
|
||||
<Typography variant="standard">
|
||||
{t('serial-detection.new-device.p2')}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button variant="primary" onClick={openWifi}>
|
||||
{t('serial-detection.open-wifi')}
|
||||
</Button>
|
||||
<Button variant="tiertiary" onClick={openSerial}>
|
||||
{t('serial-detection.open-serial')}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={closeModal}>
|
||||
{t('serial-detection.close')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{showWifiForm && (
|
||||
<form
|
||||
onSubmit={handleSubmit(modalWifiSubmit)}
|
||||
className="flex flex-col gap-3"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<BulbIcon></BulbIcon>
|
||||
<Typography variant="main-title">
|
||||
{t('serial-detection.new-device.p0')}
|
||||
</Typography>
|
||||
<Typography variant="standard">
|
||||
{t('serial-detection.new-device.p1')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 rounded-xl max-w-sm">
|
||||
<WifiForm></WifiForm>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={!formState.isValid}
|
||||
>
|
||||
{t('serial-detection.submit')}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={closeModal}>
|
||||
{t('serial-detection.close')}
|
||||
</Button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
81
gui/src/components/TopBar.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import { ReactNode } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import packagejson from '../../package.json';
|
||||
import { CloseIcon } from './commons/icon/CloseIcon';
|
||||
import { MaximiseIcon } from './commons/icon/MaximiseIcon';
|
||||
import { MinimiseIcon } from './commons/icon/MinimiseIcon';
|
||||
import { SlimeVRIcon } from './commons/icon/SimevrIcon';
|
||||
import { ProgressBar } from './commons/ProgressBar';
|
||||
import { Typography } from './commons/Typography';
|
||||
|
||||
export function TopBar({
|
||||
progress,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
progress?: number;
|
||||
}) {
|
||||
return (
|
||||
<div data-tauri-drag-region className="flex gap-2 h-[38px] z-50">
|
||||
<div
|
||||
className="flex px-2 pb-1 mt-3 justify-around z-50"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div className="flex gap-1" data-tauri-drag-region>
|
||||
<NavLink
|
||||
to="/"
|
||||
className="flex justify-around flex-col select-all"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<SlimeVRIcon></SlimeVRIcon>
|
||||
</NavLink>
|
||||
<div className="flex justify-around flex-col" data-tauri-drag-region>
|
||||
<Typography>SlimeVR</Typography>
|
||||
</div>
|
||||
<div
|
||||
className="mx-2 flex justify-around flex-col text-standard-bold text-status-success bg-status-success bg-opacity-20 rounded-lg px-3"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
v{packagejson.version}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-grow items-center h-full justify-center z-50"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div
|
||||
className="flex max-w-xl h-full items-center w-full"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
{progress !== undefined && (
|
||||
<ProgressBar progress={progress} height={3} parts={3}></ProgressBar>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex justify-end items-center px-2 gap-2 z-50"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center hover:bg-background-60 rounded-full w-7 h-7"
|
||||
onClick={() => appWindow.minimize()}
|
||||
>
|
||||
<MinimiseIcon></MinimiseIcon>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center justify-center hover:bg-background-60 rounded-full w-7 h-7"
|
||||
onClick={() => appWindow.toggleMaximize()}
|
||||
>
|
||||
<MaximiseIcon></MaximiseIcon>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center justify-center hover:bg-background-60 rounded-full w-7 h-7"
|
||||
onClick={() => appWindow.close()}
|
||||
>
|
||||
<CloseIcon></CloseIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
48
gui/src/components/commons/ArrowLink.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode, useMemo } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from './icon/ArrowIcons';
|
||||
|
||||
export function ArrowLink({
|
||||
to,
|
||||
children,
|
||||
direction = 'left',
|
||||
variant = 'flat',
|
||||
}: {
|
||||
to: string;
|
||||
children: ReactNode;
|
||||
direction?: 'left' | 'right';
|
||||
variant?: 'flat' | 'boxed' | 'boxed-2';
|
||||
}) {
|
||||
const classes = useMemo(() => {
|
||||
const variantsMap = {
|
||||
flat: classNames('justify-start'),
|
||||
boxed: classNames(
|
||||
'justify-between bg-background-70 rounded-md hover:bg-background-60 p-3'
|
||||
),
|
||||
'boxed-2': classNames(
|
||||
'justify-between bg-background-60 rounded-md hover:bg-background-50 p-3'
|
||||
),
|
||||
};
|
||||
return classNames(
|
||||
variantsMap[variant],
|
||||
'flex gap-2 hover:fill-background-10 hover:text-background-10 fill-background-30 text-background-30'
|
||||
);
|
||||
}, [variant]);
|
||||
|
||||
return (
|
||||
<NavLink to={to} className={classes}>
|
||||
{direction === 'left' && (
|
||||
<div className="flex flex-col justify-center">
|
||||
<ArrowLeftIcon></ArrowLeftIcon>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
{direction === 'right' && (
|
||||
<div className="flex flex-col justify-center">
|
||||
<ArrowRightIcon></ArrowRightIcon>
|
||||
</div>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
28
gui/src/components/commons/BaseModal.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode } from 'react';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
export function BaseModal({
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
children: ReactNode;
|
||||
} & ReactModal.Props) {
|
||||
return (
|
||||
<ReactModal
|
||||
{...props}
|
||||
shouldCloseOnOverlayClick
|
||||
shouldCloseOnEsc
|
||||
overlayClassName={classNames(
|
||||
'fixed top-0 right-0 left-0 bottom-0 flex flex-col justify-center items-center w-full h-full bg-background-90 bg-opacity-60 z-20'
|
||||
)}
|
||||
className={classNames(
|
||||
props.className as string,
|
||||
'items-center focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent outline-none bg-background-60 p-6 rounded-lg text-white'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
36
gui/src/components/commons/BigButton.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
export function BigButton({
|
||||
text,
|
||||
icon,
|
||||
disabled,
|
||||
onClick,
|
||||
...props
|
||||
}: {
|
||||
text: string;
|
||||
disabled?: boolean;
|
||||
icon: ReactNode;
|
||||
} & React.HTMLAttributes<HTMLButtonElement>) {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
type="button"
|
||||
className={classNames(
|
||||
'flex flex-col justify-center rounded-md py-3 gap-1 px-3 cursor-pointer items-center ',
|
||||
{
|
||||
'bg-background-60 hover:bg-background-60 cursor-not-allowed text-background-40 fill-background-40':
|
||||
disabled,
|
||||
'bg-background-60 hover:bg-background-50 text-standard fill-background-10':
|
||||
!disabled,
|
||||
},
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-around">{icon}</div>
|
||||
<div className="flex text-default flex-grow">{text}</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
196
gui/src/components/commons/BodyDisplay.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
ReactNode,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import { BodyPart, TrackerDataT } from 'solarxr-protocol';
|
||||
import { FlatDeviceTracker } from '../../hooks/app';
|
||||
import { useTracker } from '../../hooks/tracker';
|
||||
import { PersonFrontIcon } from './PersonFrontIcon';
|
||||
|
||||
interface SlotDot {
|
||||
id: string;
|
||||
left: number;
|
||||
top: number;
|
||||
height: number;
|
||||
width: number;
|
||||
buttonOffset: {
|
||||
left: number;
|
||||
top: number;
|
||||
};
|
||||
}
|
||||
|
||||
type DotParams = {
|
||||
dotSize: number;
|
||||
trackers: FlatDeviceTracker[];
|
||||
} & SlotDot;
|
||||
|
||||
function Tracker({
|
||||
tracker,
|
||||
updateVelocity,
|
||||
}: {
|
||||
tracker: TrackerDataT;
|
||||
updateVelocity: (velocity: number) => void;
|
||||
}) {
|
||||
const { useVelocity } = useTracker(tracker);
|
||||
|
||||
const velocity = useVelocity();
|
||||
|
||||
useEffect(() => {
|
||||
updateVelocity(velocity);
|
||||
}, [velocity]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function Dot({
|
||||
top,
|
||||
height,
|
||||
width,
|
||||
buttonOffset,
|
||||
id,
|
||||
left,
|
||||
dotSize,
|
||||
trackers,
|
||||
}: DotParams) {
|
||||
const [velocities, setVelocities] = useState<number[]>([]);
|
||||
|
||||
const updateVelocity = (vel: number) => {
|
||||
if (velocities.length > 3) {
|
||||
velocities.shift();
|
||||
}
|
||||
velocities.push(vel);
|
||||
setVelocities(velocities);
|
||||
};
|
||||
|
||||
const globalVelocity = useMemo(
|
||||
() => velocities.reduce((curr, v) => curr + v, 0) / (trackers?.length || 1),
|
||||
[velocities, trackers]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
className="absolute z-10"
|
||||
style={{
|
||||
top: top + height / 2 - dotSize / 2 + buttonOffset.top,
|
||||
left: left + width / 2 - dotSize / 2 + buttonOffset.left,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-full outline outline-2 outline-accent-background-20 bg-background-10 transition-transform'
|
||||
)}
|
||||
style={{
|
||||
width: dotSize,
|
||||
height: dotSize,
|
||||
outlineWidth: globalVelocity * 2 + 2,
|
||||
}}
|
||||
></div>
|
||||
{trackers?.map(({ tracker }, index) => (
|
||||
<Tracker
|
||||
tracker={tracker}
|
||||
key={index}
|
||||
updateVelocity={(vel) => updateVelocity(vel)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function BodyDisplay({
|
||||
leftControls,
|
||||
rightControls,
|
||||
trackers,
|
||||
width = 228,
|
||||
dotsSize = 20,
|
||||
variant = 'tracker-select',
|
||||
}: {
|
||||
leftControls?: ReactNode;
|
||||
rightControls?: ReactNode;
|
||||
width?: number;
|
||||
dotsSize?: number;
|
||||
variant?: 'dots' | 'tracker-select';
|
||||
trackers: FlatDeviceTracker[];
|
||||
}) {
|
||||
const personRef = useRef<HTMLDivElement | null>(null);
|
||||
const [slotsButtonsPos, setSlotsButtonPos] = useState<SlotDot[]>([]);
|
||||
|
||||
const getSlotsPos = () => {
|
||||
return (
|
||||
(personRef.current && [
|
||||
...(personRef.current.querySelectorAll('.body-part-circle') as any),
|
||||
]) ||
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
const getOffset = (el: HTMLDivElement, offset = { left: 0, top: 0 }) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return {
|
||||
left: rect.left - (offset.left || 0),
|
||||
top: rect.top - (offset.top || 0),
|
||||
width: rect.width || el.offsetWidth,
|
||||
height: rect.height || el.offsetHeight,
|
||||
};
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!personRef.current) return;
|
||||
|
||||
const slotsPos = getSlotsPos();
|
||||
const personBox = personRef.current.getBoundingClientRect();
|
||||
const slots = slotsPos.map((slot: HTMLDivElement) => {
|
||||
const slotPosition = getOffset(slot, personBox);
|
||||
return {
|
||||
...slotPosition,
|
||||
id: slot.id,
|
||||
buttonOffset: {
|
||||
left: personBox.left - personBox.left,
|
||||
top: personBox.top - personBox.top,
|
||||
},
|
||||
};
|
||||
});
|
||||
setSlotsButtonPos(slots);
|
||||
}, [leftControls, rightControls, variant]);
|
||||
|
||||
const trackerPartGrouped = useMemo(
|
||||
() =>
|
||||
trackers.reduce<{ [key: number]: FlatDeviceTracker[] }>((curr, td) => {
|
||||
if (!td) return curr;
|
||||
|
||||
const key = td.tracker.info?.bodyPart || BodyPart.NONE;
|
||||
return {
|
||||
...curr,
|
||||
[key]: [...(curr[key] || []), td],
|
||||
};
|
||||
}, {}),
|
||||
[trackers]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div
|
||||
ref={personRef}
|
||||
className={classNames(
|
||||
'relative w-full flex justify-center',
|
||||
variant === 'tracker-select' && 'mx-10'
|
||||
)}
|
||||
>
|
||||
<PersonFrontIcon width={width}></PersonFrontIcon>
|
||||
{slotsButtonsPos.map((dotData) => (
|
||||
<Dot
|
||||
{...dotData}
|
||||
dotSize={dotsSize}
|
||||
key={dotData.id}
|
||||
trackers={trackerPartGrouped[(BodyPart as any)[dotData.id]]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
193
gui/src/components/commons/BodyInteractions.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { BodyPart } from 'solarxr-protocol';
|
||||
import { PersonFrontIcon } from './PersonFrontIcon';
|
||||
|
||||
export function BodyInteractions({
|
||||
leftControls,
|
||||
rightControls,
|
||||
assignedRoles,
|
||||
width = 228,
|
||||
dotsSize = 20,
|
||||
variant = 'tracker-select',
|
||||
}: {
|
||||
leftControls?: ReactNode;
|
||||
rightControls?: ReactNode;
|
||||
width?: number;
|
||||
dotsSize?: number;
|
||||
variant?: 'dots' | 'tracker-select';
|
||||
assignedRoles: BodyPart[];
|
||||
}) {
|
||||
const personRef = useRef<HTMLDivElement | null>(null);
|
||||
const leftContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const rightContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const canvasRefRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const [slotsButtonsPos, setSlotsButtonPos] = useState<
|
||||
{
|
||||
id: string;
|
||||
left: number;
|
||||
top: number;
|
||||
height: number;
|
||||
width: number;
|
||||
hidden: boolean;
|
||||
buttonOffset: {
|
||||
left: number;
|
||||
top: number;
|
||||
};
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
const getSlotsPos = () => {
|
||||
return (
|
||||
(personRef.current && [
|
||||
...(personRef.current.querySelectorAll('.body-part-circle') as any),
|
||||
]) ||
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
const getControlsPos = () => {
|
||||
const pos = (container: HTMLDivElement) =>
|
||||
[...(container.querySelectorAll('.control') as any)].filter(
|
||||
({ id }) => !!id
|
||||
);
|
||||
|
||||
const left =
|
||||
(leftContainerRef.current && pos(leftContainerRef.current)) || [];
|
||||
const right =
|
||||
(rightContainerRef.current && pos(rightContainerRef.current)) || [];
|
||||
return [...left, ...right];
|
||||
};
|
||||
|
||||
const getOffset = (el: HTMLDivElement, offset = { left: 0, top: 0 }) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return {
|
||||
left: rect.left - (offset.left || 0),
|
||||
top: rect.top - (offset.top || 0),
|
||||
width: rect.width || el.offsetWidth,
|
||||
height: rect.height || el.offsetHeight,
|
||||
};
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (
|
||||
!(
|
||||
personRef.current &&
|
||||
canvasRefRef.current &&
|
||||
rightContainerRef.current &&
|
||||
leftContainerRef.current
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
const ctx = canvasRefRef.current.getContext('2d');
|
||||
if (!ctx) return;
|
||||
const slotsPos = getSlotsPos();
|
||||
const controlsPos = getControlsPos();
|
||||
|
||||
canvasRefRef.current.width = canvasRefRef.current.offsetWidth;
|
||||
canvasRefRef.current.height = canvasRefRef.current.offsetHeight;
|
||||
|
||||
ctx.strokeStyle = '#608AAB';
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
const canvasBox = canvasRefRef.current.getBoundingClientRect();
|
||||
const personBox = personRef.current.getBoundingClientRect();
|
||||
|
||||
const controlsPosIds = controlsPos.map(({ id: cid }) => cid);
|
||||
const slots = slotsPos.map((slot: HTMLDivElement) => {
|
||||
const slotPosition = getOffset(slot, canvasBox);
|
||||
return {
|
||||
...slotPosition,
|
||||
id: slot.id,
|
||||
hidden:
|
||||
variant === 'tracker-select' && !controlsPosIds.includes(slot.id),
|
||||
buttonOffset: {
|
||||
left: canvasBox.left - personBox.left,
|
||||
top: canvasBox.top - personBox.top,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
if (variant === 'tracker-select') {
|
||||
slots.forEach((slot) => {
|
||||
const controls = controlsPos.filter(({ id }) => id === slot.id);
|
||||
controls.forEach((control) => {
|
||||
const controlPosition = getOffset(control, canvasBox);
|
||||
|
||||
const offsetX =
|
||||
controlPosition.left < slot.left ? controlPosition.width : 0;
|
||||
|
||||
const constolLeft = controlPosition.left + offsetX;
|
||||
const LINE_BREAK_WIDTH = 40;
|
||||
const leftOffsetX =
|
||||
LINE_BREAK_WIDTH * (controlPosition.left < slot.left ? -1 : 1);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(
|
||||
constolLeft,
|
||||
controlPosition.top + controlPosition.height / 2
|
||||
);
|
||||
ctx.lineTo(
|
||||
constolLeft - leftOffsetX,
|
||||
controlPosition.top + controlPosition.height / 2
|
||||
);
|
||||
ctx.lineTo(slot.left + slot.width / 2, slot.top + slot.height / 2);
|
||||
ctx.stroke();
|
||||
});
|
||||
});
|
||||
}
|
||||
setSlotsButtonPos(slots);
|
||||
}, [leftControls, rightControls, variant]);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<canvas
|
||||
ref={canvasRefRef}
|
||||
className="absolute w-full h-full top-0 z-10"
|
||||
width="100%"
|
||||
height="100%"
|
||||
></canvas>
|
||||
<div className="flex">
|
||||
<div ref={leftContainerRef} className="z-10">
|
||||
{leftControls}
|
||||
</div>
|
||||
<div
|
||||
ref={personRef}
|
||||
className={classNames(
|
||||
'relative w-full flex justify-center',
|
||||
variant === 'tracker-select' && 'mx-10'
|
||||
)}
|
||||
>
|
||||
<PersonFrontIcon width={width}></PersonFrontIcon>
|
||||
{slotsButtonsPos.map(
|
||||
({ top, left, height, width, id, hidden, buttonOffset }) => (
|
||||
<div
|
||||
key={id}
|
||||
className="absolute z-10"
|
||||
style={{
|
||||
top: top + height / 2 - dotsSize / 2 + buttonOffset.top,
|
||||
left: left + width / 2 - dotsSize / 2 + buttonOffset.left,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-full outline outline-2 outline-background-20 transition-opacity',
|
||||
(assignedRoles.includes((BodyPart as any)[id]) &&
|
||||
'bg-background-70') ||
|
||||
'bg-background-10',
|
||||
(hidden && 'opacity-0') || 'opacity-100'
|
||||
)}
|
||||
style={{ width: dotsSize, height: dotsSize }}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div ref={rightContainerRef} className="z-10">
|
||||
{rightControls}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
109
gui/src/components/commons/Button.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactNode, useMemo } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { LoaderIcon } from './icon/LoaderIcon';
|
||||
|
||||
function ButtonContent({
|
||||
loading,
|
||||
icon,
|
||||
children,
|
||||
}: {
|
||||
loading: boolean;
|
||||
icon?: ReactNode;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
{ 'opacity-0': loading },
|
||||
'flex flex-row gap-2 justify-center'
|
||||
)}
|
||||
>
|
||||
{icon && (
|
||||
<div className="flex justify-center items-center fill-background-10 w-5 h-5">
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
{loading && (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center fill-background-10">
|
||||
<LoaderIcon youSpinMeRightRoundBabyRightRound></LoaderIcon>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Button({
|
||||
children,
|
||||
variant,
|
||||
disabled,
|
||||
to,
|
||||
loading = false,
|
||||
state = {},
|
||||
icon,
|
||||
rounded = false,
|
||||
...props
|
||||
}: {
|
||||
children: ReactNode;
|
||||
icon?: ReactNode;
|
||||
variant: 'primary' | 'secondary' | 'tiertiary' | 'quaternary';
|
||||
to?: string;
|
||||
loading?: boolean;
|
||||
rounded?: boolean;
|
||||
state?: any;
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
const classes = useMemo(() => {
|
||||
const variantsMap = {
|
||||
primary: classNames({
|
||||
'bg-accent-background-30 hover:bg-accent-background-20 text-standard text-background-10':
|
||||
!disabled,
|
||||
'bg-accent-background-40 hover:bg-accent-background-40 cursor-not-allowed text-accent-background-10':
|
||||
disabled,
|
||||
}),
|
||||
secondary: classNames({
|
||||
'bg-background-60 hover:bg-background-50 text-standard text-background-10':
|
||||
!disabled,
|
||||
'bg-background-60 hover:bg-background-60 cursor-not-allowed text-background-40':
|
||||
disabled,
|
||||
}),
|
||||
tiertiary: classNames({
|
||||
'bg-background-50 hover:bg-background-40 text-standard text-background-10':
|
||||
!disabled,
|
||||
'bg-background-50 hover:bg-background-50 cursor-not-allowed text-background-40':
|
||||
disabled,
|
||||
}),
|
||||
quaternary: classNames({
|
||||
'bg-background-70 hover:bg-background-60 text-standard text-background-10':
|
||||
!disabled,
|
||||
'bg-background-70 hover:bg-background-70 cursor-not-allowed text-background-40':
|
||||
disabled,
|
||||
}),
|
||||
};
|
||||
return classNames(
|
||||
variantsMap[variant],
|
||||
'focus:ring-4 text-center relative',
|
||||
{
|
||||
'rounded-full p-2 text-center min-h-[35px] min-w-[35px]': rounded,
|
||||
'rounded-md px-5 py-2.5': !rounded,
|
||||
},
|
||||
props.className
|
||||
);
|
||||
}, [variant, disabled, rounded]);
|
||||
|
||||
return to ? (
|
||||
<NavLink to={to} className={classes} state={state}>
|
||||
<ButtonContent icon={icon} loading={loading}>
|
||||
{children}
|
||||
</ButtonContent>
|
||||
</NavLink>
|
||||
) : (
|
||||
<button type="button" {...props} className={classes} disabled={disabled}>
|
||||
<ButtonContent icon={icon} loading={loading}>
|
||||
{children}
|
||||
</ButtonContent>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
92
gui/src/components/commons/Checkbox.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import classNames from 'classnames';
|
||||
import { useMemo } from 'react';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
|
||||
export function CheckBox({
|
||||
label,
|
||||
variant = 'checkbox',
|
||||
control,
|
||||
outlined,
|
||||
name,
|
||||
// input props
|
||||
disabled,
|
||||
...props
|
||||
}: {
|
||||
label: string;
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
variant?: 'checkbox' | 'toggle';
|
||||
outlined?: boolean;
|
||||
} & React.HTMLProps<HTMLInputElement>) {
|
||||
const classes = useMemo(() => {
|
||||
const vriantsMap = {
|
||||
checkbox: {
|
||||
checkbox: classNames(
|
||||
'bg-background-50 border-background-50 rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent'
|
||||
),
|
||||
toggle: '',
|
||||
pin: '',
|
||||
},
|
||||
toggle: {
|
||||
checkbox: classNames('hidden'),
|
||||
toggle: classNames('w-10 h-4 rounded-full relative transition-colors'),
|
||||
pin: classNames('h-2 w-2 bg-background-10 rounded-full absolute m-1'),
|
||||
},
|
||||
};
|
||||
return vriantsMap[variant];
|
||||
}, [variant]);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field: { onChange, value, ref, name } }) => (
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
'bg-background-60 rounded-lg text-white': outlined,
|
||||
'text-background-30': !outlined,
|
||||
},
|
||||
'flex items-center gap-2 w-full'
|
||||
)}
|
||||
>
|
||||
<label
|
||||
className={classNames(
|
||||
'w-full py-3 flex gap-2 items-center text-standard-bold',
|
||||
{
|
||||
'px-3': outlined,
|
||||
'cursor-pointer': !disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<input
|
||||
ref={ref}
|
||||
onChange={onChange}
|
||||
checked={value}
|
||||
name={name}
|
||||
className={classes.checkbox}
|
||||
type="checkbox"
|
||||
{...props}
|
||||
/>
|
||||
{variant === 'toggle' && (
|
||||
<div
|
||||
className={classNames(classes.toggle, {
|
||||
'bg-accent-background-30': value,
|
||||
'bg-background-50': !value,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames(classes.pin, {
|
||||
'left-0': !value,
|
||||
'right-0': value,
|
||||
})}
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
114
gui/src/components/commons/Dropdown.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import classNames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
|
||||
export interface DropdownItem {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type DropdownDirection = 'up' | 'down';
|
||||
|
||||
export function Dropdown({
|
||||
direction = 'up',
|
||||
variant = 'primary',
|
||||
placeholder,
|
||||
control,
|
||||
name,
|
||||
items = [],
|
||||
}: {
|
||||
direction?: DropdownDirection;
|
||||
variant?: 'primary' | 'secondary';
|
||||
placeholder: string;
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
items: DropdownItem[];
|
||||
}) {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<>
|
||||
{isOpen && (
|
||||
<div
|
||||
className="absolute top-0 left-0 w-full h-full bg-transparent"
|
||||
onClick={() => setOpen(false)}
|
||||
></div>
|
||||
)}
|
||||
<div className="relative w-fit">
|
||||
<div
|
||||
className={classNames(
|
||||
'min-h-[35px] text-white px-5 py-2.5 rounded-md focus:ring-4 text-center flex',
|
||||
variant == 'primary' &&
|
||||
'bg-background-60 hover:bg-background-50',
|
||||
variant == 'secondary' &&
|
||||
'bg-background-70 hover:bg-background-60'
|
||||
)}
|
||||
onClick={() => setOpen((open) => !open)}
|
||||
>
|
||||
<div className="flex-grow">
|
||||
{items.find((i) => i.value == value)?.label || placeholder}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'ml-2',
|
||||
direction == 'up' && 'rotate-180',
|
||||
direction == 'down' && 'rotate-0'
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
className="justify-end w-4 h-4 "
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M19 9l-7 7-7-7"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute z-10 rounded shadow right-0',
|
||||
direction === 'up' && 'bottom-[45px]',
|
||||
direction === 'down' && 'top-[45px]',
|
||||
variant == 'primary' && 'bg-background-60',
|
||||
variant == 'secondary' && 'bg-background-70'
|
||||
)}
|
||||
>
|
||||
<ul className="py-1 text-sm text-gray-200 flex flex-col ">
|
||||
{items.map((item) => (
|
||||
<li
|
||||
className={classNames(
|
||||
'py-2 px-4 hover:text-white min-w-max',
|
||||
variant == 'primary' && 'hover:bg-background-50',
|
||||
variant == 'secondary' && 'hover:bg-background-60'
|
||||
)}
|
||||
onClick={() => {
|
||||
onChange(item.value);
|
||||
setOpen(false);
|
||||
}}
|
||||
key={item.value}
|
||||
>
|
||||
{item.label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
65
gui/src/components/commons/Input.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
forwardRef,
|
||||
HTMLInputTypeAttribute,
|
||||
MouseEvent,
|
||||
useMemo,
|
||||
useState
|
||||
} from 'react';
|
||||
import { EyeIcon } from './icon/EyeIcon';
|
||||
|
||||
export interface InputProps {
|
||||
type: HTMLInputTypeAttribute;
|
||||
placeholder?: string | null;
|
||||
label?: string | null;
|
||||
autocomplete?: boolean;
|
||||
variant?: 'primary' | 'secondary';
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function AppInput(
|
||||
{ type, placeholder, label, autocomplete, variant = 'primary', ...props },
|
||||
ref
|
||||
) {
|
||||
const [forceText, setForceText] = useState(false);
|
||||
|
||||
const togglePassword = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
setForceText(!forceText);
|
||||
};
|
||||
|
||||
const classes = useMemo(() => {
|
||||
const variantsMap = {
|
||||
primary: classNames('bg-background-60 border-background-60'),
|
||||
secondary: classNames('bg-background-50 border-background-50'),
|
||||
};
|
||||
|
||||
return classNames(
|
||||
variantsMap[variant],
|
||||
'w-full focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent rounded-md bg-background-60 border-background-60 focus:border-accent-background-40 placeholder:text-background-30 text-standard relative'
|
||||
);
|
||||
}, [variant]);
|
||||
|
||||
return (
|
||||
<label className="flex flex-col gap-1">
|
||||
{label}
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
type={forceText ? 'text' : type}
|
||||
ref={ref}
|
||||
className={classNames(classes, { 'pr-10': type === 'password' })}
|
||||
placeholder={placeholder || undefined}
|
||||
autoComplete={autocomplete ? 'off' : 'on'}
|
||||
{...props}
|
||||
></input>
|
||||
{type === 'password' && (
|
||||
<div
|
||||
className="fill-background-10 absolute top-0 h-full flex flex-col justify-center right-0 p-4"
|
||||
onClick={togglePassword}
|
||||
>
|
||||
<EyeIcon></EyeIcon>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
});
|
||||
43
gui/src/components/commons/LangSelector.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useConfig } from '../../hooks/config';
|
||||
import i18next, { langs } from '../../i18n/config';
|
||||
import { Dropdown, DropdownDirection } from './Dropdown';
|
||||
|
||||
export function LangSelector({
|
||||
direction = 'up',
|
||||
}: {
|
||||
direction?: DropdownDirection;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { config, setConfig } = useConfig();
|
||||
const { control, watch, handleSubmit } = useForm<{ lang: string }>({
|
||||
defaultValues: { lang: config?.lang || 'en' },
|
||||
});
|
||||
|
||||
const languagesItems = useMemo(
|
||||
() => langs.map(({ key, name }) => ({ label: name, value: key })),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = watch(() => handleSubmit(onSubmit)());
|
||||
return () => subscription.unsubscribe();
|
||||
}, []);
|
||||
|
||||
const onSubmit = (value: { lang: string }) => {
|
||||
i18next.changeLanguage(value.lang);
|
||||
setConfig({ lang: value.lang });
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
control={control}
|
||||
name="lang"
|
||||
placeholder={t('settings.interface.lang.placeholder')}
|
||||
items={languagesItems}
|
||||
direction={direction}
|
||||
></Dropdown>
|
||||
);
|
||||
}
|
||||
25
gui/src/components/commons/Modal.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode } from 'react';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
export function EmptyModal({
|
||||
children,
|
||||
...props
|
||||
}: { children?: ReactNode } & ReactModal.Props) {
|
||||
return (
|
||||
<ReactModal
|
||||
{...props}
|
||||
shouldCloseOnOverlayClick
|
||||
shouldCloseOnEsc
|
||||
overlayClassName={classNames(
|
||||
'fixed top-0 right-0 left-0 bottom-0 flex flex-col justify-center items-center w-full h-full bg-background-90 bg-opacity-60 z-20'
|
||||
)}
|
||||
className={classNames(
|
||||
props.className as string,
|
||||
'items-center focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent outline-none'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
64
gui/src/components/commons/NumberSelector.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
import { Button } from './Button';
|
||||
import { Typography } from './Typography';
|
||||
|
||||
export function NumberSelector({
|
||||
label,
|
||||
valueLabelFormat,
|
||||
control,
|
||||
name,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
}: {
|
||||
label: string;
|
||||
valueLabelFormat?: (value: number) => string;
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number | ((value: number, add: boolean) => number);
|
||||
}) {
|
||||
const stepFn =
|
||||
typeof step === 'function'
|
||||
? step
|
||||
: (value: number, add: boolean) =>
|
||||
+(add ? value + step : value - step).toFixed(2);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<Typography bold>{label}</Typography>
|
||||
<div className="flex gap-2 bg-background-60 p-2 rounded-lg">
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="tiertiary"
|
||||
rounded
|
||||
onClick={() => onChange(stepFn(value, false))}
|
||||
disabled={stepFn(value, false) < min}
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-grow justify-center items-center w-10">
|
||||
{valueLabelFormat ? valueLabelFormat(value) : value}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="tiertiary"
|
||||
rounded
|
||||
onClick={() => onChange(stepFn(value, true))}
|
||||
disabled={stepFn(value, true) > max}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
151
gui/src/components/commons/PersonFrontIcon.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import { BodyPart } from 'solarxr-protocol';
|
||||
|
||||
export function PersonFrontIcon({ width }: { width?: number }) {
|
||||
const CIRCLE_RADIUS = 0.0001;
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={width || 240}
|
||||
viewBox="0 0 165 392"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M84.53 224.074C83.953 230.874 88.569 266.874 90.951 280.984C92.085 287.671 95.195 298.565 94.076 304.349C92.476 312.411 92.017 322.843 92.896 328.918C93.451 332.607 95.196 349.618 92.696 355.845C91.389 359.108 88.996 375.832 88.996 375.832C82.756 391.587 86.278 390.812 86.278 390.812C88.21 393.183 91.519 390.998 91.519 390.998C92.1549 391.464 92.9388 391.682 93.7241 391.612C94.5094 391.542 95.2421 391.188 95.785 390.616C97.949 392.407 100.471 390.396 100.471 390.396C103.189 391.807 105.71 389.205 105.71 389.205C107.271 389.991 107.653 388.998 107.653 388.998C112.337 388.698 105.039 373.706 105.039 373.706C103.291 360.242 106.773 352.748 106.773 352.748C118.178 318.926 118.758 309.948 114.199 297.204C112.915 293.524 112.59 292.067 113.181 290.47C114.547 286.783 113.551 271.953 115.217 266.064C118.431 254.706 121.602 225.903 123.254 212.464C125.475 194.364 115.388 170.088 115.388 170.088C113.179 160.21 116.418 125.016 116.418 125.016C120.941 132.054 120.768 144.477 120.768 144.477C120.05 157.506 131.294 177.42 131.294 177.42C136.694 185.649 138.742 193.456 138.742 194.036C138.742 196.407 138.223 202.145 138.223 202.145L138.43 207.145C138.803 209.721 139.034 212.316 139.123 214.918C138.28 227.953 140.35 225.501 140.35 225.501C142.098 225.501 144.018 215.011 144.018 215.011C144.018 217.711 143.357 225.811 144.818 228.869C146.564 232.512 147.848 228.244 147.871 227.387C148.333 210.787 149.33 215.138 149.33 215.138C150.301 228.602 151.494 231.644 153.63 230.591C155.25 229.818 153.769 214.433 153.769 214.433C156.544 223.572 158.649 225.027 158.649 225.027C163.229 228.243 160.397 219.361 159.76 217.602C156.371 208.256 156.267 205.017 156.267 205.017C160.501 213.417 163.692 213.104 163.692 213.104C167.822 211.786 160.083 199.894 155.548 194.197C153.234 191.297 150.248 187.408 149.384 185.097C147.973 181.188 146.907 168.62 146.907 168.62C146.48 153.79 142.813 147.348 142.813 147.348C136.544 137.314 135.365 118.598 135.365 118.598L135.09 87C132.89 65.445 117.01 65.29 117.01 65.29C100.957 62.9 98.723 57.714 98.723 57.714C95.323 52.821 97.266 43.44 97.266 43.44C100.087 41.145 101.175 35.053 101.175 35.053C105.859 31.461 105.63 26.205 103.466 26.262C101.73 26.308 102.123 24.87 102.123 24.87C105.052 1.208 84.046 0 84.046 0H80.836C80.836 0 59.821 1.208 62.746 24.864C62.746 24.864 63.139 26.304 61.388 26.256C59.23 26.199 59.029 31.456 63.696 35.047C63.696 35.047 64.783 41.137 67.605 43.434C67.605 43.434 69.548 52.814 66.148 57.708C66.148 57.708 63.922 62.894 47.861 65.284C47.861 65.284 31.952 65.44 29.788 86.994L29.488 118.594C29.488 118.594 28.331 137.311 22.038 147.344C22.038 147.344 18.389 153.787 17.967 168.616C17.967 168.616 16.898 181.184 15.492 185.093C14.635 187.393 11.653 191.276 9.32001 194.193C4.74601 199.878 -2.94199 211.745 1.17101 213.1C1.17101 213.1 4.37901 213.412 8.59601 205.013C8.59601 205.013 8.50901 208.229 5.12501 217.598C4.46001 219.334 1.63201 228.217 6.21301 225.024C6.21301 225.024 8.33501 223.567 11.093 214.43C11.093 214.43 9.61301 229.815 11.26 230.588C13.412 231.642 14.586 228.599 15.56 215.135C15.56 215.135 16.56 210.787 17.017 227.384C17.04 228.241 18.295 232.509 20.049 228.866C21.529 225.811 20.864 217.727 20.864 215.008C20.864 215.008 22.764 225.498 24.536 225.498C24.536 225.498 26.624 227.95 25.767 214.915C25.628 212.786 26.375 208.415 26.467 207.142L26.667 202.142C26.667 202.142 26.146 196.417 26.146 194.033C26.146 193.442 28.194 185.646 33.594 177.417C33.594 177.417 44.826 157.494 44.103 144.474C44.103 144.474 43.947 132.051 48.47 125.013C48.47 125.013 51.68 160.205 49.505 170.085C49.505 170.085 39.405 194.358 41.629 212.461C43.27 225.937 46.435 254.702 49.657 266.061C51.34 271.938 50.345 286.761 51.693 290.467C52.301 292.076 51.982 293.558 50.675 297.201C46.141 309.947 46.718 318.925 58.123 352.745C58.123 352.745 61.633 360.239 59.859 373.703C59.859 373.703 52.572 388.695 57.239 388.995C57.239 388.995 57.604 389.988 59.182 389.202C59.182 389.202 61.703 391.802 64.427 390.393C64.427 390.393 66.95 392.407 69.106 390.613C69.6451 391.185 70.3751 391.54 71.158 391.61C71.9409 391.681 72.7225 391.462 73.355 390.995C73.355 390.995 76.664 393.227 78.63 390.809C78.63 390.809 82.123 391.584 75.904 375.829C75.904 375.829 73.522 359.129 72.209 355.842C69.709 349.621 71.474 332.57 72.009 328.915C72.87 322.806 72.409 312.398 70.835 304.346C69.684 298.575 72.801 287.679 73.952 280.981C76.317 266.881 80.952 230.881 80.373 224.071L82.288 224.743C83.0863 224.756 83.8692 224.522 84.53 224.074Z" />
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82.004"
|
||||
cy="120"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.CHEST]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82.004"
|
||||
cy="181"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.HIP]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82.004"
|
||||
cy="181"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.WAIST]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82"
|
||||
cy="50"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.NECK]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82"
|
||||
cy="50"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.HEAD]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="139.004"
|
||||
cy="170"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_HAND]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="122"
|
||||
cy="93"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_UPPER_ARM]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="122"
|
||||
cy="93"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_SHOULDER]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="139.004"
|
||||
cy="170"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_LOWER_ARM]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="97.004"
|
||||
cy="360"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_LOWER_LEG]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="103.004"
|
||||
cy="260"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_UPPER_LEG]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="97.004"
|
||||
cy="360"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.LEFT_FOOT]}
|
||||
/>
|
||||
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="26.004"
|
||||
cy="170"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_HAND]}
|
||||
/>
|
||||
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="43"
|
||||
cy="93"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_UPPER_ARM]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="43"
|
||||
cy="93"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_SHOULDER]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="26.004"
|
||||
cy="170"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_LOWER_ARM]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="67.004"
|
||||
cy="360"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_LOWER_LEG]}
|
||||
/>
|
||||
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="61.004"
|
||||
cy="260"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_UPPER_LEG]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="67.004"
|
||||
cy="360"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.RIGHT_FOOT]}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
43
gui/src/components/commons/ProgressBar.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import classNames from 'classnames';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function ProgressBar({
|
||||
progress,
|
||||
parts = 1,
|
||||
height = 10,
|
||||
}: {
|
||||
progress: number;
|
||||
parts?: number;
|
||||
height?: number;
|
||||
}) {
|
||||
const Bar = ({ index }: { index: number }) => {
|
||||
const value = useMemo(
|
||||
() => Math.min(Math.max((progress * parts) / 1 - index, 0), 1),
|
||||
[index, progress]
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className="flex relative flex-grow bg-background-50 rounded-lg overflow-hidden"
|
||||
style={{ height: `${height}px` }}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'bg-accent-background-20 rounded-lg overflow-hidden absolute top-0'
|
||||
)}
|
||||
style={{
|
||||
width: `${value * 100}%`,
|
||||
height: `${height}px`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-row gap-2">
|
||||
{Array.from({ length: parts }).map((_, key) => (
|
||||
<Bar index={key} key={key}></Bar>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
58
gui/src/components/commons/Radio.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import classNames from 'classnames';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
import { Typography } from './Typography';
|
||||
|
||||
export function Radio({
|
||||
control,
|
||||
name,
|
||||
label,
|
||||
value,
|
||||
desciption,
|
||||
// input props
|
||||
disabled,
|
||||
...props
|
||||
}: {
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
label: string;
|
||||
value: string | number;
|
||||
desciption?: string | null;
|
||||
} & React.HTMLProps<HTMLInputElement>) {
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field: { onChange, ref, name, value: checked } }) => (
|
||||
<label
|
||||
className={classNames(
|
||||
'w-full bg-background-60 p-3 rounded-md flex gap-3 border-2',
|
||||
{
|
||||
'border-accent-background-30': value == checked,
|
||||
'border-transparent': value != checked,
|
||||
'cursor-pointer': !disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
className="text-accent-background-30 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent"
|
||||
name={name}
|
||||
ref={ref}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
checked={value == checked}
|
||||
{...props}
|
||||
></input>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Typography bold>{label}</Typography>
|
||||
{desciption && (
|
||||
<Typography variant="standard" color="secondary">
|
||||
{desciption}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
16
gui/src/components/commons/TipBox.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { BulbIcon } from './icon/BulbIcon';
|
||||
import { Typography } from './Typography';
|
||||
|
||||
export function TipBox({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-row gap-4 bg-accent-background-50 p-4 rounded-md">
|
||||
<div className="fill-accent-background-20 flex flex-col justify-center">
|
||||
<BulbIcon></BulbIcon>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<Typography color="text-accent-background-10">{children}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
gui/src/components/commons/Typography.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import classNames from 'classnames';
|
||||
import { createElement, ReactNode, useMemo } from 'react';
|
||||
|
||||
export function Typography({
|
||||
variant = 'standard',
|
||||
bold = false,
|
||||
color = 'primary',
|
||||
children,
|
||||
}: {
|
||||
variant?: 'main-title' | 'section-title' | 'standard' | 'vr-accessible';
|
||||
bold?: boolean;
|
||||
block?: boolean;
|
||||
color?: 'primary' | 'secondary' | string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const tag = useMemo(() => {
|
||||
const tags = {
|
||||
'main-title': 'h1',
|
||||
'section-title': 'h2',
|
||||
standard: 'p',
|
||||
'vr-accessible': 'p',
|
||||
};
|
||||
return tags[variant];
|
||||
}, [variant]);
|
||||
|
||||
return createElement(
|
||||
tag,
|
||||
{
|
||||
className: classNames([
|
||||
variant === 'main-title' && 'text-main-title',
|
||||
variant === 'section-title' && 'text-section-title',
|
||||
variant === 'standard' &&
|
||||
(bold ? 'text-standard-bold' : 'text-standard'),
|
||||
variant === 'vr-accessible' &&
|
||||
(bold ? 'text-vr-accesible-bold' : 'text-vr-accesible'),
|
||||
color === 'primary' && 'text-background-10',
|
||||
color === 'secondary' && 'text-background-30',
|
||||
typeof color === 'string' && color,
|
||||
]),
|
||||
},
|
||||
children
|
||||
);
|
||||
}
|
||||
48
gui/src/components/commons/icon/ArrowIcons.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
export function ArrowDownIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArrowLeftIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="10"
|
||||
viewBox="0 0 12 10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 5C12 5.18941 11.921 5.37106 11.7804 5.50499C11.6397 5.63892 11.449 5.71416 11.2501 5.71416H2.56155L5.7817 8.77932C5.85142 8.84572 5.90673 8.92455 5.94446 9.0113C5.9822 9.09805 6.00162 9.19104 6.00162 9.28494C6.00162 9.37884 5.9822 9.47183 5.94446 9.55858C5.90673 9.64534 5.85142 9.72417 5.7817 9.79056C5.71197 9.85696 5.6292 9.90964 5.5381 9.94557C5.447 9.98151 5.34936 10 5.25075 10C5.15215 10 5.05451 9.98151 4.96341 9.94557C4.87231 9.90964 4.78954 9.85696 4.71981 9.79056L0.220316 5.50562C0.150479 5.43928 0.0950707 5.36048 0.0572652 5.27371C0.0194598 5.18695 0 5.09394 0 5C0 4.90606 0.0194598 4.81305 0.0572652 4.72629C0.0950707 4.63952 0.150479 4.56072 0.220316 4.49438L4.71981 0.209436C4.86063 0.0753365 5.05161 0 5.25075 0C5.4499 0 5.64088 0.0753365 5.7817 0.209436C5.92251 0.343536 6.00162 0.525414 6.00162 0.715059C6.00162 0.904705 5.92251 1.08658 5.7817 1.22068L2.56155 4.28584H11.2501C11.449 4.28584 11.6397 4.36108 11.7804 4.49502C11.921 4.62895 12 4.81059 12 5Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArrowRightIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="10"
|
||||
viewBox="0 0 12 10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M-3.2717e-07 5C-3.2717e-07 4.81059 0.0790079 4.62894 0.219644 4.49501C0.360281 4.36108 0.551026 4.28584 0.749916 4.28584L9.43845 4.28584L6.2183 1.22068C6.14858 1.15428 6.09327 1.07545 6.05554 0.9887C6.0178 0.901945 5.99838 0.808961 5.99838 0.715058C5.99838 0.621155 6.0178 0.528172 6.05554 0.441417C6.09327 0.354662 6.14858 0.275834 6.2183 0.209435C6.28803 0.143036 6.3708 0.0903648 6.4619 0.0544297C6.553 0.0184946 6.65064 -2.87569e-07 6.74925 -2.87569e-07C6.84785 -2.87569e-07 6.94549 0.0184946 7.03659 0.0544297C7.12769 0.0903648 7.21046 0.143036 7.28019 0.209435L11.7797 4.49438C11.8495 4.56072 11.9049 4.63952 11.9427 4.72629C11.9805 4.81305 12 4.90606 12 5C12 5.09394 11.9805 5.18695 11.9427 5.27371C11.9049 5.36048 11.8495 5.43928 11.7797 5.50562L7.28019 9.79056C7.13937 9.92466 6.94839 10 6.74925 10C6.5501 10 6.35912 9.92466 6.2183 9.79056C6.07749 9.65646 5.99838 9.47459 5.99838 9.28494C5.99838 9.0953 6.07749 8.91342 6.2183 8.77932L9.43845 5.71416L0.749916 5.71416C0.551026 5.71416 0.360281 5.63891 0.219644 5.50498C0.0790079 5.37105 -3.2717e-07 5.18941 -3.2717e-07 5Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
64
gui/src/components/commons/icon/BatteryIcon.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import classNames from 'classnames';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function BatteryIcon({
|
||||
value,
|
||||
disabled = false,
|
||||
}: {
|
||||
value: number;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
const col = useMemo(() => {
|
||||
const colorsMap: { [key: number]: string } = {
|
||||
0.4: 'fill-status-success',
|
||||
0.2: 'fill-status-warning',
|
||||
0: 'fill-status-critical',
|
||||
};
|
||||
|
||||
const val = Object.keys(colorsMap)
|
||||
.filter((key) => +key < value)
|
||||
.sort((a, b) => +b - +a)[0];
|
||||
return disabled
|
||||
? 'fill-background-40'
|
||||
: colorsMap[+val] || 'fill-background-10';
|
||||
}, [value, disabled]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="19"
|
||||
height="9"
|
||||
viewBox="0 0 19 9"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.0833 0H1.31203C0.995003 0.00131561 0.691347 0.121213 0.467167 0.333594C0.242986 0.545976 0.116428 0.83365 0.115039 1.134V7.383C0.114754 7.68458 0.240506 7.97399 0.464808 8.18799C0.689109 8.40198 0.993714 8.52315 1.31203 8.525H11.0833V0Z"
|
||||
fill="#3D6381"
|
||||
/>
|
||||
<path
|
||||
d="M15.0005 8.525C15.3175 8.52368 15.6212 8.40379 15.8454 8.19141C16.0696 7.97902 16.1961 7.69135 16.1975 7.391V5.968H17.9972V2.558H16.1975V1.134C16.1961 0.83365 16.0696 0.545976 15.8454 0.333594C15.6212 0.121213 15.3175 0.00131561 15.0005 0H10.9672V8.525H15.0005Z"
|
||||
fill="#3D6381"
|
||||
/>
|
||||
<mask
|
||||
id="mask0_4_39"
|
||||
style={{ maskType: 'alpha' }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="18"
|
||||
height="9"
|
||||
>
|
||||
<path
|
||||
d="M11.0833 0H1.31203C0.995003 0.00131561 0.691347 0.121213 0.467167 0.333594C0.242986 0.545976 0.116428 0.83365 0.115039 1.134V7.383C0.114754 7.68458 0.240506 7.97399 0.464808 8.18799C0.689109 8.40198 0.993714 8.52315 1.31203 8.525H11.0833V0Z"
|
||||
fill="#3D6381"
|
||||
/>
|
||||
<path
|
||||
d="M15.0005 8.525C15.3175 8.52368 15.6212 8.40379 15.8454 8.19141C16.0696 7.97902 16.1961 7.69135 16.1975 7.391V5.968H17.9972V2.558H16.1975V1.134C16.1961 0.83365 16.0696 0.545976 15.8454 0.333594C15.6212 0.121213 15.3175 0.00131561 15.0005 0H10.9672V8.525H15.0005Z"
|
||||
fill="#3D6381"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4_39)" className={classNames(col, 'opacity-100')}>
|
||||
<rect width={value * 18} height="9" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
12
gui/src/components/commons/icon/BulbIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export function BulbIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={20}
|
||||
height={20}
|
||||
viewBox="0 0 352 512"
|
||||
>
|
||||
<path d="M96.06 454.35c.01 6.29 1.87 12.45 5.36 17.69l17.09 25.69a31.99 31.99 0 0 0 26.64 14.28h61.71a31.99 31.99 0 0 0 26.64-14.28l17.09-25.69a31.989 31.989 0 0 0 5.36-17.69l.04-38.35H96.01l.05 38.35zM0 176c0 44.37 16.45 84.85 43.56 115.78 16.52 18.85 42.36 58.23 52.21 91.45.04.26.07.52.11.78h160.24c.04-.26.07-.51.11-.78 9.85-33.22 35.69-72.6 52.21-91.45C335.55 260.85 352 220.37 352 176 352 78.61 272.91-.3 175.45 0 73.44.31 0 82.97 0 176zm176-80c-44.11 0-80 35.89-80 80 0 8.84-7.16 16-16 16s-16-7.16-16-16c0-61.76 50.24-112 112-112 8.84 0 16 7.16 16 16s-7.16 16-16 16z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
19
gui/src/components/commons/icon/CheckIcon.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
export function CheckIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
width="9"
|
||||
height="7"
|
||||
viewBox="0 0 9 7"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_548_761)">
|
||||
<path d="M3.05666 6.86296L0.132685 3.81321C0.0906355 3.76962 0.0572652 3.71778 0.034494 3.66068C0.0117228 3.60358 0 3.54235 0 3.48051C0 3.41867 0.0117228 3.35744 0.034494 3.30035C0.0572652 3.24325 0.0906355 3.19141 0.132685 3.14781L0.770644 2.48241C0.812442 2.43856 0.862143 2.40375 0.916885 2.38C0.971628 2.35625 1.03033 2.34402 1.08962 2.34402C1.14891 2.34402 1.20762 2.35625 1.26236 2.38C1.3171 2.40375 1.3668 2.43856 1.4086 2.48241L3.37883 4.53739L7.59946 0.135219C7.64126 0.0913603 7.69096 0.0565546 7.7457 0.0328039C7.80044 0.00905319 7.85915 -0.00317383 7.91844 -0.00317383C7.97773 -0.00317383 8.03643 0.00905319 8.09117 0.0328039C8.14592 0.0565546 8.19562 0.0913603 8.23742 0.135219L8.87537 0.800618C8.91742 0.844214 8.95079 0.896052 8.97357 0.95315C8.99634 1.01025 9.00806 1.07148 9.00806 1.13332C9.00806 1.19516 8.99634 1.25639 8.97357 1.31349C8.95079 1.37058 8.91742 1.42242 8.87537 1.46602L3.69303 6.86296C3.65126 6.90657 3.60166 6.94116 3.54706 6.96477C3.49246 6.98837 3.43394 7.00052 3.37484 7.00052C3.31574 7.00052 3.25722 6.98837 3.20263 6.96477C3.14803 6.94116 3.09843 6.90657 3.05666 6.86296Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_548_761">
|
||||
<rect width="9" height="7" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
13
gui/src/components/commons/icon/CircleIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export function CircleIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 6 6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M3 6C4.65685 6 6 4.65685 6 3C6 1.34315 4.65685 0 3 0C1.34315 0 0 1.34315 0 3C0 4.65685 1.34315 6 3 6Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
29
gui/src/components/commons/icon/CloseIcon.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
export function CloseIcon({
|
||||
className,
|
||||
size = 35,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={className}
|
||||
viewBox="0 0 31 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19.3804 17.8804L12.619 11.119"
|
||||
stroke="#C0A1D8"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.6196 17.8804L19.381 11.119"
|
||||
stroke="#C0A1D8"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
11
gui/src/components/commons/icon/CrossIcon.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export function CrossIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
15
gui/src/components/commons/icon/CubeIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export function CubeIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M14.835 3.752L8.377 0.852002C8.31295 0.823464 8.24362 0.808716 8.1735 0.808716C8.10338 0.808716 8.03405 0.823464 7.97 0.852002L1.509 3.752C1.45917 3.77586 1.4169 3.81302 1.38684 3.85938C1.35678 3.90573 1.34011 3.95949 1.33867 4.01472C1.33722 4.06995 1.35105 4.12451 1.37863 4.17237C1.40622 4.22024 1.44649 4.25956 1.495 4.286L7.938 7.679C8.00997 7.71812 8.09058 7.73862 8.1725 7.73862C8.25442 7.73862 8.33503 7.71812 8.407 7.679L14.85 4.286C14.8985 4.25947 14.9387 4.22008 14.9662 4.17215C14.9937 4.12423 15.0074 4.06964 15.0059 4.01441C15.0043 3.95918 14.9875 3.90545 14.9574 3.85915C14.9272 3.81284 14.8849 3.77577 14.835 3.752Z" />
|
||||
<path d="M9.04301 15.774L15.537 12.338C15.5774 12.3112 15.6104 12.2748 15.6331 12.232C15.6558 12.1892 15.6674 12.1414 15.667 12.093V5.429C15.6697 5.38221 15.6601 5.33552 15.6392 5.29359C15.6182 5.25166 15.5867 5.21596 15.5476 5.19006C15.5085 5.16415 15.4634 5.14894 15.4166 5.14595C15.3698 5.14296 15.3231 5.15228 15.281 5.173L8.78701 8.519C8.74073 8.54365 8.70214 8.58059 8.6755 8.62576C8.64886 8.67092 8.63519 8.72257 8.63601 8.775V15.529C8.63442 15.5774 8.64577 15.6253 8.6689 15.6679C8.69203 15.7104 8.7261 15.746 8.76758 15.771C8.80907 15.796 8.85647 15.8094 8.90489 15.8099C8.95331 15.8105 9.00099 15.7981 9.04301 15.774Z" />
|
||||
<path d="M0.67101 5.428V12.088C0.670579 12.1364 0.682243 12.1842 0.704943 12.227C0.727644 12.2698 0.760664 12.3062 0.80101 12.333L7.29601 15.769C7.33755 15.7933 7.38493 15.8059 7.43307 15.8055C7.48122 15.805 7.52834 15.7915 7.5694 15.7663C7.61046 15.7412 7.64392 15.7053 7.66622 15.6627C7.68851 15.62 7.69881 15.5721 7.69601 15.524V8.771C7.69683 8.71856 7.68317 8.66692 7.65652 8.62175C7.62988 8.57659 7.59129 8.53965 7.54501 8.515L1.05501 5.169C1.01276 5.14923 0.966102 5.14076 0.919596 5.14439C0.87309 5.14802 0.828319 5.16364 0.789646 5.18973C0.750973 5.21581 0.719718 5.25147 0.698927 5.29323C0.678135 5.33498 0.668517 5.38142 0.67101 5.428Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
12
gui/src/components/commons/icon/EyeIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export function EyeIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="10"
|
||||
viewBox="0 0 14 10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M9.09817 4.99914C9.09817 6.11133 8.15709 7.01294 6.9962 7.01294C5.83532 7.01294 4.89424 6.11133 4.89424 4.99914C4.89424 3.88693 5.83532 2.98533 6.9962 2.98533C8.15709 2.98532 9.09817 3.88694 9.09817 4.99914ZM7 0.806091C5.79804 0.811423 4.55217 1.10403 3.37279 1.66426C2.49711 2.09735 1.64372 2.70838 0.90293 3.46257C0.539093 3.84756 0.0750283 4.40501 0 4.99979C0.00886667 5.515 0.561517 6.15093 0.90293 6.53703C1.5976 7.2616 2.42877 7.85557 3.37279 8.33578C4.47262 8.86954 5.68997 9.17685 7 9.19395C8.2031 9.18853 9.44869 8.89255 10.6268 8.33578C11.5024 7.90269 12.3563 7.29122 13.0971 6.53703C13.4609 6.15204 13.925 5.59457 14 4.99979C13.9911 4.48458 13.4385 3.84863 13.0971 3.46254C12.4024 2.73797 11.5708 2.14446 10.6268 1.66423C9.52751 1.13088 8.30716 0.82568 7 0.806091ZM6.99911 1.84732C8.8205 1.84732 10.297 3.25891 10.297 5.00025C10.297 6.74157 8.8205 8.15316 6.99911 8.15316C5.17773 8.15316 3.70124 6.74156 3.70124 5.00025C3.70124 3.25891 5.17773 1.84732 6.99911 1.84732Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
12
gui/src/components/commons/icon/FootIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export function FootIcon({ width }: { width?: number }) {
|
||||
return (
|
||||
<svg
|
||||
width={width || 28}
|
||||
viewBox="0 0 28 28"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="28" height="28" rx="2" fill="#56407B" />
|
||||
<path d="M18.5981 6.05798L18.1461 7.57104C18.0991 7.72904 18.0361 7.92496 18.0061 8.00696C17.8731 8.3476 17.7678 8.69847 17.6911 9.05603C17.6078 9.35662 17.595 9.67231 17.6538 9.97864C17.7126 10.285 17.8414 10.5737 18.0301 10.822C18.0651 10.866 18.2771 11.2161 18.5031 11.6021C18.7291 11.9881 18.9321 12.3339 18.9581 12.3719C19.0375 12.4967 19.1065 12.6279 19.1641 12.764C19.2954 13.1107 19.3199 13.4887 19.2346 13.8494C19.1492 14.2101 18.9578 14.5369 18.6851 14.788C18.3588 15.1236 17.9785 15.4021 17.5601 15.6119C16.885 15.93 16.3258 16.4504 15.9601 17.101C15.8219 17.345 15.6665 17.579 15.4951 17.801C15.3751 17.967 15.1531 18.292 15.0011 18.524C14.7241 18.945 14.5201 19.2441 14.3791 19.4301C14.0713 19.8357 13.7085 20.1964 13.3011 20.502L12.9011 20.796C12.4511 21.131 12.4491 21.132 12.2011 21.146C12.0364 21.1349 11.8735 21.1855 11.7441 21.288C11.6483 21.3637 11.5381 21.4192 11.4201 21.451C11.3022 21.4829 11.179 21.4904 11.0581 21.473C10.8581 21.473 10.8231 21.4731 10.7951 21.451C10.7435 21.4122 10.7026 21.3609 10.6761 21.302C10.6746 21.2885 10.6682 21.276 10.6581 21.267C10.6411 21.254 10.6301 21.26 10.5821 21.306C10.5023 21.376 10.4081 21.4276 10.3061 21.457C10.2078 21.4797 10.1066 21.4871 10.0061 21.479H9.7921L9.7121 21.438C9.67073 21.4185 9.63235 21.3933 9.5981 21.363L9.5641 21.328H9.3391C9.0311 21.328 8.9941 21.32 8.8991 21.228C8.84921 21.1797 8.79222 21.1392 8.7301 21.108C8.61137 21.0413 8.50891 20.9491 8.4301 20.838C8.41279 20.7935 8.40728 20.7453 8.4141 20.698L8.4201 20.604L8.5061 20.515C8.5961 20.423 8.6061 20.39 8.5811 20.337C8.53854 20.293 8.48119 20.2662 8.4201 20.262C8.32553 20.2388 8.24225 20.1828 8.1851 20.104C8.09834 20.0317 8.03903 19.9318 8.0171 19.821C7.98191 19.6196 8.00125 19.4124 8.0731 19.2209C8.1321 19.0639 8.1321 19.064 8.3671 19.063C8.57004 19.0815 8.77415 19.0437 8.9571 18.954C9.02435 18.9097 9.10263 18.8851 9.1831 18.8829C9.2331 18.8829 9.2711 18.862 9.5831 18.671C9.83458 18.5055 10.1027 18.3666 10.3831 18.257L10.4881 18.223L10.5591 18.0229C10.7588 17.4891 10.9019 16.9357 10.9861 16.3719C11.0496 15.9465 11.1422 15.5259 11.2631 15.113C11.3728 14.7304 11.5064 14.355 11.6631 13.989C12.0182 13.0652 12.2417 12.096 12.3271 11.11C12.3801 10.764 12.4431 10.3661 12.4661 10.2271C12.4891 10.0881 12.5171 9.89894 12.5281 9.80994C12.5391 9.72094 12.5631 9.51606 12.5821 9.35706C12.6011 9.19806 12.6291 8.96499 12.6421 8.83899C12.6551 8.71299 12.6771 8.51101 12.6901 8.38501C12.7441 7.88501 12.8191 7.11596 12.8711 6.53796C12.8901 6.31196 12.9111 6.09799 12.9151 6.06299L12.9221 6H18.6161L18.5981 6.05798Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
20
gui/src/components/commons/icon/FrontOfChair.tsx
Normal file
12
gui/src/components/commons/icon/GearIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export function GearIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 14 13"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M7.00099 11.9C7.31948 11.9003 7.63041 11.997 7.89283 12.1775C8.15524 12.3579 8.35682 12.6137 8.47099 12.911C9.11419 12.757 9.72885 12.5018 10.292 12.155C10.2026 11.9531 10.1559 11.7348 10.155 11.514C10.1546 11.2516 10.2198 10.9933 10.3448 10.7626C10.4697 10.5319 10.6505 10.3362 10.8705 10.1932C11.0905 10.0503 11.3427 9.96464 11.6043 9.94417C11.8659 9.92369 12.1284 9.96902 12.368 10.076C12.7147 9.51424 12.9699 8.90091 13.124 8.259C12.8268 8.1447 12.5711 7.9431 12.3906 7.68071C12.2102 7.41833 12.1134 7.10745 12.113 6.789C12.1134 6.47055 12.2102 6.15968 12.3906 5.89729C12.5711 5.6349 12.8268 5.4333 13.124 5.319C12.97 4.6758 12.7148 4.06115 12.368 3.498C12.1284 3.60499 11.8659 3.65031 11.6043 3.62983C11.3427 3.60936 11.0905 3.52374 10.8705 3.38078C10.6505 3.23782 10.4697 3.04207 10.3448 2.81138C10.2198 2.58068 10.1546 2.32237 10.155 2.06C10.1543 1.83899 10.201 1.62041 10.292 1.419C9.72669 1.07498 9.11135 0.820948 8.46799 0.666C8.35405 0.963474 8.15253 1.21938 7.89007 1.39989C7.6276 1.5804 7.31654 1.67703 6.99799 1.677C6.67886 1.67742 6.36711 1.581 6.10393 1.40049C5.84075 1.21998 5.63853 0.963872 5.52399 0.666C4.882 0.821335 4.26872 1.07752 3.70699 1.425C3.83599 1.71551 3.87394 2.03828 3.81586 2.35079C3.75778 2.66329 3.60638 2.95087 3.38162 3.17563C3.15686 3.40039 2.86928 3.55178 2.55678 3.60987C2.24427 3.66795 1.9215 3.62999 1.63099 3.501C1.28471 4.06442 1.02957 4.67899 0.874993 5.322C1.17184 5.43574 1.42719 5.63685 1.60733 5.89878C1.78746 6.1607 1.8839 6.47111 1.8839 6.789C1.8839 7.10689 1.78746 7.4173 1.60733 7.67922C1.42719 7.94115 1.17184 8.14226 0.874993 8.256C1.02899 8.8992 1.28417 9.51385 1.63099 10.077C1.92149 9.94858 2.24405 9.91106 2.55627 9.96935C2.86849 10.0276 3.15577 10.179 3.38036 10.4036C3.60495 10.6282 3.75634 10.9155 3.81464 11.2277C3.87294 11.5399 3.83541 11.8625 3.70699 12.153C4.26924 12.4985 4.88243 12.7533 5.52399 12.908C5.6399 12.6107 5.84296 12.3554 6.10653 12.1755C6.37011 11.9956 6.68189 11.8996 7.00099 11.9ZM4.45399 6.817C4.4536 6.31238 4.60287 5.81897 4.88294 5.3992C5.16301 4.97942 5.56128 4.65214 6.02738 4.45876C6.49348 4.26537 7.00646 4.21457 7.50144 4.31277C7.99642 4.41098 8.45115 4.65378 8.80811 5.01046C9.16508 5.36715 9.40824 5.82169 9.50683 6.31659C9.60542 6.81149 9.55502 7.32451 9.362 7.79076C9.16898 8.25701 8.84201 8.65554 8.42246 8.93594C8.00291 9.21634 7.50962 9.366 7.00499 9.366C6.67012 9.36613 6.3385 9.3003 6.02907 9.17228C5.71964 9.04425 5.43846 8.85653 5.20158 8.61983C4.96469 8.38313 4.77675 8.1021 4.64848 7.79277C4.52021 7.48344 4.45412 7.15187 4.45399 6.817Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
32
gui/src/components/commons/icon/LoaderIcon.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
export function LoaderIcon({
|
||||
youSpinMeRightRoundBabyRightRound = false,
|
||||
}: {
|
||||
youSpinMeRightRoundBabyRightRound?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
width="19"
|
||||
height="19"
|
||||
viewBox="0 0 19 19"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={classNames({
|
||||
'animate-spin': youSpinMeRightRoundBabyRightRound,
|
||||
})}
|
||||
>
|
||||
<path d="M16.0312 8.90625H13.0625C12.7359 8.90625 12.4688 9.17344 12.4688 9.5C12.4688 9.82656 12.7359 10.0938 13.0625 10.0938H16.0312C16.3578 10.0938 16.625 9.82656 16.625 9.5C16.625 9.17344 16.3578 8.90625 16.0312 8.90625Z" />
|
||||
<path d="M6.53125 9.5C6.53125 9.17344 6.26406 8.90625 5.9375 8.90625H2.96875C2.64219 8.90625 2.375 9.17344 2.375 9.5C2.375 9.82656 2.64219 10.0938 2.96875 10.0938H5.9375C6.26406 10.0938 6.53125 9.82656 6.53125 9.5Z" />
|
||||
<path d="M9.5 12.4688C9.17344 12.4688 8.90625 12.7359 8.90625 13.0625V16.0312C8.90625 16.3578 9.17344 16.625 9.5 16.625C9.82656 16.625 10.0938 16.3578 10.0938 16.0312V13.0625C10.0938 12.7359 9.82656 12.4688 9.5 12.4688Z" />
|
||||
<path d="M9.5 2.375C9.17344 2.375 8.90625 2.64219 8.90625 2.96875V5.9375C8.90625 6.26406 9.17344 6.53125 9.5 6.53125C9.82656 6.53125 10.0938 6.26406 10.0938 5.9375V2.96875C10.0938 2.64219 9.82656 2.375 9.5 2.375Z" />
|
||||
<path d="M12.0716 8.01562C12.2349 8.30137 12.5986 8.39785 12.8843 8.23457L15.456 6.75019C15.7417 6.58691 15.8382 6.22324 15.6749 5.9375C15.5117 5.65176 15.148 5.55527 14.8623 5.71855L12.2906 7.20293C12.0048 7.36992 11.9083 7.72988 12.0716 8.01562Z" />
|
||||
<path d="M6.92836 10.9844C6.76508 10.6986 6.40141 10.6021 6.11567 10.7654L3.54399 12.2498C3.25825 12.4131 3.16176 12.7768 3.32505 13.0625C3.48833 13.3482 3.852 13.4447 4.13774 13.2814L6.70942 11.7971C6.99516 11.6301 7.09165 11.2701 6.92836 10.9844Z" />
|
||||
<path d="M11.7971 12.2869C11.6338 12.0012 11.2702 11.9047 10.9844 12.068C10.6987 12.2312 10.6022 12.5949 10.7655 12.8807L12.2498 15.4523C12.4131 15.7381 12.7768 15.8346 13.0625 15.6713C13.3483 15.508 13.4448 15.1443 13.2815 14.8586L11.7971 12.2869Z" />
|
||||
<path d="M6.75024 3.54765C6.58696 3.26191 6.22329 3.16543 5.93754 3.32871C5.6518 3.49199 5.55532 3.85566 5.7186 4.1414L7.20297 6.71308C7.36626 6.99882 7.72993 7.09531 8.01567 6.93203C8.30141 6.76875 8.3979 6.40507 8.23461 6.11933L6.75024 3.54765Z" />
|
||||
<path d="M10.9844 6.92832C11.2702 7.0916 11.6301 6.99512 11.7971 6.70937L13.2815 4.13769C13.4448 3.85195 13.3483 3.49199 13.0625 3.325C12.7768 3.16172 12.4168 3.2582 12.2498 3.54394L10.7655 6.11562C10.6022 6.40137 10.6987 6.76504 10.9844 6.92832Z" />
|
||||
<path d="M8.01567 12.0717C7.72993 11.9084 7.36997 12.0049 7.20297 12.2906L5.7186 14.8623C5.55532 15.148 5.6518 15.508 5.93754 15.675C6.22329 15.8383 6.58325 15.7418 6.75024 15.456L8.23461 12.8844C8.3979 12.5986 8.30141 12.235 8.01567 12.0717Z" />
|
||||
<path d="M15.4523 12.2498L12.8807 10.7654C12.5949 10.6021 12.235 10.6986 12.068 10.9844C11.9047 11.2701 12.0012 11.6301 12.2869 11.7971L14.8586 13.2814C15.1443 13.4447 15.5043 13.3482 15.6713 13.0625C15.8346 12.7768 15.7381 12.4168 15.4523 12.2498Z" />
|
||||
<path d="M3.54765 6.75019L6.11933 8.23457C6.40507 8.39785 6.76504 8.30137 6.93203 8.01562C7.09902 7.72988 6.99882 7.36992 6.71308 7.20293L4.1414 5.71855C3.85566 5.55527 3.4957 5.65176 3.32871 5.9375C3.16543 6.22324 3.26191 6.5832 3.54765 6.75019Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
17
gui/src/components/commons/icon/MaximiseIcon.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
export function MaximiseIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
width="35"
|
||||
height="35"
|
||||
className={className}
|
||||
viewBox="0 0 31 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18 11.5H14C13.1716 11.5 12.5 12.1716 12.5 13V17C12.5 17.8284 13.1716 18.5 14 18.5H18C18.8284 18.5 19.5 17.8284 19.5 17V13C19.5 12.1716 18.8284 11.5 18 11.5Z"
|
||||
stroke="#C0A1D8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
13
gui/src/components/commons/icon/MinimiseIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export function MinimiseIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
width="35"
|
||||
height="35"
|
||||
className={className}
|
||||
viewBox="0 0 31 29"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M20.5 15.5H10.5" stroke="#C0A1D8" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
7
gui/src/components/commons/icon/RecordIcon.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export function RecordIcon({ width = 33 }: { width?: number }) {
|
||||
return (
|
||||
<svg width={width} height="29" viewBox="0 0 24 29">
|
||||
<path d="M20.84 2.18L16.91 2.96L19.65 6.5L21.62 6.1L20.84 2.18M13.97 3.54L12 3.93L14.75 7.46L16.71 7.07L13.97 3.54M9.07 4.5L7.1 4.91L9.85 8.44L11.81 8.05L9.07 4.5M4.16 5.5L3.18 5.69A2 2 0 0 0 1.61 8.04L2 10L6.9 9.03L4.16 5.5M2 10V20C2 21.11 2.9 22 4 22H20C21.11 22 22 21.11 22 20V10H2Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
84
gui/src/components/commons/icon/ResetIcon.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
export function ResetIcon({ width = 33 }: { width?: number }) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height="29"
|
||||
viewBox="0 0 33 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.39801 3.061V11.524H10.861"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.93801 18.576C6.85461 21.1713 8.58991 23.3983 10.8824 24.9215C13.1749 26.4447 15.9003 27.1816 18.648 27.021C21.3957 26.8604 24.0168 25.8111 26.1162 24.0312C28.2157 22.2514 29.6797 19.8373 30.2878 17.153C30.8958 14.4686 30.6149 11.6593 29.4874 9.14845C28.3599 6.63762 26.4468 4.56127 24.0365 3.23233C21.6262 1.90339 18.8493 1.39384 16.1242 1.7805C13.3991 2.16715 10.8735 3.42904 8.92801 5.376L2.39301 11.523"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function QuickResetIcon({ width = 33 }: { width?: number }) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height="29"
|
||||
viewBox="0 0 33 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.39804 3.04599V11.509H10.861"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.93804 18.561C6.85464 21.1563 8.58994 23.3833 10.8824 24.9065C13.1749 26.4297 15.9003 27.1666 18.648 27.006C21.3957 26.8454 24.0168 25.7961 26.1163 24.0162C28.2157 22.2363 29.6798 19.8223 30.2878 17.1379C30.8959 14.4536 30.615 11.6443 29.4874 9.13344C28.3599 6.6226 26.4468 4.54626 24.0365 3.21732C21.6262 1.88837 18.8493 1.37883 16.1242 1.76548C13.3991 2.15213 10.8735 3.41402 8.92804 5.36099L2.39304 11.508"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16.633 9.33699C17.879 11.837 14.133 13.083 14.133 15.581C14.133 18.079 16.633 19.327 16.633 19.327C15.407 16.855 19.133 15.581 19.133 13.083C19.133 10.585 16.633 9.33699 16.633 9.33699ZM20.377 13.083C21.626 15.583 17.877 16.829 17.877 19.327H21.626C22.126 19.327 22.875 18.703 22.875 16.827C22.875 14.337 20.377 13.083 20.377 13.083Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function MountingResetIcon({ width = 33 }: { width?: number }) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height="29"
|
||||
viewBox="0 0 33 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.39801 3.061V11.524H10.861"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.93801 18.576C6.85461 21.1713 8.58991 23.3983 10.8824 24.9215C13.1749 26.4447 15.9003 27.1816 18.648 27.021C21.3957 26.8604 24.0168 25.8111 26.1162 24.0312C28.2157 22.2514 29.6797 19.8373 30.2878 17.153C30.8958 14.4686 30.6149 11.6593 29.4874 9.14845C28.3599 6.63762 26.4468 4.56127 24.0365 3.23233C21.6262 1.90339 18.8493 1.39384 16.1242 1.7805C13.3991 2.16715 10.8735 3.42904 8.92801 5.376L2.39301 11.523"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
31
gui/src/components/commons/icon/SimevrIcon.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
export function SlimeVRIcon({ drag }: { drag?: boolean }) {
|
||||
return (
|
||||
<svg
|
||||
width="49"
|
||||
height="29"
|
||||
viewBox="0 0 49 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-tauri-drag-region={drag}
|
||||
>
|
||||
<path
|
||||
d="M2 26.996C10.44 25.59 29.16 23.1571 46.509 26.9091C46.509 26.9091 48.89 -0.199966 35.761 2.14503"
|
||||
stroke="#A44FED"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.52161 15.0107L12.3649 9.20459L17.5044 13.9572"
|
||||
stroke="#A44FED"
|
||||
strokeWidth="3.00157"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M27.9566 14.1435L33.7372 9.27062L37.9695 14.8458"
|
||||
stroke="#A44FED"
|
||||
strokeWidth="3.00136"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
15
gui/src/components/commons/icon/SquaresIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export function SquaresIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M6.425 0.125H0.125V6.425H6.425V0.125Z" />
|
||||
<path d="M6.425 9.57495H0.125V15.875H6.425V9.57495Z" />
|
||||
<path d="M9.57501 9.57495H15.875V15.875H9.57501V9.57495Z" />
|
||||
<path d="M15.875 0.125H9.57501V6.425H15.875V0.125Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
12
gui/src/components/commons/icon/SteamIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export function SteamIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="18"
|
||||
viewBox="0 0 20 18"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M17.6562 5.32031C17.6562 6.10938 17.3903 6.7832 16.8583 7.3418C16.3263 7.90039 15.6845 8.17969 14.933 8.17969C14.1815 8.17969 13.5398 7.90039 13.0078 7.3418C12.4758 6.7832 12.2098 6.10938 12.2098 5.32031C12.2098 4.53125 12.4758 3.85742 13.0078 3.29883C13.5398 2.74023 14.1815 2.46094 14.933 2.46094C15.6845 2.46094 16.3263 2.74023 16.8583 3.29883C17.3903 3.85742 17.6562 4.53125 17.6562 5.32031ZM9.0625 14.0156C9.0625 13.2031 8.79092 12.5117 8.24777 11.9414C7.70461 11.3711 7.04613 11.0859 6.27232 11.0859C6.07143 11.0859 5.87054 11.1094 5.66964 11.1562L6.83036 11.6484C7.40327 11.8906 7.81064 12.3066 8.05246 12.8965C8.29427 13.4863 8.29985 14.0781 8.0692 14.6719C7.83854 15.2734 7.44048 15.6992 6.875 15.9492C6.30952 16.1992 5.74405 16.2031 5.17857 15.9609C5.02232 15.8984 4.79167 15.8027 4.48661 15.6738C4.18155 15.5449 3.95461 15.4492 3.8058 15.3867C4.0439 15.8555 4.38244 16.2324 4.82143 16.5176C5.26042 16.8027 5.74405 16.9453 6.27232 16.9453C7.04613 16.9453 7.70461 16.6602 8.24777 16.0898C8.79092 15.5195 9.0625 14.8281 9.0625 14.0156ZM18.3259 5.33203C18.3259 4.34766 17.9929 3.50586 17.327 2.80664C16.6611 2.10742 15.8594 1.75781 14.9219 1.75781C13.9769 1.75781 13.1715 2.10742 12.5056 2.80664C11.8397 3.50586 11.5067 4.34766 11.5067 5.33203C11.5067 6.32422 11.8397 7.16797 12.5056 7.86328C13.1715 8.55859 13.9769 8.90625 14.9219 8.90625C15.8594 8.90625 16.6611 8.55859 17.327 7.86328C17.9929 7.16797 18.3259 6.32422 18.3259 5.33203ZM20 5.33203C20 6.80859 19.5033 8.06641 18.51 9.10547C17.5167 10.1445 16.3207 10.6641 14.9219 10.6641L10.0446 14.4023C9.95536 15.4102 9.54985 16.2617 8.82812 16.957C8.1064 17.6523 7.25446 18 6.27232 18C5.37202 18 4.57589 17.7031 3.88393 17.1094C3.19196 16.5156 2.75298 15.7656 2.56696 14.8594L0 13.7812V8.75391L4.34152 10.5938C4.92932 10.2188 5.57292 10.0312 6.27232 10.0312C6.36905 10.0312 6.49926 10.0391 6.66295 10.0547L9.83259 5.28516C9.84747 3.82422 10.3516 2.57812 11.3449 1.54688C12.3382 0.515625 13.5305 0 14.9219 0C16.3207 0 17.5167 0.521484 18.51 1.56445C19.5033 2.60742 20 3.86328 20 5.33203Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
7
gui/src/components/commons/icon/UsbIcon.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export function USBIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
|
||||
<path d="M24 44q-1.6 0-2.6-1t-1-2.6q0-1.1.55-2 .55-.9 1.55-1.45V31.4h-7.4q-1.2 0-2.1-.9-.9-.9-.9-2.1v-5.45q-1-.45-1.55-1.325T10 19.6q0-1.6 1-2.6t2.6-1q1.6 0 2.6 1t1 2.6q0 1.15-.55 2.025T15.1 22.95v5.45h7.4v-17h-4L24 3.95l5.5 7.45h-4v17h7.4v-5.2h-2.1V16H38v7.2h-2.1v5.2q0 1.2-.9 2.1-.9.9-2.1.9h-7.4v5.55q1 .55 1.55 1.5t.55 1.95q0 1.6-1 2.6T24 44Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
10
gui/src/components/commons/icon/VRCIcon.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export function VRCIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M246.1 418.9h531.8c8.4 0 16 3.4 21.5 8.9s8.9 13.1 8.9 21.5v125.4c0 8.4-3.4 16-8.9 21.5-4.8 4.8-11.1 8-18.1 8.7v46.3c0 10.9-8.8 19-19.5 15.1-3.8-1.4-7.9-4.6-11.9-9.9L709.6 605H246.1c-8.4 0-16-3.4-21.5-8.9s-8.9-13.1-8.9-21.5V449.3c0-8.4 3.4-16 8.9-21.5s13.1-8.9 21.5-8.9zm523.8 174.8h8c5.2 0 10-2.1 13.5-5.6 3.5-3.5 5.6-8.2 5.6-13.5V449.3c0-5.2-2.1-10-5.6-13.5-3.5-3.5-8.2-5.6-13.5-5.6H246.1c-5.2 0-10 2.1-13.5 5.6-3.5 3.5-5.6 8.2-5.6 13.5v125.4c0 5.2 2.1 10 5.6 13.5 3.5 3.5 8.2 5.6 13.5 5.6h469.4c15.8 19.8 31.1 40 46.9 59.8 2.5 3.3 7.7 3.9 7.6-2.5v-57.4h-.1zm-132.3-67.1h23.9l-12-42.2-11.9 42.2zm110.8-74.2H460.6c-10.8 0-19.6 8.8-19.6 19.6v79.7c0 10.8 8.8 19.6 19.6 19.6h287.8c10.8 0 19.6-8.8 19.6-19.6V472c0-10.7-8.8-19.6-19.6-19.6zm-270.3 91.4c2.5 2.6 6.2 3.9 10.9 3.9 3.1 0 5.6-.5 7.4-1.5 1.8-1 3.2-2.2 4.1-3.7.9-1.5 1.5-3.1 1.9-4.7.3-1.7.5-3.3.6-4.7.2-2.1 1.1-3.6 2.8-4.4 1.7-.8 3.7-1.2 6.1-1.2 3.1 0 5.4.6 6.9 1.8 1.5 1.2 2.2 3.5 2.2 7 0 4.1-.8 7.9-2.4 11.3-1.6 3.4-3.9 6.3-6.8 8.6s-6.4 4.1-10.5 5.4c-4 1.3-8.4 1.9-13.2 1.9-4.3 0-8.4-.6-12.2-1.7-3.9-1.1-7.2-3-10.1-5.4-2.9-2.5-5.2-5.7-6.8-9.7-1.7-4-2.5-8.9-2.5-14.7v-38.4c0-5.8.8-10.7 2.5-14.7s4-7.2 6.9-9.7c2.9-2.5 6.3-4.3 10.1-5.4 3.9-1.1 7.9-1.7 12.2-1.7 4.8 0 9.2.6 13.2 1.9 4 1.2 7.5 3 10.4 5.2 2.9 2.2 5.1 5 6.7 8.3 1.6 3.3 2.4 6.9 2.4 10.8 0 3.6-.7 5.9-2.2 7.1-1.5 1.1-3.7 1.7-6.7 1.7-2.6 0-4.7-.4-6.3-1.2-1.7-.8-2.5-2.3-2.6-4.4-.1-1.1-.3-2.4-.6-3.9s-.9-3-1.9-4.5c-1-1.5-2.4-2.7-4.2-3.7-1.8-1-4.4-1.5-7.7-1.5-4.7 0-8.3 1.3-10.7 3.9-2.5 2.6-3.7 6.6-3.7 12v38.4c0 5.1 1.3 9.1 3.8 11.6zm122.7 13.1c0 1.8-.9 3.2-2.8 4.2s-3.9 1.4-6.1 1.4c-2.3 0-4.4-.5-6.2-1.4-1.8-1-2.8-2.4-2.8-4.2v-39.5h-31v39.5c0 1.8-.9 3.2-2.8 4.2s-3.9 1.4-6.1 1.4c-2.3 0-4.4-.5-6.2-1.4-1.8-1-2.8-2.4-2.8-4.2v-89.4c0-1.9.9-3.3 2.8-4.2 1.8-.9 3.9-1.3 6.2-1.3 2.2 0 4.2.4 6.1 1.3 1.9.9 2.8 2.3 2.8 4.2v35.9h31v-35.9c0-1.9.9-3.3 2.8-4.2 1.8-.9 3.9-1.3 6.2-1.3 2.2 0 4.2.4 6.1 1.3 1.9.9 2.8 2.3 2.8 4.2v89.4zm86 1.2c-.7.9-1.7 1.7-2.8 2.3-1.1.7-2.3 1.2-3.6 1.6-1.3.4-2.6.6-3.8.6-1.6 0-2.9-.3-3.9-.8-1.1-.6-1.8-1.5-2.1-2.8l-5.2-18.3h-31.5l-5.2 18.3c-.4 1.3-1.1 2.2-2.1 2.8-1.1.5-2.4.8-3.9.8-1.2 0-2.5-.2-3.8-.6-1.3-.4-2.5-.9-3.6-1.5s-2-1.4-2.8-2.3c-.7-.9-1.1-1.9-1.1-3 0-.2.1-.6.3-1.4l26.6-86.5c.6-2.1 2.1-3.6 4.3-4.6 2.2-1 4.6-1.4 7.2-1.4 2.7 0 5.1.5 7.3 1.4 2.2 1 3.6 2.5 4.3 4.6l26.6 86.5c.2.7.3 1.2.3 1.4-.4 1-.7 2-1.5 2.9zm65-82.5c-1 1.7-2.4 2.5-4.2 2.5h-20.1v78.8c0 1.8-.9 3.2-2.8 4.2s-3.9 1.4-6.1 1.4c-2.3 0-4.4-.5-6.2-1.4-1.8-1-2.8-2.4-2.8-4.2v-78.8h-20.2c-1.8 0-3.2-.8-4.2-2.4-1-1.6-1.4-3.5-1.4-5.7 0-1.9.5-3.7 1.4-5.4.9-1.7 2.3-2.5 4.3-2.5h58.2c1.9 0 3.3.8 4.3 2.5.9 1.7 1.4 3.5 1.4 5.4-.2 2.1-.7 3.9-1.6 5.6zm-495.2-4.8 26.6 86.5c.7 2.1 2.2 3.6 4.3 4.6 2.2 1 4.6 1.4 7.2 1.4 2.7 0 5.1-.5 7.3-1.4 2.2-1 3.6-2.5 4.3-4.6l26.4-86.5c.2-.7.3-1.2.3-1.4 0-1.1-.4-2.1-1.1-3-.7-.9-1.7-1.7-2.8-2.3-1.1-.7-2.3-1.2-3.6-1.6-1.3-.4-2.6-.5-3.8-.5-1.6 0-2.9.3-3.9.8-1.1.5-1.8 1.5-2.1 2.8l-20.9 74.6-21-74.6c-.4-1.3-1.1-2.2-2.1-2.8-1.1-.6-2.4-.8-3.9-.8-1.2 0-2.5.2-3.8.5-1.3.4-2.5.9-3.6 1.5s-2 1.4-2.8 2.3c-.7.9-1.1 1.9-1.1 3-.2.3-.1.7.1 1.5zm86.9-3.3c0-1.5.5-2.8 1.6-3.9s2.5-1.7 4.2-1.7h27.2c4.5 0 8.7.5 12.7 1.4 3.9 1 7.4 2.5 10.4 4.7s5.3 5.1 7 8.8c1.7 3.7 2.5 8.3 2.5 13.8 0 7.9-1.7 14.1-5.2 18.5-3.4 4.4-7.7 7.5-12.9 9l18 33.4c.3.4.5.8.5 1.2.1.4.1.8.1 1 0 1.1-.3 2.2-1 3.4-.7 1.1-1.6 2.2-2.6 3-1.1.9-2.2 1.6-3.6 2.1-1.3.6-2.6.8-3.9.8-1.3 0-2.5-.4-3.5-1.1-1.1-.7-1.9-1.8-2.7-3.2l-19.5-38h-11.6v35.9c0 1.8-.9 3.2-2.8 4.2-1.8 1-3.9 1.4-6.2 1.4-2.2 0-4.2-.5-6.1-1.4-1.9-1-2.8-2.4-2.8-4.2v-89.1h.2zm17.9 10.2V507h15.1c4.5 0 8.1-1.1 10.7-3.2 2.7-2.2 4-5.9 4-11.3s-1.3-9.2-4-11.4c-2.7-2.2-6.2-3.3-10.7-3.3h-15.1v-.1z"
|
||||
fill="#fff"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||