mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-15 07:07:45 +01:00
Compare commits
1058 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db02dbcaa4 | ||
|
|
badcbc7aef | ||
|
|
4b91601b98 | ||
|
|
93e0202b86 | ||
|
|
e75d6abe33 | ||
|
|
24a4e3494e | ||
|
|
124fb35dcd | ||
|
|
1aa2839c51 | ||
|
|
8d3260f394 | ||
|
|
7e194b9148 | ||
|
|
f4abc1c7cb | ||
|
|
6766bc8f59 | ||
|
|
adda44e861 | ||
|
|
6aab017d3b | ||
|
|
68c1dca9d1 | ||
|
|
066b7ea4f4 | ||
|
|
0763c1b9b8 | ||
|
|
323e27a047 | ||
|
|
bd02206df2 | ||
|
|
3411575ecc | ||
|
|
1193e1d7aa | ||
|
|
1b63f33d43 | ||
|
|
9f04cfb673 | ||
|
|
1fd1a901f8 | ||
|
|
06517417b7 | ||
|
|
d924709b0c | ||
|
|
6d274cea56 | ||
|
|
b95fa25898 | ||
|
|
12f4c2c668 | ||
|
|
e47a2c5fab | ||
|
|
fc279f67a1 | ||
|
|
6ed6e3e0e0 | ||
|
|
b5242b9a3f | ||
|
|
a0950ce5b8 | ||
|
|
1dd8034cf9 | ||
|
|
7e73b680e6 | ||
|
|
352ffe9415 | ||
|
|
e461851f6f | ||
|
|
673f2920a1 | ||
|
|
e4ba408534 | ||
|
|
96ae1d467b | ||
|
|
48c239b7dc | ||
|
|
331f1eb9b9 | ||
|
|
1e9f10a99f | ||
|
|
6eb2cbd582 | ||
|
|
d9a7dba2e2 | ||
|
|
d885e6cb0a | ||
|
|
4cafc30f21 | ||
|
|
598a051bf8 | ||
|
|
965b579eb7 | ||
|
|
d3a6125488 | ||
|
|
7f3492a1f0 | ||
|
|
c3970ba756 | ||
|
|
6f0a0a55a4 | ||
|
|
30fe3ee5cb | ||
|
|
1f637c583a | ||
|
|
3050995fed | ||
|
|
8081c86ec6 | ||
|
|
ed300dd320 | ||
|
|
989b5a8058 | ||
|
|
71013fd701 | ||
|
|
76dd4380ce | ||
|
|
a643713139 | ||
|
|
1cb86bc1b7 | ||
|
|
cc9bd2bc70 | ||
|
|
90bf96c7c1 | ||
|
|
4e4c93c34e | ||
|
|
c2ce446692 | ||
|
|
d1a2c9622d | ||
|
|
ecd8b93bd1 | ||
|
|
dd032a9e4d | ||
|
|
f350f3b5f4 | ||
|
|
45cb1ef0fe | ||
|
|
d586ec5fb4 | ||
|
|
ad8a4933da | ||
|
|
499d0b09ba | ||
|
|
ac1181c1b4 | ||
|
|
b9671781d3 | ||
|
|
42fd7cb21e | ||
|
|
fe9f765c1d | ||
|
|
087a1e5ea1 | ||
|
|
6e156497da | ||
|
|
ad3c2ed113 | ||
|
|
166bae6104 | ||
|
|
b3e6a2b34f | ||
|
|
547f1c9139 | ||
|
|
4d7ded61f8 | ||
|
|
c92472dbac | ||
|
|
d347f2db77 | ||
|
|
80c1675331 | ||
|
|
23fba45fcb | ||
|
|
80777f712c | ||
|
|
6ffb0015c6 | ||
|
|
9d9b855c04 | ||
|
|
e7cbcc7b20 | ||
|
|
20a5ab3666 | ||
|
|
311fda887f | ||
|
|
55e33df8d6 | ||
|
|
71c9c8ba8c | ||
|
|
11c2b9bb2c | ||
|
|
5cfd208c23 | ||
|
|
3a0b647ce4 | ||
|
|
8367835061 | ||
|
|
55276b968e | ||
|
|
9e04226097 | ||
|
|
51487d5412 | ||
|
|
0dccba4ab2 | ||
|
|
13183ae837 | ||
|
|
f24fd13c45 | ||
|
|
972dffe959 | ||
|
|
9f7033ab06 | ||
|
|
cd07123157 | ||
|
|
b62901e295 | ||
|
|
96996f73d5 | ||
|
|
4782fd376b | ||
|
|
41dfa15feb | ||
|
|
47d4fffc20 | ||
|
|
994f314cff | ||
|
|
0f8f086e01 | ||
|
|
19280c3bbc | ||
|
|
3cd7c1ef2e | ||
|
|
6a5f61e84b | ||
|
|
a444984aed | ||
|
|
c0e253ef84 | ||
|
|
7910d1b0fc | ||
|
|
d26d20180e | ||
|
|
54d9a78474 | ||
|
|
216c41d8cf | ||
|
|
bf2c392380 | ||
|
|
1835fa3ba4 | ||
|
|
d701381041 | ||
|
|
89c19858df | ||
|
|
01136921df | ||
|
|
17ab201b96 | ||
|
|
5d3182da4c | ||
|
|
293a91012f | ||
|
|
a43f601b6a | ||
|
|
447932697d | ||
|
|
0c75c2289e | ||
|
|
c97c9b599d | ||
|
|
fc972b04f7 | ||
|
|
e8410f70a1 | ||
|
|
70c63fc63c | ||
|
|
c4e1353b51 | ||
|
|
fd6959e868 | ||
|
|
854c19c1a1 | ||
|
|
9370b4879b | ||
|
|
0b2da8e7ae | ||
|
|
2e0982b999 | ||
|
|
2ec3be4374 | ||
|
|
b1e6f52396 | ||
|
|
5ce928068c | ||
|
|
76b1e5b103 | ||
|
|
4a95128416 | ||
|
|
cae6eb2b24 | ||
|
|
cdcfb585cf | ||
|
|
d4d56dfe1b | ||
|
|
f6c491498b | ||
|
|
e01cdd1dad | ||
|
|
9e41d366c1 | ||
|
|
fee46de974 | ||
|
|
fa14b7aaf8 | ||
|
|
bb44bfca49 | ||
|
|
6fcb3948ce | ||
|
|
f716291328 | ||
|
|
9f8eabe22b | ||
|
|
6d9e14784f | ||
|
|
1d5f3260e2 | ||
|
|
883fcdb6b8 | ||
|
|
c9040a6e5f | ||
|
|
151450f55b | ||
|
|
5722880890 | ||
|
|
d361a40903 | ||
|
|
cd9e720835 | ||
|
|
50e2ab3a03 | ||
|
|
dc5dfeb022 | ||
|
|
0b8a18edae | ||
|
|
0bb3f14b4c | ||
|
|
c18d30b89f | ||
|
|
8f06a834c8 | ||
|
|
bd948f9cd6 | ||
|
|
d70254bcdc | ||
|
|
fac5385d5d | ||
|
|
e8bcc695bb | ||
|
|
88aeb0e530 | ||
|
|
92b1582232 | ||
|
|
196990c077 | ||
|
|
c42b8cf538 | ||
|
|
085274c960 | ||
|
|
7ef187abf6 | ||
|
|
12eac34d95 | ||
|
|
c29b3c6509 | ||
|
|
7faa423aba | ||
|
|
5b2162e48d | ||
|
|
ee776143b2 | ||
|
|
7a18fb29e4 | ||
|
|
4485cad9e8 | ||
|
|
33763b6d2f | ||
|
|
641fe86cf7 | ||
|
|
65e7c804d7 | ||
|
|
23ec8bee8f | ||
|
|
8aeeaa2e09 | ||
|
|
57f01d249e | ||
|
|
6c5a914db6 | ||
|
|
155e51aa74 | ||
|
|
012a984456 | ||
|
|
25800202f2 | ||
|
|
a058064f1f | ||
|
|
9f40804532 | ||
|
|
26cc467858 | ||
|
|
be1cf7bf65 | ||
|
|
ea4f104ca7 | ||
|
|
32a28a3170 | ||
|
|
6215914212 | ||
|
|
a2dbd7f849 | ||
|
|
5bb9815f4b | ||
|
|
7cd8ac3b21 | ||
|
|
456d3aaf52 | ||
|
|
113743f7cf | ||
|
|
01a4d6f4ac | ||
|
|
ff5cfe744e | ||
|
|
29c4b51f54 | ||
|
|
2f74bf8db8 | ||
|
|
b30d784d06 | ||
|
|
ac0adaf3d8 | ||
|
|
c246d3a748 | ||
|
|
ae40728c1e | ||
|
|
0430ec883b | ||
|
|
eba6f33536 | ||
|
|
dae2873376 | ||
|
|
cb2f56d9a1 | ||
|
|
7d82aa70a4 | ||
|
|
adea234987 | ||
|
|
37d0b487b8 | ||
|
|
d507f8c99f | ||
|
|
60406ac83f | ||
|
|
8fe6f3a335 | ||
|
|
69e027c302 | ||
|
|
4232bdd298 | ||
|
|
ef1c5da027 | ||
|
|
e250326868 | ||
|
|
125f503cfa | ||
|
|
8f086425fe | ||
|
|
ae2da73873 | ||
|
|
3cd6688ffb | ||
|
|
8538da8879 | ||
|
|
b0e4ece278 | ||
|
|
5e058af03e | ||
|
|
5108bc92f3 | ||
|
|
3c5fdcb18d | ||
|
|
4672de4a47 | ||
|
|
239f6da141 | ||
|
|
ec148d1736 | ||
|
|
392b549646 | ||
|
|
993d4dc65d | ||
|
|
e326405f4f | ||
|
|
a83e094f00 | ||
|
|
e683a2cb2a | ||
|
|
1f231c2722 | ||
|
|
0fc2575ef6 | ||
|
|
44c718e6bc | ||
|
|
495df847ab | ||
|
|
5dbb9852f3 | ||
|
|
82d3304c38 | ||
|
|
5e2218fd64 | ||
|
|
5b12fad173 | ||
|
|
8d6f23eacb | ||
|
|
75a09859bc | ||
|
|
dc47c4ceba | ||
|
|
8ae0290aed | ||
|
|
f01d1e704b | ||
|
|
3f72996e64 | ||
|
|
fe862517fb | ||
|
|
b39335dc4f | ||
|
|
8f3bdb5039 | ||
|
|
b47ec01f9c | ||
|
|
36728d1d1f | ||
|
|
c3f0351445 | ||
|
|
9a5364187c | ||
|
|
a9db14994f | ||
|
|
2383e8468f | ||
|
|
a79d485b6a | ||
|
|
7eb931d689 | ||
|
|
995cd2aa7b | ||
|
|
1ce50e0c1b | ||
|
|
bce3d26a1a | ||
|
|
d9680ad0fa | ||
|
|
e2e026e1ff | ||
|
|
16739d9a37 | ||
|
|
524a4f2275 | ||
|
|
ba66883dc2 | ||
|
|
2caa735a2e | ||
|
|
90637fda6b | ||
|
|
2cef46b46a | ||
|
|
14b3abf76c | ||
|
|
8cd3961f87 | ||
|
|
5eb2e2b5a2 | ||
|
|
5dd897e042 | ||
|
|
9c77ec296d | ||
|
|
696e72323b | ||
|
|
3e8c01f436 | ||
|
|
80fc4dec09 | ||
|
|
671885fc8c | ||
|
|
002b61f0d7 | ||
|
|
d32b462bd9 | ||
|
|
5c8b47288a | ||
|
|
8e662e6feb | ||
|
|
83aaeb94f6 | ||
|
|
8606940dee | ||
|
|
60e469a1c9 | ||
|
|
57d548743f | ||
|
|
dab7ee2492 | ||
|
|
9360fd7e43 | ||
|
|
7b4bc53c0f | ||
|
|
0f7969d10a | ||
|
|
6b373b5985 | ||
|
|
c0068a1561 | ||
|
|
fad5e03f8b | ||
|
|
a22fc68764 | ||
|
|
59a442e8c0 | ||
|
|
35e8155ecf | ||
|
|
8b9a5cb5fb | ||
|
|
29f9825f04 | ||
|
|
ad8a6beacf | ||
|
|
194ac78814 | ||
|
|
0542f1ebea | ||
|
|
8de917ea5e | ||
|
|
0b494bbfbf | ||
|
|
7e74f71c79 | ||
|
|
bd7fd676a5 | ||
|
|
cb1c6a4d8c | ||
|
|
b640f0c357 | ||
|
|
967590c7ff | ||
|
|
321d6ec9d7 | ||
|
|
79b4fd7829 | ||
|
|
52eb4d338f | ||
|
|
4b8b48e92f | ||
|
|
e3f45b58e5 | ||
|
|
a5f1922446 | ||
|
|
22e7b15a63 | ||
|
|
c6bb2a73f8 | ||
|
|
bde961f95d | ||
|
|
c4867b3f68 | ||
|
|
1c36e9a2f0 | ||
|
|
1c82d8a738 | ||
|
|
a2df591102 | ||
|
|
c950ee0fb8 | ||
|
|
0ce5a9fcce | ||
|
|
bd1970a805 | ||
|
|
3aaa4b1050 | ||
|
|
31d2426846 | ||
|
|
25c4e4edaf | ||
|
|
1a5bf53b02 | ||
|
|
e2d1b62044 | ||
|
|
6186104d11 | ||
|
|
f1e97eaea4 | ||
|
|
bb0e58bf8b | ||
|
|
dc802fa055 | ||
|
|
3c8e19367f | ||
|
|
a89887eab8 | ||
|
|
7d3f0ad549 | ||
|
|
5608db1334 | ||
|
|
10443b3c02 | ||
|
|
7d272f3cd6 | ||
|
|
d60a62ab24 | ||
|
|
2b0f73b7c5 | ||
|
|
0915ea8585 | ||
|
|
ed9a0b8ac7 | ||
|
|
7437f39ed3 | ||
|
|
27f80cd97b | ||
|
|
04cf688a9b | ||
|
|
178af308ce | ||
|
|
d40732a910 | ||
|
|
d8a579b7b3 | ||
|
|
bf8ef40708 | ||
|
|
41060d5d43 | ||
|
|
ab6cef064c | ||
|
|
829d10d7b9 | ||
|
|
57e1f641a7 | ||
|
|
7df965e74b | ||
|
|
0e1153fdfd | ||
|
|
9797bc1830 | ||
|
|
9d00eb98d2 | ||
|
|
88f6e882c6 | ||
|
|
230a773909 | ||
|
|
e6da1afa82 | ||
|
|
a70d9e57be | ||
|
|
8afe0c0755 | ||
|
|
e47c2513a8 | ||
|
|
276bcfceeb | ||
|
|
6fd1b35a50 | ||
|
|
bc33244549 | ||
|
|
33c982b288 | ||
|
|
98493a1167 | ||
|
|
a8c10f9601 | ||
|
|
1051f84bbf | ||
|
|
eea137e677 | ||
|
|
27cf4e925f | ||
|
|
6b239c4e3f | ||
|
|
4c7ee5ef80 | ||
|
|
3ddb79899e | ||
|
|
b14595a3e2 | ||
|
|
82c94369ae | ||
|
|
7e77d2e9a8 | ||
|
|
ce5ce76b21 | ||
|
|
27d6b30cf9 | ||
|
|
9285dd5d70 | ||
|
|
977234808e | ||
|
|
e1ccd327f5 | ||
|
|
a0e3e71a4b | ||
|
|
2d4037da37 | ||
|
|
d21608bb82 | ||
|
|
f16239de73 | ||
|
|
d00f34c240 | ||
|
|
fd77437f59 | ||
|
|
f9cd8d24a1 | ||
|
|
c0a9f79834 | ||
|
|
ca63c101a1 | ||
|
|
dbd0b46ef2 | ||
|
|
89793ebe2f | ||
|
|
e1ffc387ea | ||
|
|
1efd0852ef | ||
|
|
59d532ed4d | ||
|
|
f034cda7cd | ||
|
|
5c1cf1e57d | ||
|
|
780960f5de | ||
|
|
aada95e9da | ||
|
|
89b5003cce | ||
|
|
3559333461 | ||
|
|
fbb6cab567 | ||
|
|
30fca29c6d | ||
|
|
ec1e521b39 | ||
|
|
a8998cd696 | ||
|
|
df178baed6 | ||
|
|
e2f75371e6 | ||
|
|
9f25ab39c6 | ||
|
|
937df6a024 | ||
|
|
7deb7182a8 | ||
|
|
82b14c56be | ||
|
|
58b98734c9 | ||
|
|
c2f58f328c | ||
|
|
36eabe7528 | ||
|
|
8347093e8b | ||
|
|
75282cb43a | ||
|
|
3a253a758d | ||
|
|
f2955c26c1 | ||
|
|
e6c0317b37 | ||
|
|
0cfb218b07 | ||
|
|
8ef103fbde | ||
|
|
ec5cdfb025 | ||
|
|
7f4d082c17 | ||
|
|
e10011ef34 | ||
|
|
1eb6999c37 | ||
|
|
35596916bf | ||
|
|
d61b22dd87 | ||
|
|
5565a9db9a | ||
|
|
b803655306 | ||
|
|
20df2bf13a | ||
|
|
e8fab85ce5 | ||
|
|
ce730f3dbe | ||
|
|
2328973de5 | ||
|
|
37dea50c91 | ||
|
|
40dfc8b30a | ||
|
|
72971bb9ec | ||
|
|
f835599502 | ||
|
|
0caa4f8099 | ||
|
|
71d3c9acd1 | ||
|
|
239ffa174a | ||
|
|
eae79ab14b | ||
|
|
ef7912615a | ||
|
|
7f20b67380 | ||
|
|
504337c178 | ||
|
|
d6b4f06ac8 | ||
|
|
28cbf5474d | ||
|
|
d797b84d4e | ||
|
|
720745857d | ||
|
|
a74ea1aeda | ||
|
|
643f5e7f26 | ||
|
|
a208b934e4 | ||
|
|
5892774082 | ||
|
|
13bfc1c3bd | ||
|
|
00289734c7 | ||
|
|
09c7c2cb91 | ||
|
|
bbe62abd20 | ||
|
|
cae9a4fd36 | ||
|
|
e7ee4a8867 | ||
|
|
b020574c88 | ||
|
|
192e1da5b6 | ||
|
|
0640079e59 | ||
|
|
a9d99aa17f | ||
|
|
b4667e1f88 | ||
|
|
7bc26fd448 | ||
|
|
94a9a5d5d5 | ||
|
|
bcfa2c411f | ||
|
|
1e3d43dbae | ||
|
|
c6b5e4d5df | ||
|
|
1293870e11 | ||
|
|
8309c19167 | ||
|
|
63a381c55a | ||
|
|
d891f891b7 | ||
|
|
0b12a6b318 | ||
|
|
0144ab05a0 | ||
|
|
1f71d4372f | ||
|
|
5ed1ea07ef | ||
|
|
4d2b101278 | ||
|
|
1dfdadde98 | ||
|
|
d19729869e | ||
|
|
5f782ad109 | ||
|
|
27dbdc9b5a | ||
|
|
4892863dd7 | ||
|
|
dc077209cc | ||
|
|
d8d5344d21 | ||
|
|
d148a8384d | ||
|
|
6884e9428b | ||
|
|
25ebccfcd7 | ||
|
|
7adb0cbb50 | ||
|
|
50866abda4 | ||
|
|
c83d904cf0 | ||
|
|
160447f8f6 | ||
|
|
184b13cc2a | ||
|
|
8fa965e055 | ||
|
|
1dd0b69079 | ||
|
|
be8dd107e3 | ||
|
|
8f139f10ef | ||
|
|
8b0823984b | ||
|
|
d82d2229d4 | ||
|
|
4be1bc17f3 | ||
|
|
e6af0c6009 | ||
|
|
e5d10bd72b | ||
|
|
9426885bb8 | ||
|
|
89e5d4f235 | ||
|
|
eecb3fbc7f | ||
|
|
244f1319b4 | ||
|
|
6a2b76e760 | ||
|
|
bc0278fce9 | ||
|
|
959841fb22 | ||
|
|
fbc878dbe5 | ||
|
|
7d183c6580 | ||
|
|
0555420ad9 | ||
|
|
ae25561c7e | ||
|
|
92e2b2b8f9 | ||
|
|
2a4a830fb7 | ||
|
|
0ad4c7cd7e | ||
|
|
41413d52ad | ||
|
|
eabc1bd305 | ||
|
|
9c312e12c1 | ||
|
|
18ea9dda3d | ||
|
|
56d4212f68 | ||
|
|
d932c19513 | ||
|
|
b800f00b7e | ||
|
|
e43af5234f | ||
|
|
aa59d96e55 | ||
|
|
5be04ceea6 | ||
|
|
da3392ac53 | ||
|
|
a2c9c98b21 | ||
|
|
8298d6e031 | ||
|
|
d6331022ad | ||
|
|
5e75d8c9a7 | ||
|
|
51944a212a | ||
|
|
49ed756479 | ||
|
|
1d207d5fbd | ||
|
|
6b1d15415d | ||
|
|
df3bfaaab7 | ||
|
|
ad0b8a8e7d | ||
|
|
d5459229b9 | ||
|
|
adbf596f0b | ||
|
|
c740966394 | ||
|
|
7adab73af3 | ||
|
|
c964f255d8 | ||
|
|
a7ffed9716 | ||
|
|
488d66d248 | ||
|
|
93cb469cb8 | ||
|
|
03a2fc8ee5 | ||
|
|
2d72e5792e | ||
|
|
9bf9a87f12 | ||
|
|
e2fca399e0 | ||
|
|
e13fdddf98 | ||
|
|
890eea299d | ||
|
|
9beac00981 | ||
|
|
1a64d7d9e6 | ||
|
|
bd5ae12f2e | ||
|
|
dbaf1da3ce | ||
|
|
a03c5df440 | ||
|
|
0776f6a2be | ||
|
|
ac9d050d9e | ||
|
|
b885c358a3 | ||
|
|
84d447973e | ||
|
|
019ec4de20 | ||
|
|
26189067cd | ||
|
|
caf39071af | ||
|
|
08a1223482 | ||
|
|
24bd90745e | ||
|
|
7a72920e66 | ||
|
|
81a8276e2b | ||
|
|
8fb6f51555 | ||
|
|
b0026aff66 | ||
|
|
3ced7f7c18 | ||
|
|
33f6995193 | ||
|
|
8af526682a | ||
|
|
d0d4324957 | ||
|
|
2bcd0e0bbe | ||
|
|
1b99c7e10f | ||
|
|
2831bb61b8 | ||
|
|
320670de2a | ||
|
|
1f39a36f26 | ||
|
|
1f84ba716e | ||
|
|
d034965d9c | ||
|
|
5eb30489e5 | ||
|
|
05458a0753 | ||
|
|
7975ef0699 | ||
|
|
5f810a1b4c | ||
|
|
c4093e79cf | ||
|
|
956f8ed2ea | ||
|
|
7b32f76a1e | ||
|
|
0dd11bc051 | ||
|
|
aa38df28af | ||
|
|
32962a6336 | ||
|
|
b268b72a4a | ||
|
|
eb0c90311b | ||
|
|
1070bf8e8f | ||
|
|
9bdb01987c | ||
|
|
3130fb948a | ||
|
|
e46de9eebd | ||
|
|
4360355c8c | ||
|
|
3baaddba0b | ||
|
|
e77c1c2a46 | ||
|
|
38422d59fd | ||
|
|
c884cedfc2 | ||
|
|
91074a35d8 | ||
|
|
9a02b63a6b | ||
|
|
4470409a24 | ||
|
|
8a978420be | ||
|
|
dfc01d51ca | ||
|
|
758154fa14 | ||
|
|
ef23a85577 | ||
|
|
e4533088ed | ||
|
|
4ffce4a534 | ||
|
|
6b77a62934 | ||
|
|
f603867040 | ||
|
|
b6eb7418aa | ||
|
|
39c97681cf | ||
|
|
2d0f0d8f6b | ||
|
|
209a767c91 | ||
|
|
71f2a724cb | ||
|
|
4b89b95738 | ||
|
|
b535722acd | ||
|
|
a9806a06a2 | ||
|
|
948e19b82f | ||
|
|
cd841fa13a | ||
|
|
3ffcf91abd | ||
|
|
9fd84a3bfc | ||
|
|
627c451cd1 | ||
|
|
e6ee89464a | ||
|
|
136e9f2738 | ||
|
|
d97986e526 | ||
|
|
7661aa9819 | ||
|
|
b7596f3f78 | ||
|
|
4e2231674c | ||
|
|
d36d023a5c | ||
|
|
3d5a7ebe3d | ||
|
|
819384e952 | ||
|
|
2f34c5e821 | ||
|
|
0c89c154ee | ||
|
|
060dce7440 | ||
|
|
91707529bd | ||
|
|
22f182a8eb | ||
|
|
6ccc42dc3f | ||
|
|
655f5a8eed | ||
|
|
cd06a72d6f | ||
|
|
0a5d71ecc2 | ||
|
|
9c214b619c | ||
|
|
6fddb727be | ||
|
|
b5d3b9a803 | ||
|
|
a74c8a94db | ||
|
|
abc6d0ff1e | ||
|
|
afdbcd0a38 | ||
|
|
c31ae805a6 | ||
|
|
7c1afd7fe5 | ||
|
|
ad470a3fd2 | ||
|
|
7755d06bba | ||
|
|
5aed4b79be | ||
|
|
f0e3f29606 | ||
|
|
abbcc525bf | ||
|
|
ef8ade45b1 | ||
|
|
8919d598c2 | ||
|
|
e21e42f5fe | ||
|
|
658aed8a29 | ||
|
|
a666f7d216 | ||
|
|
62429df469 | ||
|
|
7aed699c3f | ||
|
|
a14db0ab74 | ||
|
|
7b67770dc7 | ||
|
|
7766d8439b | ||
|
|
fac0683a71 | ||
|
|
79e04967f5 | ||
|
|
47eb8947f5 | ||
|
|
959c55315c | ||
|
|
b392f0c00f | ||
|
|
c8e674d015 | ||
|
|
90fbb81e1d | ||
|
|
21e58ca644 | ||
|
|
cfb0aa55ea | ||
|
|
9e0c021481 | ||
|
|
4acca8dd06 | ||
|
|
88eea09428 | ||
|
|
90d2743267 | ||
|
|
89945532a0 | ||
|
|
671860e053 | ||
|
|
a87ca6af47 | ||
|
|
b59ea4991c | ||
|
|
b6fd59219f | ||
|
|
38ce7650c1 | ||
|
|
2b21e28096 | ||
|
|
3206651248 | ||
|
|
ba8aa0e218 | ||
|
|
975a001635 | ||
|
|
30e7f7ccfe | ||
|
|
cca8d97d63 | ||
|
|
455e6c3520 | ||
|
|
d05ee3ec16 | ||
|
|
b1b5d08e89 | ||
|
|
6202e59daa | ||
|
|
c4a8603b81 | ||
|
|
5d15d1565c | ||
|
|
045ff558f8 | ||
|
|
504ed583cc | ||
|
|
15087f2d5a | ||
|
|
eb1cd1c14b | ||
|
|
0918d86654 | ||
|
|
d0e2220df7 | ||
|
|
ccb04f0b45 | ||
|
|
54b6e1c7c3 | ||
|
|
7c8acdc956 | ||
|
|
3973ae15be | ||
|
|
86dadf4f5e | ||
|
|
dc4cb4be74 | ||
|
|
2a29c4b551 | ||
|
|
2f64f45aba | ||
|
|
2f998ddfb6 | ||
|
|
93369a779d | ||
|
|
74e28123a8 | ||
|
|
fef7db8710 | ||
|
|
cb5d7626ac | ||
|
|
e74f9f33c0 | ||
|
|
0d27c17e28 | ||
|
|
f0b6d32639 | ||
|
|
9ebd506056 | ||
|
|
a5a91d2444 | ||
|
|
4435a66ece | ||
|
|
874a1079c3 | ||
|
|
1dc239d243 | ||
|
|
73a04276c0 | ||
|
|
9955064484 | ||
|
|
7975b528ec | ||
|
|
7d9fbda975 | ||
|
|
fce06747e3 | ||
|
|
db0cca54a7 | ||
|
|
4424caecc5 | ||
|
|
b866dcd566 | ||
|
|
9e4e6435ab | ||
|
|
41b10ff442 | ||
|
|
79f3307104 | ||
|
|
026629cd6d | ||
|
|
cf570a8f88 | ||
|
|
4a64384468 | ||
|
|
a9065c3380 | ||
|
|
e5e5548562 | ||
|
|
44cf2f670f | ||
|
|
d04d91d6dd | ||
|
|
3eb25b1507 | ||
|
|
a95cae5610 | ||
|
|
9cc6e62f28 | ||
|
|
24117f7c8d | ||
|
|
7245e6e593 | ||
|
|
4a5b1f4da3 | ||
|
|
c1ac67cc31 | ||
|
|
4085361b7e | ||
|
|
8b4227dbff | ||
|
|
3f7ed9e177 | ||
|
|
f9f1ba9e24 | ||
|
|
29309500a6 | ||
|
|
6b07fe105f | ||
|
|
90b03a0b97 | ||
|
|
d54a468006 | ||
|
|
7e98b6d853 | ||
|
|
8069094201 | ||
|
|
b28dc01e6b | ||
|
|
6bc1f9f494 | ||
|
|
e85dd32005 | ||
|
|
efc4e3bf6c | ||
|
|
768a13ff4d | ||
|
|
a8d73f74f4 | ||
|
|
4dddfc0cc5 | ||
|
|
4163bebe3b | ||
|
|
ad13744d14 | ||
|
|
9f9b3a5b21 | ||
|
|
63e68baeb1 | ||
|
|
4382ca6582 | ||
|
|
99f2f4c211 | ||
|
|
6b23379e5a | ||
|
|
4deda36e8f | ||
|
|
0ce7831cfb | ||
|
|
4e9005e240 | ||
|
|
9147252e5b | ||
|
|
b6b549ca18 | ||
|
|
85c25fb71e | ||
|
|
50637ba9fd | ||
|
|
bfe55dd55a | ||
|
|
1a2bb3d12e | ||
|
|
452a0c3ed5 | ||
|
|
b594a4690f | ||
|
|
7d2ba43463 | ||
|
|
107db42c33 | ||
|
|
dbacb9fc8d | ||
|
|
1fa3499ca6 | ||
|
|
62f3198d27 | ||
|
|
529550934b | ||
|
|
ca3b22a881 | ||
|
|
0ecf2d9123 | ||
|
|
af9bb7138b | ||
|
|
6cbfa74f97 | ||
|
|
2845fb8d35 | ||
|
|
fbe2660a57 | ||
|
|
1519282ac6 | ||
|
|
5d8d8dca70 | ||
|
|
0254e2c31d | ||
|
|
368b494d62 | ||
|
|
00d2213d05 | ||
|
|
b04602bcb9 | ||
|
|
80bfbd503a | ||
|
|
bfa186aebb | ||
|
|
818595e961 | ||
|
|
b5e9852f8d | ||
|
|
ff3734d65a | ||
|
|
4aa771ba29 | ||
|
|
5848c381fa | ||
|
|
03b35beae4 | ||
|
|
49856bb6f7 | ||
|
|
0cb56f1a85 | ||
|
|
4a0d698776 | ||
|
|
a4ebdb5736 | ||
|
|
6a5ecc2880 | ||
|
|
7a5bd92442 | ||
|
|
63c1111608 | ||
|
|
5704fb560a | ||
|
|
33dc3b719d | ||
|
|
d3329b4628 | ||
|
|
42e2c9f96f | ||
|
|
3e8a10757f | ||
|
|
8028d82fd0 | ||
|
|
4ad9feba68 | ||
|
|
ef469be7a9 | ||
|
|
14cd73d75a | ||
|
|
fbe6039845 | ||
|
|
f3fbd070dd | ||
|
|
7c2611a5a7 | ||
|
|
ae949f4616 | ||
|
|
89da434c0e | ||
|
|
4a98d1d655 | ||
|
|
bb41f0e4fe | ||
|
|
d2e0717d91 | ||
|
|
7912204fcb | ||
|
|
83e41ad618 | ||
|
|
dd7e46c2cc | ||
|
|
fb7424714a | ||
|
|
47481b711e | ||
|
|
0f86c3a731 | ||
|
|
b91d1b8b3c | ||
|
|
cd5ae6691c | ||
|
|
0f58986b4c | ||
|
|
af9755c513 | ||
|
|
56ce25f953 | ||
|
|
ed70c15ee9 | ||
|
|
d88ae99425 | ||
|
|
cf7ef55b7d | ||
|
|
2f12424f8d | ||
|
|
96aacec4fc | ||
|
|
7e57bb98a8 | ||
|
|
298f5c5a99 | ||
|
|
bf44ea7864 | ||
|
|
9abdc45e93 | ||
|
|
0d82cc7574 | ||
|
|
402b2a551f | ||
|
|
700c589ae2 | ||
|
|
8929d736d9 | ||
|
|
8c65fce357 | ||
|
|
b81f1e9e6b | ||
|
|
44d083d773 | ||
|
|
2b308282d4 | ||
|
|
c7738743c5 | ||
|
|
abc60667c6 | ||
|
|
53162e8bca | ||
|
|
6b95574e3d | ||
|
|
c9d62ae961 | ||
|
|
a70019927d | ||
|
|
01cd7f951a | ||
|
|
50f92bcfab | ||
|
|
362d9f8e59 | ||
|
|
0b1cf53942 | ||
|
|
a4c769b33c | ||
|
|
74a1cb61c1 | ||
|
|
268545c728 | ||
|
|
184bbb01c5 | ||
|
|
66fa7ac419 | ||
|
|
a80f3d997e | ||
|
|
efa7c24c9f | ||
|
|
91de21c7ad | ||
|
|
1e9ba57ef0 | ||
|
|
464389b248 | ||
|
|
107eb72eda | ||
|
|
ee7bf838f4 | ||
|
|
f63bcd94d1 | ||
|
|
16d5cb6ade | ||
|
|
63decdef8b | ||
|
|
e54e88f46d | ||
|
|
b6f53d059e | ||
|
|
9e1771c5ec | ||
|
|
8306bc2198 | ||
|
|
3810fb7d51 | ||
|
|
5b4984113e | ||
|
|
baf9efe246 | ||
|
|
f4a3109a31 | ||
|
|
241f2f39ec | ||
|
|
d9b40dca83 | ||
|
|
cb3f053057 | ||
|
|
c4ab045e57 | ||
|
|
d9b38efd97 | ||
|
|
5981588c95 | ||
|
|
388bef59b8 | ||
|
|
3d8de61c1c | ||
|
|
91b818f98d | ||
|
|
3a7f0898f9 | ||
|
|
c2f6de875a | ||
|
|
0647001807 | ||
|
|
4181d87792 | ||
|
|
58cf0a2015 | ||
|
|
d80c3d305b | ||
|
|
4d64a2bc2f | ||
|
|
5c54beaaa9 | ||
|
|
eee98f32b2 | ||
|
|
0949de66bf | ||
|
|
5a6acb24d9 | ||
|
|
2e5cab62c7 | ||
|
|
903db5fffd | ||
|
|
e3faf32708 | ||
|
|
97926ddc03 | ||
|
|
e891804051 | ||
|
|
a2982f8b77 | ||
|
|
321941baab | ||
|
|
e2e25dc30b | ||
|
|
1170ca4789 | ||
|
|
d159f62138 | ||
|
|
25005eef1b | ||
|
|
b17824c88d | ||
|
|
00f4a008f8 | ||
|
|
3e2bc67b59 | ||
|
|
efe072c7c4 | ||
|
|
59f1d11e40 | ||
|
|
5f947ac983 | ||
|
|
aa66f957f2 | ||
|
|
cf5f69271f | ||
|
|
c6653af782 | ||
|
|
fa1f33a2ac | ||
|
|
9f479882ad | ||
|
|
41db316489 | ||
|
|
50f0f5885e | ||
|
|
a609f09d50 | ||
|
|
43f2aa3068 | ||
|
|
a094e13bd5 | ||
|
|
eb076afbe4 | ||
|
|
7b41488a38 | ||
|
|
ca12d48352 | ||
|
|
0331d3b2b0 | ||
|
|
4d4814583c | ||
|
|
da3e158516 | ||
|
|
7b9d6baff0 | ||
|
|
550f1b7c6f | ||
|
|
74e8ee1786 | ||
|
|
55d3ea01b6 | ||
|
|
4cfe51cbb2 | ||
|
|
1f5ac60523 | ||
|
|
26bf4f2abd | ||
|
|
54671fff79 | ||
|
|
378309d763 | ||
|
|
e7d4bba8b3 | ||
|
|
fe3de0bc98 | ||
|
|
f3e38d7b71 | ||
|
|
eafa3b2de9 | ||
|
|
d8cc075bd0 | ||
|
|
bdb877a936 | ||
|
|
5c96e83a22 | ||
|
|
43abb57f77 | ||
|
|
9c6d821978 | ||
|
|
7e9babf515 | ||
|
|
ee36baf432 | ||
|
|
0bf57dcab7 | ||
|
|
e647d23adc | ||
|
|
d1b5b9c371 | ||
|
|
92ec514442 | ||
|
|
107466dd58 | ||
|
|
4246260ce6 | ||
|
|
936787d327 | ||
|
|
f976397283 | ||
|
|
657c6ad9a9 | ||
|
|
ede07c3b0e | ||
|
|
9326e8dcce | ||
|
|
8ff2adf833 | ||
|
|
5e387b513a | ||
|
|
dca316c0d5 | ||
|
|
db2d786d50 | ||
|
|
c0d7962142 | ||
|
|
5d14354ca9 | ||
|
|
347dace6de | ||
|
|
d952c75e3c | ||
|
|
abc3745be1 | ||
|
|
1382d7c523 | ||
|
|
3de0edf0f9 | ||
|
|
5513d39152 | ||
|
|
1a9547d1d2 | ||
|
|
26cc64d3a0 | ||
|
|
e465b33365 | ||
|
|
957739ba5e | ||
|
|
59967374c5 | ||
|
|
43bc346a2b | ||
|
|
5fbd3f67cd | ||
|
|
e352343d62 | ||
|
|
baee0f0c6f | ||
|
|
fcc5eead88 | ||
|
|
29d1f687d1 | ||
|
|
f568266c7f | ||
|
|
a1e42b8ddb | ||
|
|
e6389b1153 | ||
|
|
f0835acb33 | ||
|
|
35b04b219f | ||
|
|
01b62e936a | ||
|
|
09d16a03ef | ||
|
|
bb61602fd2 | ||
|
|
f30b78ea1f | ||
|
|
6ee652ca75 | ||
|
|
67d8bbc154 | ||
|
|
b852484559 | ||
|
|
0b077e5e88 | ||
|
|
381d957db2 | ||
|
|
f8f77abc12 | ||
|
|
0b0484c610 | ||
|
|
2b769088af | ||
|
|
856d13c603 | ||
|
|
76e398b8a1 | ||
|
|
24a7d85be0 | ||
|
|
989271f653 | ||
|
|
3fd8cd5713 | ||
|
|
43f1c7caf6 | ||
|
|
bd91ac88a3 | ||
|
|
fbcbf2e5a5 | ||
|
|
31eb03da0d | ||
|
|
a4dbfed712 | ||
|
|
784cb53ec6 | ||
|
|
43c799bd60 |
@@ -19,7 +19,6 @@ module.exports = {
|
||||
'src-tauri/**/*',
|
||||
'plugins/**/*',
|
||||
'tailwind.config.cjs',
|
||||
'vite.config.ts',
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
|
||||
45
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
45
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: Bug Report
|
||||
description: "Something isn't working properly in Yaak"
|
||||
title: "Short description"
|
||||
labels: ["bug", "needs triage"]
|
||||
assignees:
|
||||
- gschier
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report 🤗
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
placeholder: 2024.8.0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: What operating system are you on?
|
||||
multiple: false
|
||||
options:
|
||||
- macOS
|
||||
- Windows
|
||||
- Linux
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request, Question, etc.
|
||||
url: https://feedback.yaak.app
|
||||
about: Report all non-bugs to the feedback board 👉🏼
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -81,9 +81,7 @@ jobs:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install yaak CLI
|
||||
run: |
|
||||
npm install -g @yaakapp/cli
|
||||
yaakcli --version
|
||||
run: go install github.com/yaakapp/yaakcli@latest
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
14
.run/Dev Desktop.run.xml
Normal file
14
.run/Dev Desktop.run.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Dev Desktop" type="js.build_tools.npm">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="start" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs>
|
||||
<env name="RUST_BACKTRACE" value="1" />
|
||||
</envs>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,12 +0,0 @@
|
||||
## Developer Setup
|
||||
|
||||
Development requires the following tools
|
||||
|
||||
- [Node.js](https://nodejs.org/en/download/package-manager)
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
|
||||
Then, you can run the app.
|
||||
|
||||
1. Checkout the [plugins](https://github.com/yaakapp/plugins) repository
|
||||
2. Run `YAAK_PLUGINS_DIR="..." npm run build` to generate an icon, fetch external binaries, and build local JS dependencies
|
||||
3. Run the desktop app in dev mode `npm start`
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Yaak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
13
Makefile
Normal file
13
Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
.PHONY: sqlx-prepare, dev, migrate, build
|
||||
|
||||
sqlx-prepare:
|
||||
cd src-tauri && cargo sqlx prepare --database-url 'sqlite://db.sqlite'
|
||||
|
||||
dev:
|
||||
npm run tauri-dev
|
||||
|
||||
migrate:
|
||||
cd src-tauri && cargo sqlx migrate run --database-url 'sqlite://db.sqlite?mode=rw'
|
||||
|
||||
build:
|
||||
./node_modules/.bin/tauri build
|
||||
25
README.md
25
README.md
@@ -1,16 +1,21 @@
|
||||
# [Yaak API Client](https://yaak.app)
|
||||
# Yaak Network Toolkit
|
||||
|
||||
Yaak is a desktop API client for organizing and executing REST, GraphQL, and gRPC
|
||||
requests. It's built using [Tauri](https://tauri.app), Rust, and ReactJS.
|
||||
The most fun you'll ever have working with APIs.
|
||||
|
||||

|
||||
## Common Commands
|
||||
|
||||
## Feedback and Bug Reports
|
||||
```sh
|
||||
# Start dev app
|
||||
npm run tauri-dev
|
||||
|
||||
Please [Create an Issue](https://github.com/yaakapp/app/issues/new) for bug reports and
|
||||
submit all other feedback to the [Feedback Board](https://feedback.yaak.app).
|
||||
# Migration commands
|
||||
cd src-tauri
|
||||
cargo sqlx migrate add ${MIGRATION_NAME}
|
||||
cargo sqlx migrate run --database-url 'sqlite://db.sqlite?mode=rw'
|
||||
cargo sqlx prepare --database-url 'sqlite://db.sqlite'
|
||||
```
|
||||
|
||||
## Contribution Policy
|
||||
## Add App->Plugin API
|
||||
|
||||
Yaak open source, but currently only accepting contributions for bug fixes. See [
|
||||
`DEVELOPMENT.md`](DEVELOPMENT.md).
|
||||
- Add event in `events.rs`
|
||||
- Add handler to `index.worker.ts`
|
||||
|
||||
BIN
design/Icons.afdesign
Normal file
BIN
design/Icons.afdesign
Normal file
Binary file not shown.
BIN
design/icon-dev.png
Normal file
BIN
design/icon-dev.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
design/icon.png
Normal file
BIN
design/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
BIN
design/logo.afdesign
Normal file
BIN
design/logo.afdesign
Normal file
Binary file not shown.
5107
package-lock.json
generated
5107
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
139
package.json
139
package.json
@@ -3,10 +3,6 @@
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/yaakapp/app.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run tauri-dev:desktop",
|
||||
"tauri-dev:desktop": "tauri dev --no-watch --config ./src-tauri/tauri-dev.conf.json",
|
||||
@@ -27,92 +23,91 @@
|
||||
"replace-version": "node scripts/replace-version.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6",
|
||||
"@codemirror/lang-javascript": "^6",
|
||||
"@codemirror/lang-json": "^6",
|
||||
"@codemirror/lang-xml": "^6",
|
||||
"@codemirror/language": "^6",
|
||||
"@codemirror/search": "^6",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@react-hook/resize-observer": "^2.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.55.4",
|
||||
"@tauri-apps/api": "^2.0.0-rc.4",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.0.0-rc.2",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0-rc.2",
|
||||
"@tauri-apps/plugin-log": "^2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc.1",
|
||||
"@yaakapp/api": "^0.1.17",
|
||||
"@codemirror/commands": "^6.2.1",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
"@codemirror/search": "^6.2.3",
|
||||
"@lezer/generator": "^1.2.2",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
"@tailwindcss/container-queries": "^0.1.0",
|
||||
"@tanstack/react-query": "^5.45.1",
|
||||
"@tauri-apps/api": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-log": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc.0",
|
||||
"@yaakapp/api": "^0.1.13",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.5.1",
|
||||
"classnames": "^2.3.2",
|
||||
"cm6-graphql": "^0.0.9",
|
||||
"codemirror": "^6.0.1",
|
||||
"codemirror-json-schema": "^0.7.8",
|
||||
"date-fns": "^3.6.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"codemirror-json-schema": "^0.6.1",
|
||||
"date-fns": "^3.3.1",
|
||||
"fast-fuzzy": "^1.12.0",
|
||||
"focus-trap-react": "^10.2.3",
|
||||
"format-graphql": "^1.5.0",
|
||||
"framer-motion": "^11.5.4",
|
||||
"focus-trap-react": "^10.1.1",
|
||||
"format-graphql": "^1.4.0",
|
||||
"framer-motion": "^9.0.4",
|
||||
"jotai": "^2.9.3",
|
||||
"lucide-react": "^0.439.0",
|
||||
"mime": "^4.0.4",
|
||||
"lucide-react": "^0.309.0",
|
||||
"mime": "^4.0.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"parse-color": "^1.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-helmet-async": "^2.0.5",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-use": "^17.5.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-pdf": "^9.0.0",
|
||||
"react-router-dom": "^6.8.1",
|
||||
"react-use": "^17.4.0",
|
||||
"slugify": "^1.6.6",
|
||||
"uuid": "^10.0.0",
|
||||
"xml-formatter": "^3.6.3"
|
||||
"uuid": "^9.0.0",
|
||||
"xml-formatter": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
|
||||
"@tanstack/react-query-devtools": "^5.55.4",
|
||||
"@tauri-apps/cli": "^2.0.0-rc.14",
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/parse-color": "^1.0.3",
|
||||
"@types/parse-json": "^4.0.2",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
||||
"@typescript-eslint/parser": "^8.5.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"@tanstack/react-query-devtools": "^5.45.1",
|
||||
"@tauri-apps/cli": "^2.0.0-rc.2",
|
||||
"@types/node": "^18.7.10",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/parse-color": "^1.0.1",
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"@types/react": "^18.0.31",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||
"@typescript-eslint/parser": "^7.0.2",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"decompress": "^4.2.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-prettier": "^8",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.0",
|
||||
"eslint-plugin-react": "^7.35.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"husky": "^9.1.5",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"husky": "^8.0.3",
|
||||
"internal-ip": "^8.0.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"lint-staged": "^15.0.2",
|
||||
"nodejs-file-downloader": "^4.13.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.45",
|
||||
"postcss-nesting": "^13.0.0",
|
||||
"prettier": "^3.3.3",
|
||||
"react-devtools": "^5.3.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "^5.6.0",
|
||||
"vite": "^5.4.4",
|
||||
"vite-plugin-static-copy": "^1.0.6",
|
||||
"postcss": "^8.4.21",
|
||||
"postcss-nesting": "^11.2.1",
|
||||
"prettier": "^2.8.4",
|
||||
"react-devtools": "^4.27.2",
|
||||
"rimraf": "^5.0.7",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.0.0",
|
||||
"vite-plugin-static-copy": "^1.0.5",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"vite-plugin-top-level-await": "^1.4.4"
|
||||
"vite-plugin-top-level-await": "^1.4.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": "eslint --cache --fix",
|
||||
|
||||
31
plugin-runtime-types/package-lock.json
generated
31
plugin-runtime-types/package-lock.json
generated
@@ -1,34 +1,32 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.1.17",
|
||||
"version": "0.1.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.1.17",
|
||||
"version": "0.1.11",
|
||||
"dependencies": {
|
||||
"@types/node": "^22.5.4"
|
||||
"@types/node": "^22.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
|
||||
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||
"license": "MIT",
|
||||
"version": "22.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz",
|
||||
"integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.11.1"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -38,10 +36,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"license": "MIT"
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz",
|
||||
"integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.1.17",
|
||||
"version": "0.1.13",
|
||||
"main": "lib/index.js",
|
||||
"typings": "./lib/index.d.ts",
|
||||
"files": [
|
||||
@@ -11,9 +11,9 @@
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^22.5.4"
|
||||
"@types/node": "^22.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type PluginReloadResponse = {};
|
||||
export type BootRequest = { dir: string, };
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type PluginBootResponse = { name: string, version: string, capabilities: Array<string>, };
|
||||
export type BootResponse = { name: string, version: string, capabilities: Array<string>, };
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { GrpcEventType } from "./GrpcEventType";
|
||||
|
||||
export type GrpcEvent = { id: string, model: "grpc_event", workspaceId: string, requestId: string, connectionId: string, createdAt: string, updatedAt: string, content: string, eventType: GrpcEventType, metadata: { [key: string]: string }, status: number | null, error: string | null, };
|
||||
export type GrpcEvent = { id: string, model: "grpc_event", workspaceId: string, requestId: string, connectionId: string, createdAt: string, content: string, eventType: GrpcEventType, metadata: { [key: string]: string }, status: number | null, error: string | null, };
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { BootRequest } from "./BootRequest";
|
||||
import type { BootResponse } from "./BootResponse";
|
||||
import type { CallHttpRequestActionRequest } from "./CallHttpRequestActionRequest";
|
||||
import type { CallTemplateFunctionRequest } from "./CallTemplateFunctionRequest";
|
||||
import type { CallTemplateFunctionResponse } from "./CallTemplateFunctionResponse";
|
||||
@@ -17,14 +19,10 @@ import type { GetHttpRequestByIdResponse } from "./GetHttpRequestByIdResponse";
|
||||
import type { GetTemplateFunctionsResponse } from "./GetTemplateFunctionsResponse";
|
||||
import type { ImportRequest } from "./ImportRequest";
|
||||
import type { ImportResponse } from "./ImportResponse";
|
||||
import type { PluginBootRequest } from "./PluginBootRequest";
|
||||
import type { PluginBootResponse } from "./PluginBootResponse";
|
||||
import type { PluginReloadRequest } from "./PluginReloadRequest";
|
||||
import type { PluginReloadResponse } from "./PluginReloadResponse";
|
||||
import type { RenderHttpRequestRequest } from "./RenderHttpRequestRequest";
|
||||
import type { RenderHttpRequestResponse } from "./RenderHttpRequestResponse";
|
||||
import type { SendHttpRequestRequest } from "./SendHttpRequestRequest";
|
||||
import type { SendHttpRequestResponse } from "./SendHttpRequestResponse";
|
||||
import type { ShowToastRequest } from "./ShowToastRequest";
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & PluginBootRequest | { "type": "boot_response" } & PluginBootResponse | { "type": "reload_request" } & PluginReloadRequest | { "type": "reload_response" } & PluginReloadResponse | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyResponse;
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyResponse;
|
||||
|
||||
@@ -8,8 +8,7 @@ import type { GrpcRequest } from "./GrpcRequest";
|
||||
import type { HttpRequest } from "./HttpRequest";
|
||||
import type { HttpResponse } from "./HttpResponse";
|
||||
import type { KeyValue } from "./KeyValue";
|
||||
import type { Plugin } from "./Plugin";
|
||||
import type { Settings } from "./Settings";
|
||||
import type { Workspace } from "./Workspace";
|
||||
|
||||
export type Model = Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | KeyValue | Workspace | CookieJar | Settings | Plugin;
|
||||
export type Model = Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | KeyValue | Workspace | CookieJar | Settings;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Plugin = { id: string, model: "plugin", createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, url: string | null, enabled: boolean, };
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type PluginBootRequest = { dir: string, };
|
||||
export type RenderRequest = { template: string, };
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type PluginReloadRequest = {};
|
||||
export type RenderResponse = { rendered: string, };
|
||||
@@ -2,6 +2,8 @@ export type * from './plugins';
|
||||
export type * from './themes';
|
||||
|
||||
// TODO: The next ts-rs release includes the ability to put everything in 1 file!
|
||||
export * from './gen/BootRequest';
|
||||
export * from './gen/BootResponse';
|
||||
export * from './gen/CallHttpRequestActionArgs';
|
||||
export * from './gen/CallHttpRequestActionRequest';
|
||||
export * from './gen/CallTemplateFunctionRequest';
|
||||
@@ -43,10 +45,6 @@ export * from './gen/InternalEvent';
|
||||
export * from './gen/InternalEventPayload';
|
||||
export * from './gen/KeyValue';
|
||||
export * from './gen/Model';
|
||||
export * from './gen/PluginBootRequest';
|
||||
export * from './gen/PluginBootResponse';
|
||||
export * from './gen/PluginReloadRequest';
|
||||
export * from './gen/PluginReloadResponse';
|
||||
export * from './gen/RenderHttpRequestRequest';
|
||||
export * from './gen/RenderHttpRequestResponse';
|
||||
export * from './gen/RenderPurpose';
|
||||
@@ -65,4 +63,3 @@ export * from './gen/TemplateFunctionSelectOption';
|
||||
export * from './gen/TemplateFunctionTextArg';
|
||||
export * from './gen/ToastVariant';
|
||||
export * from './gen/Workspace';
|
||||
export * from './gen/Plugin';
|
||||
|
||||
@@ -9,7 +9,7 @@ export type { Context } from './Context';
|
||||
/**
|
||||
* The global structure of a Yaak plugin
|
||||
*/
|
||||
export type PluginDefinition = {
|
||||
export type Plugin = {
|
||||
importer?: ImporterPlugin;
|
||||
theme?: ThemePlugin;
|
||||
filter?: FilterPlugin;
|
||||
|
||||
492
plugin-runtime/package-lock.json
generated
492
plugin-runtime/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
"intercept-stdout": "^0.1.2",
|
||||
"long": "^5.2.3",
|
||||
"nice-grpc": "^2.1.9",
|
||||
"protobufjs": "^7.4.0"
|
||||
"protobufjs": "^7.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/intercept-stdout": "^0.1.3",
|
||||
@@ -19,7 +19,7 @@
|
||||
"nodemon": "^3.1.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-proto": "^2.2.0",
|
||||
"typescript": "^5.6.0"
|
||||
"ts-proto": "^1.180.0",
|
||||
"typescript": "^5.5.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,35 +4,30 @@ import { Worker } from 'node:worker_threads';
|
||||
import { EventChannel } from './EventChannel';
|
||||
|
||||
export class PluginHandle {
|
||||
#worker: Worker;
|
||||
readonly #worker: Worker;
|
||||
|
||||
constructor(
|
||||
readonly pluginDir: string,
|
||||
readonly pluginRefId: string,
|
||||
readonly events: EventChannel,
|
||||
) {
|
||||
this.#worker = this.#createWorker();
|
||||
const workerPath = process.env.YAAK_WORKER_PATH ?? path.join(__dirname, 'index.worker.cjs');
|
||||
this.#worker = new Worker(workerPath, {
|
||||
workerData: {
|
||||
pluginDir,
|
||||
pluginRefId,
|
||||
},
|
||||
});
|
||||
|
||||
this.#worker.on('message', (e) => this.events.emit(e));
|
||||
this.#worker.on('error', this.#handleError.bind(this));
|
||||
this.#worker.on('exit', this.#handleExit.bind(this));
|
||||
}
|
||||
|
||||
sendToWorker(event: InternalEvent) {
|
||||
this.#worker.postMessage(event);
|
||||
}
|
||||
|
||||
#createWorker(): Worker {
|
||||
const workerPath = process.env.YAAK_WORKER_PATH ?? path.join(__dirname, 'index.worker.cjs');
|
||||
const worker = new Worker(workerPath, {
|
||||
workerData: { pluginDir: this.pluginDir, pluginRefId: this.pluginRefId },
|
||||
});
|
||||
|
||||
worker.on('message', (e) => this.events.emit(e));
|
||||
worker.on('error', this.#handleError.bind(this));
|
||||
worker.on('exit', this.#handleExit.bind(this));
|
||||
|
||||
console.log('Created plugin worker for ', this.pluginDir);
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
async #handleError(err: Error) {
|
||||
console.error('Plugin errored', this.pluginDir, err);
|
||||
}
|
||||
@@ -41,7 +36,7 @@ export class PluginHandle {
|
||||
if (code === 0) {
|
||||
console.log('Plugin exited successfully', this.pluginDir);
|
||||
} else {
|
||||
console.log('Plugin exited with status', code, this.pluginDir);
|
||||
console.log('Plugin exited with error', code, this.pluginDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.2.0
|
||||
// protoc-gen-ts_proto v1.180.0
|
||||
// protoc v3.19.1
|
||||
// source: plugins/runtime.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
import { type CallContext, type CallOptions } from "nice-grpc-common";
|
||||
import * as _m0 from "protobufjs/minimal";
|
||||
|
||||
export const protobufPackage = "yaak.plugins.runtime";
|
||||
|
||||
@@ -18,16 +18,16 @@ function createBaseEventStreamEvent(): EventStreamEvent {
|
||||
return { event: "" };
|
||||
}
|
||||
|
||||
export const EventStreamEvent: MessageFns<EventStreamEvent> = {
|
||||
encode(message: EventStreamEvent, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
export const EventStreamEvent = {
|
||||
encode(message: EventStreamEvent, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
||||
if (message.event !== "") {
|
||||
writer.uint32(10).string(message.event);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EventStreamEvent {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
decode(input: _m0.Reader | Uint8Array, length?: number): EventStreamEvent {
|
||||
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEventStreamEvent();
|
||||
while (reader.pos < end) {
|
||||
@@ -44,7 +44,7 @@ export const EventStreamEvent: MessageFns<EventStreamEvent> = {
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
reader.skipType(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
@@ -114,12 +114,3 @@ function isSet(value: any): boolean {
|
||||
}
|
||||
|
||||
export type ServerStreamingMethodResult<Response> = { [Symbol.asyncIterator](): AsyncIterator<Response, void> };
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||
fromJSON(object: any): T;
|
||||
toJSON(message: T): unknown;
|
||||
create(base?: DeepPartial<T>): T;
|
||||
fromPartial(object: DeepPartial<T>): T;
|
||||
}
|
||||
|
||||
@@ -10,31 +10,34 @@ import {
|
||||
SendHttpRequestResponse,
|
||||
TemplateFunction,
|
||||
} from '@yaakapp/api';
|
||||
import { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpRequestActionPlugin';
|
||||
import { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/httpRequestAction';
|
||||
import { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
import interceptStdout from 'intercept-stdout';
|
||||
import * as console from 'node:console';
|
||||
import { Stats, readFileSync, statSync, watch } from 'node:fs';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import * as util from 'node:util';
|
||||
import { parentPort, workerData } from 'node:worker_threads';
|
||||
|
||||
async function initialize() {
|
||||
new Promise<void>(async (resolve, reject) => {
|
||||
const { pluginDir, pluginRefId } = workerData;
|
||||
const pathPkg = path.join(pluginDir, 'package.json');
|
||||
|
||||
// NOTE: Use POSIX join because require() needs forward slash
|
||||
const pathMod = path.posix.join(pluginDir, 'build', 'index.js');
|
||||
async function importModule() {
|
||||
const id = require.resolve(pathMod);
|
||||
delete require.cache[id];
|
||||
return require(id);
|
||||
}
|
||||
|
||||
const pkg = JSON.parse(readFileSync(pathPkg, 'utf8'));
|
||||
let pkg: { [x: string]: any };
|
||||
try {
|
||||
pkg = JSON.parse(readFileSync(pathPkg, 'utf8'));
|
||||
} catch (err) {
|
||||
// TODO: Do something better here
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
prefixStdout(`[plugin][${pkg.name}] %s`);
|
||||
|
||||
let mod = await importModule();
|
||||
const mod = (await import(pathMod)).default ?? {};
|
||||
|
||||
const capabilities: string[] = [];
|
||||
if (typeof mod.pluginHookExport === 'function') capabilities.push('export');
|
||||
@@ -91,19 +94,6 @@ async function initialize() {
|
||||
return promise as unknown as Promise<T>;
|
||||
}
|
||||
|
||||
async function reloadModule() {
|
||||
mod = await importModule();
|
||||
}
|
||||
|
||||
// Reload plugin if JS or package.json changes
|
||||
const cb = async () => {
|
||||
await reloadModule();
|
||||
return sendPayload({ type: 'reload_response' }, null);
|
||||
};
|
||||
|
||||
watchFile(pathMod, cb);
|
||||
watchFile(pathPkg, cb);
|
||||
|
||||
const ctx: Context = {
|
||||
clipboard: {
|
||||
async copyText(text) {
|
||||
@@ -258,10 +248,6 @@ async function initialize() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.type === 'reload_request') {
|
||||
await reloadModule();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Plugin call threw exception', payload.type, err);
|
||||
// TODO: Return errors to server
|
||||
@@ -270,9 +256,9 @@ async function initialize() {
|
||||
// No matches, so send back an empty response so the caller doesn't block forever
|
||||
sendEmpty(replyId);
|
||||
});
|
||||
}
|
||||
|
||||
initialize().catch((err) => {
|
||||
resolve();
|
||||
}).catch((err) => {
|
||||
console.log('failed to boot plugin', err);
|
||||
});
|
||||
|
||||
@@ -299,23 +285,3 @@ function prefixStdout(s: string) {
|
||||
return newText.trimEnd();
|
||||
});
|
||||
}
|
||||
|
||||
const watchedFiles: Record<string, Stats> = {};
|
||||
|
||||
/**
|
||||
* Watch a file and trigger callback on change.
|
||||
*
|
||||
* We also track the stat for each file because fs.watch will
|
||||
* trigger a "change" event when the access date changes
|
||||
*/
|
||||
function watchFile(filepath: string, cb: (filepath: string) => void) {
|
||||
watch(filepath, (_event, _name) => {
|
||||
const stat = statSync(filepath);
|
||||
if (stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
|
||||
cb(filepath);
|
||||
} else {
|
||||
console.log('SKIPPING SAME FILE STAT', filepath, stat);
|
||||
}
|
||||
watchedFiles[filepath] = stat;
|
||||
});
|
||||
}
|
||||
|
||||
404
src-tauri/Cargo.lock
generated
404
src-tauri/Cargo.lock
generated
@@ -124,7 +124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"core-graphics 0.23.2",
|
||||
"core-graphics",
|
||||
"image 0.25.2",
|
||||
"log",
|
||||
"objc2",
|
||||
@@ -188,7 +188,7 @@ version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"brotli 6.0.0",
|
||||
"flate2",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
@@ -577,6 +577,17 @@ dependencies = [
|
||||
"syn_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor 2.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "6.0.0"
|
||||
@@ -585,7 +596,17 @@ checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
"brotli-decompressor 4.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -814,25 +835,9 @@ checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"cocoa-foundation 0.1.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics 0.23.2",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"block",
|
||||
"cocoa-foundation 0.2.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics 0.24.0",
|
||||
"cocoa-foundation",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
"objc",
|
||||
@@ -846,22 +851,8 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa-foundation"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"block",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics-types 0.2.0",
|
||||
"core-foundation",
|
||||
"core-graphics-types",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
@@ -962,21 +953,11 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
@@ -985,21 +966,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics-types 0.2.0",
|
||||
"core-foundation",
|
||||
"core-graphics-types",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
@@ -1011,18 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics-types"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-foundation",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -1639,15 +1596,6 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
@@ -2640,9 +2588,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.16.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847"
|
||||
checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199"
|
||||
dependencies = [
|
||||
"cfb",
|
||||
]
|
||||
@@ -2807,27 +2755,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "json-patch"
|
||||
version = "2.0.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
|
||||
checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b"
|
||||
dependencies = [
|
||||
"jsonptr",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonptr"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627"
|
||||
dependencies = [
|
||||
"fluent-uri",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyboard-types"
|
||||
version = "0.7.0"
|
||||
@@ -3117,11 +3053,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "muda"
|
||||
version = "0.14.1"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba8ac4080fb1e097c2c22acae467e46e4da72d941f02e82b67a87a2a89fa38b1"
|
||||
checksum = "86b959f97c97044e4c96e32e1db292a7d594449546a3c6b77ae613dc3a5b5145"
|
||||
dependencies = [
|
||||
"cocoa 0.26.0",
|
||||
"cocoa",
|
||||
"crossbeam-channel",
|
||||
"dpi",
|
||||
"gtk",
|
||||
@@ -3131,7 +3067,7 @@ dependencies = [
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3159,16 +3095,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
||||
checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 1.3.2",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.5.2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -3180,9 +3115,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.6.0+11769913"
|
||||
version = "0.4.1+23.1.7779620"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
|
||||
checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
|
||||
dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
@@ -3296,23 +3231,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.3"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.3"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.2",
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4221,9 +4156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.8"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
|
||||
checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand 0.8.5",
|
||||
@@ -4367,6 +4302,12 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.6.2"
|
||||
@@ -4564,7 +4505,7 @@ dependencies = [
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
@@ -4931,7 +4872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
@@ -5261,7 +5202,7 @@ checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cfg_aliases 0.2.1",
|
||||
"core-graphics 0.23.2",
|
||||
"core-graphics",
|
||||
"foreign-types 0.5.0",
|
||||
"js-sys",
|
||||
"log",
|
||||
@@ -5269,7 +5210,7 @@ dependencies = [
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"redox_syscall 0.5.3",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
@@ -5600,9 +5541,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.7"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7"
|
||||
checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"serde",
|
||||
@@ -5671,7 +5612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
@@ -5700,14 +5641,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.30.0"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a93f2c6b8fdaeb7f417bda89b5bc767999745c3052969664ae1fa65892deb7e"
|
||||
checksum = "ea538df05fbc2dcbbd740ba0cfe8607688535f4798d213cbbfa13ce494f3451f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cocoa 0.26.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics 0.24.0",
|
||||
"cocoa",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
"dlopen2",
|
||||
@@ -5726,13 +5667,13 @@ dependencies = [
|
||||
"objc",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"scopeguard",
|
||||
"tao-macros",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows 0.57.0",
|
||||
"windows-core 0.57.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
]
|
||||
@@ -5773,13 +5714,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.0.0-rc.12"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e5d4a319f11ae72c77d0f4dbd9703ab6b401eb91d1ca88d89c33d13d4ea20c"
|
||||
checksum = "19ee93e545e49458813d4ed16179c67ee6141dba140ec3d4f078dda3b8d4e0d1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cocoa 0.26.0",
|
||||
"cocoa",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"embed_plist",
|
||||
@@ -5797,8 +5738,7 @@ dependencies = [
|
||||
"muda",
|
||||
"objc",
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5819,14 +5759,14 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows 0.58.0",
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "148441d64674b2885c1ba5baf3ae61662bb8753859ffcfb541962cbc6b847f39"
|
||||
checksum = "96a58b3a716b51d7f671f729bb8c0a53cd2551eec8450c64e828ef4e6c9f948e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -5846,12 +5786,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72a15c3f9282c82871c69ddb65d02ae552738bbac848c8adcab521bf14d8b9e6"
|
||||
checksum = "90a9e63ecd827d57228864764e0234935c9aac230099cf145197c8c08e754ced"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
"brotli 3.5.0",
|
||||
"ico",
|
||||
"json-patch",
|
||||
"plist",
|
||||
@@ -5873,9 +5813,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.0.0-rc.10"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d1aa317bec56f78388cf6012d788876d838595a48f95cbd7835642db356a0"
|
||||
checksum = "a54f5d5b289aa6215ffcfed7d4ff9960a04b7a854436d04519a9fcf911050cba"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -5887,9 +5827,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d82a2adea16b8a71b7a5ad23f720bb13f8d2830b820cc1c266512314ba99bf67"
|
||||
checksum = "03ce2ac5e182251ff932750d69c9b240a78e44901a7a6234814d63c595b43660"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@@ -5920,12 +5860,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.0.0-rc.7"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "785722c81beb4a6b729ae55d06aeb68d47166c933e64b727e33254dcb5d4d82d"
|
||||
checksum = "6c538457a755a75b8bb1594ed40d1512f8f6386251d3fcde492f8f46768ec85b"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"rfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5933,19 +5874,16 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.0.0-rc.5"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cb1dfbbea322afbc9dec49351bc29edf4e85e74d37d9a3fcc72d67ed55ffdbd"
|
||||
checksum = "5df6b25b1f2b7b61565e66c4dbee9eb39e5635d2a763206e380e07cc3f601a67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
"glob",
|
||||
"percent-encoding",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5959,13 +5897,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-log"
|
||||
version = "2.0.0-rc.2"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b57e4666c4a5d81f81b7bb8eacf51ae32c4e69c35071aabb480ad20a80836e4e"
|
||||
checksum = "380d27f23c39cde6a73024e65d8ec9b5b0af861e968dbe16b3aad86cd2c578e5"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"byte-unit",
|
||||
"cocoa 0.25.0",
|
||||
"cocoa",
|
||||
"fern",
|
||||
"log",
|
||||
"objc",
|
||||
@@ -5981,9 +5919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-os"
|
||||
version = "2.0.0-rc.1"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee761edd532fce2232453e9c8e0f7d9c0b6fe125c4b90b3eb4362ee84224"
|
||||
checksum = "6b54cfeb26356822d3be3db4282041b03552f573a694b6b28aded7d95c62a039"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"log",
|
||||
@@ -5999,9 +5937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-shell"
|
||||
version = "2.0.0-rc.3"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83800ddf78b820172efb5ed7310344e8e4f97fd30cd8237a3f20c12a79eb136"
|
||||
checksum = "9209f6c32caec61e156a5616f7d80ba7683ca4a0a5641cbe5d3086ab371aaab2"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
@@ -6020,9 +5958,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.0.0-rc.3"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391ebb8ae8cd6aec44b5d96d3005659d88cde69c57326f639bbc660116a30d63"
|
||||
checksum = "5b5f10ba18d2fc65e16bdf053b7beccb621dcf880c52d2ab08bdeb2d685e3e14"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
@@ -6043,15 +5981,15 @@ dependencies = [
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-window-state"
|
||||
version = "2.0.0-rc.4"
|
||||
version = "2.0.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c9b841c8164b949eac9e4fe8d56a23ed5e8ef0ac1c52e26a500709f7a50e50"
|
||||
checksum = "4a86654a4937427217c7531594a60bede289e0e6d27baef0527d5678fb3d263a"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"log",
|
||||
@@ -6064,36 +6002,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389f78c8e8e6eff3897d8d9581087943b5976ea96a0ab5036be691f28c2b0df0"
|
||||
checksum = "8f01b129b1ebdf09563c354760dbe7c0e96a166b4e33362d9c8d207f527c7ea5"
|
||||
dependencies = [
|
||||
"dpi",
|
||||
"gtk",
|
||||
"http 1.1.0",
|
||||
"jni",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"url",
|
||||
"windows 0.58.0",
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "466c418aef2ddc7d31173a5b00bfae3df9b58ed66644339f3ca55008bfc54f25"
|
||||
checksum = "fcda27639094ace2bf25f00bc10e35ea4e3af2f92753b1bdd2a174d1fa5a6292"
|
||||
dependencies = [
|
||||
"cocoa 0.26.0",
|
||||
"cocoa",
|
||||
"gtk",
|
||||
"http 1.1.0",
|
||||
"jni",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"softbuffer",
|
||||
"tao",
|
||||
"tauri-runtime",
|
||||
@@ -6101,17 +6039,17 @@ dependencies = [
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows 0.58.0",
|
||||
"windows 0.57.0",
|
||||
"wry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.0.0-rc.11"
|
||||
version = "2.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3019641087c9039b57ebfca95fa42a93c07056845b7d8d57c8966061bcee83b4"
|
||||
checksum = "28bb83cffa26e9cb7a2b3d0c31ab87bf277f44aaaa90f17159aef4d37aabd051"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"brotli 3.5.0",
|
||||
"cargo_metadata",
|
||||
"ctor",
|
||||
"dunce",
|
||||
@@ -6610,23 +6548,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tray-icon"
|
||||
version = "0.17.0"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "044d7738b3d50f288ddef035b793228740ad4d927f5466b0af55dc15e7e03cfe"
|
||||
checksum = "3ad8319cca93189ea9ab1b290de0595960529750b6b8b501a399ed1ec3775d60"
|
||||
dependencies = [
|
||||
"core-graphics 0.24.0",
|
||||
"cocoa",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"libappindicator",
|
||||
"muda",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6801,18 +6738,13 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
|
||||
checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"regex",
|
||||
"serde",
|
||||
"unic-ucd-ident",
|
||||
@@ -7084,23 +7016,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.33.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c"
|
||||
checksum = "6516cfa64c6b3212686080eeec378e662c2af54bb2a5b2a22749673f5cb2226f"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows 0.57.0",
|
||||
"windows-core 0.57.0",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-macros"
|
||||
version = "0.8.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431"
|
||||
checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -7109,13 +7041,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-sys"
|
||||
version = "0.33.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
||||
checksum = "c76d5b77320ff155660be1df3e6588bc85c75f1a9feef938cc4dc4dd60d1d7cf"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows 0.57.0",
|
||||
"windows-core 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7171,9 +7103,9 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33082acd404763b315866e14a0d5193f3422c81086657583937a750cdd3ec340"
|
||||
dependencies = [
|
||||
"cocoa 0.25.0",
|
||||
"cocoa",
|
||||
"objc",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-version",
|
||||
]
|
||||
@@ -7189,11 +7121,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.58.0",
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@@ -7208,22 +7140,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.58.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -7232,9 +7163,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.58.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -7243,23 +7174,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
@@ -7504,14 +7425,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.43.1"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4d715cf5fe88e9647f3d17b207b6d060d4a88e7171d4ccb2d2c657dd1d44728"
|
||||
checksum = "68b00c945786b02d7805d09a969fa36d0eee4e0bd4fb3ec2a79d2bf45a1b44cd"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block",
|
||||
"cocoa 0.26.0",
|
||||
"core-graphics 0.24.0",
|
||||
"cocoa",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dpi",
|
||||
"dunce",
|
||||
@@ -7524,11 +7445,13 @@ dependencies = [
|
||||
"kuchikiki",
|
||||
"libc",
|
||||
"ndk",
|
||||
"ndk-context",
|
||||
"ndk-sys",
|
||||
"objc",
|
||||
"objc_id",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.6.2",
|
||||
"sha2",
|
||||
"soup3",
|
||||
"tao-macros",
|
||||
@@ -7536,8 +7459,8 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
"webview2-com",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows 0.57.0",
|
||||
"windows-core 0.57.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
]
|
||||
@@ -7617,7 +7540,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"cocoa 0.26.0",
|
||||
"cocoa",
|
||||
"datetime",
|
||||
"hex_color",
|
||||
"http 1.1.0",
|
||||
@@ -7645,7 +7568,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"yaak_grpc",
|
||||
"yaak_models",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[workspace]
|
||||
members = ["yaak_grpc", "yaak_templates", "yaak_plugin_runtime", "yaak_models"]
|
||||
|
||||
|
||||
[package]
|
||||
name = "yaak-app"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
authors = ["Gregory Schier"]
|
||||
|
||||
# Produce a library for mobile support
|
||||
[lib]
|
||||
@@ -20,7 +20,7 @@ tauri-build = { version = "2.0.0-rc.0", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2.7"
|
||||
cocoa = "0.26.0"
|
||||
cocoa = "0.25.0"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work
|
||||
@@ -44,24 +44,23 @@ reqwest_cookie_store = "0.8.0"
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
serde_json = { version = "1.0.116", features = ["raw_value"] }
|
||||
serde_yaml = "0.9.34"
|
||||
tauri = { workspace = true }
|
||||
tauri = { workspace = true, features = ["unstable"] }
|
||||
tauri-plugin-shell = { workspace = true }
|
||||
tauri-plugin-clipboard-manager = "2.1.0-beta.7"
|
||||
tauri-plugin-dialog = "2.0.0-rc.7"
|
||||
tauri-plugin-fs = "2.0.0-rc.5"
|
||||
tauri-plugin-log = { version = "2.0.0-rc.2", features = ["colored"] }
|
||||
tauri-plugin-os = "2.0.0-rc.1"
|
||||
tauri-plugin-updater = "2.0.0-rc.3"
|
||||
tauri-plugin-window-state = "2.0.0-rc.4"
|
||||
tauri-plugin-dialog = "2.0.0-rc.0"
|
||||
tauri-plugin-fs = "2.0.0-rc.0"
|
||||
tauri-plugin-log = { version = "2.0.0-rc.0", features = ["colored"] }
|
||||
tauri-plugin-os = "2.0.0-rc.0"
|
||||
tauri-plugin-updater = "2.0.0-rc.0"
|
||||
tauri-plugin-window-state = "2.0.0-rc.0"
|
||||
tokio = { version = "1.36.0", features = ["sync"] }
|
||||
tokio-stream = "0.1.15"
|
||||
uuid = "1.7.0"
|
||||
thiserror = "1.0.61"
|
||||
mime_guess = "2.0.5"
|
||||
urlencoding = "2.1.3"
|
||||
|
||||
[workspace.dependencies]
|
||||
yaak_models = { path = "yaak_models" }
|
||||
yaak_plugin_runtime = { path = "yaak_plugin_runtime" }
|
||||
tauri-plugin-shell = "2.0.0-rc.3"
|
||||
tauri = { version = "2.0.0-rc.12", features = ["devtools", "protocol-asset"] }
|
||||
tauri = { version = "2.0.0-rc.2", features = ["devtools", "protocol-asset"] }
|
||||
tauri-plugin-shell = "2.0.0-rc.0"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
CREATE TABLE plugins
|
||||
(
|
||||
id TEXT NOT NULL
|
||||
PRIMARY KEY,
|
||||
model TEXT DEFAULT 'plugin' NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
checked_at DATETIME NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
directory TEXT NULL NOT NULL,
|
||||
url TEXT NULL
|
||||
);
|
||||
@@ -28,7 +28,6 @@ pub enum AnalyticsResource {
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
KeyValue,
|
||||
Plugin,
|
||||
Setting,
|
||||
Sidebar,
|
||||
Theme,
|
||||
|
||||
@@ -23,7 +23,7 @@ use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::watch::Receiver;
|
||||
use yaak_models::models::{
|
||||
Cookie, CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseHeader, HttpUrlParameter,
|
||||
Cookie, CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseHeader,
|
||||
};
|
||||
use yaak_models::queries::{get_workspace, update_response_if_id, upsert_cookie_jar};
|
||||
|
||||
@@ -40,8 +40,13 @@ pub async fn send_http_request<R: Runtime>(
|
||||
.expect("Failed to get Workspace");
|
||||
let cb = &*window.app_handle().state::<PluginTemplateCallback>();
|
||||
let cb = cb.for_send();
|
||||
let rendered_request =
|
||||
render_http_request(&request, &workspace, environment.as_ref(), &cb).await;
|
||||
let rendered_request = render_http_request(
|
||||
&request,
|
||||
&workspace,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut url_string = rendered_request.url;
|
||||
|
||||
@@ -96,23 +101,6 @@ pub async fn send_http_request<R: Runtime>(
|
||||
|
||||
let client = client_builder.build().expect("Failed to build client");
|
||||
|
||||
// Render query parameters
|
||||
let mut query_params = Vec::new();
|
||||
for p in rendered_request.url_parameters {
|
||||
if !p.enabled || p.name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace path parameters with values from URL parameters
|
||||
let old_url_string = url_string.clone();
|
||||
url_string = replace_path_placeholder(&p, url_string.as_str());
|
||||
|
||||
// Treat as regular param if wasn't used as path param
|
||||
if old_url_string == url_string {
|
||||
query_params.push((p.name, p.value));
|
||||
}
|
||||
}
|
||||
|
||||
let uri = match http::Uri::from_str(url_string.as_str()) {
|
||||
Ok(u) => u,
|
||||
Err(e) => {
|
||||
@@ -139,7 +127,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
|
||||
let m = Method::from_bytes(rendered_request.method.to_uppercase().as_bytes())
|
||||
.expect("Failed to create method");
|
||||
let mut request_builder = client.request(m, url).query(&query_params);
|
||||
let mut request_builder = client.request(m, url);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(USER_AGENT, HeaderValue::from_static("yaak"));
|
||||
@@ -204,7 +192,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = format!("{username}:{password}");
|
||||
let encoded = base64::engine::general_purpose::STANDARD.encode(auth);
|
||||
let encoded = base64::engine::general_purpose::STANDARD_NO_PAD.encode(auth);
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("Basic {}", encoded)).unwrap(),
|
||||
@@ -222,6 +210,15 @@ pub async fn send_http_request<R: Runtime>(
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params = Vec::new();
|
||||
for p in rendered_request.url_parameters {
|
||||
if !p.enabled || p.name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
query_params.push((p.name, p.value));
|
||||
}
|
||||
request_builder = request_builder.query(&query_params);
|
||||
|
||||
let request_body = rendered_request.body;
|
||||
if let Some(body_type) = &rendered_request.body_type {
|
||||
if request_body.contains_key("text") {
|
||||
@@ -492,119 +489,3 @@ fn get_str_h<'a>(v: &'a HashMap<String, Value>, key: &str) -> &'a str {
|
||||
Some(v) => v.as_str().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_path_placeholder(p: &HttpUrlParameter, url: &str) -> String {
|
||||
if !p.enabled {
|
||||
return url.to_string();
|
||||
}
|
||||
|
||||
let re = regex::Regex::new(format!("(/){}([/?#]|$)", p.name).as_str()).unwrap();
|
||||
let result = re
|
||||
.replace_all(url, |cap: ®ex::Captures| {
|
||||
format!(
|
||||
"{}{}{}",
|
||||
cap[1].to_string(),
|
||||
urlencoding::encode(p.value.as_str()),
|
||||
cap[2].to_string()
|
||||
)
|
||||
})
|
||||
.into_owned();
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::http_request::replace_path_placeholder;
|
||||
use yaak_models::models::HttpUrlParameter;
|
||||
|
||||
#[test]
|
||||
fn placeholder_middle() {
|
||||
let p = HttpUrlParameter {
|
||||
name: ":foo".into(),
|
||||
value: "xxx".into(),
|
||||
enabled: true,
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
|
||||
"https://example.com/xxx/bar",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_end() {
|
||||
let p = HttpUrlParameter {
|
||||
name: ":foo".into(),
|
||||
value: "xxx".into(),
|
||||
enabled: true,
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||
"https://example.com/xxx",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_query() {
|
||||
let p = HttpUrlParameter {
|
||||
name: ":foo".into(),
|
||||
value: "xxx".into(),
|
||||
enabled: true,
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
|
||||
"https://example.com/xxx?:foo",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_missing() {
|
||||
let p = HttpUrlParameter {
|
||||
enabled: true,
|
||||
name: "".to_string(),
|
||||
value: "".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:missing"),
|
||||
"https://example.com/:missing",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_disabled() {
|
||||
let p = HttpUrlParameter {
|
||||
enabled: false,
|
||||
name: ":foo".to_string(),
|
||||
value: "xxx".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||
"https://example.com/:foo",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_prefix() {
|
||||
let p = HttpUrlParameter {
|
||||
name: ":foo".into(),
|
||||
value: "xxx".into(),
|
||||
enabled: true,
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foooo"),
|
||||
"https://example.com/:foooo",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_encode() {
|
||||
let p = HttpUrlParameter {
|
||||
name: ":foo".into(),
|
||||
value: "Hello World".into(),
|
||||
enabled: true,
|
||||
};
|
||||
assert_eq!(
|
||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||
"https://example.com/Hello%20World",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
extern crate core;
|
||||
#[cfg(target_os = "macos")]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -11,7 +12,6 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use fern::colors::ColoredLevelConfig;
|
||||
use log::{debug, error, info, warn};
|
||||
use rand::random;
|
||||
@@ -41,7 +41,7 @@ use crate::updates::{UpdateMode, YaakUpdater};
|
||||
use crate::window_menu::app_menu;
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Plugin, Settings, Workspace,
|
||||
GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Settings, Workspace,
|
||||
};
|
||||
use yaak_models::queries::{
|
||||
cancel_pending_grpc_connections, cancel_pending_responses, create_default_http_response,
|
||||
@@ -50,19 +50,17 @@ use yaak_models::queries::{
|
||||
delete_http_response, delete_workspace, duplicate_grpc_request, duplicate_http_request,
|
||||
generate_model_id, get_cookie_jar, get_environment, get_folder, get_grpc_connection,
|
||||
get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
|
||||
get_or_create_settings, get_plugin, get_workspace, list_cookie_jars, list_environments,
|
||||
list_folders, list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests,
|
||||
list_http_responses, list_plugins, list_workspaces, set_key_value_raw, update_response_if_id,
|
||||
get_or_create_settings, get_workspace, list_cookie_jars, list_environments, list_folders,
|
||||
list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests,
|
||||
list_http_responses, list_workspaces, set_key_value_raw, update_response_if_id,
|
||||
update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
|
||||
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace,
|
||||
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_workspace,
|
||||
};
|
||||
use yaak_plugin_runtime::events::{
|
||||
CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
|
||||
InternalEvent, InternalEventPayload, PluginBootResponse, RenderHttpRequestResponse,
|
||||
SendHttpRequestResponse, ShowToastRequest, ToastVariant,
|
||||
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse,
|
||||
};
|
||||
use yaak_plugin_runtime::handle::PluginHandle;
|
||||
use yaak_templates::{Parser, Tokens};
|
||||
|
||||
mod analytics;
|
||||
@@ -80,9 +78,6 @@ mod window_menu;
|
||||
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
|
||||
const DEFAULT_WINDOW_HEIGHT: f64 = 600.0;
|
||||
|
||||
const MIN_WINDOW_WIDTH: f64 = 300.0;
|
||||
const MIN_WINDOW_HEIGHT: f64 = 300.0;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
struct AppMetaData {
|
||||
@@ -847,33 +842,16 @@ async fn cmd_import_data(
|
||||
imported_resources.environments.len()
|
||||
);
|
||||
|
||||
// Folders can foreign-key to themselves, so we need to import from
|
||||
// the top of the tree to the bottom to avoid foreign key conflicts.
|
||||
// We do this by looping until we've imported them all, only importing if:
|
||||
// - The parent folder has been imported
|
||||
// - The folder hasn't already been imported
|
||||
// The loop exits when imported.len == to_import.len
|
||||
while imported_resources.folders.len() < resources.folders.len() {
|
||||
for mut v in resources.folders.clone() {
|
||||
v.id = maybe_gen_id(v.id.as_str(), ModelType::TypeFolder, &mut id_map);
|
||||
v.workspace_id = maybe_gen_id(
|
||||
v.workspace_id.as_str(),
|
||||
ModelType::TypeWorkspace,
|
||||
&mut id_map,
|
||||
);
|
||||
v.folder_id = maybe_gen_id_opt(v.folder_id, ModelType::TypeFolder, &mut id_map);
|
||||
if let Some(fid) = v.folder_id.clone() {
|
||||
let imported_parent = imported_resources.folders.iter().find(|f| f.id == fid);
|
||||
if imported_parent.is_none() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(_) = imported_resources.folders.iter().find(|f| f.id == v.id) {
|
||||
continue;
|
||||
}
|
||||
let x = upsert_folder(&w, v).await.map_err(|e| e.to_string())?;
|
||||
imported_resources.folders.push(x.clone());
|
||||
}
|
||||
for mut v in resources.folders {
|
||||
v.id = maybe_gen_id(v.id.as_str(), ModelType::TypeFolder, &mut id_map);
|
||||
v.workspace_id = maybe_gen_id(
|
||||
v.workspace_id.as_str(),
|
||||
ModelType::TypeWorkspace,
|
||||
&mut id_map,
|
||||
);
|
||||
v.folder_id = maybe_gen_id_opt(v.folder_id, ModelType::TypeFolder, &mut id_map);
|
||||
let x = upsert_folder(&w, v).await.map_err(|e| e.to_string())?;
|
||||
imported_resources.folders.push(x.clone());
|
||||
}
|
||||
info!("Imported {} folders", imported_resources.folders.len());
|
||||
|
||||
@@ -1171,24 +1149,6 @@ async fn cmd_create_workspace(name: &str, w: WebviewWindow) -> Result<Workspace,
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_create_plugin(
|
||||
directory: &str,
|
||||
url: Option<String>,
|
||||
w: WebviewWindow,
|
||||
) -> Result<Plugin, String> {
|
||||
upsert_plugin(
|
||||
&w,
|
||||
Plugin {
|
||||
directory: directory.into(),
|
||||
url,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_update_cookie_jar(
|
||||
cookie_jar: CookieJar,
|
||||
@@ -1432,9 +1392,10 @@ async fn cmd_list_grpc_requests(
|
||||
workspace_id: &str,
|
||||
w: WebviewWindow,
|
||||
) -> Result<Vec<GrpcRequest>, String> {
|
||||
list_grpc_requests(&w, workspace_id)
|
||||
let requests = list_grpc_requests(&w, workspace_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1442,9 +1403,11 @@ async fn cmd_list_http_requests(
|
||||
workspace_id: &str,
|
||||
w: WebviewWindow,
|
||||
) -> Result<Vec<HttpRequest>, String> {
|
||||
list_http_requests(&w, workspace_id)
|
||||
let requests = list_http_requests(&w, workspace_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
.expect("Failed to find requests");
|
||||
// .map_err(|e| e.to_string())
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1452,33 +1415,11 @@ async fn cmd_list_environments(
|
||||
workspace_id: &str,
|
||||
w: WebviewWindow,
|
||||
) -> Result<Vec<Environment>, String> {
|
||||
list_environments(&w, workspace_id)
|
||||
let environments = list_environments(&w, workspace_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
.expect("Failed to find environments");
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_list_plugins(w: WebviewWindow) -> Result<Vec<Plugin>, String> {
|
||||
list_plugins(&w).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_reload_plugins(plugin_manager: State<'_, PluginManager>) -> Result<(), String> {
|
||||
plugin_manager.reload_all().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_plugin_info(
|
||||
id: &str,
|
||||
w: WebviewWindow,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> Result<PluginBootResponse, String> {
|
||||
let plugin = get_plugin(&w, id).await.map_err(|e| e.to_string())?;
|
||||
plugin_manager
|
||||
.get_plugin_info(plugin.directory.as_str())
|
||||
.await
|
||||
.ok_or("Failed to find plugin info".to_string())
|
||||
Ok(environments)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1732,7 +1673,6 @@ pub fn run() {
|
||||
cmd_create_folder,
|
||||
cmd_create_grpc_request,
|
||||
cmd_create_http_request,
|
||||
cmd_create_plugin,
|
||||
cmd_create_workspace,
|
||||
cmd_curl_to_request,
|
||||
cmd_delete_all_grpc_connections,
|
||||
@@ -1746,6 +1686,9 @@ pub fn run() {
|
||||
cmd_delete_http_response,
|
||||
cmd_delete_workspace,
|
||||
cmd_dismiss_notification,
|
||||
cmd_parse_template,
|
||||
cmd_template_tokens_to_string,
|
||||
cmd_render_template,
|
||||
cmd_duplicate_grpc_request,
|
||||
cmd_duplicate_http_request,
|
||||
cmd_export_data,
|
||||
@@ -1761,6 +1704,7 @@ pub fn run() {
|
||||
cmd_grpc_go,
|
||||
cmd_grpc_reflect,
|
||||
cmd_http_request_actions,
|
||||
cmd_template_functions,
|
||||
cmd_import_data,
|
||||
cmd_list_cookie_jars,
|
||||
cmd_list_environments,
|
||||
@@ -1770,22 +1714,15 @@ pub fn run() {
|
||||
cmd_list_grpc_requests,
|
||||
cmd_list_http_requests,
|
||||
cmd_list_http_responses,
|
||||
cmd_list_plugins,
|
||||
cmd_list_workspaces,
|
||||
cmd_metadata,
|
||||
cmd_new_nested_window,
|
||||
cmd_new_window,
|
||||
cmd_parse_template,
|
||||
cmd_plugin_info,
|
||||
cmd_reload_plugins,
|
||||
cmd_render_template,
|
||||
cmd_save_response,
|
||||
cmd_send_ephemeral_request,
|
||||
cmd_send_http_request,
|
||||
cmd_set_key_value,
|
||||
cmd_set_update_mode,
|
||||
cmd_template_functions,
|
||||
cmd_template_tokens_to_string,
|
||||
cmd_track_event,
|
||||
cmd_update_cookie_jar,
|
||||
cmd_update_environment,
|
||||
@@ -1878,7 +1815,6 @@ fn create_nested_window(
|
||||
.title(title)
|
||||
.parent(&window)
|
||||
.unwrap()
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
||||
.inner_size(DEFAULT_WINDOW_WIDTH * 0.7, DEFAULT_WINDOW_HEIGHT * 0.9);
|
||||
|
||||
// Add macOS-only things
|
||||
@@ -1889,7 +1825,7 @@ fn create_nested_window(
|
||||
.title_bar_style(TitleBarStyle::Overlay);
|
||||
}
|
||||
|
||||
// Add non-macOS things
|
||||
// Add non-MacOS things
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
win_builder = win_builder.decorations(false);
|
||||
@@ -1922,7 +1858,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
||||
100.0 + random::<f64>() * 30.0,
|
||||
100.0 + random::<f64>() * 30.0,
|
||||
)
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
||||
.min_inner_size(300.0, 300.0)
|
||||
.title(handle.package_info().name.to_string());
|
||||
|
||||
// Add macOS-only things
|
||||
@@ -1948,8 +1884,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
||||
return;
|
||||
}
|
||||
|
||||
let event_id = event.id().0.as_str();
|
||||
match event_id {
|
||||
match event.id().0.as_str() {
|
||||
"quit" => exit(0),
|
||||
"close" => w.close().unwrap(),
|
||||
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
||||
@@ -2009,26 +1944,18 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
let app_handle = app_handle.clone();
|
||||
let plugin = plugin_manager
|
||||
.get_plugin(event.plugin_ref_id.as_str())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
||||
// want to block here
|
||||
tauri::async_runtime::spawn(async move {
|
||||
handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||
handle_plugin_event(&app_handle, &event).await;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn handle_plugin_event<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
event: &InternalEvent,
|
||||
plugin_handle: &PluginHandle,
|
||||
) {
|
||||
// info!("Got event to app {}", event.id);
|
||||
async fn handle_plugin_event<R: Runtime>(app_handle: &AppHandle<R>, event: &InternalEvent) {
|
||||
info!("Got event to app {}", event.id);
|
||||
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
||||
InternalEventPayload::CopyTextRequest(req) => {
|
||||
app_handle
|
||||
@@ -2085,36 +2012,6 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
},
|
||||
))
|
||||
}
|
||||
InternalEventPayload::ReloadResponse(_) => {
|
||||
let w = get_focused_window_no_lock(app_handle).expect("No focused window");
|
||||
let plugins = list_plugins(&w).await.unwrap();
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
continue;
|
||||
}
|
||||
|
||||
upsert_plugin(
|
||||
&w,
|
||||
Plugin {
|
||||
// TODO: Add reloaded_at field to use instead
|
||||
updated_at: Utc::now().naive_utc(),
|
||||
..plugin
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
let plugin_name = plugin_handle.info().await.unwrap().name;
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_name),
|
||||
variant: ToastVariant::Info,
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SendHttpRequestRequest(req) => {
|
||||
let w = get_focused_window_no_lock(app_handle).expect("No focused window");
|
||||
let url = w.url().unwrap();
|
||||
@@ -2175,6 +2072,7 @@ fn get_focused_window_no_lock<R: Runtime>(app_handle: &AppHandle<R>) -> Option<W
|
||||
// TODO: Getting the focused window doesn't seem to work on Windows, so
|
||||
// we'll need to pass the window label into plugin events instead.
|
||||
if app_handle.webview_windows().len() == 1 {
|
||||
debug!("Returning only webview window");
|
||||
let w = app_handle
|
||||
.webview_windows()
|
||||
.iter()
|
||||
|
||||
@@ -2,17 +2,14 @@ use std::fmt::{Display, Formatter};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use log::info;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
use tauri_plugin_updater::UpdaterExt;
|
||||
use tokio::task::block_in_place;
|
||||
use yaak_plugin_runtime::manager::PluginManager;
|
||||
|
||||
use crate::is_dev;
|
||||
|
||||
const MAX_UPDATE_CHECK_HOURS_STABLE: u64 = 12;
|
||||
const MAX_UPDATE_CHECK_HOURS_BETA: u64 = 3;
|
||||
const MAX_UPDATE_CHECK_HOURS_ALPHA: u64 = 1;
|
||||
// Check for updates every 3 hours
|
||||
const MAX_UPDATE_CHECK_SECONDS: u64 = 60 * 60 * 3;
|
||||
|
||||
// Create updater struct
|
||||
pub struct YaakUpdater {
|
||||
@@ -52,7 +49,6 @@ impl YaakUpdater {
|
||||
last_update_check: SystemTime::UNIX_EPOCH,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn force_check(
|
||||
&mut self,
|
||||
app_handle: &AppHandle,
|
||||
@@ -62,22 +58,8 @@ impl YaakUpdater {
|
||||
|
||||
info!("Checking for updates mode={}", mode);
|
||||
|
||||
let h = app_handle.clone();
|
||||
let update_check_result = app_handle
|
||||
.updater_builder()
|
||||
.on_before_exit(move || {
|
||||
// Kill plugin manager before exit or NSIS installer will fail to replace sidecar
|
||||
// while it's running.
|
||||
// NOTE: This is only called on Windows
|
||||
let h = h.clone();
|
||||
block_in_place(|| {
|
||||
tauri::async_runtime::block_on(async move {
|
||||
info!("Shutting down plugin manager before update");
|
||||
let plugin_manager = h.state::<PluginManager>();
|
||||
plugin_manager.cleanup().await;
|
||||
});
|
||||
});
|
||||
})
|
||||
.header("X-Update-Mode", mode.to_string())?
|
||||
.build()?
|
||||
.check()
|
||||
@@ -130,13 +112,8 @@ impl YaakUpdater {
|
||||
app_handle: &AppHandle,
|
||||
mode: UpdateMode,
|
||||
) -> Result<bool, tauri_plugin_updater::Error> {
|
||||
let update_period_seconds = match mode {
|
||||
UpdateMode::Stable => MAX_UPDATE_CHECK_HOURS_STABLE,
|
||||
UpdateMode::Beta => MAX_UPDATE_CHECK_HOURS_BETA,
|
||||
UpdateMode::Alpha => MAX_UPDATE_CHECK_HOURS_ALPHA,
|
||||
} * (60 * 60);
|
||||
let seconds_since_last_check = self.last_update_check.elapsed().unwrap().as_secs();
|
||||
let ignore_check = seconds_since_last_check < update_period_seconds;
|
||||
let ignore_check =
|
||||
self.last_update_check.elapsed().unwrap().as_secs() < MAX_UPDATE_CHECK_SECONDS;
|
||||
if ignore_check {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"publisher": "Yaak",
|
||||
"license": "MIT",
|
||||
"copyright": "Yaak",
|
||||
"homepage": "https://yaak.app",
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"externalBin": [
|
||||
|
||||
@@ -2,9 +2,9 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("SQL error: {0}")]
|
||||
#[error("SQL error")]
|
||||
SqlError(#[from] rusqlite::Error),
|
||||
#[error("JSON error: {0}")]
|
||||
#[error("JSON error")]
|
||||
JsonError(#[from] serde_json::Error),
|
||||
#[error("unknown error")]
|
||||
Unknown,
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct Settings {
|
||||
pub theme_light: String,
|
||||
pub update_channel: String,
|
||||
pub interface_font_size: i32,
|
||||
pub interface_scale: f32,
|
||||
pub interface_scale: i32,
|
||||
pub editor_font_size: i32,
|
||||
pub editor_soft_wrap: bool,
|
||||
pub telemetry: bool,
|
||||
@@ -655,7 +655,6 @@ pub struct GrpcEvent {
|
||||
pub request_id: String,
|
||||
pub connection_id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub content: String,
|
||||
pub event_type: GrpcEventType,
|
||||
pub metadata: HashMap<String, String>,
|
||||
@@ -694,7 +693,6 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
|
||||
request_id: r.get("request_id")?,
|
||||
connection_id: r.get("connection_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
content: r.get("content")?,
|
||||
event_type: serde_json::from_str(event_type.as_str()).unwrap_or_default(),
|
||||
metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(),
|
||||
@@ -704,51 +702,6 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct Plugin {
|
||||
pub id: String,
|
||||
#[ts(type = "\"plugin\"")]
|
||||
pub model: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub checked_at: Option<NaiveDateTime>,
|
||||
pub directory: String,
|
||||
pub url: Option<String>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum PluginIden {
|
||||
#[iden = "plugins"]
|
||||
Table,
|
||||
Id,
|
||||
Model,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CheckedAt,
|
||||
Directory,
|
||||
Url,
|
||||
Enabled,
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&Row<'s>> for Plugin {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(Plugin {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
checked_at: r.get("checked_at")?,
|
||||
url: r.get("url")?,
|
||||
directory: r.get("directory")?,
|
||||
enabled: r.get("enabled")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct KeyValue {
|
||||
@@ -805,7 +758,6 @@ pub enum ModelType {
|
||||
TypeGrpcRequest,
|
||||
TypeHttpRequest,
|
||||
TypeHttpResponse,
|
||||
TypePlugin,
|
||||
TypeWorkspace,
|
||||
}
|
||||
|
||||
@@ -820,7 +772,6 @@ impl ModelType {
|
||||
ModelType::TypeGrpcRequest => "gr",
|
||||
ModelType::TypeHttpRequest => "rq",
|
||||
ModelType::TypeHttpResponse => "rs",
|
||||
ModelType::TypePlugin => "pg",
|
||||
ModelType::TypeWorkspace => "wk",
|
||||
}
|
||||
.to_string()
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
use std::fs;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::models::{CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden, Settings, SettingsIden, Workspace, WorkspaceIden};
|
||||
use crate::models::{
|
||||
CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection,
|
||||
GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest,
|
||||
HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden,
|
||||
ModelType, Settings, SettingsIden, Workspace, WorkspaceIden,
|
||||
};
|
||||
use crate::plugin::SqliteConnection;
|
||||
use log::{debug, error};
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
@@ -843,7 +848,7 @@ pub async fn upsert_environment<R: Runtime>(
|
||||
serde_json::to_string(&environment.variables)?.into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::column(EnvironmentIden::Id)
|
||||
OnConflict::column(GrpcEventIden::Id)
|
||||
.update_columns([
|
||||
EnvironmentIden::UpdatedAt,
|
||||
EnvironmentIden::Name,
|
||||
@@ -872,88 +877,6 @@ pub async fn get_environment<R: Runtime>(mgr: &impl Manager<R>, id: &str) -> Res
|
||||
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
|
||||
}
|
||||
|
||||
pub async fn get_plugin<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
id: &str
|
||||
) -> Result<Plugin> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::select()
|
||||
.from(PluginIden::Table)
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(EnvironmentIden::Id).eq(id))
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
|
||||
}
|
||||
|
||||
pub async fn list_plugins<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
) -> Result<Vec<Plugin>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::select()
|
||||
.from(PluginIden::Table)
|
||||
.column(Asterisk)
|
||||
.order_by(PluginIden::CreatedAt, Order::Desc)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn upsert_plugin<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
plugin: Plugin,
|
||||
) -> Result<Plugin> {
|
||||
let id = match plugin.id.as_str() {
|
||||
"" => generate_model_id(ModelType::TypePlugin),
|
||||
_ => plugin.id.to_string(),
|
||||
};
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::insert()
|
||||
.into_table(PluginIden::Table)
|
||||
.columns([
|
||||
PluginIden::Id,
|
||||
PluginIden::CreatedAt,
|
||||
PluginIden::UpdatedAt,
|
||||
PluginIden::CheckedAt,
|
||||
PluginIden::Directory,
|
||||
PluginIden::Url,
|
||||
PluginIden::Enabled,
|
||||
])
|
||||
.values_panic([
|
||||
id.as_str().into(),
|
||||
CurrentTimestamp.into(),
|
||||
CurrentTimestamp.into(),
|
||||
plugin.checked_at.into(),
|
||||
plugin.directory.into(),
|
||||
plugin.url.into(),
|
||||
plugin.enabled.into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::column(PluginIden::Id)
|
||||
.update_columns([
|
||||
PluginIden::UpdatedAt,
|
||||
PluginIden::CheckedAt,
|
||||
PluginIden::Directory,
|
||||
PluginIden::Url,
|
||||
PluginIden::Enabled,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.returning_all()
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let m = stmt.query_row(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(emit_upserted_model(window, m))
|
||||
}
|
||||
|
||||
pub async fn get_folder<R: Runtime>(mgr: &impl Manager<R>, id: &str) -> Result<Folder> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
@@ -4,7 +4,7 @@ use ts_rs::TS;
|
||||
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest,
|
||||
HttpResponse, KeyValue, Plugin, Settings, Workspace,
|
||||
HttpResponse, KeyValue, Settings, Workspace,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
@@ -21,11 +21,8 @@ pub struct InternalEvent {
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export)]
|
||||
pub enum InternalEventPayload {
|
||||
BootRequest(PluginBootRequest),
|
||||
BootResponse(PluginBootResponse),
|
||||
|
||||
ReloadRequest(EmptyResponse),
|
||||
ReloadResponse(EmptyResponse),
|
||||
BootRequest(BootRequest),
|
||||
BootResponse(BootResponse),
|
||||
|
||||
ImportRequest(ImportRequest),
|
||||
ImportResponse(ImportResponse),
|
||||
@@ -74,14 +71,14 @@ pub struct EmptyResponse {}
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct PluginBootRequest {
|
||||
pub struct BootRequest {
|
||||
pub dir: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct PluginBootResponse {
|
||||
pub struct BootResponse {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub capabilities: Vec<String>,
|
||||
@@ -404,5 +401,4 @@ pub enum Model {
|
||||
Workspace(Workspace),
|
||||
CookieJar(CookieJar),
|
||||
Settings(Settings),
|
||||
Plugin(Plugin),
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
use crate::events::{EmptyResponse, InternalEvent, InternalEventPayload, PluginBootResponse};
|
||||
use crate::server::plugin_runtime::EventStreamEvent;
|
||||
use crate::util::gen_id;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginHandle {
|
||||
pub ref_id: String,
|
||||
pub dir: String,
|
||||
pub(crate) to_plugin_tx: Arc<Mutex<mpsc::Sender<tonic::Result<EventStreamEvent>>>>,
|
||||
pub(crate) boot_resp: Arc<Mutex<Option<PluginBootResponse>>>,
|
||||
}
|
||||
|
||||
impl PluginHandle {
|
||||
pub async fn name(&self) -> String {
|
||||
match &*self.boot_resp.lock().await {
|
||||
None => "__NOT_BOOTED__".to_string(),
|
||||
Some(r) => r.name.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn info(&self) -> Option<PluginBootResponse> {
|
||||
let resp = &*self.boot_resp.lock().await;
|
||||
resp.clone()
|
||||
}
|
||||
|
||||
pub fn build_event_to_send(
|
||||
&self,
|
||||
payload: &InternalEventPayload,
|
||||
reply_id: Option<String>,
|
||||
) -> InternalEvent {
|
||||
InternalEvent {
|
||||
id: gen_id(),
|
||||
plugin_ref_id: self.ref_id.clone(),
|
||||
reply_id,
|
||||
payload: payload.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reload(&self) -> crate::error::Result<()> {
|
||||
let event = self.build_event_to_send(&InternalEventPayload::ReloadRequest(EmptyResponse{}), None);
|
||||
self.send(&event).await
|
||||
}
|
||||
|
||||
pub async fn send(&self, event: &InternalEvent) -> crate::error::Result<()> {
|
||||
// info!(
|
||||
// "Sending event to plugin {} {:?}",
|
||||
// event.id,
|
||||
// self.name().await
|
||||
// );
|
||||
self.to_plugin_tx
|
||||
.lock()
|
||||
.await
|
||||
.send(Ok(EventStreamEvent {
|
||||
event: serde_json::to_string(&event)?,
|
||||
}))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn boot(&self, resp: &PluginBootResponse) {
|
||||
let mut boot_resp = self.boot_resp.lock().await;
|
||||
*boot_resp = Some(resp.clone());
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,3 @@ pub mod manager;
|
||||
mod nodejs;
|
||||
pub mod plugin;
|
||||
mod server;
|
||||
pub mod handle;
|
||||
mod util;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::error::Result;
|
||||
use crate::events::{
|
||||
PluginBootResponse, CallHttpRequestActionRequest, CallTemplateFunctionArgs,
|
||||
CallHttpRequestActionRequest, CallTemplateFunctionArgs, RenderPurpose,
|
||||
CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse,
|
||||
GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
|
||||
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
|
||||
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -15,7 +15,6 @@ use std::time::Duration;
|
||||
use tauri::{AppHandle, Runtime};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::watch::Sender;
|
||||
use crate::handle::PluginHandle;
|
||||
|
||||
pub struct PluginManager {
|
||||
kill_tx: Sender<bool>,
|
||||
@@ -39,10 +38,6 @@ impl PluginManager {
|
||||
PluginManager { kill_tx, server }
|
||||
}
|
||||
|
||||
pub async fn reload_all(&self) {
|
||||
self.server.reload_plugins().await
|
||||
}
|
||||
|
||||
pub async fn subscribe(&self) -> (String, mpsc::Receiver<InternalEvent>) {
|
||||
self.server.subscribe().await
|
||||
}
|
||||
@@ -69,14 +64,6 @@ impl PluginManager {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_plugin_info(&self, dir: &str) -> Option<PluginBootResponse> {
|
||||
self.server.plugin_by_dir(dir).await.ok()?.info().await
|
||||
}
|
||||
|
||||
pub async fn get_plugin(&self, ref_id: &str) -> Result<PluginHandle> {
|
||||
self.server.plugin_by_ref_id(ref_id).await
|
||||
}
|
||||
|
||||
pub async fn get_http_request_actions(&self) -> Result<Vec<GetHttpRequestActionsResponse>> {
|
||||
let reply_events = self
|
||||
.server
|
||||
@@ -168,9 +155,7 @@ impl PluginManager {
|
||||
});
|
||||
|
||||
match result {
|
||||
None => Err(PluginErr(
|
||||
"No importers found for file contents".to_string(),
|
||||
)),
|
||||
None => Err(PluginErr("No importers found for file contents".to_string())),
|
||||
Some((resp, ref_id)) => {
|
||||
let plugin = self.server.plugin_by_ref_id(ref_id.as_str()).await?;
|
||||
let plugin_name = plugin.name().await;
|
||||
|
||||
@@ -15,30 +15,21 @@ use tokio::fs::read_dir;
|
||||
use tokio::net::TcpListener;
|
||||
use tonic::codegen::tokio_stream;
|
||||
use tonic::transport::Server;
|
||||
use yaak_models::queries::list_plugins;
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("yaak_plugin_runtime")
|
||||
.setup(|app_handle, _| {
|
||||
let plugins_dir = app_handle
|
||||
.setup(|app, _| {
|
||||
let plugins_dir = app
|
||||
.path()
|
||||
.resolve("plugins", BaseDirectory::Resource)
|
||||
.expect("failed to resolve plugin directory resource");
|
||||
|
||||
tauri::async_runtime::block_on(async move {
|
||||
let bundled_plugin_dirs = read_plugins_dir(&plugins_dir)
|
||||
let plugin_dirs = read_plugins_dir(&plugins_dir)
|
||||
.await
|
||||
.expect(format!("Failed to read plugins dir: {:?}", plugins_dir).as_str());
|
||||
|
||||
let plugins = list_plugins(app_handle).await.unwrap_or_default();
|
||||
let installed_plugin_dirs = plugins
|
||||
.iter()
|
||||
.map(|p| p.directory.to_owned())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let plugin_dirs = [installed_plugin_dirs, bundled_plugin_dirs].concat();
|
||||
let manager = PluginManager::new(&app_handle, plugin_dirs).await;
|
||||
app_handle.manage(manager);
|
||||
.expect("Failed to read plugins dir");
|
||||
let manager = PluginManager::new(&app, plugin_dirs).await;
|
||||
app.manage(manager);
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use log::info;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use log::warn;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tonic::codegen::tokio_stream::wrappers::ReceiverStream;
|
||||
@@ -10,10 +11,8 @@ use tonic::{Request, Response, Status, Streaming};
|
||||
|
||||
use crate::error::Error::PluginNotFoundErr;
|
||||
use crate::error::Result;
|
||||
use crate::events::{InternalEvent, InternalEventPayload, PluginBootRequest, PluginBootResponse};
|
||||
use crate::handle::PluginHandle;
|
||||
use crate::events::{BootRequest, BootResponse, InternalEvent, InternalEventPayload};
|
||||
use crate::server::plugin_runtime::plugin_runtime_server::PluginRuntime;
|
||||
use crate::util::gen_id;
|
||||
use plugin_runtime::EventStreamEvent;
|
||||
use yaak_models::queries::generate_id;
|
||||
|
||||
@@ -24,6 +23,57 @@ pub mod plugin_runtime {
|
||||
type ResponseStream =
|
||||
Pin<Box<dyn Stream<Item = std::result::Result<EventStreamEvent, Status>> + Send>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginHandle {
|
||||
dir: String,
|
||||
to_plugin_tx: Arc<Mutex<mpsc::Sender<tonic::Result<EventStreamEvent>>>>,
|
||||
ref_id: String,
|
||||
boot_resp: Arc<Mutex<Option<BootResponse>>>,
|
||||
}
|
||||
|
||||
impl PluginHandle {
|
||||
pub async fn name(&self) -> String {
|
||||
match &*self.boot_resp.lock().await {
|
||||
None => "__NOT_BOOTED__".to_string(),
|
||||
Some(r) => r.name.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_event_to_send(
|
||||
&self,
|
||||
payload: &InternalEventPayload,
|
||||
reply_id: Option<String>,
|
||||
) -> InternalEvent {
|
||||
InternalEvent {
|
||||
id: gen_id(),
|
||||
plugin_ref_id: self.ref_id.clone(),
|
||||
reply_id,
|
||||
payload: payload.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send(&self, event: &InternalEvent) -> Result<()> {
|
||||
info!(
|
||||
"Sending event to plugin {} {:?}",
|
||||
event.id,
|
||||
self.name().await
|
||||
);
|
||||
self.to_plugin_tx
|
||||
.lock()
|
||||
.await
|
||||
.send(Ok(EventStreamEvent {
|
||||
event: serde_json::to_string(&event)?,
|
||||
}))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn boot(&self, resp: &BootResponse) {
|
||||
let mut boot_resp = self.boot_resp.lock().await;
|
||||
*boot_resp = Some(resp.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginRuntimeGrpcServer {
|
||||
plugin_ref_to_plugin: Arc<Mutex<HashMap<String, PluginHandle>>>,
|
||||
@@ -42,15 +92,6 @@ impl PluginRuntimeGrpcServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn plugins(&self) -> Vec<PluginHandle> {
|
||||
self.plugin_ref_to_plugin
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.map(|p| p.1.to_owned())
|
||||
.collect::<Vec<PluginHandle>>()
|
||||
}
|
||||
|
||||
pub async fn subscribe(&self) -> (String, Receiver<InternalEvent>) {
|
||||
let (tx, rx) = mpsc::channel(128);
|
||||
let rx_id = generate_id();
|
||||
@@ -70,15 +111,23 @@ impl PluginRuntimeGrpcServer {
|
||||
|
||||
pub async fn remove_plugin(&self, id: &str) {
|
||||
match self.plugin_ref_to_plugin.lock().await.remove(id) {
|
||||
None => println!("Tried to remove non-existing plugin {}", id),
|
||||
Some(plugin) => println!("Removed plugin {} {}", id, plugin.name().await),
|
||||
None => {
|
||||
println!("Tried to remove non-existing plugin {}", id);
|
||||
}
|
||||
Some(plugin) => {
|
||||
println!("Removed plugin {} {}", id, plugin.name().await);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn boot_plugin(&self, id: &str, resp: &PluginBootResponse) {
|
||||
pub async fn boot_plugin(&self, id: &str, resp: &BootResponse) {
|
||||
match self.plugin_ref_to_plugin.lock().await.get(id) {
|
||||
None => println!("Tried booting non-existing plugin {}", id),
|
||||
Some(plugin) => plugin.clone().boot(resp).await,
|
||||
None => {
|
||||
println!("Tried booting non-existing plugin {}", id);
|
||||
}
|
||||
Some(plugin) => {
|
||||
plugin.clone().boot(resp).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,27 +151,61 @@ impl PluginRuntimeGrpcServer {
|
||||
plugin_handle
|
||||
}
|
||||
|
||||
// pub async fn callback(
|
||||
// &self,
|
||||
// source_event: InternalEvent,
|
||||
// payload: InternalEventPayload,
|
||||
// ) -> Result<InternalEvent> {
|
||||
// let reply_id = match source_event.clone().reply_id {
|
||||
// None => {
|
||||
// let msg = format!("Source event missing reply Id {:?}", source_event.clone());
|
||||
// return Err(MissingCallbackIdErr(msg));
|
||||
// }
|
||||
// Some(id) => id,
|
||||
// };
|
||||
//
|
||||
// let callbacks = self.callbacks.lock().await;
|
||||
// let plugin_name = match callbacks.get(reply_id.as_str()) {
|
||||
// None => {
|
||||
// let msg = format!("Callback not found {:?}", source_event);
|
||||
// return Err(MissingCallbackErr(msg));
|
||||
// }
|
||||
// Some(n) => n,
|
||||
// };
|
||||
//
|
||||
// let plugins = self.plugins.lock().await;
|
||||
// let plugin = match plugins.get(plugin_name) {
|
||||
// None => {
|
||||
// let msg = format!(
|
||||
// "Plugin not found {plugin_name}. Choices were {:?}",
|
||||
// plugins.keys()
|
||||
// );
|
||||
// return Err(UnknownPluginErr(msg));
|
||||
// }
|
||||
// Some(n) => n,
|
||||
// };
|
||||
//
|
||||
// plugin.send(&payload, Some(reply_id)).await
|
||||
// }
|
||||
|
||||
pub async fn plugin_by_ref_id(&self, ref_id: &str) -> Result<PluginHandle> {
|
||||
let plugins = self.plugin_ref_to_plugin.lock().await;
|
||||
if plugins.is_empty() {
|
||||
return Err(PluginNotFoundErr(ref_id.into()));
|
||||
}
|
||||
|
||||
match plugins.get(ref_id) {
|
||||
None => Err(PluginNotFoundErr(ref_id.into())),
|
||||
Some(p) => Ok(p.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn plugin_by_dir(&self, dir: &str) -> Result<PluginHandle> {
|
||||
let plugins = self.plugin_ref_to_plugin.lock().await;
|
||||
for p in plugins.values() {
|
||||
if p.dir == dir {
|
||||
return Ok(p.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
Err(PluginNotFoundErr(dir.into()))
|
||||
}
|
||||
|
||||
pub async fn plugin_by_name(&self, plugin_name: &str) -> Result<PluginHandle> {
|
||||
let plugins = self.plugin_ref_to_plugin.lock().await;
|
||||
if plugins.is_empty() {
|
||||
return Err(PluginNotFoundErr(plugin_name.into()));
|
||||
}
|
||||
|
||||
for p in plugins.values() {
|
||||
if p.name().await == plugin_name {
|
||||
return Ok(p.to_owned());
|
||||
@@ -246,14 +329,6 @@ impl PluginRuntimeGrpcServer {
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub async fn reload_plugins(&self) {
|
||||
for (_, plugin) in self.plugin_ref_to_plugin.lock().await.clone() {
|
||||
if let Err(e) = plugin.reload().await {
|
||||
warn!("Failed to reload plugin {} {}", plugin.dir, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_plugins(
|
||||
&self,
|
||||
to_plugin_tx: mpsc::Sender<tonic::Result<EventStreamEvent>>,
|
||||
@@ -266,7 +341,7 @@ impl PluginRuntimeGrpcServer {
|
||||
plugin_ids.push(plugin.clone().ref_id);
|
||||
|
||||
let event = plugin.build_event_to_send(
|
||||
&InternalEventPayload::BootRequest(PluginBootRequest {
|
||||
&InternalEventPayload::BootRequest(BootRequest {
|
||||
dir: dir.to_string(),
|
||||
}),
|
||||
None,
|
||||
@@ -351,3 +426,7 @@ impl PluginRuntime for PluginRuntimeGrpcServer {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_id() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 5)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
|
||||
pub fn gen_id() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 5)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems';
|
||||
import type { DropdownProps } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
@@ -8,13 +7,7 @@ interface Props extends Omit<DropdownProps, 'items'> {
|
||||
}
|
||||
|
||||
export function CreateDropdown({ hideFolder, children, ...props }: Props) {
|
||||
const activeRequest = useActiveRequest();
|
||||
const folderId = activeRequest?.folderId ?? null;
|
||||
const items = useCreateDropdownItems({
|
||||
hideFolder,
|
||||
hideIcons: true,
|
||||
folderId,
|
||||
});
|
||||
const items = useCreateDropdownItems({ hideFolder, hideIcons: true });
|
||||
return (
|
||||
<Dropdown items={items} {...props}>
|
||||
{children}
|
||||
|
||||
@@ -89,7 +89,6 @@ export function ExportDataDialog({
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions,jsx-a11y/click-events-have-key-events */}
|
||||
<td
|
||||
className="py-1 pl-4 text whitespace-nowrap overflow-x-auto hide-scrollbars"
|
||||
onClick={() => setSelectedWorkspaces((prev) => ({ ...prev, [w.id]: !prev[w.id] }))}
|
||||
|
||||
@@ -18,7 +18,6 @@ import { httpResponsesQueryKey } from '../hooks/useHttpResponses';
|
||||
import { keyValueQueryKey } from '../hooks/useKeyValue';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { useNotificationToast } from '../hooks/useNotificationToast';
|
||||
import { pluginsAtom } from '../hooks/usePlugins';
|
||||
import { useRecentCookieJars } from '../hooks/useRecentCookieJars';
|
||||
import { useRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||
@@ -68,7 +67,6 @@ export function GlobalHooks() {
|
||||
|
||||
const setSettings = useSetAtom(settingsAtom);
|
||||
const setWorkspaces = useSetAtom(workspacesAtom);
|
||||
const setPlugins = useSetAtom(pluginsAtom);
|
||||
const setHttpRequests = useSetAtom(httpRequestsAtom);
|
||||
const setGrpcRequests = useSetAtom(grpcRequestsAtom);
|
||||
const setEnvironments = useSetAtom(environmentsAtom);
|
||||
@@ -102,8 +100,6 @@ export function GlobalHooks() {
|
||||
|
||||
if (model.model === 'workspace') {
|
||||
setWorkspaces(updateModelList(model, pushToFront));
|
||||
} else if (model.model === 'plugin') {
|
||||
setPlugins(updateModelList(model, pushToFront));
|
||||
} else if (model.model === 'http_request') {
|
||||
setHttpRequests(updateModelList(model, pushToFront));
|
||||
} else if (model.model === 'grpc_request') {
|
||||
@@ -133,8 +129,6 @@ export function GlobalHooks() {
|
||||
|
||||
if (model.model === 'workspace') {
|
||||
setWorkspaces(removeById(model));
|
||||
} else if (model.model === 'plugin') {
|
||||
setPlugins(removeById(model));
|
||||
} else if (model.model === 'http_request') {
|
||||
setHttpRequests(removeById(model));
|
||||
} else if (model.model === 'http_response') {
|
||||
@@ -168,16 +162,15 @@ export function GlobalHooks() {
|
||||
document.documentElement.style.setProperty('--editor-font-size', `${editorFontSize}px`);
|
||||
}, [settings]);
|
||||
|
||||
// Handle Zoom.
|
||||
// Note, Mac handles it in the app menu, so need to also handle keyboard
|
||||
// Handle Zoom. Note, Mac handles it in app menu, so need to also handle keyboard
|
||||
// shortcuts for Windows/Linux
|
||||
const zoom = useZoom();
|
||||
useHotKey('app.zoom_in', zoom.zoomIn);
|
||||
useListenToTauriEvent('zoom_in', zoom.zoomIn);
|
||||
useHotKey('app.zoom_out', zoom.zoomOut);
|
||||
useListenToTauriEvent('zoom_out', zoom.zoomOut);
|
||||
useHotKey('app.zoom_reset', zoom.zoomReset);
|
||||
useListenToTauriEvent('zoom_reset', zoom.zoomReset);
|
||||
useHotKey('app.zoom_in', () => zoom.zoomIn);
|
||||
useListenToTauriEvent('zoom_in', () => zoom.zoomIn);
|
||||
useHotKey('app.zoom_out', () => zoom.zoomOut);
|
||||
useListenToTauriEvent('zoom_out', () => zoom.zoomOut);
|
||||
useHotKey('app.zoom_reset', () => zoom.zoomReset);
|
||||
useListenToTauriEvent('zoom_reset', () => zoom.zoomReset);
|
||||
|
||||
const copy = useCopy();
|
||||
useListenToTauriEvent('generate_theme_css', () => {
|
||||
|
||||
@@ -37,7 +37,6 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
const variables = p.variables;
|
||||
const operationName = p.operationName;
|
||||
return { query, variables, operationName };
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (err) {
|
||||
return { query: '' };
|
||||
}
|
||||
@@ -47,7 +46,6 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
(b: GraphQLBody) => {
|
||||
try {
|
||||
onChange?.(JSON.stringify(b, null, 2));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (err) {
|
||||
// Meh, not much we can do here
|
||||
}
|
||||
@@ -64,7 +62,6 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
(variables: string) => {
|
||||
try {
|
||||
handleChange({ query, variables: JSON.parse(variables || '{}') });
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (err) {
|
||||
// Don't do anything if invalid JSON. The user probably hasn't finished
|
||||
// typing yet.
|
||||
@@ -131,7 +128,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
return (
|
||||
<div className="h-full w-full grid grid-cols-1 grid-rows-[minmax(0,100%)_auto]">
|
||||
<Editor
|
||||
language="graphql"
|
||||
contentType="application/graphql"
|
||||
defaultValue={query ?? ''}
|
||||
format={formatGraphQL}
|
||||
heightMode="auto"
|
||||
@@ -147,7 +144,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
</Separator>
|
||||
<Editor
|
||||
format={tryFormatJson}
|
||||
language="json"
|
||||
contentType="application/json"
|
||||
defaultValue={JSON.stringify(variables, null, 2)}
|
||||
heightMode="auto"
|
||||
onChange={handleChangeVariables}
|
||||
|
||||
@@ -6,10 +6,8 @@ import {
|
||||
handleRefresh,
|
||||
jsonCompletion,
|
||||
jsonSchemaLinter,
|
||||
// eslint-disable-next-line import/named
|
||||
stateExtensions,
|
||||
// eslint-disable-next-line import/named
|
||||
updateSchema,
|
||||
stateExtensions,
|
||||
} from 'codemirror-json-schema';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useAlert } from '../hooks/useAlert';
|
||||
@@ -138,10 +136,10 @@ export function GrpcEditor({
|
||||
reflectionLoading
|
||||
? 'secondary'
|
||||
: reflectionUnavailable
|
||||
? 'info'
|
||||
: reflectionError
|
||||
? 'danger'
|
||||
: 'secondary'
|
||||
? 'info'
|
||||
: reflectionError
|
||||
? 'danger'
|
||||
: 'secondary'
|
||||
}
|
||||
isLoading={reflectionLoading}
|
||||
onClick={() => {
|
||||
@@ -162,14 +160,14 @@ export function GrpcEditor({
|
||||
{reflectionLoading
|
||||
? 'Inspecting Schema'
|
||||
: reflectionUnavailable
|
||||
? 'Select Proto Files'
|
||||
: reflectionError
|
||||
? 'Server Error'
|
||||
: protoFiles.length > 0
|
||||
? count('File', protoFiles.length)
|
||||
: services != null && protoFiles.length === 0
|
||||
? 'Schema Detected'
|
||||
: 'Select Schema'}
|
||||
? 'Select Proto Files'
|
||||
: reflectionError
|
||||
? 'Server Error'
|
||||
: protoFiles.length > 0
|
||||
? count('File', protoFiles.length)
|
||||
: services != null && protoFiles.length === 0
|
||||
? 'Schema Detected'
|
||||
: 'Select Schema'}
|
||||
</Button>
|
||||
</div>,
|
||||
],
|
||||
@@ -187,7 +185,7 @@ export function GrpcEditor({
|
||||
return (
|
||||
<div className="h-full w-full grid grid-cols-1 grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
|
||||
<Editor
|
||||
language="json"
|
||||
contentType="application/json"
|
||||
autocompleteVariables
|
||||
useTemplating
|
||||
forceUpdateKey={request.id}
|
||||
|
||||
@@ -45,9 +45,10 @@ export function GrpcProtoSelection({ requestId }: Props) {
|
||||
multiple: true,
|
||||
filters: [{ name: 'Proto Files', extensions: ['proto'] }],
|
||||
});
|
||||
if (selected == null) return;
|
||||
|
||||
const newFiles = selected.filter((p) => !protoFiles.includes(p));
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
const newFiles = selected.map((f) => f.path).filter((p) => !protoFiles.includes(p));
|
||||
await protoFilesKv.set([...protoFiles, ...newFiles]);
|
||||
await grpc.reflect.refetch();
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,6 @@ import classNames from 'classnames';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import { useIsFullscreen } from '../hooks/useIsFullscreen';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
|
||||
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: ReactNode;
|
||||
@@ -17,22 +16,17 @@ export function HeaderSize({
|
||||
ignoreStoplights,
|
||||
...props
|
||||
}: HeaderSizeProps) {
|
||||
const settings = useSettings();
|
||||
const platform = useOsInfo();
|
||||
const fullscreen = useIsFullscreen();
|
||||
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen && !ignoreStoplights;
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
style={{
|
||||
...style,
|
||||
// Add padding for macOS stoplights, but keep it the same width (account for the interface scale)
|
||||
paddingLeft: stoplightsVisible ? 72 / settings.interfaceScale : undefined,
|
||||
}}
|
||||
style={style}
|
||||
className={classNames(
|
||||
className,
|
||||
'pt-[1px] w-full border-b border-border-subtle min-w-0',
|
||||
stoplightsVisible ? 'pr-1' : 'pl-1',
|
||||
stoplightsVisible ? 'pl-20 pr-1' : 'pl-1',
|
||||
size === 'md' && 'h-[27px]',
|
||||
size === 'lg' && 'h-[38px]',
|
||||
)}
|
||||
|
||||
@@ -40,7 +40,7 @@ export function ImportCurlButton() {
|
||||
await clear(); // Clear the clipboard so the button goes away
|
||||
setClipboardText('');
|
||||
} catch (e) {
|
||||
console.log('Failed to import curl', e);
|
||||
// Nothing
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { HttpResponse } from '@yaakapp/api';
|
||||
import { useCopyHttpResponse } from '../hooks/useCopyHttpResponse';
|
||||
import { useDeleteHttpResponse } from '../hooks/useDeleteHttpResponse';
|
||||
import { useDeleteHttpResponses } from '../hooks/useDeleteHttpResponses';
|
||||
import { useSaveResponse } from '../hooks/useSaveResponse';
|
||||
@@ -26,7 +25,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
||||
const deleteAllResponses = useDeleteHttpResponses(activeResponse?.requestId);
|
||||
const latestResponseId = responses[0]?.id ?? 'n/a';
|
||||
const saveResponse = useSaveResponse(activeResponse);
|
||||
const copyResponse = useCopyHttpResponse(activeResponse);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@@ -39,14 +37,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
||||
hidden: responses.length === 0,
|
||||
disabled: responses.length === 0,
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
label: 'Copy to Clipboard',
|
||||
onSelect: copyResponse.mutate,
|
||||
leftSlot: <Icon icon="copy" />,
|
||||
hidden: responses.length === 0,
|
||||
disabled: responses.length === 0,
|
||||
},
|
||||
{
|
||||
key: 'clear-single',
|
||||
label: 'Delete',
|
||||
|
||||
@@ -7,14 +7,11 @@ import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
||||
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
||||
import { useImportCurl } from '../hooks/useImportCurl';
|
||||
import { useIsResponseLoading } from '../hooks/useIsResponseLoading';
|
||||
import { useImportQuerystring } from '../hooks/useImportQuerystring';
|
||||
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
||||
import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor';
|
||||
import { useRequests } from '../hooks/useRequests';
|
||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||
import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { languageFromContentType } from '../lib/contentType';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
import {
|
||||
AUTH_TYPE_BASIC,
|
||||
@@ -36,7 +33,6 @@ import { CountBadge } from './core/CountBadge';
|
||||
import { Editor } from './core/Editor';
|
||||
import type { GenericCompletionOption } from './core/Editor/genericCompletion';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import type { Pair } from './core/PairEditor';
|
||||
import type { TabItem } from './core/Tabs/Tabs';
|
||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||
import { EmptyStateText } from './EmptyStateText';
|
||||
@@ -55,14 +51,7 @@ interface Props {
|
||||
activeRequest: HttpRequest;
|
||||
}
|
||||
|
||||
const useActiveTab = createGlobalState<Record<string, string>>({});
|
||||
|
||||
const TAB_BODY = 'body';
|
||||
const TAB_PARAMS = 'params';
|
||||
const TAB_HEADERS = 'headers';
|
||||
const TAB_AUTH = 'auth';
|
||||
|
||||
const DEFAULT_TAB = TAB_BODY;
|
||||
const useActiveTab = createGlobalState<string>('body');
|
||||
|
||||
export const RequestPane = memo(function RequestPane({
|
||||
style,
|
||||
@@ -73,10 +62,9 @@ export const RequestPane = memo(function RequestPane({
|
||||
const requests = useRequests();
|
||||
const activeRequestId = activeRequest.id;
|
||||
const updateRequest = useUpdateAnyHttpRequest();
|
||||
const [activeTabs, setActiveTabs] = useActiveTab();
|
||||
const [activeTab, setActiveTab] = useActiveTab();
|
||||
const [forceUpdateHeaderEditorKey, setForceUpdateHeaderEditorKey] = useState<number>(0);
|
||||
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
||||
const [{ urlKey }] = useRequestEditor();
|
||||
const contentType = useContentTypeFromHeaders(activeRequest.headers);
|
||||
|
||||
const handleContentTypeChange = useCallback(
|
||||
@@ -100,27 +88,10 @@ export const RequestPane = memo(function RequestPane({
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const { urlParameterPairs, urlParametersKey } = useMemo(() => {
|
||||
const placeholderNames = Array.from(activeRequest.url.matchAll(/\/(:[^/]+)/g)).map(
|
||||
(m) => m[1] ?? '',
|
||||
);
|
||||
const nonEmptyParameters = activeRequest.urlParameters.filter((p) => p.name || p.value);
|
||||
const items: Pair[] = [...nonEmptyParameters];
|
||||
for (const name of placeholderNames) {
|
||||
const index = items.findIndex((p) => p.name === name);
|
||||
if (index >= 0) {
|
||||
items[index]!.readOnlyName = true;
|
||||
} else {
|
||||
items.push({ name, value: '', enabled: true, readOnlyName: true });
|
||||
}
|
||||
}
|
||||
return { urlParameterPairs: items, urlParametersKey: placeholderNames.join(',') };
|
||||
}, [activeRequest.url, activeRequest.urlParameters]);
|
||||
|
||||
const tabs: TabItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
value: TAB_BODY,
|
||||
value: 'body',
|
||||
options: {
|
||||
value: activeRequest.bodyType,
|
||||
items: [
|
||||
@@ -186,16 +157,16 @@ export const RequestPane = memo(function RequestPane({
|
||||
},
|
||||
},
|
||||
{
|
||||
value: TAB_PARAMS,
|
||||
value: 'params',
|
||||
label: (
|
||||
<div className="flex items-center">
|
||||
Params
|
||||
<CountBadge count={urlParameterPairs.length} />
|
||||
<CountBadge count={activeRequest.urlParameters.filter((p) => p.name).length} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: TAB_HEADERS,
|
||||
value: 'headers',
|
||||
label: (
|
||||
<div className="flex items-center">
|
||||
Headers
|
||||
@@ -204,7 +175,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
),
|
||||
},
|
||||
{
|
||||
value: TAB_AUTH,
|
||||
value: 'auth',
|
||||
label: 'Auth',
|
||||
options: {
|
||||
value: activeRequest.authenticationType,
|
||||
@@ -240,11 +211,11 @@ export const RequestPane = memo(function RequestPane({
|
||||
activeRequest.bodyType,
|
||||
activeRequest.headers,
|
||||
activeRequest.method,
|
||||
activeRequest.urlParameters,
|
||||
activeRequestId,
|
||||
handleContentTypeChange,
|
||||
toast,
|
||||
updateRequest,
|
||||
urlParameterPairs,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -294,22 +265,9 @@ export const RequestPane = memo(function RequestPane({
|
||||
[activeRequestId, updateRequest],
|
||||
);
|
||||
|
||||
const isLoading = useIsResponseLoading(activeRequestId);
|
||||
const { updateKey } = useRequestUpdateKey(activeRequestId);
|
||||
const isLoading = useIsResponseLoading(activeRequestId ?? null);
|
||||
const { updateKey } = useRequestUpdateKey(activeRequestId ?? null);
|
||||
const importCurl = useImportCurl();
|
||||
const importQuerystring = useImportQuerystring(activeRequestId);
|
||||
|
||||
const activeTab = activeTabs[activeRequestId] ?? DEFAULT_TAB;
|
||||
const setActiveTab = useCallback(
|
||||
(tab: string) => {
|
||||
setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab }));
|
||||
},
|
||||
[activeRequest.id, setActiveTabs],
|
||||
);
|
||||
|
||||
useRequestEditorEvent('request_pane.focus_tab', () => {
|
||||
setActiveTab(TAB_PARAMS);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -319,16 +277,15 @@ export const RequestPane = memo(function RequestPane({
|
||||
{activeRequest && (
|
||||
<>
|
||||
<UrlBar
|
||||
key={forceUpdateKey + urlKey}
|
||||
key={forceUpdateKey}
|
||||
url={activeRequest.url}
|
||||
method={activeRequest.method}
|
||||
placeholder="https://example.com"
|
||||
onPaste={(text) => {
|
||||
if (text.startsWith('curl ')) {
|
||||
importCurl.mutate({ overwriteRequestId: activeRequestId, command: text });
|
||||
} else {
|
||||
importQuerystring.mutate(text);
|
||||
onPaste={(command) => {
|
||||
if (!command.startsWith('curl ')) {
|
||||
return;
|
||||
}
|
||||
importCurl.mutate({ overwriteRequestId: activeRequestId, command });
|
||||
}}
|
||||
autocomplete={{
|
||||
minMatch: 3,
|
||||
@@ -342,7 +299,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
({
|
||||
type: 'constant',
|
||||
label: r.url,
|
||||
}) as GenericCompletionOption,
|
||||
} as GenericCompletionOption),
|
||||
),
|
||||
]
|
||||
: [
|
||||
@@ -358,14 +315,13 @@ export const RequestPane = memo(function RequestPane({
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Tabs
|
||||
key={activeRequest.id} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
label="Request"
|
||||
onChangeValue={setActiveTab}
|
||||
tabs={tabs}
|
||||
tabListClassName="mt-2 !mb-1.5"
|
||||
>
|
||||
<TabContent value={TAB_AUTH}>
|
||||
<TabContent value="auth">
|
||||
{activeRequest.authenticationType === AUTH_TYPE_BASIC ? (
|
||||
<BasicAuth key={forceUpdateKey} request={activeRequest} />
|
||||
) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? (
|
||||
@@ -376,21 +332,21 @@ export const RequestPane = memo(function RequestPane({
|
||||
</EmptyStateText>
|
||||
)}
|
||||
</TabContent>
|
||||
<TabContent value={TAB_HEADERS}>
|
||||
<TabContent value="headers">
|
||||
<HeadersEditor
|
||||
forceUpdateKey={`${forceUpdateHeaderEditorKey}::${forceUpdateKey}`}
|
||||
headers={activeRequest.headers}
|
||||
onChange={handleHeadersChange}
|
||||
/>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_PARAMS}>
|
||||
<TabContent value="params">
|
||||
<UrlParametersEditor
|
||||
forceUpdateKey={forceUpdateKey + urlParametersKey}
|
||||
pairs={urlParameterPairs}
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
urlParameters={activeRequest.urlParameters}
|
||||
onChange={handleUrlParametersChange}
|
||||
/>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_BODY}>
|
||||
<TabContent value="body">
|
||||
{activeRequest.bodyType === BODY_TYPE_JSON ? (
|
||||
<Editor
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
@@ -399,7 +355,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
placeholder="..."
|
||||
heightMode={fullHeight ? 'full' : 'auto'}
|
||||
defaultValue={`${activeRequest.body?.text ?? ''}`}
|
||||
language="json"
|
||||
contentType="application/json"
|
||||
onChange={handleBodyTextChange}
|
||||
format={tryFormatJson}
|
||||
/>
|
||||
@@ -411,7 +367,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
placeholder="..."
|
||||
heightMode={fullHeight ? 'full' : 'auto'}
|
||||
defaultValue={`${activeRequest.body?.text ?? ''}`}
|
||||
language="xml"
|
||||
contentType="text/xml"
|
||||
onChange={handleBodyTextChange}
|
||||
/>
|
||||
) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? (
|
||||
@@ -446,7 +402,6 @@ export const RequestPane = memo(function RequestPane({
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
useTemplating
|
||||
autocompleteVariables
|
||||
language={languageFromContentType(contentType)}
|
||||
placeholder="..."
|
||||
heightMode={fullHeight ? 'full' : 'auto'}
|
||||
defaultValue={`${activeRequest.body?.text ?? ''}`}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { HttpRequest } from '@yaakapp/api';
|
||||
import classNames from 'classnames';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { createGlobalState } from 'react-use';
|
||||
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
||||
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
||||
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
||||
import type { HttpRequest } from '@yaakapp/api';
|
||||
import { isResponseLoading } from '../lib/models';
|
||||
import { Banner } from './core/Banner';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
@@ -23,10 +23,11 @@ import { ResponseHeaders } from './ResponseHeaders';
|
||||
import { ResponseInfo } from './ResponseInfo';
|
||||
import { AudioViewer } from './responseViewers/AudioViewer';
|
||||
import { CsvViewer } from './responseViewers/CsvViewer';
|
||||
import { HTMLOrTextViewer } from './responseViewers/HTMLOrTextViewer';
|
||||
import { ImageViewer } from './responseViewers/ImageViewer';
|
||||
import { PdfViewer } from './responseViewers/PdfViewer';
|
||||
import { TextViewer } from './responseViewers/TextViewer';
|
||||
import { VideoViewer } from './responseViewers/VideoViewer';
|
||||
import { WebPageViewer } from './responseViewers/WebPageViewer';
|
||||
|
||||
interface Props {
|
||||
style?: CSSProperties;
|
||||
@@ -34,30 +35,18 @@ interface Props {
|
||||
activeRequest: HttpRequest;
|
||||
}
|
||||
|
||||
const useActiveTab = createGlobalState<Record<string, string>>({});
|
||||
|
||||
const TAB_BODY = 'body';
|
||||
const TAB_HEADERS = 'headers';
|
||||
const TAB_INFO = 'info';
|
||||
const DEFAULT_TAB = TAB_BODY;
|
||||
const useActiveTab = createGlobalState<string>('body');
|
||||
|
||||
export const ResponsePane = memo(function ResponsePane({ style, className, activeRequest }: Props) {
|
||||
const { activeResponse, setPinnedResponseId, responses } = usePinnedHttpResponse(activeRequest);
|
||||
const [viewMode, setViewMode] = useResponseViewMode(activeResponse?.requestId);
|
||||
const [activeTabs, setActiveTabs] = useActiveTab();
|
||||
const [activeTab, setActiveTab] = useActiveTab();
|
||||
const contentType = useContentTypeFromHeaders(activeResponse?.headers ?? null);
|
||||
const activeTab = activeTabs[activeRequest.id] ?? DEFAULT_TAB;
|
||||
const setActiveTab = useCallback(
|
||||
(tab: string) => {
|
||||
setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab }));
|
||||
},
|
||||
[activeRequest.id, setActiveTabs],
|
||||
);
|
||||
|
||||
const tabs = useMemo<TabItem[]>(
|
||||
() => [
|
||||
{
|
||||
value: TAB_BODY,
|
||||
value: 'body',
|
||||
label: 'Preview Mode',
|
||||
options: {
|
||||
value: viewMode,
|
||||
@@ -69,7 +58,6 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
||||
},
|
||||
},
|
||||
{
|
||||
value: TAB_HEADERS,
|
||||
label: (
|
||||
<div className="flex items-center">
|
||||
Headers
|
||||
@@ -78,10 +66,11 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
value: 'headers',
|
||||
},
|
||||
{
|
||||
value: TAB_INFO,
|
||||
label: 'Info',
|
||||
value: 'info',
|
||||
},
|
||||
],
|
||||
[activeResponse?.headers, contentType, setViewMode, viewMode],
|
||||
@@ -154,15 +143,20 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
||||
</Banner>
|
||||
) : (
|
||||
<Tabs
|
||||
key={activeRequest.id} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
onChangeValue={setActiveTab}
|
||||
tabs={tabs}
|
||||
label="Response"
|
||||
tabs={tabs}
|
||||
className="ml-3 mr-3 mb-3"
|
||||
tabListClassName="mt-1.5"
|
||||
>
|
||||
<TabContent value={TAB_BODY}>
|
||||
<TabContent value="headers">
|
||||
<ResponseHeaders response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value="info">
|
||||
<ResponseInfo response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value="body">
|
||||
{!activeResponse.contentLength ? (
|
||||
<div className="pb-2 h-full">
|
||||
<EmptyStateText>Empty Body</EmptyStateText>
|
||||
@@ -177,22 +171,18 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
||||
<PdfViewer response={activeResponse} />
|
||||
) : contentType?.match(/csv|tab-separated/) ? (
|
||||
<CsvViewer className="pb-2" response={activeResponse} />
|
||||
) : viewMode === 'pretty' && contentType?.includes('html') ? (
|
||||
<WebPageViewer response={activeResponse} />
|
||||
) : (
|
||||
// ) : viewMode === 'pretty' && contentType?.includes('json') ? (
|
||||
// <JsonAttributeTree attrValue={activeResponse} />
|
||||
<HTMLOrTextViewer
|
||||
textViewerClassName="-mr-2 bg-surface" // Pull to the right
|
||||
<TextViewer
|
||||
className="-mr-2 bg-surface" // Pull to the right
|
||||
response={activeResponse}
|
||||
pretty={viewMode === 'pretty'}
|
||||
/>
|
||||
)}
|
||||
</TabContent>
|
||||
<TabContent value={TAB_HEADERS}>
|
||||
<ResponseHeaders response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_INFO}>
|
||||
<ResponseInfo response={activeResponse} />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,32 +9,22 @@ import { HStack } from './core/Stacks';
|
||||
type Props = ButtonProps & {
|
||||
onChange: (value: { filePath: string | null; contentType: string | null }) => void;
|
||||
filePath: string | null;
|
||||
directory?: boolean;
|
||||
inline?: boolean;
|
||||
noun?: string;
|
||||
};
|
||||
|
||||
// Special character to insert ltr text in rtl element
|
||||
const rtlEscapeChar = <>‎</>;
|
||||
|
||||
export function SelectFile({
|
||||
onChange,
|
||||
filePath,
|
||||
inline,
|
||||
className,
|
||||
directory,
|
||||
noun,
|
||||
size = 'sm',
|
||||
...props
|
||||
}: Props) {
|
||||
export function SelectFile({ onChange, filePath, inline, className }: Props) {
|
||||
const handleClick = async () => {
|
||||
const filePath = await open({
|
||||
const selected = await open({
|
||||
title: 'Select File',
|
||||
multiple: false,
|
||||
directory,
|
||||
});
|
||||
if (filePath == null) return;
|
||||
const contentType = filePath ? mime.getType(filePath) : null;
|
||||
if (selected == null) return;
|
||||
|
||||
const filePath = selected.path;
|
||||
const contentType = typeof filePath === 'string' && filePath ? mime.getType(filePath) : null;
|
||||
onChange({ filePath, contentType });
|
||||
};
|
||||
|
||||
@@ -42,41 +32,31 @@ export function SelectFile({
|
||||
onChange({ filePath: null, contentType: null });
|
||||
};
|
||||
|
||||
const itemLabel = noun ?? (directory ? 'Folder' : 'File');
|
||||
const selectOrChange = (filePath ? 'Change ' : 'Select ') + itemLabel;
|
||||
|
||||
return (
|
||||
<HStack space={1.5} className="group relative justify-stretch overflow-hidden">
|
||||
<HStack space={1.5} className="group relative justify-stretch">
|
||||
<Button
|
||||
className={classNames(className, 'font-mono text-xs rtl', inline && 'w-full')}
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={handleClick}
|
||||
size={size}
|
||||
{...props}
|
||||
>
|
||||
{rtlEscapeChar}
|
||||
{inline ? filePath || selectOrChange : selectOrChange}
|
||||
{inline ? <>{filePath || 'Select File'}</> : <>Select File</>}
|
||||
</Button>
|
||||
{!inline && (
|
||||
<>
|
||||
{filePath && (
|
||||
<IconButton
|
||||
size={size}
|
||||
size="sm"
|
||||
variant="border"
|
||||
icon="x"
|
||||
title={'Unset ' + itemLabel}
|
||||
title="Unset File"
|
||||
onClick={handleClear}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={classNames(
|
||||
'font-mono truncate rtl pl-1.5 pr-3 text-text',
|
||||
size === 'xs' && 'text-xs',
|
||||
size === 'sm' && 'text-sm',
|
||||
)}
|
||||
>
|
||||
<div className="text-sm font-mono truncate rtl pr-3 text">
|
||||
{rtlEscapeChar}
|
||||
{filePath ?? `No ${itemLabel.toLowerCase()} selected`}
|
||||
{filePath ?? 'No file selected'}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -10,15 +10,13 @@ import { HeaderSize } from '../HeaderSize';
|
||||
import { WindowControls } from '../WindowControls';
|
||||
import { SettingsAppearance } from './SettingsAppearance';
|
||||
import { SettingsGeneral } from './SettingsGeneral';
|
||||
import { SettingsPlugins } from './SettingsPlugins';
|
||||
|
||||
enum Tab {
|
||||
General = 'general',
|
||||
Appearance = 'appearance',
|
||||
Plugins = 'plugins',
|
||||
}
|
||||
|
||||
const tabs = [Tab.General, Tab.Appearance, Tab.Plugins];
|
||||
const tabs = [Tab.General, Tab.Appearance];
|
||||
|
||||
export const Settings = () => {
|
||||
const osInfo = useOsInfo();
|
||||
@@ -60,9 +58,6 @@ export const Settings = () => {
|
||||
<TabContent value={Tab.Appearance} className="pt-3 overflow-y-auto h-full px-4">
|
||||
<SettingsAppearance />
|
||||
</TabContent>
|
||||
<TabContent value={Tab.Plugins} className="pt-3 overflow-y-auto h-full px-4">
|
||||
<SettingsPlugins />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -198,7 +198,7 @@ export function SettingsAppearance() {
|
||||
'};',
|
||||
].join('\n')}
|
||||
heightMode="auto"
|
||||
language="javascript"
|
||||
contentType="application/javascript"
|
||||
/>
|
||||
</VStack>
|
||||
</VStack>
|
||||
|
||||
@@ -117,7 +117,7 @@ export function SettingsDesign() {
|
||||
'};',
|
||||
].join('\n')}
|
||||
heightMode="auto"
|
||||
language="javascript"
|
||||
contentType="application/javascript"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import type { Plugin } from '@yaakapp/api';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import React from 'react';
|
||||
import { useCreatePlugin } from '../../hooks/useCreatePlugin';
|
||||
import { usePluginInfo } from '../../hooks/usePluginInfo';
|
||||
import { usePlugins, useRefreshPlugins } from '../../hooks/usePlugins';
|
||||
import { Button } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import { EmptyStateText } from '../EmptyStateText';
|
||||
import { SelectFile } from '../SelectFile';
|
||||
|
||||
export function SettingsPlugins() {
|
||||
const [directory, setDirectory] = React.useState<string | null>(null);
|
||||
const plugins = usePlugins();
|
||||
const createPlugin = useCreatePlugin();
|
||||
const refreshPlugins = useRefreshPlugins();
|
||||
return (
|
||||
<div className="grid grid-rows-[minmax(0,1fr)_auto] h-full">
|
||||
{plugins.length === 0 ? (
|
||||
<div className="pb-4">
|
||||
<EmptyStateText className="text-center">
|
||||
Plugins extend the functionality of Yaak.
|
||||
<br />
|
||||
Add your first plugin to get started.
|
||||
</EmptyStateText>
|
||||
</div>
|
||||
) : (
|
||||
<table className="w-full text-sm mb-auto min-w-full max-w-full divide-y divide-surface-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th className="py-2 text-left">Plugin</th>
|
||||
<th className="py-2 text-right">Version</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{plugins.map((p) => (
|
||||
<PluginInfo key={p.id} plugin={p} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (directory == null) return;
|
||||
createPlugin.mutate(directory);
|
||||
setDirectory(null);
|
||||
}}
|
||||
>
|
||||
<footer className="grid grid-cols-[minmax(0,1fr)_auto] -mx-4 py-2 px-4 border-t bg-surface-highlight border-border-subtle min-w-0">
|
||||
<SelectFile
|
||||
size="xs"
|
||||
noun="Plugin"
|
||||
directory
|
||||
onChange={({ filePath }) => setDirectory(filePath)}
|
||||
filePath={directory}
|
||||
/>
|
||||
<HStack>
|
||||
{directory && (
|
||||
<Button size="xs" type="submit" color="primary" className="ml-auto">
|
||||
Add Plugin
|
||||
</Button>
|
||||
)}
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="refresh"
|
||||
title="Reload plugins"
|
||||
spin={refreshPlugins.isPending}
|
||||
onClick={() => refreshPlugins.mutate()}
|
||||
/>
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="help"
|
||||
title="View documentation"
|
||||
onClick={() => open('https://feedback.yaak.app/help/articles/6911763-quick-start')}
|
||||
/>
|
||||
</HStack>
|
||||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PluginInfo({ plugin }: { plugin: Plugin }) {
|
||||
const pluginInfo = usePluginInfo(plugin.id);
|
||||
return (
|
||||
<tr className="group">
|
||||
<td className="pr-2">
|
||||
<Checkbox hideLabel checked={true} title="foo" onChange={() => null} />
|
||||
</td>
|
||||
<td className="py-2 select-text cursor-text w-full">
|
||||
<InlineCode>{pluginInfo.data?.name}</InlineCode>
|
||||
</td>
|
||||
<td className="py-2 select-text cursor-text text-right">
|
||||
<InlineCode>{pluginInfo.data?.version}</InlineCode>
|
||||
</td>
|
||||
<td className="py-2 select-text cursor-text pl-2">
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="trash"
|
||||
title="Uninstall plugin"
|
||||
className="text-text-subtlest"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
@@ -243,7 +243,7 @@ export function Sidebar({ className }: Props) {
|
||||
await setHidden(false);
|
||||
}
|
||||
|
||||
// Select 0th index on focus if none selected
|
||||
// Select 0 index on focus if none selected
|
||||
focusActiveRequest(
|
||||
selectedTree != null && selectedId != null
|
||||
? { forced: { id: selectedId, tree: selectedTree } }
|
||||
@@ -310,7 +310,7 @@ export function Sidebar({ className }: Props) {
|
||||
let hoveredIndex = dragIndex + (side === 'above' ? 0 : 1);
|
||||
|
||||
if (hoveredItem?.model === 'folder' && side === 'below' && !isCollapsed(hoveredItem.id)) {
|
||||
// Move into the folder if it's open and we're moving below it
|
||||
// Move into folder if it's open and we're moving below it
|
||||
hoveredTree = hoveredTree?.children.find((n) => n.item.id === id) ?? null;
|
||||
hoveredIndex = 0;
|
||||
}
|
||||
@@ -814,10 +814,11 @@ function SidebarItem({
|
||||
hotKeyAction: 'http_request.duplicate',
|
||||
hotKeyLabelOnly: true, // Would trigger for every request (bad)
|
||||
leftSlot: <Icon icon="copy" />,
|
||||
onSelect: () =>
|
||||
onSelect: () => {
|
||||
itemModel === 'http_request'
|
||||
? duplicateHttpRequest.mutate()
|
||||
: duplicateGrpcRequest.mutate(),
|
||||
: duplicateGrpcRequest.mutate();
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'moveWorkspace',
|
||||
|
||||
@@ -52,16 +52,13 @@ export function TemplateVariableDialog({ hide, onChange, initialTokens }: Props)
|
||||
<VStack space={2}>
|
||||
<Select
|
||||
name="variable"
|
||||
label="Select Variable"
|
||||
label="Variable"
|
||||
value={selectedVariableName}
|
||||
options={variables.map((v) => ({ label: v.name, value: v.name }))}
|
||||
onChange={setSelectedVariableName}
|
||||
/>
|
||||
</VStack>
|
||||
<VStack>
|
||||
<div className="text-sm text-text-subtle">Render Preview</div>
|
||||
<InlineCode className="select-text cursor-text">{rendered.data}</InlineCode>
|
||||
</VStack>
|
||||
<InlineCode className="select-text cursor-text">{rendered.data}</InlineCode>
|
||||
<Button color="primary" onClick={handleDone}>
|
||||
Done
|
||||
</Button>
|
||||
|
||||
@@ -55,8 +55,7 @@ export const UrlBar = memo(function UrlBar({
|
||||
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (isLoading) onCancel();
|
||||
else onSend();
|
||||
isLoading ? onCancel() : onSend();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -68,7 +67,7 @@ export const UrlBar = memo(function UrlBar({
|
||||
wrapLines={isFocused}
|
||||
hideLabel
|
||||
useTemplating
|
||||
language="url"
|
||||
contentType="url"
|
||||
className="pl-0 pr-1.5 py-0.5"
|
||||
name="url"
|
||||
label="Enter URL"
|
||||
|
||||
@@ -1,46 +1,23 @@
|
||||
import type { HttpRequest } from '@yaakapp/api';
|
||||
import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor';
|
||||
import type { PairEditorRef } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useRef } from 'react';
|
||||
|
||||
type Props = {
|
||||
forceUpdateKey: string;
|
||||
pairs: HttpRequest['headers'];
|
||||
urlParameters: HttpRequest['headers'];
|
||||
onChange: (headers: HttpRequest['urlParameters']) => void;
|
||||
};
|
||||
|
||||
export function UrlParametersEditor({ pairs, forceUpdateKey, onChange }: Props) {
|
||||
const pairEditor = useRef<PairEditorRef>(null);
|
||||
const [{ urlParametersKey }] = useRequestEditor();
|
||||
|
||||
useRequestEditorEvent(
|
||||
'request_params.focus_value',
|
||||
(name) => {
|
||||
const pairIndex = pairs.findIndex((p) => p.name === name);
|
||||
if (pairIndex >= 0) {
|
||||
pairEditor.current?.focusValue(pairIndex);
|
||||
} else {
|
||||
console.log("Couldn't find pair to focus", { name, pairs });
|
||||
}
|
||||
},
|
||||
[pairs],
|
||||
);
|
||||
|
||||
export function UrlParametersEditor({ urlParameters, forceUpdateKey, onChange }: Props) {
|
||||
return (
|
||||
<VStack className="h-full">
|
||||
<PairOrBulkEditor
|
||||
ref={pairEditor}
|
||||
preferenceName="url_parameters"
|
||||
valueAutocompleteVariables
|
||||
nameAutocompleteVariables
|
||||
namePlaceholder="param_name"
|
||||
valuePlaceholder="Value"
|
||||
pairs={pairs}
|
||||
onChange={onChange}
|
||||
forceUpdateKey={forceUpdateKey + urlParametersKey}
|
||||
/>
|
||||
</VStack>
|
||||
<PairOrBulkEditor
|
||||
preferenceName="url_parameters"
|
||||
valueAutocompleteVariables
|
||||
nameAutocompleteVariables
|
||||
namePlaceholder="param_name"
|
||||
valuePlaceholder="Value"
|
||||
pairs={urlParameters}
|
||||
onChange={onChange}
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function WindowControls({ className, onlyX }: Props) {
|
||||
)}
|
||||
<Button
|
||||
color="custom"
|
||||
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-danger hocus:text"
|
||||
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-fg-danger hocus:text"
|
||||
onClick={() => getCurrentWebviewWindow().close()}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
|
||||
@@ -14,9 +14,8 @@ export function Banner({ children, className, color = 'secondary' }: Props) {
|
||||
className={classNames(
|
||||
className,
|
||||
`x-theme-banner--${color}`,
|
||||
'border border-dashed border-border-subtle bg-surface-highlight',
|
||||
'italic px-3 py-2 rounded select-auto cursor-text',
|
||||
'overflow-x-auto text-text',
|
||||
'border border-dashed italic px-3 py-2 rounded select-auto cursor-text',
|
||||
'border-border-subtle bg-surface-highlight text',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -36,19 +36,18 @@ export function BulkPairEditor({
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
placeholder={`${namePlaceholder ?? 'name'}: ${valuePlaceholder ?? 'value'}`}
|
||||
defaultValue={pairsText}
|
||||
language="pairs"
|
||||
contentType="pairs"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function lineToPair(line: string): PairEditorProps['pairs'][0] {
|
||||
const [, name, value] = line.match(/^(:?[^:]+):\s+([^$]*)/) ?? [];
|
||||
|
||||
function lineToPair(l: string): PairEditorProps['pairs'][0] {
|
||||
const [name, ...values] = l.split(':');
|
||||
const pair: PairEditorProps['pairs'][0] = {
|
||||
enabled: true,
|
||||
name: (name ?? '').trim(),
|
||||
value: (value ?? '').trim(),
|
||||
value: values.join(':').trim(),
|
||||
};
|
||||
return pair;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
||||
size === 'md' && 'h-md px-3 rounded-md',
|
||||
size === 'sm' && 'h-sm px-2.5 rounded-md',
|
||||
size === 'xs' && 'h-xs px-2 text-sm rounded-md',
|
||||
size === '2xs' && 'h-2xs px-1 text-xs rounded',
|
||||
size === '2xs' && 'h-5 px-1 text-xs rounded',
|
||||
|
||||
// Solids
|
||||
variant === 'solid' && 'border-transparent',
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
}
|
||||
|
||||
.cm-line {
|
||||
@apply w-full;
|
||||
/* Important! Ensure it spans the entire width */
|
||||
@apply w-full; /* Important! Ensure it spans the entire width */
|
||||
@apply w-full text-text pl-1 pr-1.5;
|
||||
}
|
||||
|
||||
@@ -76,10 +75,6 @@
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
&:hover > * {
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
-webkit-text-security: none;
|
||||
}
|
||||
}
|
||||
@@ -174,14 +169,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Cursor and mouse cursor for readonly mode */
|
||||
.cm-wrapper.cm-readonly {
|
||||
.cm-editor .cm-cursor {
|
||||
@apply hidden !important;
|
||||
}
|
||||
|
||||
&.cm-singleline .cm-line {
|
||||
@apply cursor-default;
|
||||
.cm-wrapper.cm-readonly .cm-editor {
|
||||
.cm-cursor {
|
||||
@apply border-danger !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +201,7 @@
|
||||
}
|
||||
|
||||
&::after {
|
||||
@apply text-text bg-text h-3 w-3 ml-1;
|
||||
@apply text-text bg-surface-highlight h-3 w-3 ml-1;
|
||||
content: '';
|
||||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='black' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-size: contain;
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
} from 'react';
|
||||
import { useActiveEnvironmentVariables } from '../../../hooks/useActiveEnvironmentVariables';
|
||||
import { parseTemplate } from '../../../hooks/useParseTemplate';
|
||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||
import { useSettings } from '../../../hooks/useSettings';
|
||||
import { useTemplateFunctions } from '../../../hooks/useTemplateFunctions';
|
||||
import { useDialog } from '../../DialogContext';
|
||||
@@ -40,11 +39,10 @@ export { formatSdl } from 'format-graphql';
|
||||
export interface EditorProps {
|
||||
id?: string;
|
||||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
type?: 'text' | 'password';
|
||||
className?: string;
|
||||
heightMode?: 'auto' | 'full';
|
||||
language?: 'javascript' | 'json' | 'html' | 'xml' | 'graphql' | 'url' | 'pairs' | 'text';
|
||||
contentType?: string | null;
|
||||
forceUpdateKey?: string | number;
|
||||
autoFocus?: boolean;
|
||||
autoSelect?: boolean;
|
||||
@@ -73,7 +71,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
readOnly,
|
||||
type = 'text',
|
||||
heightMode,
|
||||
language = 'text',
|
||||
contentType,
|
||||
autoFocus,
|
||||
autoSelect,
|
||||
placeholder,
|
||||
@@ -188,7 +186,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
dialog.show({
|
||||
size: 'dynamic',
|
||||
id: 'template-variable',
|
||||
title: 'Change Variable',
|
||||
title: 'Configure Variable',
|
||||
render: ({ hide }) => (
|
||||
<TemplateVariableDialog
|
||||
hide={hide}
|
||||
@@ -228,20 +226,12 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
[dialog],
|
||||
);
|
||||
|
||||
const [, { focusParamValue }] = useRequestEditor();
|
||||
const onClickPathParameter = useCallback(
|
||||
async (name: string) => {
|
||||
focusParamValue(name);
|
||||
},
|
||||
[focusParamValue],
|
||||
);
|
||||
|
||||
// Update the language extension when the language changes
|
||||
// Update language extension when contentType changes
|
||||
useEffect(() => {
|
||||
if (cm.current === null) return;
|
||||
const { view, languageCompartment } = cm.current;
|
||||
const ext = getLanguageExtension({
|
||||
language,
|
||||
contentType,
|
||||
environmentVariables,
|
||||
useTemplating,
|
||||
autocomplete,
|
||||
@@ -249,11 +239,10 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
onClickFunction,
|
||||
onClickVariable,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
});
|
||||
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
|
||||
}, [
|
||||
language,
|
||||
contentType,
|
||||
autocomplete,
|
||||
useTemplating,
|
||||
environmentVariables,
|
||||
@@ -261,7 +250,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
onClickFunction,
|
||||
onClickVariable,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
]);
|
||||
|
||||
// Initialize the editor when ref mounts
|
||||
@@ -277,7 +265,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
try {
|
||||
const languageCompartment = new Compartment();
|
||||
const langExt = getLanguageExtension({
|
||||
language,
|
||||
contentType,
|
||||
useTemplating,
|
||||
autocomplete,
|
||||
environmentVariables,
|
||||
@@ -285,7 +273,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
onClickVariable,
|
||||
onClickFunction,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
});
|
||||
|
||||
const state = EditorState.create({
|
||||
@@ -474,6 +461,6 @@ function getExtensions({
|
||||
|
||||
const placeholderElFromText = (text: string) => {
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = text.replaceAll('\n', '<br/>');
|
||||
el.innerHTML = text.replace('\n', '<br/>');
|
||||
return el;
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
} from '@codemirror/view';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import type { EnvironmentVariable, TemplateFunction } from '@yaakapp/api';
|
||||
import { graphql } from 'cm6-graphql';
|
||||
import { graphql, graphqlLanguageSupport } from 'cm6-graphql';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { EditorProps } from './index';
|
||||
import { pairs } from './pairs/extension';
|
||||
@@ -46,10 +46,6 @@ export const syntaxHighlightStyle = HighlightStyle.define([
|
||||
color: 'var(--textSubtlest)',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
{
|
||||
tag: [t.emphasis],
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
{
|
||||
tag: [t.paren, t.bracket, t.brace],
|
||||
color: 'var(--textSubtle)',
|
||||
@@ -68,19 +64,19 @@ export const syntaxHighlightStyle = HighlightStyle.define([
|
||||
|
||||
const syntaxTheme = EditorView.theme({}, { dark: true });
|
||||
|
||||
const syntaxExtensions: Record<NonNullable<EditorProps['language']>, LanguageSupport | null> = {
|
||||
graphql: null,
|
||||
json: json(),
|
||||
javascript: javascript(),
|
||||
html: xml(), // HTML as XML because HTML is oddly slow
|
||||
xml: xml(),
|
||||
const syntaxExtensions: Record<string, LanguageSupport> = {
|
||||
'application/graphql': graphqlLanguageSupport(),
|
||||
'application/json': json(),
|
||||
'application/javascript': javascript(),
|
||||
'text/html': xml(), // HTML as XML because HTML is oddly slow
|
||||
'application/xml': xml(),
|
||||
'text/xml': xml(),
|
||||
url: url(),
|
||||
pairs: pairs(),
|
||||
text: text(),
|
||||
};
|
||||
|
||||
export function getLanguageExtension({
|
||||
language,
|
||||
contentType,
|
||||
useTemplating = false,
|
||||
environmentVariables,
|
||||
autocomplete,
|
||||
@@ -88,20 +84,18 @@ export function getLanguageExtension({
|
||||
onClickVariable,
|
||||
onClickFunction,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
}: {
|
||||
environmentVariables: EnvironmentVariable[];
|
||||
templateFunctions: TemplateFunction[];
|
||||
onClickFunction: (option: TemplateFunction, tagValue: string, startPos: number) => void;
|
||||
onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void;
|
||||
onClickPathParameter: (name: string) => void;
|
||||
} & Pick<EditorProps, 'language' | 'useTemplating' | 'autocomplete'>) {
|
||||
if (language === 'graphql') {
|
||||
} & Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'>) {
|
||||
const justContentType = contentType?.split(';')[0] ?? contentType ?? '';
|
||||
if (justContentType === 'application/graphql') {
|
||||
return graphql();
|
||||
}
|
||||
|
||||
const base = syntaxExtensions[language ?? 'text'] ?? text();
|
||||
const base = syntaxExtensions[justContentType] ?? text();
|
||||
if (!useTemplating) {
|
||||
return base;
|
||||
}
|
||||
@@ -114,7 +108,6 @@ export function getLanguageExtension({
|
||||
onClickFunction,
|
||||
onClickVariable,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Decoration, hoverTooltip, MatchDecorator, ViewPlugin } from '@codemirro
|
||||
import { EditorView } from 'codemirror';
|
||||
|
||||
const REGEX =
|
||||
/(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+*~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+*.~#?&/=]*))/g;
|
||||
/(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*))/g;
|
||||
|
||||
const tooltip = hoverTooltip(
|
||||
(view, pos, side) => {
|
||||
@@ -43,7 +43,7 @@ const tooltip = hoverTooltip(
|
||||
};
|
||||
},
|
||||
{
|
||||
hoverTime: 150,
|
||||
hoverTime: 100,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@top pairs { (Key Sep Value "\n")* }
|
||||
@top pairs { (Key? Sep Value)* }
|
||||
|
||||
@tokens {
|
||||
Sep { ":" }
|
||||
Key { ":"? ![:]+ }
|
||||
Key { ![:]+ }
|
||||
Value { ![\n]+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
pairs = 1,
|
||||
Key = 2,
|
||||
Sep = 3,
|
||||
Value = 4
|
||||
@@ -3,17 +3,17 @@ import {LRParser} from "@lezer/lr"
|
||||
import {highlight} from "./highlight"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "zQQOPOOOVOQO'#CaQQOPOOO[OSO,58{OOOO-E6_-E6_OaOQO1G.gOOOO7+$R7+$R",
|
||||
stateData: "f~OQPO~ORRO~OSTO~OVUO~O",
|
||||
goto: "]UPPPPPVQQORSQ",
|
||||
states: "!QQQOPOOOYOQO'#CaO_OPO'#CaQQOPOOOOOO,58{,58{OdOQO,58{OOOO-E6_-E6_OOOO1G.g1G.g",
|
||||
stateData: "i~OQQORPO~OSSO~ORTO~OSVO~O",
|
||||
goto: "]UPPPPPVQRORUR",
|
||||
nodeNames: "⚠ pairs Key Sep Value",
|
||||
maxTerm: 7,
|
||||
maxTerm: 6,
|
||||
propSources: [highlight],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 1,
|
||||
tokenData: "$]VRVOYhYZ#[Z![h![!]#o!];'Sh;'S;=`#U<%lOhToVQPSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOhP!ZSQPO![!U!];'S!U;'S;=`!g<%lO!UP!jP;=`<%l!US!rSSSOY!mZ;'S!m;'S;=`#O<%lO!mS#RP;=`<%l!mT#XP;=`<%lhR#cSVQQPO![!U!];'S!U;'S;=`!g<%lO!UV#vVRQSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOh",
|
||||
tokenizers: [0, 1, 2],
|
||||
tokenData: "#oRRVOYhYZ!UZ![h![!]#[!];'Sh;'S;=`#U<%lOhRoVQPSQOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOhP!ZSQPO![!U!];'S!U;'S;=`!g<%lO!UP!jP;=`<%l!UQ!rSSQOY!mZ;'S!m;'S;=`#O<%lO!mQ#RP;=`<%l!mR#XP;=`<%lhR#cSRPSQOY!mZ;'S!m;'S;=`#O<%lO!m",
|
||||
tokenizers: [0, 1],
|
||||
topRules: {"pairs":[0,1]},
|
||||
tokenPrec: 0,
|
||||
termNames: {"0":"⚠","1":"@top","2":"Key","3":"Sep","4":"Value","5":"(Key Sep Value \"\\n\")+","6":"␄","7":"\"\\n\""}
|
||||
tokenPrec: 0
|
||||
})
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ export function twig({
|
||||
onClickFunction,
|
||||
onClickVariable,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
}: {
|
||||
base: LanguageSupport;
|
||||
environmentVariables: EnvironmentVariable[];
|
||||
@@ -27,7 +26,6 @@ export function twig({
|
||||
onClickFunction: (option: TemplateFunction, tagValue: string, startPos: number) => void;
|
||||
onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void;
|
||||
onClickPathParameter: (name: string) => void;
|
||||
}) {
|
||||
const language = mixLanguage(base);
|
||||
|
||||
@@ -64,11 +62,11 @@ export function twig({
|
||||
return [
|
||||
language,
|
||||
base.support,
|
||||
templateTagsPlugin(options, onClickMissingVariable),
|
||||
language.data.of({ autocomplete: completions }),
|
||||
base.language.data.of({ autocomplete: completions }),
|
||||
language.data.of({ autocomplete: genericCompletion(autocomplete) }),
|
||||
base.language.data.of({ autocomplete: genericCompletion(autocomplete) }),
|
||||
templateTagsPlugin(options, onClickMissingVariable, onClickPathParameter),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,42 +2,9 @@ import { syntaxTree } from '@codemirror/language';
|
||||
import type { Range } from '@codemirror/state';
|
||||
import type { DecorationSet, ViewUpdate } from '@codemirror/view';
|
||||
import { Decoration, ViewPlugin, WidgetType } from '@codemirror/view';
|
||||
import type { SyntaxNodeRef } from '@lezer/common';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { TwigCompletionOption } from './completion';
|
||||
|
||||
class PathPlaceholderWidget extends WidgetType {
|
||||
readonly #clickListenerCallback: () => void;
|
||||
|
||||
constructor(readonly rawText: string, readonly startPos: number, readonly onClick: () => void) {
|
||||
super();
|
||||
this.#clickListenerCallback = () => {
|
||||
this.onClick?.();
|
||||
};
|
||||
}
|
||||
|
||||
eq(other: PathPlaceholderWidget) {
|
||||
return this.startPos === other.startPos && this.rawText === other.rawText;
|
||||
}
|
||||
|
||||
toDOM() {
|
||||
const elt = document.createElement('span');
|
||||
elt.className = `x-theme-templateTag x-theme-templateTag--secondary template-tag`;
|
||||
elt.textContent = this.rawText;
|
||||
elt.addEventListener('click', this.#clickListenerCallback);
|
||||
return elt;
|
||||
}
|
||||
|
||||
destroy(dom: HTMLElement) {
|
||||
dom.removeEventListener('click', this.#clickListenerCallback);
|
||||
super.destroy(dom);
|
||||
}
|
||||
|
||||
ignoreEvent() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateTagWidget extends WidgetType {
|
||||
readonly #clickListenerCallback: () => void;
|
||||
|
||||
@@ -95,40 +62,20 @@ function templateTags(
|
||||
view: EditorView,
|
||||
options: TwigCompletionOption[],
|
||||
onClickMissingVariable: (name: string, rawTag: string, startPos: number) => void,
|
||||
onClickPathParameter: (name: string) => void,
|
||||
): DecorationSet {
|
||||
const widgets: Range<Decoration>[] = [];
|
||||
for (const { from, to } of view.visibleRanges) {
|
||||
const tree = syntaxTree(view.state);
|
||||
tree.iterate({
|
||||
syntaxTree(view.state).iterate({
|
||||
from,
|
||||
to,
|
||||
enter(node) {
|
||||
if (node.name === 'Text') {
|
||||
// Find the `url` node and then jump into it to find the placeholders
|
||||
for (let i = node.from; i < node.to; i++) {
|
||||
const innerTree = syntaxTree(view.state).resolveInner(i);
|
||||
if (innerTree.node.name === 'url') {
|
||||
innerTree.toTree().iterate({
|
||||
enter(node) {
|
||||
if (node.name !== 'Placeholder') return;
|
||||
if (isSelectionInsideNode(view, node)) return;
|
||||
|
||||
const globalFrom = innerTree.node.from + node.from;
|
||||
const globalTo = innerTree.node.from + node.to;
|
||||
const rawText = view.state.doc.sliceString(globalFrom, globalTo);
|
||||
const onClick = () => onClickPathParameter(rawText);
|
||||
const widget = new PathPlaceholderWidget(rawText, globalFrom, onClick);
|
||||
const deco = Decoration.replace({ widget, inclusive: false });
|
||||
widgets.push(deco.range(globalFrom, globalTo));
|
||||
},
|
||||
});
|
||||
break;
|
||||
if (node.name == 'Tag') {
|
||||
// Don't decorate if the cursor is inside the match
|
||||
for (const r of view.state.selection.ranges) {
|
||||
if (r.from > node.from && r.to < node.to) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (node.name === 'Tag') {
|
||||
// Don't decorate if the cursor is inside the match
|
||||
if (isSelectionInsideNode(view, node)) return;
|
||||
|
||||
const rawTag = view.state.doc.sliceString(node.from, node.to);
|
||||
|
||||
@@ -167,28 +114,17 @@ function templateTags(
|
||||
export function templateTagsPlugin(
|
||||
options: TwigCompletionOption[],
|
||||
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void,
|
||||
onClickPathParameter: (name: string) => void,
|
||||
) {
|
||||
return ViewPlugin.fromClass(
|
||||
class {
|
||||
decorations: DecorationSet;
|
||||
|
||||
constructor(view: EditorView) {
|
||||
this.decorations = templateTags(
|
||||
view,
|
||||
options,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
);
|
||||
this.decorations = templateTags(view, options, onClickMissingVariable);
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
this.decorations = templateTags(
|
||||
update.view,
|
||||
options,
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
);
|
||||
this.decorations = templateTags(update.view, options, onClickMissingVariable);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -200,13 +136,13 @@ export function templateTagsPlugin(
|
||||
return view.plugin(plugin)?.decorations || Decoration.none;
|
||||
});
|
||||
},
|
||||
eventHandlers: {
|
||||
mousedown(e) {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target.classList.contains('template-tag')) console.log('CLICKED TEMPLATE TAG');
|
||||
// return toggleBoolean(view, view.posAtDOM(target));
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function isSelectionInsideNode(view: EditorView, node: SyntaxNodeRef) {
|
||||
for (const r of view.state.selection.ranges) {
|
||||
if (r.from > node.from && r.to < node.to) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
Template = 1,
|
||||
export const Template = 1,
|
||||
Tag = 2,
|
||||
TagOpen = 3,
|
||||
TagContent = 4,
|
||||
TagClose = 5,
|
||||
Text = 6
|
||||
Open = 3,
|
||||
Close = 5,
|
||||
Text = 6;
|
||||
|
||||
@@ -16,3 +16,4 @@ export const parser = LRParser.deserialize({
|
||||
topRules: {"Template":[0,1]},
|
||||
tokenPrec: 0
|
||||
})
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import { styleTags, tags as t } from '@lezer/highlight';
|
||||
|
||||
export const highlight = styleTags({
|
||||
Protocol: t.comment,
|
||||
Placeholder: t.emphasis,
|
||||
// PathSegment: t.tagName,
|
||||
// Port: t.attributeName,
|
||||
// Host: t.variableName,
|
||||
// Path: t.bool,
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
@top url { Protocol? Host Port? Path? Query? }
|
||||
|
||||
Path { ("/" (Placeholder | PathSegment))+ }
|
||||
|
||||
Query { "?" queryPair ("&" queryPair)* }
|
||||
Query {
|
||||
"?" queryPair ("&" queryPair)*
|
||||
}
|
||||
|
||||
@tokens {
|
||||
Protocol { $[a-zA-Z]+ "://" }
|
||||
Host { $[a-zA-Z0-9-_.]+ }
|
||||
Port { ":" $[0-9]+ }
|
||||
Placeholder { ":" ![/?#]+ }
|
||||
PathSegment { ![?#/]+ }
|
||||
Path { ("/" $[a-zA-Z0-9\-_.]*)+ }
|
||||
queryPair { ($[a-zA-Z0-9]+ ("=" $[a-zA-Z0-9]*)?) }
|
||||
Port { ":" $[0-9]+ }
|
||||
Host { $[a-zA-Z0-9-_.]+ }
|
||||
|
||||
// Protocol/host overlaps, so give proto explicit precedence
|
||||
@precedence { Protocol, Host }
|
||||
@precedence { Placeholder, PathSegment }
|
||||
}
|
||||
|
||||
@external propSource highlight from "./highlight"
|
||||
|
||||
@@ -5,6 +5,4 @@ export const
|
||||
Host = 3,
|
||||
Port = 4,
|
||||
Path = 5,
|
||||
Placeholder = 6,
|
||||
PathSegment = 7,
|
||||
Query = 8
|
||||
Query = 6
|
||||
|
||||
@@ -3,17 +3,16 @@ import {LRParser} from "@lezer/lr"
|
||||
import {highlight} from "./highlight"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "#SOQOPOOQYOPOOOTOPOOOeOQO'#CeOmOPO'#CaOxOSO'#CdQOOOOOQ`OPOOQ]OPOOOOOO,59P,59POOOO-E6c-E6cO}OPO,59OO!VOSO'#CfO![OPO1G.jOOOO,59Q,59QOOOO-E6d-E6d",
|
||||
stateData: "!j~OQQORPO~OSWO[RO]TO~OUXOVXO~O[ROZTX]TX~O^ZO~O_[OZWa~O^^O~O_[OZWi~OQRUVU~",
|
||||
goto: "rZPPPPP[PP`elTVPWVUPVWSSPWRYSQ]ZR_]",
|
||||
nodeNames: "⚠ url Protocol Host Port Path Placeholder PathSegment Query",
|
||||
maxTerm: 15,
|
||||
states: "!jOQOPOOQYOPOOOTOPOOOeOQO'#CbQOOOOOQ`OPOOQ]OPOOOjOPO,58|OrOQO'#CcOwOPO1G.hOOOO,58},58}OOOO-E6a-E6a",
|
||||
stateData: "!S~OQQORPO~OSUOTTOXRO~OYVO~OZWOWUa~OYYO~OZWOWUi~OQR~",
|
||||
goto: "dWPPPPPPX^VSPTUQXVRZX",
|
||||
nodeNames: "⚠ url Protocol Host Port Path Query",
|
||||
maxTerm: 11,
|
||||
propSources: [highlight],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 2,
|
||||
tokenData: "+U~RdOs!atv!avw#Ow}!a}!O#i!O!P#i!P!Q$o!Q![$t![!]&|!]!a!a!a!b(w!b!c!a!c!}(|!}#R!a#R#S#i#S#T!a#T#o(|#o;'S!a;'S;=`!x<%lO!aQ!fUVQOs!at!P!a!Q!a!a!b;'S!a;'S;=`!x<%lO!aQ!{P;=`<%l!aR#VU_PVQOs!at!P!a!Q!a!a!b;'S!a;'S;=`!x<%lO!aR#p_RPVQOs!at}!a}!O#i!O!P#i!Q![#i![!a!a!b!c!a!c!}#i!}#R!a#R#S#i#S#T!a#T#o#i#o;'S!a;'S;=`!x<%lO!a~$tO[~V$}a^SRPVQOs!at}!a}!O#i!O!P#i!Q![$t![!_!a!_!`&S!`!a!a!b!c!a!c!}$t!}#R!a#R#S#i#S#T!a#T#o$t#o;'S!a;'S;=`!x<%lO!aU&ZZ^SVQOs!at!P!a!Q![&S![!a!a!b!c!a!c!}&S!}#T!a#T#o&S#o;'S!a;'S;=`!x<%lO!aR'RVVQOs'ht!P'h!Q![(X![!a'h!b;'S'h;'S;=`(R<%lO'hQ'oUUQVQOs'ht!P'h!Q!a'h!b;'S'h;'S;=`(R<%lO'hQ(UP;=`<%l'hR(bVSPUQVQOs'ht!P'h!Q![(X![!a'h!b;'S'h;'S;=`(R<%lO'h~(|O]~V)Vb^SRPVQOs!at}!a}!O#i!O!P#i!Q![$t![!]*_!]!_!a!_!`&S!`!a!a!b!c!a!c!}(|!}#R!a#R#S#i#S#T!a#T#o(|#o;'S!a;'S;=`!x<%lO!aR*dVVQOs!at!P!a!P!Q*y!Q!a!a!b;'S!a;'S;=`!x<%lO!aP*|P!P!Q+PP+UOQP",
|
||||
tokenizers: [0, 1, 2],
|
||||
repeatNodeCount: 1,
|
||||
tokenData: "%[~RYvwq}!Ov!O!Pv!P!Q!_!Q![!y![!]#u!a!b$T!c!}$Y#R#Sv#T#o$Y~vOZ~P{URP}!Ov!O!Pv!Q![v!c!}v#R#Sv#T#ov~!dVT~}!O!_!O!P!_!P!Q!_!Q![!_!c!}!_#R#S!_#T#o!_R#QVYQRP}!Ov!O!Pv!Q![!y!_!`#g!c!}!y#R#Sv#T#o!yQ#lRYQ!Q![#g!c!}#g#T#o#g~#xP!Q![#{~$QPS~!Q![#{~$YOX~R$aWYQRP}!Ov!O!Pv!Q![!y![!]$y!_!`#g!c!}$Y#R#Sv#T#o$YP$|P!P!Q%PP%SP!P!Q%VP%[OQP",
|
||||
tokenizers: [0, 1],
|
||||
topRules: {"url":[0,1]},
|
||||
tokenPrec: 66,
|
||||
termNames: {"0":"⚠","1":"@top","2":"Protocol","3":"Host","4":"Port","5":"Path","6":"Placeholder","7":"PathSegment","8":"Query","9":"(\"/\" (Placeholder | PathSegment))+","10":"(\"&\" queryPair)+","11":"␄","12":"\"/\"","13":"\"?\"","14":"queryPair","15":"\"&\""}
|
||||
tokenPrec: 47
|
||||
})
|
||||
|
||||
@@ -39,7 +39,6 @@ const icons = {
|
||||
folderOutput: lucide.FolderOutputIcon,
|
||||
gripVertical: lucide.GripVerticalIcon,
|
||||
hand: lucide.HandIcon,
|
||||
help: lucide.CircleHelpIcon,
|
||||
house: lucide.HomeIcon,
|
||||
info: lucide.InfoIcon,
|
||||
keyboard: lucide.KeyboardIcon,
|
||||
@@ -55,6 +54,7 @@ const icons = {
|
||||
plug: lucide.Plug,
|
||||
plus: lucide.PlusIcon,
|
||||
plusCircle: lucide.PlusCircleIcon,
|
||||
question: lucide.ShieldQuestionIcon,
|
||||
refresh: lucide.RefreshCwIcon,
|
||||
save: lucide.SaveIcon,
|
||||
search: lucide.SearchIcon,
|
||||
@@ -70,7 +70,6 @@ const icons = {
|
||||
update: lucide.RefreshCcwIcon,
|
||||
upload: lucide.UploadIcon,
|
||||
x: lucide.XIcon,
|
||||
_unknown: lucide.ShieldAlertIcon,
|
||||
|
||||
empty: (props: HTMLAttributes<HTMLSpanElement>) => <span {...props} />,
|
||||
};
|
||||
@@ -84,7 +83,7 @@ export interface IconProps {
|
||||
}
|
||||
|
||||
export const Icon = memo(function Icon({ icon, spin, size = 'md', className, title }: IconProps) {
|
||||
const Component = icons[icon] ?? icons._unknown;
|
||||
const Component = icons[icon] ?? icons.question;
|
||||
return (
|
||||
<Component
|
||||
title={title}
|
||||
|
||||
@@ -58,9 +58,9 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButt
|
||||
'!px-0',
|
||||
color === 'custom' && 'text-text-subtle',
|
||||
color === 'default' && 'text-text-subtle',
|
||||
size === 'md' && 'w-md',
|
||||
size === 'sm' && 'w-sm',
|
||||
size === 'xs' && 'w-xs',
|
||||
size === 'md' && 'w-9',
|
||||
size === 'sm' && 'w-8',
|
||||
size === 'xs' && 'w-6',
|
||||
size === '2xs' && 'w-5',
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -6,7 +6,6 @@ export function InlineCode({ className, ...props }: HTMLAttributes<HTMLSpanEleme
|
||||
<code
|
||||
className={classNames(
|
||||
className,
|
||||
'select-text cursor-text',
|
||||
'font-mono text-shrink bg-surface-highlight border border-border-subtle',
|
||||
'px-1.5 py-0.5 rounded text shadow-inner break-words',
|
||||
)}
|
||||
|
||||
@@ -14,7 +14,7 @@ export type InputProps = Omit<
|
||||
> &
|
||||
Pick<
|
||||
EditorProps,
|
||||
| 'language'
|
||||
| 'contentType'
|
||||
| 'useTemplating'
|
||||
| 'autocomplete'
|
||||
| 'forceUpdateKey'
|
||||
@@ -22,7 +22,6 @@ export type InputProps = Omit<
|
||||
| 'autoSelect'
|
||||
| 'autocompleteVariables'
|
||||
| 'onKeyDown'
|
||||
| 'readOnly'
|
||||
> & {
|
||||
name: string;
|
||||
type?: 'text' | 'password';
|
||||
@@ -69,7 +68,6 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
||||
size = 'md',
|
||||
type = 'text',
|
||||
validate,
|
||||
readOnly,
|
||||
...props
|
||||
}: InputProps,
|
||||
ref,
|
||||
@@ -79,10 +77,9 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
||||
const [focused, setFocused] = useState(false);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
if (readOnly) return;
|
||||
setFocused(true);
|
||||
onFocus?.();
|
||||
}, [onFocus, readOnly]);
|
||||
}, [onFocus]);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setFocused(false);
|
||||
@@ -182,7 +179,6 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
||||
className={editorClassName}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
readOnly={readOnly}
|
||||
{...props}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import type { EditorView } from 'codemirror';
|
||||
import {
|
||||
Fragment,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { XYCoord } from 'react-dnd';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -25,10 +16,6 @@ import type { InputProps } from './Input';
|
||||
import { Input } from './Input';
|
||||
import { RadioDropdown } from './RadioDropdown';
|
||||
|
||||
export interface PairEditorRef {
|
||||
focusValue(index: number): void;
|
||||
}
|
||||
|
||||
export type PairEditorProps = {
|
||||
pairs: Pair[];
|
||||
onChange: (pairs: Pair[]) => void;
|
||||
@@ -54,7 +41,6 @@ export type Pair = {
|
||||
value: string;
|
||||
contentType?: string;
|
||||
isFile?: boolean;
|
||||
readOnlyName?: boolean;
|
||||
};
|
||||
|
||||
type PairContainer = {
|
||||
@@ -62,28 +48,24 @@ type PairContainer = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function PairEditor(
|
||||
{
|
||||
className,
|
||||
forceUpdateKey,
|
||||
nameAutocomplete,
|
||||
nameAutocompleteVariables,
|
||||
namePlaceholder,
|
||||
nameValidate,
|
||||
valueType,
|
||||
onChange,
|
||||
noScroll,
|
||||
pairs: originalPairs,
|
||||
valueAutocomplete,
|
||||
valueAutocompleteVariables,
|
||||
valuePlaceholder,
|
||||
valueValidate,
|
||||
allowFileValues,
|
||||
}: PairEditorProps,
|
||||
ref,
|
||||
) {
|
||||
const [forceFocusNamePairId, setForceFocusNamePairId] = useState<string | null>(null);
|
||||
const [forceFocusValuePairId, setForceFocusValuePairId] = useState<string | null>(null);
|
||||
export function PairEditor({
|
||||
className,
|
||||
forceUpdateKey,
|
||||
nameAutocomplete,
|
||||
nameAutocompleteVariables,
|
||||
namePlaceholder,
|
||||
nameValidate,
|
||||
valueType,
|
||||
onChange,
|
||||
noScroll,
|
||||
pairs: originalPairs,
|
||||
valueAutocomplete,
|
||||
valueAutocompleteVariables,
|
||||
valuePlaceholder,
|
||||
valueValidate,
|
||||
allowFileValues,
|
||||
}: PairEditorProps) {
|
||||
const [forceFocusPairId, setForceFocusPairId] = useState<string | null>(null);
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||
const [pairs, setPairs] = useState<PairContainer[]>(() => {
|
||||
// Remove empty headers on initial render
|
||||
@@ -92,13 +74,6 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
return [...pairs, newPairContainer()];
|
||||
});
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focusValue(index: number) {
|
||||
const id = pairs[index]?.id ?? 'n/a';
|
||||
setForceFocusValuePairId(id);
|
||||
},
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
// Remove empty headers on initial render
|
||||
// TODO: Make this not refresh the entire editor when forceUpdateKey changes, using some
|
||||
@@ -159,18 +134,17 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
if (focusPrevious) {
|
||||
const index = pairs.findIndex((p) => p.id === pair.id);
|
||||
const id = pairs[index - 1]?.id ?? null;
|
||||
setForceFocusNamePairId(id);
|
||||
setForceFocusPairId(id);
|
||||
}
|
||||
return setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id));
|
||||
},
|
||||
[setPairsAndSave, setForceFocusNamePairId, pairs],
|
||||
[setPairsAndSave, setForceFocusPairId, pairs],
|
||||
);
|
||||
|
||||
const handleFocus = useCallback(
|
||||
(pair: PairContainer) =>
|
||||
setPairs((pairs) => {
|
||||
setForceFocusNamePairId(null); // Remove focus override when something focused
|
||||
setForceFocusValuePairId(null); // Remove focus override when something focused
|
||||
setForceFocusPairId(null); // Remove focus override when something focused
|
||||
const isLast = pair.id === pairs[pairs.length - 1]?.id;
|
||||
return isLast ? [...pairs, newPairContainer()] : pairs;
|
||||
}),
|
||||
@@ -210,8 +184,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
nameAutocompleteVariables={nameAutocompleteVariables}
|
||||
valueAutocompleteVariables={valueAutocompleteVariables}
|
||||
valueType={valueType}
|
||||
forceFocusNamePairId={forceFocusNamePairId}
|
||||
forceFocusValuePairId={forceFocusValuePairId}
|
||||
forceFocusPairId={forceFocusPairId}
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
nameAutocomplete={nameAutocomplete}
|
||||
valueAutocomplete={valueAutocomplete}
|
||||
@@ -230,7 +203,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
enum ItemTypes {
|
||||
ROW = 'pair-row',
|
||||
@@ -239,8 +212,7 @@ enum ItemTypes {
|
||||
type PairEditorRowProps = {
|
||||
className?: string;
|
||||
pairContainer: PairContainer;
|
||||
forceFocusNamePairId?: string | null;
|
||||
forceFocusValuePairId?: string | null;
|
||||
forceFocusPairId?: string | null;
|
||||
onMove: (id: string, side: 'above' | 'below') => void;
|
||||
onEnd: (id: string) => void;
|
||||
onChange: (pair: PairContainer) => void;
|
||||
@@ -266,8 +238,7 @@ type PairEditorRowProps = {
|
||||
function PairEditorRow({
|
||||
allowFileValues,
|
||||
className,
|
||||
forceFocusNamePairId,
|
||||
forceFocusValuePairId,
|
||||
forceFocusPairId,
|
||||
forceUpdateKey,
|
||||
isLast,
|
||||
nameAutocomplete,
|
||||
@@ -283,26 +254,19 @@ function PairEditorRow({
|
||||
valueAutocomplete,
|
||||
valueAutocompleteVariables,
|
||||
valuePlaceholder,
|
||||
valueType,
|
||||
valueValidate,
|
||||
valueType,
|
||||
}: PairEditorRowProps) {
|
||||
const { id } = pairContainer;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const prompt = usePrompt();
|
||||
const nameInputRef = useRef<EditorView>(null);
|
||||
const valueInputRef = useRef<EditorView>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (forceFocusNamePairId === pairContainer.id) {
|
||||
if (forceFocusPairId === pairContainer.id) {
|
||||
nameInputRef.current?.focus();
|
||||
}
|
||||
}, [forceFocusNamePairId, pairContainer.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (forceFocusValuePairId === pairContainer.id) {
|
||||
valueInputRef.current?.focus();
|
||||
}
|
||||
}, [forceFocusValuePairId, pairContainer.id]);
|
||||
}, [forceFocusPairId, pairContainer.id]);
|
||||
|
||||
const handleChangeEnabled = useMemo(
|
||||
() => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }),
|
||||
@@ -410,7 +374,6 @@ function PairEditorRow({
|
||||
ref={nameInputRef}
|
||||
hideLabel
|
||||
useTemplating
|
||||
readOnly={pairContainer.pair.readOnlyName}
|
||||
size="sm"
|
||||
require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value}
|
||||
validate={nameValidate}
|
||||
@@ -435,7 +398,6 @@ function PairEditorRow({
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
ref={valueInputRef}
|
||||
hideLabel
|
||||
useTemplating
|
||||
size="sm"
|
||||
@@ -514,14 +476,7 @@ function PairEditorRow({
|
||||
</RadioDropdown>
|
||||
) : (
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'Delete',
|
||||
onSelect: handleDelete,
|
||||
variant: 'danger',
|
||||
},
|
||||
]}
|
||||
items={[{ key: 'delete', label: 'Delete', onSelect: handleDelete, variant: 'danger' }]}
|
||||
>
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import classNames from 'classnames';
|
||||
import { forwardRef } from 'react';
|
||||
import { useKeyValue } from '../../hooks/useKeyValue';
|
||||
import { BulkPairEditor } from './BulkPairEditor';
|
||||
import { IconButton } from './IconButton';
|
||||
import type { PairEditorProps, PairEditorRef } from './PairEditor';
|
||||
import type { PairEditorProps } from './PairEditor';
|
||||
import { PairEditor } from './PairEditor';
|
||||
|
||||
interface Props extends PairEditorProps {
|
||||
preferenceName: string;
|
||||
}
|
||||
|
||||
export const PairOrBulkEditor = forwardRef<PairEditorRef, Props>(function PairOrBulkEditor(
|
||||
{ preferenceName, ...props }: Props,
|
||||
ref,
|
||||
) {
|
||||
export function PairOrBulkEditor({ preferenceName, ...props }: Props) {
|
||||
const { value: useBulk, set: setUseBulk } = useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['bulk_edit', preferenceName],
|
||||
@@ -22,7 +18,7 @@ export const PairOrBulkEditor = forwardRef<PairEditorRef, Props>(function PairOr
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full group/wrapper">
|
||||
{useBulk ? <BulkPairEditor {...props} /> : <PairEditor ref={ref} {...props} />}
|
||||
{useBulk ? <BulkPairEditor {...props} /> : <PairEditor {...props} />}
|
||||
<div className="absolute right-0 bottom-0">
|
||||
<IconButton
|
||||
size="sm"
|
||||
@@ -38,4 +34,4 @@ export const PairOrBulkEditor = forwardRef<PairEditorRef, Props>(function PairOr
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
import { Icon } from '../Icon';
|
||||
import type { RadioDropdownProps } from '../RadioDropdown';
|
||||
import { RadioDropdown } from '../RadioDropdown';
|
||||
@@ -39,23 +39,31 @@ export function Tabs({
|
||||
}: Props) {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Update tabs when value changes
|
||||
useEffect(() => {
|
||||
const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`);
|
||||
for (const tab of tabs ?? []) {
|
||||
const v = tab.getAttribute('data-tab');
|
||||
if (v === value) {
|
||||
tab.setAttribute('tabindex', '-1');
|
||||
tab.setAttribute('data-state', 'active');
|
||||
tab.setAttribute('aria-hidden', 'false');
|
||||
tab.style.display = 'block';
|
||||
} else {
|
||||
tab.setAttribute('data-state', 'inactive');
|
||||
tab.setAttribute('aria-hidden', 'true');
|
||||
tab.style.display = 'none';
|
||||
const handleTabChange = useCallback(
|
||||
(value: string) => {
|
||||
const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`);
|
||||
for (const tab of tabs ?? []) {
|
||||
const v = tab.getAttribute('data-tab');
|
||||
if (v === value) {
|
||||
tab.setAttribute('tabindex', '-1');
|
||||
tab.setAttribute('data-state', 'active');
|
||||
tab.setAttribute('aria-hidden', 'false');
|
||||
tab.style.display = 'block';
|
||||
} else {
|
||||
tab.setAttribute('data-state', 'inactive');
|
||||
tab.setAttribute('aria-hidden', 'true');
|
||||
tab.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [value]);
|
||||
onChangeValue(value);
|
||||
},
|
||||
[onChangeValue],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value === undefined) return;
|
||||
handleTabChange(value);
|
||||
}, [handleTabChange, value]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -95,7 +103,7 @@ export function Tabs({
|
||||
onChange={t.options.onChange}
|
||||
>
|
||||
<button
|
||||
onClick={isActive ? undefined : () => onChangeValue(t.value)}
|
||||
onClick={isActive ? undefined : () => handleTabChange(t.value)}
|
||||
className={btnClassName}
|
||||
>
|
||||
{option && 'shortLabel' in option
|
||||
@@ -113,7 +121,7 @@ export function Tabs({
|
||||
return (
|
||||
<button
|
||||
key={t.value}
|
||||
onClick={() => onChangeValue(t.value)}
|
||||
onClick={() => handleTabChange(t.value)}
|
||||
className={btnClassName}
|
||||
>
|
||||
{t.label}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { HttpResponse } from '@yaakapp/api';
|
||||
import { useContentTypeFromHeaders } from '../../hooks/useContentTypeFromHeaders';
|
||||
import { useResponseBodyText } from '../../hooks/useResponseBodyText';
|
||||
import { isJSON, languageFromContentType } from '../../lib/contentType';
|
||||
import { BinaryViewer } from './BinaryViewer';
|
||||
import { TextViewer } from './TextViewer';
|
||||
import { WebPageViewer } from './WebPageViewer';
|
||||
|
||||
interface Props {
|
||||
response: HttpResponse;
|
||||
pretty: boolean;
|
||||
textViewerClassName?: string;
|
||||
}
|
||||
|
||||
export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Props) {
|
||||
const rawBody = useResponseBodyText(response);
|
||||
let language = languageFromContentType(useContentTypeFromHeaders(response.headers));
|
||||
|
||||
// A lot of APIs return JSON with `text/html` content type, so interpret as JSON if so
|
||||
if (language === 'html' && isJSON(rawBody.data ?? '')) {
|
||||
language = 'json';
|
||||
}
|
||||
|
||||
if (rawBody.isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rawBody.data == null) {
|
||||
return <BinaryViewer response={response} />;
|
||||
}
|
||||
|
||||
if (language === 'html' && pretty) {
|
||||
return <WebPageViewer response={response} />;
|
||||
} else {
|
||||
return <TextViewer response={response} pretty={pretty} className={textViewerClassName} />;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user