Compare commits
788 Commits
700dd7b45a
...
v0.0.32.1
Author | SHA1 | Date | |
---|---|---|---|
951155f1b6 | |||
1b678175ef | |||
8eb1f40eec | |||
235887b3bf | |||
0b3d66dd48 | |||
beb9ef3754 | |||
9f6a480736 | |||
b3bac2927d | |||
ef389f2ba2 | |||
ef21dc6ae8 | |||
6e55b6b49e | |||
db115ef1bd | |||
678838dbd5 | |||
586f87625d | |||
1542370f9b | |||
1f7d5968c7 | |||
39e51f7790 | |||
052663efbe | |||
8f84ff2611 | |||
37e1c5d97b | |||
cef526bcf3 | |||
6af36cafa9 | |||
fca859d93d | |||
2178300d8d | |||
636bdcce6b | |||
94b7703ca9 | |||
a391dd1316 | |||
b6ba5211b7 | |||
8e8e130045 | |||
1f40bc1a0f | |||
5437212222 | |||
a8ab845cd2 | |||
8cee6dc98b | |||
70c2b73414 | |||
98013c4422 | |||
e9e22b762d | |||
620db19936 | |||
94a79dd62c | |||
b56c3efde0 | |||
066827f8f1 | |||
c3b65d9cd8 | |||
a15b916b06 | |||
31d0a5c233 | |||
140179e80a | |||
53cba2d7e4 | |||
e54312d3b8 | |||
cadc27b7b5 | |||
388b829ec1 | |||
67861f0f33 | |||
a1f1eb34d5 | |||
2a6789063e | |||
cbf1273a55 | |||
8143a23ced | |||
3c17810747 | |||
bea7a2e9ed | |||
2f0a2ac6b0 | |||
18908b6b56 | |||
b135a210cc | |||
3a2a829940 | |||
e56dd2dd2d | |||
3f41a48bc7 | |||
65ed53281a | |||
1121557a2e | |||
d4a7b86ee7 | |||
626c18b04e | |||
bfa97ed7c7 | |||
deae4d5367 | |||
899605c860 | |||
dc9a279991 | |||
2a53892581 | |||
6bef0eb764 | |||
462b40640c | |||
72e1b2025c | |||
fc7c4b1257 | |||
6c22c59056 | |||
94c2b1184f | |||
45231d703d | |||
7882fcbe8f | |||
3bbc8c4d35 | |||
8ae10dc80b | |||
9b11c2c629 | |||
e2a231fb4a | |||
8a9502d1f2 | |||
534438df63 | |||
45a4feec96 | |||
aa7a32395e | |||
ab9f57f044 | |||
4040d6aa08 | |||
1c96f5c35e | |||
4d3e42812d | |||
f7b3711d4f | |||
2408e076ff | |||
6f71ffb477 | |||
214433f36a | |||
309b22732e | |||
6fe7687b2a | |||
a8cbf757ff | |||
4a4bedfe2b | |||
051291f725 | |||
d2b338095f | |||
899827a8f2 | |||
5fcbe3d6a9 | |||
a0a40e6cb2 | |||
bb1190e3f8 | |||
0a3baed1da | |||
4931c489ed | |||
996f9abaa2 | |||
08c097e176 | |||
daa861a98b | |||
a25d08fd76 | |||
392d31cc53 | |||
92926fa8df | |||
61ae9ae465 | |||
89622697d5 | |||
17694f5646 | |||
5a1303149f | |||
8a0e190a86 | |||
0d7dfd8c9e | |||
f979ff7050 | |||
e3fcdea362 | |||
476fec2757 | |||
53c215399b | |||
2c330802da | |||
851d7046ea | |||
c0019d7246 | |||
7688e4d3a8 | |||
ef58749ce3 | |||
35941a7ddc | |||
1f2664e5a8 | |||
35656a5c34 | |||
799f22e989 | |||
e226a37251 | |||
8e3bc9d700 | |||
58c3e6c2ab | |||
0dc148bfea | |||
3eff1b08a9 | |||
02d789471f | |||
d367d47c4d | |||
c93b8fc045 | |||
eb9377e21d | |||
a1764eee42 | |||
86ef74e20d | |||
4de53b9926 | |||
99a195a3fd | |||
f1ced31f69 | |||
b3cedf2baa | |||
3bf19fabda | |||
cf81ebe8ad | |||
278b5566a1 | |||
e8c1390f09 | |||
3c04abda45 | |||
2597f99ccf | |||
9d3a07c1cf | |||
bdfd8925b5 | |||
1a4d1985f4 | |||
6273f3ea53 | |||
5bdc6fa471 | |||
3ba41291db | |||
0867811952 | |||
8d961cd805 | |||
97cea7b40b | |||
4106834db8 | |||
a4a8f7cab2 | |||
9e209ee800 | |||
ddfa84f040 | |||
6b3a6ec7c1 | |||
4d037c02bf | |||
deaeab10d8 | |||
2a5375b1e7 | |||
e7a03e3283 | |||
efb3a12dcc | |||
3830d695d7 | |||
f36620927b | |||
5423cbd628 | |||
abde709e54 | |||
27f2d319ab | |||
66234b14bc | |||
6a9167e565 | |||
3c60f8ca06 | |||
c26bf5c112 | |||
41cbde934a | |||
946941d95e | |||
50f0104239 | |||
40fa7edadf | |||
d6926569c6 | |||
a8bba324ca | |||
5bba5776b3 | |||
8104f6f228 | |||
3f4738e593 | |||
1516e17f5d | |||
676d2702b7 | |||
5d39548964 | |||
67d458bd38 | |||
d9684c7d62 | |||
5a818d2119 | |||
6f96d4ce65 | |||
f72395756a | |||
38d746b310 | |||
f7270987ea | |||
6f565c0f0a | |||
7f252e79b6 | |||
ba2bb17638 | |||
bc7c658293 | |||
4d84e69bb5 | |||
03fac74908 | |||
5252ff1ecf | |||
20100d3fd4 | |||
dd3b2656ad | |||
657f25e22b | |||
8be354fc49 | |||
e574758340 | |||
40cf519492 | |||
0e4fda54e9 | |||
868f91e1ef | |||
b9000c154f | |||
894c72a82f | |||
c128cfc25c | |||
36c88b463c | |||
8a66e74074 | |||
ea60b165da | |||
1011e0026b | |||
9462521287 | |||
576022c41a | |||
70c38b7ea8 | |||
36370f2dea | |||
db9bf7f7bd | |||
fa7aef0c37 | |||
b135ea17f6 | |||
4b1643bc47 | |||
240a8ce9c7 | |||
8928e8722b | |||
d692734e55 | |||
50197198b4 | |||
1ee1107c93 | |||
cf90533b6c | |||
f0211f621e | |||
d9693af89b | |||
13722232fb | |||
0bcb033349 | |||
e92c439724 | |||
7f34b585d3 | |||
d7e9fd918a | |||
9899c0c5e2 | |||
c50de0b0f0 | |||
bb7d2d7ae0 | |||
862d172ca8 | |||
3671051d0e | |||
223e20cbbc | |||
9af4312561 | |||
934e40240e | |||
edb1980387 | |||
bb7b04013f | |||
26a3007268 | |||
5de2b09596 | |||
3660577a23 | |||
98b4c7cf04 | |||
427a7b8d25 | |||
67b84830cd | |||
973cd53266 | |||
1afdbe6932 | |||
942f582329 | |||
951a80389a | |||
b7ecfc9925 | |||
59e389d793 | |||
2ec047cc00 | |||
ee33f54745 | |||
7a79534ca8 | |||
a74a9fc821 | |||
e2c388b9db | |||
0f573ce09e | |||
bc70e41b7c | |||
f500e14aa3 | |||
8b47938238 | |||
8912212d8e | |||
6a346bf940 | |||
b5bdae4611 | |||
6590da5793 | |||
eb2b426ec7 | |||
4864a0411f | |||
68590cae33 | |||
abf2bbaec2 | |||
0da7e2722f | |||
60d4b06057 | |||
4c3df34950 | |||
7737e60b52 | |||
71e816bc13 | |||
c74f90ef04 | |||
26cb7e5a17 | |||
2bad6672d8 | |||
71c4011526 | |||
5e81078f59 | |||
31b78e74df | |||
2ff689aab0 | |||
0a6f0ed3f7 | |||
bfec46673d | |||
3a16614c72 | |||
9c9efb845c | |||
6192f1b94d | |||
ce451b2449 | |||
8534e16469 | |||
f6cc6f2eae | |||
0904425221 | |||
a729886522 | |||
e5dfedc7d1 | |||
f02423d084 | |||
8f4b6e83eb | |||
3029919553 | |||
ac67db0591 | |||
1e08838f5b | |||
d814f7ee77
|
|||
7edfb9d386 | |||
6928d6caba | |||
1a626875cf | |||
6eb3b64334 | |||
09f3595e93 | |||
11e89622d4 | |||
0fa8acc264 | |||
afc7c64ed8 | |||
c794c1b885 | |||
6247529799 | |||
ad3eedc1fb | |||
1cfac3cae6 | |||
478bcd5d13 | |||
6932da69b3 | |||
857f47bf55 | |||
373d742751 | |||
575622c522 | |||
a3e86ccb1d | |||
e491798ff1 | |||
15df4ac236 | |||
017a74c4e4 | |||
6e5b1127f3 | |||
95f4f88949 | |||
92858882d7 | |||
4cba95ec8c | |||
bbb2f89d85 | |||
88213038b2 | |||
cc18b41e76 | |||
f16127a238 | |||
2a6ecfaede | |||
cf4a09bf03 | |||
20e60262fd | |||
9e3928c976 | |||
95d8768545 | |||
8679d09040 | |||
4cc5c6acb3 | |||
6f9b548b1a | |||
fbff3386a9 | |||
e5899fca58 | |||
dddec489b9 | |||
b4049eaeaa | |||
b3fd724b5a | |||
aca25be86a | |||
0f8cbdac57 | |||
630219d667 | |||
fef268e434 | |||
e18dcf3a48 | |||
e019320146 | |||
65b31e14f9 | |||
9cb5cbcde9 | |||
25914ff5a7 | |||
c4af799279 | |||
3f8f0e14f4 | |||
5414b30e7f | |||
7aee897c1b | |||
4060f9cc11 | |||
5b526cbf5b | |||
f5065ff42b | |||
86b5546f5f | |||
43ae2a293b | |||
b8ddbd4255 | |||
87cdba1db8 | |||
df83187e33 | |||
eccdbf29ab | |||
28d181f8bc | |||
d386daf2ff | |||
0c1e116c1e | |||
bb0ed67827 | |||
b111d06851 | |||
79388845ea | |||
99faef2e77 | |||
21fffbfe10 | |||
64fb9f0c2a | |||
a42e0bef2c | |||
45a09006e1 | |||
240484be4c | |||
22f4d115e3 | |||
32920e0e5d | |||
f03a5918d1 | |||
dd1870b52a | |||
f0c1a8f98f | |||
0c181d7e7c | |||
e3dc0e833a | |||
6de875edea | |||
986a55173f | |||
59c8cabf02 | |||
a33b9fab07 | |||
f8a725e1e7 | |||
b3ab3af01b | |||
79f9463e56 | |||
4257b2ed51 | |||
6488ab60ec | |||
18bd3dfcf9 | |||
ec114e160d | |||
de033af18f | |||
e971c6fcb7 | |||
d3c465391c | |||
f1fa19593d | |||
60b6f9c73e | |||
55b95ddecb | |||
0800a251b2 | |||
82cf7a80eb | |||
379f3d12eb | |||
e52972d4d4 | |||
1a0ca4dec2 | |||
0bac9d8d5a | |||
a9608363c5 | |||
f1a2c5ae8e | |||
192a81ede7 | |||
916aa5abbd | |||
d4a5cc6eee | |||
d19605cc8d | |||
8d529327a4 | |||
697e2f2071 | |||
f7fb112f21 | |||
b2c0211190 | |||
c59e0ea6e5 | |||
6c2fc1444d | |||
94d969fe46 | |||
a7bc3d301c | |||
18bab849f7 | |||
04878fcc30 | |||
ffe1299548 | |||
64bdbf5725 | |||
b82deb557f | |||
d529a48a11 | |||
b711e4e6bd | |||
186eecfbff | |||
d766c33f59 | |||
2c9257f1a8 | |||
71ff604f90 | |||
83e00763ea | |||
5b647e0937 | |||
e0444510f4 | |||
82e876a892 | |||
c728e05032 | |||
c7a8ce7060 | |||
762b4339fe | |||
f824b8988e | |||
6280d6d167 | |||
4f18e744b4 | |||
01d8f720e8 | |||
18cf058af3 | |||
e2406df367 | |||
1fd669bdb3 | |||
f6add12c80 | |||
0f643bfe39 | |||
15be498e4b | |||
fba465dd62 | |||
19dbe354e7 | |||
fca5d37b7e | |||
aa04ad2dc2 | |||
7ef4d814ef | |||
3f3deb665c | |||
97fc22ce57 | |||
616f3ad76d | |||
faca63946c | |||
57bae341a2 | |||
fd09a766d2 | |||
11564a5292 | |||
ac12e350bf | |||
2def15337d | |||
3e3d58a4a9 | |||
5ce4f55228 | |||
21788fc7b0 | |||
a1c4382fde | |||
364e95698e | |||
6eec142499 | |||
a8f6b3a39a | |||
250933bf41 | |||
56c77c781a | |||
f7602b39a1 | |||
8c86092356 | |||
db0a4bff77 | |||
e198ff9cb1 | |||
b8eaa5cf97 | |||
0d597721bf | |||
003e0caada | |||
053637cfb4 | |||
8178213f1a | |||
b4222a41de | |||
f28e409ea5 | |||
287c6c06e1 | |||
8216bdb4b3 | |||
aa15da50ab | |||
02759c6f83 | |||
6b0c49752c | |||
2e4f792fc3 | |||
17eba059f0 | |||
e59a00922b | |||
872201c886 | |||
3352098284 | |||
d0bbd7f24f | |||
7f87714b58 | |||
5594bee618 | |||
c469ef23e6 | |||
f6e74f2526 | |||
10b6e9c537 | |||
3f27af30b7 | |||
23db09f9b7 | |||
d1b7681efc | |||
61ad405ad8 | |||
aff98110e0 | |||
2f36db9142 | |||
aa86ee1066 | |||
dbbcce8165 | |||
1ed066ef0f | |||
763f7d45d8 | |||
2328f3afb5 | |||
2223245861 | |||
36226b01cd | |||
da31f9cadd | |||
9da4857066 | |||
75c71135ba | |||
0cb5025a16 | |||
44d9f69434 | |||
3f343b283b | |||
03a28fc3c5 | |||
3513619221 | |||
0c9f5769d3 | |||
587a666ab6 | |||
f26deea508 | |||
b8e19040b5 | |||
7d9e0f4080 | |||
16ce7fbc7b | |||
639fce376a | |||
3cdbac5c22 | |||
3dcafdf403 | |||
cd2fe9f8d9 | |||
fd40596ce7 | |||
7ecda69703 | |||
a3b76cd5c2 | |||
54df862998 | |||
301b7a4911 | |||
e0a048abe6 | |||
671e3e19ff | |||
0c394c2e61 | |||
4ecbb5234c | |||
98f1700049 | |||
2f0b4a0187 | |||
f66c6ed0c3 | |||
5d9785ac2d | |||
bb97a8cccc | |||
571cf5b5b8 | |||
1974ed1c03 | |||
98275f7c87 | |||
eca8726909 | |||
baf125c450 | |||
efcc710d91 | |||
5980ee4c86 | |||
db9b7a22c2 | |||
5e24d4f322 | |||
2dd32cdce2 | |||
9cddd93dad | |||
4127898655 | |||
45d48483d0 | |||
852c25296a | |||
aea631138e | |||
683fdbb02a | |||
c3bbab35e2 | |||
ba8941046e | |||
d202f4e00d | |||
42da5d8d32 | |||
5af3533598 | |||
7843168fad | |||
8f51eb63b0 | |||
855f5f7af4 | |||
c85dd2655c | |||
fb0e4060cd | |||
707b4990a6 | |||
9c8b922069 | |||
d4b421421d | |||
58e9646fa6 | |||
500f172561 | |||
68f6c90ea4 | |||
41e91f2922 | |||
999117cfeb | |||
6185df512f | |||
0cbf66c007 | |||
cd378b721d | |||
547d38d1ef | |||
dca56af5b9 | |||
224442772e | |||
003951fdf7 | |||
d51b3da1b4 | |||
69f4af84db | |||
771759b252 | |||
20c7a71db6 | |||
8475ee0985 | |||
f42811d3d4 | |||
c3b1832cfb | |||
eb6753afe1 | |||
5051cecb84 | |||
cd03ede358 | |||
6563f8c738 | |||
e5279b4827 | |||
79ff505963 | |||
8a67eba5fc | |||
6609a5f340 | |||
d9972cb349 | |||
28d2539432 | |||
f28386b71f | |||
53717076f5 | |||
a9aa928629 | |||
8df121148d | |||
5e23c32ae8 | |||
9c0f6481c0 | |||
68ae45dd58 | |||
3091747438 | |||
2f266b8dd4 | |||
ee20b87ee2 | |||
83e025d0bb | |||
5115c6e217 | |||
76f6a94de5 | |||
954830be18 | |||
ea70299a45 | |||
88da071ed6 | |||
1dbf162a71 | |||
1c0964753b | |||
daa1c7f577 | |||
854416ceb2 | |||
2230351e3e | |||
7da3244da2 | |||
bfeb0c2988 | |||
d4e75c1dec | |||
405bddcde0 | |||
8a27c45ab1 | |||
10b15896b3 | |||
0e97bbe37c | |||
e0d7e90894 | |||
5d13f6aab6 | |||
1ebfbbe89e | |||
91ad43fdfc | |||
6fe6fc180d | |||
d84d0bec38 | |||
7e7b1c6ee1 | |||
effb354d1b | |||
ba7d1ad35f | |||
3ca2b19502 | |||
8e0d91dcf5 | |||
cd2c2587ae | |||
53044696ba | |||
6d6927213f | |||
be1b5bce4f | |||
4b4fd0735b | |||
c565b2a31f | |||
55f2261905 | |||
51912f2b83 | |||
7f4e2617ee | |||
960a385202 | |||
21f48d3485 | |||
7f9605e55f | |||
cc409dc3f7 | |||
af6091760c | |||
e1d93c003c | |||
ff9dd2dd03 | |||
7a306bb3d2 | |||
7ffc148358 | |||
50fef2edfa | |||
aa40084010 | |||
740d788c7c | |||
4c2fa2c1b3 | |||
4350c7b7a9 | |||
595f14d98d | |||
2e95d6ea63 | |||
0da6abeb98 | |||
e4e050e8e7 | |||
5bc082b75e | |||
beedbd7646 | |||
507b069ffe | |||
71444b0427 | |||
a08bba438e | |||
df1e6711af | |||
f6d4e934e3 | |||
d5bd4c6735 | |||
eb12ba6ed2 | |||
6e83c08535 | |||
b6bfdec48d | |||
f9ec796291 | |||
3beb1d0683 | |||
8836c7f0ca | |||
ef5ce1d6e1 | |||
0ea1213139 | |||
51fe372f60 | |||
eb8f9f8936 | |||
afc1524874 | |||
fbb975625c | |||
53e75d8209 | |||
5bdf970c10 | |||
50089f72c6 | |||
62e15e0208 | |||
3d8b02a7f3 | |||
20701d9cf1 | |||
fa94442eb2 | |||
68ff77e172 | |||
102e9be3a8 | |||
92bf01a183 | |||
559504ae29 | |||
9b00b41a1e | |||
b1f6ad17e1 | |||
e7979fe9db | |||
7a276adbbc | |||
db4997fdc4 | |||
44ebb841f0 | |||
09ae4e2096 | |||
0b46efe4ea | |||
f1dda43e66 | |||
ce483138d7 | |||
73cc39226d | |||
57257f63dd | |||
88b25790e8 | |||
e01defc4aa | |||
cb50c43e93 | |||
5908d15f91 | |||
f66cfaec12 | |||
259f92c53b | |||
a84f850e91 | |||
5a765e6f07 | |||
791889c659 | |||
5da63faf1f | |||
30d108fc35 | |||
a09fefab5e | |||
f74ca1c236 | |||
30e027092b | |||
fd4ac7c9b9 | |||
4482049b94 | |||
5839380437 | |||
2152470fdc | |||
93b2a81495 | |||
e139e952c0 | |||
cf1c57ccb8 | |||
f7a2138488 | |||
9614d03bef | |||
32a335c676 | |||
06e27fc1e0 | |||
1f40e8dcd9 | |||
77ff8cef1f | |||
ef844fbccb | |||
070dc5a4c0 | |||
177ef1cdcc | |||
4b1ebf02e1 | |||
863e50203e | |||
01b8c209de | |||
30e92f2bc1 | |||
02accabb4a | |||
fa00a41fe0 | |||
2e66666bdf | |||
4fe3c9a751 | |||
0a35e14590 | |||
e979c176e3 | |||
a0d9c3dc29 | |||
efcb68351c | |||
94e8bf2e1c | |||
82d1a294a6 | |||
de20274589 | |||
2f193e64c8 | |||
86751362cb | |||
4118323631 | |||
0d134f7f10 | |||
409724cfcd | |||
799a33be40 | |||
2fa9c66495 | |||
ad818a8e7e | |||
581f72b3f8 | |||
1dd7e4347c | |||
36cc9398c7 | |||
68817feeec | |||
97661e2ca2 | |||
72def5ae6d | |||
e638b155a1 | |||
32db18b0d6 | |||
b653a5250d | |||
30329f7cad | |||
29a1478c86 | |||
c882bf31ec | |||
17ccb8f083 | |||
0e7d2a8b0e | |||
3743543ef8 |
@ -1,4 +1,3 @@
|
||||
.svn
|
||||
db.sqlite
|
||||
out/**/*.o
|
||||
out/**/*.d
|
||||
.git
|
||||
db.sqlite*
|
||||
out/
|
||||
|
@ -6,15 +6,51 @@ jobs:
|
||||
Build-All:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
valid_volumes: ['/opt/keys']
|
||||
image: node:23-bookworm-slim
|
||||
valid_volumes:
|
||||
- '/opt/keys'
|
||||
- '/opt/deps'
|
||||
volumes:
|
||||
- /opt/keys:/opt/keys
|
||||
- /opt/deps:/opt/deps
|
||||
steps:
|
||||
- name: check out code
|
||||
- name: Install build dependencies
|
||||
run: >
|
||||
apt update && apt install -y \
|
||||
build-essential \
|
||||
clang-19 \
|
||||
cmake \
|
||||
curl \
|
||||
docker.io \
|
||||
doxygen \
|
||||
file \
|
||||
gcc-aarch64-linux-gnu \
|
||||
git \
|
||||
graphviz \
|
||||
libgpgme11 \
|
||||
libssl-dev \
|
||||
mingw-w64 \
|
||||
rsync \
|
||||
unzip \
|
||||
zip \
|
||||
zlib1g-dev
|
||||
- name: Get code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: ln -s /opt/keys .keys
|
||||
- name: Setup environment
|
||||
run: |
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100
|
||||
ln -s /opt/keys .keys
|
||||
ln -sf /opt/deps/ios_toolchain deps/
|
||||
ln -sf /opt/deps/macos_toolchain deps/
|
||||
- name: Build documentation
|
||||
run: |
|
||||
mkdir -p out/html/ ~/.ssh/
|
||||
make -j`nproc` docs
|
||||
echo 'pildefriends ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKD3Kde5vDO0TrMBDK0IGGeNGe/XinWAZkSQ/rXxwUjt' >> ~/.ssh/known_hosts
|
||||
rsync -avP --delete -e "ssh -i /opt/keys/ssh.ed25519" out/html/ tfdocs@pildefriends:docs/html/
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
@ -23,16 +59,13 @@ jobs:
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264'
|
||||
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 libgpgme11
|
||||
- run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs
|
||||
- run: docker build .
|
||||
- uses: actions/upload-artifact@v3
|
||||
packages: 'tools platform-tools build-tools;35.0.0 platforms;android-35 ndk;27.2.12479018'
|
||||
- name: Docker build
|
||||
run: DOCKER_BUILDKIT=1 docker build .
|
||||
- name: Build
|
||||
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: out/TildeFriends-release.fdroid.apk
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: out/winrelease/tildefriends.exe
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: out/tildefriends-x86_64.AppImage
|
||||
name: dist
|
||||
path: dist/*
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,10 +1,13 @@
|
||||
build/
|
||||
*.core
|
||||
db.*
|
||||
deps/ios_toolchain/
|
||||
deps/ios_toolchain
|
||||
deps/macos_toolchain
|
||||
deps/openssl/
|
||||
dist/
|
||||
.flatpak-builder
|
||||
.keys
|
||||
**/.DS_Store
|
||||
logs/
|
||||
**/node_modules
|
||||
out
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -26,3 +26,6 @@
|
||||
[submodule "deps/c-ares"]
|
||||
path = deps/c-ares
|
||||
url = https://github.com/c-ares/c-ares.git
|
||||
[submodule "deps/zsign"]
|
||||
path = deps/zsign
|
||||
url = https://github.com/zhlynn/zsign.git
|
||||
|
@ -1,19 +1,16 @@
|
||||
FROM bitnami/minideb:bullseye AS build
|
||||
FROM bitnami/minideb:bookworm AS build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
libssl-dev \
|
||||
perl \
|
||||
make
|
||||
|
||||
COPY . /app
|
||||
RUN make -C /app -j $(nproc) release
|
||||
|
||||
FROM bitnami/minideb:bullseye
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libssl1.1
|
||||
FROM bitnami/minideb:bookworm
|
||||
|
||||
COPY --from=build /app/out/release/tildefriends /app/out/release/tildefriends
|
||||
COPY --from=build /app/apps /app/apps
|
||||
|
16
Doxyfile
16
Doxyfile
@ -943,7 +943,9 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = src/
|
||||
INPUT = README.md \
|
||||
docs/ \
|
||||
src/
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@ -1049,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = docs/images/
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@ -1110,7 +1112,7 @@ FILTER_SOURCE_PATTERNS =
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
@ -1680,7 +1682,7 @@ DISABLE_INDEX = NO
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
GENERATE_TREEVIEW = NO
|
||||
GENERATE_TREEVIEW = YES
|
||||
|
||||
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
|
||||
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
|
||||
@ -2268,7 +2270,7 @@ GENERATE_AUTOGEN_DEF = NO
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
#GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
@ -2276,7 +2278,7 @@ GENERATE_SQLITE3 = NO
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
#SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
@ -2284,7 +2286,7 @@ SQLITE3_OUTPUT = sqlite3
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
#SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
|
609
GNUmakefile
609
GNUmakefile
@ -3,11 +3,27 @@
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 28
|
||||
VERSION_NUMBER := 0.0.24-wip
|
||||
VERSION_NAME := Honey bunches of boats.
|
||||
## == Tilde Friends build. ==
|
||||
##
|
||||
## This is a list of all supported build targets.
|
||||
##
|
||||
## Consider passing -j$(nproc) or adding it to your $MAKEFLAGS to build in
|
||||
## parallel (faster).
|
||||
##
|
||||
## Useful variables to override:
|
||||
## CC := Compiler.
|
||||
## AS := Assembler.
|
||||
## LD := Linker.
|
||||
## ANDROID_SDK := Path to the Android SDK.
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip
|
||||
VERSION_CODE := 39
|
||||
VERSION_CODE_IOS := 15
|
||||
VERSION_NUMBER := 0.0.32.1
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
IPHONEOS_VERSION_MIN=14.0
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500200.zip
|
||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||
@ -20,18 +36,28 @@ UNAME_M := $(shell uname -m)
|
||||
ANDROID_SDK ?= ~/Android/Sdk
|
||||
BUNDLETOOL = out/bundletool.jar
|
||||
|
||||
HAVE_WIN := 0
|
||||
HAVE_WIN :=
|
||||
HAVE_CROSS_AARCH64 :=
|
||||
USE_SYSTEM_SSL :=
|
||||
|
||||
export SOURCE_DATE_EPOCH=1
|
||||
export TZ=UTC
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease
|
||||
BUILD_TYPES := debug release iosdebug iosrelease iossimdebug iossimrelease
|
||||
HAVE_ANDROID = 0
|
||||
HAVE_LINUX_IOS = 0
|
||||
HAVE_LINUX_MACOS = 0
|
||||
HAVE_WIN = 0
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
BUILD_TYPES := debug release
|
||||
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1,0)
|
||||
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1,0)
|
||||
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1,0)
|
||||
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1)
|
||||
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1)
|
||||
HAVE_LINUX_MACOS = $(if $(shell which deps/macos_toolchain/bin/oa64-clang),1)
|
||||
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1)
|
||||
ifneq ($(UNAME_M),aarch64)
|
||||
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1)
|
||||
endif
|
||||
else ifeq ($(UNAME_S),Haiku)
|
||||
BUILD_TYPES := debug release
|
||||
CFLAGS += -Dstatic_assert=_Static_assert
|
||||
@ -39,6 +65,11 @@ LDFLAGS += \
|
||||
-lbsd \
|
||||
-lnetwork \
|
||||
-Wno-stringop-overflow
|
||||
USE_SYSTEM_SSL := 1
|
||||
HAVE_ANDROID = 0
|
||||
HAVE_LINUX_IOS = 0
|
||||
HAVE_LINUX_MACOS = 0
|
||||
HAVE_WIN = 0
|
||||
else ifeq ($(UNAME_S),OpenBSD)
|
||||
BUILD_TYPES := debug release
|
||||
CFLAGS += \
|
||||
@ -46,18 +77,24 @@ CFLAGS += \
|
||||
LDFLAGS += \
|
||||
-lexecinfo \
|
||||
-lc++abi
|
||||
HAVE_ANDROID := 0
|
||||
HAVE_LINUX_IOS := 0
|
||||
HAVE_ANDROID :=
|
||||
HAVE_LINUX_IOS :=
|
||||
HAVE_LINUX_MACOS :=
|
||||
USE_SYSTEM_SSL := 1
|
||||
else
|
||||
$(error Unexpected host platform $(UNAME_S).)
|
||||
endif
|
||||
|
||||
# Everything is set above.
|
||||
$(info Building Tilde Friends $(VERSION_NUMBER) android=$(if $(HAVE_ANDROID),1,0) win=$(if $(HAVE_WIN),1,0) cross_aarch64=$(if $(HAVE_CROSS_AARCH64),1,0) cross_ios=$(if $(HAVE_LINUX_IOS),1,0) cross_macos=$(if $(HAVE_LINUX_MACOS),1,0) system_ssl=$(if $(USE_SYSTEM_SSL),1,0))
|
||||
|
||||
CFLAGS += \
|
||||
-std=gnu11 \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-cast-function-type-mismatch \
|
||||
-Wno-unknown-warning-option \
|
||||
-Wno-unused-parameter \
|
||||
-MMD \
|
||||
-MP \
|
||||
-ffunction-sections \
|
||||
@ -66,13 +103,13 @@ CFLAGS += \
|
||||
-g
|
||||
LDFLAGS += \
|
||||
-Wno-attributes \
|
||||
-flto=auto
|
||||
-Wno-aggressive-loop-optimizations
|
||||
|
||||
ANDROID_MIN_SDK_VERSION := 24
|
||||
ANDROID_TARGET_SDK_VERSION := 34
|
||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
|
||||
ANDROID_TARGET_SDK_VERSION := 35
|
||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/35.0.0
|
||||
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.3.11579264
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/27.2.12479018
|
||||
|
||||
ANDROID_ARMV7A_TARGETS := \
|
||||
out/androiddebug-armv7a/tildefriends \
|
||||
@ -109,14 +146,38 @@ WINDOWS_TARGETS := \
|
||||
out/winrelease/tildefriends.exe
|
||||
ifeq ($(HAVE_WIN),1)
|
||||
BUILD_TYPES += windebug winrelease
|
||||
all: out/windebug/tildefriends.standalone.exe out/winrelease/tildefriends.standalone.exe
|
||||
endif
|
||||
|
||||
LINUX_TARGETS := \
|
||||
AARCH64_TARGETS := \
|
||||
out/armdebug/tildefriends \
|
||||
out/armrelease/tildefriends
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
BUILD_TYPES += armdebug armrelease
|
||||
endif
|
||||
|
||||
HOST_TARGETS := \
|
||||
out/debug/tildefriends \
|
||||
out/release/tildefriends
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
MACOS_TARGETS := \
|
||||
out/macosdebug/tildefriends \
|
||||
out/macosrelease/tildefriends
|
||||
out/debug/tildefriends \
|
||||
out/release/tildefriends
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
MACOS_TARGETS := \
|
||||
out/macosdebug-arm/tildefriends \
|
||||
out/macosrelease-arm/tildefriends \
|
||||
out/macosdebug-x86_64/tildefriends \
|
||||
out/macosrelease-x86_64/tildefriends
|
||||
all: out/macosdebug/tildefriends.standalone
|
||||
all: out/macosrelease/tildefriends.standalone
|
||||
else
|
||||
MACOS_TARGETS :=
|
||||
endif
|
||||
else
|
||||
MACOS_TARGETS :=
|
||||
endif
|
||||
IOS_TARGETS := \
|
||||
out/iosdebug/tildefriends \
|
||||
out/iosrelease/tildefriends
|
||||
@ -130,44 +191,60 @@ ifeq ($(HAVE_LINUX_IOS),1)
|
||||
BUILD_TYPES += iosdebug iosrelease
|
||||
all: $(IOS_APPS)
|
||||
endif
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
BUILD_TYPES += \
|
||||
macosdebug-arm \
|
||||
macosrelease-arm \
|
||||
macosdebug-x86_64 \
|
||||
macosrelease-x86_64
|
||||
all: $(IOS_APPS)
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
all: $(IOS_APPS) \
|
||||
out/tildefriends-iossimdebug.app/tildefriends \
|
||||
out/tildefriends-iossimrelease.app/tildefriends
|
||||
endif
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
all: out/armrelease/tildefriends.standalone
|
||||
endif
|
||||
|
||||
DEBUG_TARGETS := \
|
||||
out/debug/tildefriends \
|
||||
out/windebug/tildefriends.exe \
|
||||
out/iosdebug/tildefriends \
|
||||
out/iossimdebug/tildefriends \
|
||||
out/macosdebug/tildefriends \
|
||||
out/androiddebug/tildefriends \
|
||||
out/androiddebug-armv7a/tildefriends \
|
||||
out/androiddebug-x86_64/tildefriends \
|
||||
out/androiddebug-x86/tildefriends
|
||||
out/androiddebug-x86/tildefriends \
|
||||
out/armdebug/tildefriends \
|
||||
out/macosdebug-arm/tildefriends \
|
||||
out/macosdebug-x86_64/tildefriends
|
||||
RELEASE_TARGETS := \
|
||||
out/release/tildefriends \
|
||||
out/winrelease/tildefriends.exe \
|
||||
out/iosrelease/tildefriends \
|
||||
out/iossimrelease/tildefriends \
|
||||
out/macosrelease/tildefriends \
|
||||
out/androidrelease/tildefriends \
|
||||
out/androidrelease-armv7a/tildefriends \
|
||||
out/androidrelease-x86_64/tildefriends \
|
||||
out/androidrelease-x86/tildefriends
|
||||
out/androidrelease-x86/tildefriends \
|
||||
out/armrelease/tildefriends \
|
||||
out/macosrelease-arm/tildefriends \
|
||||
out/macosrelease-x86_64/tildefriends
|
||||
ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS)
|
||||
ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS))
|
||||
NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS))
|
||||
NONANDROID_TARGETS := $(filter-out $(ANDROID_TARGETS),$(ALL_TARGETS))
|
||||
NONMACOS_TARGETS := $(filter-out $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS),$(ALL_TARGETS))
|
||||
DEADSTRIP_TARGETS := $(filter-out $(ANDROID_TARGETS),$(NONMACOS_TARGETS))
|
||||
ifneq ($(UNAME_S),OpenBSD)
|
||||
$(NONMACOS_TARGETS): LDFLAGS += -static-libgcc
|
||||
endif
|
||||
|
||||
$(NONANDROID_TARGETS): CFLAGS += -fno-omit-frame-pointer
|
||||
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += -rdynamic
|
||||
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += \
|
||||
-rdynamic \
|
||||
-gz=zlib
|
||||
$(ANDROID_TARGETS): CFLAGS += \
|
||||
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
|
||||
-fPIC \
|
||||
@ -178,31 +255,42 @@ $(ANDROID_TARGETS): CFLAGS += \
|
||||
-Wno-unknown-warning-option
|
||||
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
|
||||
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
|
||||
$(DEBUG_TARGETS): LDFLAGS += -Og
|
||||
$(RELEASE_TARGETS): CFLAGS += \
|
||||
-DNDEBUG \
|
||||
-flto
|
||||
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -O3
|
||||
-DNDEBUG
|
||||
$(ANDROID_RELEASE_TARGETS): CFLAGS += -Oz
|
||||
$(ANDROID_RELEASE_TARGETS): LDFLAGS += -Oz
|
||||
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -Os
|
||||
$(NONANDROID_RELEASE_TARGETS): LDFLAGS += -Os
|
||||
$(WINDOWS_TARGETS): CC = x86_64-w64-mingw32-gcc-win32
|
||||
$(WINDOWS_TARGETS): AS = $(CC)
|
||||
$(WINDOWS_TARGETS): CFLAGS += \
|
||||
-D_WIN32_WINNT=0x0A00 \
|
||||
-DWINVER=0x0A00 \
|
||||
-DNTDDI_VERSION=NTDDI_WIN10 \
|
||||
-Ideps/openssl/mingw64/usr/local/include
|
||||
-Iout/openssl/$(UNAME_S)/mingw64/usr/local/include
|
||||
$(WINDOWS_TARGETS): LDFLAGS += \
|
||||
-static \
|
||||
-lm \
|
||||
-Ldeps/openssl/mingw64/usr/local/lib
|
||||
-Lout/openssl/$(UNAME_S)/mingw64/usr/local/lib
|
||||
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
|
||||
$(AARCH64_TARGETS): AS = $(CC)
|
||||
$(AARCH64_TARGETS): CFLAGS += -Iout/openssl/Linux/aarch64/usr/local/include
|
||||
$(AARCH64_TARGETS): LDFLAGS += -Lout/openssl/Linux/aarch64/usr/local/lib
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
$(MACOS_TARGETS): CC = xcrun clang
|
||||
$(HOST_TARGETS): CC = xcrun clang
|
||||
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
|
||||
$(IOS_TARGETS): CC = xcrun --sdk iphoneos clang -isysroot $(IOS_SYSROOT) -arch arm64
|
||||
$(IOSSIM_TARGETS): IOSSIM_SYSROOT := $(shell xcrun --sdk iphonesimulator --show-sdk-path)
|
||||
$(IOSSIM_TARGETS): CC = xcrun --sdk iphonesimulator clang -isysroot $(IOSSIM_SYSROOT) -arch x86_64
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
$(IOS_TARGETS): IOS_SYSROOT := deps/iPhoneOS17.0.sdk
|
||||
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
|
||||
$(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64 -DTARGET_OS_IPHONE=1
|
||||
$(IOS_TARGETS): LDFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk
|
||||
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/ios_toolchain/target/lib deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
|
||||
endif
|
||||
$(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android
|
||||
$(ANDROID_X86_TARGETS): ANDROID_NDK_TARGET_TRIPLE := i686-linux-android
|
||||
@ -213,25 +301,43 @@ $(ANDROID_TARGETS): AS = $(CC)
|
||||
$(ANDROID_TARGETS): CFLAGS += \
|
||||
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
||||
-Wno-unknown-warning-option
|
||||
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Ideps/openssl/android/armeabi-v7a/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Ldeps/openssl/android/armeabi-v7a/usr/local/lib
|
||||
$(ANDROID_ARM64_TARGETS): CFLAGS += -Ideps/openssl/android/arm64-v8a/usr/local/include
|
||||
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Ldeps/openssl/android/arm64-v8a/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Ideps/openssl/android/x86/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Iout/openssl/android/armeabi-v7a/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Lout/openssl/android/armeabi-v7a/usr/local/lib
|
||||
$(ANDROID_ARM64_TARGETS): CFLAGS += -Iout/openssl/android/arm64-v8a/usr/local/include
|
||||
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Lout/openssl/android/arm64-v8a/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Iout/openssl/android/x86/usr/local/include
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Wno-atomic-alignment
|
||||
$(ANDROID_X86_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86/usr/local/lib
|
||||
$(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/include
|
||||
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86_64/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): LDFLAGS += -Lout/openssl/android/x86/usr/local/lib
|
||||
$(ANDROID_X86_64_TARGETS): CFLAGS += -Iout/openssl/android/x86_64/usr/local/include
|
||||
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Lout/openssl/android/x86_64/usr/local/lib
|
||||
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
|
||||
$(DEADSTRIP_TARGETS): LDFLAGS += -Wl,--gc-sections
|
||||
$(IOS_TARGETS): CFLAGS += -mios-version-min=9.0 -Ideps/openssl/ios/ios64-xcrun/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
|
||||
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
|
||||
$(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib
|
||||
$(MACOS_TARGETS): LDFLAGS += -Wl,-dead_strip
|
||||
$(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections -Wl,--as-needed
|
||||
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
$(IOS_TARGETS): CFLAGS += -Iout/openssl/ios/ios64-xcrun/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/ios/ios64-xcrun/usr/local/lib
|
||||
else
|
||||
$(IOS_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/ios64-cross/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
|
||||
endif
|
||||
$(IOSSIM_TARGETS): CFLAGS += -Iout/openssl/ios/iossimulator-xcrun/usr/local/include
|
||||
$(IOSSIM_TARGETS): LDFLAGS += -Lout/openssl/ios/iossimulator-xcrun/usr/local/lib
|
||||
$(HOST_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
|
||||
$(HOST_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
|
||||
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
all: appimage
|
||||
all: appimage out/release/tildefriends.standalone
|
||||
endif
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
|
||||
@ -246,13 +352,14 @@ endif
|
||||
|
||||
get_objs = \
|
||||
$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \
|
||||
$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
|
||||
$(foreach build_type,windebug winrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_win))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androiddebug-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
|
||||
$(foreach build_type,macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
|
||||
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_ios))))) \
|
||||
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86)))))
|
||||
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease macosdebug-arm macosrelease-arm macosdebug-x86_64 macosrelease-x86_64,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
|
||||
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86))))) \
|
||||
$(if $(findstring Darwin,$(UNAME_S)),$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos)))))) \
|
||||
$(if $(findstring Darwin,$(UNAME_S)),,$(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))))
|
||||
|
||||
APP_SOURCES := $(wildcard src/*.c)
|
||||
APP_SOURCES_ios := $(wildcard src/*.m)
|
||||
@ -274,10 +381,12 @@ $(APP_OBJS): CFLAGS += \
|
||||
-Ideps/valgrind \
|
||||
-Wdouble-promotion \
|
||||
-Werror
|
||||
ifneq ($(UNAME_S),Darwin)
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/macos% $(BUILD_DIR)/ios%,$(APP_OBJS)): CFLAGS += \
|
||||
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/ios% $(BUILD_DIR)/macos%,$(APP_OBJS)): CFLAGS += \
|
||||
-fanalyzer
|
||||
endif
|
||||
endif
|
||||
|
||||
ARES_SOURCES := \
|
||||
deps/c-ares/src/lib/ares_addrinfo2hostent.c \
|
||||
@ -505,15 +614,16 @@ $(UV_OBJS): CFLAGS += \
|
||||
-Ideps/libuv/include \
|
||||
-Ideps/libuv/src \
|
||||
-Wno-dangling-pointer \
|
||||
-Wno-format-truncation \
|
||||
-Wno-incompatible-pointer-types \
|
||||
-Wno-maybe-uninitialized \
|
||||
-Wno-nonnull \
|
||||
-Wno-sign-compare \
|
||||
-Wno-unknown-attributes \
|
||||
-Wno-unused-but-set-parameter \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-result \
|
||||
-Wno-unused-variable \
|
||||
-Wno-nonnull
|
||||
$(UV_OBJS): CFLAGS += -fno-lto
|
||||
-Wno-unused-variable
|
||||
$(filter out/win%,$(UV_OBJS)): \
|
||||
CFLAGS += \
|
||||
-Wno-cast-function-type \
|
||||
@ -540,6 +650,7 @@ SODIUM_SOURCES := \
|
||||
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
||||
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
|
||||
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
|
||||
deps/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c \
|
||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
|
||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
||||
@ -593,7 +704,6 @@ ifneq ($(UNAME_S),OpenBSD)
|
||||
$(filter-out $(BUILD_DIR)/win%,$(SODIUM_OBJS)): CFLAGS += \
|
||||
-DHAVE_ALLOCA_H
|
||||
endif
|
||||
$(SODIUM_OBJS): CFLAGS := $(filter-out -flto,$(CFLAGS))
|
||||
|
||||
SQLITE_SOURCES := deps/sqlite/sqlite3.c
|
||||
SQLITE_OBJS := $(call get_objs,SQLITE_SOURCES)
|
||||
@ -610,12 +720,12 @@ $(SQLITE_OBJS): CFLAGS += \
|
||||
-DSQLITE_MAX_COMPOUND_SELECT=300 \
|
||||
-DSQLITE_MAX_EXPR_DEPTH=40 \
|
||||
-DSQLITE_MAX_FUNCTION_ARG=8 \
|
||||
-DSQLITE_MAX_LENGTH=5242880 \
|
||||
-DSQLITE_MAX_LENGTH=10485760 \
|
||||
-DSQLITE_MAX_LIKE_PATTERN_LENGTH=50 \
|
||||
-DSQLITE_MAX_SQL_LENGTH=100000 \
|
||||
-DSQLITE_MAX_TRIGGER_DEPTH=10 \
|
||||
-DSQLITE_MAX_VARIABLE_NUMBER=100 \
|
||||
-DSQLITE_MAX_VDBE_OP=25000 \
|
||||
-DSQLITE_MAX_VDBE_OP=50000 \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_DESERIALIZE \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
@ -634,7 +744,7 @@ $(SQLITE_OBJS): CFLAGS += \
|
||||
|
||||
QUICKJS_SOURCES := \
|
||||
deps/quickjs/cutils.c \
|
||||
deps/quickjs/libbf.c \
|
||||
deps/quickjs/dtoa.c \
|
||||
deps/quickjs/libregexp.c \
|
||||
deps/quickjs/libunicode.c \
|
||||
deps/quickjs/quickjs.c
|
||||
@ -711,12 +821,12 @@ $(MINIUNZIP_OBJS): CFLAGS += \
|
||||
LDFLAGS += \
|
||||
-pthread \
|
||||
-lm
|
||||
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS) $(filter-out $(HOST_TARGETS),$(MACOS_TARGETS)): LDFLAGS += \
|
||||
-lssl \
|
||||
-lcrypto
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
ifneq ($(UNAME_S),OpenBSD)
|
||||
debug release $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
-ldl
|
||||
endif
|
||||
endif
|
||||
@ -746,9 +856,16 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
-framework UIKit \
|
||||
-framework WebKit
|
||||
|
||||
unix: debug release
|
||||
win: windebug winrelease
|
||||
all: $(BUILD_TYPES)
|
||||
##
|
||||
## Common targets:
|
||||
##
|
||||
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
|
||||
debug: ## Build a debug executable for the current host platform.
|
||||
release: ## Build a release executable for the current host platform.
|
||||
armdebug: ## Cross-compile aarch64 debug on Linux.
|
||||
armrelease: ## Cross-compile aarch64 release on Linux.
|
||||
windebug: ## Cross-compile a debug win32 executable on Linux.
|
||||
winrelease: ## Cross-compile a release win32 executable on Linux.
|
||||
.PHONY: all win unix
|
||||
|
||||
ALL_APP_OBJS := \
|
||||
@ -806,35 +923,58 @@ src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
|
||||
-e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \
|
||||
$@
|
||||
|
||||
# Android support.
|
||||
src/ios/Info.plist : $(firstword $(MAKEFILE_LIST))
|
||||
@echo "[ios_version] $@"
|
||||
@cat $@ | \
|
||||
tr '\n' '^' | \
|
||||
sed -r \
|
||||
-e 's@(<key>CFBundleShortVersionString</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(VERSION_NUMBER:%-wip=%)\2@' \
|
||||
-e 's@(<key>CFBundleVersion</key>\^[[:space:]]*<string>)[[:digit:]]+(</string>)@\1$(VERSION_CODE_IOS)\2@' \
|
||||
-e 's@(<key>MinimumOSVersion</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(IPHONEOS_VERSION_MIN)\2@' | \
|
||||
tr '^' '\n' > \
|
||||
$@.tmp && mv $@.tmp $@ || rm -f $@.tmp
|
||||
|
||||
##
|
||||
## Android targets:
|
||||
##
|
||||
androiddebug: ## Build a debug 64-bit ARM Android APK.
|
||||
androidrelease: ## Build a release 64-bit ARM Android APK.
|
||||
androiddebug-armv7a: ## Build a debug 32-bit ARM Android APK.
|
||||
androidrelease-armv7a: ## Build a release 32-bit ARM Android APK.
|
||||
androiddebug-x86: ## Build a debug x86 Android APK.
|
||||
androidrelease-x86: ## Build a release x86 Android APK.
|
||||
androiddebug-x86_64: ## Build a debug x86_64 Android APK.
|
||||
androidrelease-x86_64: ## Build a release x86_64 Android APK.
|
||||
|
||||
out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "[aapt2] $@"
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/layout/activity_main.xml
|
||||
|
||||
out/res/drawable_icon.xml.flat: src/android/res/drawable/icon.xml
|
||||
out/res/drawable_%.xml.flat: src/android/res/drawable/%.xml
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "[aapt2] $@"
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/drawable/icon.xml
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ $(<)
|
||||
|
||||
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
|
||||
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
|
||||
@echo [aapt2 link] res.apk
|
||||
@mkdir -p out/apk/
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat \
|
||||
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
|
||||
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
-o out/apk/res.apk \
|
||||
--java out/gen/
|
||||
|
||||
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
|
||||
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
|
||||
@echo [aapt2 link] res.fdroid.apk
|
||||
@mkdir -p out/apk/
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
|
||||
@sed -e 's@drawable/icon@drawable/icon_fdroid@' src/android/AndroidManifest.xml > out/apk/AndroidManifest.fdroid.xml
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat \
|
||||
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
|
||||
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
|
||||
--rename-manifest-package com.unprompted.tildefriends.fdroid \
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
--manifest out/apk/AndroidManifest.fdroid.xml \
|
||||
-o out/apk/res.fdroid.apk \
|
||||
--java out/gen_fdroid/
|
||||
|
||||
@ -851,11 +991,12 @@ out/apk/classes.dex: $(CLASS_FILES)
|
||||
@$(ANDROID_BUILD_TOOLS)/d8 --lib $(ANDROID_PLATFORM)/android.jar --output $(dir $@) out/classes/com/unprompted/tildefriends/*.class
|
||||
|
||||
PACKAGE_DIRS := \
|
||||
apps/ \
|
||||
core/ \
|
||||
deps/codemirror/ \
|
||||
deps/prettier/ \
|
||||
deps/lit/
|
||||
apps \
|
||||
core \
|
||||
deps/codemirror \
|
||||
deps/prettier \
|
||||
deps/lit \
|
||||
deps/speedscope
|
||||
|
||||
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
||||
|
||||
@ -876,7 +1017,7 @@ $(BUNDLETOOL):
|
||||
@curl -q -L --create-dirs -o $@ $(BUNDLETOOL_URL)
|
||||
|
||||
out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGETS)) $(RAW_FILES) out/apk/res.apk src/android/AndroidManifest.xml $(BUNDLETOOL)
|
||||
@rm -rf out/aab/staging/
|
||||
@rm -rf out/aab/staging/ out/aab/base.zip
|
||||
@mkdir -p out/aab/staging
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link --proto-format -o out/aab/temporary.apk \
|
||||
-I $(ANDROID_PLATFORM)/android.jar \
|
||||
@ -885,6 +1026,7 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
-R out/res/layout_activity_main.xml.flat \
|
||||
-R out/res/drawable_icon.xml.flat \
|
||||
-R out/res/drawable_logo.xml.flat \
|
||||
--auto-add-overlay
|
||||
@unzip out/aab/temporary.apk -d out/aab/staging/
|
||||
@mkdir -p out/aab/staging/root/deps
|
||||
@ -895,14 +1037,11 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
@cp out/apk/classes.dex out/aab/staging/dex/
|
||||
@rm -fv out/base.zip
|
||||
@mkdir -p out/aab/staging/lib/arm64-v8a out/aab/staging/lib/armeabi-v7a out/aab/staging/lib/x86_64 out/aab/staging/lib/x86
|
||||
@cp out/androidrelease/tildefriends out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@cp out/androidrelease-armv7a/tildefriends out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@cp out/androidrelease-x86_64/tildefriends out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@cp out/androidrelease-x86/tildefriends out/aab/staging/lib/x86/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86/libtildefriends.so
|
||||
@mkdir -p out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64 out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease/tildefriends -o out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-armv7a/tildefriends -o out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-x86_64/tildefriends -o out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-x86/tildefriends -o out/aab/staging/lib/x86/libtildefriends.so
|
||||
@cp -r apps/ out/aab/staging/root/
|
||||
@rm -rf out/aab/staging/root/apps/welcome*
|
||||
@cp -r core/ out/aab/staging/root/
|
||||
@ -911,9 +1050,14 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
@cp -r deps/codemirror/ out/aab/staging/root/deps/
|
||||
@cd out/aab/staging/; zip -r ../base.zip *; cd ../../../
|
||||
@java -jar $(BUNDLETOOL) build-bundle --overwrite --config=src/android/BundleConfig.json --modules=out/aab/base.zip --output=$@
|
||||
@jarsigner -keystore .keys/android.jks $@ androidKey -storepass android
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-armv7a/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86_64/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym
|
||||
@cd out/aab/staging; zip -u ../../../$@ BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym; cd ../../../
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign -ks .keys/android.jks --ks-key-alias androidKey -ks-pass pass:android --min-sdk-version=$(ANDROID_MIN_SDK_VERSION) --alignment-preserved $@
|
||||
|
||||
aab: out/TildeFriends.aab
|
||||
aab: out/TildeFriends.aab ## Build an Android App Bundle.
|
||||
.PHONY: aab
|
||||
|
||||
out/TildeFriends.apks: out/TildeFriends.aab $(BUNDLETOOL)
|
||||
@ -973,27 +1117,39 @@ out/apk/TildeFriends-%.fdroid.unsigned.apk:
|
||||
|
||||
out/%.apk: out/apk/%.unsigned.apk
|
||||
@echo "[apksigner] $(notdir $@)"
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $<
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $<
|
||||
|
||||
out/%.zopfli.apk: out/%.apk
|
||||
@echo "[zopfli] $(notdir $@)"
|
||||
$(ANDROID_BUILD_TOOLS)/zipalign -f -z 4 $< $@.zopfli
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $@.zopfli
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $@.zopfli
|
||||
|
||||
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk
|
||||
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk ## Build an Android release APK.
|
||||
.PHONY: release-apk
|
||||
|
||||
apkgo: out/TildeFriends-arm-debug.apk
|
||||
fdroid: out/apk/TildeFriends-release.fdroid.unsigned.apk ## Build Android APK for distribution on F-Droid.
|
||||
.PHONY: fdroid
|
||||
|
||||
apkgo: out/TildeFriends-arm-debug.apk ## Build, install, and run a debug Android APK.
|
||||
@adb install -r $<
|
||||
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
||||
.PHONY: apkgo
|
||||
|
||||
releaseapkgo: out/TildeFriends-arm-release.apk
|
||||
releaseapkgo: out/TildeFriends-arm-release.apk ## Build, install, and run a release Android APK.
|
||||
@adb install -r $<
|
||||
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
||||
.PHONY: releaseapkgo
|
||||
|
||||
# iOS Support
|
||||
apklog: ## Display Android log output.
|
||||
@adb logcat *:S tildefriends
|
||||
.PHONY: apklog
|
||||
|
||||
##
|
||||
## iPhoneOS targets:
|
||||
##
|
||||
iosdebug: ## Build a debug iPhoneOS executable.
|
||||
iosrelease: ## Build a release iPhoneOS executable.
|
||||
|
||||
out/%.app/Info.plist: src/ios/Info.plist
|
||||
@mkdir -p $(dir $@)
|
||||
@cp -v $< $@
|
||||
@ -1002,14 +1158,27 @@ out/%.app/tildefriends.png: src/ios/tildefriends.png
|
||||
@cp -v $< $@
|
||||
|
||||
out/data.zip: $(RAW_FILES)
|
||||
@echo [zip] $@
|
||||
@zip -u $@ -q -9 $(RAW_FILES)
|
||||
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip
|
||||
out/zsign_build/zsign: $(wildcard deps/zsign/*.cpp deps/zsign/*.h deps/zsign/*.txt deps/zsign/common/*)
|
||||
@+echo [cmake] $@
|
||||
@cmake -B out/zsign_build deps/zsign
|
||||
@cmake --build out/zsign_build -- COLOR=0 VERBOSE=0 MAKESILENT=-s
|
||||
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
ZSIGN_DEP = out/zsign_build/zsign
|
||||
else
|
||||
ZSIGN_DEP =
|
||||
endif
|
||||
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip $(ZSIGN_DEP)
|
||||
@mkdir -p $(dir $@)
|
||||
@cp -v $< $@
|
||||
@cp -v $(filter-out out/zsign%,$<) $@
|
||||
@cp -v out/data.zip $(@D)/
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
@zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
|
||||
@mkdir -p $(realpath $(dir $@))/_CodeSignature
|
||||
@out/zsign_build/zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
|
||||
endif
|
||||
.SECONDARY:
|
||||
out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
|
||||
@ -1030,58 +1199,120 @@ out/%/tildefriends.standalone.exe: out/%/tildefriends.exe out/data.zip
|
||||
@cat $< out/data.zip > $@
|
||||
@chmod +x $@
|
||||
|
||||
iossimdebug-app: out/tildefriends-iossimdebug.app/tildefriends
|
||||
iossimrelease-app: out/tildefriends-iossimrelease.app/tildefriends
|
||||
iosdebug-app: out/tildefriends-iosdebug.app/tildefriends
|
||||
iosrelease-app: out/tildefriends-iosrelease.app/tildefriends
|
||||
iossimdebug-app: out/tildefriends-iossimdebug.app/tildefriends ## Build a debug iOS Simulator .app directory.
|
||||
iossimrelease-app: out/tildefriends-iossimrelease.app/tildefriends ## Build a release iOS Simulator .app directory.
|
||||
iosdebug-app: out/tildefriends-iosdebug.app/tildefriends ## Build a debug iOS .app directory.
|
||||
iosrelease-app: out/tildefriends-iosrelease.app/tildefriends ## Build a release iOS .app directory.
|
||||
|
||||
iosdebug-ipa: out/tildefriends-debug.ipa
|
||||
iosrelease-ipa: out/tildefriends-release.ipa
|
||||
iosdebug-ipa: out/tildefriends-debug.ipa ## Build a debug iOS .ipa.
|
||||
iosrelease-ipa: out/tildefriends-release.ipa ## Build a release iOS .ipa.
|
||||
.PHONY: iossimdebug-app iossimrelease-app iosdebug-app iosrelease-app
|
||||
|
||||
ios%go: out/tildefriends-ios%.app/tildefriends
|
||||
ideviceinstaller -i $(realpath $(dir $<))
|
||||
|
||||
iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends
|
||||
iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends ## Build, install, and run an iOS debug build.
|
||||
xcrun simctl install booted out/tildefriends-iossimdebug.app/
|
||||
xcrun simctl launch booted com.unprompted.tildefriends
|
||||
.PHONY: iossimdebuggo
|
||||
|
||||
apklog:
|
||||
@adb logcat *:S tildefriends
|
||||
.PHONY: apklog
|
||||
|
||||
fetchdeps:
|
||||
@echo "[fetch] sqlite"
|
||||
@test -f out/deps/sqlite.zip && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p out/deps/ && curl -q $(SQLITE_URL) -o out/deps/sqlite.zip)
|
||||
@test -d deps/sqlite/ && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p deps/sqlite/ && unzip -qDjo -d deps/sqlite/ out/deps/sqlite.zip)
|
||||
@echo -n $(SQLITE_URL) > out/deps/sqlite.txt
|
||||
@echo "[fetch] prettier"
|
||||
@test -f deps/prettier/standalone.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/standalone.mjs
|
||||
@test -f deps/prettier/html.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/html.mjs
|
||||
@test -f deps/prettier/babel.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/babel.mjs
|
||||
@test -f deps/prettier/estree.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/estree.mjs
|
||||
.PHONY: fetchdeps
|
||||
|
||||
ANDROID_DEPS := deps/openssl/android/arm64-v8a/usr/local/lib/libssl.a
|
||||
ANDROID_DEPS := out/openssl/android/arm64-v8a/usr/local/lib/libssl.a
|
||||
$(ANDROID_DEPS):
|
||||
+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android
|
||||
+@export ANDROID_NDK_ROOT=$(ANDROID_NDK)
|
||||
+@export BUILD_PLATFORM=android
|
||||
+@export TOOLCHAIN=$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64
|
||||
+@PATH="$$TOOLCHAIN/x86_64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86_64 SSL_TARGET=android-x86_64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/i686-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86 SSL_TARGET=android-x86 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/arm-linux-androideabi/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=armeabi-v7a SSL_TARGET=android-arm OPTIONS="--target=armv7a-linux-androideabi -Wl,--fix-cortex-a8 -D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/aarch64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=arm64-v8a SSL_TARGET=android-arm64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS)
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifneq ($(USE_SYSTEM_SSL),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 SSL_TARGET=linux-aarch64 tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/armdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/armrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=deps/ios_toolchain/target/bin:$$PATH \
|
||||
BUILD_TARGET=ios64-cross \
|
||||
SSL_TARGET=ios64-cross \
|
||||
CROSS_COMPILE=../../deps/ios_toolchain/target/bin/arm-apple-darwin11- \
|
||||
CROSS_TOP=../../deps/ios_toolchain/target \
|
||||
CROSS_SDK=iPhoneOS18.2.sdk \
|
||||
CC=clang \
|
||||
OPTIONS=-miphoneos-version-min=$(IPHONEOS_VERSION_MIN) \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-arm/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
|
||||
BUILD_TARGET=macos-arm \
|
||||
SSL_TARGET=darwin64-arm64 \
|
||||
CC=../../deps/macos_toolchain/bin/oa64-clang \
|
||||
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
|
||||
AR=../../deps/macos_toolchain/bin/arm64-apple-darwin24-ar \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/% $(BUILD_DIR)/macosdebug-arm/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
|
||||
BUILD_TARGET=macos-x86_64 \
|
||||
SSL_TARGET=darwin64-x86_64 \
|
||||
CC=../../deps/macos_toolchain/bin/o64-clang \
|
||||
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
|
||||
AR=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ar \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/% $(BUILD_DIR)/macosdebug-x86_64/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WIN),1)
|
||||
WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a
|
||||
WINDOWS_DEPS := out/openssl/$(UNAME_S)/mingw64/usr/local/lib/libssl.a
|
||||
$(WINDOWS_DEPS):
|
||||
+@tools/ssl-mingw64
|
||||
+@BUILD_TARGET=mingw64 SSL_TARGET=mingw64 OPTIONS="--cross-compile-prefix=x86_64-w64-mingw32-" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/win%,$(APP_OBJS)): | $(WINDOWS_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
IOS_DEPS := deps/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
|
||||
IOS_DEPS := out/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
|
||||
$(IOS_DEPS):
|
||||
+@tools/ssl-ios
|
||||
+@BUILD_PLATFORM=ios BUILD_TARGET=ios64-xcrun SSL_TARGET=ios64-xcrun OPTIONS="-fPIC -Wno-macro-redefined -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)" tools/ssl-local
|
||||
+@BUILD_PLATFORM=ios BUILD_TARGET=iossimulator-xcrun SSL_TARGET=iossimulator-xcrun OPTIONS="-fPIC -Wno-macro-redefined" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
|
||||
endif
|
||||
|
||||
out/macos%/tildefriends: out/macos%-arm/tildefriends out/macos%-x86_64/tildefriends
|
||||
@echo [lipo] $@
|
||||
@mkdir -p $(@D)
|
||||
@deps/macos_toolchain/bin/lipo -create -output $@ $^
|
||||
|
||||
##
|
||||
## Linux package targets:
|
||||
##
|
||||
|
||||
out/tildefriends-x86_64.AppImage: out/release/tildefriends out/data.zip
|
||||
@echo "[appimage] $$@"
|
||||
@rm -rf out/tildefriends.AppDir
|
||||
@ -1101,14 +1332,37 @@ out/tildefriends-x86_64.AppImage: out/release/tildefriends out/data.zip
|
||||
@cd out; ./appimagetool --appimage-extract; cd ..
|
||||
@cd out; unset SOURCE_DATE_EPOCH; PATH=$$PATH:squashfs-root/usr/bin ARCH=x86_64 squashfs-root/usr/bin/appimagetool -u 'zsync|https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage.zsync' tildefriends.AppDir tildefriends-x86_64.AppImage; cd ..
|
||||
|
||||
appimage: out/tildefriends-x86_64.AppImage
|
||||
appimage: out/tildefriends-x86_64.AppImage ## Build an AppImage.
|
||||
.PHONY: appimage
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
.PHONY: clean
|
||||
flatpak: out/ ## Build a flatpak.
|
||||
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak-builder --user --disable-rofiles-fuse --install-deps-from=flathub --install --repo=out/flatpak-repo out/flatpak src/com.unprompted.tildefriends.yml
|
||||
flatpak build-bundle out/flatpak-repo out/tildefriends.flatpak com.unprompted.tildefriends
|
||||
.PHONY: flatpak
|
||||
|
||||
tarball:
|
||||
##
|
||||
## Targets for release management:
|
||||
##
|
||||
|
||||
fetchdeps: ## Update various external sources that live in the tree that can't be pulled in as git submodules.
|
||||
@echo "[fetch] sqlite"
|
||||
@test -f out/deps/sqlite.zip && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p out/deps/ && curl -q $(SQLITE_URL) -o out/deps/sqlite.zip)
|
||||
@test -d deps/sqlite/ && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p deps/sqlite/ && unzip -qDjo -d deps/sqlite/ out/deps/sqlite.zip)
|
||||
@echo -n $(SQLITE_URL) > out/deps/sqlite.txt
|
||||
@echo "[fetch] prettier"
|
||||
@test -f deps/prettier/standalone.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/standalone.mjs
|
||||
@test -f deps/prettier/html.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/html.mjs
|
||||
@test -f deps/prettier/babel.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/babel.mjs
|
||||
@test -f deps/prettier/estree.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/estree.mjs
|
||||
.PHONY: fetchdeps
|
||||
|
||||
shots: ## Copy generated screenshots from `tildefriends test -t=auto` into place in the metadata/ directory.
|
||||
@echo [shots] $(wildcard out/screenshot*.png)
|
||||
@cp -f out/screenshot*.png metadata/en-US/images/phoneScreenshots/
|
||||
.PHONY: shots
|
||||
|
||||
tarball: ## Build an all-inclusive source tarball (.tar.xz).
|
||||
@echo [archive] out/tildefriends-$(VERSION_NUMBER).tar.xz
|
||||
@rm -rf out/tildefriends-$(VERSION_NUMBER)
|
||||
@mkdir -p out/tildefriends-$(VERSION_NUMBER)
|
||||
@ -1123,7 +1377,6 @@ tarball:
|
||||
--exclude=deps/libsodium/test \
|
||||
--exclude=deps/libuv/docs \
|
||||
--exclude=deps/libuv/test \
|
||||
--exclude=deps/openssl \
|
||||
--exclude=deps/speedscope/*.map \
|
||||
--exclude=deps/sqlite/shell.c \
|
||||
--exclude=deps/zlib/contrib/vstudio \
|
||||
@ -1133,7 +1386,21 @@ tarball:
|
||||
tildefriends-$(VERSION_NUMBER)
|
||||
.PHONY: tarball
|
||||
|
||||
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage tarball
|
||||
dist: ## Build versions of all distributables for release.
|
||||
dist: release-apk aab out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
dist: iosrelease-ipa
|
||||
endif
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
dist: out/macosrelease/tildefriends.standalone
|
||||
endif
|
||||
ifeq ($(HAVE_WIN),1)
|
||||
dist: out/winrelease/tildefriends.standalone.exe
|
||||
endif
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
dist: out/armrelease/tildefriends.standalone
|
||||
endif
|
||||
dist:
|
||||
@mkdir -p dist/
|
||||
@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
|
||||
@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
|
||||
@ -1141,8 +1408,10 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
|
||||
@cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
|
||||
@cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
@test $(HAVE_LINUX_IOS) && echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@test $(HAVE_LINUX_IOS) && cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
@test $(HAVE_LINUX_MACOS) && echo "[cp] tildefriends-macos-$(VERSION_NUMBER)"
|
||||
@test $(HAVE_LINUX_MACOS) && cp out/macosrelease/tildefriends.standalone dist/tildefriends-macos-$(VERSION_NUMBER)
|
||||
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
|
||||
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).aab"
|
||||
@ -1151,26 +1420,82 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
|
||||
@cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk
|
||||
@echo "[cp] TildeFriends-x86_64-$(VERSION_NUMBER).AppImage"
|
||||
@cp out/tildefriends-x86_64.AppImage dist/TildeFriends-x86_64-$(VERSION_NUMBER).AppImage
|
||||
@echo "[cp] tildefriends-linux-$(UNAME_M)-$(VERSION_NUMBER)"
|
||||
@cp out/release/tildefriends.standalone dist/tildefriends-linux-$(UNAME_M)-$(VERSION_NUMBER)
|
||||
@test $(HAVE_CROSS_AARCH64) && echo "[cp] tildefriends-linux-aarch64-$(VERSION_NUMBER)"
|
||||
@test $(HAVE_CROSS_AARCH64) && cp out/armrelease/tildefriends.standalone dist/tildefriends-linux-aarch64-$(VERSION_NUMBER)
|
||||
.PHONY: dist
|
||||
|
||||
dist-test: dist
|
||||
dist-test: dist ## Exercise some built distributable files, making sure they work as intended.
|
||||
@tar -xf tildefriends-$(VERSION_NUMBER).tar.xz
|
||||
@$(MAKE) -C tildefriends-$(VERSION_NUMBER)/ debug release
|
||||
@docker build tildefriends-$(VERSION_NUMBER)/
|
||||
@rm -rf tildefriends-$(VERSION_NUMBER)
|
||||
.PHONY: dist-test
|
||||
|
||||
format:
|
||||
dist-ios: iosrelease-app
|
||||
rm -rfv out/Payload out/tildefriends.ipa
|
||||
mkdir -p out/Payload/tildefriends.app
|
||||
cp -avR out/tildefriends-iosrelease.app/* out/Payload/tildefriends.app/
|
||||
cp src/ios/tildefriends.png out/Payload/tildefriends.app/
|
||||
xcrun -sdk iphoneos actool --compile out/Payload/tildefriends.app/ --platform iphoneos --minimum-deployment-target $(IPHONEOS_VERSION_MIN) --app-icon AppIcon src/ios/icons/Assets.xcassets src/ios/icons/*.png --output-partial-info-plist out/actool.plist
|
||||
cp src/ios/distribution.mobileprovision out/Payload/tildefriends.app/embedded.mobileprovision
|
||||
xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/Payload/tildefriends.app
|
||||
cd out; zip -r tildefriends.ipa Payload; cd ..
|
||||
xcrun -sdk iphoneos altool --upload-app -f out/tildefriends.ipa -t ios -u $$(cat .keys/altool-user) -p $$(cat .keys/altool-password)
|
||||
|
||||
##
|
||||
## Targets for tidying up:
|
||||
##
|
||||
|
||||
format: ## Standardize formatting of C source.
|
||||
@clang-format -i $(wildcard src/*.c src/*.h src/*.m)
|
||||
.PHONY: format
|
||||
|
||||
prettier:
|
||||
prettier: ## Standardize formatting of JavaScript and Markdown source.
|
||||
@npm run prettier
|
||||
.PHONY: prettier
|
||||
|
||||
docs:
|
||||
clean: ## Clean all generated files from the out/ directory.
|
||||
rm -rf $(BUILD_DIR)
|
||||
.PHONY: clean
|
||||
|
||||
##
|
||||
## Documentation:
|
||||
##
|
||||
help: ## Display this help message.
|
||||
@awk \
|
||||
-F: \
|
||||
-vG=$$(tput setaf 2) \
|
||||
-vO=$$(tput setaf 3) \
|
||||
-vB=$$(tput setaf 4) \
|
||||
-vM=$$(tput setaf 5) \
|
||||
-vC=$$(tput setaf 6) \
|
||||
-vR=$$(tput sgr0) ' \
|
||||
/^## ==.*==$$/ { sub(/^## ?/, ""); printf "%s%s%s\n", C, $$0, R } \
|
||||
/^##.*:=.*/ { sub(/^## ?/, ""); sub(/:=/, ":"); printf " %s%-20s%s %s%s%s\n", M, $$1, R, O, $$2, R } \
|
||||
/^##/ { sub(/^## ?/, ""); print $$0 } \
|
||||
/^[[:alnum:]-]+:.*##/ { \
|
||||
sub(/:.*##\s?/, ":"); \
|
||||
printf " %s%-21s%s %s%s%s\n", G, $$1, R, O, $$2, R \
|
||||
} \
|
||||
' < $(filter-out %.d,$(MAKEFILE_LIST))
|
||||
@echo "" # Blank line.
|
||||
.PHONY: help
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
docs: debug
|
||||
docs: ## Build HTML docs.
|
||||
@echo '# CLI Usage\n' > docs/usage.md
|
||||
@echo "## tildefriends -h" >> docs/usage.md
|
||||
@echo '\n```' >> docs/usage.md
|
||||
@out/debug/tildefriends -h >> docs/usage.md
|
||||
@echo '```' >> docs/usage.md
|
||||
@for command in $$(out/debug/tildefriends -h | grep -Po '[A-Za-z_]*(?= - )'); do
|
||||
@ echo "\n## tildefriends $$command -h" >> docs/usage.md
|
||||
@ echo '\n```' >> docs/usage.md
|
||||
@ out/debug/tildefriends $$command -h >> docs/usage.md
|
||||
@ echo '```' >> docs/usage.md
|
||||
@done
|
||||
@doxygen
|
||||
.PHONY: docs
|
||||
|
||||
fdroid: out/apk/TildeFriends-release.fdroid.unsigned.apk
|
||||
.PHONY: fdroid
|
||||
|
59
README.md
59
README.md
@ -1,23 +1,18 @@
|
||||
# Tilde Friends
|
||||
|
||||
Tilde Friends is a tool for making and sharing.
|
||||
Tilde Friends participates in the Secure Scuttlebutt decentralized social
|
||||
network while also functioning as a platform for making, sharing, and running
|
||||
web applications.
|
||||
|
||||
A public instance lives at https://www.tildefriends.net/.
|
||||
|
||||
It is both a peer-to-peer social network client, participating in Secure
|
||||
Scuttlebutt, as well as a platform for writing and running web applications.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Make it easy and fun to run all sorts of web applications.
|
||||
2. Provide security that is easy to understand and protects your data.
|
||||
3. Make creating and sharing web applications accessible to anyone with a
|
||||
browser.
|
||||
1. Be the fanciest, best-maintained Secure Scuttlebutt client in town.
|
||||
1. Make it easy to make, share, and run all sorts of applications while
|
||||
respecting the privacy and safety of your data.
|
||||
|
||||
## Building
|
||||
|
||||
Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. Builds for
|
||||
all of those host platforms plus mingw64, iOS, and android.
|
||||
## Getting the Source
|
||||
|
||||
Tilde Friends uses git submodules, so either:
|
||||
|
||||
@ -35,29 +30,41 @@ git submodule update --init --recursive
|
||||
|
||||
The `.tar.xz` source releases are all-inclusive.
|
||||
|
||||
1. On Linux only, system OpenSSL libraries (`libssl-dev`, in debian-speak) is
|
||||
assumed to be available.
|
||||
2. To build, run `make debug` or `make release`. An executable will be
|
||||
generated in a subdirectory of `out/`.
|
||||
3. It's possible to build for Android, iOS, and Windows on Linux, if you have
|
||||
the right dependencies in the right places. `make windebug winrelease
|
||||
iosdebug-ipa iosrelease-ipa release-apk`.
|
||||
4. To build in docker, `docker build .`.
|
||||
5. `make format` will normalize formatting to the coding standard.
|
||||
## Building
|
||||
|
||||
Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. It's possible
|
||||
to build for Android, iOS, and Windows on Linux, if you have the right
|
||||
dependencies in the right places.
|
||||
|
||||
### Requirements
|
||||
|
||||
System OpenSSL libraries are assumed to be available on Haiku and OpenBSD.
|
||||
|
||||
On MacOS, Xcode's command-line tools are expected to be available.
|
||||
|
||||
### Build Commands
|
||||
|
||||
Run `make` with no arguments to see available build targets and options. `make
|
||||
debug` is a good place to start.
|
||||
|
||||
To build in docker, `docker build .`.
|
||||
|
||||
`make format` and `make prettier` will normalize formatting to the coding
|
||||
standard.
|
||||
|
||||
## Running
|
||||
|
||||
By default, running the built `tildefriends` executable will start a web server
|
||||
at <http://localhost:12345/>. `tildefriends -h` lists further options.
|
||||
By default, running the built `out/debug/tildefriends` executable will start a
|
||||
web server at <http://localhost:12345/>. `tildefriends -h` lists further
|
||||
options.
|
||||
|
||||
The first user to create an account and log in will be granted administrative
|
||||
privileges. Further administration can be done at
|
||||
privileges. Further administration can be done in the `admin` app at
|
||||
<http://localhost:12345/~core/admin/>.
|
||||
|
||||
## Documentation
|
||||
|
||||
Docs are a work in progress:
|
||||
<https://www.tildefriends.net/~cory/wiki/#test-wiki/tf-app-quick-reference>.
|
||||
Docs live here: <https://docs.tildefriends.net/>.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🎛",
|
||||
"previous": "&R49FywYF8CXPhoSEydLbSCgvCddeyTiBwGuDU/gqY+M=.sha256"
|
||||
"previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256"
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ ${description.value}</textarea
|
||||
</button>
|
||||
</li>
|
||||
`;
|
||||
} else {
|
||||
} else if (description.type != 'hidden') {
|
||||
return html`
|
||||
<li class="w3-row">
|
||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${title_case(key)}</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📜",
|
||||
"previous": "&miGORZ8BwjHg2YO0t4bms6SI28XWPYqnqOZ8u9zsbZc=.sha256"
|
||||
"previous": "&BEf0nraBdHk/+PWqx6tOSu5rheWVaxaL7orAOz3285M=.sha256"
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ function* treeify(prefix, o) {
|
||||
|
||||
function markdown(md) {
|
||||
let parsed = new commonmark.Parser().parse(md ?? '*undocumented*');
|
||||
return new commonmark.HtmlRenderer().render(parsed);
|
||||
return new commonmark.HtmlRenderer({safe: true}).render(parsed);
|
||||
}
|
||||
|
||||
function document(api) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💻",
|
||||
"previous": "&icsPplXHgmpkbNWyo/WTvRdT/A8BXxW4lJixOtP4ueQ=.sha256"
|
||||
"previous": "&sFRTDn/RpxP1NJeECXHrXKwCRUJsEOEDVaCMPl50zpM=.sha256"
|
||||
}
|
||||
|
@ -19,10 +19,6 @@ async function fetch_info(apps) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
async function fetch_shared_apps() {
|
||||
let messages = {};
|
||||
|
||||
@ -69,17 +65,17 @@ async function main() {
|
||||
const stylesheet = `
|
||||
body {
|
||||
color: whitesmoke;
|
||||
font-family: sans-serif;
|
||||
margin: 16px;
|
||||
margin: 8px;
|
||||
}
|
||||
.container {
|
||||
|
||||
.iconbox {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 64px);
|
||||
gap: 1em;
|
||||
justify-content: space-around;
|
||||
background-color: #ffffff10;
|
||||
border: 2px solid #073642;
|
||||
border-radius: 8px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
}
|
||||
|
||||
.iconbox::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.app {
|
||||
@ -101,16 +97,28 @@ async function main() {
|
||||
`;
|
||||
|
||||
const body = `
|
||||
<h1 style="text-shadow: #808080 0 0 10px;">Welcome to Tilde Friends.</h1>
|
||||
<h1>Welcome to Tilde Friends</h1>
|
||||
|
||||
<h2>your apps</h2>
|
||||
<div id="apps" class="container"></div>
|
||||
<div class="w3-card-4 w3-margin-top">
|
||||
<header class="w3-container w3-light-blue">
|
||||
<h2>Your Apps</h2>
|
||||
</header>
|
||||
<div id="apps" class="w3-indigo iconbox"></div>
|
||||
</div>
|
||||
|
||||
<h2>shared apps</h2>
|
||||
<div id="shared_apps" class="container"></div>
|
||||
<div class="w3-card-4 w3-margin-top">
|
||||
<header class="w3-container w3-light-blue">
|
||||
<h2>Shared Apps</h2>
|
||||
</header>
|
||||
<div id="shared_apps" class="w3-indigo iconbox"></div>
|
||||
</div>
|
||||
|
||||
<h2>core apps</h2>
|
||||
<div id="core_apps" class="container"></div>
|
||||
<div class="w3-card-4 w3-margin-top">
|
||||
<header class="w3-container w3-light-blue">
|
||||
<h2>Core Apps</h2>
|
||||
</header>
|
||||
<div id="core_apps" class="w3-indigo iconbox"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const script = `
|
||||
@ -126,9 +134,13 @@ async function main() {
|
||||
|
||||
// For each app in the provided list
|
||||
for (let app of Object.keys(apps).sort()) {
|
||||
|
||||
// Create the item
|
||||
let div = list.appendChild(document.createElement('div'));
|
||||
let inline = document.createElement('div');
|
||||
inline.style.display = 'inline-block';
|
||||
inline.classList.add('w3-button');
|
||||
list.appendChild(inline);
|
||||
let div = document.createElement('div');
|
||||
inline.appendChild(div);
|
||||
div.classList.add('app');
|
||||
|
||||
// The app's icon
|
||||
@ -161,12 +173,13 @@ async function main() {
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="w3.css"></link>
|
||||
<style>
|
||||
${stylesheet}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="w3-darkgray">
|
||||
${body}
|
||||
</body>
|
||||
|
||||
|
251
apps/apps/w3.css
Normal file
251
apps/apps/w3.css
Normal file
@ -0,0 +1,251 @@
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🪵",
|
||||
"previous": "&TIrBnpN3iz3O9L9MCCteAcVJZjA83EKdcfu4SCM76VE=.sha256"
|
||||
"previous": "&3jabNEk6W2uolzTvfXX6fcWF50N3501vtgZ6ZxFVJ1s=.sha256"
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ export async function get_blog_message(id) {
|
||||
}
|
||||
|
||||
export function markdown(md) {
|
||||
let reader = new commonmark.Parser({safe: true});
|
||||
let writer = new commonmark.HtmlRenderer();
|
||||
let reader = new commonmark.Parser();
|
||||
let writer = new commonmark.HtmlRenderer({safe: true});
|
||||
let parsed = reader.parse(md || '');
|
||||
let walker = parsed.walker();
|
||||
let event, node;
|
||||
|
42
apps/blog/lit-all.min.js
vendored
42
apps/blog/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💽"
|
||||
"emoji": "💽",
|
||||
"previous": "&uQzkIe/Aj8yNhLKe3hEq+5fEJsGwIUx8NVBjJKwoV2U=.sha256"
|
||||
}
|
||||
|
@ -51,6 +51,19 @@ async function key_list(db) {
|
||||
app.setDocument(doc);
|
||||
}
|
||||
|
||||
function load() {
|
||||
if (core.user?.credentials?.session) {
|
||||
database_list();
|
||||
} else {
|
||||
app.setDocument(`<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="background: #888">
|
||||
<h1>Must be signed in to examine databases.</h1>
|
||||
</body>
|
||||
</html>`);
|
||||
}
|
||||
}
|
||||
|
||||
core.register('message', async function (message) {
|
||||
if (message.event == 'hashChange') {
|
||||
let hash = message.hash.substring(1);
|
||||
@ -62,9 +75,9 @@ core.register('message', async function (message) {
|
||||
} else if (hash.length) {
|
||||
key_list(await database(hash.split(':').slice(1).join(':')));
|
||||
} else {
|
||||
database_list();
|
||||
load();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
database_list();
|
||||
load();
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "➡️"
|
||||
"emoji": "➡️",
|
||||
"previous": "&YDDSzbRD8NFAykYlZnk4r4hAK5qXjT5LmKE6rhS1s+A=.sha256"
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ async function contacts_internal(id, last_row_id, following, max_row_id) {
|
||||
result.blocking = result.blocking || {};
|
||||
let contacts = await query(
|
||||
`
|
||||
SELECT content FROM messages
|
||||
SELECT json(content) AS content FROM messages
|
||||
WHERE author = ? AND
|
||||
rowid > ? AND
|
||||
rowid <= ? AND
|
||||
@ -189,50 +189,6 @@ async function fetch_about(db, ids, users) {
|
||||
return Object.assign({}, users);
|
||||
}
|
||||
|
||||
async function getAbout(db, id) {
|
||||
if (g_about_cache[id]) {
|
||||
return g_about_cache[id];
|
||||
}
|
||||
let o = await db.get(id + ':about');
|
||||
const k_version = 4;
|
||||
let f = o ? JSON.parse(o) : o;
|
||||
if (!f || f.version != k_version) {
|
||||
f = {about: {}, sequence: 0, version: k_version};
|
||||
}
|
||||
await ssb.sqlAsync(
|
||||
'SELECT ' +
|
||||
' sequence, ' +
|
||||
' content ' +
|
||||
'FROM messages ' +
|
||||
'WHERE ' +
|
||||
' author = ?1 AND ' +
|
||||
' sequence > ?2 AND ' +
|
||||
" json_extract(content, '$.type') = 'about' AND " +
|
||||
" json_extract(content, '$.about') = ?1 " +
|
||||
'UNION SELECT MAX(sequence) as sequence, NULL FROM messages WHERE author = ?1 ' +
|
||||
'ORDER BY sequence',
|
||||
[id, f.sequence],
|
||||
function (row) {
|
||||
f.sequence = row.sequence;
|
||||
if (row.content) {
|
||||
let about = {};
|
||||
try {
|
||||
about = JSON.parse(row.content);
|
||||
} catch {}
|
||||
delete about.about;
|
||||
delete about.type;
|
||||
f.about = Object.assign(f.about, about);
|
||||
}
|
||||
}
|
||||
);
|
||||
let j = JSON.stringify(f);
|
||||
if (o != j) {
|
||||
await db.set(id + ':about', j);
|
||||
}
|
||||
g_about_cache[id] = f.about;
|
||||
return f.about;
|
||||
}
|
||||
|
||||
async function getSize(db, id) {
|
||||
let size = 0;
|
||||
await ssb.sqlAsync(
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🪪",
|
||||
"previous": "&zxsmzdLKsiG/WZt/Gw7JOxepgypoktNNbIyWiyFiJVc=.sha256"
|
||||
"previous": "&5kw/2PgcySwOYCmAkjHTR2xTkIx3i7UjQmtQ8MfgWw8=.sha256"
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import * as tfrpc from '/tfrpc.js';
|
||||
|
||||
const is_admin = core.user?.credentials?.permissions?.administration;
|
||||
|
||||
tfrpc.register(async function get_private_key(id) {
|
||||
return bip39Words(await ssb.getPrivateKey(id));
|
||||
});
|
||||
@ -15,6 +17,9 @@ tfrpc.register(async function delete_id(id) {
|
||||
tfrpc.register(async function reload() {
|
||||
await main();
|
||||
});
|
||||
tfrpc.register(async function make_server(id) {
|
||||
return await ssb.swapWithServerIdentity(id);
|
||||
});
|
||||
|
||||
async function main() {
|
||||
let ids = await ssb.getIdentities();
|
||||
@ -99,6 +104,16 @@ async function main() {
|
||||
alert('Error deleting ID: ' + e);
|
||||
}
|
||||
}
|
||||
handler.make_server = async function make_server(event) {
|
||||
let id = event.srcElement.dataset.id;
|
||||
try {
|
||||
if (confirm('Are you sure you want to make "' + id + '" the server identity?\\n\\nFor it to take effect, you will need to both sign in again and restart Tilde Friends.')) {
|
||||
await tfrpc.rpc.make_server(id);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error making server ID: ' + e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<header class="w3-theme w3-padding"><h1>SSB Identity Management</h1></header>
|
||||
<div class="w3-card-4 w3-margin">
|
||||
@ -117,13 +132,14 @@ async function main() {
|
||||
<div class="w3-card-4 w3-margin">
|
||||
<header class="w3-container w3-theme-l2"><h2>Identities</h2></header>
|
||||
<ul class="w3-ul">` +
|
||||
ids
|
||||
(ids ?? [])
|
||||
.map(
|
||||
(
|
||||
id
|
||||
) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
|
||||
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
|
||||
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
|
||||
${is_admin && id != server_id ? `<button onclick="handler.make_server(event)" data-id="${id}" class="w3-button w3-theme">Make Server Identity</button>` : ''}
|
||||
${id}${id == server_id ? ' <div class="w3-tag w3-theme-l4 w3-round">🖥 local server</div>' : ''}
|
||||
</li>`
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
5
apps/intro.json
Normal file
5
apps/intro.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💡",
|
||||
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
|
||||
}
|
16
apps/intro/app.js
Normal file
16
apps/intro/app.js
Normal file
@ -0,0 +1,16 @@
|
||||
import * as tfrpc from '/tfrpc.js';
|
||||
|
||||
async function main() {
|
||||
await app.setDocument(utf8Decode(getFile('index.html')));
|
||||
}
|
||||
|
||||
tfrpc.register(async function complete() {
|
||||
if (
|
||||
core.user?.credentials?.permissions?.administration &&
|
||||
(await core.globalSettingsGet('index')) == '/~core/intro/'
|
||||
) {
|
||||
return await core.globalSettingsSet('index', '/~core/ssb/');
|
||||
}
|
||||
});
|
||||
|
||||
main();
|
286
apps/intro/index.html
Normal file
286
apps/intro/index.html
Normal file
@ -0,0 +1,286 @@
|
||||
<!doctype html>
|
||||
<html style="height: 100%; margin: 0; padding: 0; box-sizing: border-box">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="w3.css" />
|
||||
<style>
|
||||
.slide {
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.dot {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.w3-left,
|
||||
.w3-right {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
"
|
||||
class="w3-flex w3-dark-gray w3-center"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
contain: content;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
"
|
||||
>
|
||||
<div class="slide">
|
||||
<div
|
||||
class="w3-content w3-xlarge w3-card-4 w3-blue w3-panel w3-padding-32 w3-round-xlarge"
|
||||
style="margin: 32px"
|
||||
>
|
||||
<div>
|
||||
<div>Welcome to</div>
|
||||
<div>~😎 Tilde Friends.</div>
|
||||
</div>
|
||||
<footer>
|
||||
<button class="w3-button w3-yellow proceed">Next</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-card-4 w3-gray" style="width: 90%">
|
||||
<header class="w3-container w3-blue w3-xlarge">
|
||||
<h1>This brief tutorial will introduce:</h1>
|
||||
</header>
|
||||
<ul class="w3-large w3-left-align">
|
||||
<li><b>Secure Scuttlebutt</b>, a decentralized social network.</li>
|
||||
<li>
|
||||
<b>Tilde Friends</b>, the application platform that you are using
|
||||
right now.
|
||||
</li>
|
||||
<li>
|
||||
<b>How to get started</b> if you want to get gossiping right away.
|
||||
</li>
|
||||
</ul>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow proceed">Onward</button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue">
|
||||
<h1>💻Secure Scuttlebutt in a Nutshell🦀</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>
|
||||
Secure Scuttlebutt is a social network whose technical operation
|
||||
attempts to mirror human social interaction.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
You can create your own account and post to your own feed on
|
||||
your own device. This is all <b>local</b> with no external
|
||||
communication. This puts you fully in control of your own words
|
||||
and actions.
|
||||
</li>
|
||||
<li>
|
||||
Before you can interact with others, you need to
|
||||
<b>connect over the network</b>, either directly to your friends
|
||||
(i.e., peer-to-peer between your phones on coffee shop Wi-Fi) or
|
||||
to 🚪<i>rooms</i> and 🍻<i>pubs</i> (hint: search the web for
|
||||
<i>#ssbroom</i>).
|
||||
</li>
|
||||
<li>
|
||||
Who you choose to <b>follow</b> determines what you see, with
|
||||
most people choosing to see messages from friends and friends of
|
||||
those friends. If you encounter content you'd rather not see,
|
||||
<b>block</b> the offending account to improve the experience for
|
||||
you and your followers.
|
||||
</li>
|
||||
<li>
|
||||
Your feed is an <b>immutable</b> log of your activity. Post with
|
||||
care, because like your words in real life, posts can't be taken
|
||||
back.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<a
|
||||
class="w3-button w3-light-gray"
|
||||
href="https://scuttlebutt.nz/"
|
||||
target="_blank"
|
||||
>See scuttlebutt.nz</a
|
||||
>
|
||||
<button class="w3-button w3-yellow proceed">Got It</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue w3-center">
|
||||
<h1>~😎 Let's Talk Tilde Friends ~😎</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>
|
||||
Tilde Friends is an application platform that is an application of
|
||||
its own.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
This intro is a Tilde Friends app. You can click <b>edit</b> at
|
||||
the top to look under the hood and make changes.
|
||||
</li>
|
||||
<li>
|
||||
It is already possible to make and share new applications using
|
||||
only Tilde Friends and Secure Scuttlebutt without having to set
|
||||
up development environments, configure web servers, register
|
||||
domain names, or pay for hosting services.
|
||||
</li>
|
||||
<li>
|
||||
But it's also set up so that you can't just break an app that
|
||||
everybody is using or do malicious things with personal content.
|
||||
There are <b>protections</b> in place like an operating system.
|
||||
The intent is also for it to be <b>safe</b> to run strange apps
|
||||
without worrying about adverse effects.
|
||||
</li>
|
||||
<li>
|
||||
But this is all a big 🚧work in progress🚧 and
|
||||
<b>experiment</b>. Let's see where it takes us.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow proceed">Okay</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue w3-center">
|
||||
<h1>🦀Let's Get this Tilde Friends Party Started🎉</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>The button below will take you to the Secure Scuttlebutt app.</p>
|
||||
<ul>
|
||||
<li>
|
||||
Remember:
|
||||
<ol>
|
||||
<li>You are in charge. This is all on your device.</li>
|
||||
<li>
|
||||
Make network connections to exchange messages with others.
|
||||
</li>
|
||||
<li>
|
||||
Follow more accounts to see more content, and block those
|
||||
posting content you'd rather not see.
|
||||
</li>
|
||||
<li>
|
||||
Be respectful, and consider the consequences of what you
|
||||
post.
|
||||
</li>
|
||||
<li>
|
||||
This is all under active development. Exercise patience, and
|
||||
report issues encountered.
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
To see this tutorial again later, select <b>apps</b> ->
|
||||
<b>Core Apps</b> -> <b>intro</b>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow" id="complete">Let's Go!</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w3-text-white w3-xlarge w3-center w3-flex"
|
||||
style="
|
||||
width: 100%;
|
||||
flex: 0 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
"
|
||||
>
|
||||
<div class="w3-jumbo" id="left" style="flex: 1 0; cursor: pointer">
|
||||
❮
|
||||
</div>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<div class="w3-jumbo" style="flex: 1 0; cursor: pointer" id="right">
|
||||
❯
|
||||
</div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
let index = 0;
|
||||
function set(i) {
|
||||
show(i - index);
|
||||
}
|
||||
function show(delta) {
|
||||
let slides = [...document.getElementsByClassName('slide')];
|
||||
let dots = [...document.getElementsByClassName('dot')];
|
||||
index = (index + delta + slides.length) % slides.length;
|
||||
for (let slide of slides) {
|
||||
slide.style.display =
|
||||
slides.indexOf(slide) == index ? 'block' : 'none';
|
||||
}
|
||||
for (let dot of dots) {
|
||||
if (dots.indexOf(dot) == index) {
|
||||
dot.classList.add('w3-white');
|
||||
} else {
|
||||
dot.classList.remove('w3-white');
|
||||
}
|
||||
}
|
||||
document.getElementById('left').style.visibility =
|
||||
index == 0 ? 'hidden' : 'visible';
|
||||
document.getElementById('right').style.visibility =
|
||||
index == slides.length - 1 ? 'hidden' : 'visible';
|
||||
}
|
||||
|
||||
let dots = [...document.getElementsByClassName('dot')];
|
||||
for (let dot of dots) {
|
||||
dot.onclick = () => set(dots.indexOf(dot));
|
||||
}
|
||||
for (let button of document.getElementsByClassName('proceed')) {
|
||||
button.onclick = () => show(1);
|
||||
}
|
||||
document.getElementById('left').onclick = () => show(-1);
|
||||
document.getElementById('right').onclick = () => show(1);
|
||||
document.getElementById('complete').onclick = function () {
|
||||
console.log('completing');
|
||||
tfrpc.rpc.complete().finally(function () {
|
||||
console.log('completed');
|
||||
let a = document.createElement('a');
|
||||
a.href = '/~core/ssb/';
|
||||
a.target = '_top';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
window.addEventListener('keyup', function (event) {
|
||||
if (event.key == 'ArrowLeft') {
|
||||
show(-1);
|
||||
} else if (event.key == 'ArrowRight') {
|
||||
show(1);
|
||||
}
|
||||
});
|
||||
show(0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
251
apps/intro/w3.css
Normal file
251
apps/intro/w3.css
Normal file
@ -0,0 +1,251 @@
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦟",
|
||||
"previous": "&cUqvSDUls3jn0haD85LPFAGdkc8wFuy347TtATNcJgg=.sha256"
|
||||
"previous": "&O0huuEgL/UQC9bkUfhPOyZFo9eRiz+koOkba6QwCGNA=.sha256"
|
||||
}
|
||||
|
42
apps/issues/lit-all.min.js
vendored
42
apps/issues/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,11 @@
|
||||
import * as linkify from './commonmark-linkify.js';
|
||||
|
||||
var reUnsafeProtocol = /^javascript:|vbscript:|file:|data:/i;
|
||||
var reSafeDataProtocol = /^data:image\/(?:png|gif|jpeg|webp)/i;
|
||||
var potentiallyUnsafe = function (url) {
|
||||
return reUnsafeProtocol.test(url) && !reSafeDataProtocol.test(url);
|
||||
};
|
||||
|
||||
function image(node, entering) {
|
||||
if (
|
||||
node.firstChild?.type === 'text' &&
|
||||
@ -61,8 +67,8 @@ function image(node, entering) {
|
||||
}
|
||||
|
||||
export function markdown(md) {
|
||||
var reader = new commonmark.Parser({safe: true});
|
||||
var writer = new commonmark.HtmlRenderer();
|
||||
var reader = new commonmark.Parser();
|
||||
var writer = new commonmark.HtmlRenderer({safe: true});
|
||||
writer.image = image;
|
||||
var parsed = reader.parse(md || '');
|
||||
parsed = linkify.transform(parsed);
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📝",
|
||||
"previous": "&b//KqE4Vx6kOSBRODK1p/8wjOLKZJ+CBB5IkaBt5YsM=.sha256"
|
||||
"previous": "&5LpOTEnor/rYFk3axyfmmehAoq9aEwNQRH4jwNhRQ7o=.sha256"
|
||||
}
|
||||
|
42
apps/journal/lit-all.min.js
vendored
42
apps/journal/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,8 +18,8 @@ class TfJournalEntryElement extends LitElement {
|
||||
}
|
||||
|
||||
markdown(md) {
|
||||
var reader = new commonmark.Parser({safe: true});
|
||||
var writer = new commonmark.HtmlRenderer();
|
||||
var reader = new commonmark.Parser();
|
||||
var writer = new commonmark.HtmlRenderer({safe: true});
|
||||
var parsed = reader.parse(md || '');
|
||||
return writer.render(parsed);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🚪",
|
||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
||||
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
async function main() {
|
||||
let host = core.url.match(/.*\/\/(.*?)\//)[1];
|
||||
let id = (await ssb.getServerIdentity()).substring(1);
|
||||
let room = `net:${host}:${ssb.port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
print(core.url);
|
||||
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
||||
let port = await ssb.port();
|
||||
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
|
||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
await app.setDocument(`
|
||||
<body style="color: #fff">
|
||||
<h1>Server</h1>
|
||||
|
42
apps/sneaker/lit-all.min.js
vendored
42
apps/sneaker/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🐌",
|
||||
"previous": "&qj4BgD3cfrySqxq6BbQm6u58ic20XYileUiiAXcJtLs=.sha256"
|
||||
"emoji": "🦀",
|
||||
"previous": "&Ym1vefMN4CV4UIgLuV+zu52qj58WwIScctt4v5YIHmQ=.sha256"
|
||||
}
|
||||
|
@ -21,9 +21,6 @@ tfrpc.register(async function createIdentity() {
|
||||
tfrpc.register(async function getServerIdentity() {
|
||||
return ssb.getServerIdentity();
|
||||
});
|
||||
tfrpc.register(async function setServerFollowingMe(id, following) {
|
||||
return ssb.setServerFollowingMe(id, following);
|
||||
});
|
||||
tfrpc.register(async function getIdentities() {
|
||||
return ssb.getIdentities();
|
||||
});
|
||||
@ -106,6 +103,19 @@ tfrpc.register(async function getActiveIdentity() {
|
||||
tfrpc.register(async function sync() {
|
||||
return await ssb.sync();
|
||||
});
|
||||
tfrpc.register(async function url() {
|
||||
return core.url;
|
||||
});
|
||||
tfrpc.register(async function globalSettingsGet(key) {
|
||||
return core.globalSettingsGet(key);
|
||||
});
|
||||
tfrpc.register(async function globalSettingsSet(key, value) {
|
||||
return core.globalSettingsSet(key, value);
|
||||
});
|
||||
tfrpc.register(function isAdministrator() {
|
||||
return core.user?.credentials?.permissions?.administration;
|
||||
});
|
||||
|
||||
core.register('onBroadcastsChanged', async function () {
|
||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ function textNode(text) {
|
||||
function linkNode(text, link) {
|
||||
const linkNode = new commonmark.Node('link', undefined);
|
||||
if (link.startsWith('#')) {
|
||||
linkNode.destination = `#q=${encodeURIComponent(link)}`;
|
||||
linkNode.destination = `#${encodeURIComponent(link)}`;
|
||||
} else {
|
||||
linkNode.destination = link;
|
||||
}
|
||||
|
@ -14,33 +14,20 @@ function get_emojis() {
|
||||
});
|
||||
}
|
||||
|
||||
async function get_recent(author) {
|
||||
let recent = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT DISTINCT content ->> '$.vote.expression' AS value
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
content ->> '$.type' = 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
`,
|
||||
[author]
|
||||
);
|
||||
return recent.map((x) => x.value);
|
||||
}
|
||||
|
||||
export async function picker(callback, anchor, author) {
|
||||
export async function picker(callback, anchor, author, recent) {
|
||||
let json = await get_emojis();
|
||||
let recent = await get_recent(author);
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.id = 'emoji_picker';
|
||||
div.style.color = '#000';
|
||||
div.style.background = '#fff';
|
||||
div.style.border = '1px solid #000';
|
||||
div.style.display = 'block';
|
||||
div.style.display = 'flex';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.fontWeight = 'bold';
|
||||
div.style.fontSize = 'xx-large';
|
||||
div.style.flex = '1 1';
|
||||
div.style.flexDirection = 'column';
|
||||
let input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.style.display = 'block';
|
||||
@ -50,6 +37,7 @@ export async function picker(callback, anchor, author) {
|
||||
input.style.position = 'relative';
|
||||
div.appendChild(input);
|
||||
let list = document.createElement('div');
|
||||
list.style.overflow = 'scroll';
|
||||
div.appendChild(list);
|
||||
div.addEventListener('mousedown', function (event) {
|
||||
event.stopPropagation();
|
||||
@ -142,21 +130,40 @@ export async function picker(callback, anchor, author) {
|
||||
}
|
||||
refresh();
|
||||
input.oninput = refresh;
|
||||
let modal = html`
|
||||
<style>
|
||||
${styles}
|
||||
</style>
|
||||
<div class="w3-modal" style="display: block">
|
||||
<div class="w3-modal-content w3-card-4">${div}</div>
|
||||
</div>
|
||||
`;
|
||||
let parent = document.createElement('div');
|
||||
document.body.appendChild(parent);
|
||||
function cleanup() {
|
||||
parent.parentElement.removeChild(parent);
|
||||
window.removeEventListener('keydown', key_down);
|
||||
document.body.removeEventListener('mousedown', cleanup);
|
||||
}
|
||||
let modal = html`
|
||||
<style>
|
||||
${styles}
|
||||
</style>
|
||||
<div
|
||||
class="w3-modal"
|
||||
style="display: block; box-sizing: border-box; z-index: 10"
|
||||
>
|
||||
<div class="w3-modal-content w3-card-4">
|
||||
<div
|
||||
class="w3-content w3-theme-d1"
|
||||
style="display: flex; flex-direction: column; max-height: 80vh"
|
||||
>
|
||||
<header class="w3-container" style="flex: 0 0">
|
||||
<h1>Choose a Reaction</h1>
|
||||
<span class="w3-button w3-display-topright" @click=${cleanup}
|
||||
>×</span
|
||||
>
|
||||
</header>
|
||||
${div}
|
||||
<footer class="w3-container w3-padding" style="flex: 0 0">
|
||||
<button class="w3-button" @click=${cleanup}>Close</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(parent);
|
||||
render(modal, parent);
|
||||
input.focus();
|
||||
document.body.addEventListener('mousedown', cleanup);
|
||||
|
File diff suppressed because one or more lines are too long
42
apps/ssb/lit-all.min.js
vendored
42
apps/ssb/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,10 +8,16 @@ import * as tf_compose from './tf-compose.js';
|
||||
import * as tf_news from './tf-news.js';
|
||||
import * as tf_profile from './tf-profile.js';
|
||||
import * as tf_reactions_modal from './tf-reactions-modal.js';
|
||||
import * as tf_tab_mentions from './tf-tab-mentions.js';
|
||||
import * as tf_tab_news from './tf-tab-news.js';
|
||||
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
||||
import * as tf_tab_search from './tf-tab-search.js';
|
||||
import * as tf_tab_connections from './tf-tab-connections.js';
|
||||
import * as tf_tab_query from './tf-tab-query.js';
|
||||
import * as tf_tag from './tf-tag.js';
|
||||
import * as tf_styles from './tf-styles.js';
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
let style = document.createElement('style');
|
||||
style.innerText = tf_styles.styles;
|
||||
document.body.appendChild(style);
|
||||
});
|
||||
|
@ -7,16 +7,24 @@ class TfElement extends LitElement {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
hash: {type: String},
|
||||
unread: {type: Array},
|
||||
tab: {type: String},
|
||||
broadcasts: {type: Array},
|
||||
connections: {type: Array},
|
||||
loading: {type: Boolean},
|
||||
loading_about: {type: Number},
|
||||
loaded: {type: Boolean},
|
||||
following: {type: Array},
|
||||
users: {type: Object},
|
||||
ids: {type: Array},
|
||||
tags: {type: Array},
|
||||
channels: {type: Array},
|
||||
channels_unread: {type: Object},
|
||||
channels_latest: {type: Object},
|
||||
guest: {type: Boolean},
|
||||
url: {type: String},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
is_administrator: {type: Boolean},
|
||||
stay_connected: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,14 +34,19 @@ class TfElement extends LitElement {
|
||||
super();
|
||||
let self = this;
|
||||
this.hash = '#';
|
||||
this.unread = [];
|
||||
this.tab = 'news';
|
||||
this.broadcasts = [];
|
||||
this.connections = [];
|
||||
this.following = [];
|
||||
this.users = {};
|
||||
this.loaded = false;
|
||||
this.tags = [];
|
||||
this.loading_about = 0;
|
||||
this.channels = [];
|
||||
this.channels_unread = {};
|
||||
this.channels_latest = {};
|
||||
this.loading_latest = 0;
|
||||
this.loading_latest_scheduled = 0;
|
||||
this.recent_reactions = [];
|
||||
tfrpc.rpc.getBroadcasts().then((b) => {
|
||||
self.broadcasts = b || [];
|
||||
});
|
||||
@ -62,18 +75,88 @@ class TfElement extends LitElement {
|
||||
async initial_load() {
|
||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||
this.is_administrator = await tfrpc.rpc.isAdministrator();
|
||||
this.stay_connected =
|
||||
this.is_administrator &&
|
||||
(await tfrpc.rpc.globalSettingsGet('stay_connected'));
|
||||
this.url = await tfrpc.rpc.url();
|
||||
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
||||
this.guest = !this.whoami?.length;
|
||||
this.ids = ids;
|
||||
await this.load_channels();
|
||||
}
|
||||
|
||||
async load_channels() {
|
||||
let channels = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT
|
||||
content ->> 'channel' AS channel,
|
||||
content ->> 'subscribed' AS subscribed
|
||||
FROM
|
||||
messages
|
||||
WHERE
|
||||
author = ? AND
|
||||
content ->> 'type' = 'channel'
|
||||
ORDER BY sequence
|
||||
`,
|
||||
[this.whoami]
|
||||
);
|
||||
let channel_map = {};
|
||||
for (let row of channels) {
|
||||
if (row.subscribed) {
|
||||
channel_map[row.channel] = true;
|
||||
} else {
|
||||
delete channel_map[row.channel];
|
||||
}
|
||||
}
|
||||
this.channels = Object.keys(channel_map).sort();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._keydown = this.keydown.bind(this);
|
||||
window.addEventListener('keydown', this._keydown);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener('keydown', this._keydown);
|
||||
}
|
||||
|
||||
keydown(event) {
|
||||
if (event.altKey && event.key == 'ArrowUp') {
|
||||
this.next_channel(-1);
|
||||
event.preventDefault();
|
||||
} else if (event.altKey && event.key == 'ArrowDown') {
|
||||
this.next_channel(1);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
next_channel(delta) {
|
||||
let channel_names = [
|
||||
'',
|
||||
'@',
|
||||
'👍',
|
||||
'🔐',
|
||||
...this.channels.map((x) => '#' + x),
|
||||
];
|
||||
let index = channel_names.indexOf(this.hash.substring(1));
|
||||
index = index != -1 ? index + delta : 0;
|
||||
tfrpc.rpc.setHash(
|
||||
'#' +
|
||||
encodeURIComponent(
|
||||
channel_names[(index + channel_names.length) % channel_names.length]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
set_hash(hash) {
|
||||
this.hash = hash || '#';
|
||||
this.hash = decodeURIComponent(hash || '#');
|
||||
if (this.hash.startsWith('#q=')) {
|
||||
this.tab = 'search';
|
||||
} else if (this.hash === '#connections') {
|
||||
this.tab = 'connections';
|
||||
} else if (this.hash === '#mentions') {
|
||||
this.tab = 'mentions';
|
||||
} else if (this.hash.startsWith('#sql=')) {
|
||||
this.tab = 'query';
|
||||
} else {
|
||||
@ -81,79 +164,97 @@ class TfElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async fetch_about(ids, users) {
|
||||
const k_cache_version = 1;
|
||||
async fetch_about(following, users) {
|
||||
this.loading_about++;
|
||||
let ids = Object.keys(following).sort();
|
||||
const k_cache_version = 3;
|
||||
let cache = await tfrpc.rpc.databaseGet('about');
|
||||
let original_cache = cache;
|
||||
cache = cache ? JSON.parse(cache) : {};
|
||||
if (cache.version !== k_cache_version) {
|
||||
cache = {
|
||||
version: k_cache_version,
|
||||
about: {},
|
||||
last_row_id: 0,
|
||||
};
|
||||
}
|
||||
let max_row_id = (
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT MAX(rowid) AS max_row_id FROM messages
|
||||
`,
|
||||
[]
|
||||
)
|
||||
)[0].max_row_id;
|
||||
|
||||
let ids_out_of_date = ids.filter(
|
||||
(x) =>
|
||||
(users[x]?.seq && !cache.about[x]?.seq) ||
|
||||
(users[x]?.seq && users[x]?.seq > cache.about[x].seq)
|
||||
);
|
||||
|
||||
for (let id of Object.keys(cache.about)) {
|
||||
if (ids.indexOf(id) == -1) {
|
||||
delete cache.about[id];
|
||||
} else {
|
||||
users[id] = Object.assign(cache.about[id], users[id] || {});
|
||||
}
|
||||
}
|
||||
|
||||
let abouts = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT
|
||||
messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM
|
||||
messages,
|
||||
json_each(?1) AS following
|
||||
WHERE
|
||||
messages.author = following.value AND
|
||||
messages.rowid > ?3 AND
|
||||
messages.rowid <= ?4 AND
|
||||
json_extract(messages.content, '$.type') = 'about'
|
||||
UNION
|
||||
SELECT
|
||||
messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM
|
||||
messages,
|
||||
json_each(?2) AS following
|
||||
WHERE
|
||||
messages.author = following.value AND
|
||||
messages.rowid <= ?4 AND
|
||||
json_extract(messages.content, '$.type') = 'about'
|
||||
ORDER BY messages.author, messages.sequence
|
||||
`,
|
||||
[
|
||||
JSON.stringify(ids.filter((id) => cache.about[id])),
|
||||
JSON.stringify(ids.filter((id) => !cache.about[id])),
|
||||
cache.last_row_id,
|
||||
max_row_id,
|
||||
]
|
||||
console.log(
|
||||
'loading about for',
|
||||
ids.length,
|
||||
'accounts',
|
||||
ids_out_of_date.length,
|
||||
'out of date'
|
||||
);
|
||||
for (let about of abouts) {
|
||||
let content = JSON.parse(about.content);
|
||||
if (content.about === about.author) {
|
||||
delete content.type;
|
||||
delete content.about;
|
||||
cache.about[about.author] = Object.assign(
|
||||
cache.about[about.author] || {},
|
||||
content
|
||||
if (ids_out_of_date.length) {
|
||||
try {
|
||||
let rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT all_abouts.author, json(json_group_object(all_abouts.key, all_abouts.value)) AS about
|
||||
FROM (
|
||||
SELECT
|
||||
messages.author,
|
||||
fields.key,
|
||||
RANK() OVER (PARTITION BY messages.author, fields.key ORDER BY messages.sequence DESC) AS rank,
|
||||
fields.value
|
||||
FROM messages JOIN json_each(messages.content) AS fields
|
||||
WHERE
|
||||
messages.content ->> '$.type' = 'about' AND
|
||||
messages.content ->> '$.about' = messages.author AND
|
||||
NOT fields.key IN ('about', 'type')) all_abouts
|
||||
JOIN json_each(?) AS following ON all_abouts.author = following.value
|
||||
WHERE rank = 1
|
||||
GROUP BY all_abouts.author
|
||||
`,
|
||||
[JSON.stringify(ids_out_of_date)]
|
||||
);
|
||||
users = users || {};
|
||||
for (let row of rows) {
|
||||
users[row.author] = Object.assign(
|
||||
users[row.author] || {},
|
||||
JSON.parse(row.about)
|
||||
);
|
||||
cache.about[row.author] = Object.assign(
|
||||
{seq: users[row.author].seq},
|
||||
JSON.parse(row.about)
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
cache.last_row_id = max_row_id;
|
||||
await tfrpc.rpc.databaseSet('about', JSON.stringify(cache));
|
||||
users = users || {};
|
||||
for (let id of Object.keys(cache.about)) {
|
||||
users[id] = Object.assign(users[id] || {}, cache.about[id]);
|
||||
|
||||
for (let id of ids_out_of_date) {
|
||||
if (!cache.about[id]?.seq) {
|
||||
cache.about[id] = Object.assign(cache.about[id] ?? {}, {
|
||||
seq: users[id]?.seq ?? 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.loading_about--;
|
||||
|
||||
let new_cache = JSON.stringify(cache);
|
||||
if (new_cache != original_cache) {
|
||||
let start_time = new Date();
|
||||
tfrpc.rpc.databaseSet('about', new_cache).then(function () {
|
||||
console.log('saving about took', (new Date() - start_time) / 1000);
|
||||
});
|
||||
}
|
||||
|
||||
return Object.assign({}, users);
|
||||
}
|
||||
|
||||
@ -167,10 +268,15 @@ class TfElement extends LitElement {
|
||||
`,
|
||||
[JSON.stringify(this.following), id]
|
||||
);
|
||||
if (messages && messages.length) {
|
||||
this.unread = [...this.unread, ...messages];
|
||||
this.unread = this.unread.slice(this.unread.length - 1024);
|
||||
for (let message of messages) {
|
||||
if (
|
||||
message.author == this.whoami &&
|
||||
JSON.parse(message.content)?.type == 'channel'
|
||||
) {
|
||||
this.load_channels();
|
||||
}
|
||||
}
|
||||
this.schedule_load_latest();
|
||||
}
|
||||
|
||||
async _handle_whoami_changed(event) {
|
||||
@ -185,70 +291,298 @@ class TfElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async create_identity() {
|
||||
if (confirm('Are you sure you want to create a new identity?')) {
|
||||
await tfrpc.rpc.createIdentity();
|
||||
this.ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||
if (this.ids && !this.whoami) {
|
||||
this.whoami = this.ids[0];
|
||||
async get_latest_private(following) {
|
||||
const k_version = 1;
|
||||
// { "version": 1, "range": [1234, 5678], messages: [ "%1.sha256", "%2.sha256", ... ], latest: rowid }
|
||||
let cache = JSON.parse(
|
||||
(await tfrpc.rpc.databaseGet(`private:${this.whoami}`)) ?? '{}'
|
||||
);
|
||||
if (cache.version !== k_version) {
|
||||
cache = {
|
||||
version: k_version,
|
||||
messages: [],
|
||||
range: [],
|
||||
};
|
||||
}
|
||||
let latest = (
|
||||
await tfrpc.rpc.query('SELECT MAX(rowid) AS latest FROM messages')
|
||||
)[0].latest;
|
||||
let ranges = [];
|
||||
const k_chunk_size = 512;
|
||||
if (cache.range.length) {
|
||||
for (let i = cache.range[1]; i < latest; i += k_chunk_size) {
|
||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||
}
|
||||
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
||||
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < latest; i += k_chunk_size) {
|
||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||
}
|
||||
}
|
||||
for (let range of ranges) {
|
||||
let messages = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.rowid, messages.id, json(content) AS content
|
||||
FROM messages
|
||||
WHERE
|
||||
messages.rowid > ?1 AND
|
||||
messages.rowid <= ?2 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY messages.rowid DESC
|
||||
`,
|
||||
[range[0], range[1]]
|
||||
);
|
||||
messages = (await this.decrypt(messages)).filter((x) => x.decrypted);
|
||||
if (messages.length) {
|
||||
cache.latest = Math.max(
|
||||
cache.latest ?? 0,
|
||||
...messages.map((x) => x.rowid)
|
||||
);
|
||||
if (range[2]) {
|
||||
cache.messages = [...cache.messages, ...messages.map((x) => x.id)];
|
||||
} else {
|
||||
cache.messages = [...messages.map((x) => x.id), ...cache.messages];
|
||||
}
|
||||
}
|
||||
cache.range[0] = Math.min(cache.range[0] ?? range[0], range[0]);
|
||||
cache.range[1] = Math.max(cache.range[1] ?? range[1], range[1]);
|
||||
await tfrpc.rpc.databaseSet(
|
||||
`private:${this.whoami}`,
|
||||
JSON.stringify(cache)
|
||||
);
|
||||
}
|
||||
return [cache.latest, cache.messages];
|
||||
}
|
||||
|
||||
async query_timed(sql, args) {
|
||||
let start = new Date();
|
||||
let result = await tfrpc.rpc.query(sql, args);
|
||||
let end = new Date();
|
||||
console.log((end - start) / 1000, sql.replaceAll(/\s+/g, ' ').trim());
|
||||
return result;
|
||||
}
|
||||
|
||||
async load_channels_latest(following) {
|
||||
let start_time = new Date();
|
||||
let latest_private = this.get_latest_private(following);
|
||||
const k_args = [
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
this.whoami,
|
||||
];
|
||||
let channels = (
|
||||
await Promise.all([
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN messages_refs ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
])
|
||||
).flat();
|
||||
let latest = {};
|
||||
for (let row of channels) {
|
||||
if (!latest[row.channel]) {
|
||||
latest[row.channel] = row.rowid;
|
||||
} else {
|
||||
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||
}
|
||||
}
|
||||
this.channels_latest = latest;
|
||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||
let self = this;
|
||||
start_time = new Date();
|
||||
latest_private.then(function (latest) {
|
||||
self.channels_latest = Object.assign({}, self.channels_latest, {
|
||||
'🔐': latest[0],
|
||||
});
|
||||
console.log('private took', (new Date() - start_time) / 1000.0);
|
||||
self.private_messages = latest[1];
|
||||
});
|
||||
}
|
||||
|
||||
_schedule_load_latest_timer() {
|
||||
--this.loading_latest_scheduled;
|
||||
this.schedule_load_latest();
|
||||
}
|
||||
|
||||
schedule_load_latest() {
|
||||
if (!this.loading_latest) {
|
||||
this.shadowRoot.getElementById('tf-tab-news')?.load_latest();
|
||||
this.load();
|
||||
} else if (!this.loading_latest_scheduled) {
|
||||
this.loading_latest_scheduled++;
|
||||
setTimeout(this._schedule_load_latest_timer.bind(this), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async load_recent_tags() {
|
||||
let start = new Date();
|
||||
this.tags = await tfrpc.rpc.query(
|
||||
async fetch_user_info(users) {
|
||||
let info = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
recent AS (SELECT id, json(content) AS content FROM messages
|
||||
WHERE messages.timestamp > ? AND json_extract(content, '$.type') = 'post'
|
||||
ORDER BY timestamp DESC LIMIT 1024),
|
||||
recent_channels AS (SELECT recent.id, '#' || json_extract(content, '$.channel') AS tag
|
||||
FROM recent
|
||||
WHERE json_extract(content, '$.channel') IS NOT NULL),
|
||||
recent_mentions AS (SELECT recent.id, json_extract(mention.value, '$.link') AS tag
|
||||
FROM recent, json_each(recent.content, '$.mentions') AS mention
|
||||
WHERE json_valid(mention.value) AND tag LIKE '#%'),
|
||||
combined AS (SELECT id, tag FROM recent_channels UNION ALL SELECT id, tag FROM recent_mentions),
|
||||
by_message AS (SELECT DISTINCT id, tag FROM combined)
|
||||
SELECT tag, COUNT(*) AS count FROM by_message GROUP BY tag ORDER BY count DESC LIMIT 10
|
||||
`,
|
||||
[new Date() - 7 * 24 * 60 * 60 * 1000]
|
||||
SELECT messages_stats.author, messages_stats.max_sequence, messages_stats.max_timestamp AS max_ts FROM messages_stats
|
||||
JOIN json_each(?) AS following
|
||||
ON messages_stats.author = following.value
|
||||
`,
|
||||
[JSON.stringify(Object.keys(users))]
|
||||
);
|
||||
console.log('tags took', (new Date() - start) / 1000.0, 'seconds');
|
||||
for (let row of info) {
|
||||
users[row.author] = Object.assign(users[row.author], {
|
||||
seq: row.max_sequence,
|
||||
ts: row.max_ts,
|
||||
});
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
async load_recent_reactions() {
|
||||
this.recent_reactions = (
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT DISTINCT content ->> '$.vote.expression' AS value
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
content ->> '$.type' = 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
`,
|
||||
[this.whoami]
|
||||
)
|
||||
).map((x) => x.value);
|
||||
}
|
||||
|
||||
async load() {
|
||||
let whoami = this.whoami;
|
||||
let tags = this.load_recent_tags();
|
||||
let following = await tfrpc.rpc.following([whoami], 2);
|
||||
let users = {};
|
||||
let by_count = [];
|
||||
for (let [id, v] of Object.entries(following)) {
|
||||
users[id] = {
|
||||
following: v.of,
|
||||
blocking: v.ob,
|
||||
followed: v.if,
|
||||
blocked: v.ib,
|
||||
};
|
||||
by_count.push({count: v.of, id: id});
|
||||
this.loading_latest = true;
|
||||
try {
|
||||
let start_time = new Date();
|
||||
let whoami = this.whoami;
|
||||
let following = await tfrpc.rpc.following([whoami], 2);
|
||||
let old_users = this.users ?? {};
|
||||
let users = {};
|
||||
let by_count = [];
|
||||
for (let [id, v] of Object.entries(following)) {
|
||||
users[id] = Object.assign(
|
||||
{
|
||||
following: v.of,
|
||||
blocking: v.ob,
|
||||
followed: v.if,
|
||||
blocked: v.ib,
|
||||
follow_depth: following[id]?.d,
|
||||
},
|
||||
old_users[id]
|
||||
);
|
||||
by_count.push({count: v.of, id: id});
|
||||
}
|
||||
let reactions = this.load_recent_reactions();
|
||||
this.load_channels_latest(Object.keys(following));
|
||||
this.channels_unread = JSON.parse(
|
||||
(await tfrpc.rpc.databaseGet('unread')) ?? '{}'
|
||||
);
|
||||
this.following = Object.keys(following);
|
||||
let about_start_time = new Date();
|
||||
start_time = new Date();
|
||||
users = await this.fetch_user_info(users);
|
||||
console.log(
|
||||
'user info took',
|
||||
(new Date() - start_time) / 1000.0,
|
||||
'seconds'
|
||||
);
|
||||
this.users = users;
|
||||
|
||||
let self = this;
|
||||
this.fetch_about(following, users).then(function (result) {
|
||||
self.users = result;
|
||||
console.log(
|
||||
'about took',
|
||||
(new Date() - about_start_time) / 1000.0,
|
||||
'seconds for',
|
||||
Object.keys(users).length,
|
||||
'users'
|
||||
);
|
||||
});
|
||||
console.log(
|
||||
`load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}`
|
||||
);
|
||||
await reactions;
|
||||
this.whoami = whoami;
|
||||
this.loaded = whoami;
|
||||
} finally {
|
||||
this.loading_latest = false;
|
||||
}
|
||||
console.log(by_count.sort((x, y) => y.count - x.count).slice(0, 20));
|
||||
let start_time = new Date();
|
||||
users = await this.fetch_about(Object.keys(following).sort(), users);
|
||||
console.log(
|
||||
'about took',
|
||||
(new Date() - start_time) / 1000.0,
|
||||
'seconds for',
|
||||
Object.keys(users).length,
|
||||
'users'
|
||||
}
|
||||
|
||||
channel_set_unread(event) {
|
||||
this.channels_unread[event.detail.channel ?? ''] = event.detail.unread;
|
||||
this.channels_unread = Object.assign({}, this.channels_unread);
|
||||
tfrpc.rpc.databaseSet('unread', JSON.stringify(this.channels_unread));
|
||||
}
|
||||
|
||||
async decrypt(messages) {
|
||||
let whoami = this.whoami;
|
||||
return Promise.all(
|
||||
messages.map(async function (message) {
|
||||
let content;
|
||||
try {
|
||||
content = JSON.parse(message?.content);
|
||||
} catch {}
|
||||
if (typeof content === 'string') {
|
||||
let decrypted;
|
||||
try {
|
||||
decrypted = await tfrpc.rpc.try_decrypt(whoami, content);
|
||||
} catch {}
|
||||
if (decrypted) {
|
||||
try {
|
||||
message.decrypted = JSON.parse(decrypted);
|
||||
} catch {
|
||||
message.decrypted = decrypted;
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
})
|
||||
);
|
||||
this.following = Object.keys(following);
|
||||
this.users = users;
|
||||
await tags;
|
||||
console.log(`load finished ${whoami} => ${this.whoami}`);
|
||||
this.whoami = whoami;
|
||||
this.loaded = whoami;
|
||||
}
|
||||
|
||||
render_tab() {
|
||||
@ -262,9 +596,18 @@ class TfElement extends LitElement {
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
hash=${this.hash}
|
||||
.unread=${this.unread}
|
||||
@refresh=${() => (this.unread = [])}
|
||||
?loading=${this.loading}
|
||||
?loading=${this.loading || this.loading_about != 0}
|
||||
.channels=${this.channels}
|
||||
.channels_latest=${this.channels_latest}
|
||||
.channels_unread=${this.channels_unread}
|
||||
@channelsetunread=${this.channel_set_unread}
|
||||
@refresh=${this.refresh}
|
||||
@toggle_stay_connected=${this.toggle_stay_connected}
|
||||
.connections=${this.connections}
|
||||
.private_messages=${this.private_messages}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
?is_administrator=${this.is_administrator}
|
||||
?stay_connected=${this.stay_connected}
|
||||
></tf-tab-news>
|
||||
`;
|
||||
} else if (this.tab === 'connections') {
|
||||
@ -275,14 +618,6 @@ class TfElement extends LitElement {
|
||||
.broadcasts=${this.broadcasts}
|
||||
></tf-tab-connections>
|
||||
`;
|
||||
} else if (this.tab === 'mentions') {
|
||||
return html`
|
||||
<tf-tab-mentions
|
||||
.following=${this.following}
|
||||
whoami=${this.whoami}
|
||||
.users="${this.users}}"
|
||||
></tf-tab-mentions>
|
||||
`;
|
||||
} else if (this.tab === 'search') {
|
||||
return html`
|
||||
<tf-tab-search
|
||||
@ -314,13 +649,27 @@ class TfElement extends LitElement {
|
||||
await tfrpc.rpc.setHash('#');
|
||||
} else if (tab === 'connections') {
|
||||
await tfrpc.rpc.setHash('#connections');
|
||||
} else if (tab === 'mentions') {
|
||||
await tfrpc.rpc.setHash('#mentions');
|
||||
} else if (tab === 'query') {
|
||||
await tfrpc.rpc.setHash('#sql=');
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
tfrpc.rpc.sync();
|
||||
}
|
||||
|
||||
async toggle_stay_connected() {
|
||||
let stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||
let new_stay_connected = !this.stay_connected;
|
||||
try {
|
||||
if (new_stay_connected != stay_connected) {
|
||||
await tfrpc.rpc.globalSettingsSet('stay_connected', new_stay_connected);
|
||||
}
|
||||
} finally {
|
||||
this.stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
|
||||
@ -334,13 +683,35 @@ class TfElement extends LitElement {
|
||||
const k_tabs = {
|
||||
'📰': 'news',
|
||||
'📡': 'connections',
|
||||
'@': 'mentions',
|
||||
'🔍': 'search',
|
||||
'👩💻': 'query',
|
||||
};
|
||||
|
||||
let tabs = html`
|
||||
<div class="w3-bar w3-theme-l1">
|
||||
<div
|
||||
class="w3-bar w3-theme-l1"
|
||||
style="position: static; top: 0; z-index: 10"
|
||||
>
|
||||
${this.is_administrator
|
||||
? html`
|
||||
<button
|
||||
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
||||
(this.connections?.some((x) => x.flags.one_shot)
|
||||
? ' w3-spin'
|
||||
: '')}
|
||||
style="width: 1.5em; height: 1.5em; padding: 8px"
|
||||
@click=${this.refresh}
|
||||
>
|
||||
↻
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-ripple"
|
||||
@click=${this.toggle_stay_connected}
|
||||
>
|
||||
${this.stay_connected ? '🔗' : '⛓️💥'}
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${Object.entries(k_tabs).map(
|
||||
([k, v]) => html`
|
||||
<button
|
||||
@ -359,26 +730,34 @@ class TfElement extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
let contents = !this.loaded
|
||||
? this.loading
|
||||
? html`<div
|
||||
class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge"
|
||||
let contents = this.guest
|
||||
? html`<div
|
||||
class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge w3-container"
|
||||
>
|
||||
<p>⚠️🦀 Must be logged in to Tilde Friends to scuttle here. 🦀⚠️</p>
|
||||
<footer class="w3-center">
|
||||
<a
|
||||
class="w3-button w3-theme-d1"
|
||||
href=${`/login?return=${encodeURIComponent(this.url)}`}
|
||||
>Login</a
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
${this.render_tab()}`
|
||||
: html`<div>Select or create an identity.</div>`
|
||||
: this.render_tab();
|
||||
</footer>
|
||||
</div>`
|
||||
: !this.loaded || this.loading
|
||||
? html`<div
|
||||
class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge"
|
||||
>
|
||||
<span class="w3-spin" style="display: inline-block">🦀</span>
|
||||
Loading...
|
||||
</div>`
|
||||
: this.render_tab();
|
||||
return html`
|
||||
<div
|
||||
style="width: 100vw; min-height: 100vh; height: 100%"
|
||||
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
||||
class="w3-theme-dark"
|
||||
>
|
||||
${tabs}
|
||||
<div style="padding: 8px">
|
||||
${this.tags.map(
|
||||
(x) => html`<tf-tag tag=${x.tag} count=${x.count}></tf-tag>`
|
||||
)}
|
||||
<div style="flex: 0 0">${tabs}</div>
|
||||
<div style="flex: 1 1; overflow: auto; contain: layout">
|
||||
${contents}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,6 +14,8 @@ class TfComposeElement extends LitElement {
|
||||
apps: {type: Object},
|
||||
drafts: {type: Object},
|
||||
author: {type: String},
|
||||
channel: {type: String},
|
||||
new_thread: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,6 +29,7 @@ class TfComposeElement extends LitElement {
|
||||
this.apps = undefined;
|
||||
this.drafts = {};
|
||||
this.author = undefined;
|
||||
this.new_thread = false;
|
||||
}
|
||||
|
||||
process_text(text) {
|
||||
@ -180,6 +183,13 @@ class TfComposeElement extends LitElement {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
document.execCommand(
|
||||
'insertText',
|
||||
false,
|
||||
event.clipboardData.getData('text/plain')
|
||||
);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
@ -189,11 +199,26 @@ class TfComposeElement extends LitElement {
|
||||
let message = {
|
||||
type: 'post',
|
||||
text: edit.innerText,
|
||||
channel: this.channel,
|
||||
};
|
||||
if (this.root || this.branch) {
|
||||
message.root = this.root;
|
||||
message.root = this.new_thread ? (this.branch ?? this.root) : this.root;
|
||||
message.branch = this.branch;
|
||||
}
|
||||
let reply = Object.fromEntries(
|
||||
(
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.id, messages.author FROM messages
|
||||
JOIN json_each(?) AS refs ON messages.id = refs.value
|
||||
`,
|
||||
[JSON.stringify([this.root, this.branch])]
|
||||
)
|
||||
).map((row) => [row.id, row.author])
|
||||
);
|
||||
if (Object.keys(reply).length) {
|
||||
message.reply = reply;
|
||||
}
|
||||
if (Object.values(draft.mentions || {}).length) {
|
||||
message.mentions = Object.values(draft.mentions);
|
||||
}
|
||||
@ -230,10 +255,12 @@ class TfComposeElement extends LitElement {
|
||||
let self = this;
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = function (event) {
|
||||
input.addEventListener('change', function (event) {
|
||||
input.parentNode.removeChild(input);
|
||||
let file = event.target.files[0];
|
||||
self.add_file(file);
|
||||
};
|
||||
});
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
@ -332,7 +359,7 @@ class TfComposeElement extends LitElement {
|
||||
remove_mention(id) {
|
||||
let draft = this.get_draft();
|
||||
delete draft.mentions[id];
|
||||
setTimeout(() => this.notify(), 0);
|
||||
setTimeout(() => this.notify(draft), 0);
|
||||
}
|
||||
|
||||
render_mention(mention) {
|
||||
@ -419,12 +446,15 @@ class TfComposeElement extends LitElement {
|
||||
self.apps = await tfrpc.rpc.apps();
|
||||
}
|
||||
if (!this.apps) {
|
||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
||||
return html`<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${attach_app}
|
||||
>
|
||||
Attach App
|
||||
</button>`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => (this.apps = null)}
|
||||
>
|
||||
Discard App
|
||||
@ -445,17 +475,22 @@ class TfComposeElement extends LitElement {
|
||||
if (draft.content_warning !== undefined) {
|
||||
return html`
|
||||
<div class="w3-container w3-padding">
|
||||
<p>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
||||
<label for="cw">CW</label>
|
||||
</p>
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
render_new_thread() {
|
||||
let self = this;
|
||||
if (
|
||||
this.root !== undefined &&
|
||||
this.branch !== undefined &&
|
||||
this.root != this.branch
|
||||
) {
|
||||
return html`
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
||||
<label for="cw">CW</label>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="new_thread" @change=${() => (self.new_thread = !self.new_thread)} ?checked=${self.new_thread}></input>
|
||||
<label for="new_thread">New Thread</label>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -483,7 +518,7 @@ class TfComposeElement extends LitElement {
|
||||
return html`
|
||||
<div style="display: flex; flex-direction: row; width: 100%">
|
||||
<label for="encrypt_to">🔐 To:</label>
|
||||
<input type="text" id="encrypt_to" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
|
||||
<input type="text" id="encrypt_to" class="w3-input w3-theme-d1 w3-border" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
|
||||
<button class="w3-button w3-theme-d1" @click=${() => this.set_encrypt(undefined)}>🚮</button>
|
||||
</div>
|
||||
<ul>
|
||||
@ -505,6 +540,31 @@ class TfComposeElement extends LitElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
toggle_menu(event) {
|
||||
event.srcElement.parentNode
|
||||
.querySelector('.w3-dropdown-content')
|
||||
.classList.toggle('w3-show');
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._click_callback = this.document_click.bind(this);
|
||||
document.body.addEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.removeEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
document_click(event) {
|
||||
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||
let target = event.target;
|
||||
if (content && !content.contains(target)) {
|
||||
content.classList.remove('w3-show');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
let draft = self.get_draft();
|
||||
@ -518,18 +578,31 @@ class TfComposeElement extends LitElement {
|
||||
draft.encrypt_to !== undefined
|
||||
? undefined
|
||||
: html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => this.set_encrypt([])}
|
||||
>
|
||||
🔐
|
||||
🔐 Encrypt
|
||||
</button>`;
|
||||
let result = html`
|
||||
<style>
|
||||
.w3-input:empty::before {
|
||||
content: attr(placeholder);
|
||||
}
|
||||
.w3-input:empty:focus::before {
|
||||
content: '';
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="w3-card-4 w3-theme-d4 w3-padding-small"
|
||||
class="w3-card-4 w3-theme-d4 w3-padding w3-margin-top w3-margin-bottom"
|
||||
style="box-sizing: border-box"
|
||||
>
|
||||
${this.render_encrypt()}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<header class="w3-container">
|
||||
${this.channel !== undefined
|
||||
? html`<p>To #${this.channel}:</p>`
|
||||
: undefined}
|
||||
${this.render_encrypt()}
|
||||
</header>
|
||||
<div class="w3-container" style="padding: 0 0 16px 0">
|
||||
<div class="w3-half">
|
||||
<span
|
||||
class="w3-input w3-theme-d1 w3-border"
|
||||
@ -542,25 +615,62 @@ class TfComposeElement extends LitElement {
|
||||
.innerText=${live(draft.text ?? '')}
|
||||
></span>
|
||||
</div>
|
||||
<div class="w3-half w3-padding">
|
||||
<div class="w3-half w3-container">
|
||||
${content_warning}
|
||||
<div id="preview"></div>
|
||||
<p id="preview"></p>
|
||||
</div>
|
||||
</div>
|
||||
${Object.values(draft.mentions || {}).map((x) =>
|
||||
self.render_mention(x)
|
||||
)}
|
||||
${this.render_attach_app()} ${this.render_content_warning()}
|
||||
<button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}>
|
||||
Submit
|
||||
</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
||||
Discard
|
||||
</button>
|
||||
<footer>
|
||||
${this.render_attach_app()} ${this.render_content_warning()}
|
||||
${this.render_new_thread()}
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
id="submit"
|
||||
@click=${this.submit}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<div class="w3-dropdown-click">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
⚙️
|
||||
</button>
|
||||
<div class="w3-dropdown-content w3-bar-block">
|
||||
${this.get_draft().content_warning === undefined
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning('')}
|
||||
>
|
||||
Add Content Warning
|
||||
</button>
|
||||
`
|
||||
: html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning(undefined)}
|
||||
>
|
||||
Remove Content Warning
|
||||
</button>
|
||||
`}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.attach}
|
||||
>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.discard}
|
||||
>
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
return result;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
|
||||
import {LitElement, html, unsafeHTML, repeat, until} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
@ -11,6 +11,10 @@ class TfNewsElement extends LitElement {
|
||||
following: {type: Array},
|
||||
drafts: {type: Object},
|
||||
expanded: {type: Object},
|
||||
channel: {type: String},
|
||||
channel_unread: {type: Number},
|
||||
recent_reactions: {type: Array},
|
||||
hash: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,6 +29,8 @@ class TfNewsElement extends LitElement {
|
||||
this.following = [];
|
||||
this.drafts = {};
|
||||
this.expanded = {};
|
||||
this.channel_unread = -1;
|
||||
this.recent_reactions = [];
|
||||
}
|
||||
|
||||
process_messages(messages) {
|
||||
@ -33,12 +39,13 @@ class TfNewsElement extends LitElement {
|
||||
|
||||
console.log('processing', messages.length, 'messages');
|
||||
|
||||
function ensure_message(id) {
|
||||
function ensure_message(id, rowid) {
|
||||
let found = messages_by_id[id];
|
||||
if (found) {
|
||||
return found;
|
||||
} else {
|
||||
let added = {
|
||||
rowid: rowid,
|
||||
id: id,
|
||||
placeholder: true,
|
||||
content: '"placeholder"',
|
||||
@ -53,7 +60,7 @@ class TfNewsElement extends LitElement {
|
||||
|
||||
function link_message(message) {
|
||||
if (message.content.type === 'vote') {
|
||||
let parent = ensure_message(message.content.vote.link);
|
||||
let parent = ensure_message(message.content.vote.link, message.rowid);
|
||||
if (!parent.votes) {
|
||||
parent.votes = [];
|
||||
}
|
||||
@ -62,14 +69,14 @@ class TfNewsElement extends LitElement {
|
||||
} else if (message.content.type == 'post') {
|
||||
if (message.content.root) {
|
||||
if (typeof message.content.root === 'string') {
|
||||
let m = ensure_message(message.content.root);
|
||||
let m = ensure_message(message.content.root, message.rowid);
|
||||
if (!m.child_messages) {
|
||||
m.child_messages = [];
|
||||
}
|
||||
m.child_messages.push(message);
|
||||
message.parent_message = message.content.root;
|
||||
} else {
|
||||
let m = ensure_message(message.content.root[0]);
|
||||
let m = ensure_message(message.content.root[0], message.rowid);
|
||||
if (!m.child_messages) {
|
||||
m.child_messages = [];
|
||||
}
|
||||
@ -160,8 +167,12 @@ class TfNewsElement extends LitElement {
|
||||
if (message?.content?.type === 'contact') {
|
||||
group.push(message);
|
||||
} else {
|
||||
if (group.length > 0) {
|
||||
if (group.length == 1) {
|
||||
result.push(group[0]);
|
||||
group = [];
|
||||
} else if (group.length > 1) {
|
||||
result.push({
|
||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||
type: 'contact_group',
|
||||
messages: group,
|
||||
});
|
||||
@ -170,26 +181,77 @@ class TfNewsElement extends LitElement {
|
||||
result.push(message);
|
||||
}
|
||||
}
|
||||
if (group.length == 1) {
|
||||
result.push(group[0]);
|
||||
group = [];
|
||||
} else if (group.length > 1) {
|
||||
result.push({
|
||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||
type: 'contact_group',
|
||||
messages: group,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
|
||||
}
|
||||
|
||||
load_and_render(messages) {
|
||||
let messages_by_id = this.process_messages(messages);
|
||||
let final_messages = this.group_following(
|
||||
this.finalize_messages(messages_by_id)
|
||||
);
|
||||
let unread_rowid = -1;
|
||||
if (this.unread_allowed()) {
|
||||
for (let message of final_messages) {
|
||||
if (message.rowid >= this.channel_unread) {
|
||||
unread_rowid = message.rowid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html`
|
||||
<div style="display: flex; flex-direction: column">
|
||||
${final_messages.map(
|
||||
(x) =>
|
||||
html`<tf-message
|
||||
<div>
|
||||
${repeat(
|
||||
final_messages,
|
||||
(x) => x.id,
|
||||
(x) => html`
|
||||
<tf-message
|
||||
.message=${x}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
collapsed="true"
|
||||
></tf-message>`
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-message>
|
||||
${x.rowid == unread_rowid
|
||||
? html`<div style="display: flex; flex-direction: row">
|
||||
<div
|
||||
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
|
||||
></div>
|
||||
<button
|
||||
style="color: #f00; padding: 8px"
|
||||
class="w3-button"
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new Event('mark_all_read', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
)}
|
||||
>
|
||||
unread
|
||||
</button>
|
||||
<div
|
||||
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
|
||||
></div>
|
||||
</div>`
|
||||
: undefined}
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
@ -11,7 +11,7 @@ class TfProfileElement extends LitElement {
|
||||
id: {type: String},
|
||||
users: {type: Object},
|
||||
size: {type: Number},
|
||||
server_follows_me: {type: Boolean},
|
||||
sequence: {type: Number},
|
||||
following: {type: Boolean},
|
||||
blocking: {type: Boolean},
|
||||
};
|
||||
@ -27,7 +27,7 @@ class TfProfileElement extends LitElement {
|
||||
this.id = null;
|
||||
this.users = {};
|
||||
this.size = 0;
|
||||
this.server_follows_me = undefined;
|
||||
this.sequence = 0;
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -63,27 +63,8 @@ class TfProfileElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async initial_load() {
|
||||
this.server_follows_me = undefined;
|
||||
let server_id = await tfrpc.rpc.getServerIdentity();
|
||||
let followed = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT json_extract(content, '$.following') AS following
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
json_extract(content, '$.type') = 'contact' AND
|
||||
json_extract(content, '$.contact') = ? ORDER BY sequence DESC LIMIT 1
|
||||
`,
|
||||
[server_id, this.whoami]
|
||||
);
|
||||
let is_followed = false;
|
||||
for (let row of followed) {
|
||||
is_followed = row.following != 0;
|
||||
}
|
||||
this.server_follows_me = is_followed;
|
||||
}
|
||||
|
||||
modify(change) {
|
||||
let self = this;
|
||||
tfrpc.rpc
|
||||
.appendMessage(
|
||||
this.whoami,
|
||||
@ -95,6 +76,10 @@ class TfProfileElement extends LitElement {
|
||||
change
|
||||
)
|
||||
)
|
||||
.then(function () {
|
||||
self._follow_whoami = undefined;
|
||||
self.load();
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert(error?.message);
|
||||
});
|
||||
@ -156,7 +141,8 @@ class TfProfileElement extends LitElement {
|
||||
let self = this;
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = function (event) {
|
||||
input.addEventListener('change', function (event) {
|
||||
input.parentNode.removeChild(input);
|
||||
let file = event.target.files[0];
|
||||
file
|
||||
.arrayBuffer()
|
||||
@ -171,78 +157,119 @@ class TfProfileElement extends LitElement {
|
||||
.catch(function (e) {
|
||||
alert(e.message);
|
||||
});
|
||||
};
|
||||
});
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
async server_follow_me(follow) {
|
||||
try {
|
||||
await tfrpc.rpc.setServerFollowingMe(this.whoami, follow);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
try {
|
||||
await this.initial_load();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
copy_id() {
|
||||
navigator.clipboard.writeText(this.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
this.id == this.whoami &&
|
||||
this.editing &&
|
||||
this.server_follows_me === undefined
|
||||
) {
|
||||
this.initial_load();
|
||||
show_image(link) {
|
||||
let div = document.createElement('div');
|
||||
div.style.left = 0;
|
||||
div.style.top = 0;
|
||||
div.style.width = '100%';
|
||||
div.style.height = '100%';
|
||||
div.style.position = 'fixed';
|
||||
div.style.background = '#000';
|
||||
div.style.zIndex = 100;
|
||||
div.style.display = 'grid';
|
||||
let img = document.createElement('img');
|
||||
img.src = link;
|
||||
img.style.maxWidth = '100%';
|
||||
img.style.maxHeight = '100%';
|
||||
img.style.display = 'block';
|
||||
img.style.margin = 'auto';
|
||||
img.style.objectFit = 'contain';
|
||||
img.style.width = '100%';
|
||||
div.appendChild(img);
|
||||
function image_close(event) {
|
||||
document.body.removeChild(div);
|
||||
window.removeEventListener('keydown', image_close);
|
||||
}
|
||||
div.onclick = image_close;
|
||||
window.addEventListener('keydown', image_close);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
|
||||
body_click(event) {
|
||||
if (event.srcElement.tagName == 'IMG') {
|
||||
this.show_image(event.srcElement.src);
|
||||
}
|
||||
}
|
||||
|
||||
toggle_account_list(event) {
|
||||
let content = event.srcElement.nextElementSibling;
|
||||
if (content.classList.toggle('w3-hide')) {
|
||||
event.srcElement.innerText = 'Show Followed Accounts';
|
||||
} else {
|
||||
event.srcElement.innerText = 'Hide Followed Accounts';
|
||||
}
|
||||
}
|
||||
|
||||
async load_follows() {
|
||||
let accounts = await tfrpc.rpc.following([this.id], 1);
|
||||
return html`
|
||||
<div class="w3-container">
|
||||
<button
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${this.toggle_account_list}
|
||||
>
|
||||
Show Followed Accounts
|
||||
</button>
|
||||
<div class="w3-hide w3-card">
|
||||
<ul class="w3-ul w3-theme-d4 w3-border-theme">
|
||||
${Object.keys(accounts).map(
|
||||
(x) => html`
|
||||
<li class="w3-border-theme">
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.load();
|
||||
let self = this;
|
||||
let profile = this.users[this.id] || {};
|
||||
tfrpc.rpc
|
||||
.query(
|
||||
`SELECT SUM(LENGTH(content)) AS size FROM messages WHERE author = ?`,
|
||||
`SELECT SUM(LENGTH(content)) AS size, MAX(sequence) AS sequence FROM messages WHERE author = ?`,
|
||||
[this.id]
|
||||
)
|
||||
.then(function (result) {
|
||||
self.size = result[0].size;
|
||||
self.sequence = result[0].sequence;
|
||||
});
|
||||
let edit;
|
||||
let follow;
|
||||
let block;
|
||||
if (this.id === this.whoami) {
|
||||
if (this.editing) {
|
||||
let server_follow;
|
||||
if (this.server_follows_me === true) {
|
||||
server_follow = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => this.server_follow_me(false)}
|
||||
>
|
||||
Server, Stop Following Me
|
||||
</button>`;
|
||||
} else if (this.server_follows_me === false) {
|
||||
server_follow = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => this.server_follow_me(true)}
|
||||
>
|
||||
Server, Follow Me
|
||||
</button>`;
|
||||
}
|
||||
edit = html`
|
||||
<button class="w3-button w3-theme-d1" @click=${this.save_edits}>
|
||||
<button
|
||||
id="save_profile"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.save_edits}
|
||||
>
|
||||
Save Profile
|
||||
</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard_edits}>
|
||||
Discard
|
||||
</button>
|
||||
${server_follow}
|
||||
`;
|
||||
} else {
|
||||
edit = html`<button class="w3-button w3-theme-d1" @click=${this.edit}>
|
||||
edit = html`<button
|
||||
id="edit_profile"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.edit}
|
||||
>
|
||||
Edit Profile
|
||||
</button>`;
|
||||
}
|
||||
@ -268,55 +295,67 @@ class TfProfileElement extends LitElement {
|
||||
let edit_profile = this.editing
|
||||
? html`
|
||||
<div style="flex: 1 0 50%; display: flex; flex-direction: column; gap: 8px">
|
||||
<div class="w3-container">
|
||||
<div>
|
||||
<label for="name">Name:</label>
|
||||
<input class="w3-input w3-theme-d1" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))}></input>
|
||||
</div>
|
||||
<div><label for="description">Description:</label></div>
|
||||
<textarea class="w3-input w3-theme-d1" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))}>${this.editing.description}</textarea>
|
||||
<div>
|
||||
<label for="public_web_hosting">Public Web Hosting:</label>
|
||||
<input class="w3-check w3-theme-d1" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
|
||||
</div>
|
||||
<div>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach_image}>Attach Image</button>
|
||||
</div>
|
||||
<div>
|
||||
<label for="name">Name:</label>
|
||||
<input class="w3-input w3-theme-d1" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))} placeholder="Choose a name"></input>
|
||||
</div>
|
||||
<div><label for="description">Description:</label></div>
|
||||
<textarea class="w3-input w3-theme-d1" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))} placeholder="Tell people a little bit about yourself here, if you like.">${this.editing.description}</textarea>
|
||||
<div>
|
||||
<label for="public_web_hosting">Public Web Hosting:</label>
|
||||
<input class="w3-check w3-theme-d1" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
|
||||
</div>
|
||||
<div>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach_image}>Attach Image</button>
|
||||
</div>
|
||||
</div>`
|
||||
: null;
|
||||
let image =
|
||||
typeof profile.image == 'string' ? profile.image : profile.image?.link;
|
||||
let image = profile.image;
|
||||
if (typeof image == 'string' && !image.startsWith('&')) {
|
||||
try {
|
||||
image = JSON.parse(image)?.link;
|
||||
} catch {}
|
||||
}
|
||||
image = this.editing?.image ?? image;
|
||||
let description = this.editing?.description ?? profile.description;
|
||||
return html`<div style="border: 2px solid black; background-color: rgba(255, 255, 255, 0.2); padding: 16px">
|
||||
<tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)})
|
||||
<div class="w3-row">
|
||||
<div class="w3-col s1 w3-container w3-right">
|
||||
<button class="w3-button w3-theme-d1 w3-ripple" @click=${this.copy_id}>Copy</button>
|
||||
return html`<div class="w3-card-4 w3-container w3-theme-d3" style="box-sizing: border-box">
|
||||
<header class="w3-container">
|
||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||
</header>
|
||||
<div class="w3-container" @click=${this.body_click}>
|
||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||
</div>
|
||||
<div class="w3-rest w3-container">
|
||||
<input type="text" class="w3-theme-d1" style="width: 100%; vertical-align: middle" readonly value=${this.id}></input>
|
||||
<div style="display: flex; flex-direction: row; gap: 1em">
|
||||
${edit_profile}
|
||||
<div style="flex: 1 0 50%">
|
||||
${
|
||||
image
|
||||
? html`<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div>`
|
||||
: html`<div>
|
||||
<div class="w3-jumbo">😎</div>
|
||||
<div><i>Profile image not set.</i></div>
|
||||
</div>`
|
||||
}
|
||||
<div>${unsafeHTML(tfutils.markdown(description))}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Following ${profile.following} identities.
|
||||
Followed by ${profile.followed} identities.
|
||||
Blocking ${profile.blocking} identities.
|
||||
Blocked by ${profile.blocked} identities.
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: row; gap: 1em">
|
||||
${edit_profile}
|
||||
<div style="flex: 1 0 50%">
|
||||
<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div>
|
||||
<div>${unsafeHTML(tfutils.markdown(description))}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Following ${profile.following} identities.
|
||||
Followed by ${profile.followed} identities.
|
||||
Blocking ${profile.blocking} identities.
|
||||
Blocked by ${profile.blocked} identities.
|
||||
</div>
|
||||
<div>
|
||||
${edit}
|
||||
${follow}
|
||||
${block}
|
||||
</div>
|
||||
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||
<footer class="w3-container">
|
||||
<p>
|
||||
${edit}
|
||||
${follow}
|
||||
${block}
|
||||
</p>
|
||||
</footer>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,13 @@ class TfReactionsModalElement extends LitElement {
|
||||
return this.votes?.length
|
||||
? html` <div
|
||||
class="w3-modal w3-animate-opacity"
|
||||
style="display: block; box-sizing: border-box"
|
||||
style="display: block; box-sizing: border-box; z-index: 10"
|
||||
@click=${this.clear}
|
||||
>
|
||||
<div class="w3-modal-content w3-card-4 w3-theme-d1">
|
||||
<div
|
||||
class="w3-modal-content w3-card-4 w3-theme-d1"
|
||||
onclick="event.stopPropagation()"
|
||||
>
|
||||
<div class="w3-container w3-padding">
|
||||
<header class="w3-container">
|
||||
<h2>Reactions</h2>
|
||||
@ -37,23 +41,26 @@ class TfReactionsModalElement extends LitElement {
|
||||
>
|
||||
</header>
|
||||
<ul class="w3-theme-dark w3-container w3-ul">
|
||||
${this.votes.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">
|
||||
<span class="w3-bar-item"
|
||||
>${x?.content?.vote?.expression}</span
|
||||
>
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
id=${x.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
<span class="w3-bar-item w3-right"
|
||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
${this.votes
|
||||
.sort((x, y) => y.timestamp - x.timestamp)
|
||||
.map(
|
||||
(x) => html`
|
||||
<li style="display: flex; flex-direction: row; gap: 4px">
|
||||
<span style="flex-basis: 3em"
|
||||
>${x?.content?.vote?.expression}</span
|
||||
>
|
||||
<tf-user
|
||||
style="flex: 1 1"
|
||||
id=${x.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
<span
|
||||
style="flex-shrink: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<footer class="w3-container w3-padding">
|
||||
<button class="w3-button" @click=${this.clear}>Close</button>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {css} from './lit-all.min.js';
|
||||
import {css, unsafeCSS} from './lit-all.min.js';
|
||||
|
||||
const tf = css`
|
||||
img {
|
||||
@ -48,7 +48,7 @@ const tf = css`
|
||||
|
||||
// prettier-ignore
|
||||
const w3 = css`
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -88,7 +88,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
@ -136,7 +136,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@ -158,6 +158,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -199,9 +203,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
@ -216,15 +220,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -285,30 +298,165 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
`;
|
||||
|
||||
// prettier-ignore
|
||||
const w3_2016_riverside = css`
|
||||
.w3-theme-l5 {color:#000 !important; background-color:#f4f6f9 !important}
|
||||
.w3-theme-l4 {color:#000 !important; background-color:#d9e1ec !important}
|
||||
.w3-theme-l3 {color:#000 !important; background-color:#b4c3d8 !important}
|
||||
.w3-theme-l2 {color:#fff !important; background-color:#8ea6c5 !important}
|
||||
.w3-theme-l1 {color:#fff !important; background-color:#6888b1 !important}
|
||||
.w3-theme-d1 {color:#fff !important; background-color:#456185 !important}
|
||||
.w3-theme-d2 {color:#fff !important; background-color:#3d5676 !important}
|
||||
.w3-theme-d3 {color:#fff !important; background-color:#354b68 !important}
|
||||
.w3-theme-d4 {color:#fff !important; background-color:#2e4059 !important}
|
||||
.w3-theme-d5 {color:#fff !important; background-color:#26364a !important}
|
||||
function rgb_to_hsl(r, g, b) {
|
||||
let min,
|
||||
max,
|
||||
i,
|
||||
l,
|
||||
s,
|
||||
maxcolor,
|
||||
h,
|
||||
rgb = [];
|
||||
rgb[0] = r / 255;
|
||||
rgb[1] = g / 255;
|
||||
rgb[2] = b / 255;
|
||||
min = rgb[0];
|
||||
max = rgb[0];
|
||||
maxcolor = 0;
|
||||
for (i = 0; i < rgb.length - 1; i++) {
|
||||
if (rgb[i + 1] <= min) {
|
||||
min = rgb[i + 1];
|
||||
}
|
||||
if (rgb[i + 1] >= max) {
|
||||
max = rgb[i + 1];
|
||||
maxcolor = i + 1;
|
||||
}
|
||||
}
|
||||
if (maxcolor == 0) {
|
||||
h = (rgb[1] - rgb[2]) / (max - min);
|
||||
}
|
||||
if (maxcolor == 1) {
|
||||
h = 2 + (rgb[2] - rgb[0]) / (max - min);
|
||||
}
|
||||
if (maxcolor == 2) {
|
||||
h = 4 + (rgb[0] - rgb[1]) / (max - min);
|
||||
}
|
||||
if (isNaN(h)) {
|
||||
h = 0;
|
||||
}
|
||||
h = h * 60;
|
||||
if (h < 0) {
|
||||
h = h + 360;
|
||||
}
|
||||
l = (min + max) / 2;
|
||||
if (min == max) {
|
||||
s = 0;
|
||||
} else {
|
||||
if (l < 0.5) {
|
||||
s = (max - min) / (max + min);
|
||||
} else {
|
||||
s = (max - min) / (2 - max - min);
|
||||
}
|
||||
}
|
||||
s = s;
|
||||
return [h, s, l];
|
||||
}
|
||||
|
||||
.w3-theme-light {color:#000 !important; background-color:#f4f6f9 !important}
|
||||
.w3-theme-dark {color:#fff !important; background-color:#26364a !important}
|
||||
.w3-theme-action {color:#fff !important; background-color:#26364a !important}
|
||||
function hex_to_rgb(hex) {
|
||||
if (hex.charAt(0) == '#') {
|
||||
hex = hex.substring(1);
|
||||
}
|
||||
return [
|
||||
parseInt(hex.substring(0, 2), 16),
|
||||
parseInt(hex.substring(2, 4), 16),
|
||||
parseInt(hex.substring(4, 6), 16),
|
||||
];
|
||||
}
|
||||
|
||||
.w3-theme {color:#fff !important; background-color:#4c6a92 !important}
|
||||
.w3-text-theme {color:#4c6a92 !important}
|
||||
.w3-border-theme {border-color:#4c6a92 !important}
|
||||
function hsl_to_rgb(hue, sat, light) {
|
||||
let t2;
|
||||
hue /= 60;
|
||||
if (light <= 0.5) {
|
||||
t2 = light * (sat + 1);
|
||||
} else {
|
||||
t2 = light + sat - light * sat;
|
||||
}
|
||||
let t1 = light * 2 - t2;
|
||||
return [
|
||||
hue_to_rgb(t1, t2, hue + 2) * 255,
|
||||
hue_to_rgb(t1, t2, hue) * 255,
|
||||
hue_to_rgb(t1, t2, hue - 2) * 255,
|
||||
];
|
||||
}
|
||||
function hue_to_rgb(t1, t2, hue) {
|
||||
if (hue < 0) {
|
||||
hue += 6;
|
||||
}
|
||||
if (hue >= 6) {
|
||||
hue -= 6;
|
||||
}
|
||||
if (hue < 1) {
|
||||
return (t2 - t1) * hue + t1;
|
||||
} else if (hue < 3) {
|
||||
return t2;
|
||||
} else if (hue < 4) {
|
||||
return (t2 - t1) * (4 - hue) + t1;
|
||||
} else {
|
||||
return t1;
|
||||
}
|
||||
}
|
||||
|
||||
.w3-hover-theme:hover {color:#fff !important; background-color:#4c6a92 !important}
|
||||
.w3-hover-text-theme:hover {color:#4c6a92 !important}
|
||||
.w3-hover-border-theme:hover {border-color:#4c6a92 !important}
|
||||
`;
|
||||
function rgb_to_hex(rgb) {
|
||||
const hex_pair = (x) => Math.floor(x).toString(16).padStart(2, '0');
|
||||
return `#${hex_pair(rgb[0])}${hex_pair(rgb[1])}${hex_pair(rgb[2])}`;
|
||||
}
|
||||
|
||||
export let styles = [tf, w3, w3_2016_riverside];
|
||||
function is_dark(hex, value) {
|
||||
let [r, g, b] = hex_to_rgb(hex);
|
||||
return (r * 299 + g * 587 + b * 114) / 1000 < value;
|
||||
}
|
||||
|
||||
function generated() {
|
||||
let now = new Date();
|
||||
let k_color = rgb_to_hex([
|
||||
(now.getDay() * 128) / 6,
|
||||
(now.getHours() * 128) / 23,
|
||||
(now.getSeconds() * 128) / 59,
|
||||
]);
|
||||
//let k_color = '#034f84';
|
||||
//let k_color = rgb_to_hex([Math.random() * 256, Math.random() * 256, Math.random() * 256]);
|
||||
let [r, g, b] = hex_to_rgb(k_color);
|
||||
let [h, s, l] = rgb_to_hsl(r, g, b);
|
||||
|
||||
let theme1 = {
|
||||
l5: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 4.7)),
|
||||
l4: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 4)),
|
||||
l3: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 3)),
|
||||
l2: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 2)),
|
||||
l1: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 1)),
|
||||
d0: rgb_to_hex(hsl_to_rgb(h, s, l)),
|
||||
d1: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 0.5)),
|
||||
d2: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 1)),
|
||||
d3: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 1.5)),
|
||||
d4: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 2)),
|
||||
d5: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 2.5)),
|
||||
};
|
||||
for (let [k, v] of Object.entries(theme1)) {
|
||||
theme1['t' + k] = is_dark(v, 165) ? '#fff' : '#000';
|
||||
}
|
||||
|
||||
let result = `
|
||||
.w3-theme-l5 {color: ${theme1.tl5} !important; background-color: ${theme1.l5} !important}
|
||||
.w3-theme-l4 {color: ${theme1.tl4} !important; background-color: ${theme1.l4} !important}
|
||||
.w3-theme-l3 {color: ${theme1.tl3} !important; background-color: ${theme1.l3} !important}
|
||||
.w3-theme-l2 {color: ${theme1.tl2} !important; background-color: ${theme1.l2} !important}
|
||||
.w3-theme-l1 {color: ${theme1.tl1} !important; background-color: ${theme1.l1} !important}
|
||||
.w3-theme-d1 {color: ${theme1.td1} !important; background-color: ${theme1.d1} !important}
|
||||
.w3-theme-d2 {color: ${theme1.td2} !important; background-color: ${theme1.d2} !important}
|
||||
.w3-theme-d3 {color: ${theme1.td3} !important; background-color: ${theme1.d3} !important}
|
||||
.w3-theme-d4 {color: ${theme1.td4} !important; background-color: ${theme1.d4} !important}
|
||||
.w3-theme-d5 {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
|
||||
.w3-theme-light {color: ${theme1.tl5} !important; background-color: ${theme1.l5} !important}
|
||||
.w3-theme-dark {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
|
||||
.w3-theme-action {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
|
||||
.w3-theme {color: ${theme1.td0} !important; background-color: ${theme1.d0} !important}
|
||||
.w3-text-theme {color: ${theme1.d0} !important}
|
||||
.w3-border-theme {border-color: ${theme1.d0} !important}
|
||||
.w3-hover-theme:hover {color: ${theme1.td0} !important; background-color: ${theme1.d0} !important}
|
||||
.w3-hover-text-theme:hover {color: ${theme1.d0} !important}
|
||||
.w3-hover-border-theme:hover {border-color: ${theme1.d0} !important}
|
||||
`;
|
||||
return unsafeCSS(result);
|
||||
}
|
||||
|
||||
export let styles = [tf, w3, generated()];
|
||||
|
@ -12,6 +12,9 @@ class TfTabConnectionsElement extends LitElement {
|
||||
stored_connections: {type: Array},
|
||||
users: {type: Object},
|
||||
server_identity: {type: String},
|
||||
connect_attempt: {type: Object},
|
||||
connect_message: {type: String},
|
||||
connect_success: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -88,20 +91,53 @@ class TfTabConnectionsElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
render_broadcast(connection) {
|
||||
return html`
|
||||
<li class="w3-bar" style="overflow: hidden; overflow-wrap: nowrap">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.connect(connection)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<div class="w3-bar-item">
|
||||
${TfTabConnectionsElement.k_broadcast_emojis[connection.origin]}
|
||||
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
|
||||
${this.render_connection_summary(connection)}
|
||||
render_message(connection) {
|
||||
return html`<div
|
||||
?hidden=${this.connect_message === undefined ||
|
||||
this.connect_attempt != connection}
|
||||
style="cursor: pointer"
|
||||
class=${'w3-panel ' + (this.connect_success ? 'w3-green' : 'w3-red')}
|
||||
@click=${() => (this.connect_attempt = undefined)}
|
||||
>
|
||||
<p>${this.connect_message}</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render_progress(name, value, max) {
|
||||
if (max && value != max) {
|
||||
return html`
|
||||
<div class="w3-theme-d1 w3-small">
|
||||
<div
|
||||
class="w3-container w3-theme-l1"
|
||||
style="width: ${Math.floor(
|
||||
(100.0 * value) / max
|
||||
)}%; text-wrap: nowrap"
|
||||
>
|
||||
${name} ${value} / ${max} (${Math.round((100.0 * value) / max)}%)
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
render_broadcast(connection) {
|
||||
let self = this;
|
||||
return html`
|
||||
<li>
|
||||
<div class="w3-bar" style="overflow: hidden; overflow-wrap: nowrap">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => self.connect(connection)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<div class="w3-bar-item">
|
||||
${TfTabConnectionsElement.k_broadcast_emojis[connection.origin]}
|
||||
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
|
||||
${this.render_connection_summary(connection)}
|
||||
</div>
|
||||
</div>
|
||||
${this.render_message(connection)}
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
@ -123,13 +159,28 @@ class TfTabConnectionsElement extends LitElement {
|
||||
}, {})
|
||||
);
|
||||
return html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.closeConnection(connection.id)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
${connection.connected
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.closeConnection(connection.id)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${connection.flags.one_shot ? '🔃' : undefined}
|
||||
<tf-user id=${connection.id} .users=${this.users}></tf-user>
|
||||
${this.render_progress(
|
||||
'recv',
|
||||
connection.progress.in.total - connection.progress.in.current,
|
||||
connection.progress.in.total
|
||||
)}
|
||||
${this.render_progress(
|
||||
'send',
|
||||
connection.progress.out.total - connection.progress.out.current,
|
||||
connection.progress.out.total
|
||||
)}
|
||||
${connection.tunnel !== undefined
|
||||
? '🚇'
|
||||
: html`(${connection.host}:${connection.port})`}
|
||||
@ -155,92 +206,148 @@ class TfTabConnectionsElement extends LitElement {
|
||||
.map((x) => html`<li>${this.render_connection(x)}</li>`)}
|
||||
${this.render_room_peers(connection.id)}
|
||||
</ul>
|
||||
<div ?hidden=${!connection.destroy_reason} class="w3-panel w3-red">
|
||||
<p>${connection.destroy_reason}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
tfrpc.rpc.sync();
|
||||
connect(address) {
|
||||
let self = this;
|
||||
self.connect_attempt = address;
|
||||
self.connect_message = undefined;
|
||||
self.connect_success = false;
|
||||
tfrpc.rpc
|
||||
.connect(address)
|
||||
.then(function () {
|
||||
if (self.connect_attempt == address) {
|
||||
self.connect_message = 'Connected.';
|
||||
self.connect_success = true;
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
if (self.connect_attempt == address) {
|
||||
self.connect_message = 'Error: ' + error;
|
||||
self.connect_success = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggle_accordian(id) {
|
||||
let element = this.renderRoot.getElementById(id);
|
||||
element.classList.toggle('w3-hide');
|
||||
}
|
||||
|
||||
valid_connections() {
|
||||
return this.connections.filter((x) => x.tunnel === undefined);
|
||||
}
|
||||
|
||||
valid_broadcasts() {
|
||||
return this.broadcasts
|
||||
.filter((x) => x.address)
|
||||
.filter((x) => this.connections.map((c) => c.id).indexOf(x.pubkey) == -1);
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
<div class="w3-container" style="box-sizing: border-box">
|
||||
<button
|
||||
class="w3-button w3-theme-l3 w3-circle w3-ripple w3-large"
|
||||
@click=${this.refresh}
|
||||
>
|
||||
🔃
|
||||
</button>
|
||||
<h2>New Connection</h2>
|
||||
<textarea class="w3-input w3-theme-d1" id="code"></textarea>
|
||||
${this.render_message(this.renderRoot.getElementById('code')?.value)}
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() =>
|
||||
tfrpc.rpc.connect(self.renderRoot.getElementById('code').value)}
|
||||
self.connect(self.renderRoot.getElementById('code')?.value)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<h2>Broadcasts</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.broadcasts
|
||||
.filter((x) => x.address)
|
||||
.map((x) => self.render_broadcast(x))}
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('connections')}
|
||||
>
|
||||
Connections (${this.valid_connections().length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border" id="connections">
|
||||
${this.valid_connections().map(
|
||||
(x) => html` <li class="w3-bar">${this.render_connection(x)}</li> `
|
||||
)}
|
||||
</ul>
|
||||
<h2>Connections</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.connections
|
||||
.filter((x) => x.tunnel === undefined)
|
||||
.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">${this.render_connection(x)}</li>
|
||||
`
|
||||
)}
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('broadcasts')}
|
||||
>
|
||||
Discovery (${this.valid_broadcasts().length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border w3-hide" id="broadcasts">
|
||||
${this.valid_broadcasts().map((x) => self.render_broadcast(x))}
|
||||
</ul>
|
||||
<h2>Stored Connections</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('stored_connections')}
|
||||
>
|
||||
Stored Connections (${this.stored_connections.length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border w3-hide" id="stored_connections">
|
||||
${this.stored_connections.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => self.forget_stored_connection(x)}
|
||||
>
|
||||
Forget
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.connect(x)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div><small>${x.address}:${x.port}</small></div>
|
||||
<li>
|
||||
<div class="w3-bar">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => self.forget_stored_connection(x)}
|
||||
>
|
||||
Forget
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => this.connect(x)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div><small>${x.address}:${x.port}</small></div>
|
||||
<div>
|
||||
<small
|
||||
>Last connection:
|
||||
${new Date(x.last_success * 1000)}</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${this.render_message(x)}
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<h2>Local Accounts</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('local_accounts')}
|
||||
>
|
||||
Local Accounts (${this.identities.length})
|
||||
</h2>
|
||||
<div class="w3-container w3-hide" id="local_accounts">
|
||||
${this.identities.map(
|
||||
(x) =>
|
||||
html`<li class="w3-bar">
|
||||
html`<div
|
||||
class="w3-tag w3-round w3-theme-l3"
|
||||
style="padding: 4px; margin: 2px; max-width: 100%; text-wrap: nowrap; overflow: hidden"
|
||||
>
|
||||
${x == this.server_identity
|
||||
? html`<span class="w3-tag w3-medium w3-round w3-theme-l1"
|
||||
>🖥 local server</span
|
||||
>`
|
||||
? html`<div class="w3-tag w3-medium w3-round w3-theme-l1">
|
||||
🖥 local server
|
||||
</div>`
|
||||
: undefined}
|
||||
${this.my_identities.indexOf(x) != -1
|
||||
? html`<span class="w3-tag w3-medium w3-round w3-theme-d1"
|
||||
>😎 you</span
|
||||
>`
|
||||
? html`<div class="w3-tag w3-medium w3-round w3-theme-d1">
|
||||
😎 you
|
||||
</div>`
|
||||
: undefined}
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
</li>`
|
||||
</div>`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
class TfTabMentionsElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
users: {type: Object},
|
||||
following: {type: Array},
|
||||
expanded: {type: Object},
|
||||
messages: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let self = this;
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.following = [];
|
||||
this.expanded = {};
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
async load() {
|
||||
console.log('Loading...', this.whoami);
|
||||
let results = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts(?)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?
|
||||
ORDER BY timestamp DESC limit 20
|
||||
`,
|
||||
[
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
JSON.stringify(this.following),
|
||||
this.whoami,
|
||||
]
|
||||
);
|
||||
console.log('Done.');
|
||||
this.messages = results;
|
||||
}
|
||||
|
||||
on_expand(event) {
|
||||
if (event.detail.expanded) {
|
||||
let expand = {};
|
||||
expand[event.detail.id] = true;
|
||||
this.expanded = Object.assign({}, this.expanded, expand);
|
||||
} else {
|
||||
delete this.expanded[event.detail.id];
|
||||
this.expanded = Object.assign({}, this.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
if (!this.loading) {
|
||||
this.loading = true;
|
||||
this.load();
|
||||
}
|
||||
return html`
|
||||
<tf-news
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
.messages=${this.messages}
|
||||
.users=${this.users}
|
||||
.expanded=${this.expanded}
|
||||
@tf-expand=${this.on_expand}
|
||||
></tf-news>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('tf-tab-mentions', TfTabMentionsElement);
|
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
|
||||
import {LitElement, cache, html, unsafeHTML, until} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
@ -12,6 +12,13 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
messages: {type: Array},
|
||||
drafts: {type: Object},
|
||||
expanded: {type: Object},
|
||||
channels_unread: {type: Object},
|
||||
channels_latest: {type: Object},
|
||||
loading: {type: Number},
|
||||
time_range: {type: Array},
|
||||
time_loading: {type: Array},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,112 +33,274 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
this.following = [];
|
||||
this.drafts = {};
|
||||
this.expanded = {};
|
||||
this.start_time = new Date().valueOf() - 24 * 60 * 60 * 1000;
|
||||
this.channels_unread = {};
|
||||
this.channels_latest = {};
|
||||
this.start_time = new Date().valueOf();
|
||||
this.time_range = [0, 0];
|
||||
this.time_loading = undefined;
|
||||
this.recent_reactions = [];
|
||||
this.loading = 0;
|
||||
}
|
||||
|
||||
async fetch_messages() {
|
||||
if (this.hash.startsWith('#@')) {
|
||||
let r = await tfrpc.rpc.query(
|
||||
channel() {
|
||||
return this.hash.startsWith('##')
|
||||
? this.hash.substring(2)
|
||||
: this.hash.substring(1);
|
||||
}
|
||||
|
||||
async _fetch_related_messages(messages) {
|
||||
let refs = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
news AS (
|
||||
SELECT value AS id FROM json_each(?)
|
||||
)
|
||||
SELECT refs_out.ref AS ref FROM messages_refs refs_out JOIN news ON refs_out.message = news.id
|
||||
UNION
|
||||
SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id
|
||||
`,
|
||||
[JSON.stringify(messages.map((x) => x.id))]
|
||||
);
|
||||
let related_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?2) refs ON messages.id = refs.value
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
`,
|
||||
[JSON.stringify(this.following), JSON.stringify(refs.map((x) => x.ref))]
|
||||
);
|
||||
let combined = [].concat(messages, related_messages);
|
||||
let refs2 = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
news AS (
|
||||
SELECT value AS id FROM json_each(?)
|
||||
)
|
||||
SELECT refs_out.ref AS ref FROM messages_refs refs_out JOIN news ON refs_out.message = news.id
|
||||
UNION
|
||||
SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id
|
||||
`,
|
||||
[JSON.stringify(combined.map((x) => x.id))]
|
||||
);
|
||||
let t0 = new Date();
|
||||
let result = [].concat(
|
||||
combined,
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
WITH mine AS (SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE messages.author = ?
|
||||
ORDER BY sequence DESC
|
||||
LIMIT 20)
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM mine
|
||||
JOIN messages_refs ON mine.id = messages_refs.ref
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM json_each(?2) refs
|
||||
JOIN messages ON messages.id = refs.value
|
||||
JOIN json_each(?1) following ON messages.author = following.value
|
||||
WHERE messages.content ->> 'type' != 'post'
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.following),
|
||||
JSON.stringify(refs2.map((x) => x.ref)),
|
||||
]
|
||||
)
|
||||
);
|
||||
let t1 = new Date();
|
||||
console.log((t1 - t0) / 1000);
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetch_messages(start_time, end_time) {
|
||||
this.time_loading = [start_time, end_time];
|
||||
let result;
|
||||
if (this.hash == '#@') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH mentions AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts(?1)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.author != ?1 AND
|
||||
(?3 IS NULL OR messages.timestamp >= ?3) AND messages.timestamp < ?4
|
||||
ORDER BY timestamp DESC limit 20)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM mentions
|
||||
JOIN messages_refs ON mentions.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT * FROM mine
|
||||
SELECT TRUE AS is_primary, * FROM mentions
|
||||
`,
|
||||
[this.hash.substring(1)]
|
||||
[
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
JSON.stringify(this.following),
|
||||
start_time,
|
||||
end_time,
|
||||
]
|
||||
);
|
||||
return r;
|
||||
} else if (this.hash.startsWith('#%')) {
|
||||
return await tfrpc.rpc.query(
|
||||
} else if (this.hash.startsWith('#@')) {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE id = ?1
|
||||
WITH
|
||||
selected AS (SELECT rowid, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE messages.author = ?1 AND (?2 IS NULL OR messages.timestamp >= 2) AND messages.timestamp < ?3
|
||||
ORDER BY sequence DESC LIMIT 20
|
||||
)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM selected
|
||||
JOIN messages_refs ON selected.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
SELECT TRUE AS is_primary, * FROM selected
|
||||
`,
|
||||
[this.hash.substring(1), start_time, end_time]
|
||||
);
|
||||
} else if (this.hash.startsWith('#%')) {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT TRUE AS is_primary, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE messages.id = ?1
|
||||
UNION
|
||||
SELECT FALSE AS is_primary, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages JOIN messages_refs
|
||||
ON messages.id = messages_refs.message
|
||||
WHERE messages_refs.ref = ?1
|
||||
`,
|
||||
[this.hash.substring(1)]
|
||||
);
|
||||
} else {
|
||||
let promises = [];
|
||||
const k_following_limit = 256;
|
||||
for (let i = 0; i < this.following.length; i += k_following_limit) {
|
||||
promises.push(
|
||||
tfrpc.rpc.query(
|
||||
`
|
||||
WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
} else if (this.hash.startsWith('##')) {
|
||||
let t0 = new Date();
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
all_news AS (
|
||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.content ->> 'channel' = ?4 AND messages.content ->> 'type' != 'vote'
|
||||
UNION
|
||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_refs
|
||||
JOIN messages ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
WHERE messages_refs.ref = '#' || ?4 AND messages.content ->> 'type' != 'vote'
|
||||
)
|
||||
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||
ORDER BY all_news.timestamp DESC LIMIT 20
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.following),
|
||||
start_time,
|
||||
end_time,
|
||||
this.hash.substring(2),
|
||||
]
|
||||
);
|
||||
let t1 = new Date();
|
||||
result = await this._fetch_related_messages(initial_messages);
|
||||
let t2 = new Date();
|
||||
console.log(
|
||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
||||
);
|
||||
} else if (this.hash == '#🔐') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT TRUE AS is_primary, messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
JOIN json_each(?1) AS private_messages ON messages.id = private_messages.value
|
||||
WHERE
|
||||
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY messages.rowid DESC LIMIT 20
|
||||
`,
|
||||
[JSON.stringify(this.private_messages), start_time, end_time]
|
||||
);
|
||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
} else if (this.hash == '#👍') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH votes AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.timestamp > ? AND messages.timestamp < ?
|
||||
ORDER BY messages.timestamp DESC)
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
UNION
|
||||
SELECT news.* FROM news
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.following.slice(i, i + k_following_limit)),
|
||||
this.start_time,
|
||||
/*
|
||||
** Don't show messages more than a day into the future to prevent
|
||||
** messages with far-future timestamps from staying at the top forever.
|
||||
*/
|
||||
new Date().valueOf() + 24 * 60 * 60 * 1000,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
return [].concat(...(await Promise.all(promises)));
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
|
||||
ORDER BY timestamp DESC limit 20)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM votes
|
||||
JOIN messages ON messages.id = votes.content ->> '$.vote.link'
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, * FROM votes
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time]
|
||||
);
|
||||
} else {
|
||||
let t0 = new Date();
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT TRUE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.timestamp < ?3 AND (?2 IS NULL OR messages.timestamp >= ?2) AND
|
||||
messages.content ->> 'type' != 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 20
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time]
|
||||
);
|
||||
let t1 = new Date();
|
||||
result = await this._fetch_related_messages(initial_messages);
|
||||
let t2 = new Date();
|
||||
console.log(
|
||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
||||
);
|
||||
}
|
||||
this.time_loading = undefined;
|
||||
return result;
|
||||
}
|
||||
|
||||
update_time_range_from_messages(messages) {
|
||||
let only_primary = messages.filter((x) => x.is_primary);
|
||||
this.time_range = [
|
||||
only_primary.reduce(
|
||||
(accumulator, current) => Math.min(accumulator, current.timestamp),
|
||||
this.time_range[0]
|
||||
),
|
||||
only_primary.reduce(
|
||||
(accumulator, current) => Math.max(accumulator, current.timestamp),
|
||||
this.time_range[1]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return (
|
||||
this.hash == '#@' ||
|
||||
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
|
||||
);
|
||||
}
|
||||
|
||||
async load_more() {
|
||||
let last_start_time = this.start_time;
|
||||
this.start_time = last_start_time - 24 * 60 * 60 * 1000;
|
||||
let more = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.timestamp > ?
|
||||
AND messages.timestamp <= ?
|
||||
ORDER BY messages.timestamp DESC)
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
UNION
|
||||
SELECT news.* FROM news
|
||||
`,
|
||||
[JSON.stringify(this.following), this.start_time, last_start_time]
|
||||
);
|
||||
this.messages = await this.decrypt([...more, ...this.messages]);
|
||||
this.loading++;
|
||||
this.loading_canceled = false;
|
||||
try {
|
||||
let more = [];
|
||||
let last_start_time = this.time_range[0];
|
||||
try {
|
||||
more = await this.fetch_messages(null, last_start_time);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
this.update_time_range_from_messages(
|
||||
more.filter((x) => x.timestamp < last_start_time)
|
||||
);
|
||||
this.messages = await this.decrypt([...more, ...this.messages]);
|
||||
} finally {
|
||||
this.loading--;
|
||||
}
|
||||
}
|
||||
|
||||
cancel_load() {
|
||||
this.loading_canceled = true;
|
||||
}
|
||||
|
||||
async decrypt(messages) {
|
||||
console.log('decrypt');
|
||||
let result = [];
|
||||
for (let message of messages) {
|
||||
let content;
|
||||
@ -156,44 +325,155 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
return result;
|
||||
}
|
||||
|
||||
async add_messages(messages) {
|
||||
this.messages = await this.decrypt([...messages, ...this.messages]);
|
||||
merge_messages(old_messages, new_messages) {
|
||||
let old_by_id = Object.fromEntries(old_messages.map((x) => [x.id, x]));
|
||||
return new_messages.map((x) => (old_by_id[x.id] ? old_by_id[x.id] : x));
|
||||
}
|
||||
|
||||
async load_latest() {
|
||||
this.loading++;
|
||||
let now = new Date().valueOf();
|
||||
let end_time = now + 24 * 60 * 60 * 1000;
|
||||
let messages = [];
|
||||
try {
|
||||
messages = await this.fetch_messages(this.time_range[0], end_time);
|
||||
messages = await this.decrypt(messages);
|
||||
this.update_time_range_from_messages(
|
||||
messages.filter(
|
||||
(x) => x.timestamp >= this.time_range[0] && x.timestamp < end_time
|
||||
)
|
||||
);
|
||||
} finally {
|
||||
this.loading--;
|
||||
}
|
||||
this.messages = this.merge_messages(
|
||||
this.messages,
|
||||
Object.values(
|
||||
Object.fromEntries(
|
||||
[...this.messages, ...messages]
|
||||
.sort((x, y) => x.timestamp - y.timestamp)
|
||||
.slice(-1024)
|
||||
.map((x) => [x.id, x])
|
||||
)
|
||||
)
|
||||
);
|
||||
console.log('done loading latest messages.');
|
||||
}
|
||||
|
||||
async load_messages() {
|
||||
let start_time = new Date();
|
||||
let self = this;
|
||||
this.loading++;
|
||||
let messages = [];
|
||||
try {
|
||||
if (this._messages_hash !== this.hash) {
|
||||
this.messages = [];
|
||||
this._messages_hash = this.hash;
|
||||
}
|
||||
this._messages_following = this.following;
|
||||
let now = new Date().valueOf();
|
||||
let start_time = now - 24 * 60 * 60 * 1000;
|
||||
this.start_time = start_time;
|
||||
this.time_range = [now + 24 * 60 * 60 * 1000, now + 24 * 60 * 60 * 1000];
|
||||
messages = await this.fetch_messages(null, this.time_range[1]);
|
||||
this.update_time_range_from_messages(
|
||||
messages.filter((x) => x.timestamp < this.time_range[1])
|
||||
);
|
||||
messages = await this.decrypt(messages);
|
||||
} finally {
|
||||
this.loading--;
|
||||
}
|
||||
this.messages = this.merge_messages(this.messages, messages);
|
||||
this.time_loading = undefined;
|
||||
console.log(
|
||||
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
||||
);
|
||||
}
|
||||
|
||||
mark_all_read() {
|
||||
let newest = this.messages.reduce(
|
||||
(accumulator, current) => Math.max(accumulator, current.rowid),
|
||||
this.channels_latest[this.channel()] ?? -1
|
||||
);
|
||||
if (newest >= 0) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('channelsetunread', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
channel: this.channel(),
|
||||
unread: newest + 1,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.messages ||
|
||||
this._messages_hash !== this.hash ||
|
||||
this._messages_following !== this.following
|
||||
JSON.stringify(this._messages_following) !==
|
||||
JSON.stringify(this.following)
|
||||
) {
|
||||
console.log(
|
||||
`loading messages for ${this.whoami} (following ${this.following.length})`
|
||||
);
|
||||
let self = this;
|
||||
this.messages = [];
|
||||
this._messages_hash = this.hash;
|
||||
this._messages_following = this.following;
|
||||
this.fetch_messages()
|
||||
.then(this.decrypt.bind(this))
|
||||
.then(function (messages) {
|
||||
self.messages = messages;
|
||||
console.log(`loading mesages done for ${self.whoami}`);
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert(JSON.stringify(error, null, 2));
|
||||
});
|
||||
this.load_messages();
|
||||
}
|
||||
let more;
|
||||
if (!this.hash.startsWith('#@') && !this.hash.startsWith('#%')) {
|
||||
if (!this.hash.startsWith('#%')) {
|
||||
more = html`
|
||||
<p>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.load_more}>
|
||||
${this.unread_allowed()
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
<button
|
||||
?disabled=${this.loading}
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.load_more}
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
<button
|
||||
class=${'w3-button w3-theme-d1' + (this.loading ? '' : ' w3-hide')}
|
||||
@click=${this.cancel_load}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<span
|
||||
>Showing
|
||||
${new Date(
|
||||
this.time_loading
|
||||
? Math.min(this.time_loading[0], this.time_range[0])
|
||||
: this.time_range[0]
|
||||
).toLocaleDateString()}
|
||||
-
|
||||
${new Date(
|
||||
this.time_loading
|
||||
? Math.max(this.time_loading[1], this.time_range[1])
|
||||
: this.time_range[1]
|
||||
).toLocaleDateString()}.</span
|
||||
>
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
return html`
|
||||
return cache(html`
|
||||
${this.unread_allowed()
|
||||
? html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>`
|
||||
: undefined}
|
||||
<tf-news
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
@ -202,9 +482,14 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
.following=${this.following}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
hash=${this.hash}
|
||||
channel=${this.channel()}
|
||||
channel_unread=${this.channels_unread?.[this.channel()]}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
@mark_all_read=${this.mark_all_read}
|
||||
></tf-news>
|
||||
${more}
|
||||
`;
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,11 @@
|
||||
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
|
||||
import {
|
||||
LitElement,
|
||||
cache,
|
||||
keyed,
|
||||
html,
|
||||
unsafeHTML,
|
||||
until,
|
||||
} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
@ -8,11 +15,19 @@ class TfTabNewsElement extends LitElement {
|
||||
whoami: {type: String},
|
||||
users: {type: Object},
|
||||
hash: {type: String},
|
||||
unread: {type: Array},
|
||||
following: {type: Array},
|
||||
drafts: {type: Object},
|
||||
expanded: {type: Object},
|
||||
loading: {type: Boolean},
|
||||
channels: {type: Array},
|
||||
channels_unread: {type: Object},
|
||||
channels_latest: {type: Object},
|
||||
connections: {type: Array},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
peer_exchange: {type: Boolean},
|
||||
is_administrator: {type: Boolean},
|
||||
stay_connected: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -24,14 +39,19 @@ class TfTabNewsElement extends LitElement {
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.hash = '#';
|
||||
this.unread = [];
|
||||
this.following = [];
|
||||
this.cache = {};
|
||||
this.drafts = {};
|
||||
this.expanded = {};
|
||||
this.channels_unread = {};
|
||||
this.channels_latest = {};
|
||||
this.channels = [];
|
||||
this.connections = [];
|
||||
this.recent_reactions = [];
|
||||
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
||||
self.drafts = JSON.parse(d || '{}');
|
||||
});
|
||||
this.check_peer_exchange();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -44,37 +64,19 @@ class TfTabNewsElement extends LitElement {
|
||||
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
||||
}
|
||||
|
||||
show_more() {
|
||||
let unread = this.unread;
|
||||
let news = this.shadowRoot?.getElementById('news');
|
||||
if (news) {
|
||||
console.log('injecting messages', news.messages);
|
||||
news.add_messages(
|
||||
Object.values(Object.fromEntries(this.unread.map((x) => [x.id, x])))
|
||||
);
|
||||
this.dispatchEvent(new CustomEvent('refresh'));
|
||||
async check_peer_exchange() {
|
||||
if (await tfrpc.rpc.isAdministrator()) {
|
||||
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||
} else {
|
||||
this.peer_exchange = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
new_messages_text() {
|
||||
if (!this.unread?.length) {
|
||||
return 'No new messages.';
|
||||
load_latest() {
|
||||
let news = this.shadowRoot?.getElementById('news');
|
||||
if (news) {
|
||||
news.load_latest();
|
||||
}
|
||||
let counts = {};
|
||||
for (let message of this.unread) {
|
||||
let type = 'private';
|
||||
try {
|
||||
type = JSON.parse(message.content).type || type;
|
||||
} catch {}
|
||||
counts[type] = (counts[type] || 0) + 1;
|
||||
}
|
||||
return (
|
||||
'↻ Show New: ' +
|
||||
Object.keys(counts)
|
||||
.sort()
|
||||
.map((x) => counts[x].toString() + ' ' + x + 's')
|
||||
.join(', ')
|
||||
);
|
||||
}
|
||||
|
||||
draft(event) {
|
||||
@ -106,14 +108,256 @@ class TfTabNewsElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
unread_status(channel) {
|
||||
if (channel === undefined) {
|
||||
if (
|
||||
Object.keys(this.channels_unread).some((x) => this.unread_status(x))
|
||||
) {
|
||||
return '✉️ ';
|
||||
}
|
||||
} else if (
|
||||
this.channels_latest[channel] &&
|
||||
this.channels_latest[channel] > 0 &&
|
||||
(this.channels_unread[channel] === undefined ||
|
||||
this.channels_unread[channel] <= this.channels_latest[channel])
|
||||
) {
|
||||
return '✉️ ';
|
||||
}
|
||||
}
|
||||
|
||||
show_sidebar() {
|
||||
this.renderRoot.getElementById('sidebar').style.display = 'block';
|
||||
this.renderRoot.getElementById('sidebar_overlay').style.display = 'block';
|
||||
}
|
||||
|
||||
hide_sidebar() {
|
||||
this.renderRoot.getElementById('sidebar').style.display = 'none';
|
||||
this.renderRoot.getElementById('sidebar_overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
async channel_toggle_subscribed() {
|
||||
let channel = this.hash.substring(2);
|
||||
let subscribed = this.channels.indexOf(channel) != -1;
|
||||
subscribed = !subscribed;
|
||||
|
||||
await tfrpc.rpc.appendMessage(this.whoami, {
|
||||
type: 'channel',
|
||||
channel: channel,
|
||||
subscribed: subscribed,
|
||||
});
|
||||
if (subscribed) {
|
||||
this.channels = [].concat([channel], this.channels).sort();
|
||||
} else {
|
||||
this.channels = this.channels.filter((x) => x != channel);
|
||||
}
|
||||
}
|
||||
|
||||
channel() {
|
||||
return this.hash.startsWith('##') ? this.hash.substring(2) : undefined;
|
||||
}
|
||||
|
||||
compare_follows() {
|
||||
const now = new Date().valueOf();
|
||||
return function (a, b) {
|
||||
return (b[1].ts > now ? -1 : b[1].ts) - (a[1].ts > now ? -1 : a[1].ts);
|
||||
};
|
||||
}
|
||||
|
||||
suggested_follows() {
|
||||
/*
|
||||
** Filter out people who have used future timestamps so that they aren't
|
||||
** pinned at the top.
|
||||
*/
|
||||
let self = this;
|
||||
return Object.entries(this.users)
|
||||
.filter((x) => x[1].follow_depth > 1)
|
||||
.sort(self.compare_follows())
|
||||
.slice(0, 8)
|
||||
.map((x) => x[0]);
|
||||
}
|
||||
|
||||
async enable_peer_exchange() {
|
||||
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||
await this.check_peer_exchange();
|
||||
}
|
||||
|
||||
render_sidebar() {
|
||||
return html`
|
||||
<div
|
||||
class="w3-sidebar w3-bar-block w3-theme-d1 w3-collapse w3-animate-left"
|
||||
style="width: 2in; left: 0; z-index: 5; box-sizing: border-box; top: 0"
|
||||
id="sidebar"
|
||||
>
|
||||
<div
|
||||
class="w3-right w3-button w3-hide-large"
|
||||
@click=${this.hide_sidebar}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
${this.is_administrator
|
||||
? html`
|
||||
<button
|
||||
class="w3-bar-item w3-button"
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new Event('refresh', {bubbles: true, composed: true})
|
||||
)}
|
||||
>
|
||||
<span style="width: 1.5em; height: 1.5em; padding: 8px">↻</span>
|
||||
Sync now
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-ripple"
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new Event('toggle_stay_connected', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
)}
|
||||
>
|
||||
${this.stay_connected ? '🔗 Online mode' : '⛓️💥 Passive mode'}
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${this.hash.startsWith('##') &&
|
||||
this.channels.indexOf(this.hash.substring(2)) == -1
|
||||
? html`
|
||||
<div class="w3-bar-item w3-theme-d2">Viewing</div>
|
||||
<a
|
||||
href="#"
|
||||
class="w3-bar-item w3-button"
|
||||
style="font-weight: bold"
|
||||
>${this.hash.substring(2)}</a
|
||||
>
|
||||
`
|
||||
: undefined}
|
||||
<h4 class="w3-bar-item w3-theme-d2">Channels</h4>
|
||||
<a
|
||||
href="#"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('')}general</a
|
||||
>
|
||||
<a
|
||||
href="#@"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('@')}@mentions</a
|
||||
>
|
||||
<a
|
||||
href="#👍"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('👍')}👍votes</a
|
||||
>
|
||||
<a
|
||||
href="#🔐"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('🔐')}🔐private</a
|
||||
>
|
||||
${Object.keys(this.drafts)
|
||||
.sort()
|
||||
.map(
|
||||
(x) => html`
|
||||
<a
|
||||
href=${'#' + encodeURIComponent(x)}
|
||||
class="w3-bar-item w3-button"
|
||||
style="text-wrap: nowrap; text-overflow: ellipsis"
|
||||
>📝 ${this.drafts[x]?.text ?? x}</a
|
||||
>
|
||||
`
|
||||
)}
|
||||
${this.channels.map(
|
||||
(x) => html`
|
||||
<a
|
||||
href=${'#' + encodeURIComponent('#' + x)}
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '##' + x ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status(x)}#${x}</a
|
||||
>
|
||||
`
|
||||
)}
|
||||
|
||||
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
||||
<h4 style="margin: 0">Connections</h4>
|
||||
</a>
|
||||
${this.connections?.filter((x) => x.id)?.length == 0
|
||||
? html`
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.connections?.some((x) => x.flags.one_shot)
|
||||
? ' w3-spin'
|
||||
: '')}
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new Event('refresh', {bubbles: true, composed: true})
|
||||
)}
|
||||
>
|
||||
↻ Sync now
|
||||
</button>
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||
@click=${this.enable_peer_exchange}
|
||||
>
|
||||
Enable peer exchange
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${this.connections
|
||||
.filter((x) => x.id)
|
||||
.map(
|
||||
(x) => html`
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
style=${x.destroy_reason
|
||||
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||
: x.connected
|
||||
? x.flags?.one_shot
|
||||
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||
: ''}
|
||||
id=${x.id}
|
||||
fallback_name=${x.host}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
`
|
||||
)}
|
||||
<h4 class="w3-bar-item w3-theme-d2">Suggested Follows</h4>
|
||||
${this.suggested_follows().map(
|
||||
(x) => html`
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
style="max-width: 100%"
|
||||
id=${x}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
class="w3-overlay"
|
||||
id="sidebar_overlay"
|
||||
@click=${this.hide_sidebar}
|
||||
></div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
let profile = this.hash.startsWith('#@')
|
||||
? html`<tf-profile
|
||||
id=${this.hash.substring(1)}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
></tf-profile>`
|
||||
: undefined;
|
||||
let profile =
|
||||
this.hash.startsWith('#@') && this.hash != '#@'
|
||||
? keyed(
|
||||
this.hash.substring(1),
|
||||
html`<tf-profile
|
||||
class="tf-profile"
|
||||
id=${this.hash.substring(1)}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
></tf-profile>`
|
||||
)
|
||||
: undefined;
|
||||
let edit_profile;
|
||||
if (
|
||||
!this.loading &&
|
||||
@ -127,41 +371,68 @@ class TfTabNewsElement extends LitElement {
|
||||
name.
|
||||
</div>`;
|
||||
}
|
||||
return html`
|
||||
<p class="w3-bar">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${this.show_more}
|
||||
>
|
||||
${this.new_messages_text()}
|
||||
</button>
|
||||
</p>
|
||||
<div class="w3-bar">
|
||||
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
|
||||
${edit_profile}
|
||||
return cache(html`
|
||||
${this.render_sidebar()}
|
||||
<div
|
||||
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout"
|
||||
id="main"
|
||||
class="w3-main"
|
||||
>
|
||||
<div style="padding: 8px">
|
||||
<p>
|
||||
${this.hash.startsWith('##')
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.channel_toggle_subscribed}
|
||||
>
|
||||
${this.channels.indexOf(this.hash.substring(2)) != -1
|
||||
? 'Unsubscribe from #'
|
||||
: 'Subscribe to #'}${this.hash.substring(2)}
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
id="show_sidebar"
|
||||
class="w3-button w3-hide-large"
|
||||
@click=${this.show_sidebar}
|
||||
>
|
||||
${this.unread_status()}☰
|
||||
</div>
|
||||
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
|
||||
${edit_profile}
|
||||
</div>
|
||||
<div>
|
||||
<tf-compose
|
||||
id="tf-compose"
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
@tf-draft=${this.draft}
|
||||
.channel=${this.channel()}
|
||||
></tf-compose>
|
||||
</div>
|
||||
${profile}
|
||||
<tf-tab-news-feed
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.following=${this.following}
|
||||
hash=${this.hash}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
@tf-draft=${this.draft}
|
||||
@tf-expand=${this.on_expand}
|
||||
.channels_unread=${this.channels_unread}
|
||||
.channels_latest=${this.channels_latest}
|
||||
.private_messages=${this.private_messages}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-tab-news-feed>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<tf-compose
|
||||
id="tf-compose"
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
@tf-draft=${this.draft}
|
||||
></tf-compose>
|
||||
</div>
|
||||
${profile}
|
||||
<tf-tab-news-feed
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.following=${this.following}
|
||||
hash=${this.hash}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
@tf-draft=${this.draft}
|
||||
@tf-expand=${this.on_expand}
|
||||
></tf-tab-news-feed>
|
||||
`;
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@ class TfTagElement extends LitElement {
|
||||
render() {
|
||||
let number = this.count ? html` (${this.count})` : undefined;
|
||||
return html`<a
|
||||
href="#q=${this.tag}"
|
||||
style="display: inline-block; margin: 3px; border: 1px solid black; background-color: #444; padding: 4px; border-radius: 3px"
|
||||
href=${'#' + encodeURIComponent(this.tag)}
|
||||
class="w3-tag w3-theme-d1 w3-round-4 w3-button"
|
||||
>${this.tag}${number}</a
|
||||
>`;
|
||||
> `;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@ class TfUserElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
id: {type: String},
|
||||
fallback_name: {type: String},
|
||||
icon_only: {type: Boolean},
|
||||
users: {type: Object},
|
||||
};
|
||||
}
|
||||
@ -15,34 +17,47 @@ class TfUserElement extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.fallback_name = null;
|
||||
this.icon_only = false;
|
||||
this.users = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
let user = this.users[this.id];
|
||||
let shape =
|
||||
user?.follow_depth === undefined || user.follow_depth >= 2
|
||||
? 'w3-circle'
|
||||
: 'w3-round';
|
||||
let image = html`<span
|
||||
class="w3-theme-light w3-circle"
|
||||
class=${'w3-theme-l4 ' + shape}
|
||||
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
|
||||
>?</span
|
||||
>😎</span
|
||||
>`;
|
||||
let name = this.users?.[this.id]?.name;
|
||||
name =
|
||||
name !== undefined
|
||||
? html`<a target="_top" href=${'#' + this.id}>${name}</a>`
|
||||
: html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`;
|
||||
let name_string = name ?? this.fallback_name ?? this.id;
|
||||
name = this.icon_only
|
||||
? undefined
|
||||
: html`<a target="_top" href=${'#' + this.id}>${name_string}</a>`;
|
||||
|
||||
if (this.users[this.id]) {
|
||||
let image_link = this.users[this.id].image;
|
||||
image_link =
|
||||
typeof image_link == 'string' ? image_link : image_link?.link;
|
||||
if (user) {
|
||||
let image_link = user.image;
|
||||
if (typeof image_link == 'string' && !image_link.startsWith('&')) {
|
||||
try {
|
||||
image_link = JSON.parse(image_link)?.link;
|
||||
} catch {}
|
||||
}
|
||||
if (image_link !== undefined) {
|
||||
image = html`<img
|
||||
class="w3-circle"
|
||||
class=${'w3-theme-l4 ' + shape}
|
||||
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
|
||||
src="/${image_link}/view"
|
||||
title=${name_string + ' (' + this.id + ')'}
|
||||
/>`;
|
||||
}
|
||||
}
|
||||
return html` <div style="display: inline-block; font-weight: bold">
|
||||
return html` <div
|
||||
style="display: inline-block; vertical-align: middle; font-weight: bold; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis"
|
||||
>
|
||||
${image} ${name}
|
||||
</div>`;
|
||||
}
|
||||
|
@ -2,6 +2,12 @@ import * as hashtagify from './commonmark-hashtag.js';
|
||||
|
||||
const k_code_classes = 'w3-theme-l4 w3-theme-border w3-round';
|
||||
|
||||
var reUnsafeProtocol = /^javascript:|vbscript:|file:|data:/i;
|
||||
var reSafeDataProtocol = /^data:image\/(?:png|gif|jpeg|webp)/i;
|
||||
var potentiallyUnsafe = function (url) {
|
||||
return reUnsafeProtocol.test(url) && !reSafeDataProtocol.test(url);
|
||||
};
|
||||
|
||||
function image(node, entering) {
|
||||
if (
|
||||
node.firstChild?.type === 'text' &&
|
||||
@ -44,9 +50,9 @@ function image(node, entering) {
|
||||
'</div>'
|
||||
);
|
||||
if (this.options.safe && potentiallyUnsafe(node.destination)) {
|
||||
this.lit('<img src="" alt="');
|
||||
this.lit('<img src="" title="');
|
||||
} else {
|
||||
this.lit('<img src="' + this.esc(node.destination) + '" alt="');
|
||||
this.lit('<img src="' + this.esc(node.destination) + '" title="');
|
||||
}
|
||||
}
|
||||
this.disableTags += 1;
|
||||
@ -81,8 +87,8 @@ function attrs(node) {
|
||||
}
|
||||
|
||||
export function markdown(md) {
|
||||
let reader = new commonmark.Parser({safe: true});
|
||||
let writer = new commonmark.HtmlRenderer();
|
||||
let reader = new commonmark.Parser();
|
||||
let writer = new commonmark.HtmlRenderer({safe: true});
|
||||
writer.image = image;
|
||||
writer.code = code;
|
||||
writer.attrs = attrs;
|
||||
|
5
apps/storage.json
Normal file
5
apps/storage.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💾",
|
||||
"previous": "&tzZFIe7Y54O4sx1QtAPdemkXh+p5qHXSG/dlS7NP6OQ=.sha256"
|
||||
}
|
126
apps/storage/app.js
Normal file
126
apps/storage/app.js
Normal file
@ -0,0 +1,126 @@
|
||||
async function query(sql, args) {
|
||||
let rows = [];
|
||||
await ssb.sqlAsync(sql, args ?? [], function (row) {
|
||||
rows.push(row);
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
async function get_biggest() {
|
||||
return query(`
|
||||
select author, size from messages_stats group by author order by size desc limit 10;
|
||||
`);
|
||||
}
|
||||
|
||||
async function get_total() {
|
||||
return (
|
||||
await query(`
|
||||
select sum(length(content)) as size, count(distinct author) as count from messages;
|
||||
`)
|
||||
)[0];
|
||||
}
|
||||
|
||||
async function get_names(identities) {
|
||||
return query(
|
||||
`
|
||||
SELECT author, name FROM (
|
||||
SELECT
|
||||
messages.author,
|
||||
RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
|
||||
messages.content ->> 'name' AS name
|
||||
FROM messages
|
||||
JOIN json_each(?) AS identities ON identities.value = messages.author
|
||||
WHERE
|
||||
json_extract(messages.content, '$.type') = 'about' AND
|
||||
content ->> 'about' = messages.author AND name IS NOT NULL)
|
||||
WHERE author_rank = 1
|
||||
`,
|
||||
[JSON.stringify(identities)]
|
||||
);
|
||||
}
|
||||
|
||||
async function get_most_follows() {
|
||||
return query(`
|
||||
select author, count(*) as count
|
||||
from messages
|
||||
where content ->> 'type' = 'contact' and content ->> 'following' = true
|
||||
group by author
|
||||
order by count desc
|
||||
limit 10;
|
||||
`);
|
||||
}
|
||||
|
||||
function nice_size(bytes) {
|
||||
let value = bytes;
|
||||
let index = 0;
|
||||
let units = ['B', 'kB', 'MB', 'GB'];
|
||||
while (value > 1024 && index < units.length - 1) {
|
||||
value /= 1024;
|
||||
index++;
|
||||
}
|
||||
return `${Math.round(value * 10) / 10} ${units[index]}`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await app.setDocument('<p style="color: #fff">Analyzing feeds...</p>');
|
||||
let most_follows = get_most_follows();
|
||||
let total = await get_total();
|
||||
let identities = await ssb.getAllIdentities();
|
||||
let following1 = await ssb.following(identities, 1);
|
||||
let following2 = await ssb.following(identities, 2);
|
||||
let biggest = await get_biggest();
|
||||
most_follows = await most_follows;
|
||||
let names = await get_names(
|
||||
[].concat(
|
||||
biggest.map((x) => x.author),
|
||||
most_follows.map((x) => x.author)
|
||||
)
|
||||
);
|
||||
names = Object.fromEntries(names.map((x) => [x.author, x.name]));
|
||||
for (let item of biggest) {
|
||||
item.name = names[item.author];
|
||||
item.following =
|
||||
identities.indexOf(item.author) != -1
|
||||
? 0
|
||||
: following1[item.author] !== undefined
|
||||
? 1
|
||||
: following2[item.author] !== undefined
|
||||
? 2
|
||||
: undefined;
|
||||
}
|
||||
for (let item of most_follows) {
|
||||
item.name = names[item.author];
|
||||
}
|
||||
let html = `<body style="color: #000; background-color: #ddd">\n
|
||||
<h1>Storage Summary</h1>
|
||||
<h2>Top Accounts by Size</h2>
|
||||
<ol>`;
|
||||
for (let item of biggest) {
|
||||
html += `<li>
|
||||
<span style="color: #888">${nice_size(item.size)}</span>
|
||||
<a target="_top" href="/~core/ssb/#${encodeURI(item.author)}">${item.name ?? item.author}</a>
|
||||
</li>
|
||||
\n`;
|
||||
}
|
||||
html += `
|
||||
</ol>
|
||||
<h2>Top Accounts by Follows</h2>
|
||||
<ol>`;
|
||||
for (let item of most_follows) {
|
||||
html += `<li>
|
||||
<span style="color: #888">${item.count}</span>
|
||||
${following2[item.author] ? '✅' : '🚫'}
|
||||
<a target="_top" href="/~core/ssb/#${encodeURI(item.author)}">${item.name ?? item.author}</a>
|
||||
</li>
|
||||
\n`;
|
||||
}
|
||||
html += `
|
||||
</ol>
|
||||
<p>Total <span style="color: #888">${nice_size(total.size)}</span> in ${total.count} accounts.</p>
|
||||
`;
|
||||
await app.setDocument(html);
|
||||
}
|
||||
|
||||
main().catch(function (e) {
|
||||
print(e);
|
||||
});
|
4
apps/test.json
Normal file
4
apps/test.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📦"
|
||||
}
|
3
apps/test/app.js
Normal file
3
apps/test/app.js
Normal file
@ -0,0 +1,3 @@
|
||||
app.setDocument(
|
||||
'<p style="color: #fff">Maybe one day this app will run tests, but for now there is nothing to see here.</p>'
|
||||
);
|
1
apps/test/hello.txt
Normal file
1
apps/test/hello.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello, world!
|
5
apps/web.json
Normal file
5
apps/web.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🕸",
|
||||
"previous": "&n7hu5b8/TsfiG6FDlCRG5nPCrIdCr96+xpIJ/aQT/uM=.sha256"
|
||||
}
|
100
apps/web/app.js
Normal file
100
apps/web/app.js
Normal file
@ -0,0 +1,100 @@
|
||||
let g_hash;
|
||||
|
||||
async function query(sql, params) {
|
||||
let results = [];
|
||||
await ssb.sqlAsync(sql, params, function (row) {
|
||||
results.push(row);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
async function resolve(id) {
|
||||
try {
|
||||
let blob = await ssb.blobGet(id);
|
||||
if (blob) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(utf8Decode(blob));
|
||||
} catch {
|
||||
return {id: utf8Decode(blob)};
|
||||
}
|
||||
if (json?.links) {
|
||||
for (let [key, value] of Object.entries(json.links)) {
|
||||
json.links[key] = await resolve(value);
|
||||
}
|
||||
return json;
|
||||
} else {
|
||||
return 'huh?' + json;
|
||||
}
|
||||
} else {
|
||||
return `missing<${id}>`;
|
||||
}
|
||||
} catch (e) {
|
||||
return id + ': ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function get_names(identities) {
|
||||
return Object.fromEntries(
|
||||
(
|
||||
await query(
|
||||
`
|
||||
SELECT author, name FROM (
|
||||
SELECT
|
||||
messages.author,
|
||||
RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
|
||||
messages.content ->> 'name' AS name
|
||||
FROM messages
|
||||
JOIN json_each(?) AS identities ON identities.value = messages.author
|
||||
WHERE
|
||||
json_extract(messages.content, '$.type') = 'about' AND
|
||||
content ->> 'about' = messages.author AND name IS NOT NULL)
|
||||
WHERE author_rank = 1
|
||||
`,
|
||||
[JSON.stringify(identities)]
|
||||
)
|
||||
).map((x) => [x.author, x.name])
|
||||
);
|
||||
}
|
||||
|
||||
async function render(hash) {
|
||||
g_hash = hash;
|
||||
if (!hash) {
|
||||
let sites = await query(
|
||||
`
|
||||
SELECT site.author, site.id
|
||||
FROM messages site
|
||||
WHERE site.content ->> 'type' = 'web-init'
|
||||
`,
|
||||
[]
|
||||
);
|
||||
let names = await get_names(sites.map((x) => x.author));
|
||||
if (hash === g_hash) {
|
||||
await app.setDocument(
|
||||
`<ul style="background-color: #ddd">${sites.map((x) => `<li><a target="_top" href="#${encodeURIComponent(x.id)}">${names[x.author] ?? x.author} - ${x.id}</a></li>`).join('\n')}</ul>`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let site_id =
|
||||
hash.charAt(0) == '#'
|
||||
? decodeURIComponent(hash.substring(1))
|
||||
: decodeURIComponent(hash);
|
||||
await app.setDocument(`<html style="margin: 0; padding: 0; width: 100vw; height: 100vh; margin: 0; padding: 0">
|
||||
<body style="display: flex; flex-direction: column; width: 100vw; height: 100vh">
|
||||
<iframe src="${encodeURIComponent(site_id)}/index.html" style="flex: 1 1; border: 0; background-color: #fff"></iframe>
|
||||
</body>
|
||||
</html>`);
|
||||
}
|
||||
}
|
||||
|
||||
core.register('message', async function message_handler(message) {
|
||||
if (message.event == 'hashChange') {
|
||||
await render(message.hash);
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
render(null);
|
||||
}
|
||||
|
||||
main();
|
63
apps/web/handler.js
Normal file
63
apps/web/handler.js
Normal file
@ -0,0 +1,63 @@
|
||||
async function query(sql, params) {
|
||||
let results = [];
|
||||
await ssb.sqlAsync(sql, params, function (row) {
|
||||
results.push(row);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function guess_content_type(name) {
|
||||
if (name.endsWith('.html')) {
|
||||
return 'text/html; charset=UTF-8';
|
||||
} else if (name.endsWith('.js') || name.endsWith('.mjs')) {
|
||||
return 'text/javascript; charset=UTF-8';
|
||||
} else if (name.endsWith('.css')) {
|
||||
return 'text/stylesheet; charset=UTF-8';
|
||||
} else {
|
||||
return 'application/binary';
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let path = request.path.replaceAll(/(%[0-9a-fA-F]{2})/g, (x) =>
|
||||
String.fromCharCode(parseInt(x.substring(1), 16))
|
||||
);
|
||||
let match = path.match(/^(%.{44}\.sha256)(?:\/)?(.*)$/);
|
||||
|
||||
let content_type = guess_content_type(request.path);
|
||||
let root = await query(
|
||||
`
|
||||
SELECT root.content ->> 'root' AS root
|
||||
FROM messages site
|
||||
JOIN messages root
|
||||
ON site.id = ? AND root.author = site.author AND root.content ->> 'site' = site.id
|
||||
ORDER BY root.sequence DESC LIMIT 1
|
||||
`,
|
||||
[match[1]]
|
||||
);
|
||||
let root_id = root[0]['root'];
|
||||
let last_id = root_id;
|
||||
let blob = await ssb.blobGet(root_id);
|
||||
try {
|
||||
for (let part of match[2]?.split('/')) {
|
||||
let dir = JSON.parse(utf8Decode(blob));
|
||||
last_id = dir?.links[part];
|
||||
blob = await ssb.blobGet(dir?.links[part]);
|
||||
content_type = guess_content_type(part);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
respond({
|
||||
status_code: 200,
|
||||
data: blob ? utf8Decode(blob) : `${last_id} not found`,
|
||||
content_type: content_type,
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(function (e) {
|
||||
respond({
|
||||
status_code: 200,
|
||||
data: `${e.message}\n${e.stack}`,
|
||||
content_type: 'text/plain',
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👋",
|
||||
"previous": "&7gFmLW5zSMhmxWWY1+jeRcHdullgujSqGJg94lVgr1k=.sha256"
|
||||
"previous": "&3puDxDNnf6C+YXpFysYLgxFMAy54/AO9V7Xpja6qO/k=.sha256"
|
||||
}
|
||||
|
1521
apps/welcome/hermietildefriends.svg
Normal file
1521
apps/welcome/hermietildefriends.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 86 KiB |
@ -10,17 +10,6 @@
|
||||
<link rel="stylesheet" href="brands.min.css" />
|
||||
|
||||
<style>
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
img {
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
@ -39,52 +28,37 @@
|
||||
<b>😎 Tilde Friends</b>
|
||||
</h1>
|
||||
<h1 class="w3-xxlarge w3-text-green">
|
||||
<b>Make apps and friends from the comfort of your web browser.</b>
|
||||
<b>a Secure Scuttlebutt decentralized social network client</b>
|
||||
</h1>
|
||||
<p>
|
||||
Tilde Friends is a platform for building, running, and sharing web
|
||||
applications.
|
||||
</p>
|
||||
<p>
|
||||
Available for lots of devices:
|
||||
<i class="fa-brands fa-linux w3-xlarge"></i>
|
||||
<i class="fa-brands fa-android w3-xlarge"></i>
|
||||
<i class="fa-brands fa-apple w3-xlarge"></i>
|
||||
<i class="fa fa-mobile-screen w3-xlarge"></i>
|
||||
<i class="fa-brands fa-windows w3-xlarge"></i>
|
||||
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
||||
a platform for building, running, and sharing applications.
|
||||
</p>
|
||||
<a
|
||||
class="w3-button w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
>🦀 Try It</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://www.tildefriends.net/~cory/apps/"
|
||||
><i class="fa fa-link"></i> Try It</a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends"
|
||||
><i class="fa fa-mug-hot"></i> Development</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/"
|
||||
><i class="fa fa-mug-hot"></i> Development</a
|
||||
href="https://docs.tildefriends.net/"
|
||||
><i class="fa fa-book"></i> Documentation</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://www.tildefriends.net/~cory/tildeblog/"
|
||||
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
||||
>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0"
|
||||
/> Get it on F-Droid</a>
|
||||
<a class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage">
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0">
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
<a class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends">
|
||||
<img src="googleplay.svg" style="height: 2em; margin: 0">
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-col l4 m6">
|
||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||
@ -102,16 +76,118 @@
|
||||
<h2>First-time user checklist:</h2>
|
||||
<ol type="1" style="text-align: left">
|
||||
<li>
|
||||
<a href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
<a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||
>Download</a
|
||||
>
|
||||
Tilde Friends or use
|
||||
<a href="https://www.tildefriends.net/"
|
||||
>https://www.tildefriends.net/</a
|
||||
>.
|
||||
</li>
|
||||
<li>
|
||||
Create an account to identify yourself with that instance.
|
||||
<div class="w3-cell-row">
|
||||
<div class="w3-container w3-cell w3-mobile">
|
||||
<h3>Mobile</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0" />
|
||||
Get it on F-Droid</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||
>
|
||||
<img
|
||||
src="googleplay.svg"
|
||||
style="height: 2em; margin: 0"
|
||||
/>
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||
>
|
||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||
Get it on iOS (TestFlight)
|
||||
</a>
|
||||
</p>
|
||||
<p>Just launch the app.</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell w3-mobile">
|
||||
<h3>Web</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
>🦀 Try It</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/login?return=/~core/intro"
|
||||
>Register an account with tildefriends.net</a
|
||||
>
|
||||
to take it for a spin right away.
|
||||
</p>
|
||||
<h3>PeachCloud</h3>
|
||||
<p>
|
||||
Tilde Friends is also a part of 🍑☁️<a
|
||||
href="https://peach-docs.commoninternet.net/"
|
||||
>PeachCloud</a
|
||||
>, which is available on
|
||||
<a href="https://apps.yunohost.org/app/peachpub"
|
||||
>YunoHost</a
|
||||
>
|
||||
for accessible self-hosting.
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell w3-mobile">
|
||||
<h3>Desktop</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||
>
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends is distributed as a single executable file (or
|
||||
source that you can
|
||||
<a href="http://dev.tildefriends.net">build yourself</a>)
|
||||
and stores all of its data in a single
|
||||
file(<code>db.sqlite</code>). You can generally download the
|
||||
latest executable from
|
||||
<a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
>releases</a
|
||||
>
|
||||
for your platform, mark it as executable (<code
|
||||
>chmod +x tildefriends*</code
|
||||
>
|
||||
on macOS and Linux), and run. Run with <code>-h</code> to
|
||||
learn more.
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends will run in the console and provide a web
|
||||
interface at
|
||||
<a href="http://localhost:12345/">http://localhost:12345/</a
|
||||
>. You will have to register a username and password to sign
|
||||
into your instance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
After a <a href="/~core/intro">brief introduction</a>, Tilde
|
||||
Friends will take you to the Secure Scuttlebutt social network
|
||||
app.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||
@ -119,13 +195,19 @@
|
||||
</li>
|
||||
<li>
|
||||
Connect to others.
|
||||
<ul>
|
||||
<li>Automatically discover peers on the same network.</li>
|
||||
<li>Manually connect to rooms and pubs, including <a href="https://www.tildefriends.net/~cory/room/"
|
||||
<ul>
|
||||
<li>Automatically discover peers on the same network.</li>
|
||||
<li>
|
||||
Manually connect to rooms and pubs, including
|
||||
<a href="https://www.tildefriends.net/~cory/room/"
|
||||
>tildefriends.net itself</a
|
||||
>.</li>
|
||||
<li>Enable <b>Peer Exchange</b> in the <b>admin</b> to discover internet peers.</li>
|
||||
</ul>
|
||||
>.
|
||||
</li>
|
||||
<li>
|
||||
Enable <b>Peer Exchange</b> in the <b>admin</b> to discover
|
||||
internet peers.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Follow people to grow your network.</li>
|
||||
<li>
|
||||
@ -140,11 +222,11 @@
|
||||
<!-- SSB Section -->
|
||||
<div class="w3-light-grey">
|
||||
<div class="w3-row-padding w3-padding-64">
|
||||
<div class="w3-col l4 m6 s4">
|
||||
<div class="w3-col l4 m6 s4 w3-center">
|
||||
<a href="https://scuttlebutt.nz/"
|
||||
><img
|
||||
class="w3-image w3-round-large"
|
||||
src="ssb.png"
|
||||
class="w3-image"
|
||||
src="hermietildefriends.svg"
|
||||
alt="Secure Scuttlebutt"
|
||||
/></a>
|
||||
</div>
|
||||
@ -214,15 +296,15 @@
|
||||
|
||||
<!-- Technlology Section -->
|
||||
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
|
||||
<h1 class="w3-jumbo"><b>Boring Technology</b></h1>
|
||||
<h1 class="w3-jumbo"><b>Built the Old Fashioned Way</b></h1>
|
||||
<p>
|
||||
Tilde Friends is built using boring, trusted tech. Unless a better reason
|
||||
presents itself, it strives to use only simple and
|
||||
widely adopted dependencies in order to keep it easy to build for
|
||||
all sorts of platforms and maintainable for a very long time.</p>
|
||||
Tilde Friends strives to use only simple and widely adopted dependencies
|
||||
in order to keep it easy to build for all sorts of platforms and
|
||||
maintainable for a very long time.
|
||||
</p>
|
||||
<p>
|
||||
Though of course for building Tilde Friends apps, you are free to use
|
||||
whatever fits.
|
||||
whatever fits on top.
|
||||
</p>
|
||||
|
||||
<div class="w3-row" style="margin-top: 64px">
|
||||
|
3
apps/welcome/ios.svg
Normal file
3
apps/welcome/ios.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="814" height="1000">
|
||||
<path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📝",
|
||||
"previous": "&DaYqKHRBKhjFGaOzbKZ1+/pLspJeEkDJYTF2B50tH6k=.sha256"
|
||||
"previous": "&4UHlsfQJvSh7L3D86uFtr7KUKCMRVBBTFxRIMqIc5as=.sha256"
|
||||
}
|
||||
|
2
apps/wiki/commonmark.min.js
vendored
2
apps/wiki/commonmark.min.js
vendored
File diff suppressed because one or more lines are too long
@ -2,8 +2,8 @@ import * as utils from './utils.js';
|
||||
import * as commonmark from './commonmark.min.js';
|
||||
|
||||
function markdown(md) {
|
||||
let reader = new commonmark.Parser({safe: true});
|
||||
let writer = new commonmark.HtmlRenderer();
|
||||
let reader = new commonmark.Parser();
|
||||
let writer = new commonmark.HtmlRenderer({safe: true});
|
||||
let parsed = reader.parse(md || '');
|
||||
let walker = parsed.walker();
|
||||
let event;
|
||||
|
42
apps/wiki/lit-all.min.js
vendored
42
apps/wiki/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -20,8 +20,8 @@ class TfWikiDocElement extends LitElement {
|
||||
}
|
||||
|
||||
markdown(md) {
|
||||
let reader = new commonmark.Parser({safe: true});
|
||||
let writer = new commonmark.HtmlRenderer();
|
||||
let reader = new commonmark.Parser();
|
||||
let writer = new commonmark.HtmlRenderer({safe: true});
|
||||
let parsed = reader.parse(md || '');
|
||||
let walker = parsed.walker();
|
||||
let event;
|
||||
|
121
core/app.js
121
core/app.js
@ -1,53 +1,26 @@
|
||||
import * as core from './core.js';
|
||||
|
||||
let g_next_id = 1;
|
||||
let g_calls = {};
|
||||
|
||||
let gSessionIndex = 0;
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
*/
|
||||
function makeSessionId() {
|
||||
return (gSessionIndex++).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
*/
|
||||
function App() {
|
||||
this._on_output = null;
|
||||
this._send_queue = [];
|
||||
this.calls = {};
|
||||
this._next_call_id = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} callback
|
||||
*/
|
||||
App.prototype.readOutput = function (callback) {
|
||||
this._on_output = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} api
|
||||
* @returns
|
||||
*/
|
||||
App.prototype.makeFunction = function (api) {
|
||||
let self = this;
|
||||
let result = function () {
|
||||
let id = g_next_id++;
|
||||
while (!id || g_calls[id]) {
|
||||
id = g_next_id++;
|
||||
let id = self._next_call_id++;
|
||||
while (!id || self.calls[id]) {
|
||||
id = self._next_call_id++;
|
||||
}
|
||||
let promise = new Promise(function (resolve, reject) {
|
||||
g_calls[id] = {resolve: resolve, reject: reject};
|
||||
self.calls[id] = {resolve: resolve, reject: reject};
|
||||
});
|
||||
let message = {
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
method: api[0],
|
||||
params: [...arguments],
|
||||
id: id,
|
||||
@ -59,10 +32,6 @@ App.prototype.makeFunction = function (api) {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} message
|
||||
*/
|
||||
App.prototype.send = function (message) {
|
||||
if (this._send_queue) {
|
||||
if (this._on_output) {
|
||||
@ -77,13 +46,7 @@ App.prototype.send = function (message) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} request
|
||||
* @param {*} response
|
||||
* @param {*} client
|
||||
*/
|
||||
async function socket(request, response, client) {
|
||||
exports.app_socket = async function socket(request, response) {
|
||||
let process;
|
||||
let options = {};
|
||||
let credentials = await httpd.auth_query(request.headers);
|
||||
@ -103,10 +66,16 @@ async function socket(request, response, client) {
|
||||
try {
|
||||
message = JSON.parse(event.data);
|
||||
} catch (error) {
|
||||
print('ERROR', error, event.data, event.data.length, event.opCode);
|
||||
print(
|
||||
'WebSocket error:',
|
||||
error,
|
||||
event.data,
|
||||
event.data.length,
|
||||
event.opCode
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (message.action == 'hello') {
|
||||
if (!process && message.action == 'hello') {
|
||||
let packageOwner;
|
||||
let packageName;
|
||||
let blobId;
|
||||
@ -123,7 +92,7 @@ async function socket(request, response, client) {
|
||||
if (!blobId) {
|
||||
response.send(
|
||||
JSON.stringify({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
method: 'error',
|
||||
params: [message.path + ' not found'],
|
||||
id: -1,
|
||||
@ -164,7 +133,7 @@ async function socket(request, response, client) {
|
||||
options.packageOwner = packageOwner;
|
||||
options.packageName = packageName;
|
||||
options.url = message.url;
|
||||
let sessionId = makeSessionId();
|
||||
let sessionId = 'session_' + (gSessionIndex++).toString();
|
||||
if (blobId) {
|
||||
if (message.edit_only) {
|
||||
response.send(
|
||||
@ -172,17 +141,28 @@ async function socket(request, response, client) {
|
||||
0x1
|
||||
);
|
||||
} else {
|
||||
process = await core.getSessionProcessBlob(
|
||||
blobId,
|
||||
sessionId,
|
||||
options
|
||||
);
|
||||
process = await core.getProcessBlob(blobId, sessionId, options);
|
||||
}
|
||||
}
|
||||
if (process) {
|
||||
process.app.readOutput(function (message) {
|
||||
process.client_api.tfrpc = function (message) {
|
||||
if (message.id) {
|
||||
let calls = process?.app?.calls;
|
||||
if (calls) {
|
||||
let call = calls[message.id];
|
||||
if (call) {
|
||||
if (message.error !== undefined) {
|
||||
call.reject(message.error);
|
||||
} else {
|
||||
call.resolve(message.result);
|
||||
}
|
||||
delete calls[message.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
process.app._on_output = (message) =>
|
||||
response.send(JSON.stringify(message), 0x1);
|
||||
});
|
||||
process.app.send();
|
||||
}
|
||||
|
||||
@ -211,26 +191,13 @@ async function socket(request, response, client) {
|
||||
if (process && process.timeout > 0) {
|
||||
setTimeout(ping, process.timeout);
|
||||
}
|
||||
} else if (message.action == 'resetPermission') {
|
||||
if (process) {
|
||||
process.resetPermission(message.permission);
|
||||
}
|
||||
} else if (message.action == 'setActiveIdentity') {
|
||||
process.setActiveIdentity(message.identity);
|
||||
} else if (message.action == 'createIdentity') {
|
||||
await process.createIdentity();
|
||||
} else if (message.message == 'tfrpc') {
|
||||
if (message.id && g_calls[message.id]) {
|
||||
if (message.error !== undefined) {
|
||||
g_calls[message.id].reject(message.error);
|
||||
} else {
|
||||
g_calls[message.id].resolve(message.result);
|
||||
}
|
||||
delete g_calls[message.id];
|
||||
}
|
||||
} else {
|
||||
if (process && process.eventHandlers['message']) {
|
||||
await core.invoke(process.eventHandlers['message'], [message]);
|
||||
if (process) {
|
||||
if (process.client_api[message.action]) {
|
||||
process.client_api[message.action](message);
|
||||
} else if (process.eventHandlers['message']) {
|
||||
await core.invoke(process.eventHandlers['message'], [message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event.opCode == 0x8) {
|
||||
@ -249,6 +216,6 @@ async function socket(request, response, client) {
|
||||
};
|
||||
|
||||
response.upgrade(100, {});
|
||||
}
|
||||
};
|
||||
|
||||
export {socket, App};
|
||||
export {App};
|
||||
|
116
core/client.js
116
core/client.js
@ -8,6 +8,7 @@ let gFiles = {};
|
||||
let gApp = {files: {}, emoji: '📦'};
|
||||
let gEditor;
|
||||
let gOriginalInput;
|
||||
let gUnloading;
|
||||
|
||||
let kErrorColor = '#dc322f';
|
||||
let kDisconnectColor = '#f00';
|
||||
@ -56,7 +57,7 @@ class TfNavigationElement extends LitElement {
|
||||
status: {type: Object},
|
||||
spark_lines: {type: Object},
|
||||
version: {type: Object},
|
||||
show_version: {type: Boolean},
|
||||
show_expanded: {type: Boolean},
|
||||
identity: {type: String},
|
||||
identities: {type: Array},
|
||||
names: {type: Object},
|
||||
@ -105,7 +106,6 @@ class TfNavigationElement extends LitElement {
|
||||
let spark_line = document.createElement('tf-sparkline');
|
||||
spark_line.title = key;
|
||||
spark_line.classList.add('w3-bar-item');
|
||||
spark_line.classList.add('w3-hide-small');
|
||||
spark_line.style.paddingRight = '0';
|
||||
if (options) {
|
||||
if (options.max) {
|
||||
@ -150,7 +150,7 @@ class TfNavigationElement extends LitElement {
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<div class="w3-dropdown-click w3-right" style="max-width: 100%">
|
||||
<button
|
||||
class="w3-button w3-rest w3-cyan"
|
||||
class="w3-button w3-rest"
|
||||
style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 100%"
|
||||
id="identity"
|
||||
@click=${self.toggle_id_dropdown}
|
||||
@ -162,6 +162,10 @@ class TfNavigationElement extends LitElement {
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4"
|
||||
style="max-width: 100%; right: 0"
|
||||
>
|
||||
<div
|
||||
style="position: fixed; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.25); z-index: -100"
|
||||
@click=${self.toggle_id_dropdown}
|
||||
></div>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-border"
|
||||
@click=${() => (window.location.href = '/~core/identity')}
|
||||
@ -169,6 +173,7 @@ class TfNavigationElement extends LitElement {
|
||||
Manage Identities...
|
||||
</button>
|
||||
<button
|
||||
id="edit_profile"
|
||||
class="w3-bar-item w3-button w3-border"
|
||||
@click=${self.edit_profile}
|
||||
>
|
||||
@ -262,6 +267,7 @@ class TfNavigationElement extends LitElement {
|
||||
<button
|
||||
@click=${() => this.reset_permission(key)}
|
||||
class="w3-button w3-red"
|
||||
id=${'permission_reset:' + key}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
@ -271,6 +277,7 @@ class TfNavigationElement extends LitElement {
|
||||
<button
|
||||
@click=${() => (this.show_permissions = false)}
|
||||
class="w3-button w3-blue"
|
||||
id="permissions_close"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
@ -311,13 +318,13 @@ class TfNavigationElement extends LitElement {
|
||||
<span
|
||||
class="w3-bar-item"
|
||||
style="cursor: pointer"
|
||||
@click=${() => (this.show_version = !this.show_version)}
|
||||
@click=${() => (this.show_expanded = !this.show_expanded)}
|
||||
>😎</span
|
||||
>
|
||||
<span
|
||||
class="w3-bar-item"
|
||||
style=${'white-space: nowrap' +
|
||||
(this.show_version ? '' : '; display: none')}
|
||||
(this.show_expanded ? '' : '; display: none')}
|
||||
title=${this.version?.name +
|
||||
' ' +
|
||||
Object.entries(this.version || {})
|
||||
@ -372,9 +379,11 @@ class TfNavigationElement extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: undefined}
|
||||
${Object.keys(this.spark_lines)
|
||||
.sort()
|
||||
.map((x) => this.spark_lines[x])}
|
||||
<span class=${this.show_expanded ? '' : 'w3-hide-small'}>
|
||||
${Object.keys(this.spark_lines)
|
||||
.sort()
|
||||
.map((x) => this.spark_lines[x])}
|
||||
</span>
|
||||
${this.render_identity()}
|
||||
</div>
|
||||
${this.status?.is_error
|
||||
@ -382,8 +391,8 @@ class TfNavigationElement extends LitElement {
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<div class="w3-model w3-animate-top" style="position: absolute; left: 50%; transform: translate(-50%); z-index: 1">
|
||||
<dijv class="w3-modal-content w3-card-4" style="display: block; padding: 1em">
|
||||
<span @click=${self.clear_error} class="w3-button w3-display-topright">×</span>
|
||||
<div style="color: ${this.status.color ?? kErrorColor}"><b>ERROR:</b><p style="white-space: pre">${this.status.message}</p></div>
|
||||
<span id="close_error" @click=${self.clear_error} class="w3-button w3-display-topright">×</span>
|
||||
<div style="color: ${this.status.color ?? kErrorColor}"><b>ERROR:</b><p id="error" style="white-space: pre">${this.status.message}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
@ -403,6 +412,7 @@ class TfFilesElement extends LitElement {
|
||||
current: {type: String},
|
||||
files: {type: Object},
|
||||
dropping: {type: Number},
|
||||
drop_target: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -441,6 +451,9 @@ class TfFilesElement extends LitElement {
|
||||
if (!this.files[file].clean) {
|
||||
classes.push('dirty');
|
||||
}
|
||||
if (this.drop_target == file) {
|
||||
classes.push('drop');
|
||||
}
|
||||
return html`<div
|
||||
class="${classes.join(' ')}"
|
||||
@click=${(x) => this.file_click(file)}
|
||||
@ -457,11 +470,12 @@ class TfFilesElement extends LitElement {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dropping = 0;
|
||||
this.drop_target = undefined;
|
||||
for (let file of event.dataTransfer.files) {
|
||||
let buffer = await file.arrayBuffer();
|
||||
let text = new TextDecoder('latin1').decode(buffer);
|
||||
gFiles[file.name] = {
|
||||
doc: new cm6.EditorState.create({
|
||||
doc: cm6.EditorState.create({
|
||||
doc: text,
|
||||
extensions: cm6.extensions,
|
||||
}),
|
||||
@ -480,6 +494,7 @@ class TfFilesElement extends LitElement {
|
||||
*/
|
||||
drag_enter(event) {
|
||||
this.dropping++;
|
||||
this.drop_target = event.srcElement.innerText.trim();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@ -489,6 +504,13 @@ class TfFilesElement extends LitElement {
|
||||
*/
|
||||
drag_leave(event) {
|
||||
this.dropping--;
|
||||
if (this.dropping == 0) {
|
||||
this.drop_target = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
drag_over(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -515,6 +537,10 @@ class TfFilesElement extends LitElement {
|
||||
background-color: #2aa198;
|
||||
}
|
||||
|
||||
div.file.drop {
|
||||
border: 4px solid red;
|
||||
}
|
||||
|
||||
div.file.dirty::after {
|
||||
content: '*';
|
||||
}
|
||||
@ -523,20 +549,12 @@ class TfFilesElement extends LitElement {
|
||||
@drop=${this.drop}
|
||||
@dragenter=${this.drag_enter}
|
||||
@dragleave=${this.drag_leave}
|
||||
@dragover=${this.drag_over}
|
||||
>
|
||||
${Object.keys(this.files)
|
||||
.sort()
|
||||
.map((x) => self.render_file(x))}
|
||||
</div>
|
||||
<div
|
||||
?hidden=${this.dropping == 0}
|
||||
@drop=${this.drop}
|
||||
@dragenter=${this.drag_enter}
|
||||
@dragleave=${this.drag_leave}
|
||||
style="text-align: center; vertical-align: middle; outline: 16px solid red; margin: -8px; background-color: rgba(255, 0, 0, 0.5); position: absolute; left: 16px; top: 16px; width: calc(100% - 16px); height: calc(100% - 16px); z-index: 1000"
|
||||
>
|
||||
Drop File(s)
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -1174,7 +1192,7 @@ function api_requestPermission(permission, id) {
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.appendChild(
|
||||
document.createTextNode('This app is requesting the following permission:')
|
||||
document.createTextNode('This app is requesting the following permission: ')
|
||||
);
|
||||
let span = document.createElement('span');
|
||||
span.style = 'font-weight: bold';
|
||||
@ -1190,6 +1208,7 @@ function api_requestPermission(permission, id) {
|
||||
check.classList.add('w3-check');
|
||||
check.classList.add('w3-blue');
|
||||
div.appendChild(check);
|
||||
div.appendChild(document.createTextNode(' '));
|
||||
let label = document.createElement('label');
|
||||
label.htmlFor = check.id;
|
||||
label.appendChild(document.createTextNode('Remember this decision.'));
|
||||
@ -1318,7 +1337,7 @@ function _receive_websocket_message(message) {
|
||||
line.append(key, message.stats[key]);
|
||||
}
|
||||
}
|
||||
} else if (message && message.message === 'tfrpc' && message.method) {
|
||||
} else if (message && message.action === 'tfrpc' && message.method) {
|
||||
let api = k_api[message.method];
|
||||
let id = message.id;
|
||||
let params = message.params;
|
||||
@ -1326,14 +1345,14 @@ function _receive_websocket_message(message) {
|
||||
Promise.resolve(api.func(...params))
|
||||
.then(function (result) {
|
||||
send({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
id: id,
|
||||
result: result,
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
send({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
id: id,
|
||||
error: error,
|
||||
});
|
||||
@ -1542,27 +1561,31 @@ function connectSocket(path) {
|
||||
_receive_websocket_message(JSON.parse(event.data));
|
||||
};
|
||||
gSocket.onclose = function (event) {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
if (gUnloading) {
|
||||
setStatusMessage('⚪ Closing...', kStatusColor);
|
||||
} else {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1836,6 +1859,9 @@ window.addEventListener('load', function () {
|
||||
window.addEventListener('blur', blur);
|
||||
window.addEventListener('message', message, false);
|
||||
window.addEventListener('online', connectSocket);
|
||||
window.addEventListener('beforeunload', function () {
|
||||
gUnloading = true;
|
||||
});
|
||||
document.getElementById('name').value = window.location.pathname;
|
||||
document
|
||||
.getElementById('closeEditor')
|
||||
|
862
core/core.js
862
core/core.js
File diff suppressed because it is too large
Load Diff
44
core/form.js
44
core/form.js
@ -1,44 +0,0 @@
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} encoded
|
||||
* @returns
|
||||
*/
|
||||
function decode(encoded) {
|
||||
let result = '';
|
||||
for (let i = 0; i < encoded.length; i++) {
|
||||
let c = encoded[i];
|
||||
if (c == '+') {
|
||||
result += ' ';
|
||||
} else if (c == '%') {
|
||||
result += String.fromCharCode(parseInt(encoded.slice(i + 1, i + 3), 16));
|
||||
i += 2;
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} encoded
|
||||
* @param {*} initial
|
||||
* @returns
|
||||
*/
|
||||
function decodeForm(encoded, initial) {
|
||||
let result = initial || {};
|
||||
if (encoded) {
|
||||
encoded = encoded.trim();
|
||||
let items = encoded.split('&');
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let equals = item.indexOf('=');
|
||||
let key = decode(item.slice(0, equals));
|
||||
let value = decode(item.slice(equals + 1));
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export {decodeForm};
|
@ -6,6 +6,26 @@
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<link type="image/svg+xml" rel="icon" href="/static/tildefriends.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="title"
|
||||
content="Tilde Friends - Make friends and apps from your web browser."
|
||||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="Tilde Friends is a Secure Scuttlebutt client and a platform for building, running, and sharing web applications. "
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://metatags.io/" />
|
||||
<meta
|
||||
property="og:title"
|
||||
content="Tilde Friends - Make friends and apps from your web browser."
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Tilde Friends is a Secure Scuttlebutt client and a platform for building, running, and sharing web applications. "
|
||||
/>
|
||||
<meta property="og:image" content="/static/tildefriends.svg" />
|
||||
|
||||
<script>
|
||||
function set_access_key_title(event) {
|
||||
if (!event.srcElement.title) {
|
||||
@ -18,8 +38,8 @@
|
||||
style="
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
@ -7,8 +7,8 @@ html {
|
||||
|
||||
body {
|
||||
font-family: monospace;
|
||||
background-color: #002b36;
|
||||
color: #eee8d5;
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
20
core/w3.css
20
core/w3.css
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
10
default.nix
10
default.nix
@ -1,4 +1,8 @@
|
||||
# How to upgrade to a newer version
|
||||
# - On the june and december release, you'll have to update nixpkgs to the current branch
|
||||
# Change `nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";`
|
||||
# to the latest release (see https://nixos.org/)
|
||||
# - Run `$ nix flake update`
|
||||
# - Comment `src.hash`
|
||||
# - Change `version`
|
||||
# - Run `$ nix build`
|
||||
@ -21,14 +25,14 @@
|
||||
}:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "tildefriends";
|
||||
version = "0.0.23";
|
||||
version = "0.0.32";
|
||||
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "dev.tildefriends.net";
|
||||
owner = "cory";
|
||||
repo = "tildefriends";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-ukZpi+BXRTFGbdvd5ApmctTo8bjtPJMHjqFPgVSyBWU=";
|
||||
hash = "sha256-Dk0NOEQIg2LeENySK0+MgpZEtfsClGq6dZL+eOOpE0U=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
@ -46,7 +50,7 @@ pkgs.stdenv.mkDerivation rec {
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
make -j $NIX_BUILD_CORES release
|
||||
make -j $NIX_BUILD_CORES release USE_SYSTEM_SSL=1
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
|
2
deps/c-ares
vendored
2
deps/c-ares
vendored
Submodule deps/c-ares updated: 0c1c60dc60...d3a507e920
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
466
deps/codemirror_src/package-lock.json
generated
vendored
466
deps/codemirror_src/package-lock.json
generated
vendored
@ -19,28 +19,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz",
|
||||
"integrity": "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==",
|
||||
"license": "MIT",
|
||||
"version": "6.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
|
||||
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.0.tgz",
|
||||
"integrity": "sha512-+cduIZ2KbesDhbykV02K25A5xIVrquSPz4UxxYBemRlAT2aW8dhwUgLDwej7q/RJUHKk4nALYcR1puecDvbdqw==",
|
||||
"license": "MIT",
|
||||
"version": "6.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
|
||||
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
@ -49,10 +41,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-css": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.0.tgz",
|
||||
"integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==",
|
||||
"license": "MIT",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
@ -65,7 +56,6 @@
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
|
||||
"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/lang-css": "^6.0.0",
|
||||
@ -79,10 +69,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
|
||||
"integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
|
||||
"license": "MIT",
|
||||
"version": "6.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
||||
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
@ -94,20 +83,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||
"license": "MIT",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
|
||||
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/json": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz",
|
||||
"integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==",
|
||||
"license": "MIT",
|
||||
"version": "6.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz",
|
||||
"integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
@ -118,21 +105,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.2.tgz",
|
||||
"integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==",
|
||||
"license": "MIT",
|
||||
"version": "6.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
|
||||
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
|
||||
"integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
|
||||
"license": "MIT",
|
||||
"version": "6.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
|
||||
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
@ -140,16 +125,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
|
||||
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
|
||||
"license": "MIT"
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||
"dependencies": {
|
||||
"@marijn/find-cluster-break": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/theme-one-dark": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
||||
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
||||
"license": "MIT",
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@ -158,22 +144,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.1.tgz",
|
||||
"integrity": "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==",
|
||||
"license": "MIT",
|
||||
"version": "6.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.0.tgz",
|
||||
"integrity": "sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
@ -188,7 +173,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@ -198,7 +182,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@ -208,7 +191,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
@ -218,42 +200,37 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.2.tgz",
|
||||
"integrity": "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw==",
|
||||
"license": "MIT"
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
|
||||
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
|
||||
},
|
||||
"node_modules/@lezer/css": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz",
|
||||
"integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==",
|
||||
"license": "MIT",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.2.1.tgz",
|
||||
"integrity": "sha512-2F5tOqzKEKbCUNraIXc0f6HKeyKlmMWJnBB0i4XW6dJgssrZO/YlZ2pY5xgyqDleqqhiNJ3dQhbrV2aClZQMvg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
"@lezer/lr": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
|
||||
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
@ -262,7 +239,6 @@
|
||||
"version": "1.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
|
||||
"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
@ -270,10 +246,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/javascript": {
|
||||
"version": "1.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.19.tgz",
|
||||
"integrity": "sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==",
|
||||
"license": "MIT",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
|
||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
@ -281,10 +256,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/json": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz",
|
||||
"integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==",
|
||||
"license": "MIT",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
|
||||
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
@ -295,16 +269,19 @@
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
|
||||
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@marijn/find-cluster-break": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
||||
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
|
||||
"integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
|
||||
"license": "MIT",
|
||||
"version": "15.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
|
||||
"integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
@ -329,7 +306,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
|
||||
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"serialize-javascript": "^6.0.1",
|
||||
"smob": "^1.0.0",
|
||||
@ -348,14 +324,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
|
||||
"integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==",
|
||||
"license": "MIT",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@ -370,231 +345,260 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz",
|
||||
"integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz",
|
||||
"integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz",
|
||||
"integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
|
||||
"integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz",
|
||||
"integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz",
|
||||
"integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz",
|
||||
"integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz",
|
||||
"integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz",
|
||||
"integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz",
|
||||
"integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz",
|
||||
"integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz",
|
||||
"integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz",
|
||||
"integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz",
|
||||
"integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz",
|
||||
"integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz",
|
||||
"integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"license": "MIT"
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -606,14 +610,12 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"license": "MIT",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@ -628,20 +630,17 @@
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -649,15 +648,13 @@
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -670,7 +667,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -679,7 +675,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@ -688,10 +683,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
|
||||
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
|
||||
"license": "MIT",
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
@ -705,22 +699,19 @@
|
||||
"node_modules/is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"license": "MIT",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
@ -731,35 +722,35 @@
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"license": "MIT",
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.13.0",
|
||||
"is-core-module": "^2.16.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
|
||||
"license": "MIT",
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz",
|
||||
"integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.6"
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
@ -769,22 +760,26 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.24.0",
|
||||
"@rollup/rollup-android-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-x64": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.24.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.24.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.24.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.44.1",
|
||||
"@rollup/rollup-android-arm64": "4.44.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.44.1",
|
||||
"@rollup/rollup-darwin-x64": "4.44.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.44.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.44.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.44.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.44.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.44.1",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.44.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.44.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.44.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.44.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.44.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.44.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@ -806,15 +801,13 @@
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
]
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
@ -823,15 +816,13 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
|
||||
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -841,7 +832,6 @@
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
@ -850,14 +840,12 @@
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -866,14 +854,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.34.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz",
|
||||
"integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==",
|
||||
"version": "5.43.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"acorn": "^8.14.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
@ -887,8 +874,7 @@
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
deps/libbacktrace
vendored
2
deps/libbacktrace
vendored
Submodule deps/libbacktrace updated: 86885d1404...f1104f3270
2
deps/libuv
vendored
2
deps/libuv
vendored
Submodule deps/libuv updated: d2e56a5e8d...5152db2cbf
42
deps/lit/lit-all.min.js
vendored
42
deps/lit/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user