mirror of
https://github.com/zadam/trilium.git
synced 2025-12-16 21:29:56 +01:00
Compare commits
834 Commits
migrate_pa
...
stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
982fb212e4 | ||
|
|
9fcee9cc53 | ||
|
|
d173cc982c | ||
|
|
471c57b3ed | ||
|
|
093d7d783b | ||
|
|
7cc20600e7 | ||
|
|
559c654fbb | ||
|
|
01a03e3e97 | ||
|
|
dd3233a556 | ||
|
|
c4a426566f | ||
|
|
c081a596df | ||
|
|
6b07908cf7 | ||
|
|
2985bd0a1c | ||
|
|
975e8487fc | ||
|
|
54408d3ec8 | ||
|
|
8b3afc1f49 | ||
|
|
e15bc5a232 | ||
|
|
8fdda59440 | ||
|
|
d5cbf362f8 | ||
|
|
6f85d3370c | ||
|
|
8c324cd185 | ||
|
|
f7f7fda040 | ||
|
|
74c11f4d4e | ||
|
|
0525cfab79 | ||
|
|
2d73627908 | ||
|
|
94d015789d | ||
|
|
af2f6246e8 | ||
|
|
ebbdf0294a | ||
|
|
5df539f0a4 | ||
|
|
4f6dfeb773 | ||
|
|
52bb83e878 | ||
|
|
286a8626d1 | ||
|
|
aa62dc3f32 | ||
|
|
045e7977d5 | ||
|
|
e0dc25ad23 | ||
|
|
9d0499a306 | ||
|
|
b971e002ce | ||
|
|
5fd488e210 | ||
|
|
eb84da4c51 | ||
|
|
49243148a2 | ||
|
|
276241cdff | ||
|
|
6772453b3a | ||
|
|
18e2f1f90c | ||
|
|
67d2175ce9 | ||
|
|
8a3283f1ea | ||
|
|
2fb47fc186 | ||
|
|
4530c9a40c | ||
|
|
a867a25d5f | ||
|
|
10910ac2ed | ||
|
|
514f5a0c81 | ||
|
|
6b18ed6477 | ||
|
|
e07b6cc409 | ||
|
|
5ae67fda7f | ||
|
|
1a03c0ca9f | ||
|
|
dc211b4d00 | ||
|
|
2e767ffde1 | ||
|
|
9f74a54c0d | ||
|
|
a7870495ac | ||
|
|
a8ec323ea8 | ||
|
|
4b7d243406 | ||
|
|
1855588270 | ||
|
|
e545e0c3a5 | ||
|
|
b24a0f1595 | ||
|
|
3f2bfc3050 | ||
|
|
66f100d534 | ||
|
|
e641d5ba0f | ||
|
|
6f8cebf954 | ||
|
|
2d2f2457cb | ||
|
|
68fa243af4 | ||
|
|
9cb133c8a6 | ||
|
|
33f6c75917 | ||
|
|
49acd5e8c2 | ||
|
|
85266adf20 | ||
|
|
27edea8380 | ||
|
|
dccddb8d43 | ||
|
|
5f9d7a223a | ||
|
|
29b71262d6 | ||
|
|
0cfcbcf5df | ||
|
|
e1258384d8 | ||
|
|
8837eddd40 | ||
|
|
f919d46e9a | ||
|
|
98094fac63 | ||
|
|
f94ccee252 | ||
|
|
7c4b338539 | ||
|
|
d88431f9e3 | ||
|
|
5f86068489 | ||
|
|
36bc226674 | ||
|
|
7947745218 | ||
|
|
19697aabfb | ||
|
|
87af23598d | ||
|
|
4ed7966a5a | ||
|
|
8d86b5c7e6 | ||
|
|
cdf9458962 | ||
|
|
83df37c2d1 | ||
|
|
bc7b3165c8 | ||
|
|
c332e9764a | ||
|
|
004d48d36c | ||
|
|
28b263a445 | ||
|
|
e0e9310907 | ||
|
|
68b9159b2b | ||
|
|
f5940cbf70 | ||
|
|
bfb143bb51 | ||
|
|
64662d5215 | ||
|
|
31cedad976 | ||
|
|
9682df6240 | ||
|
|
a718908385 | ||
|
|
12ac5147d3 | ||
|
|
17291ff61d | ||
|
|
f3e334470e | ||
|
|
9407051f1e | ||
|
|
08a6d36153 | ||
|
|
f906fb9b4c | ||
|
|
b4a6356724 | ||
|
|
8eca14069a | ||
|
|
1af0477ac0 | ||
|
|
43920f12ae | ||
|
|
5a0beec6cb | ||
|
|
98241fb54b | ||
|
|
3051664228 | ||
|
|
1ed774365c | ||
|
|
f2e33dfd58 | ||
|
|
90b5282b39 | ||
|
|
d520fc46b9 | ||
|
|
e69b5988ec | ||
|
|
3cdc1ba794 | ||
|
|
25e1008c5c | ||
|
|
a093862311 | ||
|
|
53057ea9fc | ||
|
|
94db96de3e | ||
|
|
60e4fbbf75 | ||
|
|
d35dd67632 | ||
|
|
8813985c68 | ||
|
|
538c98b587 | ||
|
|
389c7029cf | ||
|
|
d47f9e1131 | ||
|
|
c0a8d29756 | ||
|
|
668fd34af6 | ||
|
|
8aa08cf8fe | ||
|
|
16c04f5ae4 | ||
|
|
32c16021c4 | ||
|
|
7713c1173a | ||
|
|
8018f400c3 | ||
|
|
79c8293881 | ||
|
|
db5652623b | ||
|
|
0f7a48b323 | ||
|
|
309fbab2e6 | ||
|
|
99da145d65 | ||
|
|
415d2826c6 | ||
|
|
7787e7085e | ||
|
|
4ab8417168 | ||
|
|
a77e76d5c6 | ||
|
|
ca6660e2ff | ||
|
|
af94410c55 | ||
|
|
b47bc50147 | ||
|
|
5ff77c16ab | ||
|
|
11618260cf | ||
|
|
63f9006d17 | ||
|
|
7779acc7bc | ||
|
|
aacd92eee3 | ||
|
|
1bf8be2874 | ||
|
|
66f2d0c7dc | ||
|
|
597d952254 | ||
|
|
288595ce5d | ||
|
|
c89e8c78d3 | ||
|
|
81a37e3fc4 | ||
|
|
368c590976 | ||
|
|
6e982e646d | ||
|
|
030582b2d5 | ||
|
|
7dd4b10a96 | ||
|
|
b055e79b4c | ||
|
|
ba980aa93f | ||
|
|
15baf04ce9 | ||
|
|
4dc2587817 | ||
|
|
d5e046c289 | ||
|
|
ef8073ac58 | ||
|
|
38c9d25214 | ||
|
|
e2f0e4089f | ||
|
|
80ce2c04ed | ||
|
|
4ebd82beeb | ||
|
|
8cc43cd9a6 | ||
|
|
15190abb69 | ||
|
|
0b28159e8e | ||
|
|
d9e8f8e69b | ||
|
|
fa224e46bc | ||
|
|
06320953e8 | ||
|
|
2163334c4f | ||
|
|
f5d180af6b | ||
|
|
d676084cb3 | ||
|
|
0cb5941be0 | ||
|
|
732494dfc5 | ||
|
|
b8748b856a | ||
|
|
cc71f15700 | ||
|
|
124ef640b1 | ||
|
|
f5e3df0cd2 | ||
|
|
c8431181c8 | ||
|
|
07fb5ab017 | ||
|
|
6735b257b4 | ||
|
|
cef242a9ce | ||
|
|
2923d917e5 | ||
|
|
1d1639e5e1 | ||
|
|
6ab05fdb76 | ||
|
|
91ae8c0aaf | ||
|
|
fcb69c0190 | ||
|
|
2cf6fe4352 | ||
|
|
e3a2623a53 | ||
|
|
e0c0a423c1 | ||
|
|
fd99246c49 | ||
|
|
d247edd870 | ||
|
|
9a76a9069c | ||
|
|
8e1d796870 | ||
|
|
8b0d4e5c3b | ||
|
|
b9e257a39d | ||
|
|
e7eaa5fd58 | ||
|
|
c9aa992e73 | ||
|
|
f325930f68 | ||
|
|
1346ffb77e | ||
|
|
3378746530 | ||
|
|
ce2d94f04e | ||
|
|
b3c2a1e6c5 | ||
|
|
dbf63787da | ||
|
|
88a7ebef69 | ||
|
|
a716151dd9 | ||
|
|
7462f1b7a5 | ||
|
|
ec76b9dc5c | ||
|
|
79cd96ade9 | ||
|
|
a5b84406be | ||
|
|
8c1a04c4b2 | ||
|
|
ee81037173 | ||
|
|
453349be26 | ||
|
|
81a9e06b23 | ||
|
|
7d8af0f252 | ||
|
|
a68cd7526b | ||
|
|
470ca3b6dc | ||
|
|
e8bae61afc | ||
|
|
c1f663a200 | ||
|
|
22b2e21df0 | ||
|
|
5f19710791 | ||
|
|
d3f3ff4eab | ||
|
|
5af7425cae | ||
|
|
fe10c9f8c8 | ||
|
|
cd2a085d00 | ||
|
|
3c61626370 | ||
|
|
351fe5848f | ||
|
|
ca7bbefbdc | ||
|
|
7094f71e32 | ||
|
|
88b5e9db87 | ||
|
|
b4ab07bd78 | ||
|
|
fd6ad6dce3 | ||
|
|
ab97068a1d | ||
|
|
70fe3b9773 | ||
|
|
1fe8079fd5 | ||
|
|
80627997d1 | ||
|
|
12abdcaf6c | ||
|
|
a6ed4d92c9 | ||
|
|
0471640f54 | ||
|
|
4cf3e82fb5 | ||
|
|
fbbe999806 | ||
|
|
76af488d35 | ||
|
|
a54d2a5f22 | ||
|
|
a1df075194 | ||
|
|
4de2182b40 | ||
|
|
6fa88123f1 | ||
|
|
f81dbde15e | ||
|
|
484fbc6b9d | ||
|
|
8ba30135a1 | ||
|
|
ba5a72fdad | ||
|
|
70b39ddadf | ||
|
|
8200c0b0ab | ||
|
|
59ebfa6cc7 | ||
|
|
57b694162d | ||
|
|
2e6bdc225f | ||
|
|
8ced689432 | ||
|
|
53a8f6b4c0 | ||
|
|
9ae1a55896 | ||
|
|
5ecafe214f | ||
|
|
8baf0ad6af | ||
|
|
3cc64b5764 | ||
|
|
19cf07564f | ||
|
|
5847ce5c14 | ||
|
|
a7ad45635e | ||
|
|
781215394e | ||
|
|
0aafdca999 | ||
|
|
18d3cb6f0c | ||
|
|
263a96e8b7 | ||
|
|
27d5009486 | ||
|
|
4d1a91baa6 | ||
|
|
1898efa282 | ||
|
|
648ab4d736 | ||
|
|
407cac588a | ||
|
|
210dcfb989 | ||
|
|
2e431b1135 | ||
|
|
e2ec27250c | ||
|
|
1228eda5ea | ||
|
|
435794df73 | ||
|
|
7e3d0639f7 | ||
|
|
86b0005821 | ||
|
|
2fb78275f7 | ||
|
|
98f421c697 | ||
|
|
a5572b7d45 | ||
|
|
fdecbaaa6a | ||
|
|
c6afd7fa24 | ||
|
|
5cad522a60 | ||
|
|
82f64677cb | ||
|
|
3ee086a063 | ||
|
|
13da444a69 | ||
|
|
b51ceaaadc | ||
|
|
2024c72209 | ||
|
|
b5959c55e1 | ||
|
|
16f0ac97f4 | ||
|
|
073c02ee0c | ||
|
|
786f0db4bb | ||
|
|
6958e4b74f | ||
|
|
22c4fba665 | ||
|
|
f7c0e56cec | ||
|
|
5f423cd22e | ||
|
|
282b3a109c | ||
|
|
dddb051d8b | ||
|
|
63d3706003 | ||
|
|
22ca2494f5 | ||
|
|
a0f02b6877 | ||
|
|
487fcff61f | ||
|
|
bfb8897188 | ||
|
|
ce1ccf378a | ||
|
|
96d5ee3d46 | ||
|
|
6f3771e7cd | ||
|
|
e6b00b05a2 | ||
|
|
148d0afe81 | ||
|
|
c5d63dbdb9 | ||
|
|
1af072b059 | ||
|
|
a6a8fdd2f8 | ||
|
|
8836021ff9 | ||
|
|
915803c5be | ||
|
|
cd95d43457 | ||
|
|
35af5fd13c | ||
|
|
35e602f75f | ||
|
|
b4f0a1acc0 | ||
|
|
7b7e9f6868 | ||
|
|
de3892950c | ||
|
|
5c53826da3 | ||
|
|
d358073081 | ||
|
|
d2d7fd7c4c | ||
|
|
706da768e2 | ||
|
|
408073ee19 | ||
|
|
2695b7fc38 | ||
|
|
c32b6393af | ||
|
|
abbb4e793f | ||
|
|
e4fae2d660 | ||
|
|
dc572c3815 | ||
|
|
c65b03db41 | ||
|
|
45d2e1f5e2 | ||
|
|
b658f5bd0e | ||
|
|
f199d85d5b | ||
|
|
445dfaaeb4 | ||
|
|
cccd4122c5 | ||
|
|
1fcbe828bd | ||
|
|
e94b78a37b | ||
|
|
af7cecb667 | ||
|
|
4068e9c6b8 | ||
|
|
185c1fcdac | ||
|
|
29c5d35c08 | ||
|
|
0117f54ef8 | ||
|
|
56d41916c4 | ||
|
|
fddf73b1bb | ||
|
|
d670c2ae5e | ||
|
|
6f5c9eb600 | ||
|
|
49c416c90d | ||
|
|
8fef468eb9 | ||
|
|
976b1e1e0f | ||
|
|
d6de7cca96 | ||
|
|
fb02751bdd | ||
|
|
3fb5c52af1 | ||
|
|
18d17570f6 | ||
|
|
fa5eb16054 | ||
|
|
b1c77b508a | ||
|
|
b982ca2c5b | ||
|
|
219753039d | ||
|
|
c16f9af6a9 | ||
|
|
a544b0dc19 | ||
|
|
a398f07f9f | ||
|
|
7ecdee7a89 | ||
|
|
24361ccd97 | ||
|
|
3afe6df9f2 | ||
|
|
df7f79004d | ||
|
|
405bce9f82 | ||
|
|
b9ef7af791 | ||
|
|
761891abe9 | ||
|
|
6a52637695 | ||
|
|
b4b957d2c3 | ||
|
|
dda4a0a887 | ||
|
|
b069a936ab | ||
|
|
15c088ec21 | ||
|
|
015d70afb6 | ||
|
|
2158f69c09 | ||
|
|
c49c5fd58c | ||
|
|
26be131b4b | ||
|
|
f8533eb2c6 | ||
|
|
4cc545659b | ||
|
|
70a0b5c22f | ||
|
|
a2976d9ad5 | ||
|
|
01b05f186e | ||
|
|
79a4da9db8 | ||
|
|
84890fd5ad | ||
|
|
29f9d0c1cd | ||
|
|
95169bbc84 | ||
|
|
9a2979e577 | ||
|
|
9f381a7b30 | ||
|
|
c07ad348bd | ||
|
|
d7ae2e4307 | ||
|
|
71b86b3cbc | ||
|
|
621e8078d9 | ||
|
|
7155ab8bdc | ||
|
|
0a3e788d21 | ||
|
|
f83c46d1c7 | ||
|
|
11932353f7 | ||
|
|
ed373107c2 | ||
|
|
1cca15ca5d | ||
|
|
11e59a970e | ||
|
|
5abb69d022 | ||
|
|
dc1d497ff3 | ||
|
|
7f909fa098 | ||
|
|
320f064775 | ||
|
|
c9b1691998 | ||
|
|
08ade8371c | ||
|
|
081ac2d109 | ||
|
|
3fde546b83 | ||
|
|
a12b3cb51a | ||
|
|
6160945b9e | ||
|
|
6a126009a8 | ||
|
|
891e71aec6 | ||
|
|
cd164049b7 | ||
|
|
709a47bc6b | ||
|
|
9c6cd80867 | ||
|
|
832d9a2ab8 | ||
|
|
8d4e30a2e4 | ||
|
|
baf41eb104 | ||
|
|
7f0fe1681b | ||
|
|
49189bc63e | ||
|
|
87f30ed3d5 | ||
|
|
aca390ee19 | ||
|
|
598bb6d742 | ||
|
|
33b19e40e0 | ||
|
|
5b9401fafe | ||
|
|
9bed6b7e22 | ||
|
|
aca003c554 | ||
|
|
3c806558eb | ||
|
|
d195d46f8f | ||
|
|
755abc6487 | ||
|
|
718282bae3 | ||
|
|
8cc4d08eae | ||
|
|
0bcc02dfab | ||
|
|
33c3fb7de0 | ||
|
|
d99b8f5864 | ||
|
|
98bf63e94b | ||
|
|
2d8b1299b3 | ||
|
|
5edc4abfb4 | ||
|
|
26f7264f3c | ||
|
|
56c82d7f0f | ||
|
|
6946da3571 | ||
|
|
2985c762e6 | ||
|
|
8001d940eb | ||
|
|
2f440eba37 | ||
|
|
bb9cb2fb75 | ||
|
|
5531c15126 | ||
|
|
29f049c411 | ||
|
|
31180afbd1 | ||
|
|
435b856b72 | ||
|
|
af62526b92 | ||
|
|
beb7d09aee | ||
|
|
b4b1b7a3fa | ||
|
|
d0342598c4 | ||
|
|
4a71b00b71 | ||
|
|
268bbf3b9e | ||
|
|
24cdeb06e8 | ||
|
|
6b64d85db0 | ||
|
|
08e542dfb3 | ||
|
|
8b997cffdd | ||
|
|
33aece6f96 | ||
|
|
6040eea3bd | ||
|
|
9b3ca65492 | ||
|
|
c76f368fa0 | ||
|
|
664d28f105 | ||
|
|
7daee36d3e | ||
|
|
5fc10fe041 | ||
|
|
6692de33b1 | ||
|
|
494bd425af | ||
|
|
135ce2285d | ||
|
|
be2e82788f | ||
|
|
e84fb63343 | ||
|
|
97422c1d7a | ||
|
|
66cc739bb2 | ||
|
|
790454f194 | ||
|
|
d751966078 | ||
|
|
a8e9f7b445 | ||
|
|
144afc346d | ||
|
|
63854156eb | ||
|
|
7800e76b26 | ||
|
|
1e5b95f64a | ||
|
|
097e36677a | ||
|
|
7e6e10e3ef | ||
|
|
f72ce697a7 | ||
|
|
e6843bc3e0 | ||
|
|
5c0cf09c42 | ||
|
|
ab14bdbb18 | ||
|
|
8dc43dab59 | ||
|
|
0c5a6a7548 | ||
|
|
779e2f4633 | ||
|
|
644e3e200d | ||
|
|
670cc474a4 | ||
|
|
c43820776f | ||
|
|
749740242e | ||
|
|
f864746b54 | ||
|
|
42dcd4750a | ||
|
|
cb0c6a344f | ||
|
|
d1d652495d | ||
|
|
4b574cecf7 | ||
|
|
e7dbaf78b5 | ||
|
|
6f83b932b0 | ||
|
|
4552b2b158 | ||
|
|
5b708e77aa | ||
|
|
a3f1e46107 | ||
|
|
097808752d | ||
|
|
39be268969 | ||
|
|
6023d53506 | ||
|
|
1a6e653600 | ||
|
|
586c707e51 | ||
|
|
6ca941e8e9 | ||
|
|
b83675cdd0 | ||
|
|
1a583913a7 | ||
|
|
d98a644b75 | ||
|
|
11c0c93fe2 | ||
|
|
6d78867d69 | ||
|
|
54813b8b93 | ||
|
|
6ab31c67fc | ||
|
|
69af2ff7e8 | ||
|
|
b505a2a94d | ||
|
|
6a5fd0d6f0 | ||
|
|
e21557645b | ||
|
|
28cab146f3 | ||
|
|
43df4ae0e7 | ||
|
|
5037027030 | ||
|
|
5e11684665 | ||
|
|
c7793beb0f | ||
|
|
33b9e6d0c1 | ||
|
|
d87e8b729f | ||
|
|
0db08f4c62 | ||
|
|
e9796c9a35 | ||
|
|
36350bd71a | ||
|
|
e53a2255a9 | ||
|
|
d42f911df9 | ||
|
|
1b2d922c3f | ||
|
|
a5c5486474 | ||
|
|
926f0f85f3 | ||
|
|
63d430c3d9 | ||
|
|
26a009b397 | ||
|
|
be115c74c3 | ||
|
|
049721bbfe | ||
|
|
8b4e76832f | ||
|
|
25a51a71a0 | ||
|
|
f4d6e98d61 | ||
|
|
eee496a050 | ||
|
|
89a83a625b | ||
|
|
c17df24a19 | ||
|
|
bbcc2f4be4 | ||
|
|
5e63d9015f | ||
|
|
4958b89636 | ||
|
|
f4b6e9c25a | ||
|
|
a59d407f12 | ||
|
|
c95cb79672 | ||
|
|
73e7fa0f85 | ||
|
|
7f81b83955 | ||
|
|
a42ae62042 | ||
|
|
165357f444 | ||
|
|
e4c928ae87 | ||
|
|
d51e3de674 | ||
|
|
b0476c7017 | ||
|
|
1de9f715fa | ||
|
|
f15e048763 | ||
|
|
422b324f7c | ||
|
|
fb163367d4 | ||
|
|
c91eec8b3e | ||
|
|
c16eee79d4 | ||
|
|
1e86d85035 | ||
|
|
abfc2fea3e | ||
|
|
991d61600d | ||
|
|
0f4713bddc | ||
|
|
51205fffa0 | ||
|
|
9f4fd92452 | ||
|
|
354fccab8b | ||
|
|
a6ba87c2f8 | ||
|
|
ae8aa0374f | ||
|
|
dc5e073715 | ||
|
|
8c68ff5419 | ||
|
|
bc10408729 | ||
|
|
96b059f657 | ||
|
|
4db2ae53cb | ||
|
|
f7308f53d3 | ||
|
|
17fbd78b97 | ||
|
|
3ad03ebc1d | ||
|
|
32e4e69930 | ||
|
|
b20f7aca53 | ||
|
|
e72ee606fd | ||
|
|
1386d1ae32 | ||
|
|
903d678f30 | ||
|
|
828a786414 | ||
|
|
3ee8e7b755 | ||
|
|
0d0448d86b | ||
|
|
5da4762f40 | ||
|
|
36bcb2ce92 | ||
|
|
151d7c1ba5 | ||
|
|
19d444e387 | ||
|
|
e41c718bb3 | ||
|
|
2e5d91a5bc | ||
|
|
ae184ab894 | ||
|
|
1ceedf2372 | ||
|
|
416c05ed3b | ||
|
|
623da7eade | ||
|
|
84b3d6db76 | ||
|
|
25a27c9b11 | ||
|
|
456d59f582 | ||
|
|
1d641cdf82 | ||
|
|
4420d7962e | ||
|
|
21e6e3df3f | ||
|
|
7e04098971 | ||
|
|
24df4dd0c1 | ||
|
|
8ed87e77be | ||
|
|
10c0ce26dc | ||
|
|
3a38b59dad | ||
|
|
24820b914c | ||
|
|
97339fd45d | ||
|
|
a1314d645b | ||
|
|
571a4dca7a | ||
|
|
36f5e62537 | ||
|
|
a21b716d0a | ||
|
|
c25859cee9 | ||
|
|
79dc5e4344 | ||
|
|
c81aef6d05 | ||
|
|
c744e36f3b | ||
|
|
9d96b3d1d0 | ||
|
|
b246c638fd | ||
|
|
15dd8dfe8c | ||
|
|
50859fd661 | ||
|
|
eada994a82 | ||
|
|
3da2046fa0 | ||
|
|
989ca08c94 | ||
|
|
e9ccd7120d | ||
|
|
c15ae293aa | ||
|
|
50501aef56 | ||
|
|
45747183e7 | ||
|
|
c9424d6f8d | ||
|
|
5ecd8b41e5 | ||
|
|
01d6dee9fc | ||
|
|
12865ac7cc | ||
|
|
362f0b2fe5 | ||
|
|
9f800df5ad | ||
|
|
e937f1b601 | ||
|
|
5b387a0b11 | ||
|
|
9226b165bd | ||
|
|
a6586c9d1c | ||
|
|
0a36d759e4 | ||
|
|
328bcd0532 | ||
|
|
ec76e9cf2a | ||
|
|
88ae996694 | ||
|
|
d1d1d05ce7 | ||
|
|
c62a03c97b | ||
|
|
65cc57ec03 | ||
|
|
806c9a57fc | ||
|
|
3a18ce0cf9 | ||
|
|
4fde6e1293 | ||
|
|
31b43301a4 | ||
|
|
6a77e412fa | ||
|
|
53a6424348 | ||
|
|
c8d933469a | ||
|
|
e456373671 | ||
|
|
afdb806083 | ||
|
|
02c63dd22f | ||
|
|
529f9a2fb9 | ||
|
|
6b0bcf93d3 | ||
|
|
13afe33244 | ||
|
|
d441bccf8b | ||
|
|
e6847355e7 | ||
|
|
72051c8660 | ||
|
|
87afc64f16 | ||
|
|
870fef3ea6 | ||
|
|
e5ac8a0a67 | ||
|
|
87fcc0afe6 | ||
|
|
79830870dd | ||
|
|
69ad40c27f | ||
|
|
1ac7ce00fb | ||
|
|
e239bca0f2 | ||
|
|
8729fe48c3 | ||
|
|
441c55eb31 | ||
|
|
5291a6856e | ||
|
|
e011f99161 | ||
|
|
64a756cc04 | ||
|
|
f57e90b35c | ||
|
|
5a5d242ea0 | ||
|
|
4afea27fa5 | ||
|
|
fc8042aa25 | ||
|
|
2b6220beb8 | ||
|
|
78426a6c7b | ||
|
|
620e53c255 | ||
|
|
753fc6c769 | ||
|
|
3d6e1dfc0a | ||
|
|
d92431ad65 | ||
|
|
be19d1f5b5 | ||
|
|
ca08a52998 | ||
|
|
e54822f3b0 | ||
|
|
3863e657ef | ||
|
|
341ef79b49 | ||
|
|
335f34b824 | ||
|
|
c864863be4 | ||
|
|
c3ebef0dde | ||
|
|
7b7058c77b | ||
|
|
192cf9bc26 | ||
|
|
1cccbcfabe | ||
|
|
a85b37985a | ||
|
|
8b6b1ee315 | ||
|
|
021c655a1a | ||
|
|
8af8968b49 | ||
|
|
17298edfcc | ||
|
|
5281e8e5b4 | ||
|
|
cc0e30e3f5 | ||
|
|
497bb35209 | ||
|
|
7d1453ffbd | ||
|
|
89228f264f | ||
|
|
a10d99f938 | ||
|
|
d014ae4fcf | ||
|
|
a22687e2d8 | ||
|
|
44475853df | ||
|
|
bbcc670655 | ||
|
|
ae58b4af35 | ||
|
|
fbc2ffac59 | ||
|
|
f279839e6f | ||
|
|
1844a7d666 | ||
|
|
c9f648fcb8 | ||
|
|
948688a4ea | ||
|
|
35ca295d48 | ||
|
|
cacc4ad01d | ||
|
|
b12085f61f | ||
|
|
39dacafa82 | ||
|
|
57deb36027 | ||
|
|
2840df82f4 | ||
|
|
3d971108b8 | ||
|
|
5bcdce72ef | ||
|
|
398329a219 | ||
|
|
754a06343f | ||
|
|
55a79e5fbf | ||
|
|
c78a97fed1 | ||
|
|
13c8ff5cb3 | ||
|
|
9e34d3a668 | ||
|
|
57f220e64c | ||
|
|
a89756a76c | ||
|
|
88c1aa163e | ||
|
|
d2184682e5 | ||
|
|
63cc5b21b4 | ||
|
|
b7703fc4df | ||
|
|
254d3a1c8e | ||
|
|
8d3892757a | ||
|
|
a8992d08b3 | ||
|
|
5e35aa8079 | ||
|
|
df8da0fd4f | ||
|
|
f820c6f23b | ||
|
|
0c616fecdf | ||
|
|
092a84693f | ||
|
|
d1e80815d5 | ||
|
|
0f000ccd93 | ||
|
|
f90e0767cb | ||
|
|
ad6d61f1f7 | ||
|
|
47f7968dc4 | ||
|
|
455b190a5b | ||
|
|
0bc8584c35 | ||
|
|
da39cdb27f | ||
|
|
769c2e9b4e | ||
|
|
783d2b8843 | ||
|
|
baca0a17c3 | ||
|
|
f48d47bac5 | ||
|
|
14fa5d2723 | ||
|
|
70845611a4 | ||
|
|
7be11da85f | ||
|
|
f2f4b0e75b | ||
|
|
491cd27f2d | ||
|
|
7b62881113 | ||
|
|
22f46919f9 | ||
|
|
1ef7fd401f | ||
|
|
5f1dbc23b4 | ||
|
|
8d750417ec | ||
|
|
52f30052d5 | ||
|
|
655e6bafd1 | ||
|
|
d4dfb0cb53 | ||
|
|
8d08973d48 | ||
|
|
9b1b56a381 | ||
|
|
f709c27329 | ||
|
|
02859039ec | ||
|
|
e6810ef753 | ||
|
|
ef86e195c6 | ||
|
|
d69dd2a83f | ||
|
|
5dd21ac539 | ||
|
|
bd6575982b | ||
|
|
80313527c5 | ||
|
|
78901e03d7 | ||
|
|
01c1b19601 | ||
|
|
1d837092a2 | ||
|
|
bde04919fe | ||
|
|
0dd0416346 | ||
|
|
711dd64093 | ||
|
|
db7b4829b5 | ||
|
|
f97c63fe93 | ||
|
|
cb5fe95768 | ||
|
|
34359dd7b6 | ||
|
|
476d1d274e | ||
|
|
c52265c046 | ||
|
|
4fbf3d79c7 | ||
|
|
e8cc92db95 | ||
|
|
40e969bab9 | ||
|
|
3df2105016 | ||
|
|
0aa3cc3d6f | ||
|
|
666f26f516 | ||
|
|
7662dde294 | ||
|
|
d28dda876c | ||
|
|
309d7e704c | ||
|
|
ecf9ce586c | ||
|
|
d0de9e5e21 | ||
|
|
2154a5e1db | ||
|
|
dbe51ccaf3 | ||
|
|
993d53ed97 | ||
|
|
730e2da932 | ||
|
|
18a198496b | ||
|
|
5eb791fd65 | ||
|
|
27cc022fb8 | ||
|
|
35f244cf50 | ||
|
|
fa30bfc04b | ||
|
|
a072466f75 | ||
|
|
ae5c898537 | ||
|
|
921a37f4a2 |
2
.github/actions/report-size/action.yml
vendored
2
.github/actions/report-size/action.yml
vendored
@@ -44,7 +44,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
# Checkout branch to compare to [required]
|
# Checkout branch to compare to [required]
|
||||||
- name: Checkout base branch
|
- name: Checkout base branch
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.branch }}
|
ref: ${{ inputs.branch }}
|
||||||
path: br-base
|
path: br-base
|
||||||
|
|||||||
334
.github/copilot-instructions.md
vendored
Normal file
334
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
# Trilium Notes - AI Coding Agent Instructions
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. Built as a TypeScript monorepo using pnpm, it implements a three-layer caching architecture (Becca/Froca/Shaca) with a widget-based UI system and supports extensive user scripting capabilities.
|
||||||
|
|
||||||
|
## Essential Architecture Patterns
|
||||||
|
|
||||||
|
### Three-Layer Cache System (Critical to Understand)
|
||||||
|
- **Becca** (`apps/server/src/becca/`): Server-side entity cache, primary data source
|
||||||
|
- **Froca** (`apps/client/src/services/froca.ts`): Client-side mirror synchronized via WebSocket
|
||||||
|
- **Shaca** (`apps/server/src/share/`): Optimized cache for public/shared notes
|
||||||
|
|
||||||
|
**Key insight**: Never bypass these caches with direct DB queries. Always use `becca.notes[noteId]`, `froca.getNote()`, or equivalent cache methods.
|
||||||
|
|
||||||
|
### Entity Relationship Model
|
||||||
|
Notes use a **multi-parent tree** via branches:
|
||||||
|
- `BNote` - The note content and metadata
|
||||||
|
- `BBranch` - Tree relationships (one note can have multiple parents via cloning)
|
||||||
|
- `BAttribute` - Key-value metadata attached to notes (labels and relations)
|
||||||
|
|
||||||
|
### Entity Change System & Sync
|
||||||
|
Every entity modification (notes, branches, attributes) creates an `EntityChange` record that drives synchronization:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Entity changes are automatically tracked
|
||||||
|
note.title = "New Title";
|
||||||
|
note.save(); // Creates EntityChange record with changeId
|
||||||
|
|
||||||
|
// Sync protocol via WebSocket
|
||||||
|
ws.sendMessage({ type: 'sync-pull-in-progress', ... });
|
||||||
|
```
|
||||||
|
|
||||||
|
**Critical**: This is why you must use Becca/Froca methods instead of direct DB writes - they create the change tracking records needed for sync.
|
||||||
|
|
||||||
|
### Entity Lifecycle & Events
|
||||||
|
The event system (`apps/server/src/services/events.ts`) broadcasts entity lifecycle events:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Subscribe to events in widgets or services
|
||||||
|
eventService.subscribe('noteChanged', ({ noteId }) => {
|
||||||
|
// React to note changes
|
||||||
|
});
|
||||||
|
|
||||||
|
// Common events: noteChanged, branchChanged, attributeChanged, noteDeleted
|
||||||
|
// Widget method: entitiesReloadedEvent({loadResults}) for handling reloads
|
||||||
|
```
|
||||||
|
|
||||||
|
**Becca loader priorities**: Events are emitted in order (notes → branches → attributes) during initial load to ensure referential integrity.
|
||||||
|
|
||||||
|
### TaskContext for Long Operations
|
||||||
|
Use `TaskContext` for operations with progress reporting (imports, exports, bulk operations):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const taskContext = new TaskContext("task-id", "import", "Import Notes");
|
||||||
|
taskContext.increaseProgressCount();
|
||||||
|
|
||||||
|
// WebSocket messages: { type: 'taskProgressCount', taskId, taskType, data, progressCount }
|
||||||
|
|
||||||
|
**Pattern**: All long-running operations (delete note trees, export, import) use TaskContext to send WebSocket updates to the frontend.
|
||||||
|
|
||||||
|
### Protected Session Handling
|
||||||
|
Protected notes require an active encryption session:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Always check before accessing protected content
|
||||||
|
if (note.isContentAvailable()) {
|
||||||
|
const content = note.getContent(); // Safe
|
||||||
|
} else {
|
||||||
|
const title = note.getTitleOrProtected(); // Returns "[protected]"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected session management
|
||||||
|
protectedSessionService.isProtectedSessionAvailable() // Check session
|
||||||
|
protectedSessionService.startProtectedSession() // After password entry
|
||||||
|
```
|
||||||
|
|
||||||
|
**Session timeout**: Protected sessions expire after inactivity. The encryption key is kept in memory only.
|
||||||
|
|
||||||
|
### Attribute Inheritance Patterns
|
||||||
|
Attributes can be inherited through three mechanisms:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 1. Standard inheritance (#hidePromotedAttributes ~hidePromotedAttributes)
|
||||||
|
note.getInheritableAttributes() // Walks up parent tree
|
||||||
|
|
||||||
|
// 2. Child prefix inheritance (child:label copies to children)
|
||||||
|
parentNote.setLabel("child:icon", "book") // All children inherit this
|
||||||
|
|
||||||
|
// 3. Template relation inheritance (#template=templateNoteId)
|
||||||
|
note.setRelation("template", templateNoteId)
|
||||||
|
note.getInheritedAttributes() // Includes template's inheritable attributes
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cycle prevention**: Inheritance tracking prevents infinite loops when notes reference each other.
|
||||||
|
|
||||||
|
### Widget-Based UI Architecture
|
||||||
|
All UI components extend from widget base classes (`apps/client/src/widgets/`):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Right panel widget (sidebar)
|
||||||
|
class MyWidget extends RightPanelWidget {
|
||||||
|
get position() { return 100; } // Order in panel
|
||||||
|
get parentWidget() { return 'right-pane'; }
|
||||||
|
isEnabled() { return this.note && this.note.hasLabel('myLabel'); }
|
||||||
|
async refreshWithNote(note) { /* Update UI */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note-aware widget (responds to note changes)
|
||||||
|
class MyNoteWidget extends NoteContextAwareWidget {
|
||||||
|
async refreshWithNote(note) { /* Refresh when note changes */ }
|
||||||
|
async entitiesReloadedEvent({loadResults}) { /* Handle entity updates */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Widgets use jQuery (`this.$widget`) for DOM manipulation. Don't mix React patterns here.
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Running & Testing
|
||||||
|
```bash
|
||||||
|
# From root directory
|
||||||
|
pnpm install # Install dependencies
|
||||||
|
corepack enable # Enable pnpm if not available
|
||||||
|
pnpm server:start # Dev server (http://localhost:8080)
|
||||||
|
pnpm server:start-prod # Production mode server
|
||||||
|
pnpm desktop:start # Desktop app development
|
||||||
|
pnpm server:test spec/etapi/search.spec.ts # Run specific test
|
||||||
|
pnpm test:parallel # Client tests (can run parallel)
|
||||||
|
pnpm test:sequential # Server tests (sequential due to shared DB)
|
||||||
|
pnpm test:all # All tests (parallel + sequential)
|
||||||
|
pnpm coverage # Generate coverage reports
|
||||||
|
pnpm typecheck # Type check all projects
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building
|
||||||
|
```bash
|
||||||
|
pnpm client:build # Build client application
|
||||||
|
pnpm server:build # Build server application
|
||||||
|
pnpm desktop:build # Build desktop application
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Organization
|
||||||
|
- **Server tests** (`apps/server/spec/`): Must run sequentially (shared database state)
|
||||||
|
- **Client tests** (`apps/client/src/`): Can run in parallel
|
||||||
|
- **E2E tests** (`apps/server-e2e/`): Use Playwright for integration testing
|
||||||
|
- **ETAPI tests** (`apps/server/spec/etapi/`): External API contract tests
|
||||||
|
|
||||||
|
**Pattern**: When adding new API endpoints, add tests in `spec/etapi/` following existing patterns (see `search.spec.ts`).
|
||||||
|
|
||||||
|
### Monorepo Navigation
|
||||||
|
```
|
||||||
|
apps/
|
||||||
|
client/ # Frontend (shared by server & desktop)
|
||||||
|
server/ # Node.js backend with REST API
|
||||||
|
desktop/ # Electron wrapper
|
||||||
|
web-clipper/ # Browser extension for saving web content
|
||||||
|
db-compare/ # Database comparison tool
|
||||||
|
dump-db/ # Database export utility
|
||||||
|
edit-docs/ # Documentation editing tools
|
||||||
|
packages/
|
||||||
|
commons/ # Shared types and utilities
|
||||||
|
ckeditor5/ # Custom rich text editor with Trilium-specific plugins
|
||||||
|
codemirror/ # Code editor integration
|
||||||
|
highlightjs/ # Syntax highlighting
|
||||||
|
share-theme/ # Theme for shared/published notes
|
||||||
|
ckeditor5-admonition/ # Admonition blocks plugin
|
||||||
|
ckeditor5-footnotes/ # Footnotes plugin
|
||||||
|
ckeditor5-math/ # Math equations plugin
|
||||||
|
ckeditor5-mermaid/ # Mermaid diagrams plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
**Filter commands**: Use `pnpm --filter server test` to run commands in specific packages.
|
||||||
|
|
||||||
|
## Critical Code Patterns
|
||||||
|
|
||||||
|
### ETAPI Backwards Compatibility
|
||||||
|
When adding query parameters to ETAPI endpoints (`apps/server/src/etapi/`), maintain backwards compatibility by checking if new params exist before changing response format.
|
||||||
|
|
||||||
|
**Pattern**: ETAPI consumers expect specific response shapes. Always check for breaking changes.
|
||||||
|
|
||||||
|
### Frontend-Backend Communication
|
||||||
|
- **REST API**: `apps/server/src/routes/api/` - Internal endpoints (no auth required when `noAuthentication=true`)
|
||||||
|
- **ETAPI**: `apps/server/src/etapi/` - External API with authentication
|
||||||
|
- **WebSocket**: Real-time sync via `apps/server/src/services/ws.ts`
|
||||||
|
|
||||||
|
**Auth note**: ETAPI uses basic auth with tokens. Internal API endpoints trust the frontend.
|
||||||
|
|
||||||
|
### Database Migrations
|
||||||
|
- Add scripts in `apps/server/src/migrations/YYMMDD_HHMM__description.sql`
|
||||||
|
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||||
|
- Never bypass Becca cache after migrations
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
1. **Never bypass the cache layers** - Always use `becca.notes[noteId]`, `froca.getNote()`, or equivalent cache methods. Direct database queries will cause sync issues between Becca/Froca/Shaca and won't create EntityChange records needed for synchronization.
|
||||||
|
|
||||||
|
2. **Protected notes require session check** - Before accessing `note.title` or `note.getContent()` on protected notes, check `note.isContentAvailable()` or use `note.getTitleOrProtected()` which handles this automatically.
|
||||||
|
|
||||||
|
3. **Widget lifecycle matters** - Override `refreshWithNote()` for note changes, `doRenderBody()` for initial render, `entitiesReloadedEvent()` for entity updates. Widgets use jQuery (`this.$widget`) - don't mix React patterns.
|
||||||
|
|
||||||
|
4. **Tests run differently** - Server tests must run sequentially (shared database state), client tests can run in parallel. Use `pnpm test:sequential` for backend, `pnpm test:parallel` for frontend.
|
||||||
|
|
||||||
|
5. **ETAPI requires authentication** - ETAPI endpoints use basic auth with tokens. Internal API endpoints (`apps/server/src/routes/api/`) trust the frontend when `noAuthentication=true`.
|
||||||
|
|
||||||
|
6. **Search expressions are evaluated in memory** - The search service loads all matching notes, scores them in JavaScript, then sorts. You cannot add SQL-level LIMIT/OFFSET without losing scoring functionality.
|
||||||
|
|
||||||
|
7. **Documentation edits have rules** - `docs/Script API/` is auto-generated (never edit directly). `docs/User Guide/` should be edited via `pnpm edit-docs:edit-docs`, not manually. Only `docs/Developer Guide/` and `docs/Release Notes/` are safe for direct Markdown editing.
|
||||||
|
|
||||||
|
8. **pnpm workspace filtering** - Use `pnpm --filter server <command>` or shorthand `pnpm server:test` defined in root `package.json`. Note the `--filter` syntax, not `-F` or other shortcuts.
|
||||||
|
|
||||||
|
9. **Event subscription cleanup** - When subscribing to events in widgets, unsubscribe in `cleanup()` or `doDestroy()` to prevent memory leaks.
|
||||||
|
|
||||||
|
10. **Attribute inheritance can be complex** - When checking for labels/relations, use `note.getOwnedAttribute()` for direct attributes or `note.getAttribute()` for inherited ones. Don't assume attributes are directly on the note.
|
||||||
|
|
||||||
|
## TypeScript Configuration
|
||||||
|
|
||||||
|
- **Project references**: Monorepo uses TypeScript project references (`tsconfig.json`)
|
||||||
|
- **Path mapping**: Use relative imports, not path aliases
|
||||||
|
- **Build order**: `pnpm typecheck` builds all projects in dependency order
|
||||||
|
- **Build system**: Uses Vite for fast development, ESBuild for production optimization
|
||||||
|
- **Patches**: Custom patches in `patches/` directory for CKEditor and other dependencies
|
||||||
|
|
||||||
|
## Key Files for Context
|
||||||
|
|
||||||
|
- `apps/server/src/becca/entities/bnote.ts` - Note entity methods
|
||||||
|
- `apps/client/src/services/froca.ts` - Frontend cache API
|
||||||
|
- `apps/server/src/services/search/services/search.ts` - Search implementation
|
||||||
|
- `apps/server/src/routes/routes.ts` - API route registration
|
||||||
|
- `apps/client/src/widgets/basic_widget.ts` - Widget base class
|
||||||
|
- `apps/server/src/main.ts` - Server startup entry point
|
||||||
|
- `apps/client/src/desktop.ts` - Client initialization
|
||||||
|
- `apps/server/src/services/backend_script_api.ts` - Scripting API
|
||||||
|
- `apps/server/src/assets/db/schema.sql` - Database schema
|
||||||
|
|
||||||
|
## Note Types and Features
|
||||||
|
|
||||||
|
Trilium supports multiple note types with specialized widgets in `apps/client/src/widgets/type_widgets/`:
|
||||||
|
- **Text**: Rich text with CKEditor5 (markdown import/export)
|
||||||
|
- **Code**: Syntax-highlighted code editing with CodeMirror
|
||||||
|
- **File**: Binary file attachments
|
||||||
|
- **Image**: Image display with editing capabilities
|
||||||
|
- **Canvas**: Drawing/diagramming with Excalidraw
|
||||||
|
- **Mermaid**: Diagram generation
|
||||||
|
- **Relation Map**: Visual note relationship mapping
|
||||||
|
- **Web View**: Embedded web pages
|
||||||
|
- **Doc/Book**: Hierarchical documentation structure
|
||||||
|
|
||||||
|
### Collections
|
||||||
|
Notes can be marked with the `#collection` label to enable collection view modes. Collections support multiple view types:
|
||||||
|
- **List**: Standard list view
|
||||||
|
- **Grid**: Card/grid layout
|
||||||
|
- **Calendar**: Calendar-based view
|
||||||
|
- **Table**: Tabular data view
|
||||||
|
- **GeoMap**: Geographic map view
|
||||||
|
- **Board**: Kanban-style board
|
||||||
|
- **Presentation**: Slideshow presentation mode
|
||||||
|
|
||||||
|
View types are configured via `#viewType` label (e.g., `#viewType=table`). Each view mode stores its configuration in a separate attachment (e.g., `table.json`). Collections are organized separately from regular note type templates in the note creation menu.
|
||||||
|
|
||||||
|
## Common Development Tasks
|
||||||
|
|
||||||
|
### Adding New Note Types
|
||||||
|
1. Create widget in `apps/client/src/widgets/type_widgets/`
|
||||||
|
2. Register in `apps/client/src/services/note_types.ts`
|
||||||
|
3. Add backend handling in `apps/server/src/services/notes.ts`
|
||||||
|
|
||||||
|
### Extending Search
|
||||||
|
- Search expressions handled in `apps/server/src/services/search/`
|
||||||
|
- Add new search operators in search context files
|
||||||
|
- Remember: scoring happens in-memory, not at database level
|
||||||
|
|
||||||
|
### Custom CKEditor Plugins
|
||||||
|
- Create new package in `packages/` following existing plugin structure
|
||||||
|
- Register in `packages/ckeditor5/src/plugins.ts`
|
||||||
|
- See `ckeditor5-admonition`, `ckeditor5-footnotes`, `ckeditor5-math`, `ckeditor5-mermaid` for examples
|
||||||
|
|
||||||
|
### Database Migrations
|
||||||
|
- Add migration scripts in `apps/server/src/migrations/YYMMDD_HHMM__description.sql`
|
||||||
|
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||||
|
- Never bypass Becca cache after migrations
|
||||||
|
|
||||||
|
## Security & Features
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
- Per-note encryption with granular protected sessions
|
||||||
|
- CSRF protection for API endpoints
|
||||||
|
- OpenID and TOTP authentication support
|
||||||
|
- Sanitization of user-generated content
|
||||||
|
|
||||||
|
### Scripting System
|
||||||
|
Trilium provides powerful user scripting capabilities:
|
||||||
|
- **Frontend scripts**: Run in browser context with UI access
|
||||||
|
- **Backend scripts**: Run in Node.js context with full API access
|
||||||
|
- Script API documentation in `docs/Script API/`
|
||||||
|
- Backend API available via `api` object in script context
|
||||||
|
|
||||||
|
### Internationalization
|
||||||
|
- Translation files in `apps/client/src/translations/`
|
||||||
|
- Use translation system via `t()` function
|
||||||
|
- Automatic pluralization: Add `_other` suffix to translation keys (e.g., `item` and `item_other` for singular/plural)
|
||||||
|
|
||||||
|
## Testing Conventions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ETAPI test pattern
|
||||||
|
describe("etapi/feature", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
config.General.noAuthentication = false;
|
||||||
|
app = await buildApp();
|
||||||
|
token = await login(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test feature", async () => {
|
||||||
|
const response = await supertest(app)
|
||||||
|
.get("/etapi/notes?search=test")
|
||||||
|
.auth(USER, token, { type: "basic" })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.results).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Questions to Verify Understanding
|
||||||
|
|
||||||
|
Before implementing significant changes, confirm:
|
||||||
|
- Is this touching the cache layer? (Becca/Froca/Shaca must stay in sync via EntityChange records)
|
||||||
|
- Does this change API response shape? (Check backwards compatibility for ETAPI)
|
||||||
|
- Are you adding search features? (Understand expression-based architecture and in-memory scoring first)
|
||||||
|
- Is this a new widget? (Know which base class and lifecycle methods to use)
|
||||||
|
- Does this involve protected notes? (Check `isContentAvailable()` before accessing content)
|
||||||
|
- Is this a long-running operation? (Use TaskContext for progress reporting)
|
||||||
|
- Are you working with attributes? (Understand inheritance patterns: direct, child-prefix, template)
|
||||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
|||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||||
|
|||||||
2
.github/workflows/deploy-docs.yml
vendored
2
.github/workflows/deploy-docs.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
|
|||||||
6
.github/workflows/dev.yml
vendored
6
.github/workflows/dev.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- test_dev
|
- test_dev
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
- dockerfile: Dockerfile
|
- dockerfile: Dockerfile
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
8
.github/workflows/main-docker.yml
vendored
8
.github/workflows/main-docker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- dockerfile: Dockerfile
|
- dockerfile: Dockerfile
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set IMAGE_NAME to lowercase
|
- name: Set IMAGE_NAME to lowercase
|
||||||
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
||||||
@@ -141,7 +141,7 @@ jobs:
|
|||||||
run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
|
run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -155,6 +155,10 @@ jobs:
|
|||||||
- name: Update build info
|
- name: Update build info
|
||||||
run: pnpm run chore:update-build-info
|
run: pnpm run chore:update-build-info
|
||||||
|
|
||||||
|
- name: Update nightly version
|
||||||
|
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
run: pnpm run chore:ci-update-nightly-version
|
||||||
|
|
||||||
- name: Run the TypeScript build
|
- name: Run the TypeScript build
|
||||||
run: pnpm run server:build
|
run: pnpm run server:build
|
||||||
|
|
||||||
|
|||||||
19
.github/workflows/nightly.yml
vendored
19
.github/workflows/nightly.yml
vendored
@@ -45,9 +45,22 @@ jobs:
|
|||||||
image: win-signing
|
image: win-signing
|
||||||
shell: cmd
|
shell: cmd
|
||||||
forge_platform: win32
|
forge_platform: win32
|
||||||
|
# Exclude ARM64 Linux from default matrix to use native runner
|
||||||
|
exclude:
|
||||||
|
- arch: arm64
|
||||||
|
os:
|
||||||
|
name: linux
|
||||||
|
# Add ARM64 Linux with native ubuntu-24.04-arm runner for better-sqlite3 compatibility
|
||||||
|
include:
|
||||||
|
- arch: arm64
|
||||||
|
os:
|
||||||
|
name: linux
|
||||||
|
image: ubuntu-24.04-arm
|
||||||
|
shell: bash
|
||||||
|
forge_platform: linux
|
||||||
runs-on: ${{ matrix.os.image }}
|
runs-on: ${{ matrix.os.image }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -57,7 +70,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
- name: Update nightly version
|
- name: Update nightly version
|
||||||
run: npm run chore:ci-update-nightly-version
|
run: pnpm run chore:ci-update-nightly-version
|
||||||
- name: Run the build
|
- name: Run the build
|
||||||
uses: ./.github/actions/build-electron
|
uses: ./.github/actions/build-electron
|
||||||
with:
|
with:
|
||||||
@@ -109,7 +122,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Run the build
|
- name: Run the build
|
||||||
uses: ./.github/actions/build-server
|
uses: ./.github/actions/build-server
|
||||||
|
|||||||
4
.github/workflows/playwright.yml
vendored
4
.github/workflows/playwright.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
TRILIUM_DATA_DIR: "${{ github.workspace }}/apps/server/spec/db"
|
TRILIUM_DATA_DIR: "${{ github.workspace }}/apps/server/spec/db"
|
||||||
TRILIUM_INTEGRATION_TEST: memory
|
TRILIUM_INTEGRATION_TEST: memory
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
filter: tree:0
|
filter: tree:0
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
if: failure()
|
if: failure()
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: e2e report
|
name: e2e report ${{ matrix.arch }}
|
||||||
path: apps/server-e2e/test-output
|
path: apps/server-e2e/test-output
|
||||||
|
|
||||||
- name: Kill the server
|
- name: Kill the server
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
forge_platform: linux
|
forge_platform: linux
|
||||||
runs-on: ${{ matrix.os.image }}
|
runs-on: ${{ matrix.os.image }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -91,7 +91,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Run the build
|
- name: Run the build
|
||||||
uses: ./.github/actions/build-server
|
uses: ./.github/actions/build-server
|
||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: mkdir upload
|
- run: mkdir upload
|
||||||
|
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
docs/Release Notes
|
docs/Release Notes
|
||||||
|
|||||||
2
.github/workflows/website.yml
vendored
2
.github/workflows/website.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
pull-requests: write # For PR preview comments
|
pull-requests: write # For PR preview comments
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
|
|||||||
@@ -35,19 +35,19 @@
|
|||||||
"chore:generate-openapi": "tsx bin/generate-openapi.js"
|
"chore:generate-openapi": "tsx bin/generate-openapi.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.56.1",
|
"@playwright/test": "1.57.0",
|
||||||
"@stylistic/eslint-plugin": "5.5.0",
|
"@stylistic/eslint-plugin": "5.6.1",
|
||||||
"@types/express": "5.0.5",
|
"@types/express": "5.0.5",
|
||||||
"@types/node": "24.10.1",
|
"@types/node": "24.10.1",
|
||||||
"@types/yargs": "17.0.34",
|
"@types/yargs": "17.0.35",
|
||||||
"@vitest/coverage-v8": "3.2.4",
|
"@vitest/coverage-v8": "4.0.14",
|
||||||
"eslint": "9.39.1",
|
"eslint": "9.39.1",
|
||||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
"jsdoc": "4.0.5",
|
"jsdoc": "4.0.5",
|
||||||
"lorem-ipsum": "2.0.8",
|
"lorem-ipsum": "2.0.8",
|
||||||
"rcedit": "5.0.0",
|
"rcedit": "5.0.2",
|
||||||
"rimraf": "6.1.0",
|
"rimraf": "6.1.2",
|
||||||
"tslib": "2.8.1"
|
"tslib": "2.8.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Elian Doran <contact@eliandoran.me>",
|
"author": "Elian Doran <contact@eliandoran.me>",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"packageManager": "pnpm@10.22.0",
|
"packageManager": "pnpm@10.24.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redocly/cli": "2.11.1",
|
"@redocly/cli": "2.12.0",
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"fs-extra": "11.3.2",
|
"fs-extra": "11.3.2",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"typedoc": "0.28.14",
|
"typedoc": "0.28.15",
|
||||||
"typedoc-plugin-missing-exports": "4.1.2"
|
"typedoc-plugin-missing-exports": "4.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@triliumnext/client",
|
"name": "@triliumnext/client",
|
||||||
"version": "0.99.5",
|
"version": "0.100.0",
|
||||||
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"@fullcalendar/timegrid": "6.1.19",
|
"@fullcalendar/timegrid": "6.1.19",
|
||||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||||
"@mermaid-js/layout-elk": "0.2.0",
|
"@mermaid-js/layout-elk": "0.2.0",
|
||||||
"@mind-elixir/node-menu": "5.0.0",
|
"@mind-elixir/node-menu": "5.0.1",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
"@triliumnext/ckeditor5": "workspace:*",
|
"@triliumnext/ckeditor5": "workspace:*",
|
||||||
"@triliumnext/codemirror": "workspace:*",
|
"@triliumnext/codemirror": "workspace:*",
|
||||||
@@ -36,14 +36,13 @@
|
|||||||
"autocomplete.js": "0.38.1",
|
"autocomplete.js": "0.38.1",
|
||||||
"bootstrap": "5.3.8",
|
"bootstrap": "5.3.8",
|
||||||
"boxicons": "2.1.4",
|
"boxicons": "2.1.4",
|
||||||
"color": "5.0.2",
|
"clsx": "2.1.1",
|
||||||
"dayjs": "1.11.19",
|
"color": "5.0.3",
|
||||||
"dayjs-plugin-utc": "0.1.2",
|
|
||||||
"debounce": "3.0.0",
|
"debounce": "3.0.0",
|
||||||
"draggabilly": "3.0.0",
|
"draggabilly": "3.0.0",
|
||||||
"force-graph": "1.51.0",
|
"force-graph": "1.51.0",
|
||||||
"globals": "16.5.0",
|
"globals": "16.5.0",
|
||||||
"i18next": "25.6.2",
|
"i18next": "25.6.3",
|
||||||
"i18next-http-backend": "3.0.2",
|
"i18next-http-backend": "3.0.2",
|
||||||
"jquery": "3.7.1",
|
"jquery": "3.7.1",
|
||||||
"jquery.fancytree": "2.38.5",
|
"jquery.fancytree": "2.38.5",
|
||||||
@@ -53,13 +52,13 @@
|
|||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"leaflet-gpx": "2.2.0",
|
"leaflet-gpx": "2.2.0",
|
||||||
"mark.js": "8.11.1",
|
"mark.js": "8.11.1",
|
||||||
"marked": "16.4.2",
|
"marked": "17.0.1",
|
||||||
"mermaid": "11.12.1",
|
"mermaid": "11.12.1",
|
||||||
"mind-elixir": "5.3.5",
|
"mind-elixir": "5.3.7",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"panzoom": "9.4.3",
|
"panzoom": "9.4.3",
|
||||||
"preact": "10.27.2",
|
"preact": "10.27.2",
|
||||||
"react-i18next": "16.3.1",
|
"react-i18next": "16.3.5",
|
||||||
"reveal.js": "5.2.1",
|
"reveal.js": "5.2.1",
|
||||||
"svg-pan-zoom": "3.6.2",
|
"svg-pan-zoom": "3.6.2",
|
||||||
"tabulator-tables": "6.3.1",
|
"tabulator-tables": "6.3.1",
|
||||||
@@ -76,7 +75,7 @@
|
|||||||
"@types/reveal.js": "5.2.1",
|
"@types/reveal.js": "5.2.1",
|
||||||
"@types/tabulator-tables": "6.3.0",
|
"@types/tabulator-tables": "6.3.0",
|
||||||
"copy-webpack-plugin": "13.0.1",
|
"copy-webpack-plugin": "13.0.1",
|
||||||
"happy-dom": "20.0.10",
|
"happy-dom": "20.0.11",
|
||||||
"script-loader": "0.7.2",
|
"script-loader": "0.7.2",
|
||||||
"vite-plugin-static-copy": "3.1.4"
|
"vite-plugin-static-copy": "3.1.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -445,6 +445,7 @@ type EventMappings = {
|
|||||||
error: string;
|
error: string;
|
||||||
};
|
};
|
||||||
searchRefreshed: { ntxId?: string | null };
|
searchRefreshed: { ntxId?: string | null };
|
||||||
|
textEditorRefreshed: { ntxId?: string | null, editor: CKTextEditor };
|
||||||
hoistedNoteChanged: {
|
hoistedNoteChanged: {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
ntxId: string | null;
|
ntxId: string | null;
|
||||||
@@ -486,7 +487,7 @@ type EventMappings = {
|
|||||||
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
||||||
activeNoteChanged: {};
|
activeNoteChanged: {ntxId: string | null | undefined};
|
||||||
showAddLinkDialog: AddLinkOpts;
|
showAddLinkDialog: AddLinkOpts;
|
||||||
showIncludeDialog: IncludeNoteOpts;
|
showIncludeDialog: IncludeNoteOpts;
|
||||||
openBulkActionsDialog: {
|
openBulkActionsDialog: {
|
||||||
|
|||||||
@@ -321,6 +321,10 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (note.type === "search") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "")) {
|
if (!["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export default class TabManager extends Component {
|
|||||||
const activeNoteContext = this.getActiveContext();
|
const activeNoteContext = this.getActiveContext();
|
||||||
this.updateDocumentTitle(activeNoteContext);
|
this.updateDocumentTitle(activeNoteContext);
|
||||||
|
|
||||||
this.triggerEvent("activeNoteChanged", {}); // trigger this even in on popstate event
|
this.triggerEvent("activeNoteChanged", {ntxId:activeNoteContext?.ntxId}); // trigger this even in on popstate event
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateHash(): string {
|
calculateHash(): string {
|
||||||
@@ -647,7 +647,32 @@ export default class TabManager extends Component {
|
|||||||
...this.noteContexts.slice(-noteContexts.length),
|
...this.noteContexts.slice(-noteContexts.length),
|
||||||
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
|
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
|
||||||
];
|
];
|
||||||
this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId).filter((id) => id !== null) });
|
|
||||||
|
// Update mainNtxId if the restored pane is the main pane in the split pane
|
||||||
|
const { oldMainNtxId, newMainNtxId } = (() => {
|
||||||
|
if (noteContexts.length !== 1) {
|
||||||
|
return { oldMainNtxId: undefined, newMainNtxId: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainNtxId = noteContexts[0]?.mainNtxId;
|
||||||
|
const index = this.noteContexts.findIndex(c => c.ntxId === mainNtxId);
|
||||||
|
|
||||||
|
// No need to update if the restored position is after mainNtxId
|
||||||
|
if (index === -1 || lastClosedTab.position > index) {
|
||||||
|
return { oldMainNtxId: undefined, newMainNtxId: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
oldMainNtxId: this.noteContexts[index].ntxId ?? undefined,
|
||||||
|
newMainNtxId: noteContexts[0]?.ntxId ?? undefined
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
this.triggerCommand("noteContextReorder", {
|
||||||
|
ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId).filter((id) => id !== null),
|
||||||
|
oldMainNtxId,
|
||||||
|
newMainNtxId
|
||||||
|
});
|
||||||
|
|
||||||
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
|
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
|
||||||
if (mainNtx) {
|
if (mainNtx) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ function initOnElectron() {
|
|||||||
|
|
||||||
initDarkOrLightMode(style);
|
initDarkOrLightMode(style);
|
||||||
initTransparencyEffects(style, currentWindow);
|
initTransparencyEffects(style, currentWindow);
|
||||||
|
initFullScreenDetection(currentWindow);
|
||||||
|
|
||||||
if (options.get("nativeTitleBarVisible") !== "true") {
|
if (options.get("nativeTitleBarVisible") !== "true") {
|
||||||
initTitleBarButtons(style, currentWindow);
|
initTitleBarButtons(style, currentWindow);
|
||||||
@@ -87,6 +88,11 @@ function initTitleBarButtons(style: CSSStyleDeclaration, currentWindow: Electron
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initFullScreenDetection(currentWindow: Electron.BrowserWindow) {
|
||||||
|
currentWindow.on("enter-full-screen", () => document.body.classList.add("full-screen"));
|
||||||
|
currentWindow.on("leave-full-screen", () => document.body.classList.remove("full-screen"));
|
||||||
|
}
|
||||||
|
|
||||||
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
|
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
|
||||||
if (window.glob.platform === "win32") {
|
if (window.glob.platform === "win32") {
|
||||||
const material = style.getPropertyValue("--background-material");
|
const material = style.getPropertyValue("--background-material");
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export default class FNote {
|
|||||||
|
|
||||||
const aNote = this.froca.getNoteFromCache(aNoteId);
|
const aNote = this.froca.getNoteFromCache(aNoteId);
|
||||||
|
|
||||||
if (aNote.isArchived || aNote.isHiddenCompletely()) {
|
if (!aNote || aNote.isArchived || aNote.isHiddenCompletely()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +257,9 @@ export default class FNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getChildNoteIdsWithArchiveFiltering(includeArchived = false) {
|
async getChildNoteIdsWithArchiveFiltering(includeArchived = false) {
|
||||||
if (!includeArchived) {
|
const isHiddenNote = this.noteId.startsWith("_");
|
||||||
|
const isSearchNote = this.type === "search";
|
||||||
|
if (!includeArchived && !isHiddenNote && !isSearchNote) {
|
||||||
const unorderedIds = new Set(await search.searchForNoteIds(`note.parents.noteId="${this.noteId}" #!archived`));
|
const unorderedIds = new Set(await search.searchForNoteIds(`note.parents.noteId="${this.noteId}" #!archived`));
|
||||||
const results: string[] = [];
|
const results: string[] = [];
|
||||||
for (const id of this.children) {
|
for (const id of this.children) {
|
||||||
@@ -804,6 +806,16 @@ export default class FNote {
|
|||||||
return this.getAttributeValue(LABEL, name);
|
return this.getAttributeValue(LABEL, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLabelOrRelation(nameWithPrefix: string) {
|
||||||
|
if (nameWithPrefix.startsWith("#")) {
|
||||||
|
return this.getLabelValue(nameWithPrefix.substring(1));
|
||||||
|
} else if (nameWithPrefix.startsWith("~")) {
|
||||||
|
return this.getRelationValue(nameWithPrefix.substring(1));
|
||||||
|
} else {
|
||||||
|
return this.getLabelValue(nameWithPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name - relation name
|
* @param name - relation name
|
||||||
* @returns relation value if relation exists, null otherwise
|
* @returns relation value if relation exists, null otherwise
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import NoteTreeWidget from "../widgets/note_tree.js";
|
|||||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||||
import options from "../services/options.js";
|
import options from "../services/options.js";
|
||||||
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
|
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
|
||||||
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
|
||||||
import QuickSearchWidget from "../widgets/quick_search.js";
|
import QuickSearchWidget from "../widgets/quick_search.js";
|
||||||
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
|
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
|
||||||
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
||||||
@@ -31,7 +30,6 @@ import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
|||||||
import ScrollPadding from "../widgets/scroll_padding.js";
|
import ScrollPadding from "../widgets/scroll_padding.js";
|
||||||
import SearchResult from "../widgets/search_result.jsx";
|
import SearchResult from "../widgets/search_result.jsx";
|
||||||
import SharedInfo from "../widgets/shared_info.jsx";
|
import SharedInfo from "../widgets/shared_info.jsx";
|
||||||
import OriginInfo from "../widgets/note_origin.jsx";
|
|
||||||
import SpacerWidget from "../widgets/spacer.js";
|
import SpacerWidget from "../widgets/spacer.js";
|
||||||
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
||||||
import SqlResults from "../widgets/sql_result.js";
|
import SqlResults from "../widgets/sql_result.js";
|
||||||
@@ -45,6 +43,8 @@ import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
||||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
import NoteDetail from "../widgets/NoteDetail.jsx";
|
||||||
|
import RightPanelWidget from "../widgets/sidebar/RightPanelWidget.jsx";
|
||||||
|
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@@ -139,10 +139,9 @@ export default class DesktopLayout {
|
|||||||
.filling()
|
.filling()
|
||||||
.child(new ContentHeader()
|
.child(new ContentHeader()
|
||||||
.child(<ReadOnlyNoteInfoBar />)
|
.child(<ReadOnlyNoteInfoBar />)
|
||||||
.child(<OriginInfo />)
|
|
||||||
.child(<SharedInfo />)
|
.child(<SharedInfo />)
|
||||||
)
|
)
|
||||||
.child(new PromotedAttributesWidget())
|
.child(<PromotedAttributes />)
|
||||||
.child(<SqlTableSchemas />)
|
.child(<SqlTableSchemas />)
|
||||||
.child(<NoteDetail />)
|
.child(<NoteDetail />)
|
||||||
.child(<NoteList media="screen" />)
|
.child(<NoteList media="screen" />)
|
||||||
|
|||||||
@@ -22,16 +22,8 @@ import RevisionsDialog from "../widgets/dialogs/revisions.js";
|
|||||||
import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
|
import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
|
||||||
import InfoDialog from "../widgets/dialogs/info.js";
|
import InfoDialog from "../widgets/dialogs/info.js";
|
||||||
import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
|
import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
|
||||||
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
|
|
||||||
import FlexContainer from "../widgets/containers/flex_container.js";
|
|
||||||
import NoteIconWidget from "../widgets/note_icon";
|
|
||||||
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
|
||||||
import CallToActionDialog from "../widgets/dialogs/call_to_action.jsx";
|
import CallToActionDialog from "../widgets/dialogs/call_to_action.jsx";
|
||||||
import NoteTitleWidget from "../widgets/note_title.jsx";
|
import PopupEditorDialog from "../widgets/dialogs/PopupEditor.jsx";
|
||||||
import FormattingToolbar from "../widgets/ribbon/FormattingToolbar.js";
|
|
||||||
import NoteList from "../widgets/collections/NoteList.jsx";
|
|
||||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
|
||||||
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
|
||||||
|
|
||||||
export function applyModals(rootContainer: RootContainer) {
|
export function applyModals(rootContainer: RootContainer) {
|
||||||
rootContainer
|
rootContainer
|
||||||
@@ -57,16 +49,6 @@ export function applyModals(rootContainer: RootContainer) {
|
|||||||
.child(<ConfirmDialog />)
|
.child(<ConfirmDialog />)
|
||||||
.child(<PromptDialog />)
|
.child(<PromptDialog />)
|
||||||
.child(<IncorrectCpuArchDialog />)
|
.child(<IncorrectCpuArchDialog />)
|
||||||
.child(new PopupEditorDialog()
|
.child(<PopupEditorDialog />)
|
||||||
.child(new FlexContainer("row")
|
|
||||||
.class("title-row")
|
|
||||||
.css("align-items", "center")
|
|
||||||
.cssBlock(".title-row > * { margin: 5px; }")
|
|
||||||
.child(<NoteIconWidget />)
|
|
||||||
.child(<NoteTitleWidget />))
|
|
||||||
.child(<StandaloneRibbonAdapter component={FormattingToolbar} />)
|
|
||||||
.child(new PromotedAttributesWidget())
|
|
||||||
.child(<NoteDetail />)
|
|
||||||
.child(<NoteList media="screen" displayOnlyCollections />))
|
|
||||||
.child(<CallToActionDialog />);
|
.child(<CallToActionDialog />);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import NoteTitleWidget from "../widgets/note_title.js";
|
|||||||
import ContentHeader from "../widgets/containers/content_header.js";
|
import ContentHeader from "../widgets/containers/content_header.js";
|
||||||
import NoteTreeWidget from "../widgets/note_tree.js";
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||||
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
|
||||||
import QuickSearchWidget from "../widgets/quick_search.js";
|
import QuickSearchWidget from "../widgets/quick_search.js";
|
||||||
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
|
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
|
||||||
import RootContainer from "../widgets/containers/root_container.js";
|
import RootContainer from "../widgets/containers/root_container.js";
|
||||||
@@ -29,9 +28,12 @@ import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button
|
|||||||
import type AppContext from "../components/app_context.js";
|
import type AppContext from "../components/app_context.js";
|
||||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
import NoteDetail from "../widgets/NoteDetail.jsx";
|
||||||
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
|
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
|
||||||
|
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
||||||
|
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
||||||
|
|
||||||
const MOBILE_CSS = `
|
const MOBILE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
|
span.keyboard-shortcut,
|
||||||
kbd {
|
kbd {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -141,6 +143,7 @@ export default class MobileLayout {
|
|||||||
.id("detail-container")
|
.id("detail-container")
|
||||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-9")
|
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-9")
|
||||||
.child(
|
.child(
|
||||||
|
new SplitNoteContainer(() =>
|
||||||
new NoteWrapperWidget()
|
new NoteWrapperWidget()
|
||||||
.child(
|
.child(
|
||||||
new FlexContainer("row")
|
new FlexContainer("row")
|
||||||
@@ -152,7 +155,7 @@ export default class MobileLayout {
|
|||||||
.child(<MobileDetailMenu />)
|
.child(<MobileDetailMenu />)
|
||||||
)
|
)
|
||||||
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
||||||
.child(new PromotedAttributesWidget())
|
.child(<PromotedAttributes />)
|
||||||
.child(
|
.child(
|
||||||
new ScrollingContainer()
|
new ScrollingContainer()
|
||||||
.filling()
|
.filling()
|
||||||
@@ -171,6 +174,7 @@ export default class MobileLayout {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
new FlexContainer("column")
|
new FlexContainer("column")
|
||||||
.contentSized()
|
.contentSized()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { KeyboardActionNames } from "@triliumnext/commons";
|
|||||||
import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js";
|
import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js";
|
||||||
import note_tooltip from "../services/note_tooltip.js";
|
import note_tooltip from "../services/note_tooltip.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import { should } from "vitest";
|
import { h, JSX, render } from "preact";
|
||||||
|
|
||||||
export interface ContextMenuOptions<T> {
|
export interface ContextMenuOptions<T> {
|
||||||
x: number;
|
x: number;
|
||||||
@@ -15,6 +15,11 @@ export interface ContextMenuOptions<T> {
|
|||||||
onHide?: () => void;
|
onHide?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CustomMenuItem {
|
||||||
|
kind: "custom",
|
||||||
|
componentFn: () => JSX.Element | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MenuSeparatorItem {
|
export interface MenuSeparatorItem {
|
||||||
kind: "separator";
|
kind: "separator";
|
||||||
}
|
}
|
||||||
@@ -51,7 +56,7 @@ export interface MenuCommandItem<T> {
|
|||||||
columns?: number;
|
columns?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MenuItem<T> = MenuCommandItem<T> | MenuSeparatorItem | MenuHeader;
|
export type MenuItem<T> = MenuCommandItem<T> | CustomMenuItem | MenuSeparatorItem | MenuHeader;
|
||||||
export type MenuHandler<T> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
|
export type MenuHandler<T> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
|
||||||
export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent;
|
export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent;
|
||||||
|
|
||||||
@@ -160,16 +165,19 @@ class ContextMenu {
|
|||||||
let $group = $parent; // The current group or parent element to which items are being appended
|
let $group = $parent; // The current group or parent element to which items are being appended
|
||||||
let shouldStartNewGroup = false; // If true, the next item will start a new group
|
let shouldStartNewGroup = false; // If true, the next item will start a new group
|
||||||
let shouldResetGroup = false; // If true, the next item will be the last one from the group
|
let shouldResetGroup = false; // If true, the next item will be the last one from the group
|
||||||
|
let prevItemKind: string = "";
|
||||||
|
|
||||||
for (let index = 0; index < items.length; index++) {
|
for (let index = 0; index < items.length; index++) {
|
||||||
const item = items[index];
|
const item = items[index];
|
||||||
|
const itemKind = ("kind" in item) ? item.kind : "";
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current item is a header, start a new group. This group will contain the
|
// If the current item is a header, start a new group. This group will contain the
|
||||||
// header and the next item that follows the header.
|
// header and the next item that follows the header.
|
||||||
if ("kind" in item && item.kind === "header") {
|
if (itemKind === "header") {
|
||||||
if (multicolumn && !shouldResetGroup) {
|
if (multicolumn && !shouldResetGroup) {
|
||||||
shouldStartNewGroup = true;
|
shouldStartNewGroup = true;
|
||||||
}
|
}
|
||||||
@@ -195,13 +203,47 @@ class ContextMenu {
|
|||||||
shouldStartNewGroup = false;
|
shouldStartNewGroup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("kind" in item && item.kind === "separator") {
|
if (itemKind === "separator") {
|
||||||
|
if (prevItemKind === "separator") {
|
||||||
|
// Skip consecutive separators
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$group.append($("<div>").addClass("dropdown-divider"));
|
$group.append($("<div>").addClass("dropdown-divider"));
|
||||||
shouldResetGroup = true; // End the group after the next item
|
shouldResetGroup = true; // End the group after the next item
|
||||||
} else if ("kind" in item && item.kind === "header") {
|
} else if (itemKind === "header") {
|
||||||
$group.append($("<h6>").addClass("dropdown-header").text(item.title));
|
$group.append($("<h6>").addClass("dropdown-header").text((item as MenuHeader).title));
|
||||||
shouldResetGroup = true;
|
shouldResetGroup = true;
|
||||||
} else {
|
} else {
|
||||||
|
if (itemKind === "custom") {
|
||||||
|
// Custom menu item
|
||||||
|
$group.append(this.createCustomMenuItem(item as CustomMenuItem));
|
||||||
|
} else {
|
||||||
|
// Standard menu item
|
||||||
|
$group.append(this.createMenuItem(item as MenuCommandItem<any>));
|
||||||
|
}
|
||||||
|
|
||||||
|
// After adding a menu item, if the previous item was a separator or header,
|
||||||
|
// reset the group so that the next item will be appended directly to the parent.
|
||||||
|
if (shouldResetGroup) {
|
||||||
|
$group = $parent;
|
||||||
|
shouldResetGroup = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
prevItemKind = itemKind;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createCustomMenuItem(item: CustomMenuItem) {
|
||||||
|
const element = document.createElement("li");
|
||||||
|
element.classList.add("dropdown-custom-item");
|
||||||
|
element.onclick = () => this.hide();
|
||||||
|
render(h(item.componentFn, {}), element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMenuItem(item: MenuCommandItem<any>) {
|
||||||
const $icon = $("<span>");
|
const $icon = $("<span>");
|
||||||
|
|
||||||
if ("uiIcon" in item || "checked" in item) {
|
if ("uiIcon" in item || "checked" in item) {
|
||||||
@@ -257,8 +299,6 @@ class ContextMenu {
|
|||||||
// important to use mousedown instead of click since the former does not change focus
|
// important to use mousedown instead of click since the former does not change focus
|
||||||
// (especially important for focused text for spell check)
|
// (especially important for focused text for spell check)
|
||||||
.on("mousedown", (e) => {
|
.on("mousedown", (e) => {
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (e.which !== 1) {
|
if (e.which !== 1) {
|
||||||
// only left click triggers menu items
|
// only left click triggers menu items
|
||||||
return false;
|
return false;
|
||||||
@@ -272,6 +312,11 @@ class ContextMenu {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent submenu from failing to expand on mobile
|
||||||
|
if (!("items" in item && item.items)) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
if ("handler" in item && item.handler) {
|
if ("handler" in item && item.handler) {
|
||||||
item.handler(item, e);
|
item.handler(item, e);
|
||||||
}
|
}
|
||||||
@@ -283,16 +328,6 @@ class ContextMenu {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$item.on("mouseup", (e) => {
|
|
||||||
// Prevent submenu from failing to expand on mobile
|
|
||||||
if (!this.isMobile || !("items" in item && item.items)) {
|
|
||||||
e.stopPropagation();
|
|
||||||
// Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below.
|
|
||||||
this.hide();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if ("enabled" in item && item.enabled !== undefined && !item.enabled) {
|
if ("enabled" in item && item.enabled !== undefined && !item.enabled) {
|
||||||
$item.addClass("disabled");
|
$item.addClass("disabled");
|
||||||
}
|
}
|
||||||
@@ -311,17 +346,7 @@ class ContextMenu {
|
|||||||
|
|
||||||
$item.append($subMenu);
|
$item.append($subMenu);
|
||||||
}
|
}
|
||||||
|
return $item;
|
||||||
$group.append($item);
|
|
||||||
|
|
||||||
// After adding a menu item, if the previous item was a separator or header,
|
|
||||||
// reset the group so that the next item will be appended directly to the parent.
|
|
||||||
if (shouldResetGroup) {
|
|
||||||
$group = $parent;
|
|
||||||
shouldResetGroup = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async hide() {
|
async hide() {
|
||||||
|
|||||||
21
apps/client/src/menus/context_menu_utils.ts
Normal file
21
apps/client/src/menus/context_menu_utils.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { t } from "../services/i18n"
|
||||||
|
import attributes from "../services/attributes"
|
||||||
|
import FNote from "../entities/fnote"
|
||||||
|
|
||||||
|
export function getArchiveMenuItem(note: FNote) {
|
||||||
|
if (!note.isArchived) {
|
||||||
|
return {
|
||||||
|
title: t("board_view.archive-note"),
|
||||||
|
uiIcon: "bx bx-archive",
|
||||||
|
handler: () => attributes.addLabel(note.noteId, "archived")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
title: t("board_view.unarchive-note"),
|
||||||
|
uiIcon: "bx bx-archive-out",
|
||||||
|
handler: async () => {
|
||||||
|
attributes.removeOwnedLabelByName(note, "archived")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
apps/client/src/menus/custom-items/NoteColorPicker.css
Normal file
86
apps/client/src/menus/custom-items/NoteColorPicker.css
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
:root {
|
||||||
|
--note-color-picker-clear-color-cell-background: var(--primary-button-background-color);
|
||||||
|
--note-color-picker-clear-color-cell-color: var(--main-background-color);
|
||||||
|
--note-color-picker-clear-color-cell-selection-outline-color: var(--primary-button-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .color-cell {
|
||||||
|
--color-picker-cell-size: 14px;
|
||||||
|
|
||||||
|
width: var(--color-picker-cell-size);
|
||||||
|
height: var(--color-picker-cell-size);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .color-cell:not(.selected):hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .color-cell.disabled-color-cell {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .color-cell.selected {
|
||||||
|
outline: 2px solid var(--outline-color, var(--color));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RESET COLOR CELL
|
||||||
|
*/
|
||||||
|
|
||||||
|
.note-color-picker .color-cell-reset {
|
||||||
|
--color: var(--note-color-picker-clear-color-cell-background);
|
||||||
|
--outline-color: var(--note-color-picker-clear-color-cell-selection-outline-color);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .color-cell-reset svg {
|
||||||
|
width: var(--color-picker-cell-size);
|
||||||
|
height: var(--color-picker-cell-size);
|
||||||
|
fill: var(--note-color-picker-clear-color-cell-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CUSTOM COLOR CELL
|
||||||
|
*/
|
||||||
|
|
||||||
|
.note-color-picker .custom-color-cell::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "\ed35";
|
||||||
|
display: flex;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
font-size: calc(var(--color-picker-cell-size) * 1.3);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: boxicons;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .custom-color-cell {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker .custom-color-cell.custom-color-cell-empty {
|
||||||
|
background-image: url(./custom-color.png);
|
||||||
|
background-size: cover;
|
||||||
|
--foreground: transparent;
|
||||||
|
}
|
||||||
204
apps/client/src/menus/custom-items/NoteColorPicker.tsx
Normal file
204
apps/client/src/menus/custom-items/NoteColorPicker.tsx
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import "./NoteColorPicker.css";
|
||||||
|
import { t } from "../../services/i18n";
|
||||||
|
import { useCallback, useEffect, useRef, useState} from "preact/hooks";
|
||||||
|
import {ComponentChildren} from "preact";
|
||||||
|
import attributes from "../../services/attributes";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import Color, { ColorInstance } from "color";
|
||||||
|
import Debouncer from "../../utils/debouncer";
|
||||||
|
import FNote from "../../entities/fnote";
|
||||||
|
import froca from "../../services/froca";
|
||||||
|
import { isMobile } from "../../services/utils";
|
||||||
|
|
||||||
|
const COLOR_PALETTE = [
|
||||||
|
"#e64d4d", "#e6994d", "#e5e64d", "#99e64d", "#4de64d", "#4de699",
|
||||||
|
"#4de5e6", "#4d99e6", "#4d4de6", "#994de6", "#e64db3"
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface NoteColorPickerProps {
|
||||||
|
/** The target Note instance or its ID string. */
|
||||||
|
note: FNote | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NoteColorPicker(props: NoteColorPickerProps) {
|
||||||
|
if (!props.note) return null;
|
||||||
|
|
||||||
|
const [note, setNote] = useState<FNote | null>(null);
|
||||||
|
const [currentColor, setCurrentColor] = useState<string | null>(null);
|
||||||
|
const [isCustomColor, setIsCustomColor] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const retrieveNote = async (noteId: string) => {
|
||||||
|
const noteInstance = await froca.getNote(noteId, true);
|
||||||
|
if (noteInstance) {
|
||||||
|
setNote(noteInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.note === "string") {
|
||||||
|
retrieveNote(props.note); // Get the note from the given ID string
|
||||||
|
} else {
|
||||||
|
setNote(props.note);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const colorLabel = note?.getLabel("color")?.value ?? null;
|
||||||
|
if (colorLabel) {
|
||||||
|
let color = tryParseColor(colorLabel);
|
||||||
|
if (color) {
|
||||||
|
setCurrentColor(color.hex().toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [note]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsCustomColor(currentColor !== null && COLOR_PALETTE.indexOf(currentColor) === -1);
|
||||||
|
}, [currentColor])
|
||||||
|
|
||||||
|
const onColorCellClicked = useCallback((color: string | null) => {
|
||||||
|
if (note) {
|
||||||
|
if (color !== null) {
|
||||||
|
attributes.setLabel(note.noteId, "color", color);
|
||||||
|
} else {
|
||||||
|
attributes.removeOwnedLabelByName(note, "color");
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentColor(color);
|
||||||
|
}
|
||||||
|
}, [note, currentColor]);
|
||||||
|
|
||||||
|
return <div className="note-color-picker">
|
||||||
|
|
||||||
|
<ColorCell className="color-cell-reset"
|
||||||
|
tooltip={t("note-color.clear-color")}
|
||||||
|
color={null}
|
||||||
|
isSelected={(currentColor === null)}
|
||||||
|
isDisabled={(note === null)}
|
||||||
|
onSelect={onColorCellClicked}>
|
||||||
|
|
||||||
|
{/* https://pictogrammers.com/library/mdi/icon/close/ */}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||||
|
</svg>
|
||||||
|
</ColorCell>
|
||||||
|
|
||||||
|
|
||||||
|
{COLOR_PALETTE.map((color) => (
|
||||||
|
<ColorCell key={color}
|
||||||
|
tooltip={t("note-color.set-color")}
|
||||||
|
color={color}
|
||||||
|
isSelected={(color === currentColor)}
|
||||||
|
isDisabled={(note === null)}
|
||||||
|
onSelect={onColorCellClicked} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
<CustomColorCell tooltip={t("note-color.set-custom-color")}
|
||||||
|
color={currentColor}
|
||||||
|
isSelected={isCustomColor}
|
||||||
|
isDisabled={(note === null)}
|
||||||
|
onSelect={onColorCellClicked} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ColorCellProps {
|
||||||
|
children?: ComponentChildren,
|
||||||
|
className?: string,
|
||||||
|
tooltip?: string,
|
||||||
|
color: string | null,
|
||||||
|
isSelected: boolean,
|
||||||
|
isDisabled?: boolean,
|
||||||
|
onSelect?: (color: string | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function ColorCell(props: ColorCellProps) {
|
||||||
|
return <div className={clsx(props.className, {
|
||||||
|
"color-cell": true,
|
||||||
|
"selected": props.isSelected,
|
||||||
|
"disabled-color-cell": props.isDisabled
|
||||||
|
})}
|
||||||
|
style={`${(props.color !== null) ? `--color: ${props.color}` : ""}`}
|
||||||
|
title={props.tooltip}
|
||||||
|
onClick={() => props.onSelect?.(props.color)}>
|
||||||
|
{props.children}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CustomColorCell(props: ColorCellProps) {
|
||||||
|
const [pickedColor, setPickedColor] = useState<string | null>(null);
|
||||||
|
const colorInput = useRef<HTMLInputElement>(null);
|
||||||
|
const colorInputDebouncer = useRef<Debouncer<string | null> | null>(null);
|
||||||
|
const callbackRef = useRef(props.onSelect);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
colorInputDebouncer.current = new Debouncer(250, (color) => {
|
||||||
|
callbackRef.current?.(color);
|
||||||
|
setPickedColor(color);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
colorInputDebouncer.current?.destroy();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isSelected && pickedColor === null) {
|
||||||
|
setPickedColor(props.color);
|
||||||
|
}
|
||||||
|
}, [props.isSelected])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
callbackRef.current = props.onSelect;
|
||||||
|
}, [props.onSelect]);
|
||||||
|
|
||||||
|
const onSelect = useCallback(() => {
|
||||||
|
if (pickedColor !== null) {
|
||||||
|
callbackRef.current?.(pickedColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
colorInput.current?.click();
|
||||||
|
}, [pickedColor]);
|
||||||
|
|
||||||
|
return <div style={`--foreground: ${getForegroundColor(props.color)};`}
|
||||||
|
onClick={isMobile() ? (e) => {
|
||||||
|
// The color picker dropdown will close on some browser if the parent context menu is
|
||||||
|
// dismissed, so stop the click propagation to prevent dismissing the menu.
|
||||||
|
e.stopPropagation();
|
||||||
|
} : undefined}>
|
||||||
|
<ColorCell {...props}
|
||||||
|
color={pickedColor}
|
||||||
|
className={clsx("custom-color-cell", {
|
||||||
|
"custom-color-cell-empty": (pickedColor === null)
|
||||||
|
})}
|
||||||
|
onSelect={onSelect}>
|
||||||
|
|
||||||
|
<input ref={colorInput}
|
||||||
|
type="color"
|
||||||
|
value={pickedColor ?? props.color ?? "#40bfbf"}
|
||||||
|
onChange={() => {colorInputDebouncer.current?.updateValue(colorInput.current?.value ?? null)}}
|
||||||
|
style="width: 0; height: 0; opacity: 0" />
|
||||||
|
</ColorCell>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function getForegroundColor(backgroundColor: string | null) {
|
||||||
|
if (backgroundColor === null) return "inherit";
|
||||||
|
|
||||||
|
const colorHsl = tryParseColor(backgroundColor)?.hsl();
|
||||||
|
if (colorHsl) {
|
||||||
|
let l = colorHsl.lightness();
|
||||||
|
return colorHsl.saturationl(0).lightness(l >= 50 ? 0 : 100).hex();
|
||||||
|
} else {
|
||||||
|
return "inherit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryParseColor(colorStr: string): ColorInstance | null {
|
||||||
|
try {
|
||||||
|
return new Color(colorStr);
|
||||||
|
} catch(ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
BIN
apps/client/src/menus/custom-items/custom-color.png
Normal file
BIN
apps/client/src/menus/custom-items/custom-color.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -4,7 +4,7 @@ import zoomService from "../components/zoom.js";
|
|||||||
import contextMenu, { type MenuItem } from "./context_menu.js";
|
import contextMenu, { type MenuItem } from "./context_menu.js";
|
||||||
import { t } from "../services/i18n.js";
|
import { t } from "../services/i18n.js";
|
||||||
import type { BrowserWindow } from "electron";
|
import type { BrowserWindow } from "electron";
|
||||||
import type { CommandNames } from "../components/app_context.js";
|
import type { CommandNames, AppContext } from "../components/app_context.js";
|
||||||
|
|
||||||
function setupContextMenu() {
|
function setupContextMenu() {
|
||||||
const electron = utils.dynamicRequire("electron");
|
const electron = utils.dynamicRequire("electron");
|
||||||
@@ -13,6 +13,8 @@ function setupContextMenu() {
|
|||||||
// FIXME: Remove typecast once Electron is properly integrated.
|
// FIXME: Remove typecast once Electron is properly integrated.
|
||||||
const { webContents } = remote.getCurrentWindow() as BrowserWindow;
|
const { webContents } = remote.getCurrentWindow() as BrowserWindow;
|
||||||
|
|
||||||
|
let appContext: AppContext;
|
||||||
|
|
||||||
webContents.on("context-menu", (event, params) => {
|
webContents.on("context-menu", (event, params) => {
|
||||||
const { editFlags } = params;
|
const { editFlags } = params;
|
||||||
const hasText = params.selectionText.trim().length > 0;
|
const hasText = params.selectionText.trim().length > 0;
|
||||||
@@ -119,6 +121,20 @@ function setupContextMenu() {
|
|||||||
uiIcon: "bx bx-search-alt",
|
uiIcon: "bx bx-search-alt",
|
||||||
handler: () => electron.shell.openExternal(searchUrl)
|
handler: () => electron.shell.openExternal(searchUrl)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
title: t("electron_context_menu.search_in_trilium", { term: shortenedSelection }),
|
||||||
|
uiIcon: "bx bx-search",
|
||||||
|
handler: async () => {
|
||||||
|
if (!appContext) {
|
||||||
|
appContext = (await import("../components/app_context.js")).default;
|
||||||
|
}
|
||||||
|
|
||||||
|
await appContext.triggerCommand("searchNotes", {
|
||||||
|
searchString: params.selectionText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
|
|||||||
@@ -2,26 +2,32 @@ import { t } from "../services/i18n.js";
|
|||||||
import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js";
|
import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js";
|
||||||
import appContext, { type CommandNames } from "../components/app_context.js";
|
import appContext, { type CommandNames } from "../components/app_context.js";
|
||||||
import type { ViewScope } from "../services/link.js";
|
import type { ViewScope } from "../services/link.js";
|
||||||
|
import utils, { isMobile } from "../services/utils.js";
|
||||||
|
import { getClosestNtxId } from "../widgets/widget_utils.js";
|
||||||
|
import type { LeafletMouseEvent } from "leaflet";
|
||||||
|
|
||||||
function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) {
|
function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) {
|
||||||
contextMenu.show({
|
contextMenu.show({
|
||||||
x: e.pageX,
|
x: e.pageX,
|
||||||
y: e.pageY,
|
y: e.pageY,
|
||||||
items: getItems(),
|
items: getItems(e),
|
||||||
selectMenuItemHandler: ({ command }) => handleLinkContextMenuItem(command, notePath, viewScope, hoistedNoteId)
|
selectMenuItemHandler: ({ command }) => handleLinkContextMenuItem(command, e, notePath, viewScope, hoistedNoteId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItems(): MenuItem<CommandNames>[] {
|
function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandNames>[] {
|
||||||
|
const ntxId = getNtxId(e);
|
||||||
|
const isMobileSplitOpen = isMobile() && appContext.tabManager.getNoteContextById(ntxId).getMainContext().getSubContexts().length > 1;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
|
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
|
||||||
{ title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
|
{ title: !isMobileSplitOpen ? t("link_context_menu.open_note_in_new_split") : t("link_context_menu.open_note_in_other_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
|
||||||
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" },
|
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" },
|
||||||
{ title: t("link_context_menu.open_note_in_popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit" }
|
{ title: t("link_context_menu.open_note_in_popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit" }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | LeafletMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||||
if (!hoistedNoteId) {
|
if (!hoistedNoteId) {
|
||||||
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
||||||
}
|
}
|
||||||
@@ -29,15 +35,8 @@ function handleLinkContextMenuItem(command: string | undefined, notePath: string
|
|||||||
if (command === "openNoteInNewTab") {
|
if (command === "openNoteInNewTab") {
|
||||||
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
||||||
} else if (command === "openNoteInNewSplit") {
|
} else if (command === "openNoteInNewSplit") {
|
||||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
const ntxId = getNtxId(e);
|
||||||
|
if (!ntxId) return;
|
||||||
if (!subContexts) {
|
|
||||||
logError("subContexts is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ntxId } = subContexts[subContexts.length - 1];
|
|
||||||
|
|
||||||
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
||||||
} else if (command === "openNoteInNewWindow") {
|
} else if (command === "openNoteInNewWindow") {
|
||||||
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
|
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
|
||||||
@@ -46,6 +45,18 @@ function handleLinkContextMenuItem(command: string | undefined, notePath: string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) {
|
||||||
|
if (utils.isDesktop()) {
|
||||||
|
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||||
|
if (!subContexts) return null;
|
||||||
|
return subContexts[subContexts.length - 1].ntxId;
|
||||||
|
} else if (e.target instanceof HTMLElement) {
|
||||||
|
return getClosestNtxId(e.target);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getItems,
|
getItems,
|
||||||
handleLinkContextMenuItem,
|
handleLinkContextMenuItem,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import NoteColorPicker from "./custom-items/NoteColorPicker.jsx";
|
||||||
import treeService from "../services/tree.js";
|
import treeService from "../services/tree.js";
|
||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
import clipboard from "../services/clipboard.js";
|
import clipboard from "../services/clipboard.js";
|
||||||
@@ -139,7 +140,13 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
|||||||
uiIcon: "bx bx-rename",
|
uiIcon: "bx bx-rename",
|
||||||
enabled: isNotRoot && parentNotSearch && notOptionsOrHelp
|
enabled: isNotRoot && parentNotSearch && notOptionsOrHelp
|
||||||
},
|
},
|
||||||
{ title: t("tree-context-menu.convert-to-attachment"), command: "convertNoteToAttachment", uiIcon: "bx bx-paperclip", enabled: isNotRoot && !isHoisted && notOptionsOrHelp },
|
{
|
||||||
|
title:
|
||||||
|
t("tree-context-menu.convert-to-attachment"),
|
||||||
|
command: "convertNoteToAttachment",
|
||||||
|
uiIcon: "bx bx-paperclip",
|
||||||
|
enabled: isNotRoot && !isHoisted && notOptionsOrHelp && selectedNotes.some(note => note.isEligibleForConversionToAttachment())
|
||||||
|
},
|
||||||
|
|
||||||
{ kind: "separator" },
|
{ kind: "separator" },
|
||||||
|
|
||||||
@@ -243,6 +250,15 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
|||||||
|
|
||||||
{ kind: "separator"},
|
{ kind: "separator"},
|
||||||
|
|
||||||
|
(notOptionsOrHelp && selectedNotes.length === 1) ? {
|
||||||
|
kind: "custom",
|
||||||
|
componentFn: () => {
|
||||||
|
return NoteColorPicker({note});
|
||||||
|
}
|
||||||
|
} : null,
|
||||||
|
|
||||||
|
{ kind: "separator" },
|
||||||
|
|
||||||
{ title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-import", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
|
{ title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-import", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
|
||||||
|
|
||||||
{ title: t("tree-context-menu.export"), command: "exportNote", uiIcon: "bx bx-export", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
|
{ title: t("tree-context-menu.export"), command: "exportNote", uiIcon: "bx bx-export", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import FNote from "./entities/fnote";
|
import FNote from "./entities/fnote";
|
||||||
import { render } from "preact";
|
import { render } from "preact";
|
||||||
import { CustomNoteList } from "./widgets/collections/NoteList";
|
import { CustomNoteList, useNoteViewType } from "./widgets/collections/NoteList";
|
||||||
import { useCallback, useLayoutEffect, useRef } from "preact/hooks";
|
import { useCallback, useLayoutEffect, useRef } from "preact/hooks";
|
||||||
import content_renderer from "./services/content_renderer";
|
import content_renderer from "./services/content_renderer";
|
||||||
|
import { dynamicRequire, isElectron } from "./services/utils";
|
||||||
|
import { applyInlineMermaid } from "./services/content_renderer_text";
|
||||||
|
|
||||||
interface RendererProps {
|
interface RendererProps {
|
||||||
note: FNote;
|
note: FNote;
|
||||||
onReady: () => void;
|
onReady: () => void;
|
||||||
|
onProgressChanged?: (progress: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -23,13 +26,21 @@ async function main() {
|
|||||||
|
|
||||||
function App({ note, noteId }: { note: FNote | null | undefined, noteId: string }) {
|
function App({ note, noteId }: { note: FNote | null | undefined, noteId: string }) {
|
||||||
const sentReadyEvent = useRef(false);
|
const sentReadyEvent = useRef(false);
|
||||||
|
const onProgressChanged = useCallback((progress: number) => {
|
||||||
|
if (isElectron()) {
|
||||||
|
const { ipcRenderer } = dynamicRequire('electron');
|
||||||
|
ipcRenderer.send("print-progress", progress);
|
||||||
|
} else {
|
||||||
|
window.dispatchEvent(new CustomEvent("note-load-progress", { detail: { progress } }));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
const onReady = useCallback(() => {
|
const onReady = useCallback(() => {
|
||||||
if (sentReadyEvent.current) return;
|
if (sentReadyEvent.current) return;
|
||||||
window.dispatchEvent(new Event("note-ready"));
|
window.dispatchEvent(new Event("note-ready"));
|
||||||
window._noteReady = true;
|
window._noteReady = true;
|
||||||
sentReadyEvent.current = true;
|
sentReadyEvent.current = true;
|
||||||
}, []);
|
}, []);
|
||||||
const props: RendererProps | undefined | null = note && { note, onReady };
|
const props: RendererProps | undefined | null = note && { note, onReady, onProgressChanged };
|
||||||
|
|
||||||
if (!note || !props) return <Error404 noteId={noteId} />
|
if (!note || !props) return <Error404 noteId={noteId} />
|
||||||
|
|
||||||
@@ -71,6 +82,11 @@ function SingleNoteRenderer({ note, onReady }: RendererProps) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize mermaid.
|
||||||
|
if (note.type === "text") {
|
||||||
|
await applyInlineMermaid(container);
|
||||||
|
}
|
||||||
|
|
||||||
// Check custom CSS.
|
// Check custom CSS.
|
||||||
await loadCustomCss(note);
|
await loadCustomCss(note);
|
||||||
}
|
}
|
||||||
@@ -84,8 +100,10 @@ function SingleNoteRenderer({ note, onReady }: RendererProps) {
|
|||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CollectionRenderer({ note, onReady }: RendererProps) {
|
function CollectionRenderer({ note, onReady, onProgressChanged }: RendererProps) {
|
||||||
|
const viewType = useNoteViewType(note);
|
||||||
return <CustomNoteList
|
return <CustomNoteList
|
||||||
|
viewType={viewType}
|
||||||
isEnabled
|
isEnabled
|
||||||
note={note}
|
note={note}
|
||||||
notePath={note.getBestNotePath().join("/")}
|
notePath={note.getBestNotePath().join("/")}
|
||||||
@@ -96,6 +114,7 @@ function CollectionRenderer({ note, onReady }: RendererProps) {
|
|||||||
await loadCustomCss(note);
|
await loadCustomCss(note);
|
||||||
onReady();
|
onReady();
|
||||||
}}
|
}}
|
||||||
|
onProgressChanged={onProgressChanged}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ export async function setLabel(noteId: string, name: string, value: string = "",
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setRelation(noteId: string, name: string, value: string = "", isInheritable = false) {
|
||||||
|
await server.put(`notes/${noteId}/set-attribute`, {
|
||||||
|
type: "relation",
|
||||||
|
name: name,
|
||||||
|
value: value,
|
||||||
|
isInheritable
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function removeAttributeById(noteId: string, attributeId: string) {
|
async function removeAttributeById(noteId: string, attributeId: string) {
|
||||||
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
|
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
|
||||||
}
|
}
|
||||||
@@ -51,6 +60,23 @@ function removeOwnedLabelByName(note: FNote, labelName: string) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a relation identified by its name from the given note, if it exists. Note that the relation must be owned, i.e.
|
||||||
|
* it will not remove inherited attributes.
|
||||||
|
*
|
||||||
|
* @param note the note from which to remove the relation.
|
||||||
|
* @param relationName the name of the relation to remove.
|
||||||
|
* @returns `true` if an attribute was identified and removed, `false` otherwise.
|
||||||
|
*/
|
||||||
|
function removeOwnedRelationByName(note: FNote, relationName: string) {
|
||||||
|
const relation = note.getOwnedRelation(relationName);
|
||||||
|
if (relation) {
|
||||||
|
removeAttributeById(note.noteId, relation.attributeId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the attribute of the given note to the provided value if its truthy, or removes the attribute if the value is falsy.
|
* Sets the attribute of the given note to the provided value if its truthy, or removes the attribute if the value is falsy.
|
||||||
* For an attribute with an empty value, pass an empty string instead.
|
* For an attribute with an empty value, pass an empty string instead.
|
||||||
@@ -100,9 +126,7 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This doesn't seem right.
|
if (attrRow.isInheritable) {
|
||||||
//@ts-ignore
|
|
||||||
if (this.isInheritable) {
|
|
||||||
for (const owningNote of owningNotes) {
|
for (const owningNote of owningNotes) {
|
||||||
if (owningNote.hasAncestor(attrNote.noteId, true)) {
|
if (owningNote.hasAncestor(attrNote.noteId, true)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -116,8 +140,10 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
|
|||||||
export default {
|
export default {
|
||||||
addLabel,
|
addLabel,
|
||||||
setLabel,
|
setLabel,
|
||||||
|
setRelation,
|
||||||
setAttribute,
|
setAttribute,
|
||||||
removeAttributeById,
|
removeAttributeById,
|
||||||
removeOwnedLabelByName,
|
removeOwnedLabelByName,
|
||||||
|
removeOwnedRelationByName,
|
||||||
isAffecting
|
isAffecting
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -176,11 +176,6 @@ async function moveNodeUpInHierarchy(node: Fancytree.FancytreeNode) {
|
|||||||
toastService.showError(resp.message);
|
toastService.showError(resp.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hoistedNoteService.isTopLevelNode(node) && node.getParent().getChildren().length <= 1) {
|
|
||||||
node.getParent().folder = false;
|
|
||||||
node.getParent().renderTitle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSearchBranches(branchIds: string[]) {
|
function filterSearchBranches(branchIds: string[]) {
|
||||||
|
|||||||
44
apps/client/src/services/bundle.spec.ts
Normal file
44
apps/client/src/services/bundle.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { Bundle, executeBundle } from "./bundle";
|
||||||
|
import { buildNote } from "../test/easy-froca";
|
||||||
|
|
||||||
|
describe("Script bundle", () => {
|
||||||
|
it("dayjs is available", async () => {
|
||||||
|
const script = /* js */`return api.dayjs().format("YYYY-MM-DD");`;
|
||||||
|
const bundle = getBundle(script);
|
||||||
|
const result = await executeBundle(bundle, null, $());
|
||||||
|
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("dayjs is-same-or-before plugin exists", async () => {
|
||||||
|
const script = /* js */`return api.dayjs("2023-10-01").isSameOrBefore(api.dayjs("2023-10-02"));`;
|
||||||
|
const bundle = getBundle(script);
|
||||||
|
const result = await executeBundle(bundle, null, $());
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getBundle(script: string) {
|
||||||
|
const id = buildNote({
|
||||||
|
title: "Script note"
|
||||||
|
}).noteId;
|
||||||
|
const bundle: Bundle = {
|
||||||
|
script: [
|
||||||
|
'',
|
||||||
|
`apiContext.modules['${id}'] = { exports: {} };`,
|
||||||
|
`return await ((async function(exports, module, require, api) {`,
|
||||||
|
`try {`,
|
||||||
|
`${script}`,
|
||||||
|
`;`,
|
||||||
|
`} catch (e) { throw new Error(\"Load of script note \\\"Client\\\" (${id}) failed with: \" + e.message); }`,
|
||||||
|
`for (const exportKey in exports) module.exports[exportKey] = exports[exportKey];`,
|
||||||
|
`return module.exports;`,
|
||||||
|
`}).call({}, {}, apiContext.modules['${id}'], apiContext.require([]), apiContext.apis['${id}']));`,
|
||||||
|
''
|
||||||
|
].join('\n'),
|
||||||
|
html: "",
|
||||||
|
noteId: id,
|
||||||
|
allNoteIds: [ id ]
|
||||||
|
};
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ async function getAndExecuteBundle(noteId: string, originEntity = null, script =
|
|||||||
return await executeBundle(bundle, originEntity);
|
return await executeBundle(bundle, originEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
|
export async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
|
||||||
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
|
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,24 +2,21 @@ import renderService from "./render.js";
|
|||||||
import protectedSessionService from "./protected_session.js";
|
import protectedSessionService from "./protected_session.js";
|
||||||
import protectedSessionHolder from "./protected_session_holder.js";
|
import protectedSessionHolder from "./protected_session_holder.js";
|
||||||
import openService from "./open.js";
|
import openService from "./open.js";
|
||||||
import froca from "./froca.js";
|
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import linkService from "./link.js";
|
|
||||||
import treeService from "./tree.js";
|
|
||||||
import FNote from "../entities/fnote.js";
|
import FNote from "../entities/fnote.js";
|
||||||
import FAttachment from "../entities/fattachment.js";
|
import FAttachment from "../entities/fattachment.js";
|
||||||
import imageContextMenuService from "../menus/image_context_menu.js";
|
import imageContextMenuService from "../menus/image_context_menu.js";
|
||||||
import { applySingleBlockSyntaxHighlight, formatCodeBlocks } from "./syntax_highlight.js";
|
import { applySingleBlockSyntaxHighlight } from "./syntax_highlight.js";
|
||||||
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
|
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
|
||||||
import renderDoc from "./doc_renderer.js";
|
import renderDoc from "./doc_renderer.js";
|
||||||
import { t } from "../services/i18n.js";
|
import { t } from "../services/i18n.js";
|
||||||
import WheelZoom from 'vanilla-js-wheel-zoom';
|
import WheelZoom from 'vanilla-js-wheel-zoom';
|
||||||
import { renderMathInElement } from "./math.js";
|
|
||||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
||||||
|
import renderText from "./content_renderer_text.js";
|
||||||
|
|
||||||
let idCounter = 1;
|
let idCounter = 1;
|
||||||
|
|
||||||
interface Options {
|
export interface RenderOptions {
|
||||||
tooltip?: boolean;
|
tooltip?: boolean;
|
||||||
trim?: boolean;
|
trim?: boolean;
|
||||||
imageHasZoom?: boolean;
|
imageHasZoom?: boolean;
|
||||||
@@ -29,7 +26,7 @@ interface Options {
|
|||||||
|
|
||||||
const CODE_MIME_TYPES = new Set(["application/json"]);
|
const CODE_MIME_TYPES = new Set(["application/json"]);
|
||||||
|
|
||||||
export async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FAttachment, options: Options = {}) {
|
export async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FAttachment, options: RenderOptions = {}) {
|
||||||
|
|
||||||
options = Object.assign(
|
options = Object.assign(
|
||||||
{
|
{
|
||||||
@@ -116,32 +113,6 @@ export async function getRenderedContent(this: {} | { ctx: string }, entity: FNo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>, options: Options = {}) {
|
|
||||||
// entity must be FNote
|
|
||||||
const blob = await note.getBlob();
|
|
||||||
|
|
||||||
if (blob && !utils.isHtmlEmpty(blob.content)) {
|
|
||||||
$renderedContent.append($('<div class="ck-content">').html(blob.content));
|
|
||||||
|
|
||||||
if ($renderedContent.find("span.math-tex").length > 0) {
|
|
||||||
renderMathInElement($renderedContent[0], { trust: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const getNoteIdFromLink = (el: HTMLElement) => treeService.getNoteIdFromUrl($(el).attr("href") || "");
|
|
||||||
const referenceLinks = $renderedContent.find("a.reference-link");
|
|
||||||
const noteIdsToPrefetch = referenceLinks.map((i, el) => getNoteIdFromLink(el));
|
|
||||||
await froca.getNotes(noteIdsToPrefetch);
|
|
||||||
|
|
||||||
for (const el of referenceLinks) {
|
|
||||||
await linkService.loadReferenceLinkTitle($(el));
|
|
||||||
}
|
|
||||||
|
|
||||||
await formatCodeBlocks($renderedContent);
|
|
||||||
} else if (note instanceof FNote && !options.noChildrenList) {
|
|
||||||
await renderChildrenList($renderedContent, note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a code note, by displaying its content and applying syntax highlighting based on the selected MIME type.
|
* Renders a code note, by displaying its content and applying syntax highlighting based on the selected MIME type.
|
||||||
*/
|
*/
|
||||||
@@ -163,7 +134,7 @@ async function renderCode(note: FNote | FAttachment, $renderedContent: JQuery<HT
|
|||||||
await applySingleBlockSyntaxHighlight($codeBlock, normalizeMimeTypeForCKEditor(note.mime));
|
await applySingleBlockSyntaxHighlight($codeBlock, normalizeMimeTypeForCKEditor(note.mime));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderImage(entity: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>, options: Options = {}) {
|
function renderImage(entity: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>, options: RenderOptions = {}) {
|
||||||
const encodedTitle = encodeURIComponent(entity.title);
|
const encodedTitle = encodeURIComponent(entity.title);
|
||||||
|
|
||||||
let url;
|
let url;
|
||||||
@@ -305,40 +276,6 @@ async function renderMermaid(note: FNote | FAttachment, $renderedContent: JQuery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {jQuery} $renderedContent
|
|
||||||
* @param {FNote} note
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async function renderChildrenList($renderedContent: JQuery<HTMLElement>, note: FNote) {
|
|
||||||
let childNoteIds = note.getChildNoteIds();
|
|
||||||
|
|
||||||
if (!childNoteIds.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$renderedContent.css("padding", "10px");
|
|
||||||
$renderedContent.addClass("text-with-ellipsis");
|
|
||||||
|
|
||||||
if (childNoteIds.length > 10) {
|
|
||||||
childNoteIds = childNoteIds.slice(0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// just load the first 10 child notes
|
|
||||||
const childNotes = await froca.getNotes(childNoteIds);
|
|
||||||
|
|
||||||
for (const childNote of childNotes) {
|
|
||||||
$renderedContent.append(
|
|
||||||
await linkService.createLink(`${note.noteId}/${childNote.noteId}`, {
|
|
||||||
showTooltip: false,
|
|
||||||
showNoteIcon: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
$renderedContent.append("<br>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRenderingType(entity: FNote | FAttachment) {
|
function getRenderingType(entity: FNote | FAttachment) {
|
||||||
let type: string = "";
|
let type: string = "";
|
||||||
if ("type" in entity) {
|
if ("type" in entity) {
|
||||||
|
|||||||
126
apps/client/src/services/content_renderer_text.ts
Normal file
126
apps/client/src/services/content_renderer_text.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { formatCodeBlocks } from "./syntax_highlight.js";
|
||||||
|
import { getMermaidConfig } from "./mermaid.js";
|
||||||
|
import { renderMathInElement } from "./math.js";
|
||||||
|
import FNote from "../entities/fnote.js";
|
||||||
|
import FAttachment from "../entities/fattachment.js";
|
||||||
|
import tree from "./tree.js";
|
||||||
|
import froca from "./froca.js";
|
||||||
|
import link from "./link.js";
|
||||||
|
import { isHtmlEmpty } from "./utils.js";
|
||||||
|
import { default as content_renderer, type RenderOptions } from "./content_renderer.js";
|
||||||
|
|
||||||
|
export default async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>, options: RenderOptions = {}) {
|
||||||
|
// entity must be FNote
|
||||||
|
const blob = await note.getBlob();
|
||||||
|
|
||||||
|
if (blob && !isHtmlEmpty(blob.content)) {
|
||||||
|
$renderedContent.append($('<div class="ck-content">').html(blob.content));
|
||||||
|
await renderIncludedNotes($renderedContent[0]);
|
||||||
|
|
||||||
|
if ($renderedContent.find("span.math-tex").length > 0) {
|
||||||
|
renderMathInElement($renderedContent[0], { trust: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNoteIdFromLink = (el: HTMLElement) => tree.getNoteIdFromUrl($(el).attr("href") || "");
|
||||||
|
const referenceLinks = $renderedContent.find("a.reference-link");
|
||||||
|
const noteIdsToPrefetch = referenceLinks.map((i, el) => getNoteIdFromLink(el));
|
||||||
|
await froca.getNotes(noteIdsToPrefetch);
|
||||||
|
|
||||||
|
for (const el of referenceLinks) {
|
||||||
|
await link.loadReferenceLinkTitle($(el));
|
||||||
|
}
|
||||||
|
|
||||||
|
await rewriteMermaidDiagramsInContainer($renderedContent[0] as HTMLDivElement);
|
||||||
|
await formatCodeBlocks($renderedContent);
|
||||||
|
} else if (note instanceof FNote && !options.noChildrenList) {
|
||||||
|
await renderChildrenList($renderedContent, note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderIncludedNotes(contentEl: HTMLElement) {
|
||||||
|
// TODO: Consider duplicating with server's share/content_renderer.ts.
|
||||||
|
const includeNoteEls = contentEl.querySelectorAll("section.include-note");
|
||||||
|
|
||||||
|
// Gather the list of items to load.
|
||||||
|
const noteIds: string[] = [];
|
||||||
|
for (const includeNoteEl of includeNoteEls) {
|
||||||
|
const noteId = includeNoteEl.getAttribute("data-note-id");
|
||||||
|
if (noteId) {
|
||||||
|
noteIds.push(noteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the required notes.
|
||||||
|
await froca.getNotes(noteIds);
|
||||||
|
|
||||||
|
// Render and integrate the notes.
|
||||||
|
for (const includeNoteEl of includeNoteEls) {
|
||||||
|
const noteId = includeNoteEl.getAttribute("data-note-id");
|
||||||
|
if (!noteId) continue;
|
||||||
|
|
||||||
|
const note = froca.getNoteFromCache(noteId);
|
||||||
|
if (!note) {
|
||||||
|
console.warn(`Unable to include ${noteId} because it could not be found.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderedContent = (await content_renderer.getRenderedContent(note)).$renderedContent;
|
||||||
|
includeNoteEl.replaceChildren(...renderedContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rewrite the code block from <pre><code> to <div> in order not to apply a codeblock style to it. */
|
||||||
|
export async function rewriteMermaidDiagramsInContainer(container: HTMLDivElement) {
|
||||||
|
const mermaidBlocks = container.querySelectorAll('pre:has(code[class="language-mermaid"])');
|
||||||
|
if (!mermaidBlocks.length) return;
|
||||||
|
const nodes: HTMLElement[] = [];
|
||||||
|
|
||||||
|
for (const mermaidBlock of mermaidBlocks) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("mermaid-diagram");
|
||||||
|
div.innerHTML = mermaidBlock.querySelector("code")?.innerHTML ?? "";
|
||||||
|
mermaidBlock.replaceWith(div);
|
||||||
|
nodes.push(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applyInlineMermaid(container: HTMLDivElement) {
|
||||||
|
// Initialize mermaid
|
||||||
|
const mermaid = (await import("mermaid")).default;
|
||||||
|
mermaid.initialize(getMermaidConfig());
|
||||||
|
const nodes = Array.from(container.querySelectorAll<HTMLElement>("div.mermaid-diagram"));
|
||||||
|
try {
|
||||||
|
await mermaid.run({ nodes });
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderChildrenList($renderedContent: JQuery<HTMLElement>, note: FNote) {
|
||||||
|
let childNoteIds = note.getChildNoteIds();
|
||||||
|
|
||||||
|
if (!childNoteIds.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$renderedContent.css("padding", "10px");
|
||||||
|
$renderedContent.addClass("text-with-ellipsis");
|
||||||
|
|
||||||
|
if (childNoteIds.length > 10) {
|
||||||
|
childNoteIds = childNoteIds.slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// just load the first 10 child notes
|
||||||
|
const childNotes = await froca.getNotes(childNoteIds);
|
||||||
|
|
||||||
|
for (const childNote of childNotes) {
|
||||||
|
$renderedContent.append(
|
||||||
|
await link.createLink(`${note.noteId}/${childNote.noteId}`, {
|
||||||
|
showTooltip: false,
|
||||||
|
showNoteIcon: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$renderedContent.append("<br>");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
import {readCssVar} from "../utils/css-var";
|
import {readCssVar} from "../utils/css-var";
|
||||||
import Color, { ColorInstance } from "color";
|
import Color, { ColorInstance } from "color";
|
||||||
|
|
||||||
const registeredClasses = new Set<string>();
|
const registeredClasses = new Set<string>();
|
||||||
|
const colorsWithHue = new Set<string>();
|
||||||
|
|
||||||
// Read the color lightness limits defined in the theme as CSS variables
|
// Read the color lightness limits defined in the theme as CSS variables
|
||||||
|
|
||||||
@@ -26,19 +28,23 @@ function createClassForColor(colorString: string | null) {
|
|||||||
if (!registeredClasses.has(className)) {
|
if (!registeredClasses.has(className)) {
|
||||||
const adjustedColor = adjustColorLightness(color, lightThemeColorMaxLightness!,
|
const adjustedColor = adjustColorLightness(color, lightThemeColorMaxLightness!,
|
||||||
darkThemeColorMinLightness!);
|
darkThemeColorMinLightness!);
|
||||||
|
const hue = getHue(color);
|
||||||
|
|
||||||
$("head").append(`<style>
|
$("head").append(`<style>
|
||||||
.${className}, span.fancytree-active.${className} {
|
.${className}, span.fancytree-active.${className} {
|
||||||
--light-theme-custom-color: ${adjustedColor.lightThemeColor};
|
--light-theme-custom-color: ${adjustedColor.lightThemeColor};
|
||||||
--dark-theme-custom-color: ${adjustedColor.darkThemeColor};
|
--dark-theme-custom-color: ${adjustedColor.darkThemeColor};
|
||||||
--custom-color-hue: ${getHue(color) ?? 'unset'};
|
--custom-color-hue: ${hue ?? 'unset'};
|
||||||
}
|
}
|
||||||
</style>`);
|
</style>`);
|
||||||
|
|
||||||
registeredClasses.add(className);
|
registeredClasses.add(className);
|
||||||
|
if (hue !== undefined) {
|
||||||
|
colorsWithHue.add(className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return className;
|
return clsx("use-note-color", className, colorsWithHue.has(className) && "with-hue");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseColor(color: string) {
|
function parseColor(color: string) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type { FNoteRow } from "../entities/fnote.js";
|
import type { FNoteRow } from "../entities/fnote.js";
|
||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* @param whether to execute at the beginning (`false`)
|
* @param whether to execute at the beginning (`false`)
|
||||||
* @api public
|
* @api public
|
||||||
*/
|
*/
|
||||||
function debounce<T>(func: (...args: unknown[]) => T, waitMs: number, immediate: boolean = false) {
|
function debounce<T>(func: (...args: any[]) => T, waitMs: number, immediate: boolean = false) {
|
||||||
let timeout: any; // TODO: fix once we split client and server.
|
let timeout: any; // TODO: fix once we split client and server.
|
||||||
let args: unknown[] | null;
|
let args: unknown[] | null;
|
||||||
let context: unknown;
|
let context: unknown;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type FNote from "../entities/fnote.js";
|
import type FNote from "../entities/fnote.js";
|
||||||
|
import { applyReferenceLinks } from "../widgets/type_widgets/text/read_only_helper.js";
|
||||||
import { getCurrentLanguage } from "./i18n.js";
|
import { getCurrentLanguage } from "./i18n.js";
|
||||||
import { formatCodeBlocks } from "./syntax_highlight.js";
|
import { formatCodeBlocks } from "./syntax_highlight.js";
|
||||||
|
|
||||||
@@ -10,18 +11,18 @@ export default function renderDoc(note: FNote) {
|
|||||||
if (docName) {
|
if (docName) {
|
||||||
// find doc based on language
|
// find doc based on language
|
||||||
const url = getUrl(docName, getCurrentLanguage());
|
const url = getUrl(docName, getCurrentLanguage());
|
||||||
$content.load(url, (response, status) => {
|
$content.load(url, async (response, status) => {
|
||||||
// fallback to english doc if no translation available
|
// fallback to english doc if no translation available
|
||||||
if (status === "error") {
|
if (status === "error") {
|
||||||
const fallbackUrl = getUrl(docName, "en");
|
const fallbackUrl = getUrl(docName, "en");
|
||||||
$content.load(fallbackUrl, () => {
|
$content.load(fallbackUrl, async () => {
|
||||||
processContent(fallbackUrl, $content)
|
await processContent(fallbackUrl, $content)
|
||||||
resolve($content);
|
resolve($content);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processContent(url, $content);
|
await processContent(url, $content);
|
||||||
resolve($content);
|
resolve($content);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -32,7 +33,7 @@ export default function renderDoc(note: FNote) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function processContent(url: string, $content: JQuery<HTMLElement>) {
|
async function processContent(url: string, $content: JQuery<HTMLElement>) {
|
||||||
const dir = url.substring(0, url.lastIndexOf("/"));
|
const dir = url.substring(0, url.lastIndexOf("/"));
|
||||||
|
|
||||||
// Images are relative to the docnote but that will not work when rendered in the application since the path breaks.
|
// Images are relative to the docnote but that will not work when rendered in the application since the path breaks.
|
||||||
@@ -42,6 +43,9 @@ function processContent(url: string, $content: JQuery<HTMLElement>) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
formatCodeBlocks($content);
|
formatCodeBlocks($content);
|
||||||
|
|
||||||
|
// Apply reference links.
|
||||||
|
await applyReferenceLinks($content[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrl(docNameValue: string, language: string) {
|
function getUrl(docNameValue: string, language: string) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface Froca {
|
|||||||
|
|
||||||
getBlob(entityType: string, entityId: string): Promise<FBlob | null>;
|
getBlob(entityType: string, entityId: string): Promise<FBlob | null>;
|
||||||
getNote(noteId: string, silentNotFoundError?: boolean): Promise<FNote | null>;
|
getNote(noteId: string, silentNotFoundError?: boolean): Promise<FNote | null>;
|
||||||
getNoteFromCache(noteId: string): FNote;
|
getNoteFromCache(noteId: string): FNote | undefined;
|
||||||
getNotesFromCache(noteIds: string[], silentNotFoundError?: boolean): FNote[];
|
getNotesFromCache(noteIds: string[], silentNotFoundError?: boolean): FNote[];
|
||||||
getNotes(noteIds: string[], silentNotFoundError?: boolean): Promise<FNote[]>;
|
getNotes(noteIds: string[], silentNotFoundError?: boolean): Promise<FNote[]>;
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class FrocaImpl implements Froca {
|
|||||||
return (await this.getNotes([noteId], silentNotFoundError))[0];
|
return (await this.getNotes([noteId], silentNotFoundError))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
getNoteFromCache(noteId: string) {
|
getNoteFromCache(noteId: string): FNote | undefined {
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
throw new Error("Empty noteId");
|
throw new Error("Empty noteId");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import shortcutService from "./shortcuts.js";
|
|||||||
import dialogService from "./dialog.js";
|
import dialogService from "./dialog.js";
|
||||||
import type FNote from "../entities/fnote.js";
|
import type FNote from "../entities/fnote.js";
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type NoteContext from "../components/note_context.js";
|
import type NoteContext from "../components/note_context.js";
|
||||||
import type Component from "../components/component.js";
|
import type Component from "../components/component.js";
|
||||||
import { formatLogMessage } from "@triliumnext/commons";
|
import { formatLogMessage } from "@triliumnext/commons";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import options from "./options.js";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import i18nextHttpBackend from "i18next-http-backend";
|
import i18nextHttpBackend from "i18next-http-backend";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import type { Locale } from "@triliumnext/commons";
|
import { LOCALE_IDS, setDayjsLocale, type Locale } from "@triliumnext/commons";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
|
|
||||||
let locales: Locale[] | null;
|
let locales: Locale[] | null;
|
||||||
@@ -13,7 +13,7 @@ let locales: Locale[] | null;
|
|||||||
export let translationsInitializedPromise = $.Deferred();
|
export let translationsInitializedPromise = $.Deferred();
|
||||||
|
|
||||||
export async function initLocale() {
|
export async function initLocale() {
|
||||||
const locale = (options.get("locale") as string) || "en";
|
const locale = ((options.get("locale") as string) || "en") as LOCALE_IDS;
|
||||||
|
|
||||||
locales = await server.get<Locale[]>("options/locales");
|
locales = await server.get<Locale[]>("options/locales");
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ export async function initLocale() {
|
|||||||
returnEmptyString: false
|
returnEmptyString: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await setDayjsLocale(locale);
|
||||||
translationsInitializedPromise.resolve();
|
translationsInitializedPromise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,17 @@ async function getActionsForScope(scope: string) {
|
|||||||
return actions.filter((action) => action.scope === scope);
|
return actions.filter((action) => action.scope === scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, component: Component) {
|
async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, component: Component, ntxId: string | null | undefined) {
|
||||||
|
if (!$el[0]) return [];
|
||||||
|
|
||||||
const actions = await getActionsForScope(scope);
|
const actions = await getActionsForScope(scope);
|
||||||
const bindings: ShortcutBinding[] = [];
|
const bindings: ShortcutBinding[] = [];
|
||||||
|
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
for (const shortcut of action.effectiveShortcuts ?? []) {
|
for (const shortcut of action.effectiveShortcuts ?? []) {
|
||||||
const binding = shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
|
const binding = shortcutService.bindElShortcut($el, shortcut, () => {
|
||||||
|
component.triggerCommand(action.actionName, { ntxId });
|
||||||
|
});
|
||||||
if (binding) {
|
if (binding) {
|
||||||
bindings.push(binding);
|
bindings.push(binding);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,11 +150,16 @@ async function createLink(notePath: string | undefined, options: CreateLinkOptio
|
|||||||
$container.append($noteLink);
|
$container.append($noteLink);
|
||||||
|
|
||||||
if (showNotePath) {
|
if (showNotePath) {
|
||||||
|
let pathSegments: string[];
|
||||||
|
if (notePath == "root") {
|
||||||
|
pathSegments = ["⌂"];
|
||||||
|
} else {
|
||||||
const resolvedPathSegments = (await treeService.resolveNotePathToSegments(notePath)) || [];
|
const resolvedPathSegments = (await treeService.resolveNotePathToSegments(notePath)) || [];
|
||||||
resolvedPathSegments.pop(); // Remove last element
|
resolvedPathSegments.pop(); // Remove last element
|
||||||
|
|
||||||
const resolvedPath = resolvedPathSegments.join("/");
|
const resolvedPath = resolvedPathSegments.join("/");
|
||||||
const pathSegments = await treeService.getNotePathTitleComponents(resolvedPath);
|
pathSegments = await treeService.getNotePathTitleComponents(resolvedPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (pathSegments) {
|
if (pathSegments) {
|
||||||
if (pathSegments.length) {
|
if (pathSegments.length) {
|
||||||
@@ -302,7 +307,8 @@ export function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDo
|
|||||||
// Right click is handled separately.
|
// Right click is handled separately.
|
||||||
const isMiddleClick = evt && "which" in evt && evt.which === 2;
|
const isMiddleClick = evt && "which" in evt && evt.which === 2;
|
||||||
const targetIsBlank = ($link?.attr("target") === "_blank");
|
const targetIsBlank = ($link?.attr("target") === "_blank");
|
||||||
const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick || targetIsBlank;
|
const isDoubleClick = isLeftClick && evt?.type === "dblclick";
|
||||||
|
const openInNewTab = (isLeftClick && ctrlKey) || isDoubleClick || isMiddleClick || targetIsBlank;
|
||||||
const activate = (isLeftClick && ctrlKey && shiftKey) || (isMiddleClick && shiftKey);
|
const activate = (isLeftClick && ctrlKey && shiftKey) || (isMiddleClick && shiftKey);
|
||||||
const openInNewWindow = isLeftClick && evt?.shiftKey && !ctrlKey;
|
const openInNewWindow = isLeftClick && evt?.shiftKey && !ctrlKey;
|
||||||
|
|
||||||
@@ -323,20 +329,22 @@ export function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDo
|
|||||||
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
||||||
const outsideOfCKEditor = !$link || $link.closest("[contenteditable]").length === 0;
|
const outsideOfCKEditor = !$link || $link.closest("[contenteditable]").length === 0;
|
||||||
|
|
||||||
if (openInNewTab || (withinEditLink && (isLeftClick || isMiddleClick)) || (outsideOfCKEditor && (isLeftClick || isMiddleClick))) {
|
if (openInNewTab || openInNewWindow || (isLeftClick && (withinEditLink || outsideOfCKEditor))) {
|
||||||
if (hrefLink.toLowerCase().startsWith("http") || hrefLink.startsWith("api/")) {
|
if (hrefLink.toLowerCase().startsWith("http") || hrefLink.startsWith("api/")) {
|
||||||
window.open(hrefLink, "_blank");
|
window.open(hrefLink, "_blank");
|
||||||
} else if ((hrefLink.toLowerCase().startsWith("file:") || hrefLink.toLowerCase().startsWith("geo:")) && utils.isElectron()) {
|
|
||||||
const electron = utils.dynamicRequire("electron");
|
|
||||||
electron.shell.openPath(hrefLink);
|
|
||||||
} else {
|
} else {
|
||||||
// Enable protocols supported by CKEditor 5 to be clickable.
|
// Enable protocols supported by CKEditor 5 to be clickable.
|
||||||
if (ALLOWED_PROTOCOLS.some((protocol) => hrefLink.toLowerCase().startsWith(protocol + ":"))) {
|
if (ALLOWED_PROTOCOLS.some((protocol) => hrefLink.toLowerCase().startsWith(protocol + ":"))) {
|
||||||
|
if ( utils.isElectron()) {
|
||||||
|
const electron = utils.dynamicRequire("electron");
|
||||||
|
electron.shell.openExternal(hrefLink);
|
||||||
|
} else {
|
||||||
window.open(hrefLink, "_blank");
|
window.open(hrefLink, "_blank");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -459,6 +467,7 @@ function getReferenceLinkTitleSync(href: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glob.device !== "print") {
|
||||||
// TODO: Check why the event is not supported.
|
// TODO: Check why the event is not supported.
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
$(document).on("click", "a", goToLink);
|
$(document).on("click", "a", goToLink);
|
||||||
@@ -468,18 +477,9 @@ $(document).on("auxclick", "a", goToLink); // to handle the middle button
|
|||||||
// TODO: Check why the event is not supported.
|
// TODO: Check why the event is not supported.
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
$(document).on("contextmenu", "a", linkContextMenu);
|
$(document).on("contextmenu", "a", linkContextMenu);
|
||||||
$(document).on("dblclick", "a", (e) => {
|
// TODO: Check why the event is not supported.
|
||||||
e.preventDefault();
|
//@ts-ignore
|
||||||
e.stopPropagation();
|
$(document).on("dblclick", "a", goToLink);
|
||||||
|
|
||||||
const $link = $(e.target).closest("a");
|
|
||||||
|
|
||||||
const address = $link.attr("href");
|
|
||||||
|
|
||||||
if (address && address.startsWith("http")) {
|
|
||||||
window.open(address, "_blank");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mousedown", "a", (e) => {
|
$(document).on("mousedown", "a", (e) => {
|
||||||
if (e.which === 2) {
|
if (e.which === 2) {
|
||||||
@@ -490,6 +490,7 @@ $(document).on("mousedown", "a", (e) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getNotePathFromUrl,
|
getNotePathFromUrl,
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import type { AttachmentRow, EtapiTokenRow, OptionNames } from "@triliumnext/commons";
|
import type { AttachmentRow, EtapiTokenRow, NoteType, OptionNames } from "@triliumnext/commons";
|
||||||
import type { AttributeType } from "../entities/fattribute.js";
|
import type { AttributeType } from "../entities/fattribute.js";
|
||||||
import type { EntityChange } from "../server_types.js";
|
import type { EntityChange } from "../server_types.js";
|
||||||
|
|
||||||
// TODO: Deduplicate with server.
|
// TODO: Deduplicate with server.
|
||||||
|
|
||||||
interface NoteRow {
|
interface NoteRow {
|
||||||
|
blobId: string;
|
||||||
|
dateCreated: string;
|
||||||
|
dateModified: string;
|
||||||
isDeleted?: boolean;
|
isDeleted?: boolean;
|
||||||
|
isProtected?: boolean;
|
||||||
|
mime: string;
|
||||||
|
noteId: string;
|
||||||
|
title: string;
|
||||||
|
type: NoteType;
|
||||||
|
utcDateCreated: string;
|
||||||
|
utcDateModified: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deduplicate with BranchRow from `rows.ts`/
|
// TODO: Deduplicate with BranchRow from `rows.ts`/
|
||||||
|
|||||||
@@ -41,6 +41,17 @@ function parse(value: string) {
|
|||||||
return defObj;
|
return defObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For an attribute definition name (e.g. `label:TEST:TEST1`), extracts its type (label) and name (TEST:TEST1).
|
||||||
|
* @param definitionAttrName the attribute definition name, without the leading `#` (e.g. `label:TEST:TEST1`)
|
||||||
|
* @return a tuple of [type, name].
|
||||||
|
*/
|
||||||
|
export function extractAttributeDefinitionTypeAndName(definitionAttrName: string): [ "label" | "relation", string ] {
|
||||||
|
const valueType = definitionAttrName.startsWith("label:") ? "label" : "relation";
|
||||||
|
const valueName = definitionAttrName.substring(valueType.length + 1);
|
||||||
|
return [ valueType, valueName ];
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse
|
parse
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,46 +8,50 @@ export interface ToastOptions {
|
|||||||
delay?: number;
|
delay?: number;
|
||||||
autohide?: boolean;
|
autohide?: boolean;
|
||||||
closeAfter?: number;
|
closeAfter?: number;
|
||||||
|
progress?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toast(options: ToastOptions) {
|
function toast({ title, icon, message, id, delay, autohide, progress }: ToastOptions) {
|
||||||
const $toast = $(options.title
|
const $toast = $(title
|
||||||
? `\
|
? `\
|
||||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<strong class="me-auto">
|
<strong class="me-auto">
|
||||||
<span class="bx bx-${options.icon}"></span>
|
<span class="bx bx-${icon}"></span>
|
||||||
<span class="toast-title"></span>
|
<span class="toast-title"></span>
|
||||||
</strong>
|
</strong>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body"></div>
|
<div class="toast-body"></div>
|
||||||
|
<div class="toast-progress"></div>
|
||||||
</div>`
|
</div>`
|
||||||
: `
|
: `
|
||||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-icon">
|
<div class="toast-icon">
|
||||||
<span class="bx bx-${options.icon}"></span>
|
<span class="bx bx-${icon}"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body"></div>
|
<div class="toast-body"></div>
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toast-progress"></div>
|
||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
|
|
||||||
$toast.toggleClass("no-title", !options.title);
|
$toast.toggleClass("no-title", !title);
|
||||||
$toast.find(".toast-title").text(options.title ?? "");
|
$toast.find(".toast-title").text(title ?? "");
|
||||||
$toast.find(".toast-body").html(options.message);
|
$toast.find(".toast-body").html(message);
|
||||||
|
$toast.find(".toast-progress").css("width", `${(progress ?? 0) * 100}%`);
|
||||||
|
|
||||||
if (options.id) {
|
if (id) {
|
||||||
$toast.attr("id", `toast-${options.id}`);
|
$toast.attr("id", `toast-${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#toast-container").append($toast);
|
$("#toast-container").append($toast);
|
||||||
|
|
||||||
$toast.toast({
|
$toast.toast({
|
||||||
delay: options.delay || 3000,
|
delay: delay || 3000,
|
||||||
autohide: !!options.autohide
|
autohide: !!autohide
|
||||||
});
|
});
|
||||||
|
|
||||||
$toast.on("hidden.bs.toast", (e) => e.target.remove());
|
$toast.on("hidden.bs.toast", (e) => e.target.remove());
|
||||||
@@ -62,6 +66,7 @@ function showPersistent(options: ToastOptions) {
|
|||||||
|
|
||||||
if ($toast.length > 0) {
|
if ($toast.length > 0) {
|
||||||
$toast.find(".toast-body").html(options.message);
|
$toast.find(".toast-body").html(options.message);
|
||||||
|
$toast.find(".toast-progress").css("width", `${(options.progress ?? 0) * 100}%`);
|
||||||
} else {
|
} else {
|
||||||
options.autohide = false;
|
options.autohide = false;
|
||||||
|
|
||||||
@@ -77,11 +82,11 @@ function closePersistent(id: string) {
|
|||||||
$(`#toast-${id}`).remove();
|
$(`#toast-${id}`).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMessage(message: string, delay = 2000) {
|
function showMessage(message: string, delay = 2000, icon = "check") {
|
||||||
console.debug(utils.now(), "message:", message);
|
console.debug(utils.now(), "message:", message);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
icon: "check",
|
icon,
|
||||||
message: message,
|
message: message,
|
||||||
autohide: true,
|
autohide: true,
|
||||||
delay
|
delay
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
|||||||
|
|
||||||
effectivePathSegments.reverse();
|
effectivePathSegments.reverse();
|
||||||
|
|
||||||
if (effectivePathSegments.includes(hoistedNoteId)) {
|
if (effectivePathSegments.includes(hoistedNoteId) && effectivePathSegments.includes('root')) {
|
||||||
return effectivePathSegments;
|
return effectivePathSegments;
|
||||||
} else {
|
} else {
|
||||||
const noteId = getNoteIdFromUrl(notePath);
|
const noteId = getNoteIdFromUrl(notePath);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type { ViewScope } from "./link.js";
|
import type { ViewScope } from "./link.js";
|
||||||
import FNote from "../entities/fnote";
|
import FNote from "../entities/fnote";
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ function toObject<T, R>(array: T[], fn: (arg0: T) => [key: string, value: R]) {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomString(len: number) {
|
export function randomString(len: number) {
|
||||||
let text = "";
|
let text = "";
|
||||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ function getMimeTypeClass(mime: string) {
|
|||||||
return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`;
|
return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHtmlEmpty(html: string) {
|
export function isHtmlEmpty(html: string) {
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return true;
|
return true;
|
||||||
} else if (typeof html !== "string") {
|
} else if (typeof html !== "string") {
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu:not(.static).calendar-dropdown-menu {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.calendar-dropdown-widget {
|
.calendar-dropdown-widget {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
--bs-body-color: var(--main-text-color) !important;
|
--bs-body-color: var(--main-text-color) !important;
|
||||||
--bs-body-bg: var(--main-background-color) !important;
|
--bs-body-bg: var(--main-background-color) !important;
|
||||||
--ck-mention-list-max-height: 500px;
|
--ck-mention-list-max-height: 500px;
|
||||||
|
--tn-modal-max-height: 90vh;
|
||||||
|
|
||||||
|
--tree-item-light-theme-max-color-lightness: 50;
|
||||||
|
--tree-item-dark-theme-min-color-lightness: 75;
|
||||||
}
|
}
|
||||||
|
|
||||||
body#trilium-app.motion-disabled *,
|
body#trilium-app.motion-disabled *,
|
||||||
@@ -212,6 +216,16 @@ input::placeholder,
|
|||||||
background-color: var(--modal-backdrop-color) !important;
|
background-color: var(--modal-backdrop-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.mobile .modal .modal-dialog {
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.mobile .modal .modal-content {
|
||||||
|
border-radius: var(--bs-modal-border-radius) var(--bs-modal-border-radius) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.component {
|
.component {
|
||||||
contain: size;
|
contain: size;
|
||||||
}
|
}
|
||||||
@@ -243,6 +257,11 @@ button.close:hover {
|
|||||||
color: var(--hover-item-text-color);
|
color: var(--hover-item-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.custom-title-bar-button {
|
||||||
|
background: transparent;
|
||||||
|
border: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background-color: var(--modal-background-color) !important;
|
background-color: var(--modal-background-color) !important;
|
||||||
}
|
}
|
||||||
@@ -439,7 +458,8 @@ body.desktop .tabulator-popup-container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
|
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
|
||||||
body #context-menu-container .dropdown-item > span {
|
body #context-menu-container .dropdown-item > span,
|
||||||
|
body.mobile .dropdown .dropdown-submenu > span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -494,6 +514,10 @@ body #context-menu-container .dropdown-item > span {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu .note-color-picker {
|
||||||
|
padding: 4px 12px 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-editor {
|
.cm-editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
@@ -576,11 +600,6 @@ button.btn-sm {
|
|||||||
color: var(--left-pane-text-color);
|
color: var(--left-pane-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.active:not(.btn-primary) {
|
|
||||||
background-color: var(--button-disabled-background-color) !important;
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ck.ck-block-toolbar-button {
|
.ck.ck-block-toolbar-button {
|
||||||
transform: translateX(7px);
|
transform: translateX(7px);
|
||||||
color: var(--muted-text-color);
|
color: var(--muted-text-color);
|
||||||
@@ -701,11 +720,6 @@ table.promoted-attributes-in-tooltip th {
|
|||||||
z-index: 32767 !important;
|
z-index: 32767 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-trigger {
|
|
||||||
background: transparent;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bs-tooltip-bottom .tooltip-arrow::before {
|
.bs-tooltip-bottom .tooltip-arrow::before {
|
||||||
border-bottom-color: var(--main-border-color) !important;
|
border-bottom-color: var(--main-border-color) !important;
|
||||||
}
|
}
|
||||||
@@ -1001,10 +1015,16 @@ div[data-notify="container"] {
|
|||||||
font-family: var(--monospace-font-family);
|
font-family: var(--monospace-font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.ck-icon .note-icon {
|
svg.ck-icon {
|
||||||
|
&.ck-icon_inherit-color path[fill="#333"] {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.note-icon {
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ck-content {
|
.ck-content {
|
||||||
--ck-content-font-family: var(--detail-font-family);
|
--ck-content-font-family: var(--detail-font-family);
|
||||||
@@ -1112,10 +1132,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-empty {
|
|
||||||
margin: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding: 0.5rem 1rem 0.5rem 1rem !important; /* make modal header padding slightly smaller */
|
padding: 0.5rem 1rem 0.5rem 1rem !important; /* make modal header padding slightly smaller */
|
||||||
}
|
}
|
||||||
@@ -1137,6 +1153,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
--bs-toast-color: var(--main-text-color);
|
--bs-toast-color: var(--main-text-color);
|
||||||
z-index: 9999999999 !important;
|
z-index: 9999999999 !important;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-header {
|
.toast-header {
|
||||||
@@ -1169,6 +1186,16 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
background-color: unset !important;
|
background-color: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast .toast-progress {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
background-color: var(--toast-text-color) !important;
|
||||||
|
height: 4px;
|
||||||
|
transition: width 0.1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
.ck-mentions .ck-button {
|
.ck-mentions .ck-button {
|
||||||
font-size: var(--detail-font-size) !important;
|
font-size: var(--detail-font-size) !important;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -1300,11 +1327,11 @@ body.mobile #context-menu-container.mobile-bottom-menu {
|
|||||||
inset-inline-end: 0 !important;
|
inset-inline-end: 0 !important;
|
||||||
bottom: 0 !important;
|
bottom: 0 !important;
|
||||||
top: unset !important;
|
top: unset !important;
|
||||||
max-height: 70vh;
|
max-height: var(--tn-modal-max-height);
|
||||||
overflow: auto !important;
|
overflow: auto !important;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
padding-bottom: env(safe-area-inset-bottom) !important;
|
padding-bottom: max(env(safe-area-inset-bottom), var(--padding, var(--menu-padding-size))) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .dropdown-menu {
|
body.mobile .dropdown-menu {
|
||||||
@@ -1363,6 +1390,20 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-dropdown-widget .right-dropdown-button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-trigger {
|
||||||
|
background: transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#launcher-pane.horizontal .right-dropdown-widget {
|
#launcher-pane.horizontal .right-dropdown-widget {
|
||||||
width: 53px;
|
width: 53px;
|
||||||
}
|
}
|
||||||
@@ -1538,12 +1579,15 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
@media (max-width: 991px) {
|
@media (max-width: 991px) {
|
||||||
body.mobile #launcher-pane .dropdown.global-menu > .dropdown-menu.show,
|
body.mobile #launcher-pane .dropdown.global-menu > .dropdown-menu.show,
|
||||||
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
|
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
|
||||||
|
--dropdown-bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size));
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size)) !important;
|
bottom: var(--dropdown-bottom) !important;
|
||||||
top: unset !important;
|
top: unset !important;
|
||||||
inset-inline-start: 0 !important;
|
inset-inline-start: 0 !important;
|
||||||
inset-inline-end: 0 !important;
|
inset-inline-end: 0 !important;
|
||||||
transform: unset !important;
|
transform: unset !important;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(var(--tn-modal-max-height) - var(--dropdown-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
#mobile-sidebar-container {
|
#mobile-sidebar-container {
|
||||||
@@ -1578,6 +1622,14 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-split.empty-note {
|
||||||
|
--max-content-width: var(--preferred-max-content-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail-empty {
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
#mobile-sidebar-container.show #mobile-sidebar-wrapper {
|
#mobile-sidebar-container.show #mobile-sidebar-wrapper {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
@@ -1640,46 +1692,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
body.mobile .modal-dialog.modal-dialog-scrollable {
|
body.mobile .modal-dialog.modal-dialog-scrollable {
|
||||||
height: unset;
|
height: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .revisions-dialog .modal-dialog {
|
|
||||||
height: 95vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .modal-body {
|
|
||||||
height: 100% !important;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .revision-list {
|
|
||||||
height: unset;
|
|
||||||
max-height: 20vh;
|
|
||||||
border-bottom: 1px solid var(--main-border-color) !important;
|
|
||||||
padding: 0 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .modal-body > .revision-content-wrapper {
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .modal-body > .revision-content-wrapper > div:first-of-type {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .revision-title {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .revision-title-buttons {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .revisions-dialog .revision-content {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile, tablet mode */
|
/* Mobile, tablet mode */
|
||||||
@@ -1985,7 +1997,7 @@ body.electron.platform-darwin:not(.native-titlebar) .tab-row-container {
|
|||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.electron.platform-darwin:not(.native-titlebar) #tab-row-left-spacer {
|
body.electron.platform-darwin:not(.native-titlebar):not(.full-screen) #tab-row-left-spacer {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2434,6 +2446,15 @@ footer.webview-footer button {
|
|||||||
.admonition.caution::before { content: "\eac7"; }
|
.admonition.caution::before { content: "\eac7"; }
|
||||||
.admonition.warning::before { content: "\eac5"; }
|
.admonition.warning::before { content: "\eac5"; }
|
||||||
|
|
||||||
|
.ck-content ul.todo-list li span.todo-list__label__description {
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content ul.todo-list li:has(> span.todo-list__label input[type="checkbox"]:checked) > span.todo-list__label span.todo-list__label__description {
|
||||||
|
text-decoration: line-through;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-options-container {
|
.chat-options-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
@@ -2559,9 +2580,38 @@ iframe.print-iframe {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrolling-container > .note-detail.full-height,
|
.note-detail.full-height,
|
||||||
.scrolling-container > .note-list-widget.full-height {
|
.scrolling-container > .note-list-widget.full-height {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calendar collection */
|
||||||
|
|
||||||
|
.calendar-view a.fc-timegrid-event,
|
||||||
|
.calendar-view a.fc-daygrid-event {
|
||||||
|
/* Workaround: set font weight only if the theme-next is not active */
|
||||||
|
font-weight: var(--root-background, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
body.mobile {
|
||||||
|
.split-note-container-widget {
|
||||||
|
flex-direction: column !important;
|
||||||
|
|
||||||
|
.note-split {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-split.visible + .note-split.visible {
|
||||||
|
border-top: 1px solid var(--main-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#root-widget.virtual-keyboard-opened .note-split:not(:focus-within) {
|
||||||
|
max-height: 80px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,9 @@
|
|||||||
|
|
||||||
--mermaid-theme: dark;
|
--mermaid-theme: dark;
|
||||||
--native-titlebar-background: #00000000;
|
--native-titlebar-background: #00000000;
|
||||||
|
|
||||||
|
--calendar-coll-event-background-saturation: 30%;
|
||||||
|
--calendar-coll-event-background-lightness: 30%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body ::-webkit-calendar-picker-indicator {
|
body ::-webkit-calendar-picker-indicator {
|
||||||
@@ -109,3 +112,6 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
|||||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6) !important;
|
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.use-note-color {
|
||||||
|
--custom-color: var(--dark-theme-custom-color);
|
||||||
|
}
|
||||||
@@ -80,6 +80,9 @@ html {
|
|||||||
|
|
||||||
--mermaid-theme: default;
|
--mermaid-theme: default;
|
||||||
--native-titlebar-background: #ffffff00;
|
--native-titlebar-background: #ffffff00;
|
||||||
|
|
||||||
|
--calendar-coll-event-background-lightness: 95%;
|
||||||
|
--calendar-coll-event-background-saturation: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#left-pane .fancytree-node.tinted {
|
#left-pane .fancytree-node.tinted {
|
||||||
@@ -92,3 +95,7 @@ html {
|
|||||||
.board-note {
|
.board-note {
|
||||||
color: var(--light-theme-custom-color, inherit);
|
color: var(--light-theme-custom-color, inherit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.use-note-color {
|
||||||
|
--custom-color: var(--light-theme-custom-color);
|
||||||
|
}
|
||||||
@@ -41,6 +41,9 @@
|
|||||||
--cmd-button-keyboard-shortcut-color: white;
|
--cmd-button-keyboard-shortcut-color: white;
|
||||||
--cmd-button-disabled-opacity: 0.5;
|
--cmd-button-disabled-opacity: 0.5;
|
||||||
|
|
||||||
|
--button-group-active-button-background: #ffffff4e;
|
||||||
|
--button-group-active-button-text-color: white;
|
||||||
|
|
||||||
--icon-button-color: currentColor;
|
--icon-button-color: currentColor;
|
||||||
--icon-button-hover-background: var(--hover-item-background-color);
|
--icon-button-hover-background: var(--hover-item-background-color);
|
||||||
--icon-button-hover-color: var(--hover-item-text-color);
|
--icon-button-hover-color: var(--hover-item-text-color);
|
||||||
@@ -98,6 +101,7 @@
|
|||||||
--menu-item-delimiter-color: #ffffff1c;
|
--menu-item-delimiter-color: #ffffff1c;
|
||||||
--menu-item-group-header-color: #ffffff91;
|
--menu-item-group-header-color: #ffffff91;
|
||||||
--menu-section-background-color: #fefefe08;
|
--menu-section-background-color: #fefefe08;
|
||||||
|
--menu-submenu-mobile-background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
--modal-backdrop-color: #000;
|
--modal-backdrop-color: #000;
|
||||||
--modal-shadow-color: rgba(0, 0, 0, .5);
|
--modal-shadow-color: rgba(0, 0, 0, .5);
|
||||||
@@ -266,6 +270,14 @@
|
|||||||
--ck-editor-toolbar-button-on-color: white;
|
--ck-editor-toolbar-button-on-color: white;
|
||||||
--ck-editor-toolbar-button-on-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
|
--ck-editor-toolbar-button-on-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
|
||||||
--ck-editor-toolbar-dropdown-button-open-background: #ffffff14;
|
--ck-editor-toolbar-dropdown-button-open-background: #ffffff14;
|
||||||
|
|
||||||
|
--calendar-coll-event-background-saturation: 25%;
|
||||||
|
--calendar-coll-event-background-lightness: 20%;
|
||||||
|
--calendar-coll-event-background-color: #3c3c3c;
|
||||||
|
--calendar-coll-event-text-color: white;
|
||||||
|
--calendar-coll-event-hover-filter: brightness(1.25);
|
||||||
|
--callendar-coll-event-archived-sripe-color: #00000026;
|
||||||
|
--calendar-coll-today-background-color: #ffffff08;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -300,8 +312,12 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
|||||||
border-color: var(--muted-text-color) !important;
|
border-color: var(--muted-text-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tinted-quick-edit-dialog {
|
.quick-edit-dialog-wrapper.with-hue {
|
||||||
--modal-background-color: hsl(var(--custom-color-hue), 8.8%, 11.2%);
|
--modal-background-color: hsl(var(--custom-color-hue), 8.8%, 11.2%);
|
||||||
--modal-border-color: hsl(var(--custom-color-hue), 9.4%, 25.1%);
|
--modal-border-color: hsl(var(--custom-color-hue), 9.4%, 25.1%);
|
||||||
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 13.2%, 20.8%);
|
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 13.2%, 20.8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.use-note-color {
|
||||||
|
--custom-color: var(--dark-theme-custom-color);
|
||||||
|
}
|
||||||
@@ -41,6 +41,9 @@
|
|||||||
--cmd-button-keyboard-shortcut-color: black;
|
--cmd-button-keyboard-shortcut-color: black;
|
||||||
--cmd-button-disabled-opacity: 0.5;
|
--cmd-button-disabled-opacity: 0.5;
|
||||||
|
|
||||||
|
--button-group-active-button-background: #00000026;
|
||||||
|
--button-group-active-button-text-color: black;
|
||||||
|
|
||||||
--icon-button-color: currentColor;
|
--icon-button-color: currentColor;
|
||||||
--icon-button-hover-background: var(--hover-item-background-color);
|
--icon-button-hover-background: var(--hover-item-background-color);
|
||||||
--icon-button-hover-color: var(--hover-item-text-color);
|
--icon-button-hover-color: var(--hover-item-text-color);
|
||||||
@@ -265,6 +268,14 @@
|
|||||||
--ck-editor-toolbar-button-on-color: black;
|
--ck-editor-toolbar-button-on-color: black;
|
||||||
--ck-editor-toolbar-button-on-shadow: none;
|
--ck-editor-toolbar-button-on-shadow: none;
|
||||||
--ck-editor-toolbar-dropdown-button-open-background: #0000000f;
|
--ck-editor-toolbar-dropdown-button-open-background: #0000000f;
|
||||||
|
|
||||||
|
--calendar-coll-event-background-lightness: 95%;
|
||||||
|
--calendar-coll-event-background-saturation: 80%;
|
||||||
|
--calendar-coll-event-background-color: #eaeaea;
|
||||||
|
--calendar-coll-event-text-color: black;
|
||||||
|
--calendar-coll-event-hover-filter: brightness(.95) saturate(1.25);
|
||||||
|
--callendar-coll-event-archived-sripe-color: #0000000a;
|
||||||
|
--calendar-coll-today-background-color: #00000006;
|
||||||
}
|
}
|
||||||
|
|
||||||
#left-pane .fancytree-node.tinted {
|
#left-pane .fancytree-node.tinted {
|
||||||
@@ -276,7 +287,7 @@
|
|||||||
--custom-bg-color: hsl(var(--custom-color-hue), 37%, 89%, 1);
|
--custom-bg-color: hsl(var(--custom-color-hue), 37%, 89%, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tinted-quick-edit-dialog {
|
.quick-edit-dialog-wrapper.with-hue {
|
||||||
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
||||||
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
||||||
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
|
|
||||||
--menu-padding-size: 8px;
|
--menu-padding-size: 8px;
|
||||||
--menu-item-icon-vert-offset: -2px;
|
--menu-item-icon-vert-offset: -2px;
|
||||||
|
--menu-submenu-mobile-background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
|
||||||
--more-accented-background-color: var(--card-background-hover-color);
|
--more-accented-background-color: var(--card-background-hover-color);
|
||||||
|
|
||||||
@@ -99,6 +100,14 @@
|
|||||||
--tree-item-dark-theme-min-color-lightness: 65;
|
--tree-item-dark-theme-min-color-lightness: 65;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectable-text {
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
body.backdrop-effects-disabled {
|
body.backdrop-effects-disabled {
|
||||||
/* Backdrop effects are disabled, replace the menu background color with the
|
/* Backdrop effects are disabled, replace the menu background color with the
|
||||||
* no-backdrop fallback color */
|
* no-backdrop fallback color */
|
||||||
@@ -119,17 +128,6 @@ body.backdrop-effects-disabled {
|
|||||||
font-size: 0.9rem !important;
|
font-size: 0.9rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .dropdown-menu {
|
|
||||||
backdrop-filter: var(--dropdown-backdrop-filter);
|
|
||||||
border-radius: var(--dropdown-border-radius);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .dropdown-menu .dropdown-menu {
|
|
||||||
backdrop-filter: unset !important;
|
|
||||||
border-radius: unset !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.desktop .dropdown-menu::before,
|
body.desktop .dropdown-menu::before,
|
||||||
:root .ck.ck-dropdown__panel::before,
|
:root .ck.ck-dropdown__panel::before,
|
||||||
:root .excalidraw .popover::before,
|
:root .excalidraw .popover::before,
|
||||||
@@ -157,17 +155,12 @@ body.desktop .dropdown-submenu .dropdown-menu::before {
|
|||||||
content: unset;
|
content: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .dropdown-submenu .dropdown-menu {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.desktop .dropdown-submenu .dropdown-menu {
|
body.desktop .dropdown-submenu .dropdown-menu {
|
||||||
backdrop-filter: var(--dropdown-backdrop-filter);
|
backdrop-filter: var(--dropdown-backdrop-filter);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item,
|
.dropdown-item,
|
||||||
body.mobile .dropdown-submenu .dropdown-toggle,
|
|
||||||
.excalidraw .context-menu .context-menu-item {
|
.excalidraw .context-menu .context-menu-item {
|
||||||
--menu-item-start-padding: 8px;
|
--menu-item-start-padding: 8px;
|
||||||
--menu-item-end-padding: 22px;
|
--menu-item-end-padding: 22px;
|
||||||
@@ -201,10 +194,6 @@ body.mobile .dropdown-item:not(:last-of-type) {
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .dropdown-submenu:hover {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html body .dropdown-item.disabled,
|
html body .dropdown-item.disabled,
|
||||||
html body .dropdown-item[disabled] {
|
html body .dropdown-item[disabled] {
|
||||||
color: var(--menu-text-color) !important;
|
color: var(--menu-text-color) !important;
|
||||||
@@ -321,17 +310,126 @@ body.desktop .dropdown-menu.static .dropdown-item.active {
|
|||||||
--active-item-text-color: var(--menu-text-color);
|
--active-item-text-color: var(--menu-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #region Mobile tweaks for dropdown menus */
|
||||||
|
body.mobile #context-menu-cover {
|
||||||
|
transition: background-color 150ms ease-in;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.global-menu-cover {
|
||||||
|
bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size));
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.mobile .dropdown-menu.mobile-bottom-menu,
|
||||||
|
body.mobile .dropdown.global-menu .dropdown-menu {
|
||||||
|
border-radius: var(--dropdown-border-radius) var(--dropdown-border-radius) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.mobile .dropdown-menu {
|
||||||
|
--dropdown-menu-padding-vertical: 0.7em;
|
||||||
|
--dropdown-menu-padding-horizontal: 1em;
|
||||||
|
--hover-item-background-color: var(--card-background-color);
|
||||||
|
font-size: 1em !important;
|
||||||
|
backdrop-filter: var(--dropdown-backdrop-filter);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
top: 0.5em;
|
||||||
|
right: var(--dropdown-menu-padding-horizontal);
|
||||||
|
transform: translateX(50%) rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item.submenu-open .dropdown-toggle::after {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item,
|
||||||
|
.dropdown-custom-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: var(--dropdown-menu-padding-vertical) var(--dropdown-menu-padding-horizontal) !important;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
border-bottom: 1px solid var(--menu-item-delimiter-color) !important;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:first-of-type,
|
||||||
|
.dropdown-divider + .dropdown-item,
|
||||||
|
.dropdown-custom-item:first-of-type,
|
||||||
|
.dropdown-divider + .dropdown-custom-item {
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:last-of-type,
|
||||||
|
.dropdown-item:has(+ .dropdown-divider),
|
||||||
|
.dropdown-custom-item:last-of-type,
|
||||||
|
.dropdown-custom-item:has(+ .dropdown-divider) {
|
||||||
|
border-bottom-left-radius: 6px;
|
||||||
|
border-bottom-right-radius: 6px;
|
||||||
|
border-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-divider {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-submenu {
|
||||||
|
padding: 0 !important;
|
||||||
|
backdrop-filter: unset !important;
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
padding: var(--dropdown-menu-padding-vertical) var(--dropdown-menu-padding-horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
--menu-background-color: --menu-submenu-mobile-background-color;
|
||||||
|
--bs-dropdown-divider-margin-y: 0.25rem;
|
||||||
|
border-radius: 0;
|
||||||
|
max-height: 0;
|
||||||
|
transition: max-height 100ms ease-in;
|
||||||
|
display: block !important;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
max-height: 1000px;
|
||||||
|
padding: 0.5rem 0.75rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.submenu-open {
|
||||||
|
.dropdown-toggle {
|
||||||
|
padding-bottom: var(--dropdown-menu-padding-vertical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-custom-item:has(.note-color-picker) {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-color-picker {
|
||||||
|
padding: 0;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
.color-cell {
|
||||||
|
--color-picker-cell-size: 26px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* #endregion */
|
||||||
|
|
||||||
body.desktop .dropdown-menu .dropdown-toggle::after {
|
body.desktop .dropdown-menu .dropdown-toggle::after {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .dropdown-menu .dropdown-toggle::after {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdown item button (used in zoom buttons in global menu) */
|
/* Dropdown item button (used in zoom buttons in global menu) */
|
||||||
|
|
||||||
@@ -347,6 +445,12 @@ li.dropdown-item a.dropdown-item-button:focus-visible {
|
|||||||
outline: 2px solid var(--input-focus-outline-color) !important;
|
outline: 2px solid var(--input-focus-outline-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root .dropdown-menu .note-color-picker {
|
||||||
|
padding: 4px 10px;
|
||||||
|
--note-color-picker-clear-color-cell-background: var(--main-text-color);
|
||||||
|
--note-color-picker-clear-color-cell-selection-outline-color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TOASTS
|
* TOASTS
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
.modal .modal-header .btn-close,
|
.modal .modal-header .btn-close,
|
||||||
.modal .modal-header .help-button,
|
.modal .modal-header .help-button,
|
||||||
|
.modal .modal-header .custom-title-bar-button,
|
||||||
#toast-container .toast .toast-header .btn-close {
|
#toast-container .toast .toast-header .btn-close {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -55,15 +56,17 @@
|
|||||||
font-family: boxicons;
|
font-family: boxicons;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-header .help-button {
|
.modal .modal-header .help-button,
|
||||||
|
.modal .modal-header .custom-title-bar-button {
|
||||||
margin-inline-end: 0;
|
margin-inline-end: 0;
|
||||||
font-size: calc(var(--modal-control-button-size) * .75);
|
font-size: calc(var(--modal-control-button-size) * .70);
|
||||||
font-family: unset;
|
font-family: unset;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-header .btn-close:hover,
|
.modal .modal-header .btn-close:hover,
|
||||||
.modal .modal-header .help-button:hover,
|
.modal .modal-header .help-button:hover,
|
||||||
|
.modal .modal-header .custom-title-bar-button:hover,
|
||||||
#toast-container .toast .toast-header .btn-close:hover {
|
#toast-container .toast .toast-header .btn-close:hover {
|
||||||
background: var(--modal-control-button-hover-background);
|
background: var(--modal-control-button-hover-background);
|
||||||
color: var(--modal-control-button-hover-color);
|
color: var(--modal-control-button-hover-color);
|
||||||
@@ -71,6 +74,7 @@
|
|||||||
|
|
||||||
.modal .modal-header .btn-close:active,
|
.modal .modal-header .btn-close:active,
|
||||||
.modal .modal-header .help-button:active,
|
.modal .modal-header .help-button:active,
|
||||||
|
.modal .modal-header .custom-title-bar-button:active,
|
||||||
#toast-container .toast .toast-header .btn-close:active {
|
#toast-container .toast .toast-header .btn-close:active {
|
||||||
transform: scale(.85);
|
transform: scale(.85);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .c
|
|||||||
padding: 4px 16px;
|
padding: 4px 16px;
|
||||||
background: var(--cmd-button-background-color);
|
background: var(--cmd-button-background-color);
|
||||||
color: var(--cmd-button-text-color);
|
color: var(--cmd-button-text-color);
|
||||||
|
|
||||||
|
&.dropdown-toggle-split {
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn.btn-primary:hover,
|
button.btn.btn-primary:hover,
|
||||||
@@ -142,6 +146,14 @@ button.btn.btn-success kbd {
|
|||||||
outline: 2px solid var(--input-focus-outline-color);
|
outline: 2px solid var(--input-focus-outline-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Button groups */
|
||||||
|
|
||||||
|
/* Active button */
|
||||||
|
:root .btn-group button.btn.active {
|
||||||
|
background-color: var(--button-group-active-button-background);
|
||||||
|
color: var(--button-group-active-button-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input boxes
|
* Input boxes
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -643,7 +643,7 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-printable:not(.word-wrap) pre code {
|
.ck-content:not(.word-wrap) pre code {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,12 +124,8 @@
|
|||||||
|
|
||||||
/* The container */
|
/* The container */
|
||||||
|
|
||||||
.note-split.empty-note {
|
|
||||||
--max-content-width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-split.empty-note div.note-detail {
|
.note-split.empty-note div.note-detail {
|
||||||
margin: 50px auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The search results list */
|
/* The search results list */
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
background-color: var(--root-background);
|
background-color: var(--root-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.mobile #root-widget {
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
--native-titlebar-darwin-x-offset: 10;
|
--native-titlebar-darwin-x-offset: 10;
|
||||||
--native-titlebar-darwin-y-offset: 12 !important;
|
--native-titlebar-darwin-y-offset: 12 !important;
|
||||||
@@ -341,7 +345,7 @@ body[dir=ltr] #launcher-container {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.calendar-dropdown-widget {
|
.calendar-dropdown-widget {
|
||||||
padding: 12px;
|
padding: 18px;
|
||||||
color: var(--calendar-color);
|
color: var(--calendar-color);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
@@ -1424,9 +1428,7 @@ div.promoted-attribute-cell .tn-checkbox {
|
|||||||
height: 1cap;
|
height: 1cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Relocate the checkbox before the label */
|
|
||||||
div.promoted-attribute-cell.promoted-attribute-label-boolean > div:first-of-type {
|
div.promoted-attribute-cell.promoted-attribute-label-boolean > div:first-of-type {
|
||||||
order: -1;
|
|
||||||
margin-inline-end: 1.5em;
|
margin-inline-end: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1446,12 +1448,20 @@ div.promoted-attribute-cell .multiplicity:has(span) span {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.promoted-attribute-cell.promoted-attribute-label-color {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.promoted-attribute-cell.promoted-attribute-label-color .input-group {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Floating buttons
|
* Floating buttons
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Floating buttons container */
|
/* Floating buttons container */
|
||||||
div#center-pane .floating-buttons-children {
|
.floating-buttons-children {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
min-height: var(--floating-button-height);
|
min-height: var(--floating-button-height);
|
||||||
transform-origin: right;
|
transform-origin: right;
|
||||||
@@ -1463,12 +1473,12 @@ div#center-pane .floating-buttons-children {
|
|||||||
opacity 250ms ease-out;
|
opacity 250ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
body[dir=rtl] div#center-pane .floating-buttons-children {
|
body[dir=rtl] .floating-buttons-children {
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Floating buttons container (collapsed) */
|
/* Floating buttons container (collapsed) */
|
||||||
div#center-pane .floating-buttons-children.temporarily-hidden {
|
.floating-buttons-children.temporarily-hidden {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scaleX(0);
|
transform: scaleX(0);
|
||||||
|
|||||||
@@ -87,7 +87,11 @@ export function buildNote(noteDef: NoteDefinition) {
|
|||||||
let position = 0;
|
let position = 0;
|
||||||
for (const [ key, value ] of Object.entries(noteDef)) {
|
for (const [ key, value ] of Object.entries(noteDef)) {
|
||||||
const attributeId = utils.randomString(12);
|
const attributeId = utils.randomString(12);
|
||||||
const name = key.substring(1);
|
let name = key.substring(1);
|
||||||
|
const isInheritable = key.endsWith("(inheritable)");
|
||||||
|
if (isInheritable) {
|
||||||
|
name = name.substring(0, name.length - "(inheritable)".length);
|
||||||
|
}
|
||||||
|
|
||||||
let attribute: FAttribute | null = null;
|
let attribute: FAttribute | null = null;
|
||||||
if (key.startsWith("#")) {
|
if (key.startsWith("#")) {
|
||||||
@@ -98,7 +102,7 @@ export function buildNote(noteDef: NoteDefinition) {
|
|||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
position,
|
position,
|
||||||
isInheritable: false
|
isInheritable
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +114,7 @@ export function buildNote(noteDef: NoteDefinition) {
|
|||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
position,
|
position,
|
||||||
isInheritable: false
|
isInheritable
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ function mockServer() {
|
|||||||
attributes: []
|
attributes: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.warn(`Unsupported GET to mocked server: ${url}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async post(url: string, data: object) {
|
async post(url: string, data: object) {
|
||||||
|
|||||||
@@ -201,8 +201,12 @@
|
|||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"relation": "العلاقة",
|
"relation": "العلاقة",
|
||||||
"backlink": "{{count}} رابط راجع",
|
"backlink_zero": "",
|
||||||
"backlinks": "{{count}} روابط راجعة"
|
"backlink_one": "{{count}} رابط راجع",
|
||||||
|
"backlink_two": "",
|
||||||
|
"backlink_few": "",
|
||||||
|
"backlink_many": "{{count}} روابط راجعة",
|
||||||
|
"backlink_other": ""
|
||||||
},
|
},
|
||||||
"note_icon": {
|
"note_icon": {
|
||||||
"category": "الفئة:",
|
"category": "الفئة:",
|
||||||
@@ -230,7 +234,6 @@
|
|||||||
"geo-map": "الخريطة الجغرافية",
|
"geo-map": "الخريطة الجغرافية",
|
||||||
"collapse_all_notes": "طي كل الملاحظات",
|
"collapse_all_notes": "طي كل الملاحظات",
|
||||||
"include_archived_notes": "عرض الملاحظات المؤرشفة",
|
"include_archived_notes": "عرض الملاحظات المؤرشفة",
|
||||||
"expand_all_children": "توسيع جميع العناصر الفرعية",
|
|
||||||
"presentation": "عرض تقديمي",
|
"presentation": "عرض تقديمي",
|
||||||
"invalid_view_type": "نوع العرض {{type}} غير صالح"
|
"invalid_view_type": "نوع العرض {{type}} غير صالح"
|
||||||
},
|
},
|
||||||
@@ -512,7 +515,7 @@
|
|||||||
"title": "الملاحظات المعدلة"
|
"title": "الملاحظات المعدلة"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": ""
|
"info": "معلومات"
|
||||||
},
|
},
|
||||||
"backend_log": {
|
"backend_log": {
|
||||||
"refresh": "تحديث"
|
"refresh": "تحديث"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "Sobre Trilium Notes",
|
"title": "Sobre Trilium Notes",
|
||||||
"homepage": "Pàgina principal:"
|
"homepage": "Pàgina principal:",
|
||||||
|
"app_version": "Versió de l'aplicació:",
|
||||||
|
"db_version": "Versió de la base de dades:"
|
||||||
},
|
},
|
||||||
"add_link": {
|
"add_link": {
|
||||||
"note": "Nota"
|
"note": "Nota"
|
||||||
|
|||||||
@@ -162,7 +162,8 @@
|
|||||||
"inPageSearch": "页面内搜索",
|
"inPageSearch": "页面内搜索",
|
||||||
"newTabWithActivationNoteLink": "在新标签页打开笔记链接并激活该标签页",
|
"newTabWithActivationNoteLink": "在新标签页打开笔记链接并激活该标签页",
|
||||||
"title": "资料表",
|
"title": "资料表",
|
||||||
"newTabNoteLink": "在新标签页开启链接"
|
"newTabNoteLink": "在新标签页开启链接",
|
||||||
|
"editShortcuts": "编辑键盘快捷键"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"importIntoNote": "导入到笔记",
|
"importIntoNote": "导入到笔记",
|
||||||
@@ -735,9 +736,8 @@
|
|||||||
"zoom_out_title": "缩小"
|
"zoom_out_title": "缩小"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} 个反链",
|
"relation": "关系",
|
||||||
"backlinks": "{{count}} 个反链",
|
"backlink_other": "{{count}} 个反链"
|
||||||
"relation": "关系"
|
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "插入子笔记",
|
"insert_child_note": "插入子笔记",
|
||||||
@@ -764,7 +764,6 @@
|
|||||||
"grid": "网格",
|
"grid": "网格",
|
||||||
"list": "列表",
|
"list": "列表",
|
||||||
"collapse_all_notes": "折叠所有笔记",
|
"collapse_all_notes": "折叠所有笔记",
|
||||||
"expand_all_children": "展开所有子项",
|
|
||||||
"collapse": "折叠",
|
"collapse": "折叠",
|
||||||
"expand": "展开",
|
"expand": "展开",
|
||||||
"invalid_view_type": "无效的查看类型 '{{type}}'",
|
"invalid_view_type": "无效的查看类型 '{{type}}'",
|
||||||
@@ -774,7 +773,11 @@
|
|||||||
"geo-map": "地理地图",
|
"geo-map": "地理地图",
|
||||||
"board": "看板",
|
"board": "看板",
|
||||||
"include_archived_notes": "展示归档笔记",
|
"include_archived_notes": "展示归档笔记",
|
||||||
"presentation": "演示"
|
"presentation": "演示",
|
||||||
|
"expand_tooltip": "展开此集合的直接子代(单层深度)。点击右方箭头以查看更多选项。",
|
||||||
|
"expand_first_level": "展开直接子代",
|
||||||
|
"expand_nth_level": "展开 {{depth}} 层",
|
||||||
|
"expand_all_levels": "展开所有层级"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "今天还没有编辑过的笔记...",
|
"no_edited_notes_found": "今天还没有编辑过的笔记...",
|
||||||
@@ -837,7 +840,8 @@
|
|||||||
"search": "搜索"
|
"search": "搜索"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "笔记来源:"
|
"this_note_was_originally_taken_from": "笔记来源:",
|
||||||
|
"info": "信息"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "拥有的属性"
|
"owned_attributes": "拥有的属性"
|
||||||
@@ -1150,7 +1154,10 @@
|
|||||||
"unit": "字符"
|
"unit": "字符"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "下拉菜单可用的MIME文件类型"
|
"title": "下拉菜单可用的MIME文件类型",
|
||||||
|
"tooltip_syntax_highlighting": "语法高亮",
|
||||||
|
"tooltip_code_block_syntax": "文本笔记中的代码块",
|
||||||
|
"tooltip_code_note_syntax": "代码笔记"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Vim 快捷键",
|
"use_vim_keybindings_in_code_notes": "Vim 快捷键",
|
||||||
@@ -1550,7 +1557,8 @@
|
|||||||
"refresh-saved-search-results": "刷新保存的搜索结果",
|
"refresh-saved-search-results": "刷新保存的搜索结果",
|
||||||
"create-child-note": "创建子笔记",
|
"create-child-note": "创建子笔记",
|
||||||
"unhoist": "取消聚焦",
|
"unhoist": "取消聚焦",
|
||||||
"toggle-sidebar": "切换侧边栏"
|
"toggle-sidebar": "切换侧边栏",
|
||||||
|
"dropping-not-allowed": "不允许移动笔记到此处。"
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "保持此窗口置顶"
|
"window-on-top": "保持此窗口置顶"
|
||||||
@@ -1653,7 +1661,8 @@
|
|||||||
"duplicate-launcher": "复制启动器 <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "复制启动器 <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "自动检测"
|
"auto-detect-language": "自动检测",
|
||||||
|
"keeps-crashing": "编辑组件时持续崩溃。请尝试重启 Trilium。如果问题仍然存在,请考虑提交错误报告。"
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "代码块",
|
"title": "代码块",
|
||||||
@@ -1694,7 +1703,8 @@
|
|||||||
"copy-link": "复制链接",
|
"copy-link": "复制链接",
|
||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
"paste-as-plain-text": "以纯文本粘贴",
|
"paste-as-plain-text": "以纯文本粘贴",
|
||||||
"search_online": "用 {{searchEngine}} 搜索 \"{{term}}\""
|
"search_online": "用 {{searchEngine}} 搜索 \"{{term}}\"",
|
||||||
|
"search_in_trilium": "在 Trilium 中搜索「{{term}}」"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "复制引用到剪贴板",
|
"copy_reference_to_clipboard": "复制引用到剪贴板",
|
||||||
@@ -1887,9 +1897,7 @@
|
|||||||
"indexing_stopped": "索引已停止",
|
"indexing_stopped": "索引已停止",
|
||||||
"indexing_in_progress": "索引进行中...",
|
"indexing_in_progress": "索引进行中...",
|
||||||
"last_indexed": "最后索引时间",
|
"last_indexed": "最后索引时间",
|
||||||
"n_notes_queued_0": "{{ count }} 条笔记已加入索引队列",
|
|
||||||
"note_chat": "笔记聊天",
|
"note_chat": "笔记聊天",
|
||||||
"notes_indexed_0": "{{ count }} 条笔记已索引",
|
|
||||||
"sources": "来源",
|
"sources": "来源",
|
||||||
"start_indexing": "开始索引",
|
"start_indexing": "开始索引",
|
||||||
"use_advanced_context": "使用高级上下文",
|
"use_advanced_context": "使用高级上下文",
|
||||||
@@ -2018,7 +2026,8 @@
|
|||||||
"add-column-placeholder": "请输入列名...",
|
"add-column-placeholder": "请输入列名...",
|
||||||
"edit-note-title": "点击编辑笔记标题",
|
"edit-note-title": "点击编辑笔记标题",
|
||||||
"edit-column-title": "点击编辑列标题",
|
"edit-column-title": "点击编辑列标题",
|
||||||
"remove-from-board": "从看板上移除"
|
"remove-from-board": "从看板上移除",
|
||||||
|
"column-already-exists": "此列已在看板上。"
|
||||||
},
|
},
|
||||||
"command_palette": {
|
"command_palette": {
|
||||||
"tree-action-name": "树形:{{name}}",
|
"tree-action-name": "树形:{{name}}",
|
||||||
@@ -2087,7 +2096,14 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "当前正在查看一个只读笔记。",
|
"read-only-note": "当前正在查看一个只读笔记。",
|
||||||
"auto-read-only-note": "这条笔记以只读模式显示便于快速加载。",
|
"auto-read-only-note": "这条笔记以只读模式显示便于快速加载。",
|
||||||
"auto-read-only-learn-more": "了解更多",
|
|
||||||
"edit-note": "编辑笔记"
|
"edit-note": "编辑笔记"
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "清除笔记颜色",
|
||||||
|
"set-color": "设置笔记颜色",
|
||||||
|
"set-custom-color": "设置自定义笔记颜色"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "切换至完整编辑器"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,41 +24,90 @@
|
|||||||
"message": "Uživatelský skript z poznámky s ID \"{{id}}\" a názvem \"{{title}}\" nemohl být spuštěn z důvodu: \n\n{{message}}"
|
"message": "Uživatelský skript z poznámky s ID \"{{id}}\" a názvem \"{{title}}\" nemohl být spuštěn z důvodu: \n\n{{message}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ai_llm": {
|
|
||||||
"n_notes_queued_0": "{{ count }} poznámka ve frontě k indexaci",
|
|
||||||
"n_notes_queued_1": "{{ count }} poznámky ve frontě k indexaci",
|
|
||||||
"n_notes_queued_2": "{{ count }} poznámek ve frontě k indexaci",
|
|
||||||
"notes_indexed_0": "{{ count }} poznámka indexována",
|
|
||||||
"notes_indexed_1": "{{ count }} poznámky indexovány",
|
|
||||||
"notes_indexed_2": "{{ count }} poznámek indexováno"
|
|
||||||
},
|
|
||||||
"add_link": {
|
"add_link": {
|
||||||
"add_link": "Přidat odkaz",
|
"add_link": "Přidat odkaz",
|
||||||
"help_on_links": "Nápověda k odkazům",
|
"help_on_links": "Nápověda k odkazům",
|
||||||
"note": "Poznámka",
|
"note": "Poznámka",
|
||||||
"search_note": "hledat poznámku podle názvu",
|
"search_note": "hledat poznámku podle názvu",
|
||||||
"link_title": "Název odkazu",
|
"link_title": "Název odkazu",
|
||||||
"button_add_link": "Přidat odkaz"
|
"button_add_link": "Přidat odkaz",
|
||||||
|
"link_title_mirrors": "titulek odkazu odráží momentální titulek poznámky",
|
||||||
|
"link_title_arbitrary": "titulek odkazu může být změněn libovolně"
|
||||||
},
|
},
|
||||||
"branch_prefix": {
|
"branch_prefix": {
|
||||||
"prefix": "Prefix: ",
|
"prefix": "Předpona: ",
|
||||||
"save": "Uložit"
|
"save": "Uložit",
|
||||||
|
"edit_branch_prefix": "Upravit prefix větve",
|
||||||
|
"edit_branch_prefix_multiple": "Upravit prefix větve pro {{count}} větví",
|
||||||
|
"help_on_tree_prefix": "Nápověda k prefixu stromu",
|
||||||
|
"branch_prefix_saved": "Prefix větve byl uložen.",
|
||||||
|
"branch_prefix_saved_multiple": "Prefix větve byl uložen pro {{count}} větví.",
|
||||||
|
"affected_branches": "Ovlivněné větve ({{count}}):"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "Hromadné akce",
|
"bulk_actions": "Hromadné akce",
|
||||||
"affected_notes": "Ovlivněné poznámky",
|
"affected_notes": "Ovlivněné poznámky",
|
||||||
"notes": "Poznámky"
|
"notes": "Poznámky",
|
||||||
|
"include_descendants": "Zahrnout potomky vybraných poznámek",
|
||||||
|
"available_actions": "Dostupné akce",
|
||||||
|
"chosen_actions": "Vybrané akce",
|
||||||
|
"execute_bulk_actions": "Vykonat hromadné akce",
|
||||||
|
"bulk_actions_executed": "Hromadné akce byly úspěšně provedeny.",
|
||||||
|
"labels": "Štítky",
|
||||||
|
"relations": "Relace",
|
||||||
|
"other": "Ostatní",
|
||||||
|
"none_yet": "Zatím žádné akce... přidejte akci kliknutím na jednu z dostupných výše."
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"ok": "OK"
|
"ok": "OK",
|
||||||
|
"confirmation": "Potvrzení",
|
||||||
|
"are_you_sure_remove_note": "Opravdu chcete odstranit poznámku „{{title}}“ z mapy vztahů? ",
|
||||||
|
"if_you_dont_check": "Pokud tuto možnost nezaškrtnete, poznámka bude odstraněna pouze z mapy vztahů.",
|
||||||
|
"also_delete_note": "Odstraňte také poznámku"
|
||||||
},
|
},
|
||||||
"delete_notes": {
|
"delete_notes": {
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"close": "Zavřít"
|
"close": "Zavřít",
|
||||||
|
"delete_notes_preview": "Odstranit náhled poznámek",
|
||||||
|
"delete_all_clones_description": "Odstraňte také všechny klony (lze vrátit zpět v nedávných změnách)",
|
||||||
|
"erase_notes_description": "Normální (měkké) smazání pouze označí poznámky jako smazané a lze je během určité doby obnovit (v dialogovém okně posledních změn). Zaškrtnutím této možnosti se poznámky okamžitě vymažou a nebude možné je obnovit.",
|
||||||
|
"erase_notes_warning": "Trvale smažte poznámky (nelze vrátit zpět), včetně všech klonů. Tím se vynutí opětovné načtení aplikace.",
|
||||||
|
"notes_to_be_deleted": "Následující poznámky budou smazány ({{notesCount}})",
|
||||||
|
"no_note_to_delete": "Žádná poznámka nebude smazána (pouze klony).",
|
||||||
|
"broken_relations_to_be_deleted": "Následující vazby budou přerušeny a smazány ({{relationCount}})",
|
||||||
|
"deleted_relation_text": "Poznámka {{- note}} (bude smazána) je odkazována vazbou {{- relation}} pocházející z {{- source}}."
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"close": "Zavřít"
|
"close": "Zavřít",
|
||||||
|
"export_note_title": "Exportovat poznámku",
|
||||||
|
"export_type_subtree": "Tato poznámka a všechny její odvozené poznámky",
|
||||||
|
"format_html": "HTML – doporučeno, protože zachovává veškeré formátování",
|
||||||
|
"format_html_zip": "HTML v archivu ZIP – toto se doporučuje, protože se tak zachová veškeré formátování.",
|
||||||
|
"format_markdown": "Markdown – zachovává většinu formátování.",
|
||||||
|
"format_opml": "OPML – formát pro výměnu osnov pouze pro text. Formátování, obrázky a soubory nejsou zahrnuty.",
|
||||||
|
"opml_version_1": "OPML v1.0 – pouze prostý text",
|
||||||
|
"opml_version_2": "OPML v2.0 – umožňuje také HTML",
|
||||||
|
"export_type_single": "Pouze tato poznámka bez jejích potomků",
|
||||||
|
"export": "Exportovat",
|
||||||
|
"choose_export_type": "Nejprve vyberte typ exportu",
|
||||||
|
"export_status": "Stav exportu",
|
||||||
|
"export_in_progress": "Export probíhá: {{progressCount}}",
|
||||||
|
"export_finished_successfully": "Export byl úspěšně dokončen.",
|
||||||
|
"format_pdf": "PDF – pro tisk nebo sdílení.",
|
||||||
|
"share-format": "HTML pro publikování na webu – používá stejný motiv jako sdílené poznámky, ale lze jej publikovat jako statický web."
|
||||||
|
},
|
||||||
|
"clone_to": {
|
||||||
|
"clone_notes_to": "Klonovat poznámky do...",
|
||||||
|
"help_on_links": "Nápověda k odkazům",
|
||||||
|
"notes_to_clone": "Poznámky na klonování",
|
||||||
|
"search_for_note_by_its_name": "hledat poznámku dle jejího názvu",
|
||||||
|
"prefix_optional": "Předpona (volitelná)",
|
||||||
|
"target_parent_note": "Zaměřit rodičovskou poznámku",
|
||||||
|
"cloned_note_prefix_title": "Klonovaná poznámka se zobrazí ve stromu poznámek s danou předponou",
|
||||||
|
"clone_to_selected_note": "Klonovat vybranou poznámku",
|
||||||
|
"no_path_to_clone_to": "Žádná cest pro klonování.",
|
||||||
|
"note_cloned": "Poznámka: „{{clonedTitle}}“ bylo naklonováno do „{{targetTitle}}“"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
"bundle-error": {
|
"bundle-error": {
|
||||||
"title": "Benutzerdefiniertes Skript konnte nicht geladen werden",
|
"title": "Benutzerdefiniertes Skript konnte nicht geladen werden",
|
||||||
"message": "Skript von der Notiz mit der ID \"{{id}}\", und dem Titel \"{{title}}\" konnte nicht ausgeführt werden wegen:\n\n{{message}}"
|
"message": "Skript aus der Notiz \"{{title}}\" mit der ID \"{{id}}\", konnte nicht ausgeführt werden wegen:\n\n{{message}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"add_link": {
|
"add_link": {
|
||||||
@@ -39,7 +39,10 @@
|
|||||||
"help_on_tree_prefix": "Hilfe zum Baumpräfix",
|
"help_on_tree_prefix": "Hilfe zum Baumpräfix",
|
||||||
"prefix": "Präfix: ",
|
"prefix": "Präfix: ",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"branch_prefix_saved": "Zweigpräfix wurde gespeichert."
|
"branch_prefix_saved": "Zweigpräfix wurde gespeichert.",
|
||||||
|
"branch_prefix_saved_multiple": "Der Zweigpräfix wurde für {{count}} Zweige gespeichert.",
|
||||||
|
"edit_branch_prefix_multiple": "Branch-Präfix für {{count}} Zweige bearbeiten",
|
||||||
|
"affected_branches": "Betroffene Zweige ({{count}}):"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "Massenaktionen",
|
"bulk_actions": "Massenaktionen",
|
||||||
@@ -684,7 +687,8 @@
|
|||||||
"convert_into_attachment_failed": "Konvertierung der Notiz '{{title}}' fehlgeschlagen.",
|
"convert_into_attachment_failed": "Konvertierung der Notiz '{{title}}' fehlgeschlagen.",
|
||||||
"convert_into_attachment_successful": "Notiz '{{title}}' wurde als Anhang konvertiert.",
|
"convert_into_attachment_successful": "Notiz '{{title}}' wurde als Anhang konvertiert.",
|
||||||
"convert_into_attachment_prompt": "Bist du dir sicher, dass du die Notiz '{{title}}' in ein Anhang der übergeordneten Notiz konvertieren möchtest?",
|
"convert_into_attachment_prompt": "Bist du dir sicher, dass du die Notiz '{{title}}' in ein Anhang der übergeordneten Notiz konvertieren möchtest?",
|
||||||
"print_pdf": "Export als PDF..."
|
"print_pdf": "Export als PDF...",
|
||||||
|
"open_note_on_server": "Öffne Notiz auf dem Server"
|
||||||
},
|
},
|
||||||
"onclick_button": {
|
"onclick_button": {
|
||||||
"no_click_handler": "Das Schaltflächen-Widget „{{componentId}}“ hat keinen definierten Klick-Handler"
|
"no_click_handler": "Das Schaltflächen-Widget „{{componentId}}“ hat keinen definierten Klick-Handler"
|
||||||
@@ -728,9 +732,9 @@
|
|||||||
"zoom_out_title": "Herauszoomen"
|
"zoom_out_title": "Herauszoomen"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Rückverlinkung",
|
"relation": "Beziehung",
|
||||||
"backlinks": "{{count}} Rückverlinkungen",
|
"backlink_one": "{{count}} Rückverlinkung",
|
||||||
"relation": "Beziehung"
|
"backlink_other": "{{count}} Rückverlinkungen"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Untergeordnete Notiz einfügen",
|
"insert_child_note": "Untergeordnete Notiz einfügen",
|
||||||
@@ -757,7 +761,6 @@
|
|||||||
"grid": "Gitter",
|
"grid": "Gitter",
|
||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
"collapse_all_notes": "Alle Notizen einklappen",
|
"collapse_all_notes": "Alle Notizen einklappen",
|
||||||
"expand_all_children": "Unternotizen ausklappen",
|
|
||||||
"collapse": "Einklappen",
|
"collapse": "Einklappen",
|
||||||
"expand": "Ausklappen",
|
"expand": "Ausklappen",
|
||||||
"invalid_view_type": "Ungültiger Ansichtstyp „{{type}}“",
|
"invalid_view_type": "Ungültiger Ansichtstyp „{{type}}“",
|
||||||
@@ -767,7 +770,11 @@
|
|||||||
"geo-map": "Weltkarte",
|
"geo-map": "Weltkarte",
|
||||||
"board": "Tafel",
|
"board": "Tafel",
|
||||||
"include_archived_notes": "Zeige archivierte Notizen",
|
"include_archived_notes": "Zeige archivierte Notizen",
|
||||||
"presentation": "Präsentation"
|
"presentation": "Präsentation",
|
||||||
|
"expand_all_levels": "Alle Ebenen erweitern",
|
||||||
|
"expand_tooltip": "Erweitert die direkten Unterelemente dieser Sammlung (eine Ebene tiefer). Für weitere Optionen auf den Pfeil rechts klicken.",
|
||||||
|
"expand_first_level": "Direkte Unterelemente erweitern",
|
||||||
|
"expand_nth_level": "{{depth}} Ebenen erweitern"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
|
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
|
||||||
@@ -830,7 +837,8 @@
|
|||||||
"search": "Suchen"
|
"search": "Suchen"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Diese Notiz stammt ursprünglich aus:"
|
"this_note_was_originally_taken_from": "Diese Notiz stammt ursprünglich aus:",
|
||||||
|
"info": "Info"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Eigene Attribute"
|
"owned_attributes": "Eigene Attribute"
|
||||||
@@ -1103,7 +1111,8 @@
|
|||||||
"title": "Inhaltsbreite",
|
"title": "Inhaltsbreite",
|
||||||
"default_description": "Trilium begrenzt standardmäßig die maximale Inhaltsbreite, um die Lesbarkeit für maximierte Bildschirme auf Breitbildschirmen zu verbessern.",
|
"default_description": "Trilium begrenzt standardmäßig die maximale Inhaltsbreite, um die Lesbarkeit für maximierte Bildschirme auf Breitbildschirmen zu verbessern.",
|
||||||
"max_width_label": "Maximale Inhaltsbreite in Pixel",
|
"max_width_label": "Maximale Inhaltsbreite in Pixel",
|
||||||
"max_width_unit": "Pixel"
|
"max_width_unit": "Pixel",
|
||||||
|
"centerContent": "Inhalt zentriert halten"
|
||||||
},
|
},
|
||||||
"native_title_bar": {
|
"native_title_bar": {
|
||||||
"title": "Native Titelleiste (App-Neustart erforderlich)",
|
"title": "Native Titelleiste (App-Neustart erforderlich)",
|
||||||
@@ -1142,7 +1151,10 @@
|
|||||||
"unit": "Zeichen"
|
"unit": "Zeichen"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "Verfügbare MIME-Typen im Dropdown-Menü"
|
"title": "Verfügbare MIME-Typen im Dropdown-Menü",
|
||||||
|
"tooltip_syntax_highlighting": "Syntaxhervorhebung",
|
||||||
|
"tooltip_code_block_syntax": "Code-Blöcke in Textnotizen",
|
||||||
|
"tooltip_code_note_syntax": "Code-Notizen"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Verwende VIM-Tastenkombinationen in Codenotizen (kein Ex-Modus)",
|
"use_vim_keybindings_in_code_notes": "Verwende VIM-Tastenkombinationen in Codenotizen (kein Ex-Modus)",
|
||||||
@@ -1509,7 +1521,8 @@
|
|||||||
"refresh-saved-search-results": "Gespeicherte Suchergebnisse aktualisieren",
|
"refresh-saved-search-results": "Gespeicherte Suchergebnisse aktualisieren",
|
||||||
"create-child-note": "Unternotiz anlegen",
|
"create-child-note": "Unternotiz anlegen",
|
||||||
"unhoist": "Fokus verlassen",
|
"unhoist": "Fokus verlassen",
|
||||||
"toggle-sidebar": "Seitenleiste ein-/ausblenden"
|
"toggle-sidebar": "Seitenleiste ein-/ausblenden",
|
||||||
|
"dropping-not-allowed": "Ablegen von Notizen an dieser Stelle ist nicht zulässig."
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "Dieses Fenster immer oben halten"
|
"window-on-top": "Dieses Fenster immer oben halten"
|
||||||
@@ -1612,7 +1625,8 @@
|
|||||||
"duplicate-launcher": "Launcher duplizieren <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "Launcher duplizieren <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "Automatisch erkannt"
|
"auto-detect-language": "Automatisch erkannt",
|
||||||
|
"keeps-crashing": "Die Bearbeitungskomponente stürzt immer wieder ab. Bitte starten Sie Trilium neu. Wenn das Problem weiterhin besteht, erstellen Sie einen Fehlerbericht."
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"description": "Steuert die Syntaxhervorhebung für Codeblöcke in Textnotizen, Code-Notizen sind nicht betroffen.",
|
"description": "Steuert die Syntaxhervorhebung für Codeblöcke in Textnotizen, Code-Notizen sind nicht betroffen.",
|
||||||
@@ -1653,7 +1667,8 @@
|
|||||||
"copy-link": "Link kopieren",
|
"copy-link": "Link kopieren",
|
||||||
"paste": "Einfügen",
|
"paste": "Einfügen",
|
||||||
"paste-as-plain-text": "Als unformatierten Text einfügen",
|
"paste-as-plain-text": "Als unformatierten Text einfügen",
|
||||||
"search_online": "Suche nach \"{{term}}\" mit {{searchEngine}} starten"
|
"search_online": "Suche nach \"{{term}}\" mit {{searchEngine}} starten",
|
||||||
|
"search_in_trilium": "Suche nach \"{{term}}\" in Trilium"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "Referenz in Zwischenablage kopieren",
|
"copy_reference_to_clipboard": "Referenz in Zwischenablage kopieren",
|
||||||
@@ -1714,10 +1729,6 @@
|
|||||||
"help_title": "Zeige mehr Informationen zu diesem Fenster"
|
"help_title": "Zeige mehr Informationen zu diesem Fenster"
|
||||||
},
|
},
|
||||||
"ai_llm": {
|
"ai_llm": {
|
||||||
"n_notes_queued": "{{ count }} Notiz zur Indizierung vorgemerkt",
|
|
||||||
"n_notes_queued_plural": "{{ count }} Notizen zur Indizierung vorgemerkt",
|
|
||||||
"notes_indexed": "{{ count }} Notiz indiziert",
|
|
||||||
"notes_indexed_plural": "{{ count }} Notizen indiziert",
|
|
||||||
"not_started": "Nicht gestartet",
|
"not_started": "Nicht gestartet",
|
||||||
"title": "KI Einstellungen",
|
"title": "KI Einstellungen",
|
||||||
"processed_notes": "Verarbeitete Notizen",
|
"processed_notes": "Verarbeitete Notizen",
|
||||||
@@ -2027,7 +2038,8 @@
|
|||||||
"new-item-placeholder": "Notiz Titel eingeben...",
|
"new-item-placeholder": "Notiz Titel eingeben...",
|
||||||
"add-column-placeholder": "Spaltenname eingeben...",
|
"add-column-placeholder": "Spaltenname eingeben...",
|
||||||
"edit-note-title": "Klicke zum Editieren des Notiz-Titels",
|
"edit-note-title": "Klicke zum Editieren des Notiz-Titels",
|
||||||
"edit-column-title": "Klicke zum Editieren des Spalten-Titels"
|
"edit-column-title": "Klicke zum Editieren des Spalten-Titels",
|
||||||
|
"column-already-exists": "Die Spalte ist auf dem Board bereits vorhanden."
|
||||||
},
|
},
|
||||||
"command_palette": {
|
"command_palette": {
|
||||||
"tree-action-name": "Struktur: {{name}}",
|
"tree-action-name": "Struktur: {{name}}",
|
||||||
@@ -2077,5 +2089,18 @@
|
|||||||
"edit-slide": "Folie bearbeiten",
|
"edit-slide": "Folie bearbeiten",
|
||||||
"start-presentation": "Präsentation starten",
|
"start-presentation": "Präsentation starten",
|
||||||
"slide-overview": "Übersicht der Folien ein-/ausblenden"
|
"slide-overview": "Übersicht der Folien ein-/ausblenden"
|
||||||
|
},
|
||||||
|
"read-only-info": {
|
||||||
|
"read-only-note": "Aktuelle Notiz wird im Lese-Modus angezeigt.",
|
||||||
|
"auto-read-only-note": "Diese Notiz wird im Nur-Lesen-Modus angezeigt, um ein schnelleres Laden zu ermöglichen.",
|
||||||
|
"edit-note": "Notiz bearbeiten"
|
||||||
|
},
|
||||||
|
"calendar_view": {
|
||||||
|
"delete_note": "Notiz löschen..."
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "Notizfarbe entfernen",
|
||||||
|
"set-color": "Notizfarbe wählen",
|
||||||
|
"set-custom-color": "Eigene Notizfarbe wählen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,5 @@
|
|||||||
"title": "Κρίσιμο σφάλμα",
|
"title": "Κρίσιμο σφάλμα",
|
||||||
"message": "Συνέβη κάποιο κρίσιμο σφάλμα, το οποίο δεν επιτρέπει στην εφαρμογή χρήστη να ξεκινήσει:\n\n{{message}}\n\nΤο πιθανότερο είναι να προκλήθηκε από κάποιο script που απέτυχε απρόοπτα. Δοκιμάστε να ξεκινήσετε την εφαρμογή σε ασφαλή λειτουργία για να λύσετε το πρόβλημα."
|
"message": "Συνέβη κάποιο κρίσιμο σφάλμα, το οποίο δεν επιτρέπει στην εφαρμογή χρήστη να ξεκινήσει:\n\n{{message}}\n\nΤο πιθανότερο είναι να προκλήθηκε από κάποιο script που απέτυχε απρόοπτα. Δοκιμάστε να ξεκινήσετε την εφαρμογή σε ασφαλή λειτουργία για να λύσετε το πρόβλημα."
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ai_llm": {
|
|
||||||
"n_notes_queued": "{{ count }} σημείωση στην ουρά για εύρεση",
|
|
||||||
"n_notes_queued_plural": "{{ count }} σημειώσεις στην ουρά για εύρεση",
|
|
||||||
"notes_indexed": "{{ count }} σημείωση με ευρετήριο",
|
|
||||||
"notes_indexed_plural": "{{ count }} σημειώσεις με ευρετήριο"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
apps/client/src/translations/en-GB/translation.json
Normal file
73
apps/client/src/translations/en-GB/translation.json
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"import": {
|
||||||
|
"safeImportTooltip": "Trilium <code>.zip</code> export files can contain executable scripts which may contain harmful behaviour. Safe import will deactivate automatic execution of all imported scripts. Uncheck \"Safe import\" only if the imported archive is supposed to contain executable scripts and you completely trust the contents of the import file.",
|
||||||
|
"shrinkImagesTooltip": "<p>If you check this option, Trilium will attempt to shrink the imported images by scaling and optimisation which may affect the perceived image quality. If unchecked, images will be imported without changes.</p><p>This doesn't apply to <code>.zip</code> imports with metadata since it is assumed these files are already optimised.</p>",
|
||||||
|
"codeImportedAsCode": "Import recognised code files (e.g. <code>.json</code>) as code notes if it's unclear from metadata"
|
||||||
|
},
|
||||||
|
"upload_attachments": {
|
||||||
|
"tooltip": "If you check this option, Trilium will attempt to shrink the uploaded images by scaling and optimisation which may affect the perceived image quality. If unchecked, images will be uploaded without changes."
|
||||||
|
},
|
||||||
|
"attribute_detail": {
|
||||||
|
"auto_read_only_disabled": "text/code notes can be set automatically into read mode when they are too large. You can disable this behaviour on per-note basis by adding this label to the note",
|
||||||
|
"workspace_tab_background_color": "CSS colour used in the note tab when hoisted to this note",
|
||||||
|
"color": "defines colour of the note in note tree, links etc. Use any valid CSS colour value like 'red' or #a13d5f",
|
||||||
|
"color_type": "Colour"
|
||||||
|
},
|
||||||
|
"mobile_detail_menu": {
|
||||||
|
"error_unrecognized_command": "Unrecognised command {{command}}"
|
||||||
|
},
|
||||||
|
"promoted_attributes": {
|
||||||
|
"remove_color": "Remove the colour label"
|
||||||
|
},
|
||||||
|
"max_content_width": {
|
||||||
|
"centerContent": "Keep content centred"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"auto_theme": "Legacy (Follow system colour scheme)",
|
||||||
|
"triliumnext": "Trilium (Follow system colour scheme)"
|
||||||
|
},
|
||||||
|
"search_engine": {
|
||||||
|
"custom_name_placeholder": "Customise search engine name",
|
||||||
|
"custom_url_placeholder": "Customise search engine url"
|
||||||
|
},
|
||||||
|
"highlights_list": {
|
||||||
|
"description": "You can customise the highlights list displayed in the right panel:",
|
||||||
|
"color": "Coloured text",
|
||||||
|
"bg_color": "Text with background colour"
|
||||||
|
},
|
||||||
|
"table_of_contents": {
|
||||||
|
"description": "Table of contents will appear in text notes when the note has more than a defined number of headings. You can customise this number:"
|
||||||
|
},
|
||||||
|
"custom_date_time_format": {
|
||||||
|
"description": "Customise the format of the date and time inserted via <shortcut /> or the toolbar. See <doc>Day.js docs</doc> for available format tokens."
|
||||||
|
},
|
||||||
|
"i18n": {
|
||||||
|
"title": "Localisation"
|
||||||
|
},
|
||||||
|
"attachment_detail_2": {
|
||||||
|
"unrecognized_role": "Unrecognised attachment role '{{role}}'."
|
||||||
|
},
|
||||||
|
"ai_llm": {
|
||||||
|
"reprocess_index_started": "Search index optimisation started in the background",
|
||||||
|
"index_rebuilding": "Optimising index ({{percentage}}%)",
|
||||||
|
"index_rebuild_complete": "Index optimisation complete"
|
||||||
|
},
|
||||||
|
"highlighting": {
|
||||||
|
"color-scheme": "Colour Scheme"
|
||||||
|
},
|
||||||
|
"code_theme": {
|
||||||
|
"color-scheme": "Colour scheme"
|
||||||
|
},
|
||||||
|
"call_to_action": {
|
||||||
|
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of colour to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer."
|
||||||
|
},
|
||||||
|
"settings_appearance": {
|
||||||
|
"related_code_blocks": "Colour scheme for code blocks in text notes",
|
||||||
|
"related_code_notes": "Colour scheme for code notes"
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "Clear note colour",
|
||||||
|
"set-color": "Set note colour",
|
||||||
|
"set-custom-color": "Set custom note colour"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -112,6 +112,7 @@
|
|||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"title": "Cheatsheet",
|
"title": "Cheatsheet",
|
||||||
|
"editShortcuts": "Edit keyboard shortcuts",
|
||||||
"noteNavigation": "Note navigation",
|
"noteNavigation": "Note navigation",
|
||||||
"goUpDown": "go up/down in the list of notes",
|
"goUpDown": "go up/down in the list of notes",
|
||||||
"collapseExpand": "collapse/expand node",
|
"collapseExpand": "collapse/expand node",
|
||||||
@@ -735,8 +736,8 @@
|
|||||||
"zoom_out_title": "Zoom Out"
|
"zoom_out_title": "Zoom Out"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Backlink",
|
"backlink_one": "{{count}} Backlink",
|
||||||
"backlinks": "{{count}} Backlinks",
|
"backlink_other": "{{count}} Backlinks",
|
||||||
"relation": "relation"
|
"relation": "relation"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
@@ -764,9 +765,12 @@
|
|||||||
"grid": "Grid",
|
"grid": "Grid",
|
||||||
"list": "List",
|
"list": "List",
|
||||||
"collapse_all_notes": "Collapse all notes",
|
"collapse_all_notes": "Collapse all notes",
|
||||||
"expand_all_children": "Expand all children",
|
"expand_tooltip": "Expands the direct children of this collection (one level deep). For more options, press the arrow on the right.",
|
||||||
"collapse": "Collapse",
|
"collapse": "Collapse",
|
||||||
"expand": "Expand",
|
"expand": "Expand",
|
||||||
|
"expand_first_level": "Expand direct children",
|
||||||
|
"expand_nth_level": "Expand {{depth}} levels",
|
||||||
|
"expand_all_levels": "Expand all levels",
|
||||||
"book_properties": "Collection Properties",
|
"book_properties": "Collection Properties",
|
||||||
"invalid_view_type": "Invalid view type '{{type}}'",
|
"invalid_view_type": "Invalid view type '{{type}}'",
|
||||||
"calendar": "Calendar",
|
"calendar": "Calendar",
|
||||||
@@ -837,7 +841,8 @@
|
|||||||
"search": "Search"
|
"search": "Search"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "This note was originally taken from:"
|
"this_note_was_originally_taken_from": "This note was originally taken from:",
|
||||||
|
"info": "Info"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Owned Attributes"
|
"owned_attributes": "Owned Attributes"
|
||||||
@@ -1266,11 +1271,7 @@
|
|||||||
"indexing_stopped": "Indexing stopped",
|
"indexing_stopped": "Indexing stopped",
|
||||||
"indexing_in_progress": "Indexing in progress...",
|
"indexing_in_progress": "Indexing in progress...",
|
||||||
"last_indexed": "Last Indexed",
|
"last_indexed": "Last Indexed",
|
||||||
"n_notes_queued": "{{ count }} note queued for indexing",
|
|
||||||
"n_notes_queued_plural": "{{ count }} notes queued for indexing",
|
|
||||||
"note_chat": "Note Chat",
|
"note_chat": "Note Chat",
|
||||||
"notes_indexed": "{{ count }} note indexed",
|
|
||||||
"notes_indexed_plural": "{{ count }} notes indexed",
|
|
||||||
"sources": "Sources",
|
"sources": "Sources",
|
||||||
"start_indexing": "Start Indexing",
|
"start_indexing": "Start Indexing",
|
||||||
"use_advanced_context": "Use Advanced Context",
|
"use_advanced_context": "Use Advanced Context",
|
||||||
@@ -1308,7 +1309,10 @@
|
|||||||
"title": "Editor"
|
"title": "Editor"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "Available MIME types in the dropdown"
|
"title": "Available MIME types in the dropdown",
|
||||||
|
"tooltip_syntax_highlighting": "Syntax highlighting",
|
||||||
|
"tooltip_code_block_syntax": "Code blocks in Text notes",
|
||||||
|
"tooltip_code_note_syntax": "Code notes"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Vim keybindings",
|
"use_vim_keybindings_in_code_notes": "Vim keybindings",
|
||||||
@@ -1629,7 +1633,7 @@
|
|||||||
"import-into-note": "Import into note",
|
"import-into-note": "Import into note",
|
||||||
"apply-bulk-actions": "Apply bulk actions",
|
"apply-bulk-actions": "Apply bulk actions",
|
||||||
"converted-to-attachments": "{{count}} notes have been converted to attachments.",
|
"converted-to-attachments": "{{count}} notes have been converted to attachments.",
|
||||||
"convert-to-attachment-confirm": "Are you sure you want to convert note selected notes into attachments of their parent notes?",
|
"convert-to-attachment-confirm": "Are you sure you want to convert the selected notes into attachments of their parent notes? This operation only applies to Image notes, other notes will be skipped.",
|
||||||
"open-in-popup": "Quick edit"
|
"open-in-popup": "Quick edit"
|
||||||
},
|
},
|
||||||
"shared_info": {
|
"shared_info": {
|
||||||
@@ -1640,7 +1644,6 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "Currently viewing a read-only note.",
|
"read-only-note": "Currently viewing a read-only note.",
|
||||||
"auto-read-only-note": "This note is shown in a read-only mode for faster loading.",
|
"auto-read-only-note": "This note is shown in a read-only mode for faster loading.",
|
||||||
"auto-read-only-learn-more": "Learn more",
|
|
||||||
"edit-note": "Edit note"
|
"edit-note": "Edit note"
|
||||||
},
|
},
|
||||||
"note_types": {
|
"note_types": {
|
||||||
@@ -1720,7 +1723,8 @@
|
|||||||
"refresh-saved-search-results": "Refresh saved search results",
|
"refresh-saved-search-results": "Refresh saved search results",
|
||||||
"create-child-note": "Create child note",
|
"create-child-note": "Create child note",
|
||||||
"unhoist": "Unhoist",
|
"unhoist": "Unhoist",
|
||||||
"toggle-sidebar": "Toggle sidebar"
|
"toggle-sidebar": "Toggle sidebar",
|
||||||
|
"dropping-not-allowed": "Dropping notes into this location is not allowed."
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "Keep Window on Top"
|
"window-on-top": "Keep Window on Top"
|
||||||
@@ -1823,7 +1827,8 @@
|
|||||||
"duplicate-launcher": "Duplicate launcher <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "Duplicate launcher <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "Auto-detected"
|
"auto-detect-language": "Auto-detected",
|
||||||
|
"keeps-crashing": "Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report."
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "Code Blocks",
|
"title": "Code Blocks",
|
||||||
@@ -1864,6 +1869,7 @@
|
|||||||
"copy-link": "Copy link",
|
"copy-link": "Copy link",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"paste-as-plain-text": "Paste as plain text",
|
"paste-as-plain-text": "Paste as plain text",
|
||||||
|
"search_in_trilium": "Search for \"{{term}}\" in Trilium",
|
||||||
"search_online": "Search for \"{{term}}\" with {{searchEngine}}"
|
"search_online": "Search for \"{{term}}\" with {{searchEngine}}"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
@@ -1873,6 +1879,7 @@
|
|||||||
"link_context_menu": {
|
"link_context_menu": {
|
||||||
"open_note_in_new_tab": "Open note in a new tab",
|
"open_note_in_new_tab": "Open note in a new tab",
|
||||||
"open_note_in_new_split": "Open note in a new split",
|
"open_note_in_new_split": "Open note in a new split",
|
||||||
|
"open_note_in_other_split": "Open note in the other split",
|
||||||
"open_note_in_new_window": "Open note in a new window",
|
"open_note_in_new_window": "Open note in a new window",
|
||||||
"open_note_in_popup": "Quick edit"
|
"open_note_in_popup": "Quick edit"
|
||||||
},
|
},
|
||||||
@@ -2034,7 +2041,8 @@
|
|||||||
"add-column": "Add Column",
|
"add-column": "Add Column",
|
||||||
"add-column-placeholder": "Enter column name...",
|
"add-column-placeholder": "Enter column name...",
|
||||||
"edit-note-title": "Click to edit note title",
|
"edit-note-title": "Click to edit note title",
|
||||||
"edit-column-title": "Click to edit column title"
|
"edit-column-title": "Click to edit column title",
|
||||||
|
"column-already-exists": "This column already exists on the board."
|
||||||
},
|
},
|
||||||
"presentation_view": {
|
"presentation_view": {
|
||||||
"edit-slide": "Edit this slide",
|
"edit-slide": "Edit this slide",
|
||||||
@@ -2091,5 +2099,13 @@
|
|||||||
},
|
},
|
||||||
"collections": {
|
"collections": {
|
||||||
"rendering_error": "Unable to show content due to an error."
|
"rendering_error": "Unable to show content due to an error."
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "Clear note color",
|
||||||
|
"set-color": "Set note color",
|
||||||
|
"set-custom-color": "Set custom note color"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "Switch to full editor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -690,7 +690,8 @@
|
|||||||
"convert_into_attachment_failed": "La conversión de nota '{{title}}' falló.",
|
"convert_into_attachment_failed": "La conversión de nota '{{title}}' falló.",
|
||||||
"convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.",
|
"convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.",
|
||||||
"convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?",
|
"convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?",
|
||||||
"print_pdf": "Exportar como PDF..."
|
"print_pdf": "Exportar como PDF...",
|
||||||
|
"open_note_on_server": "Abrir nota en el servidor"
|
||||||
},
|
},
|
||||||
"onclick_button": {
|
"onclick_button": {
|
||||||
"no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido"
|
"no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido"
|
||||||
@@ -734,9 +735,10 @@
|
|||||||
"zoom_out_title": "Alejar"
|
"zoom_out_title": "Alejar"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Vínculo de retroceso",
|
"relation": "relación",
|
||||||
"backlinks": "{{count}} vínculos de retroceso",
|
"backlink_one": "{{count}} Vínculo de retroceso",
|
||||||
"relation": "relación"
|
"backlink_many": "",
|
||||||
|
"backlink_other": "{{count}} vínculos de retroceso"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Insertar subnota",
|
"insert_child_note": "Insertar subnota",
|
||||||
@@ -763,7 +765,6 @@
|
|||||||
"grid": "Cuadrícula",
|
"grid": "Cuadrícula",
|
||||||
"list": "Lista",
|
"list": "Lista",
|
||||||
"collapse_all_notes": "Contraer todas las notas",
|
"collapse_all_notes": "Contraer todas las notas",
|
||||||
"expand_all_children": "Ampliar todas las subnotas",
|
|
||||||
"collapse": "Colapsar",
|
"collapse": "Colapsar",
|
||||||
"expand": "Expandir",
|
"expand": "Expandir",
|
||||||
"invalid_view_type": "Tipo de vista inválida '{{type}}'",
|
"invalid_view_type": "Tipo de vista inválida '{{type}}'",
|
||||||
@@ -773,7 +774,11 @@
|
|||||||
"geo-map": "Mapa Geo",
|
"geo-map": "Mapa Geo",
|
||||||
"board": "Tablero",
|
"board": "Tablero",
|
||||||
"include_archived_notes": "Mostrar notas archivadas",
|
"include_archived_notes": "Mostrar notas archivadas",
|
||||||
"presentation": "Presentación"
|
"presentation": "Presentación",
|
||||||
|
"expand_tooltip": "Expande las notas hijas inmediatas de esta colección (un nivel). Para más opciones, pulsa la flecha a la derecha.",
|
||||||
|
"expand_first_level": "Expandir hijos inmediatos",
|
||||||
|
"expand_nth_level": "Expandir {{depth}} niveles",
|
||||||
|
"expand_all_levels": "Expandir todos los niveles"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "Aún no hay notas editadas en este día...",
|
"no_edited_notes_found": "Aún no hay notas editadas en este día...",
|
||||||
@@ -836,7 +841,8 @@
|
|||||||
"search": "Buscar"
|
"search": "Buscar"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Esta nota fue tomada originalmente de:"
|
"this_note_was_originally_taken_from": "Esta nota fue tomada originalmente de:",
|
||||||
|
"info": "Información"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Atributos propios"
|
"owned_attributes": "Atributos propios"
|
||||||
@@ -1257,12 +1263,7 @@
|
|||||||
"indexing_stopped": "Indexado detenido",
|
"indexing_stopped": "Indexado detenido",
|
||||||
"indexing_in_progress": "Indexado en progreso...",
|
"indexing_in_progress": "Indexado en progreso...",
|
||||||
"last_indexed": "Último indexado",
|
"last_indexed": "Último indexado",
|
||||||
"n_notes_queued_0": "{{ count }} nota agregada a la cola para indexar",
|
|
||||||
"n_notes_queued_1": "{{ count }} notas agregadas a la cola para indexar",
|
|
||||||
"n_notes_queued_2": "",
|
|
||||||
"note_chat": "Chat de nota",
|
"note_chat": "Chat de nota",
|
||||||
"notes_indexed": "{{ count }} nota indexada",
|
|
||||||
"notes_indexed_plural": "{{ count }} notas indexadas",
|
|
||||||
"sources": "Fuentes",
|
"sources": "Fuentes",
|
||||||
"start_indexing": "Comenzar indexado",
|
"start_indexing": "Comenzar indexado",
|
||||||
"use_advanced_context": "Usar contexto avanzado",
|
"use_advanced_context": "Usar contexto avanzado",
|
||||||
@@ -1300,7 +1301,10 @@
|
|||||||
"title": "Editor"
|
"title": "Editor"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "Tipos MIME disponibles en el menú desplegable"
|
"title": "Tipos MIME disponibles en el menú desplegable",
|
||||||
|
"tooltip_syntax_highlighting": "Resaltado de sintaxis",
|
||||||
|
"tooltip_code_block_syntax": "Bloques de código en notas de texto",
|
||||||
|
"tooltip_code_note_syntax": "Notas de código"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Atajos de teclas de Vim",
|
"use_vim_keybindings_in_code_notes": "Atajos de teclas de Vim",
|
||||||
@@ -1850,7 +1854,8 @@
|
|||||||
"copy-link": "Copiar enlace",
|
"copy-link": "Copiar enlace",
|
||||||
"paste": "Pegar",
|
"paste": "Pegar",
|
||||||
"paste-as-plain-text": "Pegar como texto plano",
|
"paste-as-plain-text": "Pegar como texto plano",
|
||||||
"search_online": "Buscar \"{{term}}\" con {{searchEngine}}"
|
"search_online": "Buscar \"{{term}}\" con {{searchEngine}}",
|
||||||
|
"search_in_trilium": "Buscar \"{{term}}\" en Trilium"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "Copiar referencia al portapapeles",
|
"copy_reference_to_clipboard": "Copiar referencia al portapapeles",
|
||||||
@@ -1991,7 +1996,8 @@
|
|||||||
"add-column-placeholder": "Ingresar título de la columna...",
|
"add-column-placeholder": "Ingresar título de la columna...",
|
||||||
"edit-note-title": "Haga clic para editar el título de la nota",
|
"edit-note-title": "Haga clic para editar el título de la nota",
|
||||||
"edit-column-title": "Haga clic para editar el título de la columna",
|
"edit-column-title": "Haga clic para editar el título de la columna",
|
||||||
"remove-from-board": "Eliminar del tablero"
|
"remove-from-board": "Eliminar del tablero",
|
||||||
|
"column-already-exists": "Esta columna ya existe en el tablero."
|
||||||
},
|
},
|
||||||
"content_renderer": {
|
"content_renderer": {
|
||||||
"open_externally": "Abrir externamente"
|
"open_externally": "Abrir externamente"
|
||||||
@@ -2086,10 +2092,14 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "Actualmente, está viendo una nota de solo lectura.",
|
"read-only-note": "Actualmente, está viendo una nota de solo lectura.",
|
||||||
"auto-read-only-note": "Esta nota se muestra en modo de solo lectura para una carga más rápida.",
|
"auto-read-only-note": "Esta nota se muestra en modo de solo lectura para una carga más rápida.",
|
||||||
"auto-read-only-learn-more": "Para saber más",
|
|
||||||
"edit-note": "Editar nota"
|
"edit-note": "Editar nota"
|
||||||
},
|
},
|
||||||
"calendar_view": {
|
"calendar_view": {
|
||||||
"delete_note": "Eliminar nota..."
|
"delete_note": "Eliminar nota..."
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "Borrar color de nota",
|
||||||
|
"set-color": "Asignar color de nota",
|
||||||
|
"set-custom-color": "Asignar color de nota personalizado"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,10 @@
|
|||||||
"help_on_tree_prefix": "Aide sur le préfixe de l'arbre",
|
"help_on_tree_prefix": "Aide sur le préfixe de l'arbre",
|
||||||
"prefix": "Préfixe : ",
|
"prefix": "Préfixe : ",
|
||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"branch_prefix_saved": "Le préfixe de la branche a été enregistré."
|
"branch_prefix_saved": "Le préfixe de la branche a été enregistré.",
|
||||||
|
"edit_branch_prefix_multiple": "Modifier le préfixe de branche pour {{count}} branches",
|
||||||
|
"branch_prefix_saved_multiple": "Le préfixe de la branche a été sauvegardé pour {{count}} branches.",
|
||||||
|
"affected_branches": "Branches impactées ({{count}}):"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "Actions groupées",
|
"bulk_actions": "Actions groupées",
|
||||||
@@ -730,9 +733,10 @@
|
|||||||
"zoom_out_title": "Zoom arrière"
|
"zoom_out_title": "Zoom arrière"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Lien inverse",
|
"relation": "relation",
|
||||||
"backlinks": "{{count}} Liens inverses",
|
"backlink_one": "{{count}} Lien inverse",
|
||||||
"relation": "relation"
|
"backlink_many": "",
|
||||||
|
"backlink_other": "{{count}} Liens inverses"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Insérer une note enfant",
|
"insert_child_note": "Insérer une note enfant",
|
||||||
@@ -759,7 +763,6 @@
|
|||||||
"grid": "Grille",
|
"grid": "Grille",
|
||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
"collapse_all_notes": "Réduire toutes les notes",
|
"collapse_all_notes": "Réduire toutes les notes",
|
||||||
"expand_all_children": "Développer tous les enfants",
|
|
||||||
"collapse": "Réduire",
|
"collapse": "Réduire",
|
||||||
"expand": "Développer",
|
"expand": "Développer",
|
||||||
"invalid_view_type": "Type de vue non valide '{{type}}'",
|
"invalid_view_type": "Type de vue non valide '{{type}}'",
|
||||||
@@ -832,7 +835,8 @@
|
|||||||
"search": "Recherche"
|
"search": "Recherche"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Cette note est initialement extraite de :"
|
"this_note_was_originally_taken_from": "Cette note est initialement extraite de :",
|
||||||
|
"info": "Infos"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Attributs propres"
|
"owned_attributes": "Attributs propres"
|
||||||
@@ -1763,12 +1767,6 @@
|
|||||||
"not_started": "Non démarré",
|
"not_started": "Non démarré",
|
||||||
"title": "Paramètres IA",
|
"title": "Paramètres IA",
|
||||||
"processed_notes": "Notes traitées",
|
"processed_notes": "Notes traitées",
|
||||||
"n_notes_queued_0": "{{ count }} note en attente d’indexation",
|
|
||||||
"n_notes_queued_1": "{{ count }} notes en attente d’indexation",
|
|
||||||
"n_notes_queued_2": "",
|
|
||||||
"notes_indexed_0": "{{ count }} note indexée",
|
|
||||||
"notes_indexed_1": "{{ count }} notes indexées",
|
|
||||||
"notes_indexed_2": "",
|
|
||||||
"anthropic_url_description": "URL de base pour l'API Anthropic (par défaut : https ://api.anthropic.com)",
|
"anthropic_url_description": "URL de base pour l'API Anthropic (par défaut : https ://api.anthropic.com)",
|
||||||
"anthropic_model_description": "Modèles Anthropic Claude pour la complétion",
|
"anthropic_model_description": "Modèles Anthropic Claude pour la complétion",
|
||||||
"voyage_settings": "Réglages d'IA Voyage",
|
"voyage_settings": "Réglages d'IA Voyage",
|
||||||
|
|||||||
@@ -324,7 +324,8 @@
|
|||||||
"copy-link": "Copia collegamento",
|
"copy-link": "Copia collegamento",
|
||||||
"paste-as-plain-text": "Incolla come testo semplice",
|
"paste-as-plain-text": "Incolla come testo semplice",
|
||||||
"add-term-to-dictionary": "Aggiungi \"{{term}}\" al dizionario",
|
"add-term-to-dictionary": "Aggiungi \"{{term}}\" al dizionario",
|
||||||
"search_online": "Cerca \"{{term}}\" con {{searchEngine}}"
|
"search_online": "Cerca \"{{term}}\" con {{searchEngine}}",
|
||||||
|
"search_in_trilium": "Cerca \"{{term}}\" in Trilium"
|
||||||
},
|
},
|
||||||
"editing": {
|
"editing": {
|
||||||
"editor_type": {
|
"editor_type": {
|
||||||
@@ -428,7 +429,8 @@
|
|||||||
"add-column": "Aggiungi colonna",
|
"add-column": "Aggiungi colonna",
|
||||||
"add-column-placeholder": "Inserisci il nome della colonna...",
|
"add-column-placeholder": "Inserisci il nome della colonna...",
|
||||||
"edit-note-title": "Fare clic per modificare il titolo della nota",
|
"edit-note-title": "Fare clic per modificare il titolo della nota",
|
||||||
"edit-column-title": "Fare clic per modificare il titolo della colonna"
|
"edit-column-title": "Fare clic per modificare il titolo della colonna",
|
||||||
|
"column-already-exists": "Questa colonna esiste già nella bacheca."
|
||||||
},
|
},
|
||||||
"backup": {
|
"backup": {
|
||||||
"enable_weekly_backup": "Abilita le archiviazioni settimanali",
|
"enable_weekly_backup": "Abilita le archiviazioni settimanali",
|
||||||
@@ -613,7 +615,8 @@
|
|||||||
"showSQLConsole": "mostra console SQL",
|
"showSQLConsole": "mostra console SQL",
|
||||||
"other": "Altro",
|
"other": "Altro",
|
||||||
"quickSearch": "concentrati sull'input della ricerca rapida",
|
"quickSearch": "concentrati sull'input della ricerca rapida",
|
||||||
"inPageSearch": "ricerca all'interno della pagina"
|
"inPageSearch": "ricerca all'interno della pagina",
|
||||||
|
"editShortcuts": "Modifica scorciatoie da tastiera"
|
||||||
},
|
},
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"saturday": "Sabato",
|
"saturday": "Sabato",
|
||||||
@@ -637,12 +640,6 @@
|
|||||||
"friday": "Venerdì"
|
"friday": "Venerdì"
|
||||||
},
|
},
|
||||||
"ai_llm": {
|
"ai_llm": {
|
||||||
"n_notes_queued_0": "{{ count }} nota in coda per l'indicizzazione",
|
|
||||||
"n_notes_queued_1": "{{ count }} note in coda per l'indicizzazione",
|
|
||||||
"n_notes_queued_2": "{{ count }} note in coda per l'indicizzazione",
|
|
||||||
"notes_indexed_0": "{{ count }} nota indicizzata",
|
|
||||||
"notes_indexed_1": "{{ count }} note indicizzate",
|
|
||||||
"notes_indexed_2": "{{ count }} note indicizzate",
|
|
||||||
"not_started": "Non iniziato",
|
"not_started": "Non iniziato",
|
||||||
"title": "Impostazioni AI",
|
"title": "Impostazioni AI",
|
||||||
"processed_notes": "Note elaborate",
|
"processed_notes": "Note elaborate",
|
||||||
@@ -1262,7 +1259,8 @@
|
|||||||
"convert_into_attachment_failed": "Conversione della nota '{{title}}' fallita.",
|
"convert_into_attachment_failed": "Conversione della nota '{{title}}' fallita.",
|
||||||
"convert_into_attachment_successful": "Nota '{{title}}' è stato convertito in allegato.",
|
"convert_into_attachment_successful": "Nota '{{title}}' è stato convertito in allegato.",
|
||||||
"convert_into_attachment_prompt": "Sei sicuro di voler convertire la nota '{{title}}' in un allegato della nota padre?",
|
"convert_into_attachment_prompt": "Sei sicuro di voler convertire la nota '{{title}}' in un allegato della nota padre?",
|
||||||
"print_pdf": "Esporta come PDF..."
|
"print_pdf": "Esporta come PDF...",
|
||||||
|
"open_note_on_server": "Apri una nota sul server"
|
||||||
},
|
},
|
||||||
"onclick_button": {
|
"onclick_button": {
|
||||||
"no_click_handler": "Il widget pulsante '{{componentId}}' non ha un gestore di clic definito"
|
"no_click_handler": "Il widget pulsante '{{componentId}}' non ha un gestore di clic definito"
|
||||||
@@ -1306,9 +1304,10 @@
|
|||||||
"zoom_out_title": "Rimpicciolisci"
|
"zoom_out_title": "Rimpicciolisci"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Backlink",
|
"relation": "relazione",
|
||||||
"backlinks": "{{count}} Backlinks",
|
"backlink_one": "{{count}} Backlink",
|
||||||
"relation": "relazione"
|
"backlink_many": "{{count}} Backlinks",
|
||||||
|
"backlink_other": "{{count}} Backlinks"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Inserisci nota secondaria",
|
"insert_child_note": "Inserisci nota secondaria",
|
||||||
@@ -1335,7 +1334,6 @@
|
|||||||
"grid": "Griglia",
|
"grid": "Griglia",
|
||||||
"list": "Lista",
|
"list": "Lista",
|
||||||
"collapse_all_notes": "Comprimi tutte le note",
|
"collapse_all_notes": "Comprimi tutte le note",
|
||||||
"expand_all_children": "Espandi tutti i bambini",
|
|
||||||
"collapse": "Crollo",
|
"collapse": "Crollo",
|
||||||
"expand": "Espandere",
|
"expand": "Espandere",
|
||||||
"book_properties": "Proprietà della raccolta",
|
"book_properties": "Proprietà della raccolta",
|
||||||
@@ -1345,7 +1343,11 @@
|
|||||||
"geo-map": "Mappa geografica",
|
"geo-map": "Mappa geografica",
|
||||||
"board": "Asse",
|
"board": "Asse",
|
||||||
"presentation": "Presentazione",
|
"presentation": "Presentazione",
|
||||||
"include_archived_notes": "Mostra note archiviate"
|
"include_archived_notes": "Mostra note archiviate",
|
||||||
|
"expand_tooltip": "Espande i figli diretti di questa raccolta (a un livello di profondità). Per ulteriori opzioni, premere la freccia a destra.",
|
||||||
|
"expand_first_level": "Espandi figli diretti",
|
||||||
|
"expand_nth_level": "Espandi {{depth}} livelli",
|
||||||
|
"expand_all_levels": "Espandi tutti i livelli"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "Nessuna nota modificata per questo giorno...",
|
"no_edited_notes_found": "Nessuna nota modificata per questo giorno...",
|
||||||
@@ -1408,7 +1410,8 @@
|
|||||||
"search": "Ricerca"
|
"search": "Ricerca"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Questa nota è stata originariamente tratta da:"
|
"this_note_was_originally_taken_from": "Questa nota è stata originariamente tratta da:",
|
||||||
|
"info": "Informazioni"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Attributi posseduti"
|
"owned_attributes": "Attributi posseduti"
|
||||||
@@ -1539,9 +1542,9 @@
|
|||||||
"create_label": "Per iniziare, crea un'etichetta con l'indirizzo URL che desideri incorporare, ad esempio #webViewSrc=\"https://www.google.com\""
|
"create_label": "Per iniziare, crea un'etichetta con l'indirizzo URL che desideri incorporare, ad esempio #webViewSrc=\"https://www.google.com\""
|
||||||
},
|
},
|
||||||
"vacuum_database": {
|
"vacuum_database": {
|
||||||
"title": "Database del vuoto",
|
"title": "Pulizia del database",
|
||||||
"description": "Questa operazione ricostruirà il database, generando in genere un file di dimensioni inferiori. In realtà, nessun dato verrà modificato.",
|
"description": "Questa operazione ricostruirà il database, generando in genere un file di dimensioni inferiori. In realtà, nessun dato verrà modificato.",
|
||||||
"button_text": "Database del vuoto",
|
"button_text": "Pulizia del database",
|
||||||
"vacuuming_database": "Aspirazione del database...",
|
"vacuuming_database": "Aspirazione del database...",
|
||||||
"database_vacuumed": "Il database è stato svuotato"
|
"database_vacuumed": "Il database è stato svuotato"
|
||||||
},
|
},
|
||||||
@@ -1623,7 +1626,10 @@
|
|||||||
"title": "Redattore"
|
"title": "Redattore"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "Tipi MIME disponibili nel menu a discesa"
|
"title": "Tipi MIME disponibili nel menu a discesa",
|
||||||
|
"tooltip_syntax_highlighting": "Evidenziazione della sintassi",
|
||||||
|
"tooltip_code_block_syntax": "Blocchi di codice nelle note di testo",
|
||||||
|
"tooltip_code_note_syntax": "Note sul codice"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Combinazioni di tasti di Vim",
|
"use_vim_keybindings_in_code_notes": "Combinazioni di tasti di Vim",
|
||||||
@@ -1795,8 +1801,8 @@
|
|||||||
"relation-map": "Mappa delle relazioni",
|
"relation-map": "Mappa delle relazioni",
|
||||||
"note-map": "Nota Mappa",
|
"note-map": "Nota Mappa",
|
||||||
"render-note": "Nota di rendering",
|
"render-note": "Nota di rendering",
|
||||||
"book": "Collezione",
|
"book": "Raccolta",
|
||||||
"mermaid-diagram": "Diagramma della sirena",
|
"mermaid-diagram": "Diagramma Mermaid",
|
||||||
"canvas": "Tela",
|
"canvas": "Tela",
|
||||||
"web-view": "Visualizzazione Web",
|
"web-view": "Visualizzazione Web",
|
||||||
"mind-map": "Mappa mentale",
|
"mind-map": "Mappa mentale",
|
||||||
@@ -1847,7 +1853,8 @@
|
|||||||
"refresh-saved-search-results": "Aggiorna i risultati della ricerca salvati",
|
"refresh-saved-search-results": "Aggiorna i risultati della ricerca salvati",
|
||||||
"create-child-note": "Crea nota figlio",
|
"create-child-note": "Crea nota figlio",
|
||||||
"unhoist": "Sganciare",
|
"unhoist": "Sganciare",
|
||||||
"toggle-sidebar": "Attiva/disattiva la barra laterale"
|
"toggle-sidebar": "Attiva/disattiva la barra laterale",
|
||||||
|
"dropping-not-allowed": "Non è consentito lasciare appunti in questa posizione."
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "Mantieni la finestra in primo piano"
|
"window-on-top": "Mantieni la finestra in primo piano"
|
||||||
@@ -1931,7 +1938,8 @@
|
|||||||
"duplicate-launcher": "Duplica il launcher <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "Duplica il launcher <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "Rilevato automaticamente"
|
"auto-detect-language": "Rilevato automaticamente",
|
||||||
|
"keeps-crashing": "Il componente di modifica continua a bloccarsi. Prova a riavviare Trilium. Se il problema persiste, valuta la possibilità di creare una segnalazione di bug."
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "Blocchi di codice",
|
"title": "Blocchi di codice",
|
||||||
@@ -1959,7 +1967,8 @@
|
|||||||
"open_note_in_new_tab": "Apri la nota in una nuova scheda",
|
"open_note_in_new_tab": "Apri la nota in una nuova scheda",
|
||||||
"open_note_in_new_split": "Apri nota in una nuova divisione",
|
"open_note_in_new_split": "Apri nota in una nuova divisione",
|
||||||
"open_note_in_new_window": "Apri la nota in una nuova finestra",
|
"open_note_in_new_window": "Apri la nota in una nuova finestra",
|
||||||
"open_note_in_popup": "Modifica rapida"
|
"open_note_in_popup": "Modifica rapida",
|
||||||
|
"open_note_in_other_split": "Apri nota nell'altra divisione"
|
||||||
},
|
},
|
||||||
"help-button": {
|
"help-button": {
|
||||||
"title": "Apri la pagina di aiuto pertinente"
|
"title": "Apri la pagina di aiuto pertinente"
|
||||||
@@ -2087,10 +2096,17 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "Stai visualizzando una nota di sola lettura.",
|
"read-only-note": "Stai visualizzando una nota di sola lettura.",
|
||||||
"auto-read-only-note": "Questa nota viene visualizzata in modalità di sola lettura per un caricamento più rapido.",
|
"auto-read-only-note": "Questa nota viene visualizzata in modalità di sola lettura per un caricamento più rapido.",
|
||||||
"auto-read-only-learn-more": "Per saperne di più",
|
|
||||||
"edit-note": "Modifica nota"
|
"edit-note": "Modifica nota"
|
||||||
},
|
},
|
||||||
"calendar_view": {
|
"calendar_view": {
|
||||||
"delete_note": "Eliminazione nota..."
|
"delete_note": "Eliminazione nota..."
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"set-color": "Imposta colore nota",
|
||||||
|
"set-custom-color": "Imposta colore personalizzato per le note",
|
||||||
|
"clear-color": "Pulisci colore della nota"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "Passa all'editor completo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,7 +312,8 @@
|
|||||||
"moveNoteUpDown": "ノートリストでノートを上/下に移動",
|
"moveNoteUpDown": "ノートリストでノートを上/下に移動",
|
||||||
"notSet": "未設定",
|
"notSet": "未設定",
|
||||||
"goUpDown": "ノートのリストで上下する",
|
"goUpDown": "ノートのリストで上下する",
|
||||||
"editBranchPrefix": "アクティブノートのクローンの <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">プレフィックス</a> を編集する"
|
"editBranchPrefix": "アクティブノートのクローンの <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">プレフィックス</a> を編集する",
|
||||||
|
"editShortcuts": "キーボードショートカットを編集"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"importIntoNote": "ノートにインポート",
|
"importIntoNote": "ノートにインポート",
|
||||||
@@ -496,7 +497,8 @@
|
|||||||
"new-item-placeholder": "ノートのタイトルを入力...",
|
"new-item-placeholder": "ノートのタイトルを入力...",
|
||||||
"add-column-placeholder": "列名を入力...",
|
"add-column-placeholder": "列名を入力...",
|
||||||
"edit-note-title": "クリックしてノートのタイトルを編集",
|
"edit-note-title": "クリックしてノートのタイトルを編集",
|
||||||
"edit-column-title": "クリックして列のタイトルを編集"
|
"edit-column-title": "クリックして列のタイトルを編集",
|
||||||
|
"column-already-exists": "この列は既にボード上に存在します。"
|
||||||
},
|
},
|
||||||
"code_buttons": {
|
"code_buttons": {
|
||||||
"execute_button_title": "スクリプトを実行",
|
"execute_button_title": "スクリプトを実行",
|
||||||
@@ -530,7 +532,6 @@
|
|||||||
"grid": "グリッド",
|
"grid": "グリッド",
|
||||||
"list": "リスト",
|
"list": "リスト",
|
||||||
"collapse_all_notes": "すべてのノートを折りたたむ",
|
"collapse_all_notes": "すべてのノートを折りたたむ",
|
||||||
"expand_all_children": "すべての子を展開",
|
|
||||||
"collapse": "折りたたむ",
|
"collapse": "折りたたむ",
|
||||||
"expand": "展開",
|
"expand": "展開",
|
||||||
"book_properties": "コレクションプロパティ",
|
"book_properties": "コレクションプロパティ",
|
||||||
@@ -541,7 +542,11 @@
|
|||||||
"geo-map": "ジオマップ",
|
"geo-map": "ジオマップ",
|
||||||
"board": "ボード",
|
"board": "ボード",
|
||||||
"include_archived_notes": "アーカイブされたノートを表示",
|
"include_archived_notes": "アーカイブされたノートを表示",
|
||||||
"presentation": "プレゼンテーション"
|
"presentation": "プレゼンテーション",
|
||||||
|
"expand_tooltip": "このコレクションの直下の子(1階層下)を展開します。その他のオプションについては、右側の矢印を押してください。",
|
||||||
|
"expand_first_level": "直下の子を展開",
|
||||||
|
"expand_nth_level": "{{depth}} 階層下まで展開",
|
||||||
|
"expand_all_levels": "すべての階層を展開"
|
||||||
},
|
},
|
||||||
"note_types": {
|
"note_types": {
|
||||||
"geo-map": "ジオマップ",
|
"geo-map": "ジオマップ",
|
||||||
@@ -688,6 +693,7 @@
|
|||||||
"outside_hoisted": "このパスはホイストされたノートの外側にあるため、ホイストを解除する必要があります。"
|
"outside_hoisted": "このパスはホイストされたノートの外側にあるため、ホイストを解除する必要があります。"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
|
"info": "情報",
|
||||||
"this_note_was_originally_taken_from": "このノートは元々以下から引用したものです:"
|
"this_note_was_originally_taken_from": "このノートは元々以下から引用したものです:"
|
||||||
},
|
},
|
||||||
"similar_notes": {
|
"similar_notes": {
|
||||||
@@ -1152,7 +1158,8 @@
|
|||||||
"open_note_in_popup": "クイック編集",
|
"open_note_in_popup": "クイック編集",
|
||||||
"open_note_in_new_tab": "新しいタブでノートを開く",
|
"open_note_in_new_tab": "新しいタブでノートを開く",
|
||||||
"open_note_in_new_split": "新しく分割してノートを開く",
|
"open_note_in_new_split": "新しく分割してノートを開く",
|
||||||
"open_note_in_new_window": "新しいウィンドウでノートを開く"
|
"open_note_in_new_window": "新しいウィンドウでノートを開く",
|
||||||
|
"open_note_in_other_split": "他の分割画面でノートを開く"
|
||||||
},
|
},
|
||||||
"note_tooltip": {
|
"note_tooltip": {
|
||||||
"quick-edit": "クイック編集",
|
"quick-edit": "クイック編集",
|
||||||
@@ -1207,7 +1214,8 @@
|
|||||||
"unhoist": "ホイスト解除",
|
"unhoist": "ホイスト解除",
|
||||||
"saved-search-note-refreshed": "保存した検索ノートが更新されました。",
|
"saved-search-note-refreshed": "保存した検索ノートが更新されました。",
|
||||||
"refresh-saved-search-results": "保存した検索結果を更新",
|
"refresh-saved-search-results": "保存した検索結果を更新",
|
||||||
"toggle-sidebar": "サイドバーを切り替え"
|
"toggle-sidebar": "サイドバーを切り替え",
|
||||||
|
"dropping-not-allowed": "この場所にノートをドロップすることはできません。"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "一括操作",
|
"bulk_actions": "一括操作",
|
||||||
@@ -1261,7 +1269,8 @@
|
|||||||
"reset_launcher_confirm": "本当に「{{title}}」をリセットしますか? このノート(およびその子ノート)のすべてのデータと設定が失われ、ランチャーは元の場所に戻ります。"
|
"reset_launcher_confirm": "本当に「{{title}}」をリセットしますか? このノート(およびその子ノート)のすべてのデータと設定が失われ、ランチャーは元の場所に戻ります。"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "自動検出"
|
"auto-detect-language": "自動検出",
|
||||||
|
"keeps-crashing": "編集コンポーネントがクラッシュし続けます。Trilium を再起動してください。問題が解決しない場合は、バグレポートの作成をご検討ください。"
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "コードブロック",
|
"title": "コードブロック",
|
||||||
@@ -1299,7 +1308,8 @@
|
|||||||
"copy-link": "リンクをコピー",
|
"copy-link": "リンクをコピー",
|
||||||
"paste": "貼り付け",
|
"paste": "貼り付け",
|
||||||
"paste-as-plain-text": "プレーンテキストで貼り付け",
|
"paste-as-plain-text": "プレーンテキストで貼り付け",
|
||||||
"search_online": "{{searchEngine}} で \"{{term}}\" を検索"
|
"search_online": "{{searchEngine}} で \"{{term}}\" を検索",
|
||||||
|
"search_in_trilium": "Triliumで「{{term}}」を検索"
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
"seconds": "秒",
|
"seconds": "秒",
|
||||||
@@ -1498,9 +1508,7 @@
|
|||||||
"indexing_stopped": "インデックス登録を停止しました",
|
"indexing_stopped": "インデックス登録を停止しました",
|
||||||
"indexing_in_progress": "インデックス登録中です...",
|
"indexing_in_progress": "インデックス登録中です...",
|
||||||
"last_indexed": "最終インデックス作成日時",
|
"last_indexed": "最終インデックス作成日時",
|
||||||
"n_notes_queued_0": "{{ count }} 件のノートがインデックス作成待ちです",
|
|
||||||
"note_chat": "ノートチャット",
|
"note_chat": "ノートチャット",
|
||||||
"notes_indexed_0": "{{ count }} 件のノートをインデックスしました",
|
|
||||||
"sources": "ソース",
|
"sources": "ソース",
|
||||||
"start_indexing": "インデックス作成を開始",
|
"start_indexing": "インデックス作成を開始",
|
||||||
"use_advanced_context": "高度なコンテキストを使用",
|
"use_advanced_context": "高度なコンテキストを使用",
|
||||||
@@ -1581,9 +1589,8 @@
|
|||||||
"this_launcher_doesnt_define_target_note": "このランチャーはターゲットノートを定義していません。"
|
"this_launcher_doesnt_define_target_note": "このランチャーはターゲットノートを定義していません。"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} バックリンク",
|
"relation": "リレーション",
|
||||||
"backlinks": "{{count}} バックリンク",
|
"backlink_other": "{{count}} 個のバックリンク"
|
||||||
"relation": "リレーション"
|
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"delete_this_note": "このノートを削除",
|
"delete_this_note": "このノートを削除",
|
||||||
@@ -1825,7 +1832,10 @@
|
|||||||
"app-restart-required": "(変更を有効にするにはアプリケーションの再起動が必要です)"
|
"app-restart-required": "(変更を有効にするにはアプリケーションの再起動が必要です)"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "ドロップダウンで利用可能なMIMEタイプ"
|
"title": "ドロップダウンで利用可能なMIMEタイプ",
|
||||||
|
"tooltip_syntax_highlighting": "構文ハイライト表示",
|
||||||
|
"tooltip_code_block_syntax": "テキストノート内のコードブロック",
|
||||||
|
"tooltip_code_note_syntax": "コードノート"
|
||||||
},
|
},
|
||||||
"attachment_erasure_timeout": {
|
"attachment_erasure_timeout": {
|
||||||
"attachment_erasure_timeout": "添付ファイル消去のタイムアウト",
|
"attachment_erasure_timeout": "添付ファイル消去のタイムアウト",
|
||||||
@@ -1928,7 +1938,7 @@
|
|||||||
"search-for": "「{{term}}」を検索",
|
"search-for": "「{{term}}」を検索",
|
||||||
"create-note": "子ノート「{{term}}」を作成してリンクする",
|
"create-note": "子ノート「{{term}}」を作成してリンクする",
|
||||||
"insert-external-link": "「{{term}}」への外部リンクを挿入",
|
"insert-external-link": "「{{term}}」への外部リンクを挿入",
|
||||||
"clear-text-field": "テキストフィールドを消去",
|
"clear-text-field": "テキストフィールドをクリア",
|
||||||
"show-recent-notes": "最近のノートを表示",
|
"show-recent-notes": "最近のノートを表示",
|
||||||
"full-text-search": "全文検索"
|
"full-text-search": "全文検索"
|
||||||
},
|
},
|
||||||
@@ -2087,7 +2097,14 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "現在、読み取り専用のノートを表示しています。",
|
"read-only-note": "現在、読み取り専用のノートを表示しています。",
|
||||||
"auto-read-only-note": "このノートは読み込みを高速化するために読み取り専用モードで表示されています。",
|
"auto-read-only-note": "このノートは読み込みを高速化するために読み取り専用モードで表示されています。",
|
||||||
"auto-read-only-learn-more": "さらに詳しく",
|
|
||||||
"edit-note": "ノートを編集"
|
"edit-note": "ノートを編集"
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "ノートの色をクリア",
|
||||||
|
"set-color": "ノートの色を設定",
|
||||||
|
"set-custom-color": "ノートの色をカスタム設定"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "フルエディターに切り替え"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@
|
|||||||
"edit_branch_prefix": "브랜치 접두사 편집",
|
"edit_branch_prefix": "브랜치 접두사 편집",
|
||||||
"help_on_tree_prefix": "트리 접두사에 대한 도움말",
|
"help_on_tree_prefix": "트리 접두사에 대한 도움말",
|
||||||
"prefix": "접두사: ",
|
"prefix": "접두사: ",
|
||||||
"branch_prefix_saved": "브랜치 접두사가 저장되었습니다."
|
"branch_prefix_saved": "브랜치 접두사가 저장되었습니다.",
|
||||||
|
"edit_branch_prefix_multiple": "{{count}}개의 지점 접두사 편집",
|
||||||
|
"branch_prefix_saved_multiple": "{{count}}개의 지점에 대해 지점 접두사가 저장되었습니다."
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "대량 작업",
|
"bulk_actions": "대량 작업",
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
},
|
},
|
||||||
"widget-error": {
|
"widget-error": {
|
||||||
"title": "Starten widget mislukt",
|
"title": "Starten widget mislukt",
|
||||||
"message-unknown": "Onbekende widget kan niet gestart worden omdat:\n\n{{message}}"
|
"message-unknown": "Onbekende widget kan niet gestart worden omdat:\n\n{{message}}",
|
||||||
|
"message-custom": "Aangepaste widget van notitie met ID \"{{id}}\", getiteld \"{{title}}\" kon niet worden geïnitialiseerd vanwege:\n\n{{message}}"
|
||||||
},
|
},
|
||||||
"bundle-error": {
|
"bundle-error": {
|
||||||
"title": "Custom script laden mislukt"
|
"title": "Custom script laden mislukt",
|
||||||
|
"message": "Script van notitie met ID \"{{id}}\", getiteld \"{{title}}\" kon niet worden uitgevoerd vanwege:\n\n{{message}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"add_link": {
|
"add_link": {
|
||||||
@@ -29,14 +31,17 @@
|
|||||||
"search_note": "zoek voor notitie op naam",
|
"search_note": "zoek voor notitie op naam",
|
||||||
"link_title_mirrors": "De link titel is hetzelfde als de notitie's huidige titel",
|
"link_title_mirrors": "De link titel is hetzelfde als de notitie's huidige titel",
|
||||||
"link_title": "Link titel",
|
"link_title": "Link titel",
|
||||||
"button_add_link": "Link toevoegen"
|
"button_add_link": "Link toevoegen",
|
||||||
|
"link_title_arbitrary": "snelkoppelingsnaam kan willekeurig worden aangepast"
|
||||||
},
|
},
|
||||||
"branch_prefix": {
|
"branch_prefix": {
|
||||||
"edit_branch_prefix": "Bewerk branch prefix",
|
"edit_branch_prefix": "Bewerk branch prefix",
|
||||||
"save": "Opslaan",
|
"save": "Opslaan",
|
||||||
"branch_prefix_saved": "Branch prefix is opgeslagen.",
|
"branch_prefix_saved": "Branch prefix is opgeslagen.",
|
||||||
"help_on_tree_prefix": "Help bij boomvoorvoegsel",
|
"help_on_tree_prefix": "Help bij boomvoorvoegsel",
|
||||||
"prefix": "Voorvoegsel: "
|
"prefix": "Voorvoegsel: ",
|
||||||
|
"edit_branch_prefix_multiple": "Bewerk zijtakvoorvoegsel voor {{count}} zijtakken",
|
||||||
|
"branch_prefix_saved_multiple": "Vertakkingsvoorvoegsel opgeslagen voor {{count}} vertakkingen."
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"bulk_actions": "Bulk acties",
|
"bulk_actions": "Bulk acties",
|
||||||
|
|||||||
@@ -165,7 +165,6 @@
|
|||||||
"view_type": "Typ widoku",
|
"view_type": "Typ widoku",
|
||||||
"grid": "Siatka",
|
"grid": "Siatka",
|
||||||
"collapse_all_notes": "Zwiń wszystkie notatki",
|
"collapse_all_notes": "Zwiń wszystkie notatki",
|
||||||
"expand_all_children": "Rozwiń wszystkie dzieci",
|
|
||||||
"collapse": "Zwiń",
|
"collapse": "Zwiń",
|
||||||
"expand": "Rozwiń",
|
"expand": "Rozwiń",
|
||||||
"book_properties": "Właściwości kolekcji",
|
"book_properties": "Właściwości kolekcji",
|
||||||
@@ -448,7 +447,8 @@
|
|||||||
"search": "Szukaj"
|
"search": "Szukaj"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Ta notatka oryginalnie została wzięta z:"
|
"this_note_was_originally_taken_from": "Ta notatka oryginalnie została wzięta z:",
|
||||||
|
"info": "Info"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Posiadane atrybuty"
|
"owned_attributes": "Posiadane atrybuty"
|
||||||
@@ -750,9 +750,6 @@
|
|||||||
"indexing_stopped": "Indeksowanie zatrzymane",
|
"indexing_stopped": "Indeksowanie zatrzymane",
|
||||||
"indexing_in_progress": "Indeksowanie w trakcie...",
|
"indexing_in_progress": "Indeksowanie w trakcie...",
|
||||||
"last_indexed": "Ostatnio zindeksowane",
|
"last_indexed": "Ostatnio zindeksowane",
|
||||||
"n_notes_queued_0": "{{ count }} notatka zakolejkowana do indeksowania",
|
|
||||||
"n_notes_queued_1": "{{ count }} notatek zakolejkowanych do indeksowania",
|
|
||||||
"n_notes_queued_2": "{{ count }} notatek zakolejkowanych do indeksowania",
|
|
||||||
"note_chat": "Czat notatki",
|
"note_chat": "Czat notatki",
|
||||||
"note_title": "Tytuł notatki",
|
"note_title": "Tytuł notatki",
|
||||||
"error": "Błąd",
|
"error": "Błąd",
|
||||||
@@ -860,9 +857,6 @@
|
|||||||
"enter_message": "Wpisz swoją wiadomość...",
|
"enter_message": "Wpisz swoją wiadomość...",
|
||||||
"error_contacting_provider": "Błąd kontaktu z dostawcą AI. Sprawdź ustawienia i połączenie internetowe.",
|
"error_contacting_provider": "Błąd kontaktu z dostawcą AI. Sprawdź ustawienia i połączenie internetowe.",
|
||||||
"error_generating_response": "Błąd generowania odpowiedzi AI",
|
"error_generating_response": "Błąd generowania odpowiedzi AI",
|
||||||
"notes_indexed_0": "{{ count }} notatka zaindeksowana",
|
|
||||||
"notes_indexed_1": "{{ count }} notatek zaindeksowanych",
|
|
||||||
"notes_indexed_2": "",
|
|
||||||
"sources": "Źródła",
|
"sources": "Źródła",
|
||||||
"start_indexing": "Rozpocznij indeksowanie",
|
"start_indexing": "Rozpocznij indeksowanie",
|
||||||
"use_advanced_context": "Użyj zaawansowanego kontekstu",
|
"use_advanced_context": "Użyj zaawansowanego kontekstu",
|
||||||
@@ -1237,9 +1231,10 @@
|
|||||||
"zoom_out_title": "Pomniejsz"
|
"zoom_out_title": "Pomniejsz"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Backlink",
|
"relation": "relacja",
|
||||||
"backlinks": "{{count}} Backlinków",
|
"backlink_one": "{{count}} Backlink",
|
||||||
"relation": "relacja"
|
"backlink_few": "",
|
||||||
|
"backlink_many": "{{count}} Backlinków"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Wstaw notatkę podrzędną",
|
"insert_child_note": "Wstaw notatkę podrzędną",
|
||||||
|
|||||||
@@ -711,9 +711,10 @@
|
|||||||
"zoom_out_title": "Reduzir"
|
"zoom_out_title": "Reduzir"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Ligação Reversa",
|
"relation": "relação",
|
||||||
"backlinks": "{{count}} Ligações Reversas",
|
"backlink_one": "{{count}} Ligação Reversa",
|
||||||
"relation": "relação"
|
"backlink_many": "",
|
||||||
|
"backlink_other": "{{count}} Ligações Reversas"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Inserir nota filha",
|
"insert_child_note": "Inserir nota filha",
|
||||||
@@ -739,7 +740,6 @@
|
|||||||
"grid": "Grade",
|
"grid": "Grade",
|
||||||
"list": "Lista",
|
"list": "Lista",
|
||||||
"collapse_all_notes": "Recolher todas as notas",
|
"collapse_all_notes": "Recolher todas as notas",
|
||||||
"expand_all_children": "Expandir todos os filhos",
|
|
||||||
"collapse": "Recolher",
|
"collapse": "Recolher",
|
||||||
"expand": "Expandir",
|
"expand": "Expandir",
|
||||||
"book_properties": "Propriedades da Coleção",
|
"book_properties": "Propriedades da Coleção",
|
||||||
@@ -810,7 +810,8 @@
|
|||||||
"search": "Pesquisar"
|
"search": "Pesquisar"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:"
|
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:",
|
||||||
|
"info": "Informações"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Atributos próprios"
|
"owned_attributes": "Atributos próprios"
|
||||||
@@ -1234,13 +1235,7 @@
|
|||||||
"indexing_stopped": "Indexação interrompida",
|
"indexing_stopped": "Indexação interrompida",
|
||||||
"indexing_in_progress": "Indexação em andamento…",
|
"indexing_in_progress": "Indexação em andamento…",
|
||||||
"last_indexed": "Última Indexada",
|
"last_indexed": "Última Indexada",
|
||||||
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
|
|
||||||
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
|
|
||||||
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
|
|
||||||
"note_chat": "Conversa de Nota",
|
"note_chat": "Conversa de Nota",
|
||||||
"notes_indexed_0": "{{ count }} nota indexada",
|
|
||||||
"notes_indexed_1": "{{ count }} notas indexadas",
|
|
||||||
"notes_indexed_2": "{{ count }} notas indexadas",
|
|
||||||
"sources": "Origens",
|
"sources": "Origens",
|
||||||
"start_indexing": "Iniciar Indexação",
|
"start_indexing": "Iniciar Indexação",
|
||||||
"use_advanced_context": "Usar Contexto Avançado",
|
"use_advanced_context": "Usar Contexto Avançado",
|
||||||
|
|||||||
@@ -75,12 +75,6 @@
|
|||||||
"note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\""
|
"note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\""
|
||||||
},
|
},
|
||||||
"ai_llm": {
|
"ai_llm": {
|
||||||
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
|
|
||||||
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
|
|
||||||
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
|
|
||||||
"notes_indexed_0": "{{ count }} nota indexada",
|
|
||||||
"notes_indexed_1": "{{ count }} notas indexadas",
|
|
||||||
"notes_indexed_2": "{{ count }} notas indexadas",
|
|
||||||
"temperature": "Temperatura",
|
"temperature": "Temperatura",
|
||||||
"retry_queued": "Nota enfileirada para nova tentativa",
|
"retry_queued": "Nota enfileirada para nova tentativa",
|
||||||
"queued_notes": "Notas Enfileiradas",
|
"queued_notes": "Notas Enfileiradas",
|
||||||
@@ -976,9 +970,10 @@
|
|||||||
"reset_pan_zoom_title": "Redefinir pan & zoom para coordenadas e ampliação iniciais"
|
"reset_pan_zoom_title": "Redefinir pan & zoom para coordenadas e ampliação iniciais"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Links Reversos",
|
"relation": "relação",
|
||||||
"backlinks": "{{count}} Links Reversos",
|
"backlink_one": "",
|
||||||
"relation": "relação"
|
"backlink_many": "",
|
||||||
|
"backlink_other": "{{count}} Links Reversos"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Inserir nota filha",
|
"insert_child_note": "Inserir nota filha",
|
||||||
@@ -1004,7 +999,6 @@
|
|||||||
"grid": "Grade",
|
"grid": "Grade",
|
||||||
"list": "Lista",
|
"list": "Lista",
|
||||||
"collapse_all_notes": "Recolher todas as notas",
|
"collapse_all_notes": "Recolher todas as notas",
|
||||||
"expand_all_children": "Expandir todos os filhos",
|
|
||||||
"collapse": "Recolher",
|
"collapse": "Recolher",
|
||||||
"expand": "Expandir",
|
"expand": "Expandir",
|
||||||
"book_properties": "Propriedades da Coleção",
|
"book_properties": "Propriedades da Coleção",
|
||||||
@@ -1076,7 +1070,8 @@
|
|||||||
"outside_hoisted": "Este caminho está fora de uma nota fixada e você teria que desafixar."
|
"outside_hoisted": "Este caminho está fora de uma nota fixada e você teria que desafixar."
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:"
|
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:",
|
||||||
|
"info": "Informações"
|
||||||
},
|
},
|
||||||
"promoted_attributes": {
|
"promoted_attributes": {
|
||||||
"promoted_attributes": "Atributos Promovidos",
|
"promoted_attributes": "Atributos Promovidos",
|
||||||
|
|||||||
@@ -279,7 +279,6 @@
|
|||||||
"collapse": "Minimizează",
|
"collapse": "Minimizează",
|
||||||
"collapse_all_notes": "Minimizează toate notițele",
|
"collapse_all_notes": "Minimizează toate notițele",
|
||||||
"expand": "Expandează",
|
"expand": "Expandează",
|
||||||
"expand_all_children": "Expandează toate subnotițele",
|
|
||||||
"grid": "Grilă",
|
"grid": "Grilă",
|
||||||
"invalid_view_type": "Mod de afișare incorect „{{type}}”",
|
"invalid_view_type": "Mod de afișare incorect „{{type}}”",
|
||||||
"list": "Listă",
|
"list": "Listă",
|
||||||
@@ -290,7 +289,11 @@
|
|||||||
"geo-map": "Hartă geografică",
|
"geo-map": "Hartă geografică",
|
||||||
"board": "Tablă Kanban",
|
"board": "Tablă Kanban",
|
||||||
"include_archived_notes": "Afișează notițele arhivate",
|
"include_archived_notes": "Afișează notițele arhivate",
|
||||||
"presentation": "Prezentare"
|
"presentation": "Prezentare",
|
||||||
|
"expand_tooltip": "Expandează subnotițele directe ale acestei colecții (un singur nivel de adâncime). Pentru mai multe opțiuni, apăsați săgeata din dreapta.",
|
||||||
|
"expand_first_level": "Expandează subnotițele directe",
|
||||||
|
"expand_nth_level": "Expandează pe {{depth}} nivele",
|
||||||
|
"expand_all_levels": "Expandează pe toate nivelele"
|
||||||
},
|
},
|
||||||
"bookmark_switch": {
|
"bookmark_switch": {
|
||||||
"bookmark": "Semn de carte",
|
"bookmark": "Semn de carte",
|
||||||
@@ -302,7 +305,10 @@
|
|||||||
"edit_branch_prefix": "Editează prefixul ramurii",
|
"edit_branch_prefix": "Editează prefixul ramurii",
|
||||||
"help_on_tree_prefix": "Informații despre prefixe de ierarhie",
|
"help_on_tree_prefix": "Informații despre prefixe de ierarhie",
|
||||||
"prefix": "Prefix: ",
|
"prefix": "Prefix: ",
|
||||||
"save": "Salvează"
|
"save": "Salvează",
|
||||||
|
"edit_branch_prefix_multiple": "Editează prefixul pentru {{count}} ramuri",
|
||||||
|
"branch_prefix_saved_multiple": "Prefixul a fost modificat pentru {{count}} ramuri.",
|
||||||
|
"affected_branches": "Ramuri afectate ({{count}}):"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"affected_notes": "Notițe afectate",
|
"affected_notes": "Notițe afectate",
|
||||||
@@ -381,7 +387,10 @@
|
|||||||
"trilium_api_docs_button_title": "Deschide documentația API pentru Trilium"
|
"trilium_api_docs_button_title": "Deschide documentația API pentru Trilium"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "Tipuri MIME disponibile în meniul derulant"
|
"title": "Tipuri MIME disponibile în meniul derulant",
|
||||||
|
"tooltip_syntax_highlighting": "Evidențiere de sintaxă",
|
||||||
|
"tooltip_code_block_syntax": "Blocuri de cod în notițe text",
|
||||||
|
"tooltip_code_note_syntax": "Notițe de tip cod"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"also_delete_note": "Șterge și notița",
|
"also_delete_note": "Șterge și notița",
|
||||||
@@ -537,7 +546,8 @@
|
|||||||
"opml_version_1": "OPML v1.0 - text simplu",
|
"opml_version_1": "OPML v1.0 - text simplu",
|
||||||
"opml_version_2": "OPML v2.0 - permite și HTML",
|
"opml_version_2": "OPML v2.0 - permite și HTML",
|
||||||
"format_html": "HTML - recomandat deoarece păstrează toata formatarea",
|
"format_html": "HTML - recomandat deoarece păstrează toata formatarea",
|
||||||
"format_pdf": "PDF - cu scopul de printare sau partajare."
|
"format_pdf": "PDF - cu scopul de printare sau partajare.",
|
||||||
|
"share-format": "HTML pentru publicare web - folosește aceeași temă pentru notițele partajate, dar se pot publica într-un website static."
|
||||||
},
|
},
|
||||||
"fast_search": {
|
"fast_search": {
|
||||||
"description": "Căutarea rapidă dezactivează căutarea la nivel de conținut al notițelor cu scopul de a îmbunătăți performanța de căutare pentru baze de date mari.",
|
"description": "Căutarea rapidă dezactivează căutarea la nivel de conținut al notițelor cu scopul de a îmbunătăți performanța de căutare pentru baze de date mari.",
|
||||||
@@ -670,7 +680,8 @@
|
|||||||
"tabShortcuts": "Scurtături pentru tab-uri",
|
"tabShortcuts": "Scurtături pentru tab-uri",
|
||||||
"troubleshooting": "Unelte pentru depanare",
|
"troubleshooting": "Unelte pentru depanare",
|
||||||
"newTabWithActivationNoteLink": "pe o legătură către o notiță deschide și activează notița într-un tab nou",
|
"newTabWithActivationNoteLink": "pe o legătură către o notiță deschide și activează notița într-un tab nou",
|
||||||
"title": "Ghid rapid"
|
"title": "Ghid rapid",
|
||||||
|
"editShortcuts": "Editează scurtăturile de la tastatură"
|
||||||
},
|
},
|
||||||
"hide_floating_buttons_button": {
|
"hide_floating_buttons_button": {
|
||||||
"button_title": "Ascunde butoanele"
|
"button_title": "Ascunde butoanele"
|
||||||
@@ -753,7 +764,8 @@
|
|||||||
"placeholder": "Introduceți etichetele HTML, câte unul pe linie",
|
"placeholder": "Introduceți etichetele HTML, câte unul pe linie",
|
||||||
"reset_button": "Resetează la lista implicită",
|
"reset_button": "Resetează la lista implicită",
|
||||||
"title": "Etichete HTML la importare"
|
"title": "Etichete HTML la importare"
|
||||||
}
|
},
|
||||||
|
"importZipRecommendation": "Când importați un fișier ZIP, ierarhia notițelor va reflecta structura subdirectoarelor din arhivă."
|
||||||
},
|
},
|
||||||
"include_archived_notes": {
|
"include_archived_notes": {
|
||||||
"include_archived_notes": "Include notițele arhivate"
|
"include_archived_notes": "Include notițele arhivate"
|
||||||
@@ -799,7 +811,8 @@
|
|||||||
"default_description": "În mod implicit Trilium limitează lățimea conținutului pentru a îmbunătăți lizibilitatea pentru ferestrele maximizate pe ecrane late.",
|
"default_description": "În mod implicit Trilium limitează lățimea conținutului pentru a îmbunătăți lizibilitatea pentru ferestrele maximizate pe ecrane late.",
|
||||||
"max_width_label": "Lungimea maximă a conținutului",
|
"max_width_label": "Lungimea maximă a conținutului",
|
||||||
"max_width_unit": "pixeli",
|
"max_width_unit": "pixeli",
|
||||||
"title": "Lățime conținut"
|
"title": "Lățime conținut",
|
||||||
|
"centerContent": "Centrează conținutul"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"delete_this_note": "Șterge această notiță",
|
"delete_this_note": "Șterge această notiță",
|
||||||
@@ -856,7 +869,8 @@
|
|||||||
"convert_into_attachment_failed": "Nu s-a putut converti notița „{{title}}”.",
|
"convert_into_attachment_failed": "Nu s-a putut converti notița „{{title}}”.",
|
||||||
"convert_into_attachment_successful": "Notița „{{title}}” a fost convertită în atașament.",
|
"convert_into_attachment_successful": "Notița „{{title}}” a fost convertită în atașament.",
|
||||||
"convert_into_attachment_prompt": "Doriți convertirea notiței „{{title}}” într-un atașament al notiței părinte?",
|
"convert_into_attachment_prompt": "Doriți convertirea notiței „{{title}}” într-un atașament al notiței părinte?",
|
||||||
"print_pdf": "Exportare ca PDF..."
|
"print_pdf": "Exportare ca PDF...",
|
||||||
|
"open_note_on_server": "Deschide notița pe server"
|
||||||
},
|
},
|
||||||
"note_erasure_timeout": {
|
"note_erasure_timeout": {
|
||||||
"deleted_notes_erased": "Notițele șterse au fost eliminate permanent.",
|
"deleted_notes_erased": "Notițele șterse au fost eliminate permanent.",
|
||||||
@@ -897,6 +911,7 @@
|
|||||||
"title": "Căile notiței"
|
"title": "Căile notiței"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
|
"info": "Informații",
|
||||||
"this_note_was_originally_taken_from": "Această notiță a fost preluată original de la:"
|
"this_note_was_originally_taken_from": "Această notiță a fost preluată original de la:"
|
||||||
},
|
},
|
||||||
"note_type_chooser": {
|
"note_type_chooser": {
|
||||||
@@ -1245,11 +1260,11 @@
|
|||||||
"timeout_unit": "milisecunde"
|
"timeout_unit": "milisecunde"
|
||||||
},
|
},
|
||||||
"table_of_contents": {
|
"table_of_contents": {
|
||||||
"description": "Tabela de conținut va apărea în notițele de tip text atunci când notița are un număr de titluri mai mare decât cel definit. Acest număr se poate personaliza:",
|
"description": "Cuprinsul va apărea în notițele de tip text atunci când notița are un număr de titluri mai mare decât cel definit. Acest număr se poate personaliza:",
|
||||||
"unit": "titluri",
|
"unit": "titluri",
|
||||||
"disable_info": "De asemenea se poate dezactiva tabela de conținut setând o valoare foarte mare.",
|
"disable_info": "De asemenea se poate dezactiva cuprinsul setând o valoare foarte mare.",
|
||||||
"shortcut_info": "Se poate configura și o scurtatură pentru a comuta rapid vizibilitatea panoului din dreapta (inclusiv tabela de conținut) în Opțiuni -> Scurtături (denumirea „toggleRightPane”).",
|
"shortcut_info": "Se poate configura și o scurtatură pentru a comuta rapid vizibilitatea panoului din dreapta (inclusiv cuprinsul) în Opțiuni -> Scurtături (denumirea „toggleRightPane”).",
|
||||||
"title": "Tabelă de conținut"
|
"title": "Cuprins"
|
||||||
},
|
},
|
||||||
"text_auto_read_only_size": {
|
"text_auto_read_only_size": {
|
||||||
"description": "Marchează pragul în care o notiță de o anumită dimensiune va fi afișată în mod de citire (pentru motive de performanță).",
|
"description": "Marchează pragul în care o notiță de o anumită dimensiune va fi afișată în mod de citire (pentru motive de performanță).",
|
||||||
@@ -1348,8 +1363,9 @@
|
|||||||
"title": "Factorul de zoom (doar pentru versiunea desktop)"
|
"title": "Factorul de zoom (doar pentru versiunea desktop)"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} legături de retur",
|
"backlink_one": "{{count}} legătură de retur",
|
||||||
"backlinks": "{{count}} legături de retur",
|
"backlink_few": "{{count}} legături de retur",
|
||||||
|
"backlink_other": "{{count}} de legături de retur",
|
||||||
"relation": "relație"
|
"relation": "relație"
|
||||||
},
|
},
|
||||||
"svg_export_button": {
|
"svg_export_button": {
|
||||||
@@ -1496,13 +1512,16 @@
|
|||||||
"hoist-this-note-workspace": "Focalizează spațiul de lucru",
|
"hoist-this-note-workspace": "Focalizează spațiul de lucru",
|
||||||
"refresh-saved-search-results": "Reîmprospătează căutarea salvată",
|
"refresh-saved-search-results": "Reîmprospătează căutarea salvată",
|
||||||
"unhoist": "Defocalizează notița",
|
"unhoist": "Defocalizează notița",
|
||||||
"toggle-sidebar": "Comută bara laterală"
|
"toggle-sidebar": "Comută bara laterală",
|
||||||
|
"dropping-not-allowed": "Aici nu este permisă plasarea notițelor."
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "Menține fereastra mereu vizibilă"
|
"window-on-top": "Menține fereastra mereu vizibilă"
|
||||||
},
|
},
|
||||||
"note_detail": {
|
"note_detail": {
|
||||||
"could_not_find_typewidget": "Nu s-a putut găsi widget-ul corespunzător tipului „{{type}}”"
|
"could_not_find_typewidget": "Nu s-a putut găsi widget-ul corespunzător tipului „{{type}}”",
|
||||||
|
"printing": "Imprimare în curs...",
|
||||||
|
"printing_pdf": "Exportare ca PDF în curs..."
|
||||||
},
|
},
|
||||||
"note_title": {
|
"note_title": {
|
||||||
"placeholder": "introduceți titlul notiței aici..."
|
"placeholder": "introduceți titlul notiței aici..."
|
||||||
@@ -1608,7 +1627,8 @@
|
|||||||
"reset": "Resetează"
|
"reset": "Resetează"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "Automat"
|
"auto-detect-language": "Automat",
|
||||||
|
"keeps-crashing": "Componenta de editare se blochează în continuu. Încercați să reporniți Trilium. Dacă problema persistă, luați în considerare să raportați această problemă."
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"color-scheme": "Temă de culori",
|
"color-scheme": "Temă de culori",
|
||||||
@@ -1649,7 +1669,8 @@
|
|||||||
"cut": "Decupează",
|
"cut": "Decupează",
|
||||||
"paste": "Lipește",
|
"paste": "Lipește",
|
||||||
"paste-as-plain-text": "Lipește doar textul",
|
"paste-as-plain-text": "Lipește doar textul",
|
||||||
"search_online": "Caută „{{term}}” cu {{searchEngine}}"
|
"search_online": "Caută „{{term}}” cu {{searchEngine}}",
|
||||||
|
"search_in_trilium": "Caută „{{term}}” în Trilium"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_image_to_clipboard": "Copiază imaginea în clipboard",
|
"copy_image_to_clipboard": "Copiază imaginea în clipboard",
|
||||||
@@ -1659,7 +1680,8 @@
|
|||||||
"open_note_in_new_split": "Deschide notița într-un panou nou",
|
"open_note_in_new_split": "Deschide notița într-un panou nou",
|
||||||
"open_note_in_new_tab": "Deschide notița într-un tab nou",
|
"open_note_in_new_tab": "Deschide notița într-un tab nou",
|
||||||
"open_note_in_new_window": "Deschide notița într-o fereastră nouă",
|
"open_note_in_new_window": "Deschide notița într-o fereastră nouă",
|
||||||
"open_note_in_popup": "Editare rapidă"
|
"open_note_in_popup": "Editare rapidă",
|
||||||
|
"open_note_in_other_split": "Deschide notița în celălalt panou"
|
||||||
},
|
},
|
||||||
"note_autocomplete": {
|
"note_autocomplete": {
|
||||||
"clear-text-field": "Șterge conținutul casetei",
|
"clear-text-field": "Șterge conținutul casetei",
|
||||||
@@ -1870,13 +1892,7 @@
|
|||||||
"indexing_stopped": "Indexarea s-a oprit",
|
"indexing_stopped": "Indexarea s-a oprit",
|
||||||
"indexing_in_progress": "Indexare în curs...",
|
"indexing_in_progress": "Indexare în curs...",
|
||||||
"last_indexed": "Ultima indexare",
|
"last_indexed": "Ultima indexare",
|
||||||
"n_notes_queued_0": "O notiță adăugată în coada de indexare",
|
|
||||||
"n_notes_queued_1": "{{ count }} notițe adăugate în coada de indexare",
|
|
||||||
"n_notes_queued_2": "{{ count }} de notițe adăugate în coada de indexare",
|
|
||||||
"note_chat": "Discuție pe baza notițelor",
|
"note_chat": "Discuție pe baza notițelor",
|
||||||
"notes_indexed_0": "O notiță indexată",
|
|
||||||
"notes_indexed_1": "{{ count }} notițe indexate",
|
|
||||||
"notes_indexed_2": "{{ count }} de notițe indexate",
|
|
||||||
"sources": "Surse",
|
"sources": "Surse",
|
||||||
"start_indexing": "Indexează",
|
"start_indexing": "Indexează",
|
||||||
"use_advanced_context": "Folosește context îmbogățit",
|
"use_advanced_context": "Folosește context îmbogățit",
|
||||||
@@ -2013,7 +2029,8 @@
|
|||||||
"new-item-placeholder": "Introduceți titlul notiței...",
|
"new-item-placeholder": "Introduceți titlul notiței...",
|
||||||
"add-column-placeholder": "Introduceți denumirea coloanei...",
|
"add-column-placeholder": "Introduceți denumirea coloanei...",
|
||||||
"edit-note-title": "Clic pentru a edita titlul notiței",
|
"edit-note-title": "Clic pentru a edita titlul notiței",
|
||||||
"edit-column-title": "Clic pentru a edita titlul coloanei"
|
"edit-column-title": "Clic pentru a edita titlul coloanei",
|
||||||
|
"column-already-exists": "Această coloană deja există."
|
||||||
},
|
},
|
||||||
"command_palette": {
|
"command_palette": {
|
||||||
"tree-action-name": "Listă de notițe: {{name}}",
|
"tree-action-name": "Listă de notițe: {{name}}",
|
||||||
@@ -2075,5 +2092,21 @@
|
|||||||
"edit-slide": "Editați acest slide",
|
"edit-slide": "Editați acest slide",
|
||||||
"start-presentation": "Începeți prezentarea",
|
"start-presentation": "Începeți prezentarea",
|
||||||
"slide-overview": "Afișați o imagine de ansamblu a slide-urilor"
|
"slide-overview": "Afișați o imagine de ansamblu a slide-urilor"
|
||||||
|
},
|
||||||
|
"read-only-info": {
|
||||||
|
"read-only-note": "Vizualizați o notiță în modul doar în citire.",
|
||||||
|
"auto-read-only-note": "Această notiță este afișată în modul doar în citire din motive de performanță.",
|
||||||
|
"edit-note": "Editează notița"
|
||||||
|
},
|
||||||
|
"calendar_view": {
|
||||||
|
"delete_note": "Șterge notița..."
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "Înlăturați culoarea notiței",
|
||||||
|
"set-color": "Setați culoarea notiței",
|
||||||
|
"set-custom-color": "Setați culoare personalizată pentru notiță"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "Comută la editorul principal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -984,9 +984,10 @@
|
|||||||
"new-version-available": "Доступно обновление"
|
"new-version-available": "Доступно обновление"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} ссылки",
|
"relation": "отношение",
|
||||||
"backlinks": "{{count}} ссылок",
|
"backlink_one": "{{count}} ссылки",
|
||||||
"relation": "отношение"
|
"backlink_few": "",
|
||||||
|
"backlink_many": "{{count}} ссылок"
|
||||||
},
|
},
|
||||||
"note_icon": {
|
"note_icon": {
|
||||||
"category": "Категория:",
|
"category": "Категория:",
|
||||||
@@ -1013,7 +1014,6 @@
|
|||||||
"book_properties": "Свойства коллекции",
|
"book_properties": "Свойства коллекции",
|
||||||
"geo-map": "Карта",
|
"geo-map": "Карта",
|
||||||
"invalid_view_type": "Недопустимый тип представления '{{type}}'",
|
"invalid_view_type": "Недопустимый тип представления '{{type}}'",
|
||||||
"expand_all_children": "Развернуть все дочерние элементы",
|
|
||||||
"collapse_all_notes": "Свернуть все заметки",
|
"collapse_all_notes": "Свернуть все заметки",
|
||||||
"include_archived_notes": "Показать заархивированные заметки"
|
"include_archived_notes": "Показать заархивированные заметки"
|
||||||
},
|
},
|
||||||
@@ -1067,6 +1067,7 @@
|
|||||||
"archived": "Архивировано"
|
"archived": "Архивировано"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
|
"info": "Информация",
|
||||||
"this_note_was_originally_taken_from": "Эта заметка была первоначально взята из:"
|
"this_note_was_originally_taken_from": "Эта заметка была первоначально взята из:"
|
||||||
},
|
},
|
||||||
"promoted_attributes": {
|
"promoted_attributes": {
|
||||||
@@ -1334,16 +1335,10 @@
|
|||||||
"error_fetching": "Ошибка получения списка моделей: {{error}}",
|
"error_fetching": "Ошибка получения списка моделей: {{error}}",
|
||||||
"index_rebuild_status_error": "Ошибка проверки статуса перестроения индекса",
|
"index_rebuild_status_error": "Ошибка проверки статуса перестроения индекса",
|
||||||
"enhanced_context_description": "Предоставляет ИИ больше контекста из заметки и связанных с ней заметок для более точных ответов",
|
"enhanced_context_description": "Предоставляет ИИ больше контекста из заметки и связанных с ней заметок для более точных ответов",
|
||||||
"n_notes_queued_0": "{{ count }} заметка в очереди на индексирование",
|
|
||||||
"n_notes_queued_1": "{{ count }} заметки в очереди на индексирование",
|
|
||||||
"n_notes_queued_2": "{{ count }} заметок в очереди на индексирование",
|
|
||||||
"no_models_found_ollama": "Модели Ollama не найдены. Проверьте, запущена ли Ollama.",
|
"no_models_found_ollama": "Модели Ollama не найдены. Проверьте, запущена ли Ollama.",
|
||||||
"no_models_found_online": "Модели не найдены. Проверьте ваш ключ API и настройки.",
|
"no_models_found_online": "Модели не найдены. Проверьте ваш ключ API и настройки.",
|
||||||
"experimental_warning": "Функция LLM в настоящее время является экспериментальной — вы предупреждены.",
|
"experimental_warning": "Функция LLM в настоящее время является экспериментальной — вы предупреждены.",
|
||||||
"ollama_no_url": "Ollama не настроена. Введите корректный URL-адрес.",
|
"ollama_no_url": "Ollama не настроена. Введите корректный URL-адрес.",
|
||||||
"notes_indexed_0": "{{ count }} заметка проиндексирована",
|
|
||||||
"notes_indexed_1": "{{ count }} заметки проиндексировано",
|
|
||||||
"notes_indexed_2": "{{ count }} заметок проиндексировано",
|
|
||||||
"show_thinking_description": "Показать цепочку мыслительного процесса ИИ",
|
"show_thinking_description": "Показать цепочку мыслительного процесса ИИ",
|
||||||
"api_key_tooltip": "API-ключ для доступа к сервису",
|
"api_key_tooltip": "API-ключ для доступа к сервису",
|
||||||
"all_notes_queued_for_retry": "Все неудачные заметки поставлены в очередь на повторную попытку",
|
"all_notes_queued_for_retry": "Все неудачные заметки поставлены в очередь на повторную попытку",
|
||||||
|
|||||||
@@ -425,14 +425,6 @@
|
|||||||
"print_page_size": "Prilikom izvoza u PDF, menja veličinu stranice. Podržane vrednosti: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.",
|
"print_page_size": "Prilikom izvoza u PDF, menja veličinu stranice. Podržane vrednosti: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.",
|
||||||
"color_type": "Boja"
|
"color_type": "Boja"
|
||||||
},
|
},
|
||||||
"ai_llm": {
|
|
||||||
"n_notes_queued_0": "{{ count }} beleška stavljena u red za indeksiranje",
|
|
||||||
"n_notes_queued_1": "{{ count }} beleški stavljeno u red za indeksiranje",
|
|
||||||
"n_notes_queued_2": "{{ count }} beleški stavljeno u red za indeksiranje",
|
|
||||||
"notes_indexed_0": "{{ count }} beleška je indeksirana",
|
|
||||||
"notes_indexed_1": "{{ count }} beleški je indeksirano",
|
|
||||||
"notes_indexed_2": "{{ count }} beleški je indeksirano"
|
|
||||||
},
|
|
||||||
"attribute_editor": {
|
"attribute_editor": {
|
||||||
"help_text_body1": "Da biste dodali oznaku, samo unesite npr. <code>#rock</code> ili ako želite da dodate i vrednost, onda npr. <code>#year = 2020</code>",
|
"help_text_body1": "Da biste dodali oznaku, samo unesite npr. <code>#rock</code> ili ako želite da dodate i vrednost, onda npr. <code>#year = 2020</code>",
|
||||||
"help_text_body2": "Za relaciju, unesite <code>~author = @</code> što bi trebalo da otvori automatsko dovršavanje gde možete potražiti željenu belešku.",
|
"help_text_body2": "Za relaciju, unesite <code>~author = @</code> što bi trebalo da otvori automatsko dovršavanje gde možete potražiti željenu belešku.",
|
||||||
|
|||||||
@@ -5,25 +5,32 @@
|
|||||||
"db_version": "Veritabanı versiyonu:",
|
"db_version": "Veritabanı versiyonu:",
|
||||||
"title": "Trilium Notes Hakkında",
|
"title": "Trilium Notes Hakkında",
|
||||||
"sync_version": "Eşleştirme versiyonu:",
|
"sync_version": "Eşleştirme versiyonu:",
|
||||||
"data_directory": "Veri dizini:"
|
"data_directory": "Veri dizini:",
|
||||||
|
"build_date": "Derleme tarihi:",
|
||||||
|
"build_revision": "Derleme revizyonu:"
|
||||||
},
|
},
|
||||||
"branch_prefix": {
|
"branch_prefix": {
|
||||||
"save": "Kaydet",
|
"save": "Kaydet",
|
||||||
"edit_branch_prefix": "Dalın önekini düzenle",
|
"edit_branch_prefix": "Dalın önekini düzenle",
|
||||||
"prefix": "Önek: ",
|
"prefix": "Önek: ",
|
||||||
"branch_prefix_saved": "Dal öneki kaydedildi."
|
"branch_prefix_saved": "Dal öneki kaydedildi.",
|
||||||
|
"edit_branch_prefix_multiple": "{{count}} dal için dal ön ekini düzenle",
|
||||||
|
"help_on_tree_prefix": "Ağaç ön eki hakkında yardım",
|
||||||
|
"branch_prefix_saved_multiple": "Dal ön eki, {{count}} dal için kaydedildi.",
|
||||||
|
"affected_branches": "Etkilenen dal sayısı ({{count}}):"
|
||||||
},
|
},
|
||||||
"delete_notes": {
|
"delete_notes": {
|
||||||
"close": "Kapat",
|
"close": "Kapat",
|
||||||
"delete_notes_preview": "Not önizlemesini sil",
|
"delete_notes_preview": "Not önizlemesini sil",
|
||||||
"delete_all_clones_description": "Tüm klonları da sil (son değişikliklerden geri alınabilir)"
|
"delete_all_clones_description": "Tüm klonları da sil (son değişikliklerden geri alınabilir)",
|
||||||
|
"erase_notes_description": "Normal (yazılımsal) silme işlemi, notları yalnızca silinmiş olarak işaretler ve belirli bir süre içinde (son değişiklikler iletişim kutusunda) geri alınabilir. Bu seçeneği işaretlemek, notları hemen siler ve notların geri alınması mümkün olmaz."
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"close": "Kapat"
|
"close": "Kapat"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"chooseImportFile": "İçe aktarım dosyası",
|
"chooseImportFile": "İçe aktarım dosyası",
|
||||||
"importDescription": "Seçilen dosya(lar) alt not olarak içe aktarılacaktır"
|
"importDescription": "Seçilen dosya(lar)ın içeriği, alt not(lar) olarak şuraya içe aktarılacaktır"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"closeButton": "Kapat"
|
"closeButton": "Kapat"
|
||||||
@@ -34,21 +41,23 @@
|
|||||||
"toast": {
|
"toast": {
|
||||||
"critical-error": {
|
"critical-error": {
|
||||||
"title": "Kritik hata",
|
"title": "Kritik hata",
|
||||||
"message": "İstemci uygulamasının başlatılmasını engelleyen kritik bir hata meydana geldi\n\n{{message}}\n\nBu muhtemelen bir betiğin beklenmedik şekilde başarısız olmasından kaynaklanıyor. Uygulamayı güvenli modda başlatarak sorunu ele almayı deneyin."
|
"message": "İstemci uygulamasının başlamasını engelleyen kritik bir hata oluştu:\n\n{{message}}\n\nBunun nedeni büyük olasılıkla bir komut dosyasının beklenmedik bir şekilde başarısız olmasıdır. Uygulamayı güvenli modda başlatmayı ve sorunu gidermeyi deneyin."
|
||||||
},
|
},
|
||||||
"widget-error": {
|
"widget-error": {
|
||||||
"title": "Bir widget başlatılamadı",
|
"title": "Bir widget başlatılamadı",
|
||||||
"message-unknown": "Bilinmeyen widget aşağıdaki sebeple başlatılamadı\n\n{{message}}"
|
"message-unknown": "Bilinmeyen bir widget aşağıdaki sebeple başlatılamadı\n\n{{message}}",
|
||||||
|
"message-custom": "ID'si \"{{id}}\" ve başlığı \"{{title}}\" olan nottan alınan özel bileşen şu sebepten başlatılamadı:\n\n{{message}}"
|
||||||
},
|
},
|
||||||
"bundle-error": {
|
"bundle-error": {
|
||||||
"title": "Özel bir betik yüklenemedi"
|
"title": "Özel bir betik yüklenemedi",
|
||||||
|
"message": "ID'si \"{{id}}\" ve başlığı \"{{title}}\" olan nottan alınan komut dosyası şunun nedeniyle yürütülemedi:\n\n{{message}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"add_link": {
|
"add_link": {
|
||||||
"add_link": "Bağlantı ekle",
|
"add_link": "Bağlantı ekle",
|
||||||
"help_on_links": "Bağlantılar konusunda yardım",
|
"help_on_links": "Bağlantılar konusunda yardım",
|
||||||
"note": "Not",
|
"note": "Not",
|
||||||
"search_note": "isimle not ara",
|
"search_note": "notu adına göre ara",
|
||||||
"link_title_mirrors": "bağlantı adı notun şu anki adıyla aynı",
|
"link_title_mirrors": "bağlantı adı notun şu anki adıyla aynı",
|
||||||
"link_title_arbitrary": "bağlantı adı isteğe bağlı olarak değiştirilebilir",
|
"link_title_arbitrary": "bağlantı adı isteğe bağlı olarak değiştirilebilir",
|
||||||
"link_title": "Bağlantı adı",
|
"link_title": "Bağlantı adı",
|
||||||
@@ -85,12 +94,7 @@
|
|||||||
"cancel": "İptal",
|
"cancel": "İptal",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"are_you_sure_remove_note": "\"{{title}}\" notunu ilişki haritasından kaldırmak istediğinize emin misiniz?. ",
|
"are_you_sure_remove_note": "\"{{title}}\" notunu ilişki haritasından kaldırmak istediğinize emin misiniz?. ",
|
||||||
"also_delete_note": "Notu da sil"
|
"also_delete_note": "Notu da sil",
|
||||||
},
|
"if_you_dont_check": "Bunu işaretlemezseniz, not yalnızca ilişki haritasından kaldırılacaktır."
|
||||||
"ai_llm": {
|
|
||||||
"n_notes_queued": "{{ count }} not dizinleme için sıraya alındı",
|
|
||||||
"n_notes_queued_plural": "{{ count }} not dizinleme için sıraya alındı",
|
|
||||||
"notes_indexed": "{{ count }} not dizinlendi",
|
|
||||||
"notes_indexed_plural": "{{ count }} not dizinlendi"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,8 @@
|
|||||||
"inPageSearch": "頁面內搜尋",
|
"inPageSearch": "頁面內搜尋",
|
||||||
"title": "列表",
|
"title": "列表",
|
||||||
"newTabNoteLink": "在新分頁開啟筆記連結",
|
"newTabNoteLink": "在新分頁開啟筆記連結",
|
||||||
"newTabWithActivationNoteLink": "在新分頁開啟並切換至筆記連結"
|
"newTabWithActivationNoteLink": "在新分頁開啟並切換至筆記連結",
|
||||||
|
"editShortcuts": "編輯鍵盤快捷鍵"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"importIntoNote": "匯入至筆記",
|
"importIntoNote": "匯入至筆記",
|
||||||
@@ -732,9 +733,8 @@
|
|||||||
"zoom_out_title": "縮小"
|
"zoom_out_title": "縮小"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} 個反連結",
|
"relation": "關聯",
|
||||||
"backlinks": "{{count}} 個反連結",
|
"backlink_one": "{{count}} 個反連結"
|
||||||
"relation": "關聯"
|
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "插入子筆記",
|
"insert_child_note": "插入子筆記",
|
||||||
@@ -761,7 +761,6 @@
|
|||||||
"grid": "網格",
|
"grid": "網格",
|
||||||
"list": "列表",
|
"list": "列表",
|
||||||
"collapse_all_notes": "收摺所有筆記",
|
"collapse_all_notes": "收摺所有筆記",
|
||||||
"expand_all_children": "展開所有子項",
|
|
||||||
"collapse": "收摺",
|
"collapse": "收摺",
|
||||||
"expand": "展開",
|
"expand": "展開",
|
||||||
"invalid_view_type": "無效的查看類型 '{{type}}'",
|
"invalid_view_type": "無效的查看類型 '{{type}}'",
|
||||||
@@ -771,7 +770,11 @@
|
|||||||
"geo-map": "地理地圖",
|
"geo-map": "地理地圖",
|
||||||
"board": "看板",
|
"board": "看板",
|
||||||
"include_archived_notes": "顯示已封存筆記",
|
"include_archived_notes": "顯示已封存筆記",
|
||||||
"presentation": "簡報"
|
"presentation": "簡報",
|
||||||
|
"expand_tooltip": "展開此集合的直接子級(單層深度)。按下右側箭頭以查看更多選項。",
|
||||||
|
"expand_first_level": "展開直接子級",
|
||||||
|
"expand_nth_level": "展開 {{depth}} 層",
|
||||||
|
"expand_all_levels": "展開所有層級"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "今天還沒有編輯過的筆記...",
|
"no_edited_notes_found": "今天還沒有編輯過的筆記...",
|
||||||
@@ -834,7 +837,8 @@
|
|||||||
"search": "搜尋"
|
"search": "搜尋"
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "筆記來源:"
|
"this_note_was_originally_taken_from": "筆記來源:",
|
||||||
|
"info": "資訊"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "自有屬性"
|
"owned_attributes": "自有屬性"
|
||||||
@@ -1147,7 +1151,10 @@
|
|||||||
"unit": "字元"
|
"unit": "字元"
|
||||||
},
|
},
|
||||||
"code_mime_types": {
|
"code_mime_types": {
|
||||||
"title": "下拉選單可用的 MIME 文件類型"
|
"title": "下拉選單可用的 MIME 文件類型",
|
||||||
|
"tooltip_syntax_highlighting": "語法高亮顯示",
|
||||||
|
"tooltip_code_block_syntax": "文字筆記中的程式碼區塊",
|
||||||
|
"tooltip_code_note_syntax": "程式碼筆記"
|
||||||
},
|
},
|
||||||
"vim_key_bindings": {
|
"vim_key_bindings": {
|
||||||
"use_vim_keybindings_in_code_notes": "Vim 快捷鍵",
|
"use_vim_keybindings_in_code_notes": "Vim 快捷鍵",
|
||||||
@@ -1509,7 +1516,8 @@
|
|||||||
"refresh-saved-search-results": "重新整理儲存的搜尋結果",
|
"refresh-saved-search-results": "重新整理儲存的搜尋結果",
|
||||||
"create-child-note": "建立子筆記",
|
"create-child-note": "建立子筆記",
|
||||||
"unhoist": "取消聚焦",
|
"unhoist": "取消聚焦",
|
||||||
"toggle-sidebar": "切換側邊欄"
|
"toggle-sidebar": "切換側邊欄",
|
||||||
|
"dropping-not-allowed": "不允許移動筆記至此處。"
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "保持此視窗置頂"
|
"window-on-top": "保持此視窗置頂"
|
||||||
@@ -1612,7 +1620,8 @@
|
|||||||
"duplicate-launcher": "複製啟動器 <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "複製啟動器 <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "自動檢測"
|
"auto-detect-language": "自動檢測",
|
||||||
|
"keeps-crashing": "編輯元件持續發生崩潰。請嘗試重新啟動 Trilium。若問題仍存在,請考慮提交錯誤報告。"
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"description": "控制文字筆記程式碼區塊中的語法高亮,程式碼筆記不會受到影響。",
|
"description": "控制文字筆記程式碼區塊中的語法高亮,程式碼筆記不會受到影響。",
|
||||||
@@ -1653,7 +1662,8 @@
|
|||||||
"copy-link": "複製連結",
|
"copy-link": "複製連結",
|
||||||
"paste": "貼上",
|
"paste": "貼上",
|
||||||
"paste-as-plain-text": "以純文字貼上",
|
"paste-as-plain-text": "以純文字貼上",
|
||||||
"search_online": "用 {{searchEngine}} 搜尋 \"{{term}}\""
|
"search_online": "用 {{searchEngine}} 搜尋 \"{{term}}\"",
|
||||||
|
"search_in_trilium": "在 Trilium 中搜尋「{{term}}」"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "複製引用到剪貼簿",
|
"copy_reference_to_clipboard": "複製引用到剪貼簿",
|
||||||
@@ -1787,9 +1797,7 @@
|
|||||||
"indexing_stopped": "已停止索引",
|
"indexing_stopped": "已停止索引",
|
||||||
"indexing_in_progress": "正在進行索引…",
|
"indexing_in_progress": "正在進行索引…",
|
||||||
"last_indexed": "最後索引時間",
|
"last_indexed": "最後索引時間",
|
||||||
"n_notes_queued_0": "{{ count }} 條筆記已加入索引隊列",
|
|
||||||
"note_chat": "筆記聊天",
|
"note_chat": "筆記聊天",
|
||||||
"notes_indexed_0": "已索引 {{ count }} 條筆記",
|
|
||||||
"sources": "來源",
|
"sources": "來源",
|
||||||
"start_indexing": "開始索引",
|
"start_indexing": "開始索引",
|
||||||
"use_advanced_context": "使用進階上下文",
|
"use_advanced_context": "使用進階上下文",
|
||||||
@@ -2018,7 +2026,8 @@
|
|||||||
"new-item-placeholder": "輸入筆記標題…",
|
"new-item-placeholder": "輸入筆記標題…",
|
||||||
"add-column-placeholder": "輸入行名…",
|
"add-column-placeholder": "輸入行名…",
|
||||||
"edit-note-title": "點擊以編輯筆記標題",
|
"edit-note-title": "點擊以編輯筆記標題",
|
||||||
"edit-column-title": "點擊以編輯行標題"
|
"edit-column-title": "點擊以編輯行標題",
|
||||||
|
"column-already-exists": "此列已在看板上。"
|
||||||
},
|
},
|
||||||
"command_palette": {
|
"command_palette": {
|
||||||
"tree-action-name": "樹:{{name}}",
|
"tree-action-name": "樹:{{name}}",
|
||||||
@@ -2087,7 +2096,14 @@
|
|||||||
"read-only-info": {
|
"read-only-info": {
|
||||||
"read-only-note": "目前正在檢視唯讀筆記。",
|
"read-only-note": "目前正在檢視唯讀筆記。",
|
||||||
"auto-read-only-note": "此筆記以唯讀模式顯示以加快載入速度。",
|
"auto-read-only-note": "此筆記以唯讀模式顯示以加快載入速度。",
|
||||||
"auto-read-only-learn-more": "了解更多",
|
|
||||||
"edit-note": "編輯筆記"
|
"edit-note": "編輯筆記"
|
||||||
|
},
|
||||||
|
"note-color": {
|
||||||
|
"clear-color": "清除筆記顏色",
|
||||||
|
"set-color": "設定筆記顏色",
|
||||||
|
"set-custom-color": "設定自訂筆記顏色"
|
||||||
|
},
|
||||||
|
"popup-editor": {
|
||||||
|
"maximize": "切換至完整編輯器"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -839,9 +839,10 @@
|
|||||||
"zoom_out_title": "Зменшити масштаб"
|
"zoom_out_title": "Зменшити масштаб"
|
||||||
},
|
},
|
||||||
"zpetne_odkazy": {
|
"zpetne_odkazy": {
|
||||||
"backlink": "{{count}} Зворотне посилання",
|
"relation": "зв'язок",
|
||||||
"backlinks": "{{count}} Зворотні посилання",
|
"backlink_one": "{{count}} Зворотне посилання",
|
||||||
"relation": "зв'язок"
|
"backlink_few": "{{count}} Зворотні посилання",
|
||||||
|
"backlink_many": "{{count}} Зворотні посилання"
|
||||||
},
|
},
|
||||||
"mobile_detail_menu": {
|
"mobile_detail_menu": {
|
||||||
"insert_child_note": "Вставити дочірню нотатку",
|
"insert_child_note": "Вставити дочірню нотатку",
|
||||||
@@ -867,7 +868,6 @@
|
|||||||
"grid": "Сітка",
|
"grid": "Сітка",
|
||||||
"list": "Список",
|
"list": "Список",
|
||||||
"collapse_all_notes": "Згорнути всі нотатки",
|
"collapse_all_notes": "Згорнути всі нотатки",
|
||||||
"expand_all_children": "Розгорнути всі дочірні",
|
|
||||||
"collapse": "Згорнути",
|
"collapse": "Згорнути",
|
||||||
"expand": "Розгорнути",
|
"expand": "Розгорнути",
|
||||||
"book_properties": "Властивості Колекції",
|
"book_properties": "Властивості Колекції",
|
||||||
@@ -939,7 +939,8 @@
|
|||||||
"outside_hoisted": "Цей шлях знаходиться поза межами закріпленої нотатки і вам доведеться відкріпити."
|
"outside_hoisted": "Цей шлях знаходиться поза межами закріпленої нотатки і вам доведеться відкріпити."
|
||||||
},
|
},
|
||||||
"note_properties": {
|
"note_properties": {
|
||||||
"this_note_was_originally_taken_from": "Цю нотатку було спочатку взято з:"
|
"this_note_was_originally_taken_from": "Цю нотатку було спочатку взято з:",
|
||||||
|
"info": "Інформація"
|
||||||
},
|
},
|
||||||
"owned_attribute_list": {
|
"owned_attribute_list": {
|
||||||
"owned_attributes": "Власні Атрибути"
|
"owned_attributes": "Власні Атрибути"
|
||||||
@@ -1350,13 +1351,7 @@
|
|||||||
"indexing_stopped": "Індексацію зупинено",
|
"indexing_stopped": "Індексацію зупинено",
|
||||||
"indexing_in_progress": "Триває індексація...",
|
"indexing_in_progress": "Триває індексація...",
|
||||||
"last_indexed": "Остання індексація",
|
"last_indexed": "Остання індексація",
|
||||||
"n_notes_queued_0": "{{ count }} нотатка в черзі на індексацію",
|
|
||||||
"n_notes_queued_1": "{{ count }} нотатки в черзі на індексацію",
|
|
||||||
"n_notes_queued_2": "{{ count }} нотаток в черзі на індексацію",
|
|
||||||
"note_chat": "Нотатка Чат",
|
"note_chat": "Нотатка Чат",
|
||||||
"notes_indexed_0": "{{ count }} нотатка індексовано",
|
|
||||||
"notes_indexed_1": "{{ count }} нотатки індексовано",
|
|
||||||
"notes_indexed_2": "{{ count }} нотаток індексовано",
|
|
||||||
"sources": "Джерела",
|
"sources": "Джерела",
|
||||||
"start_indexing": "Почати індексацію",
|
"start_indexing": "Почати індексацію",
|
||||||
"use_advanced_context": "Використовувати розширений контекст",
|
"use_advanced_context": "Використовувати розширений контекст",
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
"add_link": {
|
"add_link": {
|
||||||
"add_link": "Thêm liên kết",
|
"add_link": "Thêm liên kết",
|
||||||
"button_add_link": "Thêm liên kết",
|
"button_add_link": "Thêm liên kết",
|
||||||
"help_on_links": "Trợ giúp về các liên kết"
|
"help_on_links": "Trợ giúp về các liên kết",
|
||||||
|
"link_title": "Đề mục liên kết"
|
||||||
},
|
},
|
||||||
"bulk_actions": {
|
"bulk_actions": {
|
||||||
"other": "Khác"
|
"other": "Khác"
|
||||||
@@ -30,7 +31,9 @@
|
|||||||
"cancel": "Huỷ"
|
"cancel": "Huỷ"
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"close": "Đóng"
|
"close": "Đóng",
|
||||||
|
"export": "Xuất",
|
||||||
|
"choose_export_type": "Xin hãy chọn cách xuất trước"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"other": "Khác",
|
"other": "Khác",
|
||||||
@@ -98,5 +101,11 @@
|
|||||||
},
|
},
|
||||||
"abstract_search_option": {
|
"abstract_search_option": {
|
||||||
"remove_this_search_option": "Xoá lựa chọn tìm kiếm này"
|
"remove_this_search_option": "Xoá lựa chọn tìm kiếm này"
|
||||||
|
},
|
||||||
|
"add_relation": {
|
||||||
|
"to": "tới"
|
||||||
|
},
|
||||||
|
"abstract_bulk_action": {
|
||||||
|
"remove_this_search_action": "Xoá hành động tìm kiếm này"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
apps/client/src/types.d.ts
vendored
5
apps/client/src/types.d.ts
vendored
@@ -64,6 +64,11 @@ declare global {
|
|||||||
EXCALIDRAW_ASSET_PATH?: string;
|
EXCALIDRAW_ASSET_PATH?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WindowEventMap {
|
||||||
|
"note-ready": Event;
|
||||||
|
"note-load-progress": CustomEvent<{ progress: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
interface AutoCompleteConfig {
|
interface AutoCompleteConfig {
|
||||||
appendTo?: HTMLElement | null;
|
appendTo?: HTMLElement | null;
|
||||||
hint?: boolean;
|
hint?: boolean;
|
||||||
|
|||||||
35
apps/client/src/utils/debouncer.ts
Normal file
35
apps/client/src/utils/debouncer.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export type DebouncerCallback<T> = (value: T) => void;
|
||||||
|
|
||||||
|
export default class Debouncer<T> {
|
||||||
|
|
||||||
|
private debounceInterval: number;
|
||||||
|
private callback: DebouncerCallback<T>;
|
||||||
|
private lastValue: T | undefined;
|
||||||
|
private timeoutId: any | null = null;
|
||||||
|
|
||||||
|
constructor(debounceInterval: number, onUpdate: DebouncerCallback<T>) {
|
||||||
|
this.debounceInterval = debounceInterval;
|
||||||
|
this.callback = onUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateValue(value: T) {
|
||||||
|
this.lastValue = value;
|
||||||
|
if (this.timeoutId !== null) {
|
||||||
|
clearTimeout(this.timeoutId);
|
||||||
|
}
|
||||||
|
this.timeoutId = setTimeout(this.reportUpdate.bind(this), this.debounceInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.timeoutId !== null) {
|
||||||
|
this.reportUpdate();
|
||||||
|
clearTimeout(this.timeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private reportUpdate() {
|
||||||
|
if (this.lastValue !== undefined) {
|
||||||
|
this.callback(this.lastValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ import froca from "../services/froca";
|
|||||||
import NoteLink from "./react/NoteLink";
|
import NoteLink from "./react/NoteLink";
|
||||||
import RawHtml from "./react/RawHtml";
|
import RawHtml from "./react/RawHtml";
|
||||||
import { ViewTypeOptions } from "./collections/interface";
|
import { ViewTypeOptions } from "./collections/interface";
|
||||||
|
import attributes from "../services/attributes";
|
||||||
|
import LoadResults from "../services/load_results";
|
||||||
|
|
||||||
export interface FloatingButtonContext {
|
export interface FloatingButtonContext {
|
||||||
parentComponent: Component;
|
parentComponent: Component;
|
||||||
@@ -64,7 +66,15 @@ export const MOBILE_FLOATING_BUTTONS: FloatingButtonsList = [
|
|||||||
RelationMapButtons,
|
RelationMapButtons,
|
||||||
ExportImageButtons,
|
ExportImageButtons,
|
||||||
Backlinks
|
Backlinks
|
||||||
]
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floating buttons that should be hidden in popup editor (Quick edit).
|
||||||
|
*/
|
||||||
|
export const POPUP_HIDDEN_FLOATING_BUTTONS: FloatingButtonsList = [
|
||||||
|
InAppHelpButton,
|
||||||
|
ToggleReadOnlyButton
|
||||||
|
];
|
||||||
|
|
||||||
function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) {
|
function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) {
|
||||||
const isEnabled = (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode;
|
const isEnabled = (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode;
|
||||||
@@ -302,13 +312,18 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
|
|||||||
let [ popupOpen, setPopupOpen ] = useState(false);
|
let [ popupOpen, setPopupOpen ] = useState(false);
|
||||||
const backlinksContainerRef = useRef<HTMLDivElement>(null);
|
const backlinksContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
function refresh() {
|
||||||
if (!isDefaultViewMode) return;
|
if (!isDefaultViewMode) return;
|
||||||
|
|
||||||
server.get<BacklinkCountResponse>(`note-map/${note.noteId}/backlink-count`).then(resp => {
|
server.get<BacklinkCountResponse>(`note-map/${note.noteId}/backlink-count`).then(resp => {
|
||||||
setBacklinkCount(resp.count);
|
setBacklinkCount(resp.count);
|
||||||
});
|
});
|
||||||
}, [ note ]);
|
}
|
||||||
|
|
||||||
|
useEffect(() => refresh(), [ note ]);
|
||||||
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
|
if (needsRefresh(note, loadResults)) refresh();
|
||||||
|
});
|
||||||
|
|
||||||
// Determine the max height of the container.
|
// Determine the max height of the container.
|
||||||
const { windowHeight } = useWindowSize();
|
const { windowHeight } = useWindowSize();
|
||||||
@@ -333,18 +348,18 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
|
|||||||
|
|
||||||
{popupOpen && (
|
{popupOpen && (
|
||||||
<div ref={backlinksContainerRef} className="backlinks-items dropdown-menu" style={{ display: "block" }}>
|
<div ref={backlinksContainerRef} className="backlinks-items dropdown-menu" style={{ display: "block" }}>
|
||||||
<BacklinksList noteId={note.noteId} />
|
<BacklinksList note={note} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BacklinksList({ noteId }: { noteId: string }) {
|
function BacklinksList({ note }: { note: FNote }) {
|
||||||
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
|
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
function refresh() {
|
||||||
server.get<BacklinksResponse>(`note-map/${noteId}/backlinks`).then(async (backlinks) => {
|
server.get<BacklinksResponse>(`note-map/${note.noteId}/backlinks`).then(async (backlinks) => {
|
||||||
// prefetch all
|
// prefetch all
|
||||||
const noteIds = backlinks
|
const noteIds = backlinks
|
||||||
.filter(bl => "noteId" in bl)
|
.filter(bl => "noteId" in bl)
|
||||||
@@ -352,7 +367,12 @@ function BacklinksList({ noteId }: { noteId: string }) {
|
|||||||
await froca.getNotes(noteIds);
|
await froca.getNotes(noteIds);
|
||||||
setBacklinks(backlinks);
|
setBacklinks(backlinks);
|
||||||
});
|
});
|
||||||
}, [ noteId ]);
|
}
|
||||||
|
|
||||||
|
useEffect(() => refresh(), [ note ]);
|
||||||
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
|
if (needsRefresh(note, loadResults)) refresh();
|
||||||
|
});
|
||||||
|
|
||||||
return backlinks.map(backlink => (
|
return backlinks.map(backlink => (
|
||||||
<div>
|
<div>
|
||||||
@@ -372,3 +392,9 @@ function BacklinksList({ noteId }: { noteId: string }) {
|
|||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function needsRefresh(note: FNote, loadResults: LoadResults) {
|
||||||
|
return loadResults.getAttributeRows().some(attr =>
|
||||||
|
attr.type === "relation" &&
|
||||||
|
attributes.isAffecting(attr, note));
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ export default function NoteDetail() {
|
|||||||
const { note, type, mime, noteContext, parentComponent } = useNoteInfo();
|
const { note, type, mime, noteContext, parentComponent } = useNoteInfo();
|
||||||
const { ntxId, viewScope } = noteContext ?? {};
|
const { ntxId, viewScope } = noteContext ?? {};
|
||||||
const isFullHeight = checkFullHeight(noteContext, type);
|
const isFullHeight = checkFullHeight(noteContext, type);
|
||||||
const noteTypesToRender = useRef<{ [ key in ExtendedNoteType ]?: (props: TypeWidgetProps) => VNode }>({});
|
const [ noteTypesToRender, setNoteTypesToRender ] = useState<{ [ key in ExtendedNoteType ]?: (props: TypeWidgetProps) => VNode }>({});
|
||||||
const [ activeNoteType, setActiveNoteType ] = useState<ExtendedNoteType>();
|
const [ activeNoteType, setActiveNoteType ] = useState<ExtendedNoteType>();
|
||||||
|
const widgetRequestId = useRef(0);
|
||||||
|
|
||||||
const props: TypeWidgetProps = {
|
const props: TypeWidgetProps = {
|
||||||
note: note!,
|
note: note!,
|
||||||
@@ -38,19 +39,28 @@ export default function NoteDetail() {
|
|||||||
parentComponent,
|
parentComponent,
|
||||||
noteContext
|
noteContext
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!type) return;
|
if (!type) return;
|
||||||
|
const requestId = ++widgetRequestId.current;
|
||||||
|
|
||||||
if (!noteTypesToRender.current[type]) {
|
if (!noteTypesToRender[type]) {
|
||||||
getCorrespondingWidget(type).then((el) => {
|
getCorrespondingWidget(type).then((el) => {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
noteTypesToRender.current[type] = el;
|
|
||||||
|
// Ignore stale requests
|
||||||
|
if (requestId !== widgetRequestId.current) return;
|
||||||
|
|
||||||
|
setNoteTypesToRender(prev => ({
|
||||||
|
...prev,
|
||||||
|
[type]: el
|
||||||
|
}));
|
||||||
setActiveNoteType(type);
|
setActiveNoteType(type);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setActiveNoteType(type);
|
setActiveNoteType(type);
|
||||||
}
|
}
|
||||||
}, [ note, viewScope, type ]);
|
}, [ note, viewScope, type, noteTypesToRender ]);
|
||||||
|
|
||||||
// Detect note type changes.
|
// Detect note type changes.
|
||||||
useTriliumEvent("entitiesReloaded", async ({ loadResults }) => {
|
useTriliumEvent("entitiesReloaded", async ({ loadResults }) => {
|
||||||
@@ -95,9 +105,11 @@ export default function NoteDetail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Automatically focus the editor.
|
// Automatically focus the editor.
|
||||||
useTriliumEvent("activeNoteChanged", () => {
|
useTriliumEvent("activeNoteChanged", ({ ntxId: eventNtxId }) => {
|
||||||
// Restore focus to the editor when switching tabs, but only if the note tree is not already focused.
|
if (eventNtxId != ntxId) return;
|
||||||
if (!document.activeElement?.classList.contains("fancytree-title")) {
|
// Restore focus to the editor when switching tabs,
|
||||||
|
// but only if the note tree and the note panel (e.g., note title or note detail) are not focused.
|
||||||
|
if (!document.activeElement?.classList.contains("fancytree-title") && !parentComponent.$widget[0].closest(".note-split")?.contains(document.activeElement)) {
|
||||||
parentComponent.triggerCommand("focusOnDetail", { ntxId });
|
parentComponent.triggerCommand("focusOnDetail", { ntxId });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -113,11 +125,14 @@ export default function NoteDetail() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isElectron()) return;
|
if (!isElectron()) return;
|
||||||
const { ipcRenderer } = dynamicRequire("electron");
|
const { ipcRenderer } = dynamicRequire("electron");
|
||||||
const listener = () => {
|
const onPrintProgress = (_e: any, { progress, action }: { progress: number, action: "printing" | "exporting_pdf" }) => showToast(action, progress);
|
||||||
toast.closePersistent("printing");
|
const onPrintDone = () => toast.closePersistent("printing");
|
||||||
|
ipcRenderer.on("print-progress", onPrintProgress);
|
||||||
|
ipcRenderer.on("print-done", onPrintDone);
|
||||||
|
return () => {
|
||||||
|
ipcRenderer.off("print-progress", onPrintProgress);
|
||||||
|
ipcRenderer.off("print-done", onPrintDone);
|
||||||
};
|
};
|
||||||
ipcRenderer.on("print-done", listener);
|
|
||||||
return () => ipcRenderer.off("print-done", listener);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useTriliumEvent("executeInActiveNoteDetailWidget", ({ callback }) => {
|
useTriliumEvent("executeInActiveNoteDetailWidget", ({ callback }) => {
|
||||||
@@ -139,11 +154,7 @@ export default function NoteDetail() {
|
|||||||
useTriliumEvent("printActiveNote", () => {
|
useTriliumEvent("printActiveNote", () => {
|
||||||
if (!noteContext?.isActive() || !note) return;
|
if (!noteContext?.isActive() || !note) return;
|
||||||
|
|
||||||
toast.showPersistent({
|
showToast("printing");
|
||||||
icon: "bx bx-loader-circle bx-spin",
|
|
||||||
message: t("note_detail.printing"),
|
|
||||||
id: "printing"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
const { ipcRenderer } = dynamicRequire("electron");
|
const { ipcRenderer } = dynamicRequire("electron");
|
||||||
@@ -162,6 +173,10 @@ export default function NoteDetail() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iframe.contentWindow.addEventListener("note-load-progress", (e) => {
|
||||||
|
showToast("printing", e.detail.progress);
|
||||||
|
});
|
||||||
|
|
||||||
iframe.contentWindow.addEventListener("note-ready", () => {
|
iframe.contentWindow.addEventListener("note-ready", () => {
|
||||||
toast.closePersistent("printing");
|
toast.closePersistent("printing");
|
||||||
iframe.contentWindow?.print();
|
iframe.contentWindow?.print();
|
||||||
@@ -173,11 +188,7 @@ export default function NoteDetail() {
|
|||||||
|
|
||||||
useTriliumEvent("exportAsPdf", () => {
|
useTriliumEvent("exportAsPdf", () => {
|
||||||
if (!noteContext?.isActive() || !note) return;
|
if (!noteContext?.isActive() || !note) return;
|
||||||
toast.showPersistent({
|
showToast("exporting_pdf");
|
||||||
icon: "bx bx-loader-circle bx-spin",
|
|
||||||
message: t("note_detail.printing_pdf"),
|
|
||||||
id: "printing"
|
|
||||||
});
|
|
||||||
|
|
||||||
const { ipcRenderer } = dynamicRequire("electron");
|
const { ipcRenderer } = dynamicRequire("electron");
|
||||||
ipcRenderer.send("export-as-pdf", {
|
ipcRenderer.send("export-as-pdf", {
|
||||||
@@ -193,7 +204,7 @@ export default function NoteDetail() {
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
class={`note-detail ${isFullHeight ? "full-height" : ""}`}
|
class={`note-detail ${isFullHeight ? "full-height" : ""}`}
|
||||||
>
|
>
|
||||||
{Object.entries(noteTypesToRender.current).map(([ itemType, Element ]) => {
|
{Object.entries(noteTypesToRender).map(([ itemType, Element ]) => {
|
||||||
return <NoteDetailWrapper
|
return <NoteDetailWrapper
|
||||||
Element={Element}
|
Element={Element}
|
||||||
key={itemType}
|
key={itemType}
|
||||||
@@ -322,3 +333,12 @@ function checkFullHeight(noteContext: NoteContext | undefined, type: ExtendedNot
|
|||||||
|| noteContext?.viewScope?.viewMode === "attachments"
|
|| noteContext?.viewScope?.viewMode === "attachments"
|
||||||
|| isBackendNote;
|
|| isBackendNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showToast(type: "printing" | "exporting_pdf", progress: number = 0) {
|
||||||
|
toast.showPersistent({
|
||||||
|
icon: "bx bx-loader-circle bx-spin",
|
||||||
|
message: type === "printing" ? t("note_detail.printing") : t("note_detail.printing_pdf"),
|
||||||
|
id: "printing",
|
||||||
|
progress
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
97
apps/client/src/widgets/PromotedAttributes.css
Normal file
97
apps/client/src/widgets/PromotedAttributes.css
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
body.mobile .promoted-attributes-widget {
|
||||||
|
/* https://github.com/zadam/trilium/issues/4468 */
|
||||||
|
flex-shrink: 0.4;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component.promoted-attributes-widget {
|
||||||
|
contain: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attributes-container {
|
||||||
|
margin: 0 1.5em;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 400px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell > label {
|
||||||
|
user-select: none;
|
||||||
|
font-weight: bold;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.promoted-attribute-cell > * {
|
||||||
|
display: table-cell;
|
||||||
|
padding: 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell div.input-group {
|
||||||
|
margin-inline-start: 10px;
|
||||||
|
display: flex;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
.promoted-attribute-cell strong {
|
||||||
|
word-break:keep-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell input[type="checkbox"] {
|
||||||
|
width: 22px !important;
|
||||||
|
flex-grow: 0;
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore default apperance */
|
||||||
|
.promoted-attribute-cell input[type="number"],
|
||||||
|
.promoted-attribute-cell input[type="checkbox"] {
|
||||||
|
appearance: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell input[type="color"] {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-top: 2px;
|
||||||
|
appearance: none;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
border-radius: 25% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
border-radius: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-label-number input {
|
||||||
|
text-align: right;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"] {
|
||||||
|
position: relative;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"]:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
inset-inline-start: 0px;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
464
apps/client/src/widgets/PromotedAttributes.tsx
Normal file
464
apps/client/src/widgets/PromotedAttributes.tsx
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks";
|
||||||
|
import "./PromotedAttributes.css";
|
||||||
|
import { useNoteContext, useNoteLabel, useTriliumEvent, useUniqueName } from "./react/hooks";
|
||||||
|
import { Attribute } from "../services/attribute_parser";
|
||||||
|
import FAttribute from "../entities/fattribute";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { t } from "../services/i18n";
|
||||||
|
import { DefinitionObject, extractAttributeDefinitionTypeAndName, LabelType } from "../services/promoted_attribute_definition_parser";
|
||||||
|
import server from "../services/server";
|
||||||
|
import FNote from "../entities/fnote";
|
||||||
|
import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact";
|
||||||
|
import NoteAutocomplete from "./react/NoteAutocomplete";
|
||||||
|
import ws from "../services/ws";
|
||||||
|
import { UpdateAttributeResponse } from "@triliumnext/commons";
|
||||||
|
import attributes from "../services/attributes";
|
||||||
|
import debounce from "../services/debounce";
|
||||||
|
|
||||||
|
interface Cell {
|
||||||
|
uniqueId: string;
|
||||||
|
definitionAttr: FAttribute;
|
||||||
|
definition: DefinitionObject;
|
||||||
|
valueAttr: Attribute;
|
||||||
|
valueName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CellProps {
|
||||||
|
note: FNote;
|
||||||
|
componentId: string;
|
||||||
|
cell: Cell,
|
||||||
|
cells: Cell[],
|
||||||
|
shouldFocus: boolean;
|
||||||
|
setCells: Dispatch<StateUpdater<Cell[] | undefined>>;
|
||||||
|
setCellToFocus(cell: Cell): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnChangeEventData = TargetedEvent<HTMLInputElement, Event> | InputEvent | JQuery.TriggeredEvent<HTMLInputElement, undefined, HTMLInputElement, HTMLInputElement>;
|
||||||
|
type OnChangeListener = (e: OnChangeEventData) => Promise<void>;
|
||||||
|
|
||||||
|
export default function PromotedAttributes() {
|
||||||
|
const { note, componentId } = useNoteContext();
|
||||||
|
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
|
||||||
|
const [ cellToFocus, setCellToFocus ] = useState<Cell>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="promoted-attributes-widget">
|
||||||
|
{cells && cells.length > 0 && <div className="promoted-attributes-container">
|
||||||
|
{note && cells?.map(cell => <PromotedAttributeCell
|
||||||
|
key={cell.uniqueId}
|
||||||
|
cell={cell}
|
||||||
|
cells={cells} setCells={setCells}
|
||||||
|
shouldFocus={cell === cellToFocus} setCellToFocus={setCellToFocus}
|
||||||
|
componentId={componentId} note={note}
|
||||||
|
/>)}
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the individual cells (instances for promoted attributes including empty attributes). Promoted attributes with "multiple" multiplicity will have
|
||||||
|
* each value represented as a separate cell.
|
||||||
|
*
|
||||||
|
* The cells are returned as a state since they can also be altered internally if needed, for example to add a new empty cell.
|
||||||
|
*/
|
||||||
|
function usePromotedAttributeData(note: FNote | null | undefined, componentId: string): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
|
||||||
|
const [ viewType ] = useNoteLabel(note, "viewType");
|
||||||
|
const [ cells, setCells ] = useState<Cell[]>();
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (!note || viewType === "table") {
|
||||||
|
setCells([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const promotedDefAttrs = note.getPromotedDefinitionAttributes();
|
||||||
|
const ownedAttributes = note.getOwnedAttributes();
|
||||||
|
// attrs are not resorted if position changes after the initial load
|
||||||
|
// promoted attrs are sorted primarily by order of definitions, but with multi-valued promoted attrs
|
||||||
|
// the order of attributes is important as well
|
||||||
|
ownedAttributes.sort((a, b) => a.position - b.position);
|
||||||
|
|
||||||
|
const cells: Cell[] = [];
|
||||||
|
for (const definitionAttr of promotedDefAttrs) {
|
||||||
|
const [ valueType, valueName ] = extractAttributeDefinitionTypeAndName(definitionAttr.name);
|
||||||
|
|
||||||
|
let valueAttrs = ownedAttributes.filter((el) => el.name === valueName && el.type === valueType) as Attribute[];
|
||||||
|
|
||||||
|
if (valueAttrs.length === 0) {
|
||||||
|
valueAttrs.push({
|
||||||
|
attributeId: "",
|
||||||
|
type: valueType,
|
||||||
|
name: valueName,
|
||||||
|
value: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (definitionAttr.getDefinition().multiplicity === "single") {
|
||||||
|
valueAttrs = valueAttrs.slice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [ i, valueAttr ] of valueAttrs.entries()) {
|
||||||
|
const definition = definitionAttr.getDefinition();
|
||||||
|
|
||||||
|
// if not owned, we'll force creation of a new attribute instead of updating the inherited one
|
||||||
|
if (valueAttr.noteId !== note.noteId) {
|
||||||
|
valueAttr.attributeId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueId = `${note.noteId}-${valueAttr.name}-${i}`;
|
||||||
|
cells.push({ definitionAttr, definition, valueAttr, valueName, uniqueId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCells(cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(refresh, [ note, viewType ]);
|
||||||
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
|
if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [ cells, setCells ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function PromotedAttributeCell(props: CellProps) {
|
||||||
|
const { valueName, valueAttr, definition } = props.cell;
|
||||||
|
const inputId = useUniqueName(`value-${valueAttr.name}`);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!props.shouldFocus) return;
|
||||||
|
const inputEl = document.getElementById(inputId);
|
||||||
|
if (inputEl) {
|
||||||
|
inputEl.focus();
|
||||||
|
}
|
||||||
|
}, [ props.shouldFocus ]);
|
||||||
|
|
||||||
|
let correspondingInput: ComponentChild;
|
||||||
|
let className: string | undefined;
|
||||||
|
switch (valueAttr.type) {
|
||||||
|
case "label":
|
||||||
|
correspondingInput = <LabelInput inputId={inputId} {...props} />;
|
||||||
|
className = `promoted-attribute-label-${definition.labelType}`;
|
||||||
|
break;
|
||||||
|
case "relation":
|
||||||
|
correspondingInput = <RelationInput inputId={inputId} {...props} />;
|
||||||
|
className = "promoted-attribute-relation";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ws.logError(t(`promoted_attributes.unknown_attribute_type`, { type: valueAttr.type }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx("promoted-attribute-cell", className)}>
|
||||||
|
{definition.labelType !== "boolean" && <label for={inputId}>{definition.promotedAlias ?? valueName}</label>}
|
||||||
|
{correspondingInput}
|
||||||
|
<MultiplicityCell {...props} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LABEL_MAPPINGS: Record<LabelType, HTMLInputTypeAttribute> = {
|
||||||
|
text: "text",
|
||||||
|
number: "number",
|
||||||
|
boolean: "checkbox",
|
||||||
|
date: "date",
|
||||||
|
datetime: "datetime-local",
|
||||||
|
time: "time",
|
||||||
|
color: "hidden", // handled separately.
|
||||||
|
url: "url"
|
||||||
|
};
|
||||||
|
|
||||||
|
function LabelInput({ inputId, ...props }: CellProps & { inputId: string }) {
|
||||||
|
const { valueName, valueAttr, definition, definitionAttr } = props.cell;
|
||||||
|
const onChangeListener = buildPromotedAttributeLabelChangedListener({...props});
|
||||||
|
const extraInputProps: InputHTMLAttributes = {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (definition.labelType === "text") {
|
||||||
|
const el = document.getElementById(inputId);
|
||||||
|
if (el) {
|
||||||
|
setupTextLabelAutocomplete(el as HTMLInputElement, valueAttr, onChangeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ inputId, valueAttr, onChangeListener ]);
|
||||||
|
|
||||||
|
switch (definition.labelType) {
|
||||||
|
case "number": {
|
||||||
|
let step = 1;
|
||||||
|
for (let i = 0; i < (definition.numberPrecision || 0) && i < 10; i++) {
|
||||||
|
step /= 10;
|
||||||
|
}
|
||||||
|
extraInputProps.step = step;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "url": {
|
||||||
|
extraInputProps.placeholder = t("promoted_attributes.url_placeholder");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputNode = <input
|
||||||
|
className="form-control promoted-attribute-input"
|
||||||
|
tabIndex={200 + definitionAttr.position}
|
||||||
|
id={inputId}
|
||||||
|
type={LABEL_MAPPINGS[definition.labelType ?? "text"]}
|
||||||
|
value={valueAttr.value}
|
||||||
|
placeholder={t("promoted_attributes.unset-field-placeholder")}
|
||||||
|
data-attribute-id={valueAttr.attributeId}
|
||||||
|
data-attribute-type={valueAttr.type}
|
||||||
|
data-attribute-name={valueAttr.name}
|
||||||
|
onChange={onChangeListener}
|
||||||
|
{...extraInputProps}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
if (definition.labelType === "boolean") {
|
||||||
|
return <>
|
||||||
|
<div>
|
||||||
|
<label className="tn-checkbox">{inputNode}</label>
|
||||||
|
</div>
|
||||||
|
<label for={inputId}>{definition.promotedAlias ?? valueName}</label>
|
||||||
|
</>
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="input-group">
|
||||||
|
{inputNode}
|
||||||
|
{ definition.labelType === "color" && <ColorPicker {...props} onChange={onChangeListener} inputId={inputId} />}
|
||||||
|
{ definition.labelType === "url" && (
|
||||||
|
<InputButton
|
||||||
|
className="open-external-link-button"
|
||||||
|
icon="bx bx-window-open"
|
||||||
|
title={t("promoted_attributes.open_external_link")}
|
||||||
|
onClick={(e) => {
|
||||||
|
const inputEl = document.getElementById(inputId) as HTMLInputElement | null;
|
||||||
|
const url = inputEl?.value;
|
||||||
|
if (url) {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We insert a separate input since the color input does not support empty value.
|
||||||
|
// This is a workaround to allow clearing the color input.
|
||||||
|
function ColorPicker({ cell, onChange, inputId }: CellProps & {
|
||||||
|
onChange: (e: TargetedEvent<HTMLInputElement, Event>) => Promise<void>,
|
||||||
|
inputId: string;
|
||||||
|
}) {
|
||||||
|
const defaultColor = "#ffffff";
|
||||||
|
const colorInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
ref={colorInputRef}
|
||||||
|
className="form-control promoted-attribute-input"
|
||||||
|
type="color"
|
||||||
|
value={cell.valueAttr.value || defaultColor}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<InputButton
|
||||||
|
icon="bx bxs-tag-x"
|
||||||
|
title={t("promoted_attributes.remove_color")}
|
||||||
|
onClick={(e) => {
|
||||||
|
// Indicate to the user the color was reset.
|
||||||
|
if (colorInputRef.current) {
|
||||||
|
colorInputRef.current.value = defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the actual attribute change by injecting it into the hidden field.
|
||||||
|
const inputEl = document.getElementById(inputId) as HTMLInputElement | null;
|
||||||
|
if (!inputEl) return;
|
||||||
|
inputEl.value = "";
|
||||||
|
onChange({
|
||||||
|
...e,
|
||||||
|
target: inputEl
|
||||||
|
} as unknown as TargetedInputEvent<HTMLInputElement>);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RelationInput({ inputId, ...props }: CellProps & { inputId: string }) {
|
||||||
|
return (
|
||||||
|
<NoteAutocomplete
|
||||||
|
id={inputId}
|
||||||
|
noteId={props.cell.valueAttr.value}
|
||||||
|
noteIdChanged={async (value) => {
|
||||||
|
const { note, cell, componentId, setCells } = props;
|
||||||
|
await updateAttribute(note, cell, componentId, value, setCells);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MultiplicityCell({ cell, cells, setCells, setCellToFocus, note, componentId }: CellProps) {
|
||||||
|
return (cell.definition.multiplicity === "multi" &&
|
||||||
|
<td className="multiplicity">
|
||||||
|
<PromotedActionButton
|
||||||
|
icon="bx bx-plus"
|
||||||
|
title={t("promoted_attributes.add_new_attribute")}
|
||||||
|
onClick={() => {
|
||||||
|
const index = cells.indexOf(cell);
|
||||||
|
const newCell: Cell = {
|
||||||
|
...cell,
|
||||||
|
valueAttr: {
|
||||||
|
attributeId: "",
|
||||||
|
type: cell.valueAttr.type,
|
||||||
|
name: cell.valueName,
|
||||||
|
value: ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setCells([
|
||||||
|
...cells.slice(0, index + 1),
|
||||||
|
newCell,
|
||||||
|
...cells.slice(index + 1)
|
||||||
|
]);
|
||||||
|
setCellToFocus(newCell);
|
||||||
|
}}
|
||||||
|
/>{' '}
|
||||||
|
<PromotedActionButton
|
||||||
|
icon="bx bx-trash"
|
||||||
|
title={t("promoted_attributes.remove_this_attribute")}
|
||||||
|
onClick={async () => {
|
||||||
|
// Remove the attribute from the server if it exists.
|
||||||
|
const { attributeId, type } = cell.valueAttr;
|
||||||
|
const valueName = cell.valueName;
|
||||||
|
if (attributeId) {
|
||||||
|
await server.remove(`notes/${note.noteId}/attributes/${attributeId}`, componentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = cells.indexOf(cell);
|
||||||
|
const isLastOneOfType = cells.filter(c => c.valueAttr.type === type && c.valueAttr.name === valueName).length < 2;
|
||||||
|
const newOnesToInsert: Cell[] = [];
|
||||||
|
if (isLastOneOfType) {
|
||||||
|
newOnesToInsert.push({
|
||||||
|
...cell,
|
||||||
|
valueAttr: {
|
||||||
|
attributeId: "",
|
||||||
|
type: cell.valueAttr.type,
|
||||||
|
name: cell.valueName,
|
||||||
|
value: ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setCells(cells.toSpliced(index, 1, ...newOnesToInsert));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function PromotedActionButton({ icon, title, onClick }: {
|
||||||
|
icon: string,
|
||||||
|
title: string,
|
||||||
|
onClick: MouseEventHandler<HTMLSpanElement>
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={clsx("tn-tool-button pointer", icon)}
|
||||||
|
title={title}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function InputButton({ icon, className, title, onClick }: {
|
||||||
|
icon: string;
|
||||||
|
className?: string;
|
||||||
|
title: string;
|
||||||
|
onClick: MouseEventHandler<HTMLSpanElement>;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={clsx("input-group-text", className, icon)}
|
||||||
|
title={title}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupTextLabelAutocomplete(el: HTMLInputElement, valueAttr: Attribute, onChangeListener: OnChangeListener) {
|
||||||
|
// no need to await for this, can be done asynchronously
|
||||||
|
const $input = $(el);
|
||||||
|
server.get<string[]>(`attribute-values/${encodeURIComponent(valueAttr.name)}`).then((_attributeValues) => {
|
||||||
|
if (_attributeValues.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributeValues = _attributeValues.map((attribute) => ({ value: attribute }));
|
||||||
|
|
||||||
|
$input.autocomplete(
|
||||||
|
{
|
||||||
|
appendTo: document.querySelector("body"),
|
||||||
|
hint: false,
|
||||||
|
autoselect: false,
|
||||||
|
openOnFocus: true,
|
||||||
|
minLength: 0,
|
||||||
|
tabAutocomplete: false
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
displayKey: "value",
|
||||||
|
source: function (term, cb) {
|
||||||
|
term = term.toLowerCase();
|
||||||
|
|
||||||
|
const filtered = attributeValues.filter((attr) => attr.value.toLowerCase().includes(term));
|
||||||
|
|
||||||
|
cb(filtered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$input.off("autocomplete:selected");
|
||||||
|
$input.on("autocomplete:selected", onChangeListener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPromotedAttributeLabelChangedListener({ note, cell, componentId, setCells }: CellProps): OnChangeListener {
|
||||||
|
async function onChange(e: OnChangeEventData) {
|
||||||
|
const inputEl = e.target as HTMLInputElement;
|
||||||
|
let value: string;
|
||||||
|
|
||||||
|
if (inputEl.type === "checkbox") {
|
||||||
|
value = inputEl.checked ? "true" : "false";
|
||||||
|
} else {
|
||||||
|
value = inputEl.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateAttribute(note, cell, componentId, value, setCells);
|
||||||
|
}
|
||||||
|
|
||||||
|
return debounce(onChange, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateAttribute(note: FNote, cell: Cell, componentId: string, value: string | undefined, setCells: Dispatch<StateUpdater<Cell[] | undefined>>) {
|
||||||
|
const { attributeId } = await server.put<UpdateAttributeResponse>(
|
||||||
|
`notes/${note.noteId}/attribute`,
|
||||||
|
{
|
||||||
|
attributeId: cell.valueAttr.attributeId,
|
||||||
|
type: cell.valueAttr.type,
|
||||||
|
name: cell.valueName,
|
||||||
|
value: value || ""
|
||||||
|
},
|
||||||
|
componentId
|
||||||
|
);
|
||||||
|
setCells(prev =>
|
||||||
|
prev?.map(c =>
|
||||||
|
c.uniqueId === cell.uniqueId
|
||||||
|
? { ...c, valueAttr: {
|
||||||
|
...c.valueAttr,
|
||||||
|
attributeId,
|
||||||
|
value
|
||||||
|
} }
|
||||||
|
: c
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,28 +3,27 @@ import { t } from "../services/i18n";
|
|||||||
import { useIsNoteReadOnly, useNoteContext, useTriliumEvent } from "./react/hooks"
|
import { useIsNoteReadOnly, useNoteContext, useTriliumEvent } from "./react/hooks"
|
||||||
import Button from "./react/Button";
|
import Button from "./react/Button";
|
||||||
import InfoBar from "./react/InfoBar";
|
import InfoBar from "./react/InfoBar";
|
||||||
|
import HelpButton from "./react/HelpButton";
|
||||||
|
|
||||||
export default function ReadOnlyNoteInfoBar(props: {}) {
|
export default function ReadOnlyNoteInfoBar(props: {}) {
|
||||||
const { note, noteContext } = useNoteContext();
|
const { note, noteContext } = useNoteContext();
|
||||||
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
|
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
|
||||||
const isExplicitReadOnly = note?.isLabelTruthy("readOnly");
|
const isExplicitReadOnly = note?.isLabelTruthy("readOnly");
|
||||||
|
|
||||||
return <InfoBar className="read-only-note-info-bar-widget"
|
return (
|
||||||
|
<InfoBar
|
||||||
|
className="read-only-note-info-bar-widget"
|
||||||
type={(isExplicitReadOnly ? "subtle" : "prominent")}
|
type={(isExplicitReadOnly ? "subtle" : "prominent")}
|
||||||
style={{display: (!isReadOnly) ? "none" : undefined}}>
|
style={{display: (!isReadOnly) ? "none" : undefined}}
|
||||||
|
>
|
||||||
<div class="read-only-note-info-bar-widget-content">
|
<div class="read-only-note-info-bar-widget-content">
|
||||||
{(isExplicitReadOnly) ? (
|
{(isExplicitReadOnly) ? (
|
||||||
<div>{t("read-only-info.read-only-note")}</div>
|
<div>{t("read-only-info.read-only-note")}</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{t("read-only-info.auto-read-only-note")}
|
{t("read-only-info.auto-read-only-note")}
|
||||||
|
{" "}
|
||||||
<a class="tn-link"
|
<HelpButton helpPage="CoFPLs3dRlXc" />
|
||||||
href="https://docs.triliumnotes.org/user-guide/concepts/notes/read-only-notes#automatic-read-only-mode">
|
|
||||||
|
|
||||||
{t("read-only-info.auto-read-only-learn-more")}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -32,5 +31,5 @@ export default function ReadOnlyNoteInfoBar(props: {}) {
|
|||||||
icon="bx-pencil" onClick={() => enableEditing()} />
|
icon="bx-pencil" onClick={() => enableEditing()} />
|
||||||
</div>
|
</div>
|
||||||
</InfoBar>
|
</InfoBar>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
.promoted-attributes {
|
.user-attributes {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.promoted-attributes .promoted-attribute {
|
.user-attributes .user-attribute {
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -17,15 +17,15 @@
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.promoted-attributes .promoted-attribute:hover {
|
.user-attributes .user-attribute:hover {
|
||||||
background-color: var(--chip-bg-hover, rgba(0, 0, 0, 0.12));
|
background-color: var(--chip-bg-hover, rgba(0, 0, 0, 0.12));
|
||||||
border-color: var(--chip-border-hover, rgba(0, 0, 0, 0.22));
|
border-color: var(--chip-border-hover, rgba(0, 0, 0, 0.22));
|
||||||
}
|
}
|
||||||
|
|
||||||
.promoted-attributes .promoted-attribute .name {
|
.user-attributes .user-attribute .name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.promoted-attributes .promoted-attribute .value {
|
.user-attributes .user-attribute .value {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
import "./PromotedAttributesDisplay.css";
|
import "./UserAttributesList.css";
|
||||||
import { useTriliumEvent } from "../react/hooks";
|
import { useTriliumEvent } from "../react/hooks";
|
||||||
import attributes from "../../services/attributes";
|
import attributes from "../../services/attributes";
|
||||||
import { DefinitionObject } from "../../services/promoted_attribute_definition_parser";
|
import { DefinitionObject } from "../../services/promoted_attribute_definition_parser";
|
||||||
import { formatDateTime } from "../../utils/formatters";
|
import { formatDateTime } from "../../utils/formatters";
|
||||||
import { ComponentChild, ComponentChildren, CSSProperties } from "preact";
|
import { ComponentChildren, CSSProperties } from "preact";
|
||||||
import Icon from "../react/Icon";
|
import Icon from "../react/Icon";
|
||||||
import NoteLink from "../react/NoteLink";
|
import NoteLink from "../react/NoteLink";
|
||||||
import { getReadableTextColor } from "../../services/css_class_manager";
|
import { getReadableTextColor } from "../../services/css_class_manager";
|
||||||
|
|
||||||
interface PromotedAttributesDisplayProps {
|
interface UserAttributesListProps {
|
||||||
note: FNote;
|
note: FNote;
|
||||||
ignoredAttributes?: string[];
|
ignoredAttributes?: string[];
|
||||||
}
|
}
|
||||||
@@ -23,39 +23,39 @@ interface AttributeWithDefinitions {
|
|||||||
def: DefinitionObject;
|
def: DefinitionObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PromotedAttributesDisplay({ note, ignoredAttributes }: PromotedAttributesDisplayProps) {
|
export default function UserAttributesDisplay({ note, ignoredAttributes }: UserAttributesListProps) {
|
||||||
const promotedDefinitionAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes);
|
const userAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes);
|
||||||
return promotedDefinitionAttributes?.length > 0 && (
|
return userAttributes?.length > 0 && (
|
||||||
<div className="promoted-attributes">
|
<div className="user-attributes">
|
||||||
{promotedDefinitionAttributes?.map(attr => buildPromotedAttribute(attr))}
|
{userAttributes?.map(attr => buildUserAttribute(attr))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function useNoteAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
|
function useNoteAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
|
||||||
const [ promotedDefinitionAttributes, setPromotedDefinitionAttributes ] = useState<AttributeWithDefinitions[]>(getAttributesWithDefinitions(note, attributesToIgnore));
|
const [ userAttributes, setUserAttributes ] = useState<AttributeWithDefinitions[]>(getAttributesWithDefinitions(note, attributesToIgnore));
|
||||||
|
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
|
if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
|
||||||
setPromotedDefinitionAttributes(getAttributesWithDefinitions(note, attributesToIgnore));
|
setUserAttributes(getAttributesWithDefinitions(note, attributesToIgnore));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return promotedDefinitionAttributes;
|
return userAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PromotedAttribute({ attr, children, style }: { attr: AttributeWithDefinitions, children: ComponentChildren, style?: CSSProperties }) {
|
function UserAttribute({ attr, children, style }: { attr: AttributeWithDefinitions, children: ComponentChildren, style?: CSSProperties }) {
|
||||||
const className = `${attr.type === "label" ? "label" + " " + attr.def.labelType : "relation"}`;
|
const className = `${attr.type === "label" ? "label" + " " + attr.def.labelType : "relation"}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span key={attr.friendlyName} className={`promoted-attribute type-${className}`} style={style}>
|
<span key={attr.friendlyName} className={`user-attribute type-${className}`} style={style}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPromotedAttribute(attr: AttributeWithDefinitions): ComponentChildren {
|
function buildUserAttribute(attr: AttributeWithDefinitions): ComponentChildren {
|
||||||
const defaultLabel = <><strong>{attr.friendlyName}:</strong>{" "}</>;
|
const defaultLabel = <><strong>{attr.friendlyName}:</strong>{" "}</>;
|
||||||
let content: ComponentChildren;
|
let content: ComponentChildren;
|
||||||
let style: CSSProperties | undefined;
|
let style: CSSProperties | undefined;
|
||||||
@@ -102,13 +102,13 @@ function buildPromotedAttribute(attr: AttributeWithDefinitions): ComponentChildr
|
|||||||
content = <>{defaultLabel}<NoteLink notePath={attr.value} showNoteIcon /></>;
|
content = <>{defaultLabel}<NoteLink notePath={attr.value} showNoteIcon /></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <PromotedAttribute attr={attr} style={style}>{content}</PromotedAttribute>
|
return <UserAttribute attr={attr} style={style}>{content}</UserAttribute>
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
|
function getAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
|
||||||
const promotedDefinitionAttributes = note.getAttributeDefinitions();
|
const attributeDefintions = note.getAttributeDefinitions();
|
||||||
const result: AttributeWithDefinitions[] = [];
|
const result: AttributeWithDefinitions[] = [];
|
||||||
for (const attr of promotedDefinitionAttributes) {
|
for (const attr of attributeDefintions) {
|
||||||
const def = attr.getDefinition();
|
const def = attr.getDefinition();
|
||||||
const [ type, name ] = attr.name.split(":", 2);
|
const [ type, name ] = attr.name.split(":", 2);
|
||||||
const friendlyName = def?.promotedAlias || name;
|
const friendlyName = def?.promotedAlias || name;
|
||||||
@@ -7,17 +7,10 @@ import toastService from "../../services/toast.js";
|
|||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import { Dropdown } from "bootstrap";
|
import { Dropdown } from "bootstrap";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import { dayjs, type Dayjs } from "@triliumnext/commons";
|
||||||
import isoWeek from "dayjs/plugin/isoWeek.js";
|
|
||||||
import utc from "dayjs/plugin/utc.js";
|
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
|
|
||||||
import "../../stylesheets/calendar.css";
|
import "../../stylesheets/calendar.css";
|
||||||
import type { AttributeRow, OptionDefinitions } from "@triliumnext/commons";
|
import type { AttributeRow, OptionDefinitions } from "@triliumnext/commons";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(isSameOrAfter);
|
|
||||||
dayjs.extend(isoWeek);
|
|
||||||
|
|
||||||
const MONTHS = [
|
const MONTHS = [
|
||||||
t("calendar.january"),
|
t("calendar.january"),
|
||||||
t("calendar.february"),
|
t("calendar.february"),
|
||||||
@@ -110,7 +103,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
|
|||||||
private weekNotes: string[] = [];
|
private weekNotes: string[] = [];
|
||||||
|
|
||||||
constructor(title: string = "", icon: string = "") {
|
constructor(title: string = "", icon: string = "") {
|
||||||
super(title, icon, DROPDOWN_TPL);
|
super(title, icon, DROPDOWN_TPL, "calendar-dropdown-menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@@ -211,8 +204,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
|
|||||||
const $target = $(e.target);
|
const $target = $(e.target);
|
||||||
|
|
||||||
// Keep dropdown open when clicking on month select button or year selector area
|
// Keep dropdown open when clicking on month select button or year selector area
|
||||||
if ($target.closest('.btn.dropdown-toggle.select-button').length ||
|
if ($target.closest('.btn.dropdown-toggle.select-button').length) {
|
||||||
$target.closest('.calendar-year-selector').length) {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import ActionButton from "../react/ActionButton";
|
import ActionButton from "../react/ActionButton";
|
||||||
import { useNoteContext, useTriliumEvent } from "../react/hooks";
|
import { useNoteContext, useTriliumEvents } from "../react/hooks";
|
||||||
|
import appContext from "../../components/app_context";
|
||||||
|
|
||||||
export default function ClosePaneButton() {
|
export default function ClosePaneButton() {
|
||||||
const { noteContext, ntxId, parentComponent } = useNoteContext();
|
const { noteContext, ntxId, parentComponent } = useNoteContext();
|
||||||
const [isEnabled, setIsEnabled] = useState(false);
|
const [isEnabled, setIsEnabled] = useState(false);
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
setIsEnabled(!!(noteContext && !!noteContext.mainNtxId));
|
const isMainOfSomeContext = appContext.tabManager.noteContexts.some(c => c.mainNtxId === ntxId);
|
||||||
|
setIsEnabled(!!(noteContext && (!!noteContext.mainNtxId || isMainOfSomeContext)));
|
||||||
}
|
}
|
||||||
|
|
||||||
useTriliumEvent("noteContextReorder", refresh);
|
useTriliumEvents(["noteContextRemoved", "noteContextReorder", "newNoteContextCreated"], refresh);
|
||||||
useEffect(refresh, [ntxId]);
|
useEffect(refresh, [ntxId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user