Compare commits
1111 Commits
v0.60.1
...
v0.70.0-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa77450a72 | ||
|
|
6cc84a77c8 | ||
|
|
27a6951403 | ||
|
|
9f3c8c1e3a | ||
|
|
f8d092fdc6 | ||
|
|
2c3c8b4cb0 | ||
|
|
cf193154e1 | ||
|
|
c3518cefe8 | ||
|
|
4746fb5936 | ||
|
|
c9a306b4ac | ||
|
|
c3b102f5a8 | ||
|
|
f61b870db6 | ||
|
|
1a6a807db5 | ||
|
|
01aac0de48 | ||
|
|
dc88a67f50 | ||
|
|
5ce0472a75 | ||
|
|
cc788dc5f7 | ||
|
|
7726a9ec3d | ||
|
|
fcf97ab41e | ||
|
|
bb200aa082 | ||
|
|
2cd9db1cfe | ||
|
|
467e5691b9 | ||
|
|
0bd6f9b6ce | ||
|
|
244f259331 | ||
|
|
625151806a | ||
|
|
6810490bf4 | ||
|
|
3312a06368 | ||
|
|
373902d933 | ||
|
|
f62d13de21 | ||
|
|
df2e9625b3 | ||
|
|
765773cfe6 | ||
|
|
9e5612348c | ||
|
|
aa9710f7c3 | ||
|
|
b90e1012bf | ||
|
|
96186a3dae | ||
|
|
2c1fd7b0bf | ||
|
|
9779663c6b | ||
|
|
8e02266d07 | ||
|
|
24ef80f4b6 | ||
|
|
febf992a43 | ||
|
|
e9fdb13cb5 | ||
|
|
216b1aec08 | ||
|
|
02f6928328 | ||
|
|
fe27f135c0 | ||
|
|
74f8b493b2 | ||
|
|
49379924cb | ||
|
|
14eec66e38 | ||
|
|
048da9ddce | ||
|
|
9c627e82a0 | ||
|
|
14899d867e | ||
|
|
db831c3fbb | ||
|
|
bfb43c67f8 | ||
|
|
a3da41bfad | ||
|
|
ef987cae6b | ||
|
|
41ff42ddec | ||
|
|
37a4de1a84 | ||
|
|
551dc1f318 | ||
|
|
866f0e1344 | ||
|
|
a222821dfa | ||
|
|
d49a29d793 | ||
|
|
176738d674 | ||
|
|
ebbe6e7aa9 | ||
|
|
d237bdaa9b | ||
|
|
828f406b4f | ||
|
|
e743f3b1d8 | ||
|
|
69e28d04b0 | ||
|
|
2be4f41964 | ||
|
|
97ed89a797 | ||
|
|
ad7eaca443 | ||
|
|
ddbf251b5f | ||
|
|
95098e4f29 | ||
|
|
529ccbda3a | ||
|
|
a46ca32356 | ||
|
|
12cd712b53 | ||
|
|
3cffee4065 | ||
|
|
213658f1e9 | ||
|
|
6b337914d7 | ||
|
|
386f7ba16d | ||
|
|
73e7967a12 | ||
|
|
83c98ce049 | ||
|
|
6a57bd2794 | ||
|
|
8487ae77e7 | ||
|
|
b762d70202 | ||
|
|
53cb3a4429 | ||
|
|
ef192a902a | ||
|
|
585c23e9f6 | ||
|
|
4708f5d88f | ||
|
|
a165cd596b | ||
|
|
0d31c8c1c8 | ||
|
|
8c5a0ca3a4 | ||
|
|
5c05b7d413 | ||
|
|
3da69117ae | ||
|
|
4256a96051 | ||
|
|
82e9f736bd | ||
|
|
fa620bf98f | ||
|
|
378f0c32fe | ||
|
|
77e322cb75 | ||
|
|
f669b8a029 | ||
|
|
09d57d1f26 | ||
|
|
7a629769b7 | ||
|
|
bd223f5a1f | ||
|
|
1006ada458 | ||
|
|
79f8f08caf | ||
|
|
789bbf15b7 | ||
|
|
1dd085fc92 | ||
|
|
1e18480808 | ||
|
|
93a634991b | ||
|
|
90fb9b53ad | ||
|
|
d0ce7b3516 | ||
|
|
b94c265240 | ||
|
|
8d70a22fa3 | ||
|
|
a6ffcdd0cf | ||
|
|
74843493f4 | ||
|
|
6b62ce2aaa | ||
|
|
2b1118f597 | ||
|
|
233b28a1b9 | ||
|
|
eeb21af841 | ||
|
|
a5bccecd48 | ||
|
|
0f818f2458 | ||
|
|
7187cc8a4c | ||
|
|
2bc36600d4 | ||
|
|
60f29410ca | ||
|
|
ca3c4566dd | ||
|
|
f3dee2d332 | ||
|
|
273988b8d5 | ||
|
|
b6337f59fd | ||
|
|
21a0df406f | ||
|
|
599acf0daa | ||
|
|
6458a9144e | ||
|
|
344d05045d | ||
|
|
75803d8dbb | ||
|
|
04e053a216 | ||
|
|
41bff3947c | ||
|
|
46152c6249 | ||
|
|
f65fda2fa4 | ||
|
|
96ac650465 | ||
|
|
ea16082a42 | ||
|
|
42e74e7eef | ||
|
|
738e161bc6 | ||
|
|
559e14799c | ||
|
|
eeb5b03d63 | ||
|
|
d750b02a7c | ||
|
|
c321f5d94a | ||
|
|
89da738fae | ||
|
|
8cd94060bb | ||
|
|
d8ccdff9fc | ||
|
|
47348542ef | ||
|
|
b5fb8e6b8b | ||
|
|
b0336cd27e | ||
|
|
ecd80c553c | ||
|
|
59d7f06c57 | ||
|
|
15f666a50a | ||
|
|
ec6f2a3ad4 | ||
|
|
213be3d6bd | ||
|
|
55800fc696 | ||
|
|
6a2066af6c | ||
|
|
cb8962691a | ||
|
|
bb00134f5f | ||
|
|
21d6665c37 | ||
|
|
6542b30d1f | ||
|
|
55ebfe8321 | ||
|
|
9d15b3d295 | ||
|
|
d31fd9bbf2 | ||
|
|
52babc51a0 | ||
|
|
1a3940a12e | ||
|
|
1aec691b35 | ||
|
|
70dd586be9 | ||
|
|
af85db9ea5 | ||
|
|
67b265b3d5 | ||
|
|
c8b209306e | ||
|
|
61c6c825b5 | ||
|
|
0ede89d82a | ||
|
|
6f211292b2 | ||
|
|
c49573dc11 | ||
|
|
de9c58d216 | ||
|
|
84a860e54d | ||
|
|
cb60eb8a57 | ||
|
|
d8219545c9 | ||
|
|
06f6d02579 | ||
|
|
1e02ebbd11 | ||
|
|
8c64514570 | ||
|
|
6fcb3c9020 | ||
|
|
2c47bd4a97 | ||
|
|
a5f624203e | ||
|
|
98d1b6ec5a | ||
|
|
457e1046c8 | ||
|
|
21ab1bb434 | ||
|
|
aa44de3d16 | ||
|
|
ad37034960 | ||
|
|
ebd0c5d000 | ||
|
|
f88b413f6a | ||
|
|
c2f5381e5a | ||
|
|
ea1f6689b9 | ||
|
|
b1affb13bb | ||
|
|
2679e245a5 | ||
|
|
5a334622ea | ||
|
|
5720c43fe7 | ||
|
|
af4d846428 | ||
|
|
5fb522a9b1 | ||
|
|
86e5ae1f2e | ||
|
|
aadd7f2886 | ||
|
|
067a19c971 | ||
|
|
688f179256 | ||
|
|
af77f1188a | ||
|
|
0dedc1f3a4 | ||
|
|
6c58a4f885 | ||
|
|
81e3b48f37 | ||
|
|
6da59311d1 | ||
|
|
2bc685281c | ||
|
|
7e0b6ed1c6 | ||
|
|
e08d6cd6de | ||
|
|
954c9ac3fd | ||
|
|
e4c5dfcf6c | ||
|
|
5f6313d336 | ||
|
|
70efd2bebe | ||
|
|
43b7e16c89 | ||
|
|
f99f581bfc | ||
|
|
09d3fbf04f | ||
|
|
363e3cae4b | ||
|
|
930be6706f | ||
|
|
05e99eb67e | ||
|
|
9bd400cf16 | ||
|
|
553585b9a1 | ||
|
|
674fddac87 | ||
|
|
63e7b9189d | ||
|
|
9530976f61 | ||
|
|
02c30b0091 | ||
|
|
b9c7796547 | ||
|
|
e00cb6b074 | ||
|
|
dc47552180 | ||
|
|
98a593b263 | ||
|
|
897506c797 | ||
|
|
59c9a57570 | ||
|
|
dde6cf596e | ||
|
|
2596fefa04 | ||
|
|
34b69896e4 | ||
|
|
7824ace58b | ||
|
|
b150efbd96 | ||
|
|
c20204d269 | ||
|
|
45bfcfc3b8 | ||
|
|
cf72173282 | ||
|
|
5218a2f966 | ||
|
|
95748123b5 | ||
|
|
6ad326ac58 | ||
|
|
b0652c55c6 | ||
|
|
790ef19a48 | ||
|
|
ecd44e6914 | ||
|
|
2cd9987b54 | ||
|
|
7c3dc1e3dc | ||
|
|
00b7c78e33 | ||
|
|
11800a8a78 | ||
|
|
99c5f8c713 | ||
|
|
461c2400ad | ||
|
|
073a2988e6 | ||
|
|
70aac75dd5 | ||
|
|
4dc838fbb7 | ||
|
|
d4c8fa3090 | ||
|
|
a594ba8f8a | ||
|
|
f1884d608b | ||
|
|
417db95693 | ||
|
|
0220d7ba5d | ||
|
|
e2b132ef23 | ||
|
|
7e8d9d52d3 | ||
|
|
6a6a032f1f | ||
|
|
fcea254e8e | ||
|
|
9bf0a02eae | ||
|
|
2affbcc495 | ||
|
|
8012e9fcbd | ||
|
|
cd2d593a6c | ||
|
|
9ef00ea44c | ||
|
|
91d6b66fc4 | ||
|
|
5a29a74956 | ||
|
|
db3119b553 | ||
|
|
f797dfb88f | ||
|
|
beea9b68ff | ||
|
|
82397f34d1 | ||
|
|
3cd77bfcc4 | ||
|
|
456396ca6e | ||
|
|
26b5653427 | ||
|
|
895c365485 | ||
|
|
8fa26bfe18 | ||
|
|
aca3f02590 | ||
|
|
d74fb97158 | ||
|
|
7608875625 | ||
|
|
dcf11ac7e5 | ||
|
|
5879dcc4e9 | ||
|
|
34388a1d31 | ||
|
|
3a4f8d267a | ||
|
|
0366d725ea | ||
|
|
8bd7b28056 | ||
|
|
2697112a8a | ||
|
|
9bd4bc8813 | ||
|
|
925c9e13bb | ||
|
|
da100a09fb | ||
|
|
c42da5c9b9 | ||
|
|
2733f91d8c | ||
|
|
83aefffa38 | ||
|
|
1b8763d0cf | ||
|
|
7dde54b052 | ||
|
|
b1e37378dc | ||
|
|
e61a38b3a9 | ||
|
|
2cf48c03f9 | ||
|
|
ab978ff1a3 | ||
|
|
dcd4b8f7db | ||
|
|
2eb335158b | ||
|
|
10aecc310e | ||
|
|
750e7eb833 | ||
|
|
36bc90b2b8 | ||
|
|
f6f41510d2 | ||
|
|
cffb064c16 | ||
|
|
3313387b28 | ||
|
|
d71d543337 | ||
|
|
665219fb00 | ||
|
|
1b8f23eeed | ||
|
|
5f31907127 | ||
|
|
97989b04a0 | ||
|
|
694840cdd6 | ||
|
|
1920de81d9 | ||
|
|
3b5b48c043 | ||
|
|
2080d3efff | ||
|
|
fc7b01b74e | ||
|
|
f1b35981c2 | ||
|
|
744714b478 | ||
|
|
35549ffabe | ||
|
|
855f17c378 | ||
|
|
f23f294b86 | ||
|
|
0921178b42 | ||
|
|
30872d3992 | ||
|
|
cd08d289aa | ||
|
|
9a62150dce | ||
|
|
7bbd97cfb9 | ||
|
|
5443d9cffe | ||
|
|
be3fb1e985 | ||
|
|
b97c35a468 | ||
|
|
eec3df09be | ||
|
|
d3c411677a | ||
|
|
d97a8364ad | ||
|
|
0ed731780a | ||
|
|
11c1254e71 | ||
|
|
6ba225f3a5 | ||
|
|
55eb0a3742 | ||
|
|
1ce0863158 | ||
|
|
d609237c32 | ||
|
|
4288f10873 | ||
|
|
80e035cc2c | ||
|
|
a1f273278b | ||
|
|
ffcad4e4e2 | ||
|
|
5262e8c77e | ||
|
|
5e240f98f0 | ||
|
|
189a820113 | ||
|
|
b8d423555b | ||
|
|
8a48567857 | ||
|
|
f68e8d4664 | ||
|
|
1b225fa37c | ||
|
|
a29ccb4ff8 | ||
|
|
9cd6894dc5 | ||
|
|
dd9d20be25 | ||
|
|
260164a711 | ||
|
|
359b8aaf47 | ||
|
|
1cc3e4820a | ||
|
|
b01243109e | ||
|
|
3e0f9d27a7 | ||
|
|
2dc1130902 | ||
|
|
37174f45f0 | ||
|
|
76c42af62a | ||
|
|
cf4c103660 | ||
|
|
e1eff3f4cd | ||
|
|
a47f2ca445 | ||
|
|
e659823e6c | ||
|
|
a8ed95e1dc | ||
|
|
cb1d2cd1f2 | ||
|
|
9077b058a2 | ||
|
|
7ceb5e815e | ||
|
|
992b94eef3 | ||
|
|
a0cb6542ba | ||
|
|
6530658c3e | ||
|
|
75d3d46b1b | ||
|
|
d20d21c6a2 | ||
|
|
c1f7902309 | ||
|
|
4798161118 | ||
|
|
2a5565ca93 | ||
|
|
a5edac312e | ||
|
|
e578f2530e | ||
|
|
c84201fc9f | ||
|
|
4a00f0b062 | ||
|
|
64ac84fdf4 | ||
|
|
f27a9d77d1 | ||
|
|
0186289420 | ||
|
|
6b214acbc4 | ||
|
|
d419f27d75 | ||
|
|
eb0598dac2 | ||
|
|
aa7b909b7b | ||
|
|
b552f1788c | ||
|
|
d492cbced9 | ||
|
|
19aac6a57f | ||
|
|
685bc9fed3 | ||
|
|
406663c75e | ||
|
|
c8face33fa | ||
|
|
3c1b747f64 | ||
|
|
777f05eb76 | ||
|
|
395070cb92 | ||
|
|
a4a1859dfc | ||
|
|
e3fdfe02e5 | ||
|
|
7744c9ba45 | ||
|
|
e6ca0adbcb | ||
|
|
c105f41487 | ||
|
|
ddecba143f | ||
|
|
3451a3c7fe | ||
|
|
b9cbd4084e | ||
|
|
5505a776e6 | ||
|
|
46ff0885f0 | ||
|
|
a9dc46c950 | ||
|
|
7d33520b2c | ||
|
|
e9ea751f3d | ||
|
|
d7bbfb82a3 | ||
|
|
500ecbf915 | ||
|
|
e5c6393f85 | ||
|
|
73f0459a0f | ||
|
|
0c466f806c | ||
|
|
b48e28b555 | ||
|
|
60ebe33518 | ||
|
|
72c1ee904b | ||
|
|
57e10b7dd5 | ||
|
|
4bc1d77535 | ||
|
|
d96f524fb6 | ||
|
|
1c30767592 | ||
|
|
969c314315 | ||
|
|
568de814aa | ||
|
|
27f6ae945d | ||
|
|
1b46b7a7d6 | ||
|
|
7502558631 | ||
|
|
48b6ee313f | ||
|
|
dec5f37e4e | ||
|
|
239a04ea5b | ||
|
|
ea03b48243 | ||
|
|
82824f78b6 | ||
|
|
e4507c1d74 | ||
|
|
585ac3e1be | ||
|
|
29a4baf346 | ||
|
|
cfdf0a57b8 | ||
|
|
944d6554de | ||
|
|
e3ac67784a | ||
|
|
62624b81d8 | ||
|
|
256e3e8e0f | ||
|
|
aebc6326a9 | ||
|
|
db1d93576f | ||
|
|
d2385bd6a0 | ||
|
|
19d14737bf | ||
|
|
4f864a20a7 | ||
|
|
2375741bdf | ||
|
|
46f1d5f5c2 | ||
|
|
d70996bb99 | ||
|
|
9314c0e313 | ||
|
|
5a0c39cbed | ||
|
|
41b2fde10d | ||
|
|
023ecd595b | ||
|
|
2b979d3b88 | ||
|
|
5965113fc8 | ||
|
|
a48cd9125b | ||
|
|
4c04d512db | ||
|
|
d1a44b889e | ||
|
|
04d553d4d3 | ||
|
|
2e24d128db | ||
|
|
9e59056e7f | ||
|
|
d9a892a423 | ||
|
|
3a1cd6ed3a | ||
|
|
6120d6488b | ||
|
|
82abf31ef1 | ||
|
|
9f9398476d | ||
|
|
b7294887c7 | ||
|
|
049c0f8ba4 | ||
|
|
11a39226e8 | ||
|
|
ac24600a40 | ||
|
|
d525cfd697 | ||
|
|
4436ec48eb | ||
|
|
5a9a0f9fa5 | ||
|
|
6d9b55a654 | ||
|
|
3eac3e20d5 | ||
|
|
d2cd9c94f7 | ||
|
|
3adc0b947f | ||
|
|
718f802157 | ||
|
|
f71145bb32 | ||
|
|
cd2a8579b9 | ||
|
|
d0709e7bfa | ||
|
|
fa3f100eff | ||
|
|
f0a721032d | ||
|
|
0a565c6bae | ||
|
|
af2a2d2494 | ||
|
|
cd0b663f62 | ||
|
|
2a0ddd99d2 | ||
|
|
5581674f8f | ||
|
|
b0e1d6bc7f | ||
|
|
ae11e4f798 | ||
|
|
0b0fe91545 | ||
|
|
aeea47323a | ||
|
|
e4185f38cf | ||
|
|
09e6d44873 | ||
|
|
525d84e5bf | ||
|
|
55ca085d7d | ||
|
|
03cfd23ac5 | ||
|
|
a666ca3e40 | ||
|
|
b58ae8bdd7 | ||
|
|
5e7652698d | ||
|
|
e51cbf67ab | ||
|
|
8c75df30cb | ||
|
|
1c84e77c37 | ||
|
|
b3a92979a3 | ||
|
|
55d3c09b6b | ||
|
|
436c89650a | ||
|
|
4ead1ecbbf | ||
|
|
074e3cfbd6 | ||
|
|
bb32599ded | ||
|
|
f9cbed5a1f | ||
|
|
0078bea877 | ||
|
|
bb80cee19e | ||
|
|
0c50c0959d | ||
|
|
75b8a12ab3 | ||
|
|
4c1b4953c1 | ||
|
|
c3d556d9bd | ||
|
|
d090d230e2 | ||
|
|
bca635e5d3 | ||
|
|
3938adf60a | ||
|
|
6537def97e | ||
|
|
5020c70a04 | ||
|
|
0a63d2e3e1 | ||
|
|
ce0dfde8ee | ||
|
|
44bb2ce024 | ||
|
|
6c83be3f89 | ||
|
|
0a4517f97e | ||
|
|
c34a5f3177 | ||
|
|
4f39181c4c | ||
|
|
e7e45be6e1 | ||
|
|
8621c88a3c | ||
|
|
7dae21cb36 | ||
|
|
0f4598a243 | ||
|
|
6415809b61 | ||
|
|
fe93263ad4 | ||
|
|
3b34d858b5 | ||
|
|
71eeeedc05 | ||
|
|
532a599239 | ||
|
|
9eee22ff0a | ||
|
|
94fe93c6ee | ||
|
|
93824dd239 | ||
|
|
e5f05c9f3b | ||
|
|
bdb521cb6b | ||
|
|
c613c98e37 | ||
|
|
4e4299d500 | ||
|
|
c1291a093b | ||
|
|
ccc8c247a1 | ||
|
|
8e6c5dbc3b | ||
|
|
3c53fcdb43 | ||
|
|
adf43c87dd | ||
|
|
faf265328e | ||
|
|
9bc57c0c61 | ||
|
|
95369f92eb | ||
|
|
117458f4f6 | ||
|
|
eeb32fa888 | ||
|
|
f9567ae116 | ||
|
|
c151c87e12 | ||
|
|
3190236396 | ||
|
|
17dfbb91ba | ||
|
|
c3cf056fc5 | ||
|
|
275f0ae492 | ||
|
|
f4e9759f26 | ||
|
|
fdf758e050 | ||
|
|
0dfacd7ffa | ||
|
|
36c07f940c | ||
|
|
01929037f1 | ||
|
|
0817f905a2 | ||
|
|
ad67f5e4de | ||
|
|
e9eadcaa6a | ||
|
|
4b1dcf2d55 | ||
|
|
974ef967a3 | ||
|
|
be523617c9 | ||
|
|
6cbf197226 | ||
|
|
3e8fcb04f7 | ||
|
|
42bb5f0e9f | ||
|
|
e401caff7c | ||
|
|
b222e8eb5a | ||
|
|
fb35631337 | ||
|
|
6659dac2e5 | ||
|
|
b9af2ae66e | ||
|
|
0dcdd6ea39 | ||
|
|
a66aa9c09c | ||
|
|
e6c5079a49 | ||
|
|
d7369ace6a | ||
|
|
40073f6100 | ||
|
|
65c5adff05 | ||
|
|
59e8600e4c | ||
|
|
0310e27347 | ||
|
|
9902211af1 | ||
|
|
1da5be6e8f | ||
|
|
ee66adbb49 | ||
|
|
3612c46d6d | ||
|
|
bf9c9b0103 | ||
|
|
ea8778921b | ||
|
|
2ef2b5a053 | ||
|
|
5bb7701de7 | ||
|
|
2145965749 | ||
|
|
11caba4a4c | ||
|
|
9f39dcf7cf | ||
|
|
1135aeecb8 | ||
|
|
b6f78cd5dc | ||
|
|
a6198c9a1a | ||
|
|
ad698fd110 | ||
|
|
0d1d267213 | ||
|
|
c213c98ea4 | ||
|
|
cc58607c3b | ||
|
|
58947c5c72 | ||
|
|
6871bbbc71 | ||
|
|
28aa1567ce | ||
|
|
f639c4c3d1 | ||
|
|
d61c0fb24c | ||
|
|
3d5a3634cf | ||
|
|
9ad8731897 | ||
|
|
44c3cedc48 | ||
|
|
eeeaf6d9a2 | ||
|
|
2d4deaafcd | ||
|
|
c839ab2028 | ||
|
|
5d17347a45 | ||
|
|
9ce3524eb8 | ||
|
|
03115c8d71 | ||
|
|
dafdc4b4a5 | ||
|
|
05a6bd914d | ||
|
|
fb03eb7a3c | ||
|
|
8e70e1934a | ||
|
|
1bb41b6f54 | ||
|
|
90d1d9ac82 | ||
|
|
bed06346d1 | ||
|
|
7e02ac772a | ||
|
|
c0d67d9522 | ||
|
|
d14dd27cdc | ||
|
|
6b4dd2a5de | ||
|
|
9355d501bc | ||
|
|
335db5d03d | ||
|
|
98461ea0cd | ||
|
|
5707bae9b9 | ||
|
|
bbeb685769 | ||
|
|
cea103e47c | ||
|
|
ad31c284c7 | ||
|
|
738893c527 | ||
|
|
6da04d0eee | ||
|
|
7482660456 | ||
|
|
00123ffe2b | ||
|
|
53f8744794 | ||
|
|
537d4762f6 | ||
|
|
2f5004c238 | ||
|
|
7dcd6c920f | ||
|
|
ea42bc3c9b | ||
|
|
d3ba769291 | ||
|
|
3f1b95927f | ||
|
|
c183e854d7 | ||
|
|
86f51ade60 | ||
|
|
c838a7d973 | ||
|
|
9abfa037fd | ||
|
|
5efe2ed6d3 | ||
|
|
847376a4f5 | ||
|
|
1d6af4cf20 | ||
|
|
b6c5c7871e | ||
|
|
5acae094bd | ||
|
|
4d7425f4bf | ||
|
|
2497e7c008 | ||
|
|
474a5dd4f2 | ||
|
|
18ff459014 | ||
|
|
927cfa44db | ||
|
|
be6ee3cbff | ||
|
|
7301ab4f44 | ||
|
|
0b231e58fd | ||
|
|
08b84416d2 | ||
|
|
aec8aec800 | ||
|
|
5bcf9916c9 | ||
|
|
6076a3fc61 | ||
|
|
05389dc239 | ||
|
|
d222904471 | ||
|
|
9f3ea0c87f | ||
|
|
601ec40ddc | ||
|
|
adc4a5984e | ||
|
|
0f78174d78 | ||
|
|
ad67a1b744 | ||
|
|
edc2966651 | ||
|
|
eacfa856cf | ||
|
|
fe4862d756 | ||
|
|
8312d974ac | ||
|
|
6d3bd495fc | ||
|
|
576e350bea | ||
|
|
cc1325d6f9 | ||
|
|
c411cb7eef | ||
|
|
c9ba41d002 | ||
|
|
3caa7a4916 | ||
|
|
370a6f3dbd | ||
|
|
d8685baa47 | ||
|
|
4f344f1ac1 | ||
|
|
6a07f59d34 | ||
|
|
7981cd45ed | ||
|
|
66b1283c95 | ||
|
|
d275474b23 | ||
|
|
86057ab071 | ||
|
|
4977acf6a5 | ||
|
|
aafc3a9584 | ||
|
|
dc657a647e | ||
|
|
e5f0965138 | ||
|
|
0cd2d9a9c8 | ||
|
|
b2b25acc4c | ||
|
|
0b79950510 | ||
|
|
d6d1e20f07 | ||
|
|
c58abf1b0b | ||
|
|
88d2e2e277 | ||
|
|
946c92667f | ||
|
|
f54f653d42 | ||
|
|
ef72c75fab | ||
|
|
c6e52dbef7 | ||
|
|
62547e87dd | ||
|
|
eb6b545eeb | ||
|
|
4c4ebbfa19 | ||
|
|
61f31bf010 | ||
|
|
495fd151f5 | ||
|
|
482a5bb02a | ||
|
|
4e35b26365 | ||
|
|
c180137e02 | ||
|
|
ef837232bc | ||
|
|
d0aa9f1c57 | ||
|
|
aa549d1da7 | ||
|
|
259a758849 | ||
|
|
7ac45379eb | ||
|
|
0940482c62 | ||
|
|
9cbb698b96 | ||
|
|
1820be4990 | ||
|
|
37ca232548 | ||
|
|
ea9b009a22 | ||
|
|
dbc804669c | ||
|
|
c8fbc0d348 | ||
|
|
8361b4d47a | ||
|
|
ae2021e073 | ||
|
|
c4b21a0ab5 | ||
|
|
8a095d0a55 | ||
|
|
c0bf8fd6a8 | ||
|
|
14329980e1 | ||
|
|
950626fe9e | ||
|
|
8b7e587467 | ||
|
|
552ebc0f29 | ||
|
|
9148e1d30a | ||
|
|
9156d488ca | ||
|
|
e1ac1675ea | ||
|
|
863250904b | ||
|
|
8a926c4a23 | ||
|
|
760cceb378 | ||
|
|
c5650f9334 | ||
|
|
bf968707df | ||
|
|
31fa7ca90c | ||
|
|
309f401015 | ||
|
|
33d02cedd7 | ||
|
|
daa75b5282 | ||
|
|
36c64045ae | ||
|
|
93e1e30323 | ||
|
|
9b69f9a3ff | ||
|
|
d5fbb59656 | ||
|
|
7ba95d5d6c | ||
|
|
d79aeba2c1 | ||
|
|
672b445676 | ||
|
|
e02199fa2a | ||
|
|
9e55051811 | ||
|
|
7db176a763 | ||
|
|
92886236a2 | ||
|
|
8d94de8eb2 | ||
|
|
eaebec88c0 | ||
|
|
22fa6c07dd | ||
|
|
afe9ab9d8c | ||
|
|
16139cc6b6 | ||
|
|
31a904d370 | ||
|
|
02cf7679a3 | ||
|
|
df708465d1 | ||
|
|
aa9ccf3411 | ||
|
|
6410fdc474 | ||
|
|
499d947e69 | ||
|
|
c093516351 | ||
|
|
41699224ff | ||
|
|
8886cb5786 | ||
|
|
d85d4de218 | ||
|
|
f56f0b7bbb | ||
|
|
ae79b50101 | ||
|
|
fcfc4a4298 | ||
|
|
d355bd3372 | ||
|
|
2bfd46d48c | ||
|
|
f1b41389b3 | ||
|
|
92a4998ddc | ||
|
|
23d7209f82 | ||
|
|
cf3c610eba | ||
|
|
6a010f58be | ||
|
|
0f1b0a4a78 | ||
|
|
a4a8596a29 | ||
|
|
1cdd3c0e28 | ||
|
|
22db5bffe8 | ||
|
|
a61f3b715b | ||
|
|
949a28d49c | ||
|
|
88be4fe77e | ||
|
|
625a62626e | ||
|
|
ee440cf300 | ||
|
|
cf2ec99a4d | ||
|
|
bb0f6e85a8 | ||
|
|
4412217f51 | ||
|
|
1e85361914 | ||
|
|
f611b443c0 | ||
|
|
5984be3d84 | ||
|
|
5a8061ac7b | ||
|
|
509c327b3b | ||
|
|
56a66b348d | ||
|
|
e2ba8d6df7 | ||
|
|
a7d86a164c | ||
|
|
383334633f | ||
|
|
6a2dc444c6 | ||
|
|
e9073310c4 | ||
|
|
3b67602b13 | ||
|
|
04477e9f97 | ||
|
|
990c83eabd | ||
|
|
ddc71653ad | ||
|
|
e5e5cf1314 | ||
|
|
f364a15d89 | ||
|
|
2b4fd53202 | ||
|
|
dfe2fd0386 | ||
|
|
2055f05b09 | ||
|
|
33ebfc3f10 | ||
|
|
6a4f3aaa56 | ||
|
|
c1f7ac0d8c | ||
|
|
19adfdf8bb | ||
|
|
af74d5409a | ||
|
|
2a3773240d | ||
|
|
782676dc67 | ||
|
|
68717d0fe8 | ||
|
|
8bd9577318 | ||
|
|
2ac537393d | ||
|
|
82956b618a | ||
|
|
a725ded95e | ||
|
|
113b7f6f97 | ||
|
|
aed085b168 | ||
|
|
345544646a | ||
|
|
4520227e98 | ||
|
|
f5795ffc6f | ||
|
|
8cde64d3f6 | ||
|
|
d7b8a189e4 | ||
|
|
cfde3e348c | ||
|
|
70e2951e35 | ||
|
|
ba35536664 | ||
|
|
b9f9819637 | ||
|
|
076d353e84 | ||
|
|
64e9b9f893 | ||
|
|
21ad375b42 | ||
|
|
cb9534eae0 | ||
|
|
8b43368bf9 | ||
|
|
c96c8fd782 | ||
|
|
c295f943ba | ||
|
|
e527474dd9 | ||
|
|
73f267167f | ||
|
|
40290a9a42 | ||
|
|
bd35468d18 | ||
|
|
c80395fc18 | ||
|
|
95be2c6070 | ||
|
|
fb7a92242b | ||
|
|
8c2ff69515 | ||
|
|
011085a93f | ||
|
|
dce21900a7 | ||
|
|
2b5ac535b9 | ||
|
|
484c8f7cbe | ||
|
|
7e4d582d1e | ||
|
|
50c4783333 | ||
|
|
9860dbbbea | ||
|
|
874a3605f8 | ||
|
|
088c5bac1f | ||
|
|
e135b982c1 | ||
|
|
a8bd234aa4 | ||
|
|
f99d70500c | ||
|
|
476020ae84 | ||
|
|
2f1ddc0d0f | ||
|
|
ef5844bc79 | ||
|
|
0c9ceb51e6 | ||
|
|
cedc0f64d5 | ||
|
|
9952f08cce | ||
|
|
efa6745035 | ||
|
|
4816a587c3 | ||
|
|
6514eb5209 | ||
|
|
2a38c4938d | ||
|
|
b015761131 | ||
|
|
99e6ecc466 | ||
|
|
7e411ae098 | ||
|
|
1bbb7dd126 | ||
|
|
78969d0938 | ||
|
|
bac3dc1ccd | ||
|
|
ae44a38285 | ||
|
|
77b13b1356 | ||
|
|
2e97e2dbfd | ||
|
|
75ec5c3b1b | ||
|
|
3a456b09cb | ||
|
|
022f70b1de | ||
|
|
c1e23fc6d9 | ||
|
|
a6e9d0d061 | ||
|
|
b700ea84a5 | ||
|
|
0ef62fc334 | ||
|
|
c3900565b9 | ||
|
|
a86756ed20 | ||
|
|
e3ef6d35ab | ||
|
|
038670cc6f | ||
|
|
5d87a04dc3 | ||
|
|
fbfe8a2311 | ||
|
|
bd8509990a | ||
|
|
6bdb08ab9c | ||
|
|
db8b8ef66b | ||
|
|
ac5d5e2451 | ||
|
|
fad6cfef05 | ||
|
|
c83cae60f6 | ||
|
|
9b8e6cce02 | ||
|
|
9858906463 | ||
|
|
be1dc01d9e | ||
|
|
de24b4b4e8 | ||
|
|
629d3d473c | ||
|
|
5dc82d3df8 | ||
|
|
76a1b81e45 | ||
|
|
99aa1219d2 | ||
|
|
69472f7823 | ||
|
|
723fa83909 | ||
|
|
2f064d5ccc | ||
|
|
ae9a0a99ea | ||
|
|
c2b9b08944 | ||
|
|
2aa2e5af7a | ||
|
|
b7c439f4c4 | ||
|
|
e6b29086a9 | ||
|
|
83e4e26989 | ||
|
|
caec9c1f45 | ||
|
|
e3809c267d | ||
|
|
0d9eecd2ed | ||
|
|
d7915840d0 | ||
|
|
8098697847 | ||
|
|
4c2f8406c7 | ||
|
|
e0a477265d | ||
|
|
364c3f2f00 | ||
|
|
75c79d60fe | ||
|
|
5b2dd8e4d0 | ||
|
|
9e8e227b46 | ||
|
|
adf7578007 | ||
|
|
b6e5aa3bb0 | ||
|
|
288c039929 | ||
|
|
fb5c6493cf | ||
|
|
3160d07b9c | ||
|
|
e49fc9f4b1 | ||
|
|
ed6f482e68 | ||
|
|
773f569385 | ||
|
|
219793afcc | ||
|
|
571636c526 | ||
|
|
cbc15b6b58 | ||
|
|
c410935c9c | ||
|
|
79cf5dbd4b | ||
|
|
da5203011c | ||
|
|
84c7aa9cad | ||
|
|
f8e5a08324 | ||
|
|
5e57a33df7 | ||
|
|
38bdf7ad92 | ||
|
|
5447f63e9d | ||
|
|
50ba8bdc9b | ||
|
|
6f279c0239 | ||
|
|
26ccd70e77 | ||
|
|
b0ddbeb0ad | ||
|
|
826eb113e7 | ||
|
|
2661a9cc98 | ||
|
|
b06366ebb7 | ||
|
|
c7a629ba6b | ||
|
|
d155c11729 | ||
|
|
0c3c1e1f68 | ||
|
|
6c322dc835 | ||
|
|
6019e4c37b | ||
|
|
9c8dd66b20 | ||
|
|
0c0e8688ed | ||
|
|
6146923dbb | ||
|
|
2c4f003897 | ||
|
|
0491747eed | ||
|
|
29b9651ebd | ||
|
|
48a1dd1588 | ||
|
|
9cf39b1da6 | ||
|
|
47be340cac | ||
|
|
bf98300547 | ||
|
|
46635956f4 | ||
|
|
8c6de99159 | ||
|
|
a42a703b35 | ||
|
|
59fab0bb2d | ||
|
|
c73e2c2d0f | ||
|
|
8c1c98a0bf | ||
|
|
d99a074bc0 | ||
|
|
05b4b443d9 | ||
|
|
4b09f77950 | ||
|
|
dbea3cf20c | ||
|
|
aa8fa4a6d5 | ||
|
|
dbc03e2668 | ||
|
|
4ef69c8361 | ||
|
|
895aeb033f | ||
|
|
e15cc376b0 | ||
|
|
54428ca6f6 | ||
|
|
54cf6fa838 | ||
|
|
09a0b3eb55 | ||
|
|
40c3e925ad | ||
|
|
5ef5147780 | ||
|
|
318b923bac | ||
|
|
93a30ea940 | ||
|
|
5bb2edca8b | ||
|
|
1789dfb8b1 | ||
|
|
f473eadf2d | ||
|
|
1f161b9aa1 | ||
|
|
354fefe61b | ||
|
|
19c98bb5ad | ||
|
|
2149c17a0a | ||
|
|
1716aff969 | ||
|
|
2a5d7ea2de | ||
|
|
be34c50c72 | ||
|
|
50ae3e03f7 | ||
|
|
499b8f5f55 | ||
|
|
81d83841ab | ||
|
|
cce00526b9 | ||
|
|
c9225bb87c | ||
|
|
75c339851f | ||
|
|
e39c7c62e4 | ||
|
|
b6bb2985f5 | ||
|
|
6bdbab2faf | ||
|
|
f09d6b7b95 | ||
|
|
19a2752674 | ||
|
|
5d433b1666 | ||
|
|
caeae38e3a | ||
|
|
c25acc155d | ||
|
|
4222f86537 | ||
|
|
9569323f93 | ||
|
|
0bbba90f30 | ||
|
|
f1ff557a25 | ||
|
|
23d7143298 | ||
|
|
12eab6551f | ||
|
|
d25c6b15a6 | ||
|
|
b9308ad80d | ||
|
|
6e363e464c | ||
|
|
6e53deb1b2 | ||
|
|
0717c168d9 | ||
|
|
6d020a3ee9 | ||
|
|
9a381c1803 | ||
|
|
3e23d1f48d | ||
|
|
1750fcf833 | ||
|
|
646d344a11 | ||
|
|
bc03592912 | ||
|
|
a4b518ec72 | ||
|
|
b541ac313c | ||
|
|
934474f87e | ||
|
|
3a4e802093 | ||
|
|
b3eb5f7cdf | ||
|
|
c21e0e916c | ||
|
|
d301a215f7 | ||
|
|
8044beffc7 | ||
|
|
8df84e0341 | ||
|
|
137a9cefbd | ||
|
|
55576f879b | ||
|
|
78aee53411 | ||
|
|
864020463f | ||
|
|
2d3d07d4d7 | ||
|
|
ad6f9b2499 | ||
|
|
330968434f | ||
|
|
4b12fb6b3b | ||
|
|
eef086f60f | ||
|
|
6ac0b81778 | ||
|
|
8d82702da2 | ||
|
|
dde3dfdbf6 | ||
|
|
8d609959f1 | ||
|
|
16f854b636 | ||
|
|
9c47325c25 | ||
|
|
a6a7e85894 | ||
|
|
e75dcc853b | ||
|
|
e744520d90 | ||
|
|
a6910584b6 | ||
|
|
6dfa34fcf8 | ||
|
|
b626ec3bf9 | ||
|
|
5708879b5a | ||
|
|
95bc18a995 | ||
|
|
61dc703a58 | ||
|
|
a87d9d3578 | ||
|
|
fc770c6ea5 | ||
|
|
0c68abbe17 | ||
|
|
2d25e25ec3 | ||
|
|
c4028ef116 | ||
|
|
393d728769 | ||
|
|
431ac1267a | ||
|
|
5bc074005c | ||
|
|
4a61b1011e | ||
|
|
84847ff181 | ||
|
|
0bbc02e10d | ||
|
|
0ed811b81b | ||
|
|
ce2112df43 | ||
|
|
7080dc9c23 | ||
|
|
3c62de34f7 | ||
|
|
a6cccf82f7 | ||
|
|
f8da5ab2e7 | ||
|
|
fbe5f9225c | ||
|
|
773423fcf4 | ||
|
|
a62e2a38d7 | ||
|
|
48dcc465f2 | ||
|
|
d0c50b4fbf | ||
|
|
ab3a6f775e | ||
|
|
0674ca14d9 | ||
|
|
d0b35b5e19 | ||
|
|
01570504ad | ||
|
|
506c28d2b6 | ||
|
|
53f58f72f2 | ||
|
|
c9786fe464 | ||
|
|
c2ffc7086c | ||
|
|
96f9ee784d | ||
|
|
962f087ac2 | ||
|
|
ebe8c952e4 | ||
|
|
eabd687cbc | ||
|
|
593c7a8cd1 | ||
|
|
79b9420017 | ||
|
|
db5c83eb36 | ||
|
|
56f9543a95 |
@@ -1,3 +1,11 @@
|
||||
/target
|
||||
/manifest.yml
|
||||
/migrate.yml
|
||||
**/target
|
||||
zed.xcworkspace
|
||||
.DS_Store
|
||||
plugins/bin
|
||||
script/node_modules
|
||||
styles/node_modules
|
||||
crates/collab/static/styles.css
|
||||
vendor/bin
|
||||
assets/themes/*.json
|
||||
assets/themes/internal/*.json
|
||||
assets/themes/experiments/*.json
|
||||
|
||||
45
.github/workflows/ci.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- "v*"
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request:
|
||||
@@ -39,24 +40,30 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
clean: false
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --workspace --no-fail-fast
|
||||
|
||||
- name: Build collab binaries
|
||||
run: cargo build --bins --all-features
|
||||
- name: Build collab
|
||||
run: cargo build -p collab
|
||||
|
||||
- name: Build other binaries
|
||||
run: cargo build --workspace --bins --all-features
|
||||
|
||||
bundle:
|
||||
name: Bundle app
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- bundle
|
||||
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
|
||||
needs: tests
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
|
||||
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
|
||||
ZED_AMPLITUDE_API_KEY: ${{ secrets.ZED_AMPLITUDE_API_KEY }}
|
||||
ZED_MIXPANEL_TOKEN: ${{ secrets.ZED_MIXPANEL_TOKEN }}
|
||||
steps:
|
||||
- name: Install Rust
|
||||
run: |
|
||||
@@ -75,10 +82,32 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
clean: false
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Validate version
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
run: script/validate-version
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
version=$(script/get-crate-version zed)
|
||||
channel=$(cat crates/zed/RELEASE_CHANNEL)
|
||||
echo "Publishing version: ${version} on release channel ${channel}"
|
||||
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
|
||||
|
||||
expected_tag_name=""
|
||||
case ${channel} in
|
||||
stable)
|
||||
expected_tag_name="v${version}";;
|
||||
preview)
|
||||
expected_tag_name="v${version}-pre";;
|
||||
*)
|
||||
echo "can't publish a release on channel ${channel}"
|
||||
exit 1;;
|
||||
esac
|
||||
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
|
||||
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create app bundle
|
||||
run: script/bundle
|
||||
@@ -91,12 +120,12 @@ jobs:
|
||||
path: target/release/Zed.dmg
|
||||
|
||||
- uses: softprops/action-gh-release@v1
|
||||
name: Upload app bundle to release if release tag
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
name: Upload app bundle to release
|
||||
if: ${{ env.RELEASE_CHANNEL }}
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||
files: target/release/Zed.dmg
|
||||
overwrite: true
|
||||
body: ""
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
46
.github/workflows/publish_collab_image.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Publish Collab Server Image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- collab-v*
|
||||
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
DIGITALOCEAN_ACCESS_TOKEN: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish collab server image
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- deploy
|
||||
steps:
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Sign into DigitalOcean docker registry
|
||||
run: doctl registry login
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
clean: false
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Determine version
|
||||
run: |
|
||||
set -eu
|
||||
version=$(script/get-crate-version collab)
|
||||
if [[ $GITHUB_REF_NAME != "collab-v${version}" ]]; then
|
||||
echo "release tag ${GITHUB_REF_NAME} does not match version ${version}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Publishing collab version: ${version}"
|
||||
echo "COLLAB_VERSION=${version}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build docker image
|
||||
run: docker build . --tag registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}
|
||||
|
||||
- name: Publish docker image
|
||||
run: docker push registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}
|
||||
25
.github/workflows/release_actions.yml
vendored
@@ -8,19 +8,29 @@ jobs:
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
if: ${{ ! github.event.release.prerelease }}
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: |
|
||||
📣 Zed ${{ github.event.release.tag_name }} was just released!
|
||||
|
||||
Restart your Zed or head to https://zed.dev/releases to grab it.
|
||||
Restart your Zed or head to https://zed.dev/releases/latest to grab it.
|
||||
|
||||
```md
|
||||
### Changelog
|
||||
# Changelog
|
||||
|
||||
${{ github.event.release.body }}
|
||||
```
|
||||
amplitude_release:
|
||||
discourse_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v2
|
||||
if: ${{ ! github.event.release.prerelease }}
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: script/discourse_release ${{ secrets.DISCOURSE_RELEASES_API_KEY }} ${{ github.event.release.tag_name }} ${{ github.event.release.body }}
|
||||
mixpanel_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -29,5 +39,10 @@ jobs:
|
||||
python-version: "3.10.5"
|
||||
architecture: "x64"
|
||||
cache: "pip"
|
||||
- run: pip install -r script/amplitude_release/requirements.txt
|
||||
- run: python script/amplitude_release/main.py ${{ github.event.release.tag_name }} ${{ secrets.ZED_AMPLITUDE_API_KEY }} ${{ secrets.ZED_AMPLITUDE_SECRET_KEY }}
|
||||
- run: pip install -r script/mixpanel_release/requirements.txt
|
||||
- run: >
|
||||
python script/mixpanel_release/main.py
|
||||
${{ github.event.release.tag_name }}
|
||||
${{ secrets.MIXPANEL_PROJECT_ID }}
|
||||
${{ secrets.MIXPANEL_SERVICE_ACCOUNT_USERNAME }}
|
||||
${{ secrets.MIXPANEL_SERVICE_ACCOUNT_SECRET }}
|
||||
|
||||
15
.gitignore
vendored
@@ -7,6 +7,15 @@
|
||||
/crates/collab/static/styles.css
|
||||
/vendor/bin
|
||||
/assets/themes/*.json
|
||||
/assets/themes/internal/*.json
|
||||
/assets/themes/experiments/*.json
|
||||
**/venv
|
||||
/assets/themes/Internal/*.json
|
||||
/assets/themes/Experiments/*.json
|
||||
**/venv
|
||||
.build
|
||||
Packages
|
||||
*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
**/*.db
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "crates/live_kit_server/protocol"]
|
||||
path = crates/live_kit_server/protocol
|
||||
url = https://github.com/livekit/protocol
|
||||
1527
Cargo.lock
generated
66
Cargo.toml
@@ -1,5 +1,64 @@
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
members = [
|
||||
"crates/activity_indicator",
|
||||
"crates/assets",
|
||||
"crates/auto_update",
|
||||
"crates/breadcrumbs",
|
||||
"crates/call",
|
||||
"crates/cli",
|
||||
"crates/client",
|
||||
"crates/clock",
|
||||
"crates/collab",
|
||||
"crates/collab_ui",
|
||||
"crates/collections",
|
||||
"crates/command_palette",
|
||||
"crates/context_menu",
|
||||
"crates/db",
|
||||
"crates/diagnostics",
|
||||
"crates/drag_and_drop",
|
||||
"crates/editor",
|
||||
"crates/file_finder",
|
||||
"crates/fs",
|
||||
"crates/fsevent",
|
||||
"crates/fuzzy",
|
||||
"crates/git",
|
||||
"crates/go_to_line",
|
||||
"crates/gpui",
|
||||
"crates/gpui_macros",
|
||||
"crates/journal",
|
||||
"crates/language",
|
||||
"crates/live_kit_client",
|
||||
"crates/live_kit_server",
|
||||
"crates/lsp",
|
||||
"crates/media",
|
||||
"crates/menu",
|
||||
"crates/outline",
|
||||
"crates/picker",
|
||||
"crates/plugin",
|
||||
"crates/plugin_macros",
|
||||
"crates/plugin_runtime",
|
||||
"crates/project",
|
||||
"crates/project_panel",
|
||||
"crates/project_symbols",
|
||||
"crates/recent_projects",
|
||||
"crates/rope",
|
||||
"crates/rpc",
|
||||
"crates/search",
|
||||
"crates/settings",
|
||||
"crates/snippet",
|
||||
"crates/sqlez",
|
||||
"crates/sqlez_macros",
|
||||
"crates/sum_tree",
|
||||
"crates/terminal",
|
||||
"crates/text",
|
||||
"crates/theme",
|
||||
"crates/theme_selector",
|
||||
"crates/theme_testbench",
|
||||
"crates/util",
|
||||
"crates/vim",
|
||||
"crates/workspace",
|
||||
"crates/zed",
|
||||
]
|
||||
default-members = ["crates/zed"]
|
||||
resolver = "2"
|
||||
|
||||
@@ -9,7 +68,7 @@ serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
|
||||
rand = { version = "0.8" }
|
||||
|
||||
[patch.crates-io]
|
||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "366210ae925d7ea0891bc7a0c738f60c77c04d7b" }
|
||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "36b5b6c89e55ad1a502f8b3234bb3e12ec83a5da" }
|
||||
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
||||
|
||||
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
|
||||
@@ -18,8 +77,6 @@ cocoa-foundation = { git = "https://github.com/servo/core-foundation-rs", rev =
|
||||
core-foundation = { git = "https://github.com/servo/core-foundation-rs", rev = "079665882507dd5e2ff77db3de5070c1f6c0fb85" }
|
||||
core-foundation-sys = { git = "https://github.com/servo/core-foundation-rs", rev = "079665882507dd5e2ff77db3de5070c1f6c0fb85" }
|
||||
core-graphics = { git = "https://github.com/servo/core-foundation-rs", rev = "079665882507dd5e2ff77db3de5070c1f6c0fb85" }
|
||||
# TODO - Remove when a new version of RustRocksDB is released
|
||||
rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "39dc822dde743b2a26eb160b660e8fbdab079d49" }
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
@@ -27,3 +84,4 @@ split-debuginfo = "unpacked"
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
|
||||
FROM rust:1.64-bullseye as builder
|
||||
FROM rust:1.65-bullseye as builder
|
||||
WORKDIR app
|
||||
COPY . .
|
||||
|
||||
@@ -19,5 +19,7 @@ FROM debian:bullseye-slim as runtime
|
||||
RUN apt-get update; \
|
||||
apt-get install -y --no-install-recommends libcurl4-openssl-dev ca-certificates
|
||||
WORKDIR app
|
||||
COPY --from=builder /app/collab /app
|
||||
COPY --from=builder /app/collab /app/collab
|
||||
COPY --from=builder /app/crates/collab/migrations /app/migrations
|
||||
ENV MIGRATIONS_PATH=/app/migrations
|
||||
ENTRYPOINT ["/app/collab"]
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
|
||||
FROM rust:1.64-bullseye as builder
|
||||
WORKDIR app
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=./target \
|
||||
cargo install sqlx-cli --root=/app --target-dir=/app/target --version 0.5.7
|
||||
|
||||
FROM debian:bullseye-slim as runtime
|
||||
RUN apt-get update; \
|
||||
apt-get install -y --no-install-recommends libssl1.1
|
||||
WORKDIR app
|
||||
COPY --from=builder /app/bin/sqlx /app
|
||||
COPY ./crates/collab/migrations /app/migrations
|
||||
ENTRYPOINT ["/app/sqlx", "migrate", "run"]
|
||||
5
Procfile
@@ -1,2 +1,3 @@
|
||||
web: cd ../zed.dev && PORT=3000 npx next dev
|
||||
collab: cd crates/collab && cargo run
|
||||
web: cd ../zed.dev && PORT=3000 npx vercel dev
|
||||
collab: cd crates/collab && cargo run serve
|
||||
livekit: livekit-server --dev
|
||||
98
README.md
@@ -6,36 +6,43 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
|
||||
|
||||
## Development tips
|
||||
|
||||
### Dependencies
|
||||
|
||||
* Install [Postgres.app](https://postgresapp.com) and start it.
|
||||
* Install the `LiveKit` server and the `foreman` process supervisor:
|
||||
|
||||
```
|
||||
brew install livekit
|
||||
brew install foreman
|
||||
```
|
||||
|
||||
* Ensure the Zed.dev website is checked out in a sibling directory:
|
||||
|
||||
```
|
||||
cd ..
|
||||
git clone https://github.com/zed-industries/zed.dev
|
||||
```
|
||||
|
||||
* Set up a local `zed` database and seed it with some initial users:
|
||||
|
||||
```
|
||||
script/bootstrap
|
||||
```
|
||||
|
||||
### Testing against locally-running servers
|
||||
|
||||
Make sure you have `zed.dev` cloned as a sibling to this repo.
|
||||
Start the web and collab servers:
|
||||
|
||||
```
|
||||
cd ..
|
||||
git clone https://github.com/zed-industries/zed.dev
|
||||
```
|
||||
|
||||
Make sure your local database is created, migrated, and seeded with initial data. Install [Postgres](https://postgresapp.com), then from the `zed` repository root, run:
|
||||
|
||||
```
|
||||
script/sqlx database create
|
||||
script/sqlx migrate run
|
||||
script/seed-db
|
||||
```
|
||||
|
||||
Run the web frontend and the collaboration server.
|
||||
|
||||
```
|
||||
brew install foreman
|
||||
foreman start
|
||||
```
|
||||
|
||||
If you want to run Zed pointed at the local servers, you can run:
|
||||
|
||||
```
|
||||
script/zed_with_local_servers
|
||||
script/zed-with-local-servers
|
||||
# or...
|
||||
script/zed_with_local_servers --release
|
||||
script/zed-with-local-servers --release
|
||||
```
|
||||
|
||||
### Dump element JSON
|
||||
@@ -76,56 +83,3 @@ rustup target add wasm32-wasi
|
||||
```
|
||||
|
||||
Plugins can be found in the `plugins` folder in the root. For more information about how plugins work, check the [Plugin Guide](./crates/plugin_runtime/README.md) in `crates/plugin_runtime/README.md`.
|
||||
|
||||
## Roadmap
|
||||
|
||||
We will organize our efforts around the following major milestones. We'll create tracking issues for each of these milestones to detail the individual tasks that comprise them.
|
||||
|
||||
### Minimal text editor
|
||||
|
||||
[Tracking issue](https://github.com/zed-industries/zed/issues/2)
|
||||
|
||||
Ship a minimal text editor to investors and other insiders. It should be extremely fast and stable, but all it can do is open, edit, and save text files, making it potentially useful for basic editing but not for real coding.
|
||||
|
||||
Establish basic infrastructure for building the app bundle and uploading an artifact. Once this is released, we should regularly distribute updates as features land.
|
||||
|
||||
### Collaborative code editor for internal use
|
||||
|
||||
[Tracking issue](https://github.com/zed-industries/zed/issues/6)
|
||||
|
||||
Turn the minimal text editor into a collaborative _code_ editor. This will include the minimal features that the Zed team needs to collaborate in Zed to build Zed without net loss in developer productivity. This includes productivity-critical features such as:
|
||||
|
||||
- Syntax highlighting and syntax-aware editing and navigation
|
||||
- The ability to see and edit non-local working copies of a repository
|
||||
- Language server support for Rust code navigation, refactoring, diagnostics, etc.
|
||||
- Project browsing and project-wide search and replace
|
||||
|
||||
We want to tackle collaboration fairly early so that the rest of the design of the product can flow around that assumption. We could probably produce a single-player code editor more quickly, but at the risk of having collaboration feel more "bolted on" when we eventually add it.
|
||||
|
||||
### Private alpha for Rust teams on macOS
|
||||
|
||||
The "minimal" milestones were about getting Zed to a point where the Zed team could use Zed productively to build Zed. What features are required for someone outside the company to use Zed to productively work on another project that is also written in Rust?
|
||||
|
||||
This includes infrastructure like auto-updates, error reporting, and metrics collection. It also includes some amount of polish to make the tool more discoverable for someone that didn't write it, such as a UI for updating settings and key bindings. We may also need to enhance the server to support user authentication and related concerns.
|
||||
|
||||
The initial target audience is like us. A small team working in Rust that's potentially interested in collaborating. As the alpha proceeds, we can work with teams of different sizes.
|
||||
|
||||
### Private beta for Rust teams on macOS
|
||||
|
||||
Once we're getting sufficiently positive feedback from our initial alpha users, we widen the audience by letting people share invites. Now may be a good time to get Zed running on the web, so that it's extremely easy for a Zed user to share a link and be collaborating in seconds. Once someone is using Zed on the Web, we'll let them register for the private beta and download the native binary if they're on macOS.
|
||||
|
||||
### Expand to other languages
|
||||
|
||||
Depending on how the Rust beta is going, focus hard on dominating another niche language such as Elixr or getting a foothold within a niche of a larger language, such as React/Typescript. Alternatively, go wide at this point and add decent support several widely-used languages such as Python, Ruby, Typescript, etc. This would entail taking 1-2 weeks per language and making sure we ship a solid experience based on a publicly-available language server. Each language has slightly different development practices, so we need to make sure Zed's UX meshes well with those practices.
|
||||
|
||||
### Future directions
|
||||
|
||||
Each of these sections could probably broken into multiple milestones, but this part of the roadmap is too far in the future to go into that level of detail at this point.
|
||||
|
||||
#### Expand to other platforms
|
||||
|
||||
Support Linux and Windows. We'll probably want to hire at least one person that prefers to work on each respective platform and have them spearhead the effort to port Zed to that platform. Once they've done so, they can join the general development effort while ensuring the user experience stays good on that platform.
|
||||
|
||||
#### Expand on collaboration
|
||||
|
||||
To start with, we'll focus on synchronous collaboration because that's where we're most differentiated, but there's no reason we have to limit ourselves to that. How can our tool facilitate collaboration generally, whether it's sync or async? What would it take for a team to go 100% Zed and collaborate fully within the tool? If we haven't added it already, basic Git support would be nice.
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1271)">
|
||||
<path d="M10.9007 7.45347L6.60649 11.7477C6.44009 11.9168 6.22001 12 5.99993 12C5.77985 12 5.56031 11.9161 5.39283 11.7484L1.09859 7.45414C0.763098 7.11865 0.763098 6.57516 1.09859 6.23967C1.43407 5.90419 1.97756 5.90419 2.31305 6.23967L5.14108 9.06904V0.834694C5.14108 0.359912 5.52568 0 5.97577 0C6.42587 0 6.85878 0.359912 6.85878 0.834694V9.06891L9.68761 6.24008C10.0231 5.90459 10.5666 5.90459 10.9021 6.24008C11.2376 6.57556 11.2362 7.11771 10.9007 7.4532V7.45347Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1271">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 590 B After Width: | Height: | Size: 734 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.0538 9.45347L8.75952 13.7477C8.59312 13.9168 8.37304 14 8.15296 14C7.93288 14 7.71334 13.9161 7.54586 13.7484L3.25162 9.45414C2.91613 9.11865 2.91613 8.57516 3.25162 8.23967C3.5871 7.90419 4.13059 7.90419 4.46608 8.23967L7.29411 11.069V2.83469C7.29411 2.35991 7.67871 2 8.1288 2C8.5789 2 9.01181 2.35991 9.01181 2.83469V11.0689L11.8406 8.24008C12.1761 7.90459 12.7196 7.90459 13.0551 8.24008C13.3906 8.57556 13.3893 9.11771 13.0538 9.4532V9.45347Z" fill="white"/>
|
||||
<path d="M13.0538 9.45347L8.75952 13.7477C8.59312 13.9168 8.37304 14 8.15296 14C7.93288 14 7.71334 13.9161 7.54586 13.7484L3.25162 9.45414C2.91613 9.11865 2.91613 8.57516 3.25162 8.23967C3.5871 7.90418 4.13059 7.90418 4.46608 8.23967L7.29411 11.069V2.83469C7.29411 2.35991 7.67871 2 8.12881 2C8.5789 2 9.01181 2.35991 9.01181 2.83469V11.0689L11.8406 8.24008C12.1761 7.90459 12.7196 7.90459 13.0551 8.24008C13.3906 8.57556 13.3893 9.11771 13.0538 9.4532V9.45347Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 581 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1354)">
|
||||
<path d="M7.26716 4.96898L4.40433 7.83181C4.2934 7.94453 4.14668 8 3.99996 8C3.85324 8 3.70688 7.94409 3.59523 7.83226L0.732395 4.96943C0.508737 4.74577 0.508737 4.38344 0.732395 4.15978C0.956054 3.93612 1.31838 3.93612 1.54204 4.15978L3.42739 6.04603V0.556463C3.42739 0.239941 3.68379 0 3.98385 0C4.28392 0 4.57252 0.239941 4.57252 0.556463V6.04594L6.45841 4.16005C6.68207 3.93639 7.0444 3.93639 7.26806 4.16005C7.49172 4.38371 7.49082 4.74514 7.26716 4.9688V4.96898Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1354">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 726 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1272)">
|
||||
<path d="M12 6.00001C12 6.47507 11.6403 6.85889 11.1653 6.85889H2.93347L5.7624 9.68781C6.0979 10.0233 6.0979 10.5668 5.7624 10.9023C5.59331 11.0701 5.37322 11.1533 5.15313 11.1533C4.93305 11.1533 4.71349 11.0694 4.54601 10.9017L0.251624 6.60726C-0.0838748 6.27176 -0.0838748 5.72825 0.251624 5.39275L4.54601 1.09837C4.88151 0.762866 5.42502 0.762866 5.76052 1.09837C6.09602 1.43386 6.09602 1.97737 5.76052 2.31287L2.93347 5.14113H11.1653C11.6403 5.14113 12 5.52494 12 6.00001Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1272">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 740 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 8.15327C14 8.62833 13.6403 9.01214 13.1653 9.01214H4.93347L7.7624 11.8411C8.0979 12.1766 8.0979 12.7201 7.7624 13.0556C7.59331 13.2233 7.37322 13.3065 7.15313 13.3065C6.93305 13.3065 6.71349 13.2227 6.54601 13.0549L2.25162 8.76052C1.91613 8.42502 1.91613 7.88151 2.25162 7.54601L6.54601 3.25162C6.88151 2.91613 7.42502 2.91613 7.76052 3.25162C8.09602 3.58712 8.09602 4.13063 7.76052 4.46613L4.93347 7.29439H13.1653C13.6403 7.29439 14 7.6782 14 8.15327Z" fill="white"/>
|
||||
<path d="M14 8.15327C14 8.62833 13.6403 9.01215 13.1653 9.01215H4.93347L7.7624 11.8411C8.0979 12.1766 8.0979 12.7201 7.7624 13.0556C7.59331 13.2233 7.37322 13.3065 7.15313 13.3065C6.93305 13.3065 6.71349 13.2227 6.54601 13.0549L2.25162 8.76052C1.91613 8.42502 1.91613 7.88151 2.25162 7.54601L6.54601 3.25162C6.88151 2.91613 7.42502 2.91613 7.76052 3.25162C8.09602 3.58712 8.09602 4.13063 7.76052 4.46613L4.93347 7.29439H13.1653C13.6403 7.29439 14 7.6782 14 8.15327Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 585 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1355)">
|
||||
<path d="M8 4.00003C8 4.31674 7.76023 4.57261 7.44352 4.57261H1.95565L3.8416 6.45856C4.06527 6.68223 4.06527 7.04457 3.8416 7.26823C3.72887 7.38007 3.58215 7.43554 3.43542 7.43554C3.2887 7.43554 3.14233 7.37962 3.03068 7.26779L0.16775 4.40486C-0.0559165 4.1812 -0.0559165 3.81886 0.16775 3.59519L3.03068 0.732264C3.25434 0.508598 3.61668 0.508598 3.84035 0.732264C4.06401 0.95593 4.06401 1.31827 3.84035 1.54194L1.95565 3.42744H7.44352C7.76023 3.42744 8 3.68331 8 4.00003Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1355">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 588 B After Width: | Height: | Size: 730 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1273)">
|
||||
<path d="M11.749 6.60576L7.46298 10.8917C7.2969 11.0605 7.07724 11.1436 6.85758 11.1436C6.63793 11.1436 6.41881 11.0598 6.25165 10.8924C5.91681 10.5576 5.91681 10.0151 6.25165 9.68029L9.07558 6.85756H0.857198C0.383864 6.85756 0 6.4745 0 6.00036C0 5.52623 0.383596 5.14317 0.85693 5.14317H9.07531L6.25192 2.31977C5.91708 1.98493 5.91708 1.44248 6.25192 1.10764C6.58676 0.772796 7.12921 0.772796 7.46405 1.10764L11.75 5.39363C12.0835 5.72981 12.0835 6.27092 11.7487 6.60576H11.749Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1273">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 599 B After Width: | Height: | Size: 743 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1356)">
|
||||
<path d="M7.83265 4.40382L4.97532 7.26115C4.8646 7.37365 4.71816 7.42901 4.57172 7.42901C4.42528 7.42901 4.2792 7.37321 4.16777 7.26159C3.94454 7.03836 3.94454 6.67673 4.16777 6.4535L6.05039 4.57169H0.571465C0.255909 4.57169 0 4.31631 0 4.00022C0 3.68413 0.255731 3.42876 0.571287 3.42876H6.05021L4.16795 1.54649C3.94472 1.32326 3.94472 0.961634 4.16795 0.738405C4.39117 0.515177 4.75281 0.515177 4.97603 0.738405L7.83336 3.59573C8.0557 3.81985 8.0557 4.18059 7.83247 4.40382H7.83265Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1356">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 742 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1274)">
|
||||
<path d="M10.9009 5.7604C10.7345 5.92948 10.5144 6.01268 10.2943 6.01268C10.0742 6.01268 9.85471 5.92881 9.68724 5.76107L6.85903 2.93408V11.1653C6.85903 11.6401 6.47444 12 6.02437 12C5.57429 12 5.14139 11.6404 5.14139 11.1653V2.93408L2.31346 5.76013C1.97798 6.09561 1.43451 6.09561 1.09903 5.76013C0.763558 5.42466 0.763558 4.88119 1.09903 4.54571L5.39314 0.251607C5.72861 -0.0838692 6.27209 -0.0838692 6.60756 0.251607L10.9017 4.54571C11.2363 4.88253 11.2363 5.42466 10.9009 5.76013V5.7604Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1274">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 755 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1357)">
|
||||
<path d="M7.26724 3.84027C7.15631 3.95299 7.0096 4.00845 6.86288 4.00845C6.71617 4.00845 6.56981 3.95254 6.45816 3.84072L4.57269 1.95605V7.44356C4.57269 7.76007 4.3163 8 4.01625 8C3.7162 8 3.4276 7.76025 3.4276 7.44356V1.95605L1.54231 3.84009C1.31866 4.06374 0.956346 4.06374 0.732695 3.84009C0.509044 3.61644 0.509044 3.25412 0.732695 3.03047L3.59543 0.167738C3.81908 -0.0559128 4.1814 -0.0559128 4.40505 0.167738L7.26778 3.03047C7.49089 3.25502 7.49089 3.61644 7.26724 3.84009V3.84027Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1357">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 745 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.17314 8L5.89399 3.29329L6.20495 7.94346L8 6.14841L7.67491 0.339222L1.85159 0L0.0565371 1.79505L4.70671 2.12014L0 6.82685L1.17314 8Z" fill="white"/>
|
||||
<path d="M6 2H6.5C6.5 1.86739 6.44732 1.74021 6.35355 1.64645C6.25979 1.55268 6.13261 1.5 6 1.5V2ZM2 1.5C1.72386 1.5 1.5 1.72386 1.5 2C1.5 2.27614 1.72386 2.5 2 2.5L2 1.5ZM5.5 6C5.5 6.27614 5.72386 6.5 6 6.5C6.27614 6.5 6.5 6.27614 6.5 6H5.5ZM1.64645 5.64645C1.45118 5.84171 1.45118 6.15829 1.64645 6.35355C1.84171 6.54882 2.15829 6.54882 2.35355 6.35355L1.64645 5.64645ZM6 1.5H2L2 2.5H6V1.5ZM5.5 2V6H6.5V2H5.5ZM5.64645 1.64645L1.64645 5.64645L2.35355 6.35355L6.35355 2.35355L5.64645 1.64645Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 608 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.14029 5.25005H9.75348C10.0652 5.25005 10.3464 5.44457 10.4355 5.73518C10.5644 6.02814 10.4824 6.3586 10.248 6.56484L4.24822 11.8146C3.98338 12.0443 3.59598 12.0631 3.31286 11.8568C3.02951 11.6506 2.92639 11.2756 3.06443 10.9545L4.86694 6.74999H2.23267C1.94135 6.74999 1.66152 6.55546 1.5516 6.26485C1.44171 5.97189 1.52465 5.64144 1.75986 5.43519L7.75893 0.185629C8.02376 -0.0449871 8.41046 -0.0625645 8.69405 0.143209C8.97763 0.349123 9.08075 0.723265 8.94248 1.04542L7.1402 5.24972L7.14029 5.25005Z" fill="white"/>
|
||||
<path d="M7.01442 5.33285H9.33914C9.61643 5.33285 9.86663 5.5059 9.94586 5.76443C10.0605 6.02505 9.98756 6.31903 9.77906 6.50251L4.4416 11.1728C4.206 11.3771 3.86136 11.3938 3.6095 11.2103C3.35743 11.0268 3.26569 10.6932 3.38849 10.4076L4.99202 6.66722H2.64855C2.38939 6.66722 2.14044 6.49417 2.04266 6.23563C1.9449 5.97501 2.01868 5.68104 2.22793 5.49756L7.56476 0.827491C7.80036 0.622333 8.14438 0.606695 8.39666 0.789754C8.64894 0.972937 8.74067 1.30578 8.61766 1.59237L7.01434 5.33256L7.01442 5.33285Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 625 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.76019 3.50003H6.50231C6.71012 3.50003 6.89761 3.62971 6.95698 3.82346C7.04292 4.01876 6.98823 4.23906 6.83199 4.37656L2.83214 7.87643C2.65558 8.02954 2.39731 8.04204 2.20857 7.90455C2.01967 7.76705 1.95092 7.51706 2.04295 7.30301L3.24462 4.49999H1.48844C1.29423 4.49999 1.10767 4.37031 1.0344 4.17657C0.961132 3.98126 1.01643 3.76096 1.17323 3.62346L5.17261 0.123753C5.34917 -0.0299914 5.60697 -0.0417097 5.79603 0.0954726C5.98508 0.232749 6.05383 0.482177 5.96164 0.69695L4.76013 3.49981L4.76019 3.50003Z" fill="white"/>
|
||||
<path d="M4.76019 3.50003H6.50231C6.71012 3.50003 6.89761 3.62971 6.95698 3.82346C7.04292 4.01876 6.98823 4.23906 6.83199 4.37656L2.83214 7.87643C2.65558 8.02954 2.39731 8.04204 2.20857 7.90455C2.01967 7.76705 1.95092 7.51706 2.04295 7.30301L3.24462 4.49999H1.48844C1.29423 4.49999 1.10767 4.37031 1.0344 4.17657C0.961132 3.98126 1.01643 3.76096 1.17323 3.62346L5.17261 0.123753C5.34917 -0.0299914 5.60697 -0.0417097 5.79603 0.0954726C5.98508 0.232749 6.05383 0.482177 5.96165 0.69695L4.76013 3.49981L4.76019 3.50003Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 633 B After Width: | Height: | Size: 633 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.00788 3.83701L7.62903 0.754503C7.87479 0.546451 8.23364 0.530593 8.4968 0.716233C8.75996 0.901999 8.85565 1.23953 8.72734 1.53017L7.05487 5.3231H9.47984C9.7691 5.3231 10.0301 5.49859 10.1127 5.76077C10.2323 6.02506 10.1562 6.32318 9.93874 6.50925L8.79258 7.48396L11.7998 9.86631C12.026 10.0397 12.0673 10.3589 11.889 10.5788C11.7106 10.7987 11.3822 10.8389 11.156 10.6655L0.200158 2.51988C-0.0268974 2.34693 -0.0666975 2.02808 0.111206 1.80735C0.28911 1.58661 0.616862 1.54792 0.843918 1.72108L4.00788 3.83701ZM2.06138 5.49043L2.40718 5.17751L4.34064 6.67658H2.52027C2.23102 6.67658 1.97003 6.50109 1.86782 6.23891C1.76777 5.97461 1.84389 5.67649 2.06138 5.49043ZM3.27278 10.4697L4.79301 7.02333L7.18318 8.83533L4.37108 11.2457C4.12532 11.4529 3.76647 11.4698 3.50331 11.2837C3.24015 11.0977 3.14446 10.7594 3.27278 10.4697Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1336)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.01443 5.33285H9.33914C9.61644 5.33285 9.86663 5.5059 9.94586 5.76443C10.0605 6.02505 9.98756 6.31903 9.77906 6.50251L9.11489 7.08366L11.4605 8.90799C11.7874 9.16229 11.8463 9.6335 11.592 9.96045C11.3377 10.2874 10.8665 10.3463 10.5395 10.092L1.53955 3.09201C1.21259 2.83771 1.15369 2.3665 1.40799 2.03954C1.66229 1.71258 2.1335 1.65368 2.46046 1.90799L4.50911 3.50138L7.56477 0.827491C7.80037 0.622333 8.14438 0.606695 8.39666 0.789754C8.64894 0.972937 8.74068 1.30578 8.61767 1.59237L7.01434 5.33256L7.01443 5.33285ZM2.88883 4.91923L7.49455 8.50146L4.4416 11.1728C4.206 11.3771 3.86136 11.3938 3.6095 11.2103C3.35743 11.0268 3.26569 10.6932 3.3885 10.4076L4.99203 6.66722H2.64855C2.38939 6.66722 2.14045 6.49416 2.04266 6.23563C1.9449 5.97501 2.01869 5.68104 2.22793 5.49756L2.88883 4.91923Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1336">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 1.1 KiB |
@@ -1,9 +1,9 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1350)">
|
||||
<path d="M2.8102 2.0687L5.08603 0.503002C5.24987 0.364301 5.4891 0.353729 5.66454 0.477489C5.83998 0.601333 5.90377 0.826356 5.81823 1.02011L4.70325 3.54873H6.3199C6.51274 3.54873 6.68673 3.66573 6.74182 3.84051C6.82157 4.01671 6.77082 4.21546 6.62583 4.3395L6.22432 4.68222L8.0048 6.08823C8.15559 6.20381 8.18314 6.41666 8.06425 6.56325C7.94536 6.70985 7.72642 6.73663 7.57563 6.62104L0.271717 1.19061C0.120346 1.07531 0.093813 0.862748 0.212416 0.715589C0.331018 0.56843 0.54952 0.542635 0.70089 0.658079L2.8102 2.0687ZM1.37426 3.66029L1.60479 3.45167L2.89376 4.45105H1.68019C1.48735 4.45105 1.31336 4.33406 1.24521 4.15927C1.17852 3.98308 1.22927 3.78433 1.37426 3.66029ZM2.18186 6.97981L3.19534 4.68222L4.78879 5.89022L2.91406 7.49712C2.75022 7.63526 2.51099 7.64653 2.33555 7.52249C2.16011 7.39845 2.09631 7.17292 2.18186 6.97981Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_702_132)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.67628 3.55525H6.22609C6.41096 3.55525 6.57775 3.67062 6.63057 3.84298C6.70702 4.01672 6.65837 4.21271 6.51937 4.33502L6.07659 4.72246L7.6403 5.93868C7.85828 6.10821 7.89754 6.42235 7.72801 6.64032C7.55847 6.8583 7.24434 6.89756 7.02636 6.72803L1.02636 2.06136C0.808388 1.89183 0.769121 1.57769 0.938656 1.35972C1.10819 1.14174 1.42233 1.10248 1.6403 1.27201L3.00607 2.33428L5.04318 0.551681C5.20024 0.414909 5.42959 0.404484 5.59777 0.526523C5.76596 0.648645 5.82712 0.870539 5.74511 1.0616L4.67622 3.55506L4.67628 3.55525ZM3.25023 4.62627L4.80474 5.83533L2.96106 7.44854C2.804 7.58476 2.57424 7.59588 2.40633 7.47356C2.23828 7.35125 2.17713 7.12885 2.25899 6.93843L3.25023 4.62627ZM1.73426 3.44719L3.01695 4.44483H1.7657C1.59292 4.44483 1.42696 4.32946 1.36177 4.15711C1.2966 3.98336 1.34579 3.78738 1.48528 3.66506L1.73426 3.44719Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1350">
|
||||
<clipPath id="clip0_702_132">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.78057 2.96296L4.59642 6.16966C4.41855 6.32749 4.21312 6.40515 4.00769 6.40515C3.80226 6.40515 3.59733 6.32686 3.44075 6.17028L0.256599 2.96358C0.00482339 2.73498 -0.06382 2.38913 0.0601891 2.09088C0.184298 1.79276 0.47686 1.59485 0.800787 1.59485H7.19164C7.51582 1.59485 7.80843 1.78976 7.93269 2.08963C8.05695 2.38926 8.01085 2.73473 7.78037 2.96271L7.78057 2.96296Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1359)">
|
||||
<path d="M6.36286 3.35324L4.37276 5.35315C4.26159 5.45158 4.1332 5.50002 4.0048 5.50002C3.87641 5.50002 3.74833 5.45119 3.65047 5.35354L1.66037 3.35363C1.50301 3.21106 1.46011 2.99537 1.53762 2.80936C1.61519 2.62343 1.79804 2.5 2.00049 2.5H5.99478C6.19739 2.5 6.38027 2.62156 6.45793 2.80858C6.53559 2.99545 6.50678 3.2109 6.36273 3.35309L6.36286 3.35324Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1359">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 613 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.03717 7.78065L1.83086 4.59689C1.67355 4.41904 1.5954 4.21363 1.5954 3.98568C1.5954 3.75774 1.67368 3.57538 1.83011 3.41882L5.03641 0.235058C5.26562 0.00560697 5.61029 -0.0630281 5.91013 0.0609657C6.20997 0.18496 6.40461 0.478537 6.40461 0.801672V7.19174C6.40461 7.51588 6.20947 7.80845 5.90988 7.9327C5.61004 8.05694 5.26486 8.01085 5.03692 7.7804L5.03717 7.78065Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1360)">
|
||||
<path d="M4.64677 6.36286L2.64687 4.37276C2.54843 4.26159 2.5 4.1332 2.5 4.0048C2.5 3.87641 2.54882 3.74833 2.64648 3.65047L4.64638 1.66037C4.78895 1.50301 5.00465 1.46011 5.19065 1.53762C5.37658 1.61519 5.50002 1.79804 5.50002 2.00049L5.50002 5.99478C5.50002 6.19739 5.37846 6.38027 5.19144 6.45793C5.00457 6.53559 4.78911 6.50678 4.64693 6.36273L4.64677 6.36286Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1360">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 622 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.95931 0.236879L6.17709 3.43204C6.33546 3.61052 6.4134 3.81666 6.4134 4.00018C6.4134 4.18369 6.33484 4.41195 6.17772 4.56907L2.95993 7.76423C2.72966 7.99425 2.384 8.06338 2.08309 7.93869C1.78217 7.81401 1.58659 7.56463 1.58659 7.21771V0.804768C1.58659 0.47947 1.78217 0.185847 2.08309 0.0611582C2.38375 -0.0635309 2.73042 0.0061038 2.95918 0.236628L2.95931 0.236879Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1361)">
|
||||
<path d="M3.35323 1.63714L5.35313 3.62724C5.45157 3.73841 5.5 3.8668 5.5 3.9952C5.5 4.12359 5.45118 4.25167 5.35352 4.34953L3.35362 6.33963C3.21105 6.49699 2.99535 6.53989 2.80935 6.46238C2.62342 6.38481 2.49998 6.20196 2.49998 5.99951L2.49998 2.00522C2.49998 1.80261 2.62154 1.61973 2.80856 1.54207C2.99543 1.46441 3.21089 1.49322 3.35307 1.63727L3.35323 1.63714Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1361">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 493 B After Width: | Height: | Size: 622 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.235788 5.03793L3.42201 1.82915C3.60024 1.67122 3.80581 1.59351 4.01137 1.59351C4.21693 1.59351 4.42199 1.67185 4.57867 1.82852L7.76489 5.0373C7.99427 5.26668 8.06321 5.61163 7.93887 5.9117C7.81453 6.21177 7.54328 6.40655 7.19734 6.40655H0.802589C0.478202 6.40655 0.1854 6.21127 0.0610601 5.91145C-0.0632802 5.61137 0.00590919 5.26593 0.235538 5.03781L0.235788 5.03793Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1362)">
|
||||
<path d="M1.63714 4.64676L3.62724 2.64685C3.73841 2.54842 3.8668 2.49998 3.9952 2.49998C4.12359 2.49998 4.25167 2.54881 4.34953 2.64646L6.33963 4.64637C6.49699 4.78894 6.53989 5.00463 6.46238 5.19064C6.38481 5.37657 6.20196 5.5 5.99951 5.5L2.00522 5.5C1.80261 5.5 1.61973 5.37844 1.54207 5.19142C1.46441 5.00455 1.49322 4.7891 1.63727 4.64691L1.63714 4.64676Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1362">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 617 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.28561 8C2.13936 8 1.99311 7.9442 1.88168 7.83259C1.65846 7.60937 1.65846 7.24776 1.88168 7.02454L4.90707 3.99996L1.88168 0.975457C1.65846 0.75224 1.65846 0.390629 1.88168 0.167413C2.10489 -0.0558042 2.4665 -0.0558042 2.68972 0.167413L6.11833 3.59602C6.34155 3.81924 6.34155 4.18085 6.11833 4.40407L2.68972 7.83268C2.57847 7.94464 2.43204 8 2.28561 8Z" fill="white"/>
|
||||
<path d="M2.28561 8C2.13936 8 1.99311 7.9442 1.88168 7.83259C1.65846 7.60937 1.65846 7.24776 1.88168 7.02454L4.90707 3.99996L1.88168 0.975457C1.65846 0.752241 1.65846 0.390629 1.88168 0.167413C2.10489 -0.0558042 2.4665 -0.0558042 2.68972 0.167413L6.11833 3.59602C6.34155 3.81924 6.34155 4.18085 6.11833 4.40407L2.68972 7.83268C2.57847 7.94464 2.43204 8 2.28561 8Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 478 B After Width: | Height: | Size: 479 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1285)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12ZM9.53033 4.28033L8.46967 3.21967L4.875 6.81434L3.53033 5.46967L2.46967 6.53033L4.875 8.93566L9.53033 4.28033Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1285">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 530 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1370)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 8C6.20914 8 8 6.20914 8 4C8 1.79086 6.20914 0 4 0C1.79086 0 0 1.79086 0 4C0 6.20914 1.79086 8 4 8ZM6.35355 2.85355L5.64645 2.14645L3.25 4.54289L2.35355 3.64645L1.64645 4.35355L3.25 5.95711L6.35355 2.85355Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1370">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 373 B After Width: | Height: | Size: 515 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1294)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 9.31371 9.31371 12 6 12C2.68629 12 0 9.31371 0 6C0 2.68629 2.68629 0 6 0C9.31371 0 12 2.68629 12 6ZM5.25 6H4.5V4.5H6.75V8.25H7.5V9.75H5.25V6ZM6.75 3.75V2.25H5.25V3.75H6.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1294">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 494 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1372)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4C8 6.20914 6.20914 8 4 8C1.79086 8 0 6.20914 0 4C0 1.79086 1.79086 0 4 0C6.20914 0 8 1.79086 8 4ZM3.5 4H3V3H4.5V5.5H5V6.5H3.5V4ZM4.5 2.5V1.5H3.5V2.5H4.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1372">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 464 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1287)">
|
||||
<path d="M6 0C2.68594 0 0 2.68594 0 6C0 9.31406 2.68594 12 6 12C9.31406 12 12 9.31406 12 6C12 2.68594 9.31406 0 6 0ZM8.97187 5.76797C8.91328 5.90859 8.77734 6 8.625 6H7.125V8.25C7.125 8.66414 6.78914 9 6.375 9H5.625C5.21086 9 4.875 8.66414 4.875 8.25V6H3.375C3.22266 6 3.08672 5.90859 3.02812 5.76797C2.96953 5.62734 3.00234 5.46797 3.11016 5.36016L5.73516 2.73516C5.88141 2.58867 6.11906 2.58867 6.26531 2.73516L8.89031 5.36016C8.99766 5.46797 9.03047 5.62734 8.97187 5.76797Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1287">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 741 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1376)">
|
||||
<path d="M4 0C1.79063 0 0 1.79063 0 4C0 6.20937 1.79063 8 4 8C6.20937 8 8 6.20937 8 4C8 1.79063 6.20937 0 4 0ZM5.98125 3.84531C5.94219 3.93906 5.85156 4 5.75 4H4.75V5.5C4.75 5.77609 4.52609 6 4.25 6H3.75C3.47391 6 3.25 5.77609 3.25 5.5V4H2.25C2.14844 4 2.05781 3.93906 2.01875 3.84531C1.97969 3.75156 2.00156 3.64531 2.07344 3.57344L3.82344 1.82344C3.92094 1.72578 4.07938 1.72578 4.17688 1.82344L5.92688 3.57344C5.99844 3.64531 6.02031 3.75156 5.98125 3.84531Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1376">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 719 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1292)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12ZM3.21967 4.28033L4.93934 6L3.21967 7.71967L4.28033 8.78033L6 7.06066L7.71967 8.78033L8.78033 7.71967L7.06066 6L8.78033 4.28033L7.71967 3.21967L6 4.93934L4.28033 3.21967L3.21967 4.28033Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1292">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 606 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1365)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 8C6.20914 8 8 6.20914 8 4C8 1.79086 6.20914 0 4 0C1.79086 0 0 1.79086 0 4C0 6.20914 1.79086 8 4 8ZM2.14645 2.85355L3.29289 4L2.14645 5.14645L2.85355 5.85355L4 4.70711L5.14645 5.85355L5.85355 5.14645L4.70711 4L5.85355 2.85355L5.14645 2.14645L4 3.29289L2.85355 2.14645L2.14645 2.85355Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1365">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 593 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.59892 2.76222C3.14641 2.17067 3.93013 1.80018 4.80011 1.80018C5.91195 1.80018 6.8813 2.40485 7.40066 3.30389C7.68565 3.09577 8.03064 3.00015 8.4 3.00015C9.39372 3.00015 10.1999 3.7895 10.1999 4.80009C10.1999 5.02884 10.1568 5.24633 10.08 5.44882C11.1749 5.67007 11.9999 6.63941 11.9999 7.8C11.9999 8.48623 11.7112 9.10497 11.233 9.52683L11.8274 9.99557C12.0224 10.1493 12.058 10.4324 11.9043 10.6274C11.7505 10.8224 11.4674 10.858 11.2724 10.7043L0.172556 2.00436C-0.0231882 1.85099 -0.0574997 1.56825 0.0958708 1.37251C0.249241 1.17676 0.531796 1.14245 0.727671 1.29582L2.59868 2.76184L2.59892 2.76222ZM1.82213 4.43635L9.13873 10.1999H2.70017C1.20903 10.1999 0.000248596 8.99059 0.000248596 7.50001C0.000248596 6.32255 0.753414 5.32133 1.80395 4.95196C1.80151 4.90134 1.8002 4.85072 1.8002 4.80009C1.8002 4.67635 1.8077 4.55448 1.82213 4.43635Z" fill="white"/>
|
||||
<path d="M2.59892 2.76222C3.14641 2.17067 3.93013 1.80018 4.80011 1.80018C5.91195 1.80018 6.8813 2.40485 7.40066 3.30389C7.68565 3.09577 8.03064 3.00015 8.4 3.00015C9.39372 3.00015 10.1999 3.7895 10.1999 4.80009C10.1999 5.02884 10.1568 5.24633 10.08 5.44882C11.1749 5.67007 11.9999 6.63941 11.9999 7.80001C11.9999 8.48623 11.7112 9.10497 11.233 9.52683L11.8274 9.99557C12.0224 10.1493 12.058 10.4324 11.9043 10.6274C11.7505 10.8224 11.4674 10.858 11.2724 10.7043L0.172556 2.00436C-0.0231882 1.85099 -0.0574997 1.56825 0.0958708 1.37251C0.249241 1.17676 0.531795 1.14245 0.727671 1.29582L2.59868 2.76184L2.59892 2.76222ZM1.82213 4.43635L9.13873 10.1999H2.70017C1.20903 10.1999 0.000248596 8.99059 0.000248596 7.50001C0.000248596 6.32255 0.753414 5.32133 1.80395 4.95196C1.80151 4.90134 1.8002 4.85072 1.8002 4.80009C1.8002 4.67635 1.8077 4.55448 1.82213 4.43635Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 981 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.73261 1.8415C2.0976 1.44713 2.62009 1.20014 3.20007 1.20014C3.9413 1.20014 4.58753 1.60325 4.93377 2.20261C5.12376 2.06387 5.35376 2.00012 5.6 2.00012C6.26248 2.00012 6.79996 2.52635 6.79996 3.20008C6.79996 3.35258 6.77122 3.49757 6.71997 3.63257C7.44995 3.78007 7.99993 4.4263 7.99993 5.20002C7.99993 5.65751 7.80744 6.07 7.48869 6.35124L7.88493 6.66373C8.01493 6.76623 8.03868 6.95497 7.93618 7.08497C7.83368 7.21496 7.64494 7.23871 7.51494 7.13622L0.115037 1.33626C-0.0154588 1.23402 -0.0383331 1.04552 0.0639139 0.915025C0.166161 0.784529 0.35453 0.761655 0.485114 0.863902L1.73245 1.84125L1.73261 1.8415ZM1.21475 2.95759L6.09249 6.79998H1.80011C0.806017 6.79998 0.000165731 5.99375 0.000165731 5.00003C0.000165731 4.21505 0.502276 3.54757 1.20263 3.30133C1.20101 3.26758 1.20013 3.23383 1.20013 3.20008C1.20013 3.11759 1.20513 3.03634 1.21475 2.95759Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1352)">
|
||||
<path d="M1.73261 1.8415C2.0976 1.44713 2.62009 1.20014 3.20007 1.20014C3.9413 1.20014 4.58753 1.60325 4.93377 2.20261C5.12376 2.06387 5.35376 2.00012 5.6 2.00012C6.26248 2.00012 6.79997 2.52635 6.79997 3.20008C6.79997 3.35258 6.77122 3.49757 6.71997 3.63257C7.44995 3.78007 7.99993 4.4263 7.99993 5.20002C7.99993 5.65751 7.80744 6.07 7.48869 6.35124L7.88493 6.66373C8.01493 6.76623 8.03868 6.95497 7.93618 7.08497C7.83368 7.21496 7.64494 7.23871 7.51494 7.13622L0.115037 1.33626C-0.0154588 1.23402 -0.0383331 1.04552 0.0639139 0.915025C0.166161 0.784529 0.35453 0.761655 0.485114 0.863902L1.73245 1.84125L1.73261 1.8415ZM1.21475 2.95759L6.09249 6.79998H1.80011C0.806017 6.79998 0.000165731 5.99375 0.000165731 5.00003C0.000165731 4.21505 0.502276 3.54757 1.20263 3.30133C1.20101 3.26758 1.20013 3.23383 1.20013 3.20008C1.20013 3.11759 1.20513 3.03634 1.21475 2.95759Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1352">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 1.1 KiB |
3
assets/icons/disable_screen_sharing_12.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 0.666656H1C0.447917 0.666656 0 1.11457 0 1.66666V8.33332C0 8.88541 0.447917 9.33332 1 9.33332H5L4.66667 10.3333H3.16667C2.89167 10.3333 2.66667 10.5583 2.66667 10.8333C2.66667 11.1083 2.89167 11.3333 3.16667 11.3333H8.83333C9.10938 11.3333 9.33333 11.1094 9.33333 10.8333C9.33333 10.5573 9.10938 10.3333 8.83333 10.3333H7.33333L7 9.33332H11C11.5521 9.33332 12 8.88541 12 8.33332V1.66666C12 1.11457 11.5521 0.666656 11 0.666656ZM10.6667 7.99999H1.33333V1.99999H10.6667V7.99999Z" fill="#979DB4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 611 B |
@@ -1,11 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_616_81)">
|
||||
<path opacity="0.6" d="M11 11L11 7L1 7L1 11L11 11Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1818 1C1.51237 1 1.04544 1.48778 1.04544 2V10C1.04544 10.5122 1.51237 11 2.1818 11H9.81817C10.4876 11 10.9545 10.5122 10.9545 10V2C10.9545 1.48778 10.4876 1 9.81817 1H2.1818ZM0.0454407 2C0.0454407 0.855367 1.04377 0 2.1818 0H9.81817C10.9562 0 11.9545 0.855367 11.9545 2V10C11.9545 11.1446 10.9562 12 9.81817 12H2.1818C1.04377 12 0.0454407 11.1446 0.0454407 10V2Z" fill="white"/>
|
||||
<path d="M11 8L11 7L1 7L1 8L11 8Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_702_214)">
|
||||
<rect x="0.5" y="0.5" width="11" height="11" rx="1" stroke="white" stroke-opacity="0.4"/>
|
||||
<path d="M12 7.5L12 10.5C12 11.3284 11.3284 12 10.5 12L1.5 12C0.671573 12 -1.67346e-07 11.3284 -1.31134e-07 10.5L0 7.5L12 7.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_616_81">
|
||||
<clipPath id="clip0_702_214">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 478 B |
@@ -1,7 +1,7 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_610_112)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1H1V7H7V1ZM1 0C0.447715 0 4.37114e-08 0.447715 4.37114e-08 1V7C4.37114e-08 7.55228 0.447715 8 1 8H7C7.55228 8 8 7.55228 8 7V1C8 0.447715 7.55228 0 7 0H1Z" fill="white"/>
|
||||
<path d="M7 4C7.55228 4 8 4.44772 8 5V7C8 7.55228 7.55228 8 7 8H1C0.447715 8 4.37114e-08 7.55228 4.37114e-08 7L0 5C2.41411e-08 4.44771 0.447715 4 1 4L7 4Z" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="7" height="7" rx="0.5" stroke="white" stroke-opacity="0.4"/>
|
||||
<path d="M8 5L8 7C8 7.55228 7.55228 8 7 8L1 8C0.447715 8 -1.11564e-07 7.55228 -8.74228e-08 7L0 5L8 5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_610_112">
|
||||
|
||||
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 447 B |
@@ -1,11 +0,0 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_616_82)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1818 1C1.51237 1 1.04544 1.48778 1.04544 2V10C1.04544 10.5122 1.51237 11 2.1818 11H9.81817C10.4876 11 10.9545 10.5122 10.9545 10V2C10.9545 1.48778 10.4876 1 9.81817 1H2.1818ZM0.0454407 2C0.0454407 0.855367 1.04377 0 2.1818 0H9.81817C10.9562 0 11.9545 0.855367 11.9545 2V10C11.9545 11.1446 10.9562 12 9.81817 12H2.1818C1.04377 12 0.0454407 11.1446 0.0454407 10V2Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 4C5.5 3.72386 5.72386 3.5 6 3.5H8C8.27614 3.5 8.5 3.72386 8.5 4V6C8.5 6.27614 8.27614 6.5 8 6.5C7.72386 6.5 7.5 6.27614 7.5 6V5.20711L5.20711 7.5H6C6.27614 7.5 6.5 7.72386 6.5 8C6.5 8.27614 6.27614 8.5 6 8.5H4C3.72386 8.5 3.5 8.27614 3.5 8V6C3.5 5.72386 3.72386 5.5 4 5.5C4.27614 5.5 4.5 5.72386 4.5 6V6.79289L6.79289 4.5H6C5.72386 4.5 5.5 4.27614 5.5 4Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_616_82">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,13 +0,0 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_610_113)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1H1V7H7V1ZM1 0C0.447715 0 0 0.447715 0 1V7C0 7.55228 0.447715 8 1 8H7C7.55228 8 8 7.55228 8 7V1C8 0.447715 7.55228 0 7 0H1Z" fill="white"/>
|
||||
<path d="M6 2H3L6 5V2Z" fill="white"/>
|
||||
<path d="M2 6L5 6L2 3L2 6Z" fill="white"/>
|
||||
<path d="M5.5 2.5L2.5 5.5" stroke="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_610_113">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 557 B |
@@ -1,11 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_620_198)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1818 1C1.51237 1 1.04544 1.48778 1.04544 2V10C1.04544 10.5122 1.51237 11 2.1818 11H9.81817C10.4876 11 10.9545 10.5122 10.9545 10V2C10.9545 1.48778 10.4876 1 9.81817 1H2.1818ZM0.0454407 2C0.0454407 0.855367 1.04377 0 2.1818 0H9.81817C10.9562 0 11.9545 0.855367 11.9545 2V10C11.9545 11.1446 10.9562 12 9.81817 12H2.1818C1.04377 12 0.0454407 11.1446 0.0454407 10V2Z" fill="white"/>
|
||||
<rect opacity="0.6" x="4" y="4" width="4" height="4" fill="white"/>
|
||||
<rect x="3.5" y="3.5" width="5" height="5" rx="0.5" stroke="white"/>
|
||||
<g clip-path="url(#clip0_702_213)">
|
||||
<rect x="2" y="3" width="8" height="6" rx="1" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="11" height="11" rx="1" stroke="white" stroke-opacity="0.4"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_620_198">
|
||||
<clipPath id="clip0_702_213">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 813 B After Width: | Height: | Size: 396 B |
@@ -1,6 +1,6 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_620_186)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1H1V7H7V1ZM1 0C0.447715 0 0 0.447715 0 1V7C0 7.55228 0.447715 8 1 8H7C7.55228 8 8 7.55228 8 7V1C8 0.447715 7.55228 0 7 0H1Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1H1V7H7V1ZM1 0C0.447715 0 0 0.447715 0 1V7C0 7.55228 0.447715 8 1 8H7C7.55228 8 8 7.55228 8 7V1C8 0.447715 7.55228 0 7 0H1Z" fill="white" fill-opacity="0.4"/>
|
||||
<rect x="2" y="2" width="4" height="4" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 485 B After Width: | Height: | Size: 504 B |
@@ -1,11 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_616_83)">
|
||||
<path opacity="0.6" d="M11 1H7V11H11V1Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1818 1C1.51237 1 1.04544 1.48778 1.04544 2V10C1.04544 10.5122 1.51237 11 2.1818 11H9.81817C10.4876 11 10.9545 10.5122 10.9545 10V2C10.9545 1.48778 10.4876 1 9.81817 1H2.1818ZM0.0454407 2C0.0454407 0.855367 1.04377 0 2.1818 0H9.81817C10.9562 0 11.9545 0.855367 11.9545 2V10C11.9545 11.1446 10.9562 12 9.81817 12H2.1818C1.04377 12 0.0454407 11.1446 0.0454407 10V2Z" fill="white"/>
|
||||
<path d="M8 1H7V11H8V1Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_702_215)">
|
||||
<rect x="0.5" y="0.5" width="11" height="11" rx="1" stroke="white" stroke-opacity="0.4"/>
|
||||
<path d="M7.5 0H10.5C11.3284 0 12 0.671573 12 1.5V10.5C12 11.3284 11.3284 12 10.5 12H7.5V0Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_616_83">
|
||||
<clipPath id="clip0_702_215">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 443 B |
@@ -1,7 +1,7 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_610_114)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1H1V7H7V1ZM1 0C0.447715 0 0 0.447715 0 1V7C0 7.55228 0.447715 8 1 8H7C7.55228 8 8 7.55228 8 7V1C8 0.447715 7.55228 0 7 0H1Z" fill="white"/>
|
||||
<path d="M4 1C4 0.447715 4.44772 0 5 0H7C7.55228 0 8 0.447715 8 1V7C8 7.55228 7.55228 8 7 8H5C4.44772 8 4 7.55228 4 7V1Z" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="7" height="7" rx="0.5" stroke="white" stroke-opacity="0.4"/>
|
||||
<path d="M5 0H7C7.55228 0 8 0.447715 8 1V7C8 7.55228 7.55228 8 7 8H5V0Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_610_114">
|
||||
|
||||
|
Before Width: | Height: | Size: 568 B After Width: | Height: | Size: 417 B |
3
assets/icons/enable_screen_sharing_12.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.53324 9.90014H7.18324L6.88324 9.00014H7.63305L6.10211 7.80014H1.78324V4.41577L0.583236 3.47452V8.10014C0.583217 8.59702 0.986361 9.00014 1.46636 9.00014H5.04949L4.74949 9.90014H3.43324C3.1848 9.90014 2.98324 10.1017 2.98324 10.3501C2.98324 10.5986 3.1848 10.8001 3.43324 10.8001H8.51637C8.7648 10.8001 8.96637 10.5986 8.96637 10.3501C8.96637 10.1017 8.79762 9.90014 8.53324 9.90014ZM11.8276 9.99577L10.5507 8.99489C11.0234 8.96789 11.3999 8.57939 11.3999 8.09996V2.09995C11.3999 1.60308 10.9968 1.19995 10.4999 1.19995H1.5168C1.28617 1.19995 1.07786 1.28939 0.918674 1.43208L0.727799 1.29595C0.645299 1.23145 0.547423 1.19995 0.450673 1.19995C0.316986 1.19995 0.184611 1.2592 0.0961106 1.37226C-0.057452 1.56801 -0.023327 1.85095 0.172236 2.00414L11.2724 10.7041C11.4693 10.8579 11.7519 10.8226 11.9041 10.6276C12.0581 10.4321 12.0224 10.149 11.8274 9.99521L11.8276 9.99577ZM10.1832 7.80014H9.00968L2.11905 2.40014H10.1816L10.1832 7.80014Z" fill="#93A1A1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1286)">
|
||||
<path d="M11.3333 1.33335H9L8.33333 0.666687H6.66667C6.29958 0.666687 6 0.96627 6 1.33335V4.66669C6 5.03377 6.29958 5.33335 6.66667 5.33335H11.3333C11.7004 5.33335 12 5.03377 12 4.66669V2.00002C12 1.63294 11.7 1.33335 11.3333 1.33335ZM11.3333 7.33335H9L8.33333 6.66669H6.66667C6.29958 6.66669 6 6.96627 6 7.33335V10.6667C6 11.0338 6.29958 11.3334 6.66667 11.3334H11.3333C11.7004 11.3334 12 11.0338 12 10.6667V8.00002C12 7.63335 11.7 7.33335 11.3333 7.33335ZM1.33333 1.00002C1.33333 0.815125 1.185 0.666687 1 0.666687H0.333333C0.148438 0.666687 0 0.81502 0 1.00002V9.33335C0 9.70044 0.299583 10 0.666667 10H5.33333V8.66669H1.33333V4.00002H5.33333V2.66669H1.33333V1.00002Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1286">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 934 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1367)">
|
||||
<path d="M7.55556 0.888902H6L5.55556 0.444458H4.44444C4.19972 0.444458 4 0.64418 4 0.888902V3.11112C4 3.35585 4.19972 3.55557 4.44444 3.55557H7.55556C7.80028 3.55557 8 3.35585 8 3.11112V1.33335C8 1.08862 7.8 0.888902 7.55556 0.888902ZM7.55556 4.8889H6L5.55556 4.44446H4.44444C4.19972 4.44446 4 4.64418 4 4.8889V7.11112C4 7.35585 4.19972 7.55557 4.44444 7.55557H7.55556C7.80028 7.55557 8 7.35585 8 7.11112V5.33335C8 5.0889 7.8 4.8889 7.55556 4.8889ZM0.888889 0.66668C0.888889 0.543416 0.79 0.444458 0.666667 0.444458H0.222222C0.0989583 0.444458 0 0.543347 0 0.66668V6.22224C0 6.46696 0.199722 6.66668 0.444444 6.66668H3.55556V5.77779H0.888889V2.66668H3.55556V1.77779H0.888889V0.66668Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1367">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 799 B After Width: | Height: | Size: 941 B |
@@ -1,6 +1,6 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_614_97)">
|
||||
<path d="M5 7.62515C5 7.77359 4.9125 7.90796 4.77812 7.96734C4.64219 8.02828 4.47031 8.00327 4.37344 7.90328L3.12344 6.77828C3.04531 6.70796 3 6.6064 3 6.50015C3 6.3939 3.04531 6.29234 3.12344 6.22203L4.37344 5.09703C4.47031 4.99703 4.64219 4.97203 4.77812 5.03296C4.9125 5.09234 5 5.22671 5 5.37515V6.00015H5.25C5.80156 6.00015 6.25 5.55171 6.25 5.00015V2.39546C5.80781 2.18921 5.5 1.76265 5.5 1.25015C5.5 0.559838 6.05937 0.000150232 6.75 0.000150232C7.44063 0.000134607 8 0.559838 8 1.25015C8 1.76265 7.69219 2.18921 7.25 2.39546V5.00015C7.25 6.10484 6.35469 7.00015 5.25 7.00015H5V7.62515ZM7.125 1.23609C7.125 1.04296 6.95781 0.861088 6.75 0.861088C6.54219 0.861088 6.375 1.04296 6.375 1.23609C6.375 1.45718 6.54219 1.61109 6.75 1.61109C6.95781 1.61109 7.125 1.45718 7.125 1.23609ZM3 0.37515C3 0.227025 3.0875 0.0928065 3.22187 0.032494C3.35781 -0.0278185 3.51562 -0.00281852 3.62656 0.096244L4.87656 1.22123C4.95469 1.29232 5 1.39373 5 1.49998C5 1.60623 4.95469 1.70779 4.87656 1.7781L3.62656 2.9031C3.51562 3.0031 3.35781 3.0281 3.22187 2.96717C3.0875 2.90779 3 2.77342 3 2.62498V1.99998H2.75C2.19844 1.99998 1.75 2.44842 1.75 2.99998V5.60467C2.19219 5.79685 2.5 6.23748 2.5 6.74998C2.5 7.4406 1.94062 7.99998 1.25 7.99998C0.559687 7.99998 0 7.4406 0 6.74998C0 6.23748 0.308594 5.79685 0.75 5.60467V2.99998C0.75 1.89529 1.64531 0.999978 2.75 0.999978H3V0.374978V0.37515ZM0.875 6.75015C0.875 6.95796 1.04297 7.12515 1.25 7.12515C1.45703 7.12515 1.625 6.95796 1.625 6.75015C1.625 6.54234 1.45703 6.37515 1.25 6.37515C1.04297 6.37515 0.875 6.54234 0.875 6.75015Z" fill="white"/>
|
||||
<path d="M5 7.62515C5 7.77359 4.9125 7.90796 4.77812 7.96734C4.64219 8.02828 4.47031 8.00328 4.37344 7.90328L3.12344 6.77828C3.04531 6.70796 3 6.6064 3 6.50015C3 6.3939 3.04531 6.29234 3.12344 6.22203L4.37344 5.09703C4.47031 4.99703 4.64219 4.97203 4.77812 5.03296C4.9125 5.09234 5 5.22671 5 5.37515V6.00015H5.25C5.80156 6.00015 6.25 5.55171 6.25 5.00015V2.39546C5.80781 2.18921 5.5 1.76265 5.5 1.25015C5.5 0.559838 6.05937 0.000150232 6.75 0.000150232C7.44063 0.000134607 8 0.559838 8 1.25015C8 1.76265 7.69219 2.18921 7.25 2.39546V5.00015C7.25 6.10484 6.35469 7.00015 5.25 7.00015H5V7.62515ZM7.125 1.23609C7.125 1.04296 6.95781 0.861088 6.75 0.861088C6.54219 0.861088 6.375 1.04296 6.375 1.23609C6.375 1.45718 6.54219 1.61109 6.75 1.61109C6.95781 1.61109 7.125 1.45718 7.125 1.23609ZM3 0.37515C3 0.227025 3.0875 0.0928065 3.22187 0.032494C3.35781 -0.0278185 3.51563 -0.00281852 3.62656 0.096244L4.87656 1.22123C4.95469 1.29232 5 1.39373 5 1.49998C5 1.60623 4.95469 1.70779 4.87656 1.7781L3.62656 2.9031C3.51563 3.0031 3.35781 3.0281 3.22187 2.96717C3.0875 2.90779 3 2.77342 3 2.62498V1.99998H2.75C2.19844 1.99998 1.75 2.44842 1.75 2.99998V5.60467C2.19219 5.79685 2.5 6.23748 2.5 6.74998C2.5 7.4406 1.94063 7.99998 1.25 7.99998C0.559687 7.99998 0 7.4406 0 6.74998C0 6.23748 0.308594 5.79685 0.75 5.60467V2.99998C0.75 1.89529 1.64531 0.999978 2.75 0.999978H3V0.374978V0.37515ZM0.875 6.75015C0.875 6.95796 1.04297 7.12515 1.25 7.12515C1.45703 7.12515 1.625 6.95796 1.625 6.75015C1.625 6.54234 1.45703 6.37515 1.25 6.37515C1.04297 6.37515 0.875 6.54234 0.875 6.75015Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_614_97">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.8167 11.0391L8.67607 7.89844C9.35576 7.06641 9.73076 6.01875 9.73076 4.875C9.73076 2.18203 7.54802 0 4.85576 0C2.16349 0 0.00161743 2.18273 0.00161743 4.875C0.00161743 7.56727 2.18412 9.75 4.85552 9.75C5.99904 9.75 7.0481 9.35367 7.87896 8.69438L11.0196 11.835C11.1508 11.9461 11.2961 12 11.4391 12C11.5821 12 11.7269 11.9449 11.8369 11.835C12.0555 11.6154 12.0555 11.2591 11.8165 11.0388L11.8167 11.0391ZM1.12685 4.875C1.12685 2.80734 2.8092 1.125 4.87685 1.125C6.94451 1.125 8.62685 2.80734 8.62685 4.875C8.62685 6.94266 6.94451 8.625 4.87685 8.625C2.8092 8.625 1.12685 6.94219 1.12685 4.875Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1288)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.40624 7.54167C6.77901 8.13568 5.93205 8.5 5 8.5C3.067 8.5 1.5 6.933 1.5 5C1.5 3.067 3.067 1.5 5 1.5C6.933 1.5 8.5 3.067 8.5 5C8.5 5.93205 8.13568 6.77901 7.54167 7.40624C7.51667 7.42558 7.49261 7.44673 7.46967 7.46967C7.44673 7.49261 7.42558 7.51667 7.40624 7.54167ZM7.96544 9.0261C7.13578 9.63821 6.11014 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5C10 6.11014 9.63821 7.13578 9.0261 7.96544L11.5303 10.4697C11.8232 10.7626 11.8232 11.2374 11.5303 11.5303C11.2374 11.8232 10.7626 11.8232 10.4697 11.5303L7.96544 9.0261Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1288">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 874 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.87779 7.35938L5.78404 5.26562C6.23716 4.71094 6.48716 4.0125 6.48716 3.25C6.48716 1.45469 5.03201 0 3.23716 0C1.44232 0 0.00106812 1.45516 0.00106812 3.25C0.00106812 5.04484 1.45607 6.5 3.23701 6.5C3.99935 6.5 4.69873 6.23578 5.25263 5.79625L7.34638 7.89C7.43388 7.96406 7.53076 8 7.62607 8C7.72138 8 7.81794 7.96328 7.89123 7.89C8.03701 7.74359 8.03701 7.50609 7.87763 7.35922L7.87779 7.35938ZM0.751224 3.25C0.751224 1.87156 1.87279 0.75 3.25122 0.75C4.62966 0.75 5.75122 1.87156 5.75122 3.25C5.75122 4.62844 4.62966 5.75 3.25122 5.75C1.87279 5.75 0.751224 4.62813 0.751224 3.25Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1369)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.16734 5.87445C4.62987 6.26778 3.96705 6.5 3.25 6.5C1.45507 6.5 0 5.04493 0 3.25C0 1.45507 1.45507 0 3.25 0C5.04493 0 6.5 1.45507 6.5 3.25C6.5 3.96705 6.26778 4.62987 5.87445 5.16734L7.85355 7.14645C8.04882 7.34171 8.04882 7.65829 7.85355 7.85355C7.65829 8.04882 7.34171 8.04882 7.14645 7.85355L5.16734 5.87445ZM5.5 3.25C5.5 4.49264 4.49264 5.5 3.25 5.5C2.00736 5.5 1 4.49264 1 3.25C1 2.00736 2.00736 1 3.25 1C4.49264 1 5.5 2.00736 5.5 3.25Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1369">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 750 B |
@@ -1,6 +1,6 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_609_69)">
|
||||
<path d="M2.77187 4.52188L0.999998 6.29375V5.50001C0.999998 5.2236 0.776405 5.00001 0.499999 5.00001C0.223593 5.00001 0 5.2236 0 5.50001V7.5C0 7.565 0.013125 7.62985 0.0385155 7.69094C0.0891405 7.81328 0.186484 7.91078 0.308984 7.96141C0.370077 7.9875 0.434921 8 0.499921 8H2.49992C2.77632 8 2.99992 7.77641 2.99992 7.5C2.99992 7.2236 2.77632 7 2.49992 7H1.70773L3.4796 5.22813C3.67492 5.03282 3.67492 4.71641 3.4796 4.5211C3.28429 4.32579 2.9671 4.32657 2.77179 4.52188H2.77187ZM7.96092 0.309078C7.91014 0.186578 7.8128 0.089078 7.6903 0.038453C7.62967 0.0131406 7.56561 0 7.49999 0H5.49999C5.22358 0 4.99999 0.223593 4.99999 0.499999C4.99999 0.776405 5.22358 0.999998 5.49999 0.999998H6.29296L4.52109 2.77187C4.32577 2.96718 4.32577 3.28359 4.52109 3.4789C4.71624 3.67406 5.03265 3.67437 5.22812 3.4789L6.99999 1.70782V2.50001C6.99999 2.77642 7.22358 3.00001 7.49999 3.00001C7.77639 3.00001 7.99998 2.77642 7.99998 2.50001V0.500015C7.99998 0.435015 7.98748 0.370171 7.96092 0.309078Z" fill="white"/>
|
||||
<path d="M2.77188 4.52188L1.00001 6.29375V5.50001C1.00001 5.2236 0.776412 5.00001 0.500007 5.00001C0.223601 5.00001 7.62939e-06 5.2236 7.62939e-06 5.50001V7.5C7.62939e-06 7.565 0.0131326 7.62984 0.0385232 7.69094C0.0891481 7.81328 0.186492 7.91078 0.308991 7.96141C0.370085 7.9875 0.434929 8 0.499929 8H2.49992C2.77633 8 2.99992 7.77641 2.99992 7.5C2.99992 7.2236 2.77633 7 2.49992 7H1.70774L3.47961 5.22813C3.67492 5.03282 3.67492 4.71641 3.47961 4.5211C3.2843 4.32579 2.96711 4.32657 2.7718 4.52188H2.77188ZM7.96093 0.309078C7.91015 0.186578 7.81281 0.089078 7.69031 0.0384531C7.62968 0.0131406 7.56562 0 7.49999 0H5.5C5.22359 0 5 0.223593 5 0.499999C5 0.776405 5.22359 0.999998 5.5 0.999998H6.29296L4.52109 2.77187C4.32578 2.96718 4.32578 3.28359 4.52109 3.4789C4.71625 3.67406 5.03265 3.67437 5.22812 3.4789L6.99999 1.70782V2.50001C6.99999 2.77642 7.22359 3.00001 7.49999 3.00001C7.7764 3.00001 7.99999 2.77642 7.99999 2.50001V0.500015C7.99999 0.435015 7.98749 0.370171 7.96093 0.309078Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_609_69">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,6 +1,6 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_609_75)">
|
||||
<path d="M0.854041 7.85284L2.75022 5.95775V6.74984C2.75022 7.02622 2.97379 7.24978 3.25017 7.24978C3.52654 7.24978 3.75011 7.02622 3.75011 6.74984V4.75008C3.75011 4.68493 3.73683 4.62009 3.71152 4.55916C3.6609 4.43605 3.56404 4.33919 3.44061 4.28919C3.37968 4.26264 3.31563 4.25014 3.25001 4.25014H1.25024C0.97387 4.25014 0.750303 4.4737 0.750303 4.75008C0.750303 5.02645 0.97387 5.25002 1.25024 5.25002H2.04312L0.146467 7.14667C-0.0488224 7.34196 -0.0488224 7.65833 0.146467 7.85362C0.341757 8.04891 0.658595 8.04813 0.853884 7.85284H0.854041ZM4.28911 3.44086C4.33973 3.56334 4.43706 3.66083 4.5597 3.71145C4.62032 3.7377 4.68437 3.7502 4.74999 3.7502H6.74976C7.02613 3.7502 7.2497 3.52663 7.2497 3.25025C7.2497 2.97388 7.02613 2.75031 6.74976 2.75031H5.95688L7.85353 0.85366C8.04882 0.65837 8.04882 0.342001 7.85353 0.146711C7.6584 -0.048422 7.34203 -0.0487345 7.14658 0.146711L5.24993 2.04415V1.25049C5.24993 0.974114 5.02636 0.750547 4.74999 0.750547C4.47362 0.750547 4.25005 0.974114 4.25005 1.25049V3.23619C4.25005 3.31587 4.26255 3.37993 4.28911 3.44086Z" fill="white"/>
|
||||
<path d="M0.854041 7.85284L2.75022 5.95775V6.74984C2.75022 7.02622 2.97379 7.24979 3.25017 7.24979C3.52654 7.24979 3.75011 7.02622 3.75011 6.74984V4.75008C3.75011 4.68493 3.73683 4.62009 3.71152 4.55916C3.6609 4.43605 3.56404 4.33919 3.44061 4.28919C3.37968 4.26264 3.31563 4.25014 3.25001 4.25014H1.25024C0.97387 4.25014 0.750303 4.4737 0.750303 4.75008C0.750303 5.02645 0.97387 5.25002 1.25024 5.25002H2.04312L0.146467 7.14667C-0.0488224 7.34196 -0.0488224 7.65833 0.146467 7.85362C0.341757 8.04891 0.658595 8.04813 0.853884 7.85284H0.854041ZM4.28911 3.44086C4.33973 3.56334 4.43706 3.66083 4.5597 3.71145C4.62032 3.7377 4.68437 3.7502 4.74999 3.7502H6.74976C7.02613 3.7502 7.2497 3.52663 7.2497 3.25025C7.2497 2.97388 7.02613 2.75031 6.74976 2.75031H5.95688L7.85353 0.85366C8.04882 0.65837 8.04882 0.342001 7.85353 0.146711C7.6584 -0.048422 7.34203 -0.0487345 7.14658 0.146711L5.24993 2.04415V1.25049C5.24993 0.974114 5.02636 0.750547 4.74999 0.750547C4.47362 0.750547 4.25005 0.974114 4.25005 1.25049V3.23619C4.25005 3.31587 4.26255 3.37993 4.28911 3.44086Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_609_75">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2.5V9.5M2.5 6H9.5" stroke="white" stroke-linecap="round"/>
|
||||
<g clip-path="url(#clip0_519_280)">
|
||||
<path d="M6 2.5V9.5M2.5 6H9.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_519_280">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 335 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 2V14M2 8H14" stroke="white" stroke-width="1.71429" stroke-linecap="round"/>
|
||||
<g clip-path="url(#clip0_702_228)">
|
||||
<path d="M8.00001 3.33331V12.6666M3.33334 7.99998H12.6667" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_702_228">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 363 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 1.5V6.5M1.5 4H6.5" stroke="white" stroke-linecap="round"/>
|
||||
<g clip-path="url(#clip0_519_287)">
|
||||
<path d="M4 1V7M1 4H7" stroke="white" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_519_287">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 302 B |
@@ -1,5 +1,12 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1301)">
|
||||
<path d="M11 1H6V11H11V1Z" fill="white" fill-opacity="0.2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1818 1C1.51237 1 1.04544 1.48778 1.04544 2V10C1.04544 10.5122 1.51237 11 2.1818 11H9.81817C10.4876 11 10.9545 10.5122 10.9545 10V2C10.9545 1.48778 10.4876 1 9.81817 1H2.1818ZM0.0454407 2C0.0454407 0.855367 1.04377 0 2.1818 0H9.81817C10.9562 0 11.9545 0.855367 11.9545 2V10C11.9545 11.1446 10.9562 12 9.81817 12H2.1818C1.04377 12 0.0454407 11.1446 0.0454407 10V2Z" fill="white"/>
|
||||
<path d="M6.09091 1H5V11H6.09091V1Z" fill="white"/>
|
||||
<path d="M6 1H5V11H6V1Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1301">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 647 B After Width: | Height: | Size: 779 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1295)">
|
||||
<path d="M0 2.25C0 1.42148 0.67148 0.75 1.5 0.75H10.5C11.3273 0.75 12 1.42148 12 2.25V9.75C12 10.5773 11.3273 11.25 10.5 11.25H1.5C0.67148 11.25 0 10.5773 0 9.75V2.25ZM2.39766 3.55781C2.18789 3.7875 2.20336 4.14141 2.43281 4.35234L4.23047 6L2.43281 7.64766C2.20336 7.85859 2.18789 8.2125 2.39766 8.44219C2.60859 8.65078 2.9625 8.68594 3.19219 8.47734L5.44219 6.41484C5.55937 6.30703 5.625 6.15703 5.625 5.97891C5.625 5.84297 5.55937 5.69297 5.44219 5.58516L3.19219 3.52266C2.9625 3.31406 2.60859 3.32813 2.39766 3.55781ZM5.8125 7.875C5.50078 7.875 5.25 8.12578 5.25 8.4375C5.25 8.74922 5.50078 9 5.8125 9H9.1875C9.4992 9 9.75 8.74922 9.75 8.4375C9.75 8.12578 9.4992 7.875 9.1875 7.875H5.8125Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1295">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 956 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1381)">
|
||||
<path d="M0 1.5C0 0.947653 0.447653 0.5 1 0.5H7C7.55153 0.5 8 0.947653 8 1.5V6.5C8 7.05153 7.55153 7.5 7 7.5H1C0.447653 7.5 0 7.05153 0 6.5V1.5ZM1.59844 2.37187C1.45859 2.525 1.46891 2.76094 1.62187 2.90156L2.82031 4L1.62187 5.09844C1.46891 5.23906 1.45859 5.475 1.59844 5.62813C1.73906 5.76719 1.975 5.79063 2.12813 5.65156L3.62813 4.27656C3.70625 4.20469 3.75 4.10469 3.75 3.98594C3.75 3.89531 3.70625 3.79531 3.62813 3.72344L2.12813 2.34844C1.975 2.20937 1.73906 2.21875 1.59844 2.37187ZM3.875 5.25C3.66719 5.25 3.5 5.41719 3.5 5.625C3.5 5.83281 3.66719 6 3.875 6H6.125C6.3328 6 6.5 5.83281 6.5 5.625C6.5 5.41719 6.3328 5.25 6.125 5.25H3.875Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1381">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 761 B After Width: | Height: | Size: 903 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1297)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 11.625H0V9.375L5.25 0.375H6.75L12 9.375V11.625ZM5.25 3.375H6.75V7.125H5.25V3.375ZM5.25 8.625H6.75V10.125H5.25V8.625Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1297">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 433 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1371)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 7.75H0V6.25L3.5 0.25H4.5L8 6.25V7.75ZM3.5 2.25H4.5V4.75H3.5V2.25ZM3.5 5.75H4.5V6.75H3.5V5.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1371">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 403 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1383)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.75 1C5.33579 1 5 1.33579 5 1.75V3H6.5V8H0.5V3H4V1.75C4 0.783502 4.7835 0 5.75 0C6.7165 0 7.5 0.783502 7.5 1.75V2H6.5V1.75C6.5 1.33579 6.16421 1 5.75 1ZM4.5 5H2.5V6H4.5V5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1383">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 480 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1299)">
|
||||
<path d="M6 0C2.68594 0 0 2.68594 0 6C0 9.31406 2.68594 12 6 12C9.31406 12 12 9.31406 12 6C12 2.68594 9.31406 0 6 0ZM6 3C6.93211 3 7.6875 3.75563 7.6875 4.6875C7.6875 5.61937 6.93281 6.375 6 6.375C5.06813 6.375 4.3125 5.61937 4.3125 4.6875C4.3125 3.75563 5.06719 3 6 3ZM6 10.5C4.75945 10.5 3.63516 9.99539 2.81953 9.1807C3.19922 8.20078 4.13672 7.5 5.25 7.5H6.75C7.86422 7.5 8.80172 8.20031 9.18047 9.1807C8.36484 9.99609 7.23984 10.5 6 10.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1299">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 706 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 0C1.79063 0 0 1.79063 0 4C0 6.20937 1.79063 8 4 8C6.20937 8 8 6.20937 8 4C8 1.79063 6.20937 0 4 0ZM4 2C4.62141 2 5.125 2.50375 5.125 3.125C5.125 3.74625 4.62187 4.25 4 4.25C3.37875 4.25 2.875 3.74625 2.875 3.125C2.875 2.50375 3.37812 2 4 2ZM4 7C3.17297 7 2.42344 6.66359 1.87969 6.12047C2.13281 5.46719 2.75781 5 3.5 5H4.5C5.24281 5 5.86781 5.46688 6.12031 6.12047C5.57656 6.66406 4.82656 7 4 7Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1382)">
|
||||
<path d="M4 0C1.79063 0 0 1.79063 0 4C0 6.20937 1.79063 8 4 8C6.20937 8 8 6.20937 8 4C8 1.79063 6.20937 0 4 0ZM4 2C4.62141 2 5.125 2.50375 5.125 3.125C5.125 3.74625 4.62187 4.25 4 4.25C3.37875 4.25 2.875 3.74625 2.875 3.125C2.875 2.50375 3.37813 2 4 2ZM4 7C3.17297 7 2.42344 6.66359 1.87969 6.12047C2.13281 5.46719 2.75781 5 3.5 5H4.5C5.24281 5 5.86781 5.46687 6.12031 6.12047C5.57656 6.66406 4.82656 7 4 7Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1382">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 523 B After Width: | Height: | Size: 665 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.2 6.00001C5.52563 6.00001 6.6 4.92545 6.6 3.60001C6.6 2.27457 5.52563 1.20001 4.2 1.20001C2.87438 1.20001 1.8 2.27457 1.8 3.60001C1.8 4.92545 2.87438 6.00001 4.2 6.00001ZM5.15063 6.90001H3.24938C1.45444 6.90001 0 8.35501 0 10.1494C0 10.5094 0.291 10.8 0.649875 10.8H7.74937C8.10938 10.8 8.4 10.5094 8.4 10.1494C8.4 8.35501 6.945 6.90001 5.15063 6.90001ZM8.98313 7.20001H7.59844C8.46 7.90689 9 8.96439 9 10.1494C9 10.3894 8.92875 10.6106 8.8125 10.8H11.4C11.7319 10.8 12 10.53 12 10.1831C12 8.54251 10.6575 7.20001 8.98313 7.20001ZM8.1 6.00001C9.26063 6.00001 10.2 5.06064 10.2 3.90001C10.2 2.73939 9.26063 1.80001 8.1 1.80001C7.62919 1.80001 7.19925 1.96042 6.849 2.22207C7.065 2.63682 7.2 3.10126 7.2 3.60001C7.2 4.26601 6.97631 4.87764 6.60769 5.37582C6.98813 5.76001 7.515 6.00001 8.1 6.00001Z" fill="white"/>
|
||||
<path d="M4.2 6.00001C5.52563 6.00001 6.6 4.92545 6.6 3.60001C6.6 2.27457 5.52563 1.20001 4.2 1.20001C2.87438 1.20001 1.8 2.27457 1.8 3.60001C1.8 4.92545 2.87438 6.00001 4.2 6.00001ZM5.15063 6.90001H3.24938C1.45444 6.90001 0 8.35501 0 10.1494C0 10.5094 0.291 10.8 0.649875 10.8H7.74938C8.10938 10.8 8.4 10.5094 8.4 10.1494C8.4 8.35501 6.945 6.90001 5.15063 6.90001ZM8.98313 7.20001H7.59844C8.46 7.90689 9 8.96439 9 10.1494C9 10.3894 8.92875 10.6106 8.8125 10.8H11.4C11.7319 10.8 12 10.53 12 10.1831C12 8.54251 10.6575 7.20001 8.98313 7.20001ZM8.1 6.00001C9.26063 6.00001 10.2 5.06064 10.2 3.90001C10.2 2.73939 9.26063 1.80001 8.1 1.80001C7.62919 1.80001 7.19925 1.96042 6.849 2.22207C7.065 2.63682 7.2 3.10126 7.2 3.60001C7.2 4.26601 6.97631 4.87764 6.60769 5.37582C6.98813 5.76001 7.515 6.00001 8.1 6.00001Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 928 B After Width: | Height: | Size: 928 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.8 3.99999C3.68375 3.99999 4.4 3.28361 4.4 2.39999C4.4 1.51636 3.68375 0.799988 2.8 0.799988C1.91625 0.799988 1.2 1.51636 1.2 2.39999C1.2 3.28361 1.91625 3.99999 2.8 3.99999ZM3.43375 4.59999H2.16625C0.969625 4.59999 0 5.56999 0 6.76624C0 7.00624 0.194 7.19999 0.43325 7.19999H5.16625C5.40625 7.19999 5.6 7.00624 5.6 6.76624C5.6 5.56999 4.63 4.59999 3.43375 4.59999ZM5.98875 4.79999H5.06563C5.64 5.27124 6 5.97624 6 6.76624C6 6.92624 5.9525 7.07374 5.875 7.19999H7.6C7.82125 7.19999 8 7.01999 8 6.78874C8 5.69499 7.105 4.79999 5.98875 4.79999ZM5.4 3.99999C6.17375 3.99999 6.8 3.37374 6.8 2.59999C6.8 1.82624 6.17375 1.19999 5.4 1.19999C5.08612 1.19999 4.7995 1.30693 4.566 1.48136C4.71 1.75786 4.8 2.06749 4.8 2.39999C4.8 2.84399 4.65088 3.25174 4.40513 3.58386C4.65875 3.83999 5.01 3.99999 5.4 3.99999Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1375)">
|
||||
<path d="M2.8 3.99999C3.68375 3.99999 4.4 3.28361 4.4 2.39999C4.4 1.51636 3.68375 0.799988 2.8 0.799988C1.91625 0.799988 1.2 1.51636 1.2 2.39999C1.2 3.28361 1.91625 3.99999 2.8 3.99999ZM3.43375 4.59999H2.16625C0.969625 4.59999 0 5.56999 0 6.76624C0 7.00624 0.194 7.19999 0.43325 7.19999H5.16625C5.40625 7.19999 5.6 7.00624 5.6 6.76624C5.6 5.56999 4.63 4.59999 3.43375 4.59999ZM5.98875 4.79999H5.06563C5.64 5.27124 6 5.97624 6 6.76624C6 6.92624 5.9525 7.07374 5.875 7.19999H7.6C7.82125 7.19999 8 7.01999 8 6.78874C8 5.69499 7.105 4.79999 5.98875 4.79999ZM5.4 3.99999C6.17375 3.99999 6.8 3.37374 6.8 2.59999C6.8 1.82624 6.17375 1.19999 5.4 1.19999C5.08613 1.19999 4.7995 1.30693 4.566 1.48136C4.71 1.75786 4.8 2.06749 4.8 2.39999C4.8 2.84399 4.65088 3.25174 4.40513 3.58386C4.65875 3.83999 5.01 3.99999 5.4 3.99999Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1375">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 929 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_430_1373)">
|
||||
<path d="M2.8 3.99999C3.68375 3.99999 4.4 3.28361 4.4 2.39999C4.4 1.51636 3.68375 0.799988 2.8 0.799988C1.91625 0.799988 1.2 1.51636 1.2 2.39999C1.2 3.28361 1.91625 3.99999 2.8 3.99999ZM3.43375 4.59999H2.16625C0.970125 4.59999 0 5.56999 0 6.76624C0 7.00624 0.194 7.19999 0.43325 7.19999H5.167C5.40625 7.19999 5.6 7.00624 5.6 6.76624C5.6 5.56999 4.63 4.59999 3.43375 4.59999ZM7.7 3.29999H7.1V2.69999C7.1 2.53499 6.96625 2.39999 6.8 2.39999C6.63375 2.39999 6.5 2.53436 6.5 2.69999V3.29999H5.9C5.735 3.29999 5.6 3.43499 5.6 3.59999C5.6 3.76499 5.73438 3.89999 5.9 3.89999H6.5V4.49999C6.5 4.66624 6.635 4.79999 6.8 4.79999C6.965 4.79999 7.1 4.66561 7.1 4.49999V3.89999H7.7C7.86625 3.89999 8 3.76624 8 3.59999C8 3.43374 7.86625 3.29999 7.7 3.29999Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1373">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 1001 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.71108 6L0 2.28892L2.28892 0L6 3.71108L9.71108 0L12 2.28892L8.28892 6L12 9.71108L9.71108 12L6 8.28892L2.28892 12L0 9.71108L3.71108 6Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1296)">
|
||||
<path d="M9.5 2.5L6 6M2.5 9.5L6 6M2.5 2.5L6 6M6 6L9.5 9.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1296">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 365 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.32959 8L1 3.67041L3.67041 1L8 5.32959L12.3296 1L15 3.67041L10.6704 8L15 12.3296L12.3296 15L8 10.6704L3.67041 15L1 12.3296L5.32959 8Z" fill="white"/>
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_702_229)">
|
||||
<path d="M11.875 3.125L7.5 7.5M3.125 11.875L7.5 7.5M3.125 3.125L7.5 7.5M7.5 7.5L11.875 11.875" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_702_229">
|
||||
<rect width="15" height="15" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 399 B |
@@ -1,3 +1,10 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.47405 4L0 1.52595L1.52595 0L4 2.47405L6.47405 0L8 1.52595L5.52595 4L8 6.47405L6.47405 8L4 5.52595L1.52595 8L0 6.47405L2.47405 4Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_430_1379)">
|
||||
<path d="M6.49999 1.49999L1.49997 6.5M1.49998 1.50002L6.49999 6.50004" stroke="white" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_430_1379">
|
||||
<rect width="8" height="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 352 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.76433 6.63513C8.0768 6.9476 8.0768 7.4538 7.76433 7.76627C7.60935 7.92251 7.40437 8 7.19939 8C6.99441 8 6.78992 7.92188 6.63394 7.76565L3.99969 5.13264L1.36568 7.7649C1.20945 7.92238 1.00472 7.99988 0.799986 7.99988C0.595255 7.99988 0.390774 7.92238 0.234414 7.7649C-0.0780566 7.45243 -0.0780566 6.94622 0.234414 6.63375L2.86917 3.999L0.234414 1.3655C-0.0780566 1.05303 -0.0780566 0.546824 0.234414 0.234353C0.546885 -0.0781177 1.05309 -0.0781177 1.36556 0.234353L3.99969 2.87023L6.63444 0.235478C6.94691 -0.0769928 7.45311 -0.0769928 7.76558 0.235478C8.07805 0.547949 8.07805 1.05415 7.76558 1.36662L5.13083 4.00138L7.76433 6.63488V6.63513Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 769 B |
@@ -3,8 +3,12 @@
|
||||
{
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"pageup": "menu::SelectFirst",
|
||||
"shift-pageup": "menu::SelectFirst",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"down": "menu::SelectNext",
|
||||
"pagedown": "menu::SelectLast",
|
||||
"shift-pagedown": "menu::SelectFirst",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"cmd-up": "menu::SelectFirst",
|
||||
"cmd-down": "menu::SelectLast",
|
||||
@@ -16,8 +20,10 @@
|
||||
"alt-cmd-left": "pane::ActivatePrevItem",
|
||||
"alt-cmd-right": "pane::ActivateNextItem",
|
||||
"cmd-w": "pane::CloseActiveItem",
|
||||
"cmd-shift-w": "workspace::CloseWindow",
|
||||
"alt-cmd-t": "pane::CloseInactiveItems",
|
||||
"cmd-k u": "pane::CloseCleanItems",
|
||||
"cmd-k cmd-w": "pane::CloseAllItems",
|
||||
"cmd-shift-w": "workspace::CloseWindow",
|
||||
"cmd-s": "workspace::Save",
|
||||
"cmd-shift-s": "workspace::SaveAs",
|
||||
"cmd-=": "zed::IncreaseBufferFontSize",
|
||||
@@ -32,6 +38,7 @@
|
||||
"cmd-n": "workspace::NewFile",
|
||||
"cmd-shift-n": "workspace::NewWindow",
|
||||
"cmd-o": "workspace::Open",
|
||||
"alt-cmd-o": "recent_projects::Toggle",
|
||||
"ctrl-`": "workspace::NewTerminal"
|
||||
}
|
||||
},
|
||||
@@ -60,13 +67,20 @@
|
||||
"cmd-z": "editor::Undo",
|
||||
"cmd-shift-z": "editor::Redo",
|
||||
"up": "editor::MoveUp",
|
||||
"pageup": "editor::PageUp",
|
||||
"shift-pageup": "editor::MovePageUp",
|
||||
"home": "editor::MoveToBeginningOfLine",
|
||||
"down": "editor::MoveDown",
|
||||
"pagedown": "editor::PageDown",
|
||||
"shift-pagedown": "editor::MovePageDown",
|
||||
"end": "editor::MoveToEndOfLine",
|
||||
"left": "editor::MoveLeft",
|
||||
"right": "editor::MoveRight",
|
||||
"ctrl-p": "editor::MoveUp",
|
||||
"ctrl-n": "editor::MoveDown",
|
||||
"ctrl-b": "editor::MoveLeft",
|
||||
"ctrl-f": "editor::MoveRight",
|
||||
"ctrl-l": "editor::NextScreen",
|
||||
"alt-left": "editor::MoveToPreviousWordStart",
|
||||
"alt-b": "editor::MoveToPreviousWordStart",
|
||||
"alt-right": "editor::MoveToNextWordEnd",
|
||||
@@ -100,6 +114,12 @@
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"shift-home": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-a": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
@@ -112,14 +132,30 @@
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"shift-end": [
|
||||
"editor::SelectToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-e": [
|
||||
"editor::SelectToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"pageup": "editor::PageUp",
|
||||
"pagedown": "editor::PageDown",
|
||||
"ctrl-v": [
|
||||
"editor::MovePageDown",
|
||||
{
|
||||
"center_cursor": true
|
||||
}
|
||||
],
|
||||
"alt-v": [
|
||||
"editor::MovePageUp",
|
||||
{
|
||||
"center_cursor": true
|
||||
}
|
||||
],
|
||||
"ctrl-cmd-space": "editor::ShowCharacterPalette"
|
||||
}
|
||||
},
|
||||
@@ -150,10 +186,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar",
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"cmd-f": "buffer_search::FocusEditor",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
"enter": "search::SelectNextMatch",
|
||||
"shift-enter": "search::SelectPrevMatch"
|
||||
}
|
||||
@@ -383,7 +419,9 @@
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"alt-enter": "editor::OpenExcerpts"
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"cmd-f8": "editor::GoToHunk",
|
||||
"cmd-shift-f8": "editor::GoToPrevHunk"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -412,6 +450,12 @@
|
||||
"shift-escape": "dock::HideDock"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"cmd-escape": "dock::MoveActiveItemToDock"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ProjectPanel",
|
||||
"bindings": {
|
||||
@@ -445,16 +489,33 @@
|
||||
"terminal::SendText",
|
||||
"\u0001"
|
||||
],
|
||||
// Terminal.app compatability
|
||||
"alt-left": [
|
||||
"terminal::SendText",
|
||||
"\u001bb"
|
||||
],
|
||||
"alt-right": [
|
||||
"terminal::SendText",
|
||||
"\u001bf"
|
||||
],
|
||||
// There are conflicting bindings for these keys in the global context.
|
||||
// these bindings override them, remove at your own risk:
|
||||
"up": [
|
||||
"terminal::SendKeystroke",
|
||||
"up"
|
||||
],
|
||||
"pageup": [
|
||||
"terminal::SendKeystroke",
|
||||
"pageup"
|
||||
],
|
||||
"down": [
|
||||
"terminal::SendKeystroke",
|
||||
"down"
|
||||
],
|
||||
"pagedown": [
|
||||
"terminal::SendKeystroke",
|
||||
"pagedown"
|
||||
],
|
||||
"escape": [
|
||||
"terminal::SendKeystroke",
|
||||
"escape"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"context": "Editor && VimControl",
|
||||
"context": "Editor && VimControl && !VimWaiting",
|
||||
"bindings": {
|
||||
"g": [
|
||||
"vim::PushOperator",
|
||||
@@ -8,6 +8,22 @@
|
||||
"Namespace": "G"
|
||||
}
|
||||
],
|
||||
"i": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"a": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"h": "vim::Left",
|
||||
"backspace": "vim::Backspace",
|
||||
"j": "vim::Down",
|
||||
@@ -37,23 +53,43 @@
|
||||
}
|
||||
],
|
||||
"%": "vim::Matching",
|
||||
"ctrl-y": [
|
||||
"vim::Scroll",
|
||||
"LineUp"
|
||||
],
|
||||
"f": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"FindForward": {
|
||||
"before": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"t": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"FindForward": {
|
||||
"before": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"shift-f": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"FindBackward": {
|
||||
"after": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"shift-t": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"FindBackward": {
|
||||
"after": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"escape": "editor::Cancel",
|
||||
"i": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"a": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
|
||||
"1": [
|
||||
"vim::Number",
|
||||
@@ -94,7 +130,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_mode == normal && vim_operator == none",
|
||||
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
|
||||
"bindings": {
|
||||
"c": [
|
||||
"vim::PushOperator",
|
||||
@@ -110,6 +146,12 @@
|
||||
"vim::PushOperator",
|
||||
"Yank"
|
||||
],
|
||||
"z": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Namespace": "Z"
|
||||
}
|
||||
],
|
||||
"i": [
|
||||
"vim::SwitchMode",
|
||||
"Insert"
|
||||
@@ -147,6 +189,30 @@
|
||||
{
|
||||
"focus": true
|
||||
}
|
||||
],
|
||||
"ctrl-f": [
|
||||
"vim::Scroll",
|
||||
"PageDown"
|
||||
],
|
||||
"ctrl-b": [
|
||||
"vim::Scroll",
|
||||
"PageUp"
|
||||
],
|
||||
"ctrl-d": [
|
||||
"vim::Scroll",
|
||||
"HalfPageDown"
|
||||
],
|
||||
"ctrl-u": [
|
||||
"vim::Scroll",
|
||||
"HalfPageUp"
|
||||
],
|
||||
"ctrl-e": [
|
||||
"vim::Scroll",
|
||||
"LineDown"
|
||||
],
|
||||
"r": [
|
||||
"vim::PushOperator",
|
||||
"Replace"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -188,6 +254,18 @@
|
||||
"y": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_operator == z",
|
||||
"bindings": {
|
||||
"t": "editor::ScrollCursorTop",
|
||||
"z": "editor::ScrollCursorCenter",
|
||||
"b": "editor::ScrollCursorBottom",
|
||||
"escape": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && VimObject",
|
||||
"bindings": {
|
||||
@@ -213,14 +291,18 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_mode == visual",
|
||||
"context": "Editor && vim_mode == visual && !VimWaiting",
|
||||
"bindings": {
|
||||
"u": "editor::Undo",
|
||||
"c": "vim::VisualChange",
|
||||
"d": "vim::VisualDelete",
|
||||
"x": "vim::VisualDelete",
|
||||
"y": "vim::VisualYank",
|
||||
"p": "vim::VisualPaste"
|
||||
"p": "vim::VisualPaste",
|
||||
"r": [
|
||||
"vim::PushOperator",
|
||||
"Replace"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -229,5 +311,11 @@
|
||||
"escape": "vim::NormalBefore",
|
||||
"ctrl-c": "vim::NormalBefore"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && VimWaiting",
|
||||
"bindings": {
|
||||
"*": "gpui::KeyPressed"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,15 +1,20 @@
|
||||
{
|
||||
// The name of the Zed theme to use for the UI
|
||||
"theme": "one-dark",
|
||||
"theme": "One Dark",
|
||||
// The name of a font to use for rendering text in the editor
|
||||
"buffer_font_family": "Zed Mono",
|
||||
// The default font size for text in the editor
|
||||
"buffer_font_size": 15,
|
||||
// The factor to grow the active pane by. Defaults to 1.0
|
||||
// which gives the same size as all other panes.
|
||||
"active_pane_magnification": 1.0,
|
||||
// Whether to enable vim modes and key bindings
|
||||
"vim_mode": false,
|
||||
// Whether to show the informational hover box when moving the mouse
|
||||
// over symbols in the editor.
|
||||
"hover_popover_enabled": true,
|
||||
// Whether the cursor blinks in the editor.
|
||||
"cursor_blink": true,
|
||||
// Whether to pop the completions menu while typing in an editor without
|
||||
// explicitly requesting it.
|
||||
"show_completions_on_input": true,
|
||||
@@ -74,6 +79,13 @@
|
||||
"hard_tabs": false,
|
||||
// How many columns a tab should occupy.
|
||||
"tab_size": 4,
|
||||
// Control what info Zed sends to our servers
|
||||
"telemetry": {
|
||||
// Send debug info like crash reports.
|
||||
"diagnostics": true,
|
||||
// Send anonymized usage data like what languages you're using Zed with.
|
||||
"metrics": true
|
||||
},
|
||||
// Git gutter behavior configuration.
|
||||
"git": {
|
||||
// Control whether the git gutter is shown. May take 2 values:
|
||||
@@ -83,9 +95,19 @@
|
||||
// "git_gutter": "hide"
|
||||
"git_gutter": "tracked_files"
|
||||
},
|
||||
// Settings specific to journaling
|
||||
"journal": {
|
||||
// The path of the directory where journal entries are stored
|
||||
"path": "~",
|
||||
// What format to display the hours in
|
||||
// May take 2 values:
|
||||
// 1. hour12
|
||||
// 2. hour24
|
||||
"hour_format": "hour12"
|
||||
},
|
||||
// Settings specific to the terminal
|
||||
"terminal": {
|
||||
// What shell to use when opening a terminal. May take 3 values:
|
||||
// What shell to use when opening a terminal. May take 3 values:
|
||||
// 1. Use the system's default terminal configuration (e.g. $TERM).
|
||||
// "shell": "system"
|
||||
// 2. A program:
|
||||
@@ -102,7 +124,7 @@
|
||||
"shell": "system",
|
||||
// What working directory to use when launching the terminal.
|
||||
// May take 4 values:
|
||||
// 1. Use the current file's project directory. Will Fallback to the
|
||||
// 1. Use the current file's project directory. Will Fallback to the
|
||||
// first project directory strategy if unsuccessful
|
||||
// "working_directory": "current_project_directory"
|
||||
// 2. Use the first project in this workspace's directory
|
||||
@@ -112,7 +134,7 @@
|
||||
// 4. Always use a specific directory. This value will be shell expanded.
|
||||
// If this path is not a valid directory the terminal will default to
|
||||
// this platform's home directory (if we can find it)
|
||||
// "working_directory": {
|
||||
// "working_directory": {
|
||||
// "always": {
|
||||
// "directory": "~/zed/projects/"
|
||||
// }
|
||||
@@ -124,7 +146,7 @@
|
||||
// May take 4 values:
|
||||
// 1. Never blink the cursor, ignoring the terminal mode
|
||||
// "blinking": "off",
|
||||
// 2. Default the cursor blink to off, but allow the terminal to
|
||||
// 2. Default the cursor blink to off, but allow the terminal to
|
||||
// set blinking
|
||||
// "blinking": "terminal_controlled",
|
||||
// 3. Always blink the cursor, ignoring the terminal mode
|
||||
@@ -132,7 +154,7 @@
|
||||
"blinking": "terminal_controlled",
|
||||
// Set whether Alternate Scroll mode (code: ?1007) is active by default.
|
||||
// Alternate Scroll mode converts mouse scroll events into up / down key
|
||||
// presses when in the alternate screen (e.g. when running applications
|
||||
// presses when in the alternate screen (e.g. when running applications
|
||||
// like vim or less). The terminal can still set and unset this mode.
|
||||
// May take 2 values:
|
||||
// 1. Default alternate scroll mode to on
|
||||
@@ -206,7 +228,7 @@
|
||||
// rust-analyzer
|
||||
// typescript-language-server
|
||||
// vscode-json-languageserver
|
||||
// "rust_analyzer": {
|
||||
// "rust-analyzer": {
|
||||
// //These initialization options are merged into Zed's defaults
|
||||
// "initialization_options": {
|
||||
// "checkOnSave": {
|
||||
|
||||
@@ -11,13 +11,12 @@ use settings::Settings;
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp::Reverse, fmt::Write, sync::Arc};
|
||||
use util::ResultExt;
|
||||
use workspace::{ItemHandle, StatusItemView, Workspace};
|
||||
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||
|
||||
actions!(lsp_status, [ShowErrorMessage]);
|
||||
|
||||
const DOWNLOAD_ICON: &str = "icons/download_12.svg";
|
||||
const WARNING_ICON: &str = "icons/triangle_exclamation_12.svg";
|
||||
const DONE_ICON: &str = "icons/circle_check_12.svg";
|
||||
|
||||
pub enum Event {
|
||||
ShowError { lsp_name: Arc<str>, error: String },
|
||||
@@ -46,6 +45,7 @@ impl ActivityIndicator {
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> ViewHandle<ActivityIndicator> {
|
||||
let project = workspace.project().clone();
|
||||
let auto_updater = AutoUpdater::get(cx);
|
||||
let this = cx.add_view(|cx: &mut ViewContext<Self>| {
|
||||
let mut status_events = languages.language_server_binary_statuses();
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
@@ -66,11 +66,14 @@ impl ActivityIndicator {
|
||||
})
|
||||
.detach();
|
||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||
if let Some(auto_updater) = auto_updater.as_ref() {
|
||||
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
||||
}
|
||||
|
||||
Self {
|
||||
statuses: Default::default(),
|
||||
project: project.clone(),
|
||||
auto_updater: AutoUpdater::get(cx),
|
||||
auto_updater,
|
||||
}
|
||||
});
|
||||
cx.subscribe(&this, move |workspace, _, event, cx| match event {
|
||||
@@ -233,7 +236,6 @@ impl ActivityIndicator {
|
||||
|
||||
// Show any application auto-update info.
|
||||
if let Some(updater) = &self.auto_updater {
|
||||
// let theme = &cx.global::<Settings>().theme.workspace.status_bar;
|
||||
match &updater.read(cx).status() {
|
||||
AutoUpdateStatus::Checking => (
|
||||
Some(DOWNLOAD_ICON),
|
||||
@@ -250,9 +252,7 @@ impl ActivityIndicator {
|
||||
"Installing Zed update…".to_string(),
|
||||
None,
|
||||
),
|
||||
AutoUpdateStatus::Updated => {
|
||||
(Some(DONE_ICON), "Restart to update Zed".to_string(), None)
|
||||
}
|
||||
AutoUpdateStatus::Updated => (None, "Restart to update Zed".to_string(), None),
|
||||
AutoUpdateStatus::Errored => (
|
||||
Some(WARNING_ICON),
|
||||
"Auto update failed".to_string(),
|
||||
@@ -285,7 +285,7 @@ impl View for ActivityIndicator {
|
||||
.workspace
|
||||
.status_bar
|
||||
.lsp_status;
|
||||
let style = if state.hovered && action.is_some() {
|
||||
let style = if state.hovered() && action.is_some() {
|
||||
theme.hover.as_ref().unwrap_or(&theme.default)
|
||||
} else {
|
||||
&theme.default
|
||||
|
||||
29
crates/assets/build.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let output = Command::new("npm")
|
||||
.current_dir("../../styles")
|
||||
.args(["install", "--no-save"])
|
||||
.output()
|
||||
.expect("failed to run npm");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"failed to install theme dependencies {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
let output = Command::new("npm")
|
||||
.current_dir("../../styles")
|
||||
.args(["run", "build"])
|
||||
.output()
|
||||
.expect("failed to run npm");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"build script failed {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=../../styles/src");
|
||||
}
|
||||
@@ -8,6 +8,7 @@ path = "src/auto_update.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
db = { path = "../db" }
|
||||
client = { path = "../client" }
|
||||
gpui = { path = "../gpui" }
|
||||
menu = { path = "../menu" }
|
||||
|
||||
@@ -2,6 +2,7 @@ mod update_notification;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
|
||||
MutableAppContext, Task, WeakViewHandle,
|
||||
@@ -11,6 +12,7 @@ use serde::Deserialize;
|
||||
use smol::{fs::File, io::AsyncReadExt, process::Command};
|
||||
use std::{env, ffi::OsString, path::PathBuf, sync::Arc, time::Duration};
|
||||
use update_notification::UpdateNotification;
|
||||
use util::channel::ReleaseChannel;
|
||||
use workspace::Workspace;
|
||||
|
||||
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
|
||||
@@ -40,7 +42,6 @@ pub struct AutoUpdater {
|
||||
current_version: AppVersion,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
pending_poll: Option<Task<()>>,
|
||||
db: Arc<project::Db>,
|
||||
server_url: String,
|
||||
}
|
||||
|
||||
@@ -54,15 +55,11 @@ impl Entity for AutoUpdater {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
db: Arc<project::Db>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
server_url: String,
|
||||
cx: &mut MutableAppContext,
|
||||
) {
|
||||
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut MutableAppContext) {
|
||||
if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) {
|
||||
let server_url = server_url;
|
||||
let auto_updater = cx.add_model(|cx| {
|
||||
let updater = AutoUpdater::new(version, db.clone(), http_client, server_url.clone());
|
||||
let updater = AutoUpdater::new(version, http_client, server_url.clone());
|
||||
updater.start_polling(cx).detach();
|
||||
updater
|
||||
});
|
||||
@@ -73,7 +70,14 @@ pub fn init(
|
||||
}
|
||||
});
|
||||
cx.add_global_action(move |_: &ViewReleaseNotes, cx| {
|
||||
cx.platform().open_url(&format!("{server_url}/releases"));
|
||||
let latest_release_url = if cx.has_global::<ReleaseChannel>()
|
||||
&& *cx.global::<ReleaseChannel>() == ReleaseChannel::Preview
|
||||
{
|
||||
format!("{server_url}/releases/preview/latest")
|
||||
} else {
|
||||
format!("{server_url}/releases/latest")
|
||||
};
|
||||
cx.platform().open_url(&latest_release_url);
|
||||
});
|
||||
cx.add_action(UpdateNotification::dismiss);
|
||||
}
|
||||
@@ -116,14 +120,12 @@ impl AutoUpdater {
|
||||
|
||||
fn new(
|
||||
current_version: AppVersion,
|
||||
db: Arc<project::Db>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
server_url: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
status: AutoUpdateStatus::Idle,
|
||||
current_version,
|
||||
db,
|
||||
http_client,
|
||||
server_url,
|
||||
pending_poll: None,
|
||||
@@ -177,9 +179,19 @@ impl AutoUpdater {
|
||||
this.current_version,
|
||||
)
|
||||
});
|
||||
|
||||
let preview_param = cx.read(|cx| {
|
||||
if cx.has_global::<ReleaseChannel>() {
|
||||
if *cx.global::<ReleaseChannel>() == ReleaseChannel::Preview {
|
||||
return "&preview=1";
|
||||
}
|
||||
}
|
||||
""
|
||||
});
|
||||
|
||||
let mut response = client
|
||||
.get(
|
||||
&format!("{server_url}/api/releases/latest?token={ZED_SECRET_CLIENT_TOKEN}&asset=Zed.dmg"),
|
||||
&format!("{server_url}/api/releases/latest?token={ZED_SECRET_CLIENT_TOKEN}&asset=Zed.dmg{preview_param}"),
|
||||
Default::default(),
|
||||
true,
|
||||
)
|
||||
@@ -211,11 +223,14 @@ impl AutoUpdater {
|
||||
let temp_dir = tempdir::TempDir::new("zed-auto-update")?;
|
||||
let dmg_path = temp_dir.path().join("Zed.dmg");
|
||||
let mount_path = temp_dir.path().join("Zed");
|
||||
let mut mounted_app_path: OsString = mount_path.join("Zed.app").into();
|
||||
mounted_app_path.push("/");
|
||||
let running_app_path = ZED_APP_PATH
|
||||
.clone()
|
||||
.map_or_else(|| cx.platform().app_path(), Ok)?;
|
||||
let running_app_filename = running_app_path
|
||||
.file_name()
|
||||
.ok_or_else(|| anyhow!("invalid running app path"))?;
|
||||
let mut mounted_app_path: OsString = mount_path.join(running_app_filename).into();
|
||||
mounted_app_path.push("/");
|
||||
|
||||
let mut dmg_file = File::create(&dmg_path).await?;
|
||||
let mut response = client.get(&release.url, Default::default(), true).await?;
|
||||
@@ -280,21 +295,28 @@ impl AutoUpdater {
|
||||
should_show: bool,
|
||||
cx: &AppContext,
|
||||
) -> Task<Result<()>> {
|
||||
let db = self.db.clone();
|
||||
cx.background().spawn(async move {
|
||||
if should_show {
|
||||
db.write([(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY, "")])?;
|
||||
KEY_VALUE_STORE
|
||||
.write_kvp(
|
||||
SHOULD_SHOW_UPDATE_NOTIFICATION_KEY.to_string(),
|
||||
"".to_string(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
db.delete([(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY)])?;
|
||||
KEY_VALUE_STORE
|
||||
.delete_kvp(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY.to_string())
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn should_show_update_notification(&self, cx: &AppContext) -> Task<Result<bool>> {
|
||||
let db = self.db.clone();
|
||||
cx.background().spawn(async move {
|
||||
Ok(db.read([(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY)])?[0].is_some())
|
||||
Ok(KEY_VALUE_STORE
|
||||
.read_kvp(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY)?
|
||||
.is_some())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ use gpui::{
|
||||
};
|
||||
use menu::Cancel;
|
||||
use settings::Settings;
|
||||
use workspace::Notification;
|
||||
use util::channel::ReleaseChannel;
|
||||
use workspace::notifications::Notification;
|
||||
|
||||
pub struct UpdateNotification {
|
||||
version: AppVersion,
|
||||
@@ -29,13 +30,15 @@ impl View for UpdateNotification {
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
let theme = &theme.update_notification;
|
||||
|
||||
let app_name = cx.global::<ReleaseChannel>().display_name();
|
||||
|
||||
MouseEventHandler::<ViewReleaseNotes>::new(0, cx, |state, cx| {
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Text::new(
|
||||
format!("Updated to Zed {}", self.version),
|
||||
format!("Updated to {app_name} {}", self.version),
|
||||
theme.message.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
@@ -49,7 +52,7 @@ impl View for UpdateNotification {
|
||||
.with_child(
|
||||
MouseEventHandler::<Cancel>::new(0, cx, |state, _| {
|
||||
let style = theme.dismiss_button.style_for(state, false);
|
||||
Svg::new("icons/x_mark_thin_8.svg")
|
||||
Svg::new("icons/x_mark_8.svg")
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
.with_width(style.icon_width)
|
||||
|
||||
@@ -4,7 +4,10 @@ use gpui::{
|
||||
use itertools::Itertools;
|
||||
use search::ProjectSearchView;
|
||||
use settings::Settings;
|
||||
use workspace::{ItemEvent, ItemHandle, ToolbarItemLocation, ToolbarItemView};
|
||||
use workspace::{
|
||||
item::{ItemEvent, ItemHandle},
|
||||
ToolbarItemLocation, ToolbarItemView,
|
||||
};
|
||||
|
||||
pub enum Event {
|
||||
UpdateLocation,
|
||||
|
||||
@@ -12,6 +12,7 @@ test-support = [
|
||||
"client/test-support",
|
||||
"collections/test-support",
|
||||
"gpui/test-support",
|
||||
"live_kit_client/test-support",
|
||||
"project/test-support",
|
||||
"util/test-support"
|
||||
]
|
||||
@@ -20,16 +21,25 @@ test-support = [
|
||||
client = { path = "../client" }
|
||||
collections = { path = "../collections" }
|
||||
gpui = { path = "../gpui" }
|
||||
log = "0.4"
|
||||
live_kit_client = { path = "../live_kit_client" }
|
||||
fs = { path = "../fs" }
|
||||
language = { path = "../language" }
|
||||
media = { path = "../media" }
|
||||
project = { path = "../project" }
|
||||
util = { path = "../util" }
|
||||
|
||||
anyhow = "1.0.38"
|
||||
async-broadcast = "0.4"
|
||||
futures = "0.3"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
|
||||
[dev-dependencies]
|
||||
client = { path = "../client", features = ["test-support"] }
|
||||
fs = { path = "../fs", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
collections = { path = "../collections", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
util = { path = "../util", features = ["test-support"] }
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
mod participant;
|
||||
pub mod participant;
|
||||
pub mod room;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{proto, Client, TypedEnvelope, User, UserStore};
|
||||
use collections::HashSet;
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
|
||||
Subscription, Task,
|
||||
Subscription, Task, WeakModelHandle,
|
||||
};
|
||||
pub use participant::ParticipantLocation;
|
||||
use postage::watch;
|
||||
@@ -21,13 +22,15 @@ pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut Mu
|
||||
#[derive(Clone)]
|
||||
pub struct IncomingCall {
|
||||
pub room_id: u64,
|
||||
pub caller: Arc<User>,
|
||||
pub calling_user: Arc<User>,
|
||||
pub participants: Vec<Arc<User>>,
|
||||
pub initial_project: Option<proto::ParticipantProject>,
|
||||
}
|
||||
|
||||
pub struct ActiveCall {
|
||||
room: Option<(ModelHandle<Room>, Vec<Subscription>)>,
|
||||
location: Option<WeakModelHandle<Project>>,
|
||||
pending_invites: HashSet<u64>,
|
||||
incoming_call: (
|
||||
watch::Sender<Option<IncomingCall>>,
|
||||
watch::Receiver<Option<IncomingCall>>,
|
||||
@@ -49,6 +52,8 @@ impl ActiveCall {
|
||||
) -> Self {
|
||||
Self {
|
||||
room: None,
|
||||
location: None,
|
||||
pending_invites: Default::default(),
|
||||
incoming_call: watch::channel(),
|
||||
_subscriptions: vec![
|
||||
client.add_request_handler(cx.handle(), Self::handle_incoming_call),
|
||||
@@ -73,9 +78,9 @@ impl ActiveCall {
|
||||
user_store.get_users(envelope.payload.participant_user_ids, cx)
|
||||
})
|
||||
.await?,
|
||||
caller: user_store
|
||||
calling_user: user_store
|
||||
.update(&mut cx, |user_store, cx| {
|
||||
user_store.get_user(envelope.payload.caller_user_id, cx)
|
||||
user_store.get_user(envelope.payload.calling_user_id, cx)
|
||||
})
|
||||
.await?,
|
||||
initial_project: envelope.payload.initial_project,
|
||||
@@ -89,12 +94,18 @@ impl ActiveCall {
|
||||
|
||||
async fn handle_call_canceled(
|
||||
this: ModelHandle<Self>,
|
||||
_: TypedEnvelope<proto::CallCanceled>,
|
||||
envelope: TypedEnvelope<proto::CallCanceled>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |this, _| {
|
||||
*this.incoming_call.0.borrow_mut() = None;
|
||||
let mut incoming_call = this.incoming_call.0.borrow_mut();
|
||||
if incoming_call
|
||||
.as_ref()
|
||||
.map_or(false, |call| call.room_id == envelope.payload.room_id)
|
||||
{
|
||||
incoming_call.take();
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -105,43 +116,61 @@ impl ActiveCall {
|
||||
|
||||
pub fn invite(
|
||||
&mut self,
|
||||
recipient_user_id: u64,
|
||||
called_user_id: u64,
|
||||
initial_project: Option<ModelHandle<Project>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) {
|
||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
||||
Some(
|
||||
room.update(&mut cx, |room, cx| room.share_project(initial_project, cx))
|
||||
.await?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !self.pending_invites.insert(called_user_id) {
|
||||
return Task::ready(Err(anyhow!("user was already invited")));
|
||||
}
|
||||
|
||||
room.update(&mut cx, |room, cx| {
|
||||
room.call(recipient_user_id, initial_project_id, cx)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let room = cx
|
||||
.update(|cx| {
|
||||
Room::create(recipient_user_id, initial_project, client, user_store, cx)
|
||||
cx.notify();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let invite = async {
|
||||
if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) {
|
||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
||||
Some(
|
||||
room.update(&mut cx, |room, cx| {
|
||||
room.share_project(initial_project, cx)
|
||||
})
|
||||
.await?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
room.update(&mut cx, |room, cx| {
|
||||
room.call(called_user_id, initial_project_id, cx)
|
||||
})
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| this.set_room(Some(room), cx));
|
||||
} else {
|
||||
let room = cx
|
||||
.update(|cx| {
|
||||
Room::create(called_user_id, initial_project, client, user_store, cx)
|
||||
})
|
||||
.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| this.set_room(Some(room), cx))
|
||||
.await?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
Ok(())
|
||||
let result = invite.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.pending_invites.remove(&called_user_id);
|
||||
cx.notify();
|
||||
});
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cancel_invite(
|
||||
&mut self,
|
||||
recipient_user_id: u64,
|
||||
called_user_id: u64,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let room_id = if let Some(room) = self.room() {
|
||||
@@ -155,7 +184,7 @@ impl ActiveCall {
|
||||
client
|
||||
.request(proto::CancelCall {
|
||||
room_id,
|
||||
recipient_user_id,
|
||||
called_user_id,
|
||||
})
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
@@ -180,7 +209,8 @@ impl ActiveCall {
|
||||
let join = Room::join(&call, self.client.clone(), self.user_store.clone(), cx);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let room = join.await?;
|
||||
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx));
|
||||
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx))
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -223,39 +253,54 @@ impl ActiveCall {
|
||||
project: Option<&ModelHandle<Project>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
self.location = project.map(|project| project.downgrade());
|
||||
if let Some((room, _)) = self.room.as_ref() {
|
||||
room.update(cx, |room, cx| room.set_location(project, cx))
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("no active call")))
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_room(&mut self, room: Option<ModelHandle<Room>>, cx: &mut ModelContext<Self>) {
|
||||
fn set_room(
|
||||
&mut self,
|
||||
room: Option<ModelHandle<Room>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
if room.as_ref() != self.room.as_ref().map(|room| &room.0) {
|
||||
cx.notify();
|
||||
if let Some(room) = room {
|
||||
if room.read(cx).status().is_offline() {
|
||||
self.room = None;
|
||||
Task::ready(Ok(()))
|
||||
} else {
|
||||
let subscriptions = vec![
|
||||
cx.observe(&room, |this, room, cx| {
|
||||
if room.read(cx).status().is_offline() {
|
||||
this.set_room(None, cx);
|
||||
this.set_room(None, cx).detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}),
|
||||
cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())),
|
||||
];
|
||||
self.room = Some((room, subscriptions));
|
||||
self.room = Some((room.clone(), subscriptions));
|
||||
let location = self.location.and_then(|location| location.upgrade(cx));
|
||||
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
|
||||
}
|
||||
} else {
|
||||
self.room = None;
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
cx.notify();
|
||||
} else {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn room(&self) -> Option<&ModelHandle<Room>> {
|
||||
self.room.as_ref().map(|(room, _)| room)
|
||||
}
|
||||
|
||||
pub fn pending_invites(&self) -> &HashSet<u64> {
|
||||
&self.pending_invites
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{proto, User};
|
||||
use collections::HashMap;
|
||||
use gpui::WeakModelHandle;
|
||||
pub use live_kit_client::Frame;
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ParticipantLocation {
|
||||
@@ -37,6 +39,25 @@ pub struct LocalParticipant {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteParticipant {
|
||||
pub user: Arc<User>,
|
||||
pub peer_id: proto::PeerId,
|
||||
pub projects: Vec<proto::ParticipantProject>,
|
||||
pub location: ParticipantLocation,
|
||||
pub tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RemoteVideoTrack {
|
||||
pub(crate) live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RemoteVideoTrack {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("RemoteVideoTrack").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteVideoTrack {
|
||||
pub fn frames(&self) -> async_broadcast::Receiver<Frame> {
|
||||
self.live_kit_track.frames()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
use crate::{
|
||||
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant},
|
||||
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
|
||||
IncomingCall,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
|
||||
use collections::{BTreeMap, HashSet};
|
||||
use futures::StreamExt;
|
||||
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
|
||||
use client::{
|
||||
proto::{self, PeerId},
|
||||
Client, TypedEnvelope, User, UserStore,
|
||||
};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use fs::Fs;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate};
|
||||
use postage::stream::Stream;
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
use util::ResultExt;
|
||||
use std::{mem, sync::Arc, time::Duration};
|
||||
use util::{post_inc, ResultExt, TryFutureExt};
|
||||
|
||||
pub const RECONNECT_TIMEOUT: Duration = client::RECEIVE_TIMEOUT;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
ParticipantLocationChanged {
|
||||
participant_id: proto::PeerId,
|
||||
},
|
||||
RemoteVideoTracksChanged {
|
||||
participant_id: proto::PeerId,
|
||||
},
|
||||
RemoteProjectShared {
|
||||
owner: Arc<User>,
|
||||
project_id: u64,
|
||||
@@ -26,9 +43,12 @@ pub enum Event {
|
||||
|
||||
pub struct Room {
|
||||
id: u64,
|
||||
live_kit: Option<LiveKitRoom>,
|
||||
status: RoomStatus,
|
||||
shared_projects: HashSet<WeakModelHandle<Project>>,
|
||||
joined_projects: HashSet<WeakModelHandle<Project>>,
|
||||
local_participant: LocalParticipant,
|
||||
remote_participants: BTreeMap<PeerId, RemoteParticipant>,
|
||||
remote_participants: BTreeMap<u64, RemoteParticipant>,
|
||||
pending_participants: Vec<Arc<User>>,
|
||||
participant_user_ids: HashSet<u64>,
|
||||
pending_call_count: usize,
|
||||
@@ -37,41 +57,87 @@ pub struct Room {
|
||||
user_store: ModelHandle<UserStore>,
|
||||
subscriptions: Vec<client::Subscription>,
|
||||
pending_room_update: Option<Task<()>>,
|
||||
maintain_connection: Option<Task<Option<()>>>,
|
||||
}
|
||||
|
||||
impl Entity for Room {
|
||||
type Event = Event;
|
||||
|
||||
fn release(&mut self, _: &mut MutableAppContext) {
|
||||
self.client.send(proto::LeaveRoom { id: self.id }).log_err();
|
||||
if self.status.is_online() {
|
||||
log::info!("room was released, sending leave message");
|
||||
let _ = self.client.send(proto::LeaveRoom {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Room {
|
||||
fn new(
|
||||
id: u64,
|
||||
live_kit_connection_info: Option<proto::LiveKitConnectionInfo>,
|
||||
client: Arc<Client>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let mut client_status = client.status();
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
let is_connected = client_status
|
||||
.next()
|
||||
.await
|
||||
.map_or(false, |s| s.is_connected());
|
||||
// Even if we're initially connected, any future change of the status means we momentarily disconnected.
|
||||
if !is_connected || client_status.next().await.is_some() {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
let _ = this.update(&mut cx, |this, cx| this.leave(cx));
|
||||
let live_kit_room = if let Some(connection_info) = live_kit_connection_info {
|
||||
let room = live_kit_client::Room::new();
|
||||
let mut status = room.status();
|
||||
// Consume the initial status of the room.
|
||||
let _ = status.try_recv();
|
||||
let _maintain_room = cx.spawn_weak(|this, mut cx| async move {
|
||||
while let Some(status) = status.next().await {
|
||||
let this = if let Some(this) = this.upgrade(&cx) {
|
||||
this
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
||||
if status == live_kit_client::ConnectionState::Disconnected {
|
||||
this.update(&mut cx, |this, cx| this.leave(cx).log_err());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
||||
let mut track_changes = room.remote_video_track_updates();
|
||||
let _maintain_tracks = cx.spawn_weak(|this, mut cx| async move {
|
||||
while let Some(track_change) = track_changes.next().await {
|
||||
let this = if let Some(this) = this.upgrade(&cx) {
|
||||
this
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.remote_video_track_updated(track_change, cx).log_err()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
cx.foreground()
|
||||
.spawn(room.connect(&connection_info.server_url, &connection_info.token))
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
Some(LiveKitRoom {
|
||||
room,
|
||||
screen_track: ScreenTrack::None,
|
||||
next_publish_id: 0,
|
||||
_maintain_room,
|
||||
_maintain_tracks,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let maintain_connection =
|
||||
cx.spawn_weak(|this, cx| Self::maintain_connection(this, client.clone(), cx).log_err());
|
||||
|
||||
Self {
|
||||
id,
|
||||
live_kit: live_kit_room,
|
||||
status: RoomStatus::Online,
|
||||
shared_projects: Default::default(),
|
||||
joined_projects: Default::default(),
|
||||
participant_user_ids: Default::default(),
|
||||
local_participant: Default::default(),
|
||||
remote_participants: Default::default(),
|
||||
@@ -82,11 +148,12 @@ impl Room {
|
||||
pending_room_update: None,
|
||||
client,
|
||||
user_store,
|
||||
maintain_connection: Some(maintain_connection),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create(
|
||||
recipient_user_id: u64,
|
||||
called_user_id: u64,
|
||||
initial_project: Option<ModelHandle<Project>>,
|
||||
client: Arc<Client>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
@@ -94,7 +161,16 @@ impl Room {
|
||||
) -> Task<Result<ModelHandle<Self>>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let response = client.request(proto::CreateRoom {}).await?;
|
||||
let room = cx.add_model(|cx| Self::new(response.id, client, user_store, cx));
|
||||
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
|
||||
let room = cx.add_model(|cx| {
|
||||
Self::new(
|
||||
room_proto.id,
|
||||
response.live_kit_connection_info,
|
||||
client,
|
||||
user_store,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
||||
let initial_project_id = room
|
||||
@@ -110,7 +186,7 @@ impl Room {
|
||||
match room
|
||||
.update(&mut cx, |room, cx| {
|
||||
room.leave_when_empty = true;
|
||||
room.call(recipient_user_id, initial_project_id, cx)
|
||||
room.call(called_user_id, initial_project_id, cx)
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -130,7 +206,15 @@ impl Room {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let response = client.request(proto::JoinRoom { id: room_id }).await?;
|
||||
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
|
||||
let room = cx.add_model(|cx| Self::new(room_id, client, user_store, cx));
|
||||
let room = cx.add_model(|cx| {
|
||||
Self::new(
|
||||
room_id,
|
||||
response.live_kit_connection_info,
|
||||
client,
|
||||
user_store,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
room.update(&mut cx, |room, cx| {
|
||||
room.leave_when_empty = true;
|
||||
room.apply_room_update(room_proto, cx)?;
|
||||
@@ -155,15 +239,194 @@ impl Room {
|
||||
|
||||
cx.notify();
|
||||
cx.emit(Event::Left);
|
||||
log::info!("leaving room");
|
||||
|
||||
for project in self.shared_projects.drain() {
|
||||
if let Some(project) = project.upgrade(cx) {
|
||||
project.update(cx, |project, cx| {
|
||||
project.unshare(cx).log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
for project in self.joined_projects.drain() {
|
||||
if let Some(project) = project.upgrade(cx) {
|
||||
project.update(cx, |project, cx| {
|
||||
project.disconnected_from_host(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.status = RoomStatus::Offline;
|
||||
self.remote_participants.clear();
|
||||
self.pending_participants.clear();
|
||||
self.participant_user_ids.clear();
|
||||
self.subscriptions.clear();
|
||||
self.client.send(proto::LeaveRoom { id: self.id })?;
|
||||
self.live_kit.take();
|
||||
self.pending_room_update.take();
|
||||
self.maintain_connection.take();
|
||||
self.client.send(proto::LeaveRoom {})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn maintain_connection(
|
||||
this: WeakModelHandle<Self>,
|
||||
client: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut client_status = client.status();
|
||||
loop {
|
||||
let is_connected = client_status
|
||||
.next()
|
||||
.await
|
||||
.map_or(false, |s| s.is_connected());
|
||||
|
||||
// Even if we're initially connected, any future change of the status means we momentarily disconnected.
|
||||
if !is_connected || client_status.next().await.is_some() {
|
||||
log::info!("detected client disconnection");
|
||||
this.upgrade(&cx)
|
||||
.ok_or_else(|| anyhow!("room was dropped"))?
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.status = RoomStatus::Rejoining;
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
// Wait for client to re-establish a connection to the server.
|
||||
{
|
||||
let mut reconnection_timeout = cx.background().timer(RECONNECT_TIMEOUT).fuse();
|
||||
let client_reconnection = async {
|
||||
let mut remaining_attempts = 3;
|
||||
while remaining_attempts > 0 {
|
||||
log::info!(
|
||||
"waiting for client status change, remaining attempts {}",
|
||||
remaining_attempts
|
||||
);
|
||||
let Some(status) = client_status.next().await else { break };
|
||||
if status.is_connected() {
|
||||
log::info!("client reconnected, attempting to rejoin room");
|
||||
|
||||
let Some(this) = this.upgrade(&cx) else { break };
|
||||
if this
|
||||
.update(&mut cx, |this, cx| this.rejoin(cx))
|
||||
.await
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
remaining_attempts -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
.fuse();
|
||||
futures::pin_mut!(client_reconnection);
|
||||
|
||||
futures::select_biased! {
|
||||
reconnected = client_reconnection => {
|
||||
if reconnected {
|
||||
log::info!("successfully reconnected to room");
|
||||
// If we successfully joined the room, go back around the loop
|
||||
// waiting for future connection status changes.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ = reconnection_timeout => {
|
||||
log::info!("room reconnection timeout expired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The client failed to re-establish a connection to the server
|
||||
// or an error occurred while trying to re-join the room. Either way
|
||||
// we leave the room and return an error.
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
log::info!("reconnection failed, leaving room");
|
||||
let _ = this.update(&mut cx, |this, cx| this.leave(cx));
|
||||
}
|
||||
return Err(anyhow!(
|
||||
"can't reconnect to room: client failed to re-establish connection"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rejoin(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
let mut projects = HashMap::default();
|
||||
let mut reshared_projects = Vec::new();
|
||||
let mut rejoined_projects = Vec::new();
|
||||
self.shared_projects.retain(|project| {
|
||||
if let Some(handle) = project.upgrade(cx) {
|
||||
let project = handle.read(cx);
|
||||
if let Some(project_id) = project.remote_id() {
|
||||
projects.insert(project_id, handle.clone());
|
||||
reshared_projects.push(proto::UpdateProject {
|
||||
project_id,
|
||||
worktrees: project.worktree_metadata_protos(cx),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
self.joined_projects.retain(|project| {
|
||||
if let Some(handle) = project.upgrade(cx) {
|
||||
let project = handle.read(cx);
|
||||
if let Some(project_id) = project.remote_id() {
|
||||
projects.insert(project_id, handle.clone());
|
||||
rejoined_projects.push(proto::RejoinProject {
|
||||
id: project_id,
|
||||
worktrees: project
|
||||
.worktrees(cx)
|
||||
.map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
proto::RejoinWorktree {
|
||||
id: worktree.id().to_proto(),
|
||||
scan_id: worktree.completed_scan_id() as u64,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
|
||||
let response = self.client.request(proto::RejoinRoom {
|
||||
id: self.id,
|
||||
reshared_projects,
|
||||
rejoined_projects,
|
||||
});
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let response = response.await?;
|
||||
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.status = RoomStatus::Online;
|
||||
this.apply_room_update(room_proto, cx)?;
|
||||
|
||||
for reshared_project in response.reshared_projects {
|
||||
if let Some(project) = projects.get(&reshared_project.id) {
|
||||
project.update(cx, |project, cx| {
|
||||
project.reshared(reshared_project, cx).log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for rejoined_project in response.rejoined_projects {
|
||||
if let Some(project) = projects.get(&rejoined_project.id) {
|
||||
project.update(cx, |project, cx| {
|
||||
project.rejoined(rejoined_project, cx).log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u64 {
|
||||
self.id
|
||||
}
|
||||
@@ -176,10 +439,16 @@ impl Room {
|
||||
&self.local_participant
|
||||
}
|
||||
|
||||
pub fn remote_participants(&self) -> &BTreeMap<PeerId, RemoteParticipant> {
|
||||
pub fn remote_participants(&self) -> &BTreeMap<u64, RemoteParticipant> {
|
||||
&self.remote_participants
|
||||
}
|
||||
|
||||
pub fn remote_participant_for_peer_id(&self, peer_id: PeerId) -> Option<&RemoteParticipant> {
|
||||
self.remote_participants
|
||||
.values()
|
||||
.find(|p| p.peer_id == peer_id)
|
||||
}
|
||||
|
||||
pub fn pending_participants(&self) -> &[Arc<User>] {
|
||||
&self.pending_participants
|
||||
}
|
||||
@@ -213,6 +482,11 @@ impl Room {
|
||||
.position(|participant| Some(participant.user_id) == self.client.user_id());
|
||||
let local_participant = local_participant_ix.map(|ix| room.participants.swap_remove(ix));
|
||||
|
||||
let pending_participant_user_ids = room
|
||||
.pending_participants
|
||||
.iter()
|
||||
.map(|p| p.user_id)
|
||||
.collect::<Vec<_>>();
|
||||
let remote_participant_user_ids = room
|
||||
.participants
|
||||
.iter()
|
||||
@@ -222,7 +496,7 @@ impl Room {
|
||||
self.user_store.update(cx, move |user_store, cx| {
|
||||
(
|
||||
user_store.get_users(remote_participant_user_ids, cx),
|
||||
user_store.get_users(room.pending_participant_user_ids, cx),
|
||||
user_store.get_users(pending_participant_user_ids, cx),
|
||||
)
|
||||
});
|
||||
self.pending_room_update = Some(cx.spawn(|this, mut cx| async move {
|
||||
@@ -240,12 +514,12 @@ impl Room {
|
||||
|
||||
if let Some(participants) = remote_participants.log_err() {
|
||||
for (participant, user) in room.participants.into_iter().zip(participants) {
|
||||
let peer_id = PeerId(participant.peer_id);
|
||||
let Some(peer_id) = participant.peer_id else { continue };
|
||||
this.participant_user_ids.insert(participant.user_id);
|
||||
|
||||
let old_projects = this
|
||||
.remote_participants
|
||||
.get(&peer_id)
|
||||
.get(&participant.user_id)
|
||||
.into_iter()
|
||||
.flat_map(|existing| &existing.projects)
|
||||
.map(|project| project.id)
|
||||
@@ -267,24 +541,66 @@ impl Room {
|
||||
}
|
||||
|
||||
for unshared_project_id in old_projects.difference(&new_projects) {
|
||||
this.joined_projects.retain(|project| {
|
||||
if let Some(project) = project.upgrade(cx) {
|
||||
project.update(cx, |project, cx| {
|
||||
if project.remote_id() == Some(*unshared_project_id) {
|
||||
project.disconnected_from_host(cx);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
cx.emit(Event::RemoteProjectUnshared {
|
||||
project_id: *unshared_project_id,
|
||||
});
|
||||
}
|
||||
|
||||
this.remote_participants.insert(
|
||||
peer_id,
|
||||
RemoteParticipant {
|
||||
user: user.clone(),
|
||||
projects: participant.projects,
|
||||
location: ParticipantLocation::from_proto(participant.location)
|
||||
.unwrap_or(ParticipantLocation::External),
|
||||
},
|
||||
);
|
||||
let location = ParticipantLocation::from_proto(participant.location)
|
||||
.unwrap_or(ParticipantLocation::External);
|
||||
if let Some(remote_participant) =
|
||||
this.remote_participants.get_mut(&participant.user_id)
|
||||
{
|
||||
remote_participant.projects = participant.projects;
|
||||
remote_participant.peer_id = peer_id;
|
||||
if location != remote_participant.location {
|
||||
remote_participant.location = location;
|
||||
cx.emit(Event::ParticipantLocationChanged {
|
||||
participant_id: peer_id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.remote_participants.insert(
|
||||
participant.user_id,
|
||||
RemoteParticipant {
|
||||
user: user.clone(),
|
||||
peer_id,
|
||||
projects: participant.projects,
|
||||
location,
|
||||
tracks: Default::default(),
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(live_kit) = this.live_kit.as_ref() {
|
||||
let tracks =
|
||||
live_kit.room.remote_video_tracks(&peer_id.to_string());
|
||||
for track in tracks {
|
||||
this.remote_video_track_updated(
|
||||
RemoteVideoTrackUpdate::Subscribed(track),
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.remote_participants.retain(|_, participant| {
|
||||
if this.participant_user_ids.contains(&participant.user.id) {
|
||||
this.remote_participants.retain(|user_id, participant| {
|
||||
if this.participant_user_ids.contains(user_id) {
|
||||
true
|
||||
} else {
|
||||
for project in &participant.projects {
|
||||
@@ -306,6 +622,7 @@ impl Room {
|
||||
|
||||
this.pending_room_update.take();
|
||||
if this.should_leave() {
|
||||
log::info!("room is empty, leaving");
|
||||
let _ = this.leave(cx);
|
||||
}
|
||||
|
||||
@@ -318,15 +635,60 @@ impl Room {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remote_video_track_updated(
|
||||
&mut self,
|
||||
change: RemoteVideoTrackUpdate,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<()> {
|
||||
match change {
|
||||
RemoteVideoTrackUpdate::Subscribed(track) => {
|
||||
let user_id = track.publisher_id().parse()?;
|
||||
let track_id = track.sid().to_string();
|
||||
let participant = self
|
||||
.remote_participants
|
||||
.get_mut(&user_id)
|
||||
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
|
||||
participant.tracks.insert(
|
||||
track_id.clone(),
|
||||
Arc::new(RemoteVideoTrack {
|
||||
live_kit_track: track,
|
||||
}),
|
||||
);
|
||||
cx.emit(Event::RemoteVideoTracksChanged {
|
||||
participant_id: participant.peer_id,
|
||||
});
|
||||
}
|
||||
RemoteVideoTrackUpdate::Unsubscribed {
|
||||
publisher_id,
|
||||
track_id,
|
||||
} => {
|
||||
let user_id = publisher_id.parse()?;
|
||||
let participant = self
|
||||
.remote_participants
|
||||
.get_mut(&user_id)
|
||||
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
|
||||
participant.tracks.remove(&track_id);
|
||||
cx.emit(Event::RemoteVideoTracksChanged {
|
||||
participant_id: participant.peer_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_invariants(&self) {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
{
|
||||
for participant in self.remote_participants.values() {
|
||||
assert!(self.participant_user_ids.contains(&participant.user.id));
|
||||
assert_ne!(participant.user.id, self.client.user_id().unwrap());
|
||||
}
|
||||
|
||||
for participant in &self.pending_participants {
|
||||
assert!(self.participant_user_ids.contains(&participant.id));
|
||||
assert_ne!(participant.id, self.client.user_id().unwrap());
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
@@ -338,7 +700,7 @@ impl Room {
|
||||
|
||||
pub(crate) fn call(
|
||||
&mut self,
|
||||
recipient_user_id: u64,
|
||||
called_user_id: u64,
|
||||
initial_project_id: Option<u64>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
@@ -354,7 +716,7 @@ impl Room {
|
||||
let result = client
|
||||
.request(proto::Call {
|
||||
room_id,
|
||||
recipient_user_id,
|
||||
called_user_id,
|
||||
initial_project_id,
|
||||
})
|
||||
.await;
|
||||
@@ -369,6 +731,32 @@ impl Room {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn join_project(
|
||||
&mut self,
|
||||
id: u64,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<ModelHandle<Project>>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let project =
|
||||
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.joined_projects.retain(|project| {
|
||||
if let Some(project) = project.upgrade(cx) {
|
||||
!project.read(cx).is_read_only()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
this.joined_projects.insert(project.downgrade());
|
||||
});
|
||||
Ok(project)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn share_project(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
@@ -380,30 +768,18 @@ impl Room {
|
||||
|
||||
let request = self.client.request(proto::ShareProject {
|
||||
room_id: self.id(),
|
||||
worktrees: project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
proto::WorktreeMetadata {
|
||||
id: worktree.id().to_proto(),
|
||||
root_name: worktree.root_name().into(),
|
||||
visible: worktree.is_visible(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
worktrees: project.read(cx).worktree_metadata_protos(cx),
|
||||
});
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let response = request.await?;
|
||||
|
||||
project.update(&mut cx, |project, cx| {
|
||||
project
|
||||
.shared(response.project_id, cx)
|
||||
.detach_and_log_err(cx)
|
||||
});
|
||||
project.shared(response.project_id, cx)
|
||||
})?;
|
||||
|
||||
// If the user's location is in this project, it changes from UnsharedProject to SharedProject.
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.shared_projects.insert(project.downgrade());
|
||||
let active_project = this.local_participant.active_project.as_ref();
|
||||
if active_project.map_or(false, |location| *location == project) {
|
||||
this.set_location(Some(&project), cx)
|
||||
@@ -417,7 +793,7 @@ impl Room {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_location(
|
||||
pub(crate) fn set_location(
|
||||
&mut self,
|
||||
project: Option<&ModelHandle<Project>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
@@ -457,11 +833,146 @@ impl Room {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_screen_sharing(&self) -> bool {
|
||||
self.live_kit.as_ref().map_or(false, |live_kit| {
|
||||
!matches!(live_kit.screen_track, ScreenTrack::None)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn share_screen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
if self.status.is_offline() {
|
||||
return Task::ready(Err(anyhow!("room is offline")));
|
||||
} else if self.is_screen_sharing() {
|
||||
return Task::ready(Err(anyhow!("screen was already shared")));
|
||||
}
|
||||
|
||||
let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
|
||||
let publish_id = post_inc(&mut live_kit.next_publish_id);
|
||||
live_kit.screen_track = ScreenTrack::Pending { publish_id };
|
||||
cx.notify();
|
||||
(live_kit.room.display_sources(), publish_id)
|
||||
} else {
|
||||
return Task::ready(Err(anyhow!("live-kit was not initialized")));
|
||||
};
|
||||
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
let publish_track = async {
|
||||
let displays = displays.await?;
|
||||
let display = displays
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("no display found"))?;
|
||||
let track = LocalVideoTrack::screen_share_for_display(&display);
|
||||
this.upgrade(&cx)
|
||||
.ok_or_else(|| anyhow!("room was dropped"))?
|
||||
.read_with(&cx, |this, _| {
|
||||
this.live_kit
|
||||
.as_ref()
|
||||
.map(|live_kit| live_kit.room.publish_video_track(&track))
|
||||
})
|
||||
.ok_or_else(|| anyhow!("live-kit was not initialized"))?
|
||||
.await
|
||||
};
|
||||
|
||||
let publication = publish_track.await;
|
||||
this.upgrade(&cx)
|
||||
.ok_or_else(|| anyhow!("room was dropped"))?
|
||||
.update(&mut cx, |this, cx| {
|
||||
let live_kit = this
|
||||
.live_kit
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
||||
|
||||
let canceled = if let ScreenTrack::Pending {
|
||||
publish_id: cur_publish_id,
|
||||
} = &live_kit.screen_track
|
||||
{
|
||||
*cur_publish_id != publish_id
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
match publication {
|
||||
Ok(publication) => {
|
||||
if canceled {
|
||||
live_kit.room.unpublish_track(publication);
|
||||
} else {
|
||||
live_kit.screen_track = ScreenTrack::Published(publication);
|
||||
cx.notify();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
if canceled {
|
||||
Ok(())
|
||||
} else {
|
||||
live_kit.screen_track = ScreenTrack::None;
|
||||
cx.notify();
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unshare_screen(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
|
||||
if self.status.is_offline() {
|
||||
return Err(anyhow!("room is offline"));
|
||||
}
|
||||
|
||||
let live_kit = self
|
||||
.live_kit
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
||||
match mem::take(&mut live_kit.screen_track) {
|
||||
ScreenTrack::None => Err(anyhow!("screen was not shared")),
|
||||
ScreenTrack::Pending { .. } => {
|
||||
cx.notify();
|
||||
Ok(())
|
||||
}
|
||||
ScreenTrack::Published(track) => {
|
||||
live_kit.room.unpublish_track(track);
|
||||
cx.notify();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn set_display_sources(&self, sources: Vec<live_kit_client::MacOSDisplay>) {
|
||||
self.live_kit
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.room
|
||||
.set_display_sources(sources);
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveKitRoom {
|
||||
room: Arc<live_kit_client::Room>,
|
||||
screen_track: ScreenTrack,
|
||||
next_publish_id: usize,
|
||||
_maintain_room: Task<()>,
|
||||
_maintain_tracks: Task<()>,
|
||||
}
|
||||
|
||||
enum ScreenTrack {
|
||||
None,
|
||||
Pending { publish_id: usize },
|
||||
Published(LocalTrackPublication),
|
||||
}
|
||||
|
||||
impl Default for ScreenTrack {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RoomStatus {
|
||||
Online,
|
||||
Rejoining,
|
||||
Offline,
|
||||
}
|
||||
|
||||
@@ -469,4 +980,8 @@ impl RoomStatus {
|
||||
pub fn is_offline(&self) -> bool {
|
||||
matches!(self, RoomStatus::Offline)
|
||||
}
|
||||
|
||||
pub fn is_online(&self) -> bool {
|
||||
matches!(self, RoomStatus::Online)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
[package]
|
||||
name = "capture"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "An example of screen capture"
|
||||
|
||||
[dependencies]
|
||||
gpui = { path = "../gpui" }
|
||||
live_kit = { path = "../live_kit" }
|
||||
media = { path = "../media" }
|
||||
|
||||
anyhow = "1.0.38"
|
||||
block = "0.1"
|
||||
bytes = "1.2"
|
||||
byteorder = "1.4"
|
||||
cocoa = "0.24"
|
||||
core-foundation = "0.9.3"
|
||||
core-graphics = "0.22.3"
|
||||
foreign-types = "0.3"
|
||||
futures = "0.3"
|
||||
hmac = "0.12"
|
||||
jwt = "0.16"
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
objc = "0.2"
|
||||
parking_lot = "0.11.1"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
sha2 = "0.10"
|
||||
simplelog = "0.9"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.59.2"
|
||||
@@ -1,7 +0,0 @@
|
||||
fn main() {
|
||||
// Find WebRTC.framework as a sibling of the executable when running outside of an application bundle
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
|
||||
|
||||
// Register exported Objective-C selectors, protocols, etc
|
||||
println!("cargo:rustc-link-arg=-Wl,-ObjC");
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::SignWithKey;
|
||||
use serde::Serialize;
|
||||
use sha2::Sha256;
|
||||
use std::{
|
||||
ops::Add,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
static DEFAULT_TTL: Duration = Duration::from_secs(6 * 60 * 60); // 6 hours
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ClaimGrants<'a> {
|
||||
iss: &'a str,
|
||||
sub: &'a str,
|
||||
iat: u64,
|
||||
exp: u64,
|
||||
nbf: u64,
|
||||
jwtid: &'a str,
|
||||
video: VideoGrant<'a>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct VideoGrant<'a> {
|
||||
room_create: Option<bool>,
|
||||
room_join: Option<bool>,
|
||||
room_list: Option<bool>,
|
||||
room_record: Option<bool>,
|
||||
room_admin: Option<bool>,
|
||||
room: Option<&'a str>,
|
||||
can_publish: Option<bool>,
|
||||
can_subscribe: Option<bool>,
|
||||
can_publish_data: Option<bool>,
|
||||
hidden: Option<bool>,
|
||||
recorder: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn create_token(
|
||||
api_key: &str,
|
||||
secret_key: &str,
|
||||
room_name: &str,
|
||||
participant_name: &str,
|
||||
) -> Result<String> {
|
||||
let secret_key: Hmac<Sha256> = Hmac::new_from_slice(secret_key.as_bytes())?;
|
||||
|
||||
let now = SystemTime::now();
|
||||
|
||||
let claims = ClaimGrants {
|
||||
iss: api_key,
|
||||
sub: participant_name,
|
||||
iat: now.duration_since(UNIX_EPOCH).unwrap().as_secs(),
|
||||
exp: now
|
||||
.add(DEFAULT_TTL)
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
nbf: 0,
|
||||
jwtid: participant_name,
|
||||
video: VideoGrant {
|
||||
room: Some(room_name),
|
||||
room_join: Some(true),
|
||||
can_publish: Some(true),
|
||||
can_subscribe: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
Ok(claims.sign_with_key(&secret_key)?)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
mod live_kit_token;
|
||||
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::{Canvas, *},
|
||||
keymap::Binding,
|
||||
platform::current::Surface,
|
||||
Menu, MenuItem, ViewContext,
|
||||
};
|
||||
use live_kit::{LocalVideoTrack, Room};
|
||||
use log::LevelFilter;
|
||||
use media::core_video::CVImageBuffer;
|
||||
use postage::watch;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::sync::Arc;
|
||||
|
||||
actions!(capture, [Quit]);
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||
|
||||
gpui::App::new(()).unwrap().run(|cx| {
|
||||
cx.platform().activate(true);
|
||||
cx.add_global_action(quit);
|
||||
|
||||
cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
|
||||
cx.set_menus(vec![Menu {
|
||||
name: "Zed",
|
||||
items: vec![MenuItem::Action {
|
||||
name: "Quit",
|
||||
action: Box::new(Quit),
|
||||
}],
|
||||
}]);
|
||||
|
||||
let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap();
|
||||
let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap();
|
||||
let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let user1_token = live_kit_token::create_token(
|
||||
&live_kit_key,
|
||||
&live_kit_secret,
|
||||
"test-room",
|
||||
"test-participant-1",
|
||||
)
|
||||
.unwrap();
|
||||
let room1 = Room::new();
|
||||
room1.connect(&live_kit_url, &user1_token).await.unwrap();
|
||||
|
||||
let user2_token = live_kit_token::create_token(
|
||||
&live_kit_key,
|
||||
&live_kit_secret,
|
||||
"test-room",
|
||||
"test-participant-2",
|
||||
)
|
||||
.unwrap();
|
||||
let room2 = Room::new();
|
||||
room2.connect(&live_kit_url, &user2_token).await.unwrap();
|
||||
cx.add_window(Default::default(), |cx| ScreenCaptureView::new(room2, cx));
|
||||
|
||||
let windows = live_kit::list_windows();
|
||||
let window = windows
|
||||
.iter()
|
||||
.find(|w| w.owner_name.as_deref() == Some("Safari"))
|
||||
.unwrap();
|
||||
let track = LocalVideoTrack::screen_share_for_window(window.id);
|
||||
room1.publish_video_track(&track).await.unwrap();
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
}
|
||||
|
||||
struct ScreenCaptureView {
|
||||
image_buffer: Option<CVImageBuffer>,
|
||||
_room: Arc<Room>,
|
||||
}
|
||||
|
||||
impl gpui::Entity for ScreenCaptureView {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl ScreenCaptureView {
|
||||
pub fn new(room: Arc<Room>, cx: &mut ViewContext<Self>) -> Self {
|
||||
let mut remote_video_tracks = room.remote_video_tracks();
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
if let Some(video_track) = remote_video_tracks.next().await {
|
||||
let (mut frames_tx, mut frames_rx) = watch::channel_with(None);
|
||||
video_track.add_renderer(move |frame| *frames_tx.borrow_mut() = Some(frame));
|
||||
|
||||
while let Some(frame) = frames_rx.next().await {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.image_buffer = frame;
|
||||
cx.notify();
|
||||
});
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {
|
||||
image_buffer: None,
|
||||
_room: room,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl gpui::View for ScreenCaptureView {
|
||||
fn ui_name() -> &'static str {
|
||||
"View"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
|
||||
let image_buffer = self.image_buffer.clone();
|
||||
let canvas = Canvas::new(move |bounds, _, cx| {
|
||||
if let Some(image_buffer) = image_buffer.clone() {
|
||||
cx.scene.push_surface(Surface {
|
||||
bounds,
|
||||
image_buffer,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(image_buffer) = self.image_buffer.as_ref() {
|
||||
canvas
|
||||
.constrained()
|
||||
.with_width(image_buffer.width() as f32)
|
||||
.with_height(image_buffer.height() as f32)
|
||||
.aligned()
|
||||
.boxed()
|
||||
} else {
|
||||
canvas.boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
|
||||
cx.platform().quit();
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "chat_panel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/chat_panel.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
client = { path = "../client" }
|
||||
editor = { path = "../editor" }
|
||||
gpui = { path = "../gpui" }
|
||||
menu = { path = "../menu" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
workspace = { path = "../workspace" }
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
time = { version = "0.3", features = ["serde", "serde-well-known"] }
|
||||
@@ -1,433 +0,0 @@
|
||||
use client::{
|
||||
channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
|
||||
Client,
|
||||
};
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::*,
|
||||
platform::CursorStyle,
|
||||
views::{ItemType, Select, SelectStyle},
|
||||
AnyViewHandle, AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
|
||||
Subscription, Task, View, ViewContext, ViewHandle,
|
||||
};
|
||||
use menu::Confirm;
|
||||
use postage::prelude::Stream;
|
||||
use settings::{Settings, SoftWrap};
|
||||
use std::sync::Arc;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
|
||||
const MESSAGE_LOADING_THRESHOLD: usize = 50;
|
||||
|
||||
pub struct ChatPanel {
|
||||
rpc: Arc<Client>,
|
||||
channel_list: ModelHandle<ChannelList>,
|
||||
active_channel: Option<(ModelHandle<Channel>, Subscription)>,
|
||||
message_list: ListState,
|
||||
input_editor: ViewHandle<Editor>,
|
||||
channel_select: ViewHandle<Select>,
|
||||
local_timezone: UtcOffset,
|
||||
_observe_status: Task<()>,
|
||||
}
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
actions!(chat_panel, [LoadMoreMessages]);
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(ChatPanel::send);
|
||||
cx.add_action(ChatPanel::load_more_messages);
|
||||
}
|
||||
|
||||
impl ChatPanel {
|
||||
pub fn new(
|
||||
rpc: Arc<Client>,
|
||||
channel_list: ModelHandle<ChannelList>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let input_editor = cx.add_view(|cx| {
|
||||
let mut editor =
|
||||
Editor::auto_height(4, Some(|theme| theme.chat_panel.input_editor.clone()), cx);
|
||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
editor
|
||||
});
|
||||
let channel_select = cx.add_view(|cx| {
|
||||
let channel_list = channel_list.clone();
|
||||
Select::new(0, cx, {
|
||||
move |ix, item_type, is_hovered, cx| {
|
||||
Self::render_channel_name(
|
||||
&channel_list,
|
||||
ix,
|
||||
item_type,
|
||||
is_hovered,
|
||||
&cx.global::<Settings>().theme.chat_panel.channel_select,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.with_style(move |cx| {
|
||||
let theme = &cx.global::<Settings>().theme.chat_panel.channel_select;
|
||||
SelectStyle {
|
||||
header: theme.header.container,
|
||||
menu: theme.menu,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let mut message_list = ListState::new(0, Orientation::Bottom, 1000., cx, {
|
||||
let this = cx.weak_handle();
|
||||
move |_, ix, cx| {
|
||||
let this = this.upgrade(cx).unwrap().read(cx);
|
||||
let message = this.active_channel.as_ref().unwrap().0.read(cx).message(ix);
|
||||
this.render_message(message, cx)
|
||||
}
|
||||
});
|
||||
message_list.set_scroll_handler(|visible_range, cx| {
|
||||
if visible_range.start < MESSAGE_LOADING_THRESHOLD {
|
||||
cx.dispatch_action(LoadMoreMessages);
|
||||
}
|
||||
});
|
||||
let _observe_status = cx.spawn_weak(|this, mut cx| {
|
||||
let mut status = rpc.status();
|
||||
async move {
|
||||
while (status.recv().await).is_some() {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |_, cx| cx.notify());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut this = Self {
|
||||
rpc,
|
||||
channel_list,
|
||||
active_channel: Default::default(),
|
||||
message_list,
|
||||
input_editor,
|
||||
channel_select,
|
||||
local_timezone: cx.platform().local_timezone(),
|
||||
_observe_status,
|
||||
};
|
||||
|
||||
this.init_active_channel(cx);
|
||||
cx.observe(&this.channel_list, |this, _, cx| {
|
||||
this.init_active_channel(cx);
|
||||
})
|
||||
.detach();
|
||||
cx.observe(&this.channel_select, |this, channel_select, cx| {
|
||||
let selected_ix = channel_select.read(cx).selected_index();
|
||||
let selected_channel = this.channel_list.update(cx, |channel_list, cx| {
|
||||
let available_channels = channel_list.available_channels()?;
|
||||
let channel_id = available_channels.get(selected_ix)?.id;
|
||||
channel_list.get_channel(channel_id, cx)
|
||||
});
|
||||
if let Some(selected_channel) = selected_channel {
|
||||
this.set_active_channel(selected_channel, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn init_active_channel(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let (active_channel, channel_count) = self.channel_list.update(cx, |list, cx| {
|
||||
let channel_count;
|
||||
let mut active_channel = None;
|
||||
|
||||
if let Some(available_channels) = list.available_channels() {
|
||||
channel_count = available_channels.len();
|
||||
if self.active_channel.is_none() {
|
||||
if let Some(channel_id) = available_channels.first().map(|channel| channel.id) {
|
||||
active_channel = list.get_channel(channel_id, cx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channel_count = 0;
|
||||
}
|
||||
|
||||
(active_channel, channel_count)
|
||||
});
|
||||
|
||||
if let Some(active_channel) = active_channel {
|
||||
self.set_active_channel(active_channel, cx);
|
||||
} else {
|
||||
self.message_list.reset(0);
|
||||
self.active_channel = None;
|
||||
}
|
||||
|
||||
self.channel_select.update(cx, |select, cx| {
|
||||
select.set_item_count(channel_count, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) {
|
||||
if self.active_channel.as_ref().map(|e| &e.0) != Some(&channel) {
|
||||
{
|
||||
let channel = channel.read(cx);
|
||||
self.message_list.reset(channel.message_count());
|
||||
let placeholder = format!("Message #{}", channel.name());
|
||||
self.input_editor.update(cx, move |editor, cx| {
|
||||
editor.set_placeholder_text(placeholder, cx);
|
||||
});
|
||||
}
|
||||
let subscription = cx.subscribe(&channel, Self::channel_did_change);
|
||||
self.active_channel = Some((channel, subscription));
|
||||
}
|
||||
}
|
||||
|
||||
fn channel_did_change(
|
||||
&mut self,
|
||||
_: ModelHandle<Channel>,
|
||||
event: &ChannelEvent,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
ChannelEvent::MessagesUpdated {
|
||||
old_range,
|
||||
new_count,
|
||||
} => {
|
||||
self.message_list.splice(old_range.clone(), *new_count);
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_channel(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let theme = &cx.global::<Settings>().theme;
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Container::new(ChildView::new(&self.channel_select, cx).boxed())
|
||||
.with_style(theme.chat_panel.channel_select.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(self.render_active_channel_messages())
|
||||
.with_child(self.render_input_box(cx))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_active_channel_messages(&self) -> ElementBox {
|
||||
let messages = if self.active_channel.is_some() {
|
||||
List::new(self.message_list.clone()).boxed()
|
||||
} else {
|
||||
Empty::new().boxed()
|
||||
};
|
||||
|
||||
FlexItem::new(messages).flex(1., true).boxed()
|
||||
}
|
||||
|
||||
fn render_message(&self, message: &ChannelMessage, cx: &AppContext) -> ElementBox {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let settings = cx.global::<Settings>();
|
||||
let theme = if message.is_pending() {
|
||||
&settings.theme.chat_panel.pending_message
|
||||
} else {
|
||||
&settings.theme.chat_panel.message
|
||||
};
|
||||
|
||||
Container::new(
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Container::new(
|
||||
Label::new(
|
||||
message.sender.github_login.clone(),
|
||||
theme.sender.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(theme.sender.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Container::new(
|
||||
Label::new(
|
||||
format_timestamp(message.timestamp, now, self.local_timezone),
|
||||
theme.timestamp.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(theme.timestamp.container)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(Text::new(message.body.clone(), theme.body.clone()).boxed())
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(theme.container)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_input_box(&self, cx: &AppContext) -> ElementBox {
|
||||
let theme = &cx.global::<Settings>().theme;
|
||||
Container::new(ChildView::new(&self.input_editor, cx).boxed())
|
||||
.with_style(theme.chat_panel.input_editor.container)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_channel_name(
|
||||
channel_list: &ModelHandle<ChannelList>,
|
||||
ix: usize,
|
||||
item_type: ItemType,
|
||||
is_hovered: bool,
|
||||
theme: &theme::ChannelSelect,
|
||||
cx: &AppContext,
|
||||
) -> ElementBox {
|
||||
let channel = &channel_list.read(cx).available_channels().unwrap()[ix];
|
||||
let theme = match (item_type, is_hovered) {
|
||||
(ItemType::Header, _) => &theme.header,
|
||||
(ItemType::Selected, false) => &theme.active_item,
|
||||
(ItemType::Selected, true) => &theme.hovered_active_item,
|
||||
(ItemType::Unselected, false) => &theme.item,
|
||||
(ItemType::Unselected, true) => &theme.hovered_item,
|
||||
};
|
||||
Container::new(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Container::new(Label::new("#".to_string(), theme.hash.text.clone()).boxed())
|
||||
.with_style(theme.hash.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(Label::new(channel.name.clone(), theme.name.clone()).boxed())
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(theme.container)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_sign_in_prompt(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
let this = cx.handle();
|
||||
|
||||
enum SignInPromptLabel {}
|
||||
|
||||
Align::new(
|
||||
MouseEventHandler::<SignInPromptLabel>::new(0, cx, |mouse_state, _| {
|
||||
Label::new(
|
||||
"Sign in to use chat".to_string(),
|
||||
if mouse_state.hovered {
|
||||
theme.chat_panel.hovered_sign_in_prompt.clone()
|
||||
} else {
|
||||
theme.chat_panel.sign_in_prompt.clone()
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, cx| {
|
||||
let rpc = rpc.clone();
|
||||
let this = this.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
if rpc
|
||||
.authenticate_and_connect(true, &cx)
|
||||
.log_err()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
cx.update(|cx| {
|
||||
if let Some(this) = this.upgrade(cx) {
|
||||
if this.is_focused(cx) {
|
||||
this.update(cx, |this, cx| cx.focus(&this.input_editor));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
||||
if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
let body = self.input_editor.update(cx, |editor, cx| {
|
||||
let body = editor.text(cx);
|
||||
editor.clear(cx);
|
||||
body
|
||||
});
|
||||
|
||||
if let Some(task) = channel
|
||||
.update(cx, |channel, cx| channel.send_message(body, cx))
|
||||
.log_err()
|
||||
{
|
||||
task.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_more_messages(&mut self, _: &LoadMoreMessages, cx: &mut ViewContext<Self>) {
|
||||
if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
channel.update(cx, |channel, cx| {
|
||||
channel.load_more_messages(cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for ChatPanel {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl View for ChatPanel {
|
||||
fn ui_name() -> &'static str {
|
||||
"ChatPanel"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let element = if self.rpc.user_id().is_some() {
|
||||
self.render_channel(cx)
|
||||
} else {
|
||||
self.render_sign_in_prompt(cx)
|
||||
};
|
||||
let theme = &cx.global::<Settings>().theme;
|
||||
ConstrainedBox::new(
|
||||
Container::new(element)
|
||||
.with_style(theme.chat_panel.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_min_width(150.)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
if matches!(
|
||||
*self.rpc.status().borrow(),
|
||||
client::Status::Connected { .. }
|
||||
) {
|
||||
cx.focus(&self.input_editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_timestamp(
|
||||
mut timestamp: OffsetDateTime,
|
||||
mut now: OffsetDateTime,
|
||||
local_timezone: UtcOffset,
|
||||
) -> String {
|
||||
timestamp = timestamp.to_offset(local_timezone);
|
||||
now = now.to_offset(local_timezone);
|
||||
|
||||
let today = now.date();
|
||||
let date = timestamp.date();
|
||||
let mut hour = timestamp.hour();
|
||||
let mut part = "am";
|
||||
if hour > 12 {
|
||||
hour -= 12;
|
||||
part = "pm";
|
||||
}
|
||||
if date == today {
|
||||
format!("{:02}:{:02}{}", hour, timestamp.minute(), part)
|
||||
} else if date.next_day() == Some(today) {
|
||||
format!("yesterday at {:02}:{:02}{}", hour, timestamp.minute(), part)
|
||||
} else {
|
||||
format!("{:02}/{}/{}", date.month() as u32, date.day(), date.year())
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,11 @@ tiny_http = "0.8"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
url = "2.2"
|
||||
serde = { version = "*", features = ["derive"] }
|
||||
settings = { path = "../settings" }
|
||||
tempfile = "3"
|
||||
|
||||
[dev-dependencies]
|
||||
collections = { path = "../collections", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
rpc = { path = "../rpc", features = ["test-support"] }
|
||||
settings = { path = "../settings", features = ["test-support"] }
|
||||
|
||||
@@ -1,820 +0,0 @@
|
||||
use super::{
|
||||
proto,
|
||||
user::{User, UserStore},
|
||||
Client, Status, Subscription, TypedEnvelope,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use futures::lock::Mutex;
|
||||
use gpui::{
|
||||
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
|
||||
};
|
||||
use postage::prelude::Stream;
|
||||
use rand::prelude::*;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
mem,
|
||||
ops::Range,
|
||||
sync::Arc,
|
||||
};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
use time::OffsetDateTime;
|
||||
use util::{post_inc, ResultExt as _, TryFutureExt};
|
||||
|
||||
pub struct ChannelList {
|
||||
available_channels: Option<Vec<ChannelDetails>>,
|
||||
channels: HashMap<u64, WeakModelHandle<Channel>>,
|
||||
client: Arc<Client>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
_task: Task<Option<()>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ChannelDetails {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub struct Channel {
|
||||
details: ChannelDetails,
|
||||
messages: SumTree<ChannelMessage>,
|
||||
loaded_all_messages: bool,
|
||||
next_pending_message_id: usize,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
rpc: Arc<Client>,
|
||||
outgoing_messages_lock: Arc<Mutex<()>>,
|
||||
rng: StdRng,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ChannelMessage {
|
||||
pub id: ChannelMessageId,
|
||||
pub body: String,
|
||||
pub timestamp: OffsetDateTime,
|
||||
pub sender: Arc<User>,
|
||||
pub nonce: u128,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ChannelMessageId {
|
||||
Saved(u64),
|
||||
Pending(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ChannelMessageSummary {
|
||||
max_id: ChannelMessageId,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Count(usize);
|
||||
|
||||
pub enum ChannelListEvent {}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ChannelEvent {
|
||||
MessagesUpdated {
|
||||
old_range: Range<usize>,
|
||||
new_count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Entity for ChannelList {
|
||||
type Event = ChannelListEvent;
|
||||
}
|
||||
|
||||
impl ChannelList {
|
||||
pub fn new(
|
||||
user_store: ModelHandle<UserStore>,
|
||||
rpc: Arc<Client>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let _task = cx.spawn_weak(|this, mut cx| {
|
||||
let rpc = rpc.clone();
|
||||
async move {
|
||||
let mut status = rpc.status();
|
||||
while let Some((status, this)) = status.recv().await.zip(this.upgrade(&cx)) {
|
||||
match status {
|
||||
Status::Connected { .. } => {
|
||||
let response = rpc
|
||||
.request(proto::GetChannels {})
|
||||
.await
|
||||
.context("failed to fetch available channels")?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.available_channels =
|
||||
Some(response.channels.into_iter().map(Into::into).collect());
|
||||
|
||||
let mut to_remove = Vec::new();
|
||||
for (channel_id, channel) in &this.channels {
|
||||
if let Some(channel) = channel.upgrade(cx) {
|
||||
channel.update(cx, |channel, cx| channel.rejoin(cx))
|
||||
} else {
|
||||
to_remove.push(*channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
for channel_id in to_remove {
|
||||
this.channels.remove(&channel_id);
|
||||
}
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
Status::SignedOut { .. } => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.available_channels = None;
|
||||
this.channels.clear();
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
});
|
||||
|
||||
Self {
|
||||
available_channels: None,
|
||||
channels: Default::default(),
|
||||
user_store,
|
||||
client: rpc,
|
||||
_task,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available_channels(&self) -> Option<&[ChannelDetails]> {
|
||||
self.available_channels.as_deref()
|
||||
}
|
||||
|
||||
pub fn get_channel(
|
||||
&mut self,
|
||||
id: u64,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Option<ModelHandle<Channel>> {
|
||||
if let Some(channel) = self.channels.get(&id).and_then(|c| c.upgrade(cx)) {
|
||||
return Some(channel);
|
||||
}
|
||||
|
||||
let channels = self.available_channels.as_ref()?;
|
||||
let details = channels.iter().find(|details| details.id == id)?.clone();
|
||||
let channel = cx.add_model(|cx| {
|
||||
Channel::new(details, self.user_store.clone(), self.client.clone(), cx)
|
||||
});
|
||||
self.channels.insert(id, channel.downgrade());
|
||||
Some(channel)
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Channel {
|
||||
type Event = ChannelEvent;
|
||||
|
||||
fn release(&mut self, _: &mut MutableAppContext) {
|
||||
self.rpc
|
||||
.send(proto::LeaveChannel {
|
||||
channel_id: self.details.id,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn init(rpc: &Arc<Client>) {
|
||||
rpc.add_model_message_handler(Self::handle_message_sent);
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
details: ChannelDetails,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
rpc: Arc<Client>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let _subscription = rpc.add_model_for_remote_entity(details.id, cx);
|
||||
|
||||
{
|
||||
let user_store = user_store.clone();
|
||||
let rpc = rpc.clone();
|
||||
let channel_id = details.id;
|
||||
cx.spawn(|channel, mut cx| {
|
||||
async move {
|
||||
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
||||
let messages =
|
||||
messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||
let loaded_all_messages = response.done;
|
||||
|
||||
channel.update(&mut cx, |channel, cx| {
|
||||
channel.insert_messages(messages, cx);
|
||||
channel.loaded_all_messages = loaded_all_messages;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
Self {
|
||||
details,
|
||||
user_store,
|
||||
rpc,
|
||||
outgoing_messages_lock: Default::default(),
|
||||
messages: Default::default(),
|
||||
loaded_all_messages: false,
|
||||
next_pending_message_id: 0,
|
||||
rng: StdRng::from_entropy(),
|
||||
_subscription,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.details.name
|
||||
}
|
||||
|
||||
pub fn send_message(
|
||||
&mut self,
|
||||
body: String,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<Task<Result<()>>> {
|
||||
if body.is_empty() {
|
||||
Err(anyhow!("message body can't be empty"))?;
|
||||
}
|
||||
|
||||
let current_user = self
|
||||
.user_store
|
||||
.read(cx)
|
||||
.current_user()
|
||||
.ok_or_else(|| anyhow!("current_user is not present"))?;
|
||||
|
||||
let channel_id = self.details.id;
|
||||
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
|
||||
let nonce = self.rng.gen();
|
||||
self.insert_messages(
|
||||
SumTree::from_item(
|
||||
ChannelMessage {
|
||||
id: pending_id,
|
||||
body: body.clone(),
|
||||
sender: current_user,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
nonce,
|
||||
},
|
||||
&(),
|
||||
),
|
||||
cx,
|
||||
);
|
||||
let user_store = self.user_store.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
let outgoing_messages_lock = self.outgoing_messages_lock.clone();
|
||||
Ok(cx.spawn(|this, mut cx| async move {
|
||||
let outgoing_message_guard = outgoing_messages_lock.lock().await;
|
||||
let request = rpc.request(proto::SendChannelMessage {
|
||||
channel_id,
|
||||
body,
|
||||
nonce: Some(nonce.into()),
|
||||
});
|
||||
let response = request.await?;
|
||||
drop(outgoing_message_guard);
|
||||
let message = ChannelMessage::from_proto(
|
||||
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
||||
&user_store,
|
||||
&mut cx,
|
||||
)
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.insert_messages(SumTree::from_item(message, &()), cx);
|
||||
Ok(())
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn load_more_messages(&mut self, cx: &mut ModelContext<Self>) -> bool {
|
||||
if !self.loaded_all_messages {
|
||||
let rpc = self.rpc.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let channel_id = self.details.id;
|
||||
if let Some(before_message_id) =
|
||||
self.messages.first().and_then(|message| match message.id {
|
||||
ChannelMessageId::Saved(id) => Some(id),
|
||||
ChannelMessageId::Pending(_) => None,
|
||||
})
|
||||
{
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
let response = rpc
|
||||
.request(proto::GetChannelMessages {
|
||||
channel_id,
|
||||
before_message_id,
|
||||
})
|
||||
.await?;
|
||||
let loaded_all_messages = response.done;
|
||||
let messages =
|
||||
messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.loaded_all_messages = loaded_all_messages;
|
||||
this.insert_messages(messages, cx);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
})
|
||||
.detach();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
|
||||
let user_store = self.user_store.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
let channel_id = self.details.id;
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
||||
let messages = messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||
let loaded_all_messages = response.done;
|
||||
|
||||
let pending_messages = this.update(&mut cx, |this, cx| {
|
||||
if let Some((first_new_message, last_old_message)) =
|
||||
messages.first().zip(this.messages.last())
|
||||
{
|
||||
if first_new_message.id > last_old_message.id {
|
||||
let old_messages = mem::take(&mut this.messages);
|
||||
cx.emit(ChannelEvent::MessagesUpdated {
|
||||
old_range: 0..old_messages.summary().count,
|
||||
new_count: 0,
|
||||
});
|
||||
this.loaded_all_messages = loaded_all_messages;
|
||||
}
|
||||
}
|
||||
|
||||
this.insert_messages(messages, cx);
|
||||
if loaded_all_messages {
|
||||
this.loaded_all_messages = loaded_all_messages;
|
||||
}
|
||||
|
||||
this.pending_messages().cloned().collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
for pending_message in pending_messages {
|
||||
let request = rpc.request(proto::SendChannelMessage {
|
||||
channel_id,
|
||||
body: pending_message.body,
|
||||
nonce: Some(pending_message.nonce.into()),
|
||||
});
|
||||
let response = request.await?;
|
||||
let message = ChannelMessage::from_proto(
|
||||
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
||||
&user_store,
|
||||
&mut cx,
|
||||
)
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.insert_messages(SumTree::from_item(message, &()), cx);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn message_count(&self) -> usize {
|
||||
self.messages.summary().count
|
||||
}
|
||||
|
||||
pub fn messages(&self) -> &SumTree<ChannelMessage> {
|
||||
&self.messages
|
||||
}
|
||||
|
||||
pub fn message(&self, ix: usize) -> &ChannelMessage {
|
||||
let mut cursor = self.messages.cursor::<Count>();
|
||||
cursor.seek(&Count(ix), Bias::Right, &());
|
||||
cursor.item().unwrap()
|
||||
}
|
||||
|
||||
pub fn messages_in_range(&self, range: Range<usize>) -> impl Iterator<Item = &ChannelMessage> {
|
||||
let mut cursor = self.messages.cursor::<Count>();
|
||||
cursor.seek(&Count(range.start), Bias::Right, &());
|
||||
cursor.take(range.len())
|
||||
}
|
||||
|
||||
pub fn pending_messages(&self) -> impl Iterator<Item = &ChannelMessage> {
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>();
|
||||
cursor.seek(&ChannelMessageId::Pending(0), Bias::Left, &());
|
||||
cursor
|
||||
}
|
||||
|
||||
async fn handle_message_sent(
|
||||
this: ModelHandle<Self>,
|
||||
message: TypedEnvelope<proto::ChannelMessageSent>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let user_store = this.read_with(&cx, |this, _| this.user_store.clone());
|
||||
let message = message
|
||||
.payload
|
||||
.message
|
||||
.ok_or_else(|| anyhow!("empty message"))?;
|
||||
|
||||
let message = ChannelMessage::from_proto(message, &user_store, &mut cx).await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.insert_messages(SumTree::from_item(message, &()), cx)
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_messages(&mut self, messages: SumTree<ChannelMessage>, cx: &mut ModelContext<Self>) {
|
||||
if let Some((first_message, last_message)) = messages.first().zip(messages.last()) {
|
||||
let nonces = messages
|
||||
.cursor::<()>()
|
||||
.map(|m| m.nonce)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let mut old_cursor = self.messages.cursor::<(ChannelMessageId, Count)>();
|
||||
let mut new_messages = old_cursor.slice(&first_message.id, Bias::Left, &());
|
||||
let start_ix = old_cursor.start().1 .0;
|
||||
let removed_messages = old_cursor.slice(&last_message.id, Bias::Right, &());
|
||||
let removed_count = removed_messages.summary().count;
|
||||
let new_count = messages.summary().count;
|
||||
let end_ix = start_ix + removed_count;
|
||||
|
||||
new_messages.push_tree(messages, &());
|
||||
|
||||
let mut ranges = Vec::<Range<usize>>::new();
|
||||
if new_messages.last().unwrap().is_pending() {
|
||||
new_messages.push_tree(old_cursor.suffix(&()), &());
|
||||
} else {
|
||||
new_messages.push_tree(
|
||||
old_cursor.slice(&ChannelMessageId::Pending(0), Bias::Left, &()),
|
||||
&(),
|
||||
);
|
||||
|
||||
while let Some(message) = old_cursor.item() {
|
||||
let message_ix = old_cursor.start().1 .0;
|
||||
if nonces.contains(&message.nonce) {
|
||||
if ranges.last().map_or(false, |r| r.end == message_ix) {
|
||||
ranges.last_mut().unwrap().end += 1;
|
||||
} else {
|
||||
ranges.push(message_ix..message_ix + 1);
|
||||
}
|
||||
} else {
|
||||
new_messages.push(message.clone(), &());
|
||||
}
|
||||
old_cursor.next(&());
|
||||
}
|
||||
}
|
||||
|
||||
drop(old_cursor);
|
||||
self.messages = new_messages;
|
||||
|
||||
for range in ranges.into_iter().rev() {
|
||||
cx.emit(ChannelEvent::MessagesUpdated {
|
||||
old_range: range,
|
||||
new_count: 0,
|
||||
});
|
||||
}
|
||||
cx.emit(ChannelEvent::MessagesUpdated {
|
||||
old_range: start_ix..end_ix,
|
||||
new_count,
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn messages_from_proto(
|
||||
proto_messages: Vec<proto::ChannelMessage>,
|
||||
user_store: &ModelHandle<UserStore>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<SumTree<ChannelMessage>> {
|
||||
let unique_user_ids = proto_messages
|
||||
.iter()
|
||||
.map(|m| m.sender_id)
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
user_store
|
||||
.update(cx, |user_store, cx| {
|
||||
user_store.get_users(unique_user_ids, cx)
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut messages = Vec::with_capacity(proto_messages.len());
|
||||
for message in proto_messages {
|
||||
messages.push(ChannelMessage::from_proto(message, user_store, cx).await?);
|
||||
}
|
||||
let mut result = SumTree::new();
|
||||
result.extend(messages, &());
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl From<proto::Channel> for ChannelDetails {
|
||||
fn from(message: proto::Channel) -> Self {
|
||||
Self {
|
||||
id: message.id,
|
||||
name: message.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChannelMessage {
|
||||
pub async fn from_proto(
|
||||
message: proto::ChannelMessage,
|
||||
user_store: &ModelHandle<UserStore>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
let sender = user_store
|
||||
.update(cx, |user_store, cx| {
|
||||
user_store.get_user(message.sender_id, cx)
|
||||
})
|
||||
.await?;
|
||||
Ok(ChannelMessage {
|
||||
id: ChannelMessageId::Saved(message.id),
|
||||
body: message.body,
|
||||
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)?,
|
||||
sender,
|
||||
nonce: message
|
||||
.nonce
|
||||
.ok_or_else(|| anyhow!("nonce is required"))?
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_pending(&self) -> bool {
|
||||
matches!(self.id, ChannelMessageId::Pending(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Item for ChannelMessage {
|
||||
type Summary = ChannelMessageSummary;
|
||||
|
||||
fn summary(&self) -> Self::Summary {
|
||||
ChannelMessageSummary {
|
||||
max_id: self.id,
|
||||
count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChannelMessageId {
|
||||
fn default() -> Self {
|
||||
Self::Saved(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Summary for ChannelMessageSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.max_id = summary.max_id;
|
||||
self.count += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for ChannelMessageId {
|
||||
fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) {
|
||||
debug_assert!(summary.max_id > *self);
|
||||
*self = summary.max_id;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for Count {
|
||||
fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) {
|
||||
self.0 += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::{FakeHttpClient, FakeServer};
|
||||
use gpui::TestAppContext;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
let user_id = 5;
|
||||
let http_client = FakeHttpClient::with_404_response();
|
||||
let client = cx.update(|cx| Client::new(http_client.clone(), cx));
|
||||
let server = FakeServer::for_client(user_id, &client, cx).await;
|
||||
|
||||
Channel::init(&client);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
|
||||
let channel_list = cx.add_model(|cx| ChannelList::new(user_store, client.clone(), cx));
|
||||
channel_list.read_with(cx, |list, _| assert_eq!(list.available_channels(), None));
|
||||
|
||||
// Get the available channels.
|
||||
let get_channels = server.receive::<proto::GetChannels>().await.unwrap();
|
||||
server
|
||||
.respond(
|
||||
get_channels.receipt(),
|
||||
proto::GetChannelsResponse {
|
||||
channels: vec![proto::Channel {
|
||||
id: 5,
|
||||
name: "the-channel".to_string(),
|
||||
}],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
channel_list.next_notification(cx).await;
|
||||
channel_list.read_with(cx, |list, _| {
|
||||
assert_eq!(
|
||||
list.available_channels().unwrap(),
|
||||
&[ChannelDetails {
|
||||
id: 5,
|
||||
name: "the-channel".into(),
|
||||
}]
|
||||
)
|
||||
});
|
||||
|
||||
let get_users = server.receive::<proto::GetUsers>().await.unwrap();
|
||||
assert_eq!(get_users.payload.user_ids, vec![5]);
|
||||
server
|
||||
.respond(
|
||||
get_users.receipt(),
|
||||
proto::UsersResponse {
|
||||
users: vec![proto::User {
|
||||
id: 5,
|
||||
github_login: "nathansobo".into(),
|
||||
avatar_url: "http://avatar.com/nathansobo".into(),
|
||||
}],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
// Join a channel and populate its existing messages.
|
||||
let channel = channel_list
|
||||
.update(cx, |list, cx| {
|
||||
let channel_id = list.available_channels().unwrap()[0].id;
|
||||
list.get_channel(channel_id, cx)
|
||||
})
|
||||
.unwrap();
|
||||
channel.read_with(cx, |channel, _| assert!(channel.messages().is_empty()));
|
||||
let join_channel = server.receive::<proto::JoinChannel>().await.unwrap();
|
||||
server
|
||||
.respond(
|
||||
join_channel.receipt(),
|
||||
proto::JoinChannelResponse {
|
||||
messages: vec![
|
||||
proto::ChannelMessage {
|
||||
id: 10,
|
||||
body: "a".into(),
|
||||
timestamp: 1000,
|
||||
sender_id: 5,
|
||||
nonce: Some(1.into()),
|
||||
},
|
||||
proto::ChannelMessage {
|
||||
id: 11,
|
||||
body: "b".into(),
|
||||
timestamp: 1001,
|
||||
sender_id: 6,
|
||||
nonce: Some(2.into()),
|
||||
},
|
||||
],
|
||||
done: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
// Client requests all users for the received messages
|
||||
let mut get_users = server.receive::<proto::GetUsers>().await.unwrap();
|
||||
get_users.payload.user_ids.sort();
|
||||
assert_eq!(get_users.payload.user_ids, vec![6]);
|
||||
server
|
||||
.respond(
|
||||
get_users.receipt(),
|
||||
proto::UsersResponse {
|
||||
users: vec![proto::User {
|
||||
id: 6,
|
||||
github_login: "maxbrunsfeld".into(),
|
||||
avatar_url: "http://avatar.com/maxbrunsfeld".into(),
|
||||
}],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
channel.next_event(cx).await,
|
||||
ChannelEvent::MessagesUpdated {
|
||||
old_range: 0..0,
|
||||
new_count: 2,
|
||||
}
|
||||
);
|
||||
channel.read_with(cx, |channel, _| {
|
||||
assert_eq!(
|
||||
channel
|
||||
.messages_in_range(0..2)
|
||||
.map(|message| (message.sender.github_login.clone(), message.body.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("nathansobo".into(), "a".into()),
|
||||
("maxbrunsfeld".into(), "b".into())
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Receive a new message.
|
||||
server.send(proto::ChannelMessageSent {
|
||||
channel_id: channel.read_with(cx, |channel, _| channel.details.id),
|
||||
message: Some(proto::ChannelMessage {
|
||||
id: 12,
|
||||
body: "c".into(),
|
||||
timestamp: 1002,
|
||||
sender_id: 7,
|
||||
nonce: Some(3.into()),
|
||||
}),
|
||||
});
|
||||
|
||||
// Client requests user for message since they haven't seen them yet
|
||||
let get_users = server.receive::<proto::GetUsers>().await.unwrap();
|
||||
assert_eq!(get_users.payload.user_ids, vec![7]);
|
||||
server
|
||||
.respond(
|
||||
get_users.receipt(),
|
||||
proto::UsersResponse {
|
||||
users: vec![proto::User {
|
||||
id: 7,
|
||||
github_login: "as-cii".into(),
|
||||
avatar_url: "http://avatar.com/as-cii".into(),
|
||||
}],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
channel.next_event(cx).await,
|
||||
ChannelEvent::MessagesUpdated {
|
||||
old_range: 2..2,
|
||||
new_count: 1,
|
||||
}
|
||||
);
|
||||
channel.read_with(cx, |channel, _| {
|
||||
assert_eq!(
|
||||
channel
|
||||
.messages_in_range(2..3)
|
||||
.map(|message| (message.sender.github_login.clone(), message.body.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
&[("as-cii".into(), "c".into())]
|
||||
)
|
||||
});
|
||||
|
||||
// Scroll up to view older messages.
|
||||
channel.update(cx, |channel, cx| {
|
||||
assert!(channel.load_more_messages(cx));
|
||||
});
|
||||
let get_messages = server.receive::<proto::GetChannelMessages>().await.unwrap();
|
||||
assert_eq!(get_messages.payload.channel_id, 5);
|
||||
assert_eq!(get_messages.payload.before_message_id, 10);
|
||||
server
|
||||
.respond(
|
||||
get_messages.receipt(),
|
||||
proto::GetChannelMessagesResponse {
|
||||
done: true,
|
||||
messages: vec![
|
||||
proto::ChannelMessage {
|
||||
id: 8,
|
||||
body: "y".into(),
|
||||
timestamp: 998,
|
||||
sender_id: 5,
|
||||
nonce: Some(4.into()),
|
||||
},
|
||||
proto::ChannelMessage {
|
||||
id: 9,
|
||||
body: "z".into(),
|
||||
timestamp: 999,
|
||||
sender_id: 6,
|
||||
nonce: Some(5.into()),
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
channel.next_event(cx).await,
|
||||
ChannelEvent::MessagesUpdated {
|
||||
old_range: 0..0,
|
||||
new_count: 2,
|
||||
}
|
||||
);
|
||||
channel.read_with(cx, |channel, _| {
|
||||
assert_eq!(
|
||||
channel
|
||||
.messages_in_range(0..2)
|
||||
.map(|message| (message.sender.github_login.clone(), message.body.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("nathansobo".into(), "y".into()),
|
||||
("maxbrunsfeld".into(), "z".into())
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||