mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
1030 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
74e5e0e4c0 | ||
|
3eeb137416 | ||
|
f04c277f03 | ||
|
e587f208be | ||
|
aaeaa643e3 | ||
|
9c01a849cb | ||
|
fb08552d46 | ||
|
3bc91f10ec | ||
|
19f758a0fb | ||
|
e6c96553d0 | ||
|
7b3f2a8986 | ||
|
87108421b3 | ||
|
809a55b2fd | ||
|
c328d6f04a | ||
|
9587a704c5 | ||
|
283d49a6ee | ||
|
c259c0a72a | ||
|
c6aea00e27 | ||
|
50322ed8b0 | ||
|
0648da8d05 | ||
|
a7024a1d34 | ||
|
df15a00430 | ||
|
3ec692e76c | ||
|
0bbaeeaedd | ||
|
b24d4f2340 | ||
|
0410540066 | ||
|
a5c3e22833 | ||
|
0d9068f11e | ||
|
67fa9c9595 | ||
|
a8dc4dc810 | ||
|
9645034b09 | ||
|
67174c8cf8 | ||
|
7264e3fe83 | ||
|
2bed9fde70 | ||
|
b8d7b0922d | ||
|
d862005c1c | ||
|
6894b7ef72 | ||
|
06ade803fa | ||
|
389c2b5435 | ||
|
2c822f79f7 | ||
|
a6898365d1 | ||
|
8e49ecce49 | ||
|
851a00630a | ||
|
fbc72085f8 | ||
|
4fd68e5be7 | ||
|
a178bdc3b0 | ||
|
2c8e855385 | ||
|
adb3badcc1 | ||
|
c9b3ccc585 | ||
|
dd17c2483f | ||
|
8e16f28827 | ||
|
d81371c2b7 | ||
|
bcf73e0e55 | ||
|
f1d375c2ba | ||
|
720da5649b | ||
|
ad8709842b | ||
|
55607ee847 | ||
|
0b9555234e | ||
|
d3ea5c081d | ||
|
74fbc88d7d | ||
|
461e8d7929 | ||
|
e593dd4dbd | ||
|
7841f3d91f | ||
|
c4e1fff1bc | ||
|
863554fea6 | ||
|
c2e3bfa06d | ||
|
c50ed4bf6a | ||
|
3594c4aac7 | ||
|
86819b6f4f | ||
|
6d0982e823 | ||
|
ab9c654708 | ||
|
cdbcd6cfb2 | ||
|
3328df6076 | ||
|
6d4484c4d0 | ||
|
e48cd0a49d | ||
|
044cceba4d | ||
|
58eae43cb0 | ||
|
22e17cd681 | ||
|
81f135fa7c | ||
|
af2b108730 | ||
|
5e4dc388eb | ||
|
d32224041a | ||
|
68370dbe30 | ||
|
73d6801406 | ||
|
4f7c925dc6 | ||
|
e2a1892e6b | ||
|
cc426e9897 | ||
|
e0e303b339 | ||
|
a88e2f93af | ||
|
e672a7fe99 | ||
|
986ae263d3 | ||
|
e445aeec14 | ||
|
b4afb10fab | ||
|
45b714fb9e | ||
|
10716d1101 | ||
|
35349dd8d7 | ||
|
f802fe1797 | ||
|
62d57e0a1a | ||
|
56b63c0e02 | ||
|
2b54bfd1d5 | ||
|
6ea7987a44 | ||
|
57f303223b | ||
|
73eeab9ace | ||
|
61908935f5 | ||
|
668a149898 | ||
|
55af72cb17 | ||
|
63e5bc9341 | ||
|
4f7523b896 | ||
|
59e558fba5 | ||
|
0d84aed89c | ||
|
fe21a7c25b | ||
|
e5f7b0b5c7 | ||
|
4b78ddbc8a | ||
|
1b4d955aec | ||
|
284bd76589 | ||
|
2dc8f8f2f7 | ||
|
df4ed389bf | ||
|
5f77b2f816 | ||
|
1891a03b11 | ||
|
73e081dd99 | ||
|
bcad0d4e51 | ||
|
9f16b329c5 | ||
|
01b629bd49 | ||
|
52d9855916 | ||
|
91429ce516 | ||
|
ddbdceba64 | ||
|
1ebfddf07e | ||
|
9b75e49b54 | ||
|
1c725879d8 | ||
|
4222b2aa21 | ||
|
f60e74fbd9 | ||
|
ac4b673c5a | ||
|
3d4442ab25 | ||
|
8b5a56a99c | ||
|
cf853a21ad | ||
|
6a511fdfcf | ||
|
e81c100e13 | ||
|
46cc04c798 | ||
|
9c41a9d2ac | ||
|
18e6b8cd7c | ||
|
d24c74e34a | ||
|
777d105602 | ||
|
06ececc1a5 | ||
|
5e5c111d25 | ||
|
d5b9dd34b7 | ||
|
1dd1cd69ac | ||
|
6ddf8291e9 | ||
|
dd8deab4e3 | ||
|
af44a63265 | ||
|
41db1464a2 | ||
|
d87e44ff03 | ||
|
47a2734ad4 | ||
|
48034eb6c9 | ||
|
ac07c8324d | ||
|
a02446c4cc | ||
|
a39ee32f93 | ||
|
9dcc23825a | ||
|
5e6fcd28b2 | ||
|
9e3072f89b | ||
|
96b87641ca | ||
|
9dd1e59421 | ||
|
1f58d8c335 | ||
|
de456fd097 | ||
|
8de343295d | ||
|
80161bf138 | ||
|
96caddfd71 | ||
|
1dc1b926c4 | ||
|
b0b19edf31 | ||
|
c3e5d0f6f2 | ||
|
fdccab3069 | ||
|
81cb43004b | ||
|
57165cbe48 | ||
|
211722d67b | ||
|
685bb9742e | ||
|
53c3fc16c1 | ||
|
b6936584fe | ||
|
3f5728d67a | ||
|
ddeb42ba55 | ||
|
4999fbd97c | ||
|
7045d242e8 | ||
|
73660c7bef | ||
|
e520a50de2 | ||
|
6f9be2eee9 | ||
|
0608c0afac | ||
|
b28ebf46b6 | ||
|
5f8c615981 | ||
|
f7ed574d04 | ||
|
a8c819e293 | ||
|
4cfa267b1b | ||
|
f31b996cb5 | ||
|
323b338cdd | ||
|
4b4880bf11 | ||
|
5289038dbd | ||
|
f4630f9808 | ||
|
d114ebf6fa | ||
|
7781dae836 | ||
|
72ad2c3261 | ||
|
e753246373 | ||
|
9e48d807cc | ||
|
d72c4e0e3f | ||
|
9353ae7415 | ||
|
74dec58e0d | ||
|
3fb26d3927 | ||
|
9199750afe | ||
|
bca21ec9b6 | ||
|
42c73c8658 | ||
|
88863262da | ||
|
d4a2502ec0 | ||
|
5136b70882 | ||
|
3c3594c52f | ||
|
3143356523 | ||
|
5b5e42361a | ||
|
22161c2264 | ||
|
9bef9f4332 | ||
|
f24aae546c | ||
|
dcf913336b | ||
|
7be808c2ff | ||
|
314aca3f2c | ||
|
4277aac974 | ||
|
ea583e0ff5 | ||
|
abb8b0f0cc | ||
|
c6ac4459a3 | ||
|
b559187722 | ||
|
50f3def2eb | ||
|
2f20c8e6da | ||
|
3f4a62d22c | ||
|
1f736a051e | ||
|
c0a8186d37 | ||
|
fc17f42f93 | ||
|
b6fb624c99 | ||
|
4e9b952116 | ||
|
a261c163a5 | ||
|
9169938448 | ||
|
5fe8f70eb1 | ||
|
61764a9a21 | ||
|
52c07e0895 | ||
|
7bf62b3dad | ||
|
690dc328c5 | ||
|
25f1afa628 | ||
|
e29bcde1d3 | ||
|
8ff4599e8a | ||
|
6c02a12e88 | ||
|
8fd5a5886b | ||
|
31d6c97e70 | ||
|
d3e5f6ebc0 | ||
|
aeac56c95d | ||
|
2aaaa71d85 | ||
|
500a1dc4c6 | ||
|
0d4e5c31e2 | ||
|
9a1d1594d6 | ||
|
cd440f6539 | ||
|
5bcd49b7ca | ||
|
2092f849f7 | ||
|
cc0b4e8174 | ||
|
6e9c6a14f7 | ||
|
f082420fc7 | ||
|
c5ea3cfce7 | ||
|
e7e4f75c86 | ||
|
3bbbf0d8e4 | ||
|
7f8994c1fd | ||
|
cb2644cdea | ||
|
dd12cacd16 | ||
|
4558f856b5 | ||
|
5e28d6f651 | ||
|
a16e34bcef | ||
|
822d9d72ea | ||
|
adc533070d | ||
|
7afe9e6481 | ||
|
efe2a06976 | ||
|
4c0b3fb7ae | ||
|
86caffb1c6 | ||
|
583de0d0e7 | ||
|
986eef9830 | ||
|
35c5d4e528 | ||
|
6f061adc1e | ||
|
a42c1fc581 | ||
|
59f7147271 | ||
|
879502c1e7 | ||
|
2c4f53e7b5 | ||
|
ab5b0cb1db | ||
|
5d34c8aef5 | ||
|
10264367aa | ||
|
25b549b034 | ||
|
30f723d9fc | ||
|
f79eaa4c8b | ||
|
91a1cc81a0 | ||
|
09ebbff30d | ||
|
f1d0e39d39 | ||
|
8a8880fca1 | ||
|
28bb5db04f | ||
|
21fec42172 | ||
|
a02504381a | ||
|
303026e01f | ||
|
b445d549db | ||
|
5331b5dc8a | ||
|
7e2ec3ea43 | ||
|
066f74ed46 | ||
|
cb1e254cb9 | ||
|
abe4bb6d54 | ||
|
cfba379777 | ||
|
0cb9dc2f0b | ||
|
9a3d239e6d | ||
|
024581de84 | ||
|
dfb8a85eed | ||
|
2f5e7778cc | ||
|
4e55126606 | ||
|
3cc8d982d5 | ||
|
bc2b3e9c4e | ||
|
3a5cd5d202 | ||
|
7385de8cf8 | ||
|
e6fcefd4d1 | ||
|
1f89e5a02a | ||
|
eb8cd900fa | ||
|
34df781699 | ||
|
81fe9aa699 | ||
|
3780321ae6 | ||
|
b2b4be5452 | ||
|
b56fb69eca | ||
|
2f7e34f66c | ||
|
2174eb8149 | ||
|
0a409c8c54 | ||
|
19c5aca404 | ||
|
d9d61e479d | ||
|
f2a96427a8 | ||
|
ca1c0e35b1 | ||
|
0e89bf90bc | ||
|
1f36fe5193 | ||
|
9aada5837f | ||
|
9667b82599 | ||
|
aae8707460 | ||
|
bfbdb9f058 | ||
|
76b8880972 | ||
|
6dc5906229 | ||
|
798099aaaf | ||
|
fa452e94b0 | ||
|
b8540a190b | ||
|
7e3c155e37 | ||
|
4f0935c494 | ||
|
606f2bd9bb | ||
|
b7b478eea8 | ||
|
480099ee97 | ||
|
2a5a780f3b | ||
|
3632a01e36 | ||
|
738c1d82c4 | ||
|
91266c831c | ||
|
4f1dbaa34b | ||
|
d81354e2a5 | ||
|
dbf995fd24 | ||
|
d8fd4c4767 | ||
|
a5fd33d08a | ||
|
d144831569 | ||
|
8f812d6a19 | ||
|
740f6548a2 | ||
|
b3ced2c4c5 | ||
|
cbc0b41773 | ||
|
b6d29d2980 | ||
|
e76c18c104 | ||
|
a98c275445 | ||
|
14ad73872b | ||
|
8155b1c365 | ||
|
00c029a65a | ||
|
24ed0ea186 | ||
|
865256f5c0 | ||
|
b2629d9718 | ||
|
00a1eaab28 | ||
|
44f77f3bb3 | ||
|
48e41d89cb | ||
|
bda15ef007 | ||
|
2c7b954a8d | ||
|
2d2ff5230c | ||
|
0a0382def2 | ||
|
bea3b5d720 | ||
|
956a80f082 | ||
|
94cf4699e6 | ||
|
2639548bdb | ||
|
9698c94ee2 | ||
|
2020d12217 | ||
|
2018a6d94a | ||
|
fb5fa8de7c | ||
|
9b0ac48cda | ||
|
78e1df28c1 | ||
|
41211137fe | ||
|
568f352cf6 | ||
|
1896732f17 | ||
|
57e845d0d7 | ||
|
1a3f100018 | ||
|
dbc7f460e4 | ||
|
a0f828a2da | ||
|
581a1e485b | ||
|
6c81cc32e4 | ||
|
3b6e93fc0e | ||
|
99ee1b887a | ||
|
36cf68a9f4 | ||
|
d2bc1e12c3 | ||
|
2d42882a4b | ||
|
c584da2ea9 | ||
|
42d838a0b3 | ||
|
a876d098fe | ||
|
c3ff146dd2 | ||
|
545d933e15 | ||
|
f1de1d4b77 | ||
|
27bc93936f | ||
|
778eb2be0e | ||
|
e4da7d2a69 | ||
|
7f6b6615ab | ||
|
6eb6527d11 | ||
|
0effbebae7 | ||
|
dc1abed9c6 | ||
|
7a961ca6f6 | ||
|
abef26974c | ||
|
8c368bda55 | ||
|
bcc2edd926 | ||
|
e8ba328a14 | ||
|
f248367ca3 | ||
|
c3479b23d9 | ||
|
37a89d280c | ||
|
5c356ab177 | ||
|
c520ad212a | ||
|
02b25d080f | ||
|
4af6955588 | ||
|
5e88edf22d | ||
|
248aec4885 | ||
|
fd9c8b86e9 | ||
|
13d8ec5768 | ||
|
f0c0949aa0 | ||
|
632d55f6af | ||
|
21e0a42060 | ||
|
f1eaa18831 | ||
|
912ea8017f | ||
|
c1e126985d | ||
|
fedc3b887f | ||
|
5d77a60939 | ||
|
da4c88f7ec | ||
|
c5e13f3f64 | ||
|
cf81bd038a | ||
|
1b8389c8b8 | ||
|
22ce54649d | ||
|
5120cf38f0 | ||
|
583c737106 | ||
|
ebe5f355d3 | ||
|
304aa38f24 | ||
|
a5b5dfdcb6 | ||
|
937c5d7e5f | ||
|
19c0100983 | ||
|
a1b943dbbc | ||
|
88df8501b3 | ||
|
06d5675d6f | ||
|
b15aabf1f8 | ||
|
36c627bd1b | ||
|
b369be9201 | ||
|
1c35ff4f3d | ||
|
2f9b247c46 | ||
|
8183849fe4 | ||
|
4bdfd9d164 | ||
|
4dccd4a053 | ||
|
2d3fb08638 | ||
|
81e52fb77c | ||
|
9181747939 | ||
|
00cc0483f7 | ||
|
890ae4bba9 | ||
|
b897c8db4a | ||
|
e1e472bf79 | ||
|
b3669afea3 | ||
|
0bc6f369b8 | ||
|
447c43c9d7 | ||
|
6067b2f913 | ||
|
4ec40d575e | ||
|
eab25c73e7 | ||
|
c94962aa3c | ||
|
f10833a516 | ||
|
814c62cc21 | ||
|
35a181e493 | ||
|
a1df95592c | ||
|
e6ccc53054 | ||
|
bb594ebc30 | ||
|
75c6e74d41 | ||
|
1966779ade | ||
|
adef199767 | ||
|
c695f73586 | ||
|
e8f4653a25 | ||
|
56ec7152d7 | ||
|
0576262bb9 | ||
|
230ef417b6 | ||
|
3eaff02c2f | ||
|
8533cff188 | ||
|
39e4a1cd99 | ||
|
77bde48a48 | ||
|
e9b8e5d4e9 | ||
|
610c9b9f24 | ||
|
a0706fcfc8 | ||
|
eb4e90c589 | ||
|
8203ebb835 | ||
|
d541558f15 | ||
|
43b06d7df5 | ||
|
064d331110 | ||
|
0921cd6b9c | ||
|
de77abe152 | ||
|
31c5036724 | ||
|
a27e7a139a | ||
|
2a9250c937 | ||
|
1b1f3e9e0c | ||
|
68e60ff9ef | ||
|
853cf20e6e | ||
|
ea36d34942 | ||
|
91862bff43 | ||
|
fadd92a6c3 | ||
|
9bff2762fc | ||
|
7b6603614f | ||
|
99e6ee2d4d | ||
|
03c3fa24e9 | ||
|
483ae22bc3 | ||
|
9396ca585c | ||
|
922007ea25 | ||
|
97b9d99503 | ||
|
e0be4dde31 | ||
|
4524f6dbfd | ||
|
52ecad1cdf | ||
|
9be3645152 | ||
|
a2d8a2a447 | ||
|
0ad96b6567 | ||
|
0c36d113bf | ||
|
410599b287 | ||
|
167b2d0fe4 | ||
|
11cc27d9c9 | ||
|
24f406917d | ||
|
5f64d4be7d | ||
|
b29f63f89e | ||
|
b4c7bb1f7b | ||
|
edbde2be37 | ||
|
1798959ddc | ||
|
d249000b52 | ||
|
58ada40586 | ||
|
f1a13f3093 | ||
|
aa3574f15f | ||
|
03e513a3e4 | ||
|
beca613745 | ||
|
dcca49e848 | ||
|
22cebe3176 | ||
|
dd2d292a56 | ||
|
c79bf951b7 | ||
|
e9107870c0 | ||
|
d50124e937 | ||
|
234baf6276 | ||
|
339dcaf506 | ||
|
5cdc3d1d18 | ||
|
8dcfe57888 | ||
|
2e35cd76ec | ||
|
51d2c9feba | ||
|
864ac3927f | ||
|
cb9e334a75 | ||
|
4f67e9f38b | ||
|
b4a401805f | ||
|
f2d5196890 | ||
|
c2ba3a773a | ||
|
3edc456fe7 | ||
|
5dbe2ebf29 | ||
|
6369ca6483 | ||
|
2b40fc7e76 | ||
|
52537904f7 | ||
|
1bf6d6d9f2 | ||
|
43dae79018 | ||
|
141850eda5 | ||
|
affdbb561f | ||
|
8f118f140f | ||
|
5b68a438f3 | ||
|
3f9cb7d575 | ||
|
85d1a008dd | ||
|
f0b20b67de | ||
|
e082138c18 | ||
|
9a647b96df | ||
|
3995238d77 | ||
|
bd9c4d28ca | ||
|
6f79f75f71 | ||
|
731ebc2eda | ||
|
635df947c5 | ||
|
63b34d9851 | ||
|
f9ae7ea949 | ||
|
df85218fa7 | ||
|
a4fe78f8ba | ||
|
4bc2d7f6ac | ||
|
5b2fff07b8 | ||
|
0517f1bbbc | ||
|
f49f9c1f96 | ||
|
f9cb6d783e | ||
|
600c9c6251 | ||
|
348805af74 | ||
|
57b299a9cd | ||
|
a662677f06 | ||
|
5fa158a37f | ||
|
c49f898886 | ||
|
81921bcd77 | ||
|
18fa925855 | ||
|
04a5d9694e | ||
|
6296cb8218 | ||
|
a4ab898933 | ||
|
ed8f5ddc33 | ||
|
65402eaa23 | ||
|
7e116f8b1f | ||
|
999c1b1105 | ||
|
0b535e98b8 | ||
|
4ecf2b9f2d | ||
|
5012237eb3 | ||
|
8d121bf38f | ||
|
d0650e6910 | ||
|
1e251c09e3 | ||
|
08a1eaad62 | ||
|
b4e29bf157 | ||
|
97d75aef73 | ||
|
25c461bd3f | ||
|
ca73a3a7b7 | ||
|
3816d7b202 | ||
|
008fcece04 | ||
|
70fa93f0c9 | ||
|
703965aec8 | ||
|
6fe4232f75 | ||
|
51e0c9130a | ||
|
85de505553 | ||
|
1d38109dcf | ||
|
6f0f420063 | ||
|
63ecca1fe2 | ||
|
b41bf7cf4e | ||
|
95a3021e53 | ||
|
b8939b8ccb | ||
|
8d6eaf3da4 | ||
|
18c74ecf09 | ||
|
f285110773 | ||
|
7f8df9533b | ||
|
e4cb916dff | ||
|
0b0b125bca | ||
|
c4e6b565a5 | ||
|
6e4236d5ee | ||
|
5c6343e85e | ||
|
50924a0672 | ||
|
b167eb9e22 | ||
|
fae364f079 | ||
|
2149bee87f | ||
|
08bee074ac | ||
|
ec8d30a19d | ||
|
31242e23eb | ||
|
9611e31481 | ||
|
5d1fff3df6 | ||
|
1dd747a24d | ||
|
cc3e27eca1 | ||
|
77f0fe5b80 | ||
|
5c316b50fa | ||
|
e8dd2e2b4d | ||
|
81f1bfd23f | ||
|
819b92a1d5 | ||
|
eaadb1c5c2 | ||
|
6a1b230c7e | ||
|
8a3547ecd0 | ||
|
4f458ce93a | ||
|
59af85ce1c | ||
|
dfa25a1796 | ||
|
2a16d24760 | ||
|
4657aef306 | ||
|
9ee2c2e9c1 | ||
|
02dca52948 | ||
|
5b954a3584 | ||
|
0f15d2d45b | ||
|
6a9f4a57b9 | ||
|
c8dace09b7 | ||
|
92ddb2b791 | ||
|
30c76eb420 | ||
|
579f43ed1c | ||
|
6e6e5f4747 | ||
|
421cda2ef0 | ||
|
0a742b867f | ||
|
82032133f1 | ||
|
b4c3985b61 | ||
|
19363f6cb9 | ||
|
d1d14bca13 | ||
|
89d7a05ea7 | ||
|
a9ebd2f80e | ||
|
d0637750e7 | ||
|
29f045636a | ||
|
2f3ded734f | ||
|
658701a943 | ||
|
61f164b5ff | ||
|
7f50d84953 | ||
|
19ff0fd618 | ||
|
d82eb373a6 | ||
|
edb46f480f | ||
|
a1f6220f33 | ||
|
410f08a317 | ||
|
e9a12b2f4e | ||
|
2997522c52 | ||
|
a87580663e | ||
|
b5d93f477a | ||
|
2c1a6ca67c | ||
|
84098ae933 | ||
|
e2f0162e5a | ||
|
59ae661f62 | ||
|
c4fbc8373d | ||
|
abb78d1d6b | ||
|
6b5aa9d06e | ||
|
6e78ec11fb | ||
|
7be3718a36 | ||
|
2c54627926 | ||
|
9d710801d8 | ||
|
3fceee8f2b | ||
|
3eb8f56b00 | ||
|
dbf964e430 | ||
|
3f406dcdf0 | ||
|
12a37e5342 | ||
|
47d4561bf8 | ||
|
93d1a264da | ||
|
f88f360404 | ||
|
83f689d9eb | ||
|
b1769153c7 | ||
|
1239026a8f | ||
|
575e21a1f5 | ||
|
96ddd5f4e5 | ||
|
15c81749b8 | ||
|
0873d3eaf9 | ||
|
d81151efa2 | ||
|
b84e6e07d1 | ||
|
c9f9bcfc86 | ||
|
6c1ba0aa13 | ||
|
df4ca46c02 | ||
|
dac9c091b2 | ||
|
e29902ed66 | ||
|
b3cdfe4fdf | ||
|
d265179f46 | ||
|
54d52a2986 | ||
|
4f80085fa3 | ||
|
c9c4f30637 | ||
|
1c095415a4 | ||
|
19acc05516 | ||
|
2cbde8b55c | ||
|
15cf87ef01 | ||
|
57bf499ce9 | ||
|
4fbbd34db9 | ||
|
9b04e43b4c | ||
|
5848ec498b | ||
|
5a570ff283 | ||
|
1dd6fa4b6a | ||
|
32377fd131 | ||
|
2f294b9f2b | ||
|
0b8295926e | ||
|
76507963d7 | ||
|
27ce0b5eb7 | ||
|
2797b4bd89 | ||
|
e9ba20f13c | ||
|
1ef68a34fe | ||
|
4189da19b1 | ||
|
ad3f8e7bf4 | ||
|
0dfe46a162 | ||
|
edeb804128 | ||
|
b49f234185 | ||
|
85a63b3bb3 | ||
|
04a0d012fc | ||
|
eaa33f2544 | ||
|
d5004d79bd | ||
|
96be2ebf35 | ||
|
fc90843207 | ||
|
679d40b3ce | ||
|
5307ee5d17 | ||
|
dd753498fa | ||
|
30e5a31591 | ||
|
2befd69c31 | ||
|
995b4d07ff | ||
|
788bbe969f | ||
|
99a89a7329 | ||
|
f8b5fe8b1e | ||
|
368cba1149 | ||
|
dac6639cc1 | ||
|
345a0e04fc | ||
|
82182015e2 | ||
|
384e8d43a6 | ||
|
b203b22d4d | ||
|
b9ea0061d5 | ||
|
e9afb39a4a | ||
|
df55a66153 | ||
|
6df0c2acf3 | ||
|
046bf20592 | ||
|
2879e2701f | ||
|
7771ecb0d0 | ||
|
cc9f452699 | ||
|
a8fb2d9bee | ||
|
165997c791 | ||
|
22e3c25e12 | ||
|
495b7eebbf | ||
|
4c39a32be1 | ||
|
584d7961eb | ||
|
977421edb7 | ||
|
f0b48aabff | ||
|
191c180946 | ||
|
03fe8f88f9 | ||
|
bb47712696 | ||
|
f466ff3243 | ||
|
306cf51da4 | ||
|
5857823c3b | ||
|
02418d2d97 | ||
|
0569b706dc | ||
|
bfda5a5492 | ||
|
03e5ca054d | ||
|
3e29d39f05 | ||
|
01f14b461f | ||
|
edb79d8c53 | ||
|
db71b87309 | ||
|
8f426a0184 | ||
|
773d42b886 | ||
|
1b7c51ebde | ||
|
e2d4f9dc8c | ||
|
0a9b595f80 | ||
|
86e9974410 | ||
|
423f88a458 | ||
|
9bc3e4e47c | ||
|
1838ca1994 | ||
|
2411006fdb | ||
|
5421925d6c | ||
|
d9b2b9f753 | ||
|
a081f78bd8 | ||
|
5275e0c904 | ||
|
2903167fad | ||
|
a25cb024fe | ||
|
6203d82f48 | ||
|
21ebe0c148 | ||
|
43510d5626 | ||
|
3cd97b0772 | ||
|
bd72f0eeb0 | ||
|
565327fe1e | ||
|
2e5bbf0445 | ||
|
ec6b6ab8d4 | ||
|
654a34f932 | ||
|
3d1312c13a | ||
|
52cf684444 | ||
|
54c37e945b | ||
|
ecab8a6cb4 | ||
|
b1ab1451ec | ||
|
b84fbed800 | ||
|
ccf66e6343 | ||
|
5e8fc39735 | ||
|
cd1a8c9224 | ||
|
ae4a9a7b14 | ||
|
6ae1b7a72b | ||
|
d8380a390a | ||
|
5fbb232d69 | ||
|
25d818ecbd | ||
|
5dd9a87dc9 | ||
|
857ce2c577 | ||
|
902acc44a2 | ||
|
22a0bf9451 | ||
|
ad95be2500 | ||
|
071b901f87 | ||
|
ad9382a98c | ||
|
49c26f3810 | ||
|
86c7a7a058 | ||
|
0178c95f6f | ||
|
440ecfc991 | ||
|
e0b5df97c4 | ||
|
e3f760a9dd | ||
|
c9783640c0 | ||
|
f4d1e2a46e | ||
|
b52f71dcce | ||
|
158aa79aed | ||
|
3fe2b21686 | ||
|
dfbeb41afa | ||
|
3d4054f10e | ||
|
6c6f914655 | ||
|
3de5cdd7e8 | ||
|
eb6e987f55 | ||
|
10f27f41d1 | ||
|
3d83d1d5b7 | ||
|
cfd6a26881 | ||
|
e92d4a3dbf | ||
|
1189734c62 | ||
|
a78bb82b44 | ||
|
3a8383ab79 | ||
|
dc1c477d62 | ||
|
e9c459690f | ||
|
43de36b331 | ||
|
39567cf278 | ||
|
0033090a93 | ||
|
ef8c347a95 | ||
|
535ec8c42d | ||
|
66911160c0 | ||
|
d861c59f27 | ||
|
938f89cd1a | ||
|
acbd01407d | ||
|
254339af34 | ||
|
6c9cabfb57 | ||
|
359277deb5 | ||
|
11e7ca76e1 | ||
|
dc1121b72a | ||
|
323493962a | ||
|
751c0723dc | ||
|
23b08173ff | ||
|
9f15e67856 | ||
|
2c84c93f3d | ||
|
bfdd071001 | ||
|
ab9aa28fe4 | ||
|
61995bb33f | ||
|
38b63b07f5 | ||
|
d185e143a8 | ||
|
4e1630c17d | ||
|
2214f284fa | ||
|
6e562bd1ff | ||
|
2d774f32b2 | ||
|
5ab449d8a1 | ||
|
124658a006 | ||
|
57de6229f9 | ||
|
e162df67fa | ||
|
dae09d58d7 | ||
|
6dd09858d4 | ||
|
985c2bcfb1 | ||
|
fc7a4538e9 | ||
|
72c672c2c2 | ||
|
77c8bc8eb5 | ||
|
1edc2b08f2 | ||
|
d34f7d3e1a | ||
|
eaca6ddaf0 | ||
|
aa89a74901 | ||
|
599904d589 | ||
|
ba302e4aa0 | ||
|
0f1a0963fd | ||
|
2b3055a84a | ||
|
1fed8ba4f7 | ||
|
5827f9638f | ||
|
6ad786f348 | ||
|
0cc048cb64 | ||
|
cb8d9ac71b | ||
|
b24509ef43 | ||
|
7553efc308 | ||
|
9fb4b3425e | ||
|
182216e711 | ||
|
52891898d2 | ||
|
f6c29c61df | ||
|
17a5d629ea | ||
|
688551ffa6 | ||
|
ece34dc337 | ||
|
179de92231 | ||
|
c4c962aeb9 | ||
|
d0182c47de | ||
|
0d30d8244f | ||
|
2e67ebd881 | ||
|
d62cdffcc0 | ||
|
77a28c7131 | ||
|
c60d4e5bb9 | ||
|
990e08ba2d | ||
|
bf0cf10403 | ||
|
7a3869c476 | ||
|
e74090139c | ||
|
09fc42a787 | ||
|
c0e5d7efae | ||
|
66ec17620f | ||
|
51e2a3afcf | ||
|
abc1555c19 | ||
|
6c29879f12 | ||
|
2f8f6844fe | ||
|
dc4a6e862b | ||
|
4bf91c7772 | ||
|
004baf7b87 | ||
|
6c8bd06308 | ||
|
65b4551864 | ||
|
7f55d108cf | ||
|
c9a9dab523 | ||
|
f149fc9aaa | ||
|
7aeb098212 | ||
|
c76c76410c | ||
|
5cc6a2dca6 | ||
|
0bdd350b8d | ||
|
9a5e667674 | ||
|
c9d8d7ba64 | ||
|
17367f2b17 | ||
|
6362f76812 | ||
|
74d627f674 | ||
|
599261dc31 | ||
|
d19c151669 | ||
|
b30d7ad51c | ||
|
4fdc533bd7 | ||
|
d17ac770e6 | ||
|
70071312d3 | ||
|
ec23356c99 | ||
|
98cc969d18 | ||
|
dc5deaf47c | ||
|
d81c94de0b | ||
|
e2e32da4e6 | ||
|
0480acebcd | ||
|
dbac2704f3 | ||
|
f8d90c1933 | ||
|
3dbdd1258e | ||
|
f92249790f | ||
|
23951b8c8a | ||
|
af38971a01 | ||
|
ed6417ebcd | ||
|
2fa038f91d | ||
|
31beb05aa1 | ||
|
4cf6a1f7da | ||
|
180dcefe40 | ||
|
307e666c27 | ||
|
b605b940eb | ||
|
967a084aad | ||
|
4d08e3e9be | ||
|
da34517943 | ||
|
7ffe3d21f8 | ||
|
ea01247bcf | ||
|
786b87952e | ||
|
26ddfd1a08 | ||
|
6f3947226f | ||
|
42fd317188 | ||
|
2411b17279 | ||
|
ab460199ab | ||
|
3a3ff89047 | ||
|
a5acc7872d | ||
|
2688d980c1 | ||
|
3abee9e37a | ||
|
5b63d0ff40 | ||
|
a96aad6073 | ||
|
02bdd56e20 | ||
|
b5a6257352 | ||
|
408a1fe846 | ||
|
44ba1cba59 | ||
|
96df705409 | ||
|
40f95bf842 | ||
|
9d45c0a018 | ||
|
470da71b3b | ||
|
92ab17b58b | ||
|
8c00f38a1f | ||
|
89705391d1 | ||
|
bbe52886da | ||
|
ef68dd07ac | ||
|
f2cae64b0d | ||
|
868778b079 | ||
|
e681f1f36f | ||
|
1832145645 | ||
|
af4f60a4af | ||
|
fa147f71a3 | ||
|
902676262a |
90
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
90
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: Bug Report 🐛
|
||||
description: Report something that's not working the intended way. Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!
|
||||
title: '[BUG] <title>'
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Where are you running SillyTavern?
|
||||
options:
|
||||
- Self-Hosted (Bare Metal)
|
||||
- Self-Hosted (Docker)
|
||||
- Android (Termux)
|
||||
- Cloud Service (Static)
|
||||
- Other (Specify below)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: system
|
||||
attributes:
|
||||
label: System
|
||||
description: >-
|
||||
For deployment issues, specify your [distro or OS](https://whatsmyos.com/) and/ or Docker version.
|
||||
For client-side issues, include your [browser version](https://www.whatsmybrowser.org/)
|
||||
placeholder: e.g. Firefox 101, Manjaro Linux 21.3.0, Docker 20.10.16
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of SillyTavern are you running?
|
||||
placeholder: (check User Settings to see the version)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desktop
|
||||
attributes:
|
||||
label: Desktop Information
|
||||
description: Please provide details about your desktop environment.
|
||||
placeholder: |
|
||||
- Node.js version (if applicable): [run `node --version` in cmd]
|
||||
- Generation API [e.g. KoboldAI, OpenAI]
|
||||
- Branch [staging, release]
|
||||
- Model [e.g. Pygmalion 6b, LLaMa 13b]
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Describe the problem
|
||||
description: Please describe exactly what is not working, include the steps to reproduce, actual result and expected result
|
||||
placeholder: When doing ABC then DEF, I expect to see XYZ, but I actually see ZYX
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Logs? Screenshots? Yes, please.
|
||||
placeholder: If the issue happens during build-time, include terminal logs. For run-time errors, include browser logs which you can view in the Dev Tools (F12), under the Console tab. Take care to blank out any personal info.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: idiot-check
|
||||
attributes:
|
||||
label: Please tick the boxes
|
||||
description: Before submitting, please ensure that
|
||||
options:
|
||||
- label: You have explained the issue clearly, and included all relevant info
|
||||
required: true
|
||||
- label: You've checked that this [issue hasn't already been raised](https://github.com/SillyTavern/SillyTavern/issues?q=is%3Aissue)
|
||||
required: true
|
||||
- label: You've checked the [docs](https://docs.sillytavern.app/) 
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Thanks 🙏
|
||||
Thank you for raising this ticket - in doing so you are helping to make SillyTavern better for everyone.
|
||||
validations:
|
||||
required: false
|
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,45 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: "Create a report to help us improve. PAY ATTENTION: Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!"
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
> **Warning**. Complete **all** the fields below. Otherwise, your bug report will be **ignored**!
|
||||
|
||||
**Have you searched for similar [bugs](https://github.com/SillyTavern/SillyTavern/issues?q=)?**
|
||||
Yes/No
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
|
||||
Providing the logs from the browser DevTools console (opened by pressing the F12 key) or SillyTavern command line window will be highly appreciated.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS/Device: [e.g. Windows 11]
|
||||
- Environment: [cloud, local]
|
||||
- Node.js version (if applicable): [run `node --version` in cmd]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Generation API [e.g. KoboldAI, OpenAI]
|
||||
- Branch [staging, release]
|
||||
- Model [e.g. Pygmalion 6b, LLaMa 13b]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
91
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
91
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: Feature Request ✨
|
||||
description: Suggest an idea for future development of this project
|
||||
title: '[FEATURE_REQUEST] <title>'
|
||||
labels: ['enhancement']
|
||||
|
||||
body:
|
||||
|
||||
# Field 1 - Did the user searched for similar requests
|
||||
- type: dropdown
|
||||
id: similarRequest
|
||||
attributes:
|
||||
label: Have you searched for similar requests?
|
||||
description:
|
||||
options:
|
||||
- 'No'
|
||||
- 'Yes'
|
||||
validations:
|
||||
required: false
|
||||
|
||||
# Field 2 - Is it bug-related
|
||||
- type: textarea
|
||||
id: issue
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? If so, please describe.
|
||||
description:
|
||||
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: false
|
||||
|
||||
# Field 3 - Describe feature
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
placeholder: An outline of how you would like this to be implemented, include as much details as possible
|
||||
validations:
|
||||
required: true
|
||||
|
||||
# Field 4 - Describe alternatives
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
placeholder: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
# Field 5 - Additional context
|
||||
- type: textarea
|
||||
id: addcontext
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
# Field 6 - Priority
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How urgent is the development of this feature
|
||||
options:
|
||||
- Low (Nice-to-have)
|
||||
- Medium (Would be very useful)
|
||||
- High (The app does not function without it)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
# Field 7 - Can the user implement
|
||||
- type: dropdown
|
||||
id: canImplement
|
||||
attributes:
|
||||
label: Is this something you would be keen to implement?
|
||||
description: Are you raising this ticket in order to get an issue number for your PR?
|
||||
options:
|
||||
- 'No'
|
||||
- 'Maybe'
|
||||
- 'Yes!'
|
||||
validations:
|
||||
required: false
|
||||
|
||||
# Final text
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Thanks 🙏
|
||||
Thank you for your feature suggestion.
|
||||
Please note that there is no guarantee that your idea will be implemented.
|
||||
validations:
|
||||
required: false
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature Request] "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Have you searched for similar [requests](https://github.com/SillyTavern/SillyTavern/issues?q=)?**
|
||||
Yes/No
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
17
.github/readme-zh_cn.md
vendored
17
.github/readme-zh_cn.md
vendored
@@ -1,8 +1,8 @@
|
||||
[English](readme.md) | 中文
|
||||
|
||||

|
||||

|
||||
|
||||
移动设备界面友好,多种人工智能服务或模型支持(KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale),类似 Galgame 的 老 婆 模 式,Horde SD,文本系统语音生成,世界信息(Lorebooks),可定制的界面,自动翻译,和比你所需要的更多的 Prompt。附带扩展服务,支持文本绘画生成与语音生成和基于向量数据库 ChromaDB 的聊天信息总结。
|
||||
移动设备界面友好,多种人工智能服务或模型支持(KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale),类似 Galgame 的 老 婆 模 式,Horde SD,文本系统语音生成,世界信息(Lorebooks),可定制的界面,自动翻译,和比你所需要的更多的 Prompt。附带扩展服务,支持文本绘画生成与语音生成和基于向量数据库 的聊天信息总结。
|
||||
|
||||
基于 TavernAI 1.2.8 的分叉版本
|
||||
|
||||
@@ -41,8 +41,6 @@ SillyTavern 本身并无用处,因为它只是一个用户聊天界面。你
|
||||
|
||||
<https://rentry.org/STAI-Termux>
|
||||
|
||||
Termux 不支持**.Webp 字符卡的导入/导出。请使用 JSON 或 PNG 格式**。
|
||||
|
||||
## 有问题或建议?
|
||||
|
||||
### 我们现在有了 Discord 社区
|
||||
@@ -83,7 +81,6 @@ SillyTavern 支持扩展服务,一些额外的人工智能模块可通过 [Sil
|
||||
* 在聊天窗口发送图片,并由人工智能解释图片内容
|
||||
* 文本图像生成(5 预设,以及 "自由模式")
|
||||
* 聊天信息的文字转语音(通过 ElevenLabs、Silero 或操作系统的语音生成)
|
||||
* ChromaDB 向量数据库,用于更智能的聊天 Prompt
|
||||
|
||||
扩展服务的完整功能介绍和使用教程,请参阅 [Docs](https://docs.sillytavern.app/extras/extensions/)。
|
||||
|
||||
@@ -173,7 +170,7 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。
|
||||
|
||||
如果要想通过点击 API 输入框旁边的按钮来查看密钥,请按照以下设置:
|
||||
|
||||
1. 打开 `config.conf` 文件,将里面的 `allowKeysExposure` 设置为 `true`。
|
||||
1. 打开 `config.yaml` 文件,将里面的 `allowKeysExposure` 设置为 `true`。
|
||||
2. 然后重启 SillyTavern 服务。
|
||||
|
||||
## 远程访问
|
||||
@@ -210,7 +207,7 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。
|
||||
|
||||
然后,文件中设置的 IP 就可以访问 SillyTavern 了。
|
||||
|
||||
*注意:"config.conf" 文件内也有一个 "whitelist" 设置,你可以用同样的方法设置它,但如果 "whitelist.txt" 文件存在,这个设置将被忽略。
|
||||
*注意:"config.yaml" 文件内也有一个 "whitelist" 设置,你可以用同样的方法设置它,但如果 "whitelist.txt" 文件存在,这个设置将被忽略。
|
||||
|
||||
### 2.获取 SillyTavern 服务的 IP 地址
|
||||
|
||||
@@ -236,19 +233,19 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。
|
||||
|
||||
### 向所有 IP 开放您的 SillyTavern 服务
|
||||
|
||||
我们不建议这样做,但您可以打开 `config.conf` 并将里面的 `whitelist` 设置改为 `false`。
|
||||
我们不建议这样做,但您可以打开 `config.yaml` 并将里面的 `whitelistMode` 设置改为 `false`。
|
||||
|
||||
你必须删除(或重命名)SillyTavern 文件夹中的 `whitelist.txt` 文件(如果有的话)。
|
||||
|
||||
这通常是不安全的做法,所以我们要求在这样做时必须设置用户名和密码。
|
||||
|
||||
用户名和密码在`config.conf`文件中设置。
|
||||
用户名和密码在`config.yaml`文件中设置。
|
||||
|
||||
重启 SillyTavern 服务后,只要知道用户名和密码,任何设备都可以访问。
|
||||
|
||||
### 还是无法访问?
|
||||
|
||||
* 为 `config.conf` 文件中的端口创建一条入站/出站防火墙规则。切勿将此误认为是路由器上的端口转发,否则,有人可能会发现你的聊天隐私,那就大错特错了。
|
||||
* 为 `config.yaml` 文件中的端口创建一条入站/出站防火墙规则。切勿将此误认为是路由器上的端口转发,否则,有人可能会发现你的聊天隐私,那就大错特错了。
|
||||
* 在 "设置" > "网络和 Internet" > "以太网" 中启用 "专用网络" 配置。这对 Windows 11 非常重要,否则即使添加了上述防火墙规则也无法连接。
|
||||
|
||||
### 性能问题?
|
||||
|
37
.github/readme.md
vendored
37
.github/readme.md
vendored
@@ -1,15 +1,19 @@
|
||||
English | [中文](readme-zh_cn.md)
|
||||
|
||||

|
||||

|
||||
|
||||
Mobile-friendly, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale), VN-like Waifu Mode, Horde SD, System TTS, WorldInfo (lorebooks), customizable UI, auto-translate, and more prompt options than you'd ever want or need. Optional Extras server for more SD/TTS options + ChromaDB/Summarize.
|
||||
Mobile-friendly layout, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale), VN-like Waifu Mode, Stable Diffusion, TTS, WorldInfo (lorebooks), customizable UI, auto-translate, and more prompt options than you'd ever want or need + ability to install third-party extensions.
|
||||
|
||||
Based on a fork of TavernAI 1.2.8
|
||||
Based on a fork of [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8
|
||||
|
||||
## Important news!
|
||||
|
||||
1. We have created a [Documentation website](https://docs.sillytavern.app/) to answer most of your questions and help you get started.
|
||||
|
||||
2. Missing extensions after the update? Since the 1.10.6 release version, most of the previously built-in extensions have been converted to downloadable add-ons. You can download them via the built-in "Download Extensions and Assets" menu in the extensions panel (stacked blocks icon in the top bar).
|
||||
|
||||
### Brought to you by Cohee, RossAscends, and the SillyTavern community
|
||||
|
||||
NOTE: We have created a [Documentation website](https://docs.sillytavern.app/) to answer most of your questions and help you get started.
|
||||
|
||||
### What is SillyTavern or TavernAI?
|
||||
|
||||
SillyTavern is a user interface you can install on your computer (and Android phones) that allows you to interact with text generation AIs and chat/roleplay with characters you or the community create.
|
||||
@@ -41,8 +45,6 @@ Since Tavern is only a user interface, it has tiny hardware requirements, it wil
|
||||
|
||||
<https://rentry.org/STAI-Termux>
|
||||
|
||||
**.webp character cards import/export is not supported in Termux. Use either JSON or PNG formats instead.**
|
||||
|
||||
## Questions or suggestions?
|
||||
|
||||
### We now have a community Discord server
|
||||
@@ -71,7 +73,6 @@ Get in touch with the developers directly:
|
||||
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
||||
* [AI Horde](https://horde.koboldai.net/) connection
|
||||
* Prompt generation formatting tweaking
|
||||
* webp character card interoperability (PNG is still an internal format)
|
||||
|
||||
## Extensions
|
||||
|
||||
@@ -83,7 +84,6 @@ SillyTavern has extensibility support, with some additional AI modules hosted vi
|
||||
* Sending images to chat, and the AI interpreting the content
|
||||
* Stable Diffusion image generation (5 chat-related presets plus 'free mode')
|
||||
* Text-to-speech for AI response messages (via ElevenLabs, Silero, or the OS's System TTS)
|
||||
* ChromaDB vector storage for smarter chat prompt formatting
|
||||
|
||||
A full list of included extensions and tutorials on how to use them can be found in the [Docs](https://docs.sillytavern.app/extras/extensions/).
|
||||
|
||||
@@ -162,8 +162,10 @@ Installing via ZIP download (discouraged)
|
||||
|
||||
### Linux
|
||||
|
||||
1. Run the `start.sh` script.
|
||||
2. Enjoy.
|
||||
1. Ensure you have Node.js v18 or higher (the latest [LTS version](https://nodejs.org/en/download/) is recommended) installed by running `node -v`.
|
||||
Alternatively, use the [Node Version Manager](https://github.com/nvm-sh/nvm#installing-and-updating) script to quickly and easily manage your Node installations.
|
||||
2. Run the `start.sh` script.
|
||||
3. Enjoy.
|
||||
|
||||
## API keys management
|
||||
|
||||
@@ -173,7 +175,7 @@ By default, they will not be exposed to a frontend after you enter them and relo
|
||||
|
||||
In order to enable viewing your keys by clicking a button in the API block:
|
||||
|
||||
1. Set the value of `allowKeysExposure` to `true` in `config.conf` file.
|
||||
1. Set the value of `allowKeysExposure` to `true` in `config.yaml` file.
|
||||
2. Restart the SillyTavern server.
|
||||
|
||||
## Remote connections
|
||||
@@ -211,7 +213,7 @@ CIDR masks are also accepted (eg. 10.0.0.0/24).
|
||||
|
||||
Now devices which have the IP specified in the file will be able to connect.
|
||||
|
||||
*Note: `config.conf` also has a `whitelist` array, which you can use in the same way, but this array will be ignored if `whitelist.txt` exists.*
|
||||
*Note: `config.yaml` also has a `whitelist` array, which you can use in the same way, but this array will be ignored if `whitelist.txt` exists.*
|
||||
|
||||
### 2. Getting the IP for the ST host machine
|
||||
|
||||
@@ -223,7 +225,7 @@ If the ST-hosting device is on the same wifi network, you will use the ST-host's
|
||||
|
||||
If you (or someone else) want to connect to your hosted ST while not being on the same network, you will need the public IP of your ST-hosting device.
|
||||
|
||||
* While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for for `IPv4`. This is what you would use to connect from the remote device.
|
||||
* While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for `IPv4`. This is what you would use to connect from the remote device.
|
||||
|
||||
### 3. Connect the remote device to the ST host machine
|
||||
|
||||
@@ -237,19 +239,19 @@ Use http:// NOT https://
|
||||
|
||||
### Opening your ST to all IPs
|
||||
|
||||
We do not recommend doing this, but you can open `config.conf` and change `whitelist` to `false`.
|
||||
We do not recommend doing this, but you can open `config.yaml` and change `whitelistMode` to `false`.
|
||||
|
||||
You must remove (or rename) `whitelist.txt` in the SillyTavern base install folder if it exists.
|
||||
|
||||
This is usually an insecure practice, so we require you to set a username and password when you do this.
|
||||
|
||||
The username and password are set in `config.conf`.
|
||||
The username and password are set in `config.yaml`.
|
||||
|
||||
After restarting your ST server, any device will be able to connect to it, regardless of their IP as long as they know the username and password.
|
||||
|
||||
### Still Unable To Connect?
|
||||
|
||||
* Create an inbound/outbound firewall rule for the port found in `config.conf`. Do NOT mistake this for port-forwarding on your router, otherwise, someone could find your chat logs and that's a big no-no.
|
||||
* Create an inbound/outbound firewall rule for the port found in `config.yaml`. Do NOT mistake this for port-forwarding on your router, otherwise, someone could find your chat logs and that's a big no-no.
|
||||
* Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise, you would be unable to connect even with the aforementioned firewall rules.
|
||||
|
||||
## Performance issues?
|
||||
@@ -295,6 +297,7 @@ GNU Affero General Public License for more details.**
|
||||
* RossAscends' additions: AGPL v3
|
||||
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
||||
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
||||
* city_unit's extensions and various QoL features (<https://github.com/city-unit>)
|
||||
* StefanDanielSchwarz's various commits and bug reports (<https://github.com/StefanDanielSchwarz>)
|
||||
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
||||
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
||||
|
45
.github/workflows/docker-publish.yml
vendored
Normal file
45
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# This workflow will publish a docker image for every full release to the GitHub package repository
|
||||
|
||||
name: Create Docker Image on Release
|
||||
|
||||
on:
|
||||
release:
|
||||
# Only runs on full releases not pre releases
|
||||
types: [released]
|
||||
|
||||
env:
|
||||
# This should allow creation of docker images even in forked repositories
|
||||
# Image name may not contain uppercase characters, so we can not use the repository name
|
||||
# Creates a string like: ghcr.io/SillyTavern/sillytavern
|
||||
image_name: ghcr.io/sillytavern/sillytavern
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Build docker image using dockerfile and tag it with branch name
|
||||
# Assumes branch name is the version number
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
docker build . --file Dockerfile --tag $image_name:${{ github.ref_name }}
|
||||
|
||||
# Login into package repository as the person who created the release
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Assumes release is the latest and marks image as such
|
||||
- name: Docker Tag and Push
|
||||
run: |
|
||||
docker tag $image_name:${{ github.ref_name }} $image_name:latest
|
||||
docker push $image_name:${{ github.ref_name }}
|
||||
docker push $image_name:latest
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -20,12 +20,15 @@ public/stats.json
|
||||
/uploads/
|
||||
*.jsonl
|
||||
/config.conf
|
||||
/config.yaml
|
||||
/config.conf.bak
|
||||
/docker/config
|
||||
.DS_Store
|
||||
public/settings.json
|
||||
/thumbnails
|
||||
whitelist.txt
|
||||
.vscode
|
||||
.idea/
|
||||
secrets.json
|
||||
/dist
|
||||
/backups/
|
||||
@@ -35,3 +38,6 @@ content.log
|
||||
cloudflared.exe
|
||||
public/assets/
|
||||
access.log
|
||||
/vectors/
|
||||
/cache/
|
||||
public/css/user.css
|
||||
|
19
Dockerfile
19
Dockerfile
@@ -13,7 +13,7 @@ ENTRYPOINT [ "tini", "--" ]
|
||||
WORKDIR ${APP_HOME}
|
||||
|
||||
# Install app dependencies
|
||||
COPY package*.json ./
|
||||
COPY package*.json post-install.js ./
|
||||
RUN \
|
||||
echo "*** Install npm packages ***" && \
|
||||
npm install && npm cache clean --force
|
||||
@@ -23,18 +23,19 @@ COPY . ./
|
||||
|
||||
# Copy default chats, characters and user avatars to <folder>.default folder
|
||||
RUN \
|
||||
IFS="," RESOURCES="characters,chats,groups,group chats,User Avatars,worlds" && \
|
||||
IFS="," RESOURCES="assets,backgrounds,user,context,instruct,QuickReplies,movingUI,themes,characters,chats,groups,group chats,User Avatars,worlds,OpenAI Settings,NovelAI Settings,KoboldAI Settings,TextGen Settings" && \
|
||||
\
|
||||
echo "*** Store default $RESOURCES in <folder>.default ***" && \
|
||||
for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done && \
|
||||
for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done || true && \
|
||||
\
|
||||
echo "*** Create symbolic links to config directory ***" && \
|
||||
for R in $RESOURCES; do ln -s "../config/$R" "public/$R"; done && \
|
||||
# rm "config.conf" "public/settings.json" "public/css/bg_load.css" && \
|
||||
ln -s "./config/config.conf" "config.conf" && \
|
||||
ln -s "../config/settings.json" "public/settings.json" && \
|
||||
ln -s "../../config/bg_load.css" "public/css/bg_load.css" && \
|
||||
mkdir "config"
|
||||
for R in $RESOURCES; do ln -s "../config/$R" "public/$R"; done || true && \
|
||||
\
|
||||
rm -f "config.yaml" "public/settings.json" "public/css/bg_load.css" || true && \
|
||||
ln -s "./config/config.yaml" "config.yaml" || true && \
|
||||
ln -s "../config/settings.json" "public/settings.json" || true && \
|
||||
ln -s "../../config/bg_load.css" "public/css/bg_load.css" || true && \
|
||||
mkdir "config" || true
|
||||
|
||||
# Cleanup unnecessary files
|
||||
RUN \
|
||||
|
@@ -4,7 +4,7 @@ echo WARNING: Cloudflare Tunnel!
|
||||
echo ========================================================================================================================
|
||||
echo This script downloads and runs the latest cloudflared.exe from Cloudflare to set up an HTTPS tunnel to your SillyTavern!
|
||||
echo Using the randomly generated temporary tunnel URL, anyone can access your SillyTavern over the Internet while the tunnel
|
||||
echo is active. Keep the URL safe and secure your SillyTavern installation by setting a username and password in config.conf!
|
||||
echo is active. Keep the URL safe and secure your SillyTavern installation by setting a username and password in config.yaml!
|
||||
echo.
|
||||
echo See https://docs.sillytavern.app/usage/remoteconnections/ for more details about how to secure your SillyTavern install.
|
||||
echo.
|
||||
|
113
colab/GPU.ipynb
113
colab/GPU.ipynb
@@ -34,45 +34,59 @@
|
||||
"source": [
|
||||
"#@markdown (RECOMMENDED) Generates an API key for you to use with the API\n",
|
||||
"secure = False #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables hosting of extensions backend for SillyTavern Extras\n",
|
||||
"use_cpu = False #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Allows to run SillyTavern Extras on CPU (use if you're out of daily GPU allowance)\n",
|
||||
"use_sd_cpu = False #@param {type:\"boolean\"}\n",
|
||||
"use_cpu = False #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Allows to run Stable Diffusion pipeline on CPU (slow!)\n",
|
||||
"extras_enable_captioning = True #@param {type:\"boolean\"}\n",
|
||||
"use_sd_cpu = False #@param {type:\"boolean\"}\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Loads the image captioning module\n",
|
||||
"Captions_Model = \"Salesforce/blip-image-captioning-large\" #@param [ \"Salesforce/blip-image-captioning-large\", \"Salesforce/blip-image-captioning-base\" ]\n",
|
||||
"extras_enable_caption = True #@param {type:\"boolean\"}\n",
|
||||
"captioning_model = \"Salesforce/blip-image-captioning-large\" #@param [ \"Salesforce/blip-image-captioning-large\", \"Salesforce/blip-image-captioning-base\" ]\n",
|
||||
"#@markdown * Salesforce/blip-image-captioning-large - good base model\n",
|
||||
"#@markdown * Salesforce/blip-image-captioning-base - slightly faster but less accurate\n",
|
||||
"extras_enable_emotions = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Loads the sentiment classification model\n",
|
||||
"Emotions_Model = \"nateraw/bert-base-uncased-emotion\" #@param [\"nateraw/bert-base-uncased-emotion\", \"joeddav/distilbert-base-uncased-go-emotions-student\"]\n",
|
||||
"extras_enable_classify = True #@param {type:\"boolean\"}\n",
|
||||
"classification_model = \"nateraw/bert-base-uncased-emotion\" #@param [\"nateraw/bert-base-uncased-emotion\", \"joeddav/distilbert-base-uncased-go-emotions-student\"]\n",
|
||||
"#@markdown * nateraw/bert-base-uncased-emotion = 6 supported emotions<br>\n",
|
||||
"#@markdown * joeddav/distilbert-base-uncased-go-emotions-student = 28 supported emotions\n",
|
||||
"extras_enable_memory = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Loads the story summarization module\n",
|
||||
"Memory_Model = \"slauw87/bart_summarisation\" #@param [ \"slauw87/bart_summarisation\", \"Qiliang/bart-large-cnn-samsum-ChatGPT_v3\", \"Qiliang/bart-large-cnn-samsum-ElectrifAi_v10\", \"distilbart-xsum-12-3\" ]\n",
|
||||
"extras_enable_summarize = True #@param {type:\"boolean\"}\n",
|
||||
"summarization_model = \"slauw87/bart_summarisation\" #@param [ \"slauw87/bart_summarisation\", \"Qiliang/bart-large-cnn-samsum-ChatGPT_v3\", \"Qiliang/bart-large-cnn-samsum-ElectrifAi_v10\", \"distilbart-xsum-12-3\" ]\n",
|
||||
"#@markdown * slauw87/bart_summarisation - general purpose summarization model\n",
|
||||
"#@markdown * Qiliang/bart-large-cnn-samsum-ChatGPT_v3 - summarization model optimized for chats\n",
|
||||
"#@markdown * Qiliang/bart-large-cnn-samsum-ElectrifAi_v10 - nice results so far, but still being evaluated\n",
|
||||
"#@markdown * distilbart-xsum-12-3 - faster, but pretty basic alternative\n",
|
||||
"extras_enable_silero_tts = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Enables Silero text-to-speech module\n",
|
||||
"extras_enable_edge_tts = True #@param {type:\"boolean\"}\n",
|
||||
"extras_enable_silero_tts = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables Microsoft Edge text-to-speech module\n",
|
||||
"extras_enable_sd = True #@param {type:\"boolean\"}\n",
|
||||
"extras_enable_edge_tts = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables RVC module\n",
|
||||
"extras_enable_rvc = False #@param {type:\"boolean\"}\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Enables Whisper speech recognition module\n",
|
||||
"extras_enable_whisper_stt = True #@param {type:\"boolean\"}\n",
|
||||
"whisper_model = \"base.en\" #@param [ \"tiny.en\", \"base.en\", \"small.en\", \"medium.en\", \"tiny\", \"base\", \"small\", \"medium\", \"large\" ]\n",
|
||||
"#@markdown There are five model sizes, four with English-only versions, offering speed and accuracy tradeoffs.\n",
|
||||
"#@markdown The .en models for English-only applications tend to perform better, especially for the tiny.en and base.en models.\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Enables SD picture generation\n",
|
||||
"SD_Model = \"ckpt/anything-v4.5-vae-swapped\" #@param [ \"ckpt/anything-v4.5-vae-swapped\", \"hakurei/waifu-diffusion\", \"philz1337/clarity\", \"prompthero/openjourney\", \"ckpt/sd15\", \"stabilityai/stable-diffusion-2-1-base\" ]\n",
|
||||
"extras_enable_sd = True #@param {type:\"boolean\"}\n",
|
||||
"sd_model = \"ckpt/anything-v4.5-vae-swapped\" #@param [ \"ckpt/anything-v4.5-vae-swapped\", \"hakurei/waifu-diffusion\", \"philz1337/clarity\", \"prompthero/openjourney\", \"ckpt/sd15\", \"stabilityai/stable-diffusion-2-1-base\" ]\n",
|
||||
"#@markdown * ckpt/anything-v4.5-vae-swapped - anime style model\n",
|
||||
"#@markdown * hakurei/waifu-diffusion - anime style model\n",
|
||||
"#@markdown * philz1337/clarity - realistic style model\n",
|
||||
"#@markdown * prompthero/openjourney - midjourney style model\n",
|
||||
"#@markdown * ckpt/sd15 - base SD 1.5\n",
|
||||
"#@markdown * stabilityai/stable-diffusion-2-1-base - base SD 2.1\n",
|
||||
"#@markdown ***\n",
|
||||
"#@markdown Enables ChromaDB module\n",
|
||||
"extras_enable_chromadb = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables ChromaDB for Infinity Context plugin\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import secrets\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# SillyTavern extras\n",
|
||||
@@ -85,28 +99,36 @@
|
||||
"if secure:\n",
|
||||
" params.append('--secure')\n",
|
||||
"params.append('--share')\n",
|
||||
"ExtrasModules = []\n",
|
||||
"modules = []\n",
|
||||
"\n",
|
||||
"if (extras_enable_captioning):\n",
|
||||
" ExtrasModules.append('caption')\n",
|
||||
"if (extras_enable_memory):\n",
|
||||
" ExtrasModules.append('summarize')\n",
|
||||
"if (extras_enable_emotions):\n",
|
||||
" ExtrasModules.append('classify')\n",
|
||||
"if (extras_enable_sd):\n",
|
||||
" ExtrasModules.append('sd')\n",
|
||||
"if (extras_enable_silero_tts):\n",
|
||||
" ExtrasModules.append('silero-tts')\n",
|
||||
"if extras_enable_caption:\n",
|
||||
" modules.append('caption')\n",
|
||||
"if extras_enable_summarize:\n",
|
||||
" modules.append('summarize')\n",
|
||||
"if extras_enable_classify:\n",
|
||||
" modules.append('classify')\n",
|
||||
"if extras_enable_sd:\n",
|
||||
" modules.append('sd')\n",
|
||||
"if extras_enable_silero_tts:\n",
|
||||
" modules.append('silero-tts')\n",
|
||||
"if extras_enable_edge_tts:\n",
|
||||
" ExtrasModules.append('edge-tts')\n",
|
||||
"if (extras_enable_chromadb):\n",
|
||||
" ExtrasModules.append('chromadb')\n",
|
||||
" modules.append('edge-tts')\n",
|
||||
"if extras_enable_chromadb:\n",
|
||||
" modules.append('chromadb')\n",
|
||||
"if extras_enable_whisper_stt:\n",
|
||||
" modules.append('whisper-stt')\n",
|
||||
" params.append(f'--stt-whisper-model-path={whisper_model}')\n",
|
||||
"if extras_enable_rvc:\n",
|
||||
" modules.append('rvc')\n",
|
||||
" params.append('--max-content-length=2000')\n",
|
||||
" params.append('--rvc-save-file')\n",
|
||||
"\n",
|
||||
"params.append(f'--classification-model={Emotions_Model}')\n",
|
||||
"params.append(f'--summarization-model={Memory_Model}')\n",
|
||||
"params.append(f'--captioning-model={Captions_Model}')\n",
|
||||
"params.append(f'--sd-model={SD_Model}')\n",
|
||||
"params.append(f'--enable-modules={\",\".join(ExtrasModules)}')\n",
|
||||
"\n",
|
||||
"params.append(f'--classification-model={classification_model}')\n",
|
||||
"params.append(f'--summarization-model={summarization_model}')\n",
|
||||
"params.append(f'--captioning-model={captioning_model}')\n",
|
||||
"params.append(f'--sd-model={sd_model}')\n",
|
||||
"params.append(f'--enable-modules={\",\".join(modules)}')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"%cd /\n",
|
||||
@@ -114,22 +136,21 @@
|
||||
"%cd /SillyTavern-extras\n",
|
||||
"!git clone https://github.com/Cohee1207/tts_samples\n",
|
||||
"!npm install -g localtunnel\n",
|
||||
"!pip install -r requirements-complete.txt\n",
|
||||
"!pip install tensorflow==2.12\n",
|
||||
"!pip install Flask-Cors\n",
|
||||
"!pip install Flask-Compress\n",
|
||||
"!pip install transformers\n",
|
||||
"!pip install Flask_Cloudflared\n",
|
||||
"!pip install webuiapi\n",
|
||||
"!pip install diffusers\n",
|
||||
"!pip install accelerate\n",
|
||||
"!pip install silero_api_server\n",
|
||||
"!pip install edge_tts\n",
|
||||
"!pip install chromadb\n",
|
||||
"!pip install sentence_transformers\n",
|
||||
"%pip install -r requirements.txt\n",
|
||||
"!wget https://github.com/cloudflare/cloudflared/releases/download/2023.5.0/cloudflared-linux-amd64 -O /tmp/cloudflared-linux-amd64\n",
|
||||
"!chmod +x /tmp/cloudflared-linux-amd64\n",
|
||||
"\n",
|
||||
"if extras_enable_rvc:\n",
|
||||
" print(\"Installing RVC requirements\")\n",
|
||||
" !pip install -r requirements-rvc.txt\n",
|
||||
"\n",
|
||||
"# Generate a random API key\n",
|
||||
"api_key = secrets.token_hex(5)\n",
|
||||
"\n",
|
||||
"# Write the API key to api_key.txt\n",
|
||||
"with open('./api_key.txt', 'w') as f:\n",
|
||||
" f.write(api_key)\n",
|
||||
"print(f\"API Key generated: {api_key}\")\n",
|
||||
"\n",
|
||||
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
||||
"print(cmd)\n",
|
||||
|
@@ -1,35 +0,0 @@
|
||||
const port = 8000;
|
||||
const whitelist = ['127.0.0.1']; //Example for add several IP in whitelist: ['127.0.0.1', '192.168.0.10']
|
||||
const whitelistMode = true; //Disabling enabling the ip whitelist mode. true/false
|
||||
const basicAuthMode = false; //Toggle basic authentication for endpoints.
|
||||
const basicAuthUser = {username: "user", password: "password"}; //Login credentials when basicAuthMode is true.
|
||||
const disableThumbnails = false; //Disables the generation of thumbnails, opting to use the raw images instead
|
||||
const autorun = true; //Autorun in the browser. true/false
|
||||
const enableExtensions = true; //Enables support for TavernAI-extras project
|
||||
const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine.
|
||||
const allowKeysExposure = false; // If true, private API keys could be fetched to the frontend.
|
||||
const skipContentCheck = false; // If true, no new default content will be delivered to you.
|
||||
|
||||
|
||||
// If true, Allows insecure settings for listen, whitelist, and authentication.
|
||||
// Change this setting only on "trusted networks". Do not change this value unless you are aware of the issues that can arise from changing this setting and configuring a insecure setting.
|
||||
const securityOverride = false;
|
||||
|
||||
// Request overrides for additional headers
|
||||
const requestOverrides = [];
|
||||
|
||||
module.exports = {
|
||||
port,
|
||||
whitelist,
|
||||
whitelistMode,
|
||||
basicAuthMode,
|
||||
basicAuthUser,
|
||||
autorun,
|
||||
enableExtensions,
|
||||
listen,
|
||||
disableThumbnails,
|
||||
allowKeysExposure,
|
||||
securityOverride,
|
||||
skipContentCheck,
|
||||
requestOverrides,
|
||||
};
|
53
default/config.yaml
Normal file
53
default/config.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
# -- NETWORK CONFIGURATION --
|
||||
# Listen for incoming connections
|
||||
listen: true
|
||||
# Server port
|
||||
port: 8000
|
||||
# Toggle whitelist mode
|
||||
whitelistMode: true
|
||||
# Whitelist of allowed IP addresses
|
||||
whitelist:
|
||||
- 127.0.0.1
|
||||
# Toggle basic authentication for endpoints
|
||||
basicAuthMode: false
|
||||
# Basic authentication credentials
|
||||
basicAuthUser:
|
||||
username: user
|
||||
password: password
|
||||
# Enables CORS proxy middleware
|
||||
enableCorsProxy: false
|
||||
# Disable security checks - NOT RECOMMENDED
|
||||
securityOverride: false
|
||||
# -- ADVANCED CONFIGURATION --
|
||||
# Open the browser automatically
|
||||
autorun: true
|
||||
# Disable thumbnail generation
|
||||
disableThumbnails: false
|
||||
# Thumbnail quality (0-100)
|
||||
thumbnailsQuality: 95
|
||||
# Allow secret keys exposure via API
|
||||
allowKeysExposure: false
|
||||
# Skip new default content checks
|
||||
skipContentCheck: false
|
||||
# Disable automatic chats backup
|
||||
disableChatBackup: false
|
||||
# API request overrides (for KoboldAI and Text Completion APIs)
|
||||
## Format is an array of objects:
|
||||
## - hosts:
|
||||
## - example.com
|
||||
## headers:
|
||||
## Content-Type: application/json
|
||||
requestOverrides: []
|
||||
# -- PLUGIN CONFIGURATION --
|
||||
# Enable UI extensions
|
||||
enableExtensions: true
|
||||
# Extension settings
|
||||
extras:
|
||||
# Disables automatic model download from HuggingFace
|
||||
disableAutoDownload: false
|
||||
# Extra models for plugins. Expects model IDs from HuggingFace model hub in ONNX format
|
||||
classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx
|
||||
captioningModel: Xenova/vit-gpt2-image-captioning
|
||||
embeddingModel: Xenova/all-mpnet-base-v2
|
||||
promptExpansionModel: Cohee/fooocus_expansion-onnx
|
||||
|
86
default/content/Default_Comfy_Workflow.json
Normal file
86
default/content/Default_Comfy_Workflow.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"3": {
|
||||
"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"cfg": "%scale%",
|
||||
"denoise": 1,
|
||||
"latent_image": [
|
||||
"5",
|
||||
0
|
||||
],
|
||||
"model": [
|
||||
"4",
|
||||
0
|
||||
],
|
||||
"negative": [
|
||||
"7",
|
||||
0
|
||||
],
|
||||
"positive": [
|
||||
"6",
|
||||
0
|
||||
],
|
||||
"sampler_name": "%sampler%",
|
||||
"scheduler": "%scheduler%",
|
||||
"seed": "%seed%",
|
||||
"steps": "%steps%"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"class_type": "CheckpointLoaderSimple",
|
||||
"inputs": {
|
||||
"ckpt_name": "%model%"
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"class_type": "EmptyLatentImage",
|
||||
"inputs": {
|
||||
"batch_size": 1,
|
||||
"height": "%height%",
|
||||
"width": "%width%"
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"clip": [
|
||||
"4",
|
||||
1
|
||||
],
|
||||
"text": "%prompt%"
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"clip": [
|
||||
"4",
|
||||
1
|
||||
],
|
||||
"text": "%negative_prompt%"
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"class_type": "VAEDecode",
|
||||
"inputs": {
|
||||
"samples": [
|
||||
"3",
|
||||
0
|
||||
],
|
||||
"vae": [
|
||||
"4",
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
"9": {
|
||||
"class_type": "SaveImage",
|
||||
"inputs": {
|
||||
"filename_prefix": "SillyTavern",
|
||||
"images": [
|
||||
"8",
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@@ -22,5 +22,9 @@
|
||||
{
|
||||
"filename": "user-default.png",
|
||||
"type": "avatar"
|
||||
},
|
||||
{
|
||||
"filename": "Default_Comfy_Workflow.json",
|
||||
"type": "workflow"
|
||||
}
|
||||
]
|
||||
|
@@ -49,7 +49,6 @@
|
||||
"ban_eos_token": false,
|
||||
"skip_special_tokens": true,
|
||||
"streaming": false,
|
||||
"streaming_url": "ws://127.0.0.1:5005/api/v1/stream",
|
||||
"mirostat_mode": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
@@ -75,9 +74,6 @@
|
||||
"always_force_name2": true,
|
||||
"user_prompt_bias": "",
|
||||
"show_user_prompt_bias": true,
|
||||
"multigen": false,
|
||||
"multigen_first_chunk": 50,
|
||||
"multigen_next_chunks": 30,
|
||||
"markdown_escape_strings": "",
|
||||
"fast_ui_mode": false,
|
||||
"avatar_style": 0,
|
||||
@@ -167,7 +163,8 @@
|
||||
"custom_stopping_strings_macro": true,
|
||||
"fuzzy_search": true,
|
||||
"encode_tags": false,
|
||||
"lazy_load": 100,
|
||||
"enableLabMode": false,
|
||||
"enableZenSliders": false,
|
||||
"ui_mode": 1
|
||||
},
|
||||
"extension_settings": {
|
||||
@@ -409,8 +406,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"use_stop_sequence": false,
|
||||
"streaming_kobold": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -420,7 +415,12 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"use_default_badwordsids": false,
|
||||
"grammar": ""
|
||||
},
|
||||
"oai_settings": {
|
||||
"preset_settings_openai": "Default",
|
||||
|
1
default/user.css
Normal file
1
default/user.css
Normal file
@@ -0,0 +1 @@
|
||||
/* Put custom styles here. */
|
@@ -4,9 +4,10 @@ services:
|
||||
build: ..
|
||||
container_name: sillytavern
|
||||
hostname: sillytavern
|
||||
image: sillytavern/sillytavern:latest
|
||||
image: ghcr.io/sillytavern/sillytavern:latest
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- "./config:/home/node/app/config"
|
||||
- "./user:/home/node/app/public/user"
|
||||
restart: unless-stopped
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Initialize missing user files
|
||||
IFS="," RESOURCES="characters,groups,group chats,chats,User Avatars,worlds"
|
||||
IFS="," RESOURCES="assets,backgrounds,user,context,instruct,QuickReplies,movingUI,themes,characters,chats,groups,group chats,User Avatars,worlds,OpenAI Settings,NovelAI Settings,KoboldAI Settings,TextGen Settings"
|
||||
for R in $RESOURCES; do
|
||||
if [ ! -e "config/$R" ]; then
|
||||
echo "Resource not found, copying from defaults: $R"
|
||||
@@ -9,9 +9,9 @@ for R in $RESOURCES; do
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -e "config/config.conf" ]; then
|
||||
echo "Resource not found, copying from defaults: config.conf"
|
||||
cp -r "default/config.conf" "config/config.conf"
|
||||
if [ ! -e "config/config.yaml" ]; then
|
||||
echo "Resource not found, copying from defaults: config.yaml"
|
||||
cp -r "default/config.yaml" "config/config.yaml"
|
||||
fi
|
||||
|
||||
if [ ! -e "config/settings.json" ]; then
|
||||
@@ -24,5 +24,20 @@ if [ ! -e "config/bg_load.css" ]; then
|
||||
cp -r "default/bg_load.css" "config/bg_load.css"
|
||||
fi
|
||||
|
||||
CONFIG_FILE="config.yaml"
|
||||
|
||||
echo "Starting with the following config:"
|
||||
cat $CONFIG_FILE
|
||||
|
||||
if grep -q "listen: false" $CONFIG_FILE; then
|
||||
echo -e "\033[1;31mThe listen parameter is set to false. If you can't connect to the server, edit the \"docker/config/config.yaml\" file and restart the container.\033[0m"
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
if grep -q "whitelistMode: true" $CONFIG_FILE; then
|
||||
echo -e "\033[1;31mThe whitelistMode parameter is set to true. If you can't connect to the server, edit the \"docker/config/config.yaml\" file and restart the container.\033[0m"
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
# Start the server
|
||||
exec node server.js
|
||||
|
1361
package-lock.json
generated
1361
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -3,43 +3,46 @@
|
||||
"@agnai/sentencepiece-js": "^1.1.1",
|
||||
"@agnai/web-tokenizers": "^0.1.3",
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"bing-translate-api": "^2.9.1",
|
||||
"command-exists": "^1.2.9",
|
||||
"compression": "^1",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"csrf-csrf": "^2.2.3",
|
||||
"device-detector-js": "^3.0.3",
|
||||
"exifreader": "^4.12.0",
|
||||
"express": "^4.18.2",
|
||||
"form-data": "^4.0.0",
|
||||
"google-translate-api-browser": "^3.0.1",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
"ip-matching": "^2.1.2",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"jimp": "^0.22.7",
|
||||
"jquery": "^3.6.4",
|
||||
"jimp": "^0.22.10",
|
||||
"json5": "^2.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-fetch": "^2.6.11",
|
||||
"open": "^8.4.2",
|
||||
"piexifjs": "^1.0.6",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"response-time": "^2.3.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sillytavern-transformers": "^2.7.3",
|
||||
"simple-git": "^3.19.1",
|
||||
"uniqolor": "^1.1.0",
|
||||
"webp-converter": "2.3.2",
|
||||
"vectra": "^0.2.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.13.0",
|
||||
"yaml": "^2.3.4",
|
||||
"yargs": "^17.7.1",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"overrides": {
|
||||
"parse-bmfont-xml": {
|
||||
"xml2js": "^0.5.0"
|
||||
},
|
||||
"vectra": {
|
||||
"openai": "^4.17.0"
|
||||
}
|
||||
},
|
||||
"name": "sillytavern",
|
||||
@@ -49,11 +52,12 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.10",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start-multi": "node server.js --disableCsrf",
|
||||
"pkg": "pkg --compress Gzip --no-bytecode --public ."
|
||||
"pkg": "pkg --compress Gzip --no-bytecode --public .",
|
||||
"postinstall": "node post-install.js"
|
||||
},
|
||||
"bin": {
|
||||
"sillytavern": "./server.js"
|
||||
@@ -78,6 +82,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"jquery": "^3.6.4",
|
||||
"pkg": "^5.8.1",
|
||||
"pkg-fetch": "^3.5.2"
|
||||
}
|
||||
|
182
post-install.js
Normal file
182
post-install.js
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Scripts to be done before starting the server for the first time.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const yaml = require('yaml');
|
||||
const _ = require('lodash');
|
||||
|
||||
/**
|
||||
* Colorizes console output.
|
||||
*/
|
||||
const color = {
|
||||
byNum: (mess, fgNum) => {
|
||||
mess = mess || '';
|
||||
fgNum = fgNum === undefined ? 31 : fgNum;
|
||||
return '\u001b[' + fgNum + 'm' + mess + '\u001b[39m';
|
||||
},
|
||||
black: (mess) => color.byNum(mess, 30),
|
||||
red: (mess) => color.byNum(mess, 31),
|
||||
green: (mess) => color.byNum(mess, 32),
|
||||
yellow: (mess) => color.byNum(mess, 33),
|
||||
blue: (mess) => color.byNum(mess, 34),
|
||||
magenta: (mess) => color.byNum(mess, 35),
|
||||
cyan: (mess) => color.byNum(mess, 36),
|
||||
white: (mess) => color.byNum(mess, 37)
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets all keys from an object recursively.
|
||||
* @param {object} obj Object to get all keys from
|
||||
* @param {string} prefix Prefix to prepend to all keys
|
||||
* @returns {string[]} Array of all keys in the object
|
||||
*/
|
||||
function getAllKeys(obj, prefix = '') {
|
||||
if (typeof obj !== 'object' || Array.isArray(obj)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.flatMap(Object.keys(obj), key => {
|
||||
const newPrefix = prefix ? `${prefix}.${key}` : key;
|
||||
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
||||
return getAllKeys(obj[key], newPrefix);
|
||||
} else {
|
||||
return [newPrefix];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the old config.conf file to the new config.yaml format.
|
||||
*/
|
||||
function convertConfig() {
|
||||
if (fs.existsSync('./config.conf')) {
|
||||
if (fs.existsSync('./config.yaml')) {
|
||||
console.log(color.yellow('Both config.conf and config.yaml exist. Please delete config.conf manually.'));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(color.blue('Converting config.conf to config.yaml. Your old config.conf will be renamed to config.conf.bak'));
|
||||
const config = require(path.join(process.cwd(), './config.conf'));
|
||||
fs.renameSync('./config.conf', './config.conf.bak');
|
||||
fs.writeFileSync('./config.yaml', yaml.stringify(config));
|
||||
console.log(color.green('Conversion successful. Please check your config.yaml and fix it if necessary.'));
|
||||
} catch (error) {
|
||||
console.error(color.red('FATAL: Config conversion failed. Please check your config.conf file and try again.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current config.yaml with the default config.yaml and adds any missing values.
|
||||
*/
|
||||
function addMissingConfigValues() {
|
||||
try {
|
||||
const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8'));
|
||||
let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8'));
|
||||
|
||||
// Get all keys from the original config
|
||||
const originalKeys = getAllKeys(config);
|
||||
|
||||
// Use lodash's defaultsDeep function to recursively apply default properties
|
||||
config = _.defaultsDeep(config, defaultConfig);
|
||||
|
||||
// Get all keys from the updated config
|
||||
const updatedKeys = getAllKeys(config);
|
||||
|
||||
// Find the keys that were added
|
||||
const addedKeys = _.difference(updatedKeys, originalKeys);
|
||||
|
||||
if (addedKeys.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Adding missing config values to config.yaml:', addedKeys);
|
||||
fs.writeFileSync('./config.yaml', yaml.stringify(config));
|
||||
} catch (error) {
|
||||
console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default config files if they don't exist yet.
|
||||
*/
|
||||
function createDefaultFiles() {
|
||||
const files = {
|
||||
settings: './public/settings.json',
|
||||
bg_load: './public/css/bg_load.css',
|
||||
config: './config.yaml',
|
||||
user: './public/css/user.css',
|
||||
};
|
||||
|
||||
for (const file of Object.values(files)) {
|
||||
try {
|
||||
if (!fs.existsSync(file)) {
|
||||
const defaultFilePath = path.join('./default', path.parse(file).base);
|
||||
fs.copyFileSync(defaultFilePath, file);
|
||||
console.log(color.green(`Created default file: ${file}`));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(color.red(`FATAL: Could not write default file: ${file}`), error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MD5 hash of the given data.
|
||||
* @param {Buffer} data Input data
|
||||
* @returns {string} MD5 hash of the input data
|
||||
*/
|
||||
function getMd5Hash(data) {
|
||||
return crypto
|
||||
.createHash('md5')
|
||||
.update(data)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the WASM binaries from the sillytavern-transformers package to the dist folder.
|
||||
*/
|
||||
function copyWasmFiles() {
|
||||
if (!fs.existsSync('./dist')) {
|
||||
fs.mkdirSync('./dist');
|
||||
}
|
||||
|
||||
const listDir = fs.readdirSync('./node_modules/sillytavern-transformers/dist');
|
||||
|
||||
for (const file of listDir) {
|
||||
if (file.endsWith('.wasm')) {
|
||||
const sourcePath = `./node_modules/sillytavern-transformers/dist/${file}`;
|
||||
const targetPath = `./dist/${file}`;
|
||||
|
||||
// Don't copy if the file already exists and is the same checksum
|
||||
if (fs.existsSync(targetPath)) {
|
||||
const sourceChecksum = getMd5Hash(fs.readFileSync(sourcePath));
|
||||
const targetChecksum = getMd5Hash(fs.readFileSync(targetPath));
|
||||
|
||||
if (sourceChecksum === targetChecksum) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
console.log(`${file} successfully copied to ./dist/${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 0. Convert config.conf to config.yaml
|
||||
convertConfig();
|
||||
// 1. Create default config files
|
||||
createDefaultFiles();
|
||||
// 2. Copy transformers WASM binaries from node_modules
|
||||
copyWasmFiles();
|
||||
// 3. Add missing config values
|
||||
addMissingConfigValues();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1.15,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.8,
|
||||
"rep_pen": 1.05,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.8,
|
||||
"rep_pen_slope": 7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
5,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.59,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.87,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.87,
|
||||
"rep_pen_slope": 0.3,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.8,
|
||||
"top_k": 100,
|
||||
"top_p": 0.9,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.15,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 0.9,
|
||||
"top_a": 0,
|
||||
"top_k": 100,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 3.4,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.51,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"tfs": 0.99,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"rep_pen": 1.2,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.99,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -17,5 +16,9 @@
|
||||
4,
|
||||
2,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.63,
|
||||
"top_k": 0,
|
||||
"top_p": 0.98,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.98,
|
||||
"rep_pen": 1.05,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 0.98,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.98,
|
||||
"rep_pen_slope": 0.1,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
5,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.7,
|
||||
"top_k": 0,
|
||||
"top_p": 0.5,
|
||||
"top_a": 0.75,
|
||||
"typical": 0.19,
|
||||
"tfs": 0.97,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 1024,
|
||||
"top_p": 0.5,
|
||||
"top_a": 0.75,
|
||||
"top_k": 0,
|
||||
"typical": 0.19,
|
||||
"tfs": 0.97,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
2,
|
||||
1,
|
||||
0
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.7,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.9,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 1024,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.9,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.66,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0.96,
|
||||
"typical": 0.6,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 1024,
|
||||
"top_p": 1,
|
||||
"top_a": 0.96,
|
||||
"top_k": 0,
|
||||
"typical": 0.6,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
0,
|
||||
2,
|
||||
3
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.94,
|
||||
"top_k": 12,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.94,
|
||||
"rep_pen": 1.05,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 12,
|
||||
"typical": 1,
|
||||
"tfs": 0.94,
|
||||
"rep_pen_slope": 0.2,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1.5,
|
||||
"top_k": 85,
|
||||
"top_p": 0.24,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 0.24,
|
||||
"top_a": 0,
|
||||
"top_k": 85,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1.05,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 1024,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
24
public/KoboldAI Settings/Miro Bronze.settings
Normal file
24
public/KoboldAI Settings/Miro Bronze.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.06,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
4,
|
||||
2,
|
||||
5
|
||||
],
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.61,
|
||||
"mirostat_eta": 1,
|
||||
"grammar": ""
|
||||
}
|
24
public/KoboldAI Settings/Miro Gold.settings
Normal file
24
public/KoboldAI Settings/Miro Gold.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.17,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
4,
|
||||
2,
|
||||
5
|
||||
],
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.91,
|
||||
"mirostat_eta": 1,
|
||||
"grammar": ""
|
||||
}
|
24
public/KoboldAI Settings/Miro Silver.settings
Normal file
24
public/KoboldAI Settings/Miro Silver.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.17,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
4,
|
||||
2,
|
||||
5
|
||||
],
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.62,
|
||||
"mirostat_eta": 1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1.07,
|
||||
"top_k": 100,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.93,
|
||||
"rep_pen": 1.05,
|
||||
"rep_pen_range": 404,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 100,
|
||||
"typical": 1,
|
||||
"tfs": 0.93,
|
||||
"rep_pen_slope": 0.8,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
2,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.44,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.9,
|
||||
"rep_pen": 1.15,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.9,
|
||||
"rep_pen_slope": 6.8,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1.35,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.69,
|
||||
"rep_pen": 1.15,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 0.69,
|
||||
"rep_pen_slope": 0.1,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
0,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 1,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 600,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -17,5 +16,9 @@
|
||||
4,
|
||||
2,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"temp": 0.72,
|
||||
"tfs": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"top_p": 0.73,
|
||||
"typical": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 2048,
|
||||
"top_p": 0.73,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.2,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -16,5 +16,9 @@
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 0.95,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -17,5 +16,9 @@
|
||||
4,
|
||||
2,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -17,5 +16,9 @@
|
||||
4,
|
||||
2,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -17,5 +16,9 @@
|
||||
4,
|
||||
2,
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"mirostat": 0,
|
||||
"mirostat_tau": 5,
|
||||
"mirostat_eta": 0.1,
|
||||
"grammar": ""
|
||||
}
|
24
public/TextGen Settings/Miro Bronze.settings
Normal file
24
public/TextGen Settings/Miro Bronze.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.06,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false,
|
||||
"mirostat_mode": 2,
|
||||
"mirostat_tau": 9.61,
|
||||
"mirostat_eta": 1,
|
||||
"rep_pen_size": 0
|
||||
}
|
24
public/TextGen Settings/Miro Gold.settings
Normal file
24
public/TextGen Settings/Miro Gold.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.17,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false,
|
||||
"mirostat_mode": 2,
|
||||
"mirostat_tau": 9.91,
|
||||
"mirostat_eta": 1,
|
||||
"rep_pen_size": 0
|
||||
}
|
24
public/TextGen Settings/Miro Silver.settings
Normal file
24
public/TextGen Settings/Miro Silver.settings
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"temp": 1.17,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"rep_pen_range": 0,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false,
|
||||
"mirostat_mode": 2,
|
||||
"mirostat_tau": 9.62,
|
||||
"mirostat_eta": 1,
|
||||
"rep_pen_size": 0
|
||||
}
|
1
public/assets/blip/.placeholder
Normal file
1
public/assets/blip/.placeholder
Normal file
@@ -0,0 +1 @@
|
||||
Put blip audio files here
|
1
public/assets/live2d/.placeholder
Normal file
1
public/assets/live2d/.placeholder
Normal file
@@ -0,0 +1 @@
|
||||
Put live2d model folders here
|
10
public/context/Adventure.json
Normal file
10
public/context/Adventure.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"example_separator": "",
|
||||
"chat_start": "",
|
||||
"always_force_name2": false,
|
||||
"trim_sentences": false,
|
||||
"include_newline": false,
|
||||
"single_line": true,
|
||||
"name": "Adventure"
|
||||
}
|
6
public/context/ChatML.json
Normal file
6
public/context/ChatML.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "<|im_start|>system\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}<|im_end|>",
|
||||
"chat_start": "",
|
||||
"example_separator": "",
|
||||
"name": "ChatML"
|
||||
}
|
6
public/context/Libra-32B.json
Normal file
6
public/context/Libra-32B.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "### Instruction:\nWrite {{char}}'s next reply in this roleplay with {{user}}. Use the provided character sheet and example dialogue for formatting direction and character speech patterns.\n\n{{#if system}}{{system}}\n\n{{/if}}### Character Sheet:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "### START ROLEPLAY:",
|
||||
"example_separator": "### Example:",
|
||||
"name": "Libra-32B"
|
||||
}
|
6
public/context/Lightning 1.1.json
Normal file
6
public/context/Lightning 1.1.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}",
|
||||
"chat_start": "This is the history of the roleplay:",
|
||||
"example_separator": "Example of an interaction:",
|
||||
"name": "Lightning 1.1"
|
||||
}
|
6
public/context/Mistral.json
Normal file
6
public/context/Mistral.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "[INST] {{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}[/INST]",
|
||||
"chat_start": "",
|
||||
"example_separator": "Examples:",
|
||||
"name": "Mistral"
|
||||
}
|
6
public/context/OldDefault.json
Normal file
6
public/context/OldDefault.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Circumstances and context of the dialogue: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "\nThen the roleplay chat between {{user}} and {{char}} begins.\n",
|
||||
"example_separator": "This is how {{char}} should talk",
|
||||
"name": "OldDefault"
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Pygmalion",
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{{char}}}'s Persona: {{description}}\n{{/if}}{{#if personality}}Personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "<START>",
|
||||
"example_separator": "<START>"
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "",
|
||||
"example_separator": ""
|
||||
}
|
||||
|
6
public/context/Story.json
Normal file
6
public/context/Story.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "",
|
||||
"example_separator": "",
|
||||
"name": "Story"
|
||||
}
|
105
public/css/character-group-overlay.css
Normal file
105
public/css/character-group-overlay.css
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .character_select {
|
||||
transition: background-color 0.4s ease;
|
||||
margin-bottom: 1px;
|
||||
background-color: rgba(170, 170, 170, 0.15);
|
||||
}
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .bogus_folder_select,
|
||||
#rm_print_characters_block.group_overlay_mode_select .group_select {
|
||||
cursor: auto;
|
||||
filter: saturate(0.3);
|
||||
}
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .bogus_folder_select:hover,
|
||||
#rm_print_characters_block.group_overlay_mode_select .group_select:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .character_select input.bulk_select_checkbox {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .character_select.character_selected {
|
||||
background-color: var(--SmartThemeQuoteColor);
|
||||
}
|
||||
|
||||
#rm_print_characters_block.group_overlay_mode_select .character_select .bulk_select_checkbox {
|
||||
visibility: hidden;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
#character_context_menu.hidden { display: none; }
|
||||
#character_context_menu {
|
||||
position: absolute;
|
||||
padding: 3px;
|
||||
z-index: 9998;
|
||||
background-color: var(--black90a);
|
||||
border: 1px solid var(--black90a);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#character_context_menu ul li button {
|
||||
border: 0;
|
||||
border-bottom-color: currentcolor;
|
||||
color: var(--SmartThemeQuoteColor);
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding: 0.5em;
|
||||
border-bottom: 1px dotted var(--SmartThemeQuoteColor);
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#character_context_menu ul li button:hover {
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
}
|
||||
|
||||
#character_context_menu ul li:last-child button {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
#character_context_menu ul li #character_context_menu_delete {
|
||||
color: var(--fullred);
|
||||
}
|
||||
|
||||
#character_context_menu ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#character_context_menu .character_context_menu_separator {
|
||||
height: 1px;
|
||||
background-color: var(--SmartThemeBotMesBlurTintColor);
|
||||
}
|
||||
|
||||
#character_context_menu li:hover {
|
||||
background-color: var(--SmartThemeBotMesBlurTintColor);
|
||||
}
|
||||
|
||||
#bulkEditButton.bulk_edit_overlay_active {
|
||||
color: var(--golden);
|
||||
}
|
||||
|
||||
#bulk_tag_shadow_popup {
|
||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
background-color: var(--black30a);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
height: 100svh;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#bulk_tag_shadow_popup #bulk_tag_popup {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#bulk_tag_shadow_popup #bulk_tag_popup #dialogue_popup_controls .menu_button {
|
||||
width: 100px;
|
||||
padding: 0.25em;
|
||||
}
|
@@ -3,11 +3,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
#extensions_status {
|
||||
/* margin-bottom: 10px; */
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.extensions_block input[type="submit"]:hover {
|
||||
background-color: green;
|
||||
}
|
||||
@@ -93,4 +88,119 @@ input.extension_missing[type="checkbox"] {
|
||||
.update-button {
|
||||
margin-right: 10px;
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fixes order of settings for extensions */
|
||||
#extensions_settings,
|
||||
#extensions_settings2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/** LEFT COLUMN **/
|
||||
/* Must be always on top */
|
||||
#extensions_settings>#assets_ui {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
#extensions_settings>.expression_settings {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#extensions_settings>.background_settings {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
#extensions_settings>.sd_settings {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
#extensions_settings>#tts_settings {
|
||||
order: 5;
|
||||
}
|
||||
|
||||
#extensions_settings>#rvc_settings {
|
||||
order: 6;
|
||||
}
|
||||
|
||||
#extensions_settings>.objective-settings {
|
||||
order: 7;
|
||||
}
|
||||
|
||||
#extensions_settings>#speech_recognition_settings {
|
||||
order: 8;
|
||||
}
|
||||
|
||||
#extensions_settings>#audio_settings {
|
||||
order: 9;
|
||||
}
|
||||
|
||||
/** RIGHT COLUMN **/
|
||||
#extensions_settings2>.translation_settings {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
#extensions_settings2>.caption_settings {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#extensions_settings2>.quickReplySettings {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
#extensions_settings2>.idle-settings {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
#extensions_settings2>#memory_settings {
|
||||
order: 5;
|
||||
}
|
||||
|
||||
#extensions_settings2>.hypebot_settings {
|
||||
order: 6;
|
||||
}
|
||||
|
||||
#extensions_settings2>.regex_settings {
|
||||
order: 7;
|
||||
}
|
||||
|
||||
#extensions_settings2>.vectors_settings {
|
||||
order: 8;
|
||||
}
|
||||
|
||||
#extensions_settings2>.chromadb_settings {
|
||||
order: 9;
|
||||
}
|
||||
|
||||
#extensions_settings2>.randomizer_settings {
|
||||
order: 10;
|
||||
}
|
||||
|
||||
/** WAND MENU **/
|
||||
#extensionsMenu>#ttsExtensionMenuItem {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
#extensionsMenu>#sd_gen {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#extensionsMenu>#send_picture {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
#extensionsMenu>#token_counter {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
#extensionsMenu>#objective-task-manual-check-menu-item {
|
||||
order: 5;
|
||||
}
|
||||
|
||||
#extensionsMenu>#roll_dice {
|
||||
order: 6;
|
||||
}
|
||||
|
||||
#extensionsMenu>#translate_chat {
|
||||
order: 7;
|
||||
}
|
||||
|
57
public/css/file-form.css
Normal file
57
public/css/file-form.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.file_attached {
|
||||
display: flex;
|
||||
min-width: 150px;
|
||||
max-width: calc(var(--sheldWidth) * 0.9);
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin: 0.25em auto;
|
||||
padding: 0 0.75em;
|
||||
border: 2px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 15px;
|
||||
background-color: var(--white20a);
|
||||
}
|
||||
|
||||
.mes_file_container {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
background-color: var(--white20a);
|
||||
border: 2px solid var(--SmartThemeBorderColor);
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.mes_file_container .right_menu_button {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.mes_file_container .mes_file_size,
|
||||
.file_attached .file_size {
|
||||
font-size: 0.9em;
|
||||
color: var(--SmartThemeQuoteColor);
|
||||
}
|
||||
|
||||
.file_attached .file_name,
|
||||
.mes_file_container .mes_file_name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#file_form {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file_modal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
text-align: left;
|
||||
}
|
25
public/css/loader.css
Normal file
25
public/css/loader.css
Normal file
@@ -0,0 +1,25 @@
|
||||
#loader {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999999;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
width: 100svw;
|
||||
height: 100svh;
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
/*for some reason the full screen blur does not work on iOS*/
|
||||
backdrop-filter: blur(30px);
|
||||
color: var(--SmartThemeBodyColor);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#load-spinner {
|
||||
transition: all 300ms ease-out;
|
||||
opacity: 1;
|
||||
}
|
@@ -35,7 +35,7 @@
|
||||
max-width: 90svw;
|
||||
}
|
||||
|
||||
.world_entry_thin_controls,
|
||||
/* .world_entry_thin_controls, */
|
||||
#persona-management-block,
|
||||
#character_popup .flex-container {
|
||||
flex-direction: column;
|
||||
@@ -63,6 +63,15 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.world_entry .inline-drawer-toggle {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#worldInfoScanningCheckboxes {
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
body {
|
||||
touch-action: none;
|
||||
overflow: hidden;
|
||||
@@ -70,6 +79,10 @@
|
||||
|
||||
}
|
||||
|
||||
.world_entry_form_control {
|
||||
/* width: 100%; */
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
min-width: unset;
|
||||
width: 100%;
|
||||
@@ -78,7 +91,7 @@
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
border: 1px solid var(--grey30);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
#select_chat_popup {
|
||||
@@ -91,7 +104,6 @@
|
||||
#top-settings-holder,
|
||||
#top-bar {
|
||||
position: fixed;
|
||||
padding-top: 3px;
|
||||
width: 100vw;
|
||||
width: 100svw;
|
||||
}
|
||||
@@ -114,18 +126,32 @@
|
||||
/* ,
|
||||
#world_popup */
|
||||
{
|
||||
max-height: calc(100vh - 36px);
|
||||
max-height: calc(100svh - 36px);
|
||||
/*max-height: calc(100vh - 36px);
|
||||
max-height: calc(100svh - 36px);*/
|
||||
width: 100% !important;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
left: 0 !important;
|
||||
resize: none !important;
|
||||
top: 36px;
|
||||
top: var(--topBarBlockSize);
|
||||
}
|
||||
|
||||
.wi-settings {
|
||||
flex-direction: column;
|
||||
gap: 5px !important;
|
||||
}
|
||||
|
||||
.WIEntryTitleAndStatus,
|
||||
.WIEntryHeaderControls {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#WIEntryHeaderTitlesPC {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.WIEntryHeaderTitleMobile {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
#character_popup,
|
||||
@@ -135,15 +161,15 @@
|
||||
|
||||
#character_popup,
|
||||
#send_form {
|
||||
border: 1px solid var(--grey30);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
max-width: 100dvw;
|
||||
}
|
||||
|
||||
#chat {
|
||||
border-left: 1px solid var(--grey30);
|
||||
border-right: 1px solid var(--grey30);
|
||||
border-bottom: 1px solid var(--grey30);
|
||||
border-left: 1px solid var(--SmartThemeBorderColor);
|
||||
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||
border-bottom: 1px solid var(--SmartThemeBorderColor);
|
||||
align-items: start;
|
||||
align-content: start;
|
||||
overflow-y: auto;
|
||||
@@ -161,7 +187,9 @@
|
||||
}
|
||||
|
||||
#showRawPrompt,
|
||||
#groupCurrentMemberPopoutButton {
|
||||
#copyPromptToClipboard,
|
||||
#groupCurrentMemberPopoutButton,
|
||||
#summaryExtensionPopoutButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -175,11 +203,11 @@
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
overflow-y: hidden;
|
||||
border-left: 1px solid var(--grey30);
|
||||
border-right: 1px solid var(--grey30);
|
||||
border-bottom: 1px solid var(--grey30);
|
||||
border-left: 1px solid var(--SmartThemeBorderColor);
|
||||
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||
border-bottom: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 0 0 20px 20px;
|
||||
top: 36px !important;
|
||||
top: var(--topBarBlockSize) !important;
|
||||
left: 0 !important;
|
||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
}
|
||||
@@ -265,12 +293,13 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#bg_menu_content {
|
||||
.bg_list {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1001px) {
|
||||
|
||||
#PygOverrides,
|
||||
#ContextFormatting,
|
||||
#UI-Theme-Block,
|
||||
@@ -310,8 +339,7 @@
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
max-height: 50vh;
|
||||
max-width: 50vh;
|
||||
width: 50vw;
|
||||
max-width: 90vw;
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
filter: drop-shadow(2px 2px 2px #51515199);
|
||||
@@ -341,6 +369,18 @@
|
||||
top: unset;
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
|
||||
#leftSendForm,
|
||||
#rightSendForm {
|
||||
width: 1.15em;
|
||||
flex-wrap: wrap;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
#extensionsMenuButton {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*iOS specific*/
|
||||
@@ -417,4 +457,4 @@
|
||||
#horde_model {
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
grid-column-end: 4;
|
||||
width: 100%;
|
||||
margin: 0.5em 0;
|
||||
background-image: linear-gradient(90deg, var(--transparent), var(--white30a), var(--transparent));
|
||||
background-image: linear-gradient(90deg, var(--transparent), var(--SmartThemeBorderColor), var(--transparent));
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt {
|
||||
align-items: center;
|
||||
padding: 0.5em;
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt .prompt_manager_prompt_controls {
|
||||
@@ -109,7 +109,7 @@
|
||||
#completion_prompt_manager_popup .completion_prompt_manager_prompt {
|
||||
margin: 1em 0;
|
||||
padding: 0.5em;
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
#completion_prompt_manager_popup .completion_prompt_manager_popup_header {
|
||||
@@ -265,7 +265,7 @@
|
||||
top: var(--topBarBlockSize);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
padding: 1em;
|
||||
border: 1px solid #333333;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
flex-direction: column;
|
||||
z-index: 3010 !important;
|
||||
border-radius: 0 0 20px 20px;
|
||||
|
@@ -14,7 +14,8 @@
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
color: rgb(188, 193, 200, 1);
|
||||
border: 1px solid #333;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 6px;
|
||||
border-radius: 10px;
|
||||
@@ -61,7 +62,8 @@
|
||||
#rm_group_add_members {
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border: 1px solid grey;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
;
|
||||
border-radius: 10px;
|
||||
background-color: var(--black30a);
|
||||
}
|
||||
@@ -76,6 +78,7 @@
|
||||
|
||||
#rm_group_members:empty {
|
||||
width: 100%;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
#rm_group_members:empty::before {
|
||||
@@ -224,4 +227,5 @@
|
||||
|
||||
.group_member .avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
flex-basis: auto;
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
/* Customize the dropdown */
|
||||
.select2-dropdown {
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
border: 1px solid var(--white30a) !important;
|
||||
border: 1px solid var(--SmartThemeBorderColor) !important;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 5px black;
|
||||
text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
|
||||
@@ -19,11 +19,24 @@
|
||||
color: var(--SmartThemeBodyColor);
|
||||
}
|
||||
|
||||
.select2-container .select2-search__field {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
color: var(--SmartThemeBodyColor);
|
||||
line-height: revert;
|
||||
padding-left: unset;
|
||||
}
|
||||
|
||||
.select2-container .select2-results>.select2-results__options {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice__remove {
|
||||
padding: revert;
|
||||
border-right: 1px solid var(--white30a);
|
||||
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||
font-size: 1.1em;
|
||||
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice__display {
|
||||
@@ -34,7 +47,7 @@
|
||||
.select2-search__field {
|
||||
background-color: var(--black30a);
|
||||
color: var(--SmartThemeBodyColor);
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 7px;
|
||||
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
||||
padding: 3px 5px;
|
||||
@@ -58,27 +71,30 @@
|
||||
background-color: var(--SmartThemeBodyColor);
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--multiple {
|
||||
.select2-container .select2-selection--multiple,
|
||||
.select2-container .select2-selection--single {
|
||||
background-color: var(--black30a);
|
||||
color: var(--SmartThemeBodyColor);
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 7px;
|
||||
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.select2-container.select2-container--focus .select2-selection--multiple {
|
||||
border: 1px solid var(--white30a);
|
||||
.select2-container.select2-container--focus .select2-selection--multiple,
|
||||
.select2-container.select2-container--focus .select2-selection--single {
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice {
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice,
|
||||
.select2-container .select2-selection--single .select2-selection__choice {
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
color: var(--SmartThemeBodyColor);
|
||||
background-color: var(--black30a);
|
||||
border-color: var(--white30a);
|
||||
border-color: var(--SmartThemeBorderColor);
|
||||
font-size: calc(var(--mainFontSize) - 5%);
|
||||
text-shadow: none !important;
|
||||
}
|
||||
@@ -114,12 +130,13 @@
|
||||
margin-top: -7px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice__remove {
|
||||
.select2-container .select2-selection--multiple .select2-selection__choice__remove,
|
||||
.select2-container .select2-selection--single .select2-selection__choice__remove {
|
||||
color: var(--SmartThemeBodyColor);
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,16 @@
|
||||
color: var(--fullred);
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
color: black;
|
||||
background-color: yellow;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
.m-t-0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.m-t-1 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -52,23 +62,24 @@
|
||||
|
||||
.margin-bot-10px,
|
||||
.marginBot10 {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.marginTop10 {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.marginBot5 {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
.marginTop5 {
|
||||
margin-top: 5px;
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
|
||||
.marginTopBot5 {
|
||||
margin: 5px 0;
|
||||
margin-top: 5px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
.margin5 {
|
||||
@@ -95,10 +106,22 @@
|
||||
align-items: flex-end !important;
|
||||
}
|
||||
|
||||
.alignItemsBaseline {
|
||||
align-items: baseline !important;
|
||||
}
|
||||
|
||||
.alignSelfStart {
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.gap0 {
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
.gap3px {
|
||||
gap: 3px !important;
|
||||
}
|
||||
|
||||
.gap5px {
|
||||
gap: 5px !important;
|
||||
}
|
||||
@@ -107,6 +130,14 @@
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
.gap10h20v {
|
||||
gap: 10px 20px !important;
|
||||
}
|
||||
|
||||
.gap10h5v {
|
||||
gap: 5px 10px !important;
|
||||
}
|
||||
|
||||
.wide10pMinFit {
|
||||
width: 10%;
|
||||
min-width: fit-content;
|
||||
@@ -120,6 +151,10 @@
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.width100px {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.widthUnset {
|
||||
width: unset;
|
||||
}
|
||||
@@ -132,6 +167,10 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.height100p {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.height100pSpaceEvenly {
|
||||
align-content: space-evenly;
|
||||
height: 100%;
|
||||
@@ -147,6 +186,10 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.textAlignCenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.margin-right-10px {
|
||||
margin-right: 10px;
|
||||
}
|
||||
@@ -186,6 +229,26 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flexBasis100p {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.flexBasis50p {
|
||||
flex-basis: 50%
|
||||
}
|
||||
|
||||
.flexBasis25p {
|
||||
flex-basis: 25%
|
||||
}
|
||||
|
||||
.flexBasis200px {
|
||||
flex-basis: 200px
|
||||
}
|
||||
|
||||
.flexBasis48p {
|
||||
flex-basis: 48%
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
@@ -200,6 +263,14 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.flexShrink {
|
||||
flex-shrink: 1
|
||||
}
|
||||
|
||||
.flexWrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flexnowrap {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
@@ -226,10 +297,18 @@
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
.alignContentCenter {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.overflowHidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.padding0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.padding5 {
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -262,6 +341,10 @@
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.flexFlowRow {
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
.wideMinContent {
|
||||
width: min-content;
|
||||
}
|
||||
@@ -270,10 +353,6 @@
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.wide50p {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.wide25p {
|
||||
width: 25%;
|
||||
}
|
||||
@@ -357,11 +436,20 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hoverglow {
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
.hoverglow:hover {
|
||||
opacity: 1 !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:disabled,
|
||||
textarea:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.debug-red {
|
||||
border: 1px solid red !important;
|
||||
}
|
||||
@@ -382,6 +470,10 @@
|
||||
border: 1px solid purple !important;
|
||||
}
|
||||
|
||||
.fontsize120p {
|
||||
font-size: calc(var(--mainFontSize) * 1.2) !important;
|
||||
}
|
||||
|
||||
.fontsize80p {
|
||||
font-size: calc(var(--mainFontSize) * 0.8) !important;
|
||||
}
|
||||
@@ -390,6 +482,11 @@
|
||||
font-size: calc(var(--mainFontSize) * 0.6) !important;
|
||||
}
|
||||
|
||||
.paddingBottom5px {
|
||||
padding: unset;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.paddingTopBot5 {
|
||||
padding: 5px 0;
|
||||
}
|
||||
@@ -415,6 +512,22 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.opacity50p {
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
.opacity1 {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.circleborder30px {
|
||||
right: 30px;
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--SmartThemeBodyColor);
|
||||
border-radius: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
#bulk_tags_div,
|
||||
#tags_div {
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -12,7 +13,7 @@
|
||||
.tag_view_item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@@ -86,10 +87,12 @@
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
#bulkTagsList,
|
||||
#tagList.tags {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#bulkTagsList,
|
||||
#tagList .tag {
|
||||
opacity: 0.6;
|
||||
}
|
||||
@@ -109,6 +112,7 @@
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
.tags_inline .tag {
|
||||
@@ -128,10 +132,16 @@
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
filter: brightness(0.8);
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
.tags_view,
|
||||
.open_alternate_greetings {
|
||||
.rm_tag_filter .tag:hover {
|
||||
|
||||
opacity: 1;
|
||||
filter: brightness(1);
|
||||
}
|
||||
|
||||
.tags_view {
|
||||
margin: 0;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
@@ -17,6 +17,13 @@ body.no-modelIcons .icon-svg {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.square-avatars .avatar,
|
||||
body.square-avatars .avatar img,
|
||||
body.square-avatars .hotswapAvatar,
|
||||
body.square-avatars .hotswapAvatar img {
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
/*char list grid mode*/
|
||||
|
||||
body.charListGrid #rm_print_characters_block {
|
||||
@@ -28,6 +35,7 @@ body.charListGrid #rm_print_characters_block {
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_select,
|
||||
body.charListGrid #rm_print_characters_block .character_select {
|
||||
width: 30%;
|
||||
align-items: flex-start;
|
||||
@@ -37,6 +45,7 @@ body.charListGrid #rm_print_characters_block .character_select {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_select .ch_name,
|
||||
body.charListGrid #rm_print_characters_block .character_select .ch_name,
|
||||
body.charListGrid #rm_print_characters_block .group_select .ch_name {
|
||||
width: 100%;
|
||||
@@ -45,10 +54,12 @@ body.charListGrid #rm_print_characters_block .group_select .ch_name {
|
||||
font-size: calc(var(--mainFontSize) * .8);
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_select .character_name_block,
|
||||
body.charListGrid #rm_print_characters_block .character_select .character_name_block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_select .character_select_container,
|
||||
body.charListGrid #rm_print_characters_block .character_select .character_select_container {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
@@ -68,6 +79,7 @@ body.charListGrid #rm_print_characters_block .group_select .group_name_block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_counter_block,
|
||||
body.charListGrid #rm_print_characters_block .ch_description,
|
||||
body.charListGrid #rm_print_characters_block .tags_inline,
|
||||
body.charListGrid #rm_print_characters_block .character_version,
|
||||
@@ -117,7 +129,7 @@ body.big-avatars .avatar img {
|
||||
height: 90px;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
border: 1px solid var(--black30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
@@ -191,7 +203,7 @@ body.bubblechat .mes {
|
||||
border-radius: 10px;
|
||||
background-color: var(--SmartThemeBotMesBlurTintColor);
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid var(--white30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
body.bubblechat .mes[is_user="true"] {
|
||||
@@ -257,31 +269,16 @@ body.no-blur #bg_custom {
|
||||
|
||||
}
|
||||
|
||||
body:not(.bubblechat).no-blur #chat,
|
||||
body.no-blur #top-bar,
|
||||
body.no-blur #send_form {
|
||||
background-color: var(--SmartThemeBlurTintColor) !important;
|
||||
}
|
||||
|
||||
body.no-blur #options,
|
||||
body.no-blur .ui-widget-content,
|
||||
body.no-blur #floatingPrompt,
|
||||
body.no-blur #extensionsMenu,
|
||||
body.no-blur .list-group,
|
||||
body.no-blur #character_popup,
|
||||
body.no-blur #world_popup,
|
||||
body.no-blur #dialogue_popup,
|
||||
body.no-blur #select_chat_popup,
|
||||
body.no-blur .drawer-content,
|
||||
body.no-blur .select2-results__options {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
/* wAIfu mode*/
|
||||
|
||||
body.waifuMode #top-bar {
|
||||
border-radius: 0 0 20px 20px;
|
||||
border: 1px solid var(--grey30a);
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
body.waifuMode #sheld {
|
||||
@@ -292,7 +289,7 @@ body.waifuMode #sheld {
|
||||
}
|
||||
|
||||
body.waifuMode #chat {
|
||||
border-top: 1px solid var(--grey30a);
|
||||
border-top: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
@@ -343,8 +340,7 @@ body.movingUI .drawer-content,
|
||||
body.movingUI #expression-holder,
|
||||
body.movingUI .zoomed_avatar,
|
||||
body.movingUI .draggable,
|
||||
body.movingUI #floatingPrompt,
|
||||
body.movingUI #groupMemberListPopout {
|
||||
body.movingUI #floatingPrompt {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
@@ -353,6 +349,7 @@ body.movingUI #groupMemberListPopout {
|
||||
height: 120px;
|
||||
margin-top: 0;
|
||||
top: 50px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/*No Text Shadows Mode*/
|
||||
@@ -360,3 +357,19 @@ body.movingUI #groupMemberListPopout {
|
||||
body.noShadows * {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
body.expandMessageActions .mes .mes_buttons .extraMesButtons {
|
||||
display: inherit !important;
|
||||
}
|
||||
|
||||
body.expandMessageActions .mes .mes_buttons .extraMesButtonsHint {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#openai_image_inlining:not(:checked)~#image_inlining_hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#openai_image_inlining:checked~#image_inlining_hint {
|
||||
display: block;
|
||||
}
|
||||
|
@@ -101,7 +101,7 @@
|
||||
height: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
min-height: 32px;
|
||||
min-height: calc(var(--mainFontSize) + 13px);
|
||||
}
|
||||
|
||||
.delete_entry_button {
|
||||
@@ -157,6 +157,37 @@
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
#world_info_search {
|
||||
width: 10em;
|
||||
#world_info_search,
|
||||
#world_info_sort_order {
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
.wi-card-entry {
|
||||
border: 1px solid;
|
||||
border-color: var(--SmartThemeBorderColor);
|
||||
border-radius: 10px;
|
||||
padding: 0 5px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.world_entry {
|
||||
transition: opacity 500ms;
|
||||
}
|
||||
|
||||
.disabledWIEntry {
|
||||
opacity: 0.4;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.disabledWIEntry:not(input):hover {
|
||||
opacity: 1;
|
||||
filter: grayscale(0.5);
|
||||
}
|
||||
|
||||
.height32px {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.WIEntryHeaderTitleMobile {
|
||||
display: none;
|
||||
}
|
||||
|
884
public/i18n.json
884
public/i18n.json
File diff suppressed because it is too large
Load Diff
71
public/img/aphrodite.svg
Normal file
71
public/img/aphrodite.svg
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 500 500"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="aphrodite.svg"
|
||||
inkscape:version="1.3 (0e150ed, 2023-07-21)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.472"
|
||||
inkscape:cx="251.05932"
|
||||
inkscape:cy="250"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="449"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg6" />
|
||||
<g
|
||||
transform="matrix(1.3637143,0,0,1.2306337,286.98714,309.0439)"
|
||||
id="b08450db-4034-4e8d-9232-9d086fc10fd0" />
|
||||
<g
|
||||
transform="matrix(1.3637143,0,0,1.2306337,286.98714,309.0439)"
|
||||
id="54daa6c1-4b17-4e19-b0bb-42d1bcbfe659" />
|
||||
<g
|
||||
transform="matrix(1.3637143,0,0,1.2306337,186.0314,431.30731)"
|
||||
id="g2" />
|
||||
<g
|
||||
transform="matrix(1.3637143,0,0,1.2306337,288.29633,320.27957)"
|
||||
id="g3" />
|
||||
<g
|
||||
transform="matrix(1.686936,0,0,1.507445,388.05263,106.65182)"
|
||||
id="g6"
|
||||
style="">
|
||||
<g
|
||||
id="g5"
|
||||
style="">
|
||||
<path
|
||||
style="opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;"
|
||||
vector-effect="non-scaling-stroke"
|
||||
d="m -189.927,161.041 32.809,-32.022 47.368,38.876 -32.619,43.738 -87.665,49.304 z"
|
||||
stroke-linecap="round"
|
||||
id="path3" />
|
||||
<path
|
||||
style="opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;"
|
||||
vector-effect="non-scaling-stroke"
|
||||
d="m -64.913,42.392 32.651,28.068 -77.49,97.438 -47.367,-38.878 91.346,-87.359 z"
|
||||
stroke-linecap="round"
|
||||
id="path4" />
|
||||
<path
|
||||
style="opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;"
|
||||
vector-effect="non-scaling-stroke"
|
||||
d="m 46.895,-67.722 -2.202,2.004 -110.467,107.379 33.512,28.799 95.769,-121.944 0.023,-0.025 c 2.011,-2.328 2.952,-5.03 2.819,-8.105 -0.131,-3.074 -1.3,-5.686 -3.502,-7.834 -2.205,-2.148 -4.846,-3.248 -7.922,-3.3 -3.077,-0.054 -5.754,0.955 -8.03,3.026 z"
|
||||
stroke-linecap="round"
|
||||
id="path5" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
3
public/img/mancer.svg
Normal file
3
public/img/mancer.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="128" height="128" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M115.36,61.84L70.22,50.49L114.45,2.4c0.41-0.45,0.43-1.13,0.05-1.6c-0.39-0.48-1.07-0.59-1.59-0.27 L12.3,61.98c-0.41,0.25-0.64,0.72-0.57,1.2c0.06,0.48,0.4,0.87,0.87,1.01l45.07,13.25L13.38,125.6c-0.42,0.46-0.44,1.15-0.04,1.61 c0.24,0.29,0.58,0.44,0.94,0.44c0.22,0,0.45-0.06,0.65-0.19l100.78-63.41c0.42-0.26,0.64-0.75,0.56-1.22 C116.19,62.34,115.84,61.95,115.36,61.84z" />
|
||||
</svg>
|
After Width: | Height: | Size: 561 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="33" height="41" viewBox="0 0 33 41" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.89418 31.9285C4.51814 29.6818 2.83212 27.8112 0.836131 26.521C0.26793 26.1537 0.124452 25.3382 0.540438 24.8047C4.15593 20.1672 9.79294 8.01868 12.7415 1.40215C13.181 0.416062 14.6883 0.738582 14.6883 1.81816V19.44C13.1242 20.1331 12.0332 21.6992 12.0332 23.5201C12.0332 24.1851 12.1787 24.8161 12.4397 25.383L5.89418 31.9285ZM7.34675 34.6814C8.03773 36.2042 8.61427 37.8368 9.07635 39.5334C9.19588 39.9722 9.59101 40.2824 10.0459 40.2824H16.4937H22.9416C23.3964 40.2824 23.7916 39.9722 23.9111 39.5334C24.3732 37.8368 24.9497 36.2042 25.6407 34.6814L22.211 31.2516L19.3551 34.1075C19.4281 34.3655 19.4672 34.6378 19.4672 34.9192C19.4672 36.5615 18.1358 37.8928 16.4935 37.8928C14.8512 37.8928 13.5198 36.5615 13.5198 34.9192C13.5198 33.2768 14.8512 31.9455 16.4935 31.9455C16.7448 31.9455 16.9888 31.9766 17.2219 32.0353L20.1083 29.1489L18.4762 27.5169C17.879 27.8137 17.2058 27.9806 16.4937 27.9806C15.7816 27.9806 15.1084 27.8137 14.5112 27.5169L7.34675 34.6814ZM27.0933 31.9285C28.4693 29.6818 30.1553 27.8112 32.1513 26.521C32.7195 26.1537 32.863 25.3382 32.447 24.8047C28.8315 20.1672 23.1945 8.01868 20.2459 1.40215C19.8065 0.416062 18.2992 0.738582 18.2992 1.81816V19.44C19.8632 20.1332 20.9542 21.6992 20.9542 23.5201C20.9542 24.1851 20.8087 24.8161 20.5478 25.383L27.0933 31.9285Z" fill="white"/>
|
||||
<svg width="33" height="41" viewBox="0 0 33 41" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.89418 31.9285C4.51814 29.6818 2.83212 27.8112 0.836131 26.521C0.26793 26.1537 0.124452 25.3382 0.540438 24.8047C4.15593 20.1672 9.79294 8.01868 12.7415 1.40215C13.181 0.416062 14.6883 0.738582 14.6883 1.81816V19.44C13.1242 20.1331 12.0332 21.6992 12.0332 23.5201C12.0332 24.1851 12.1787 24.8161 12.4397 25.383L5.89418 31.9285ZM7.34675 34.6814C8.03773 36.2042 8.61427 37.8368 9.07635 39.5334C9.19588 39.9722 9.59101 40.2824 10.0459 40.2824H16.4937H22.9416C23.3964 40.2824 23.7916 39.9722 23.9111 39.5334C24.3732 37.8368 24.9497 36.2042 25.6407 34.6814L22.211 31.2516L19.3551 34.1075C19.4281 34.3655 19.4672 34.6378 19.4672 34.9192C19.4672 36.5615 18.1358 37.8928 16.4935 37.8928C14.8512 37.8928 13.5198 36.5615 13.5198 34.9192C13.5198 33.2768 14.8512 31.9455 16.4935 31.9455C16.7448 31.9455 16.9888 31.9766 17.2219 32.0353L20.1083 29.1489L18.4762 27.5169C17.879 27.8137 17.2058 27.9806 16.4937 27.9806C15.7816 27.9806 15.1084 27.8137 14.5112 27.5169L7.34675 34.6814ZM27.0933 31.9285C28.4693 29.6818 30.1553 27.8112 32.1513 26.521C32.7195 26.1537 32.863 25.3382 32.447 24.8047C28.8315 20.1672 23.1945 8.01868 20.2459 1.40215C19.8065 0.416062 18.2992 0.738582 18.2992 1.81816V19.44C19.8632 20.1332 20.9542 21.6992 20.9542 23.5201C20.9542 24.1851 20.8087 24.8161 20.5478 25.383L27.0933 31.9285Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
67
public/img/palm.svg
Normal file
67
public/img/palm.svg
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Standard_product_icon__x28_1:1_x29_"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="192px" height="192px" viewBox="0 0 192 192" enable-background="new 0 0 192 192" xml:space="preserve">
|
||||
<symbol id="material_x5F_product_x5F_standard_x5F_icon_x5F_keylines_00000077318920148093339210000006245950728745084294_" viewBox="-96 -96 192 192">
|
||||
<g opacity="0.4">
|
||||
<defs>
|
||||
<path id="SVGID_1_" opacity="0.4" d="M-96,96V-96H96V96H-96z"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_00000071517564283228984050000017848131202901217410_">
|
||||
<use xlink:href="#SVGID_1_" overflow="visible"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#SVGID_00000071517564283228984050000017848131202901217410_)">
|
||||
<g>
|
||||
<path d="M95.75,95.75v-191.5h-191.5v191.5H95.75 M96,96H-96V-96H96V96L96,96z"/>
|
||||
</g>
|
||||
<circle fill="none" stroke="#000000" stroke-width="0.25" stroke-miterlimit="10" cx="0" cy="0" r="64"/>
|
||||
</g>
|
||||
|
||||
<circle clip-path="url(#SVGID_00000071517564283228984050000017848131202901217410_)" fill="none" stroke="#000000" stroke-width="0.25" stroke-miterlimit="10" cx="0" cy="0" r="88"/>
|
||||
|
||||
<path clip-path="url(#SVGID_00000071517564283228984050000017848131202901217410_)" fill="none" stroke="#000000" stroke-width="0.25" stroke-miterlimit="10" d="
|
||||
M64,76H-64c-6.6,0-12-5.4-12-12V-64c0-6.6,5.4-12,12-12H64c6.6,0,12,5.4,12,12V64C76,70.6,70.6,76,64,76z"/>
|
||||
|
||||
<path clip-path="url(#SVGID_00000071517564283228984050000017848131202901217410_)" fill="none" stroke="#000000" stroke-width="0.25" stroke-miterlimit="10" d="
|
||||
M52,88H-52c-6.6,0-12-5.4-12-12V-76c0-6.6,5.4-12,12-12H52c6.6,0,12,5.4,12,12V76C64,82.6,58.6,88,52,88z"/>
|
||||
|
||||
<path clip-path="url(#SVGID_00000071517564283228984050000017848131202901217410_)" fill="none" stroke="#000000" stroke-width="0.25" stroke-miterlimit="10" d="
|
||||
M76,64H-76c-6.6,0-12-5.4-12-12V-52c0-6.6,5.4-12,12-12H76c6.6,0,12,5.4,12,12V52C88,58.6,82.6,64,76,64z"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<rect id="bounding_box_1_" display="none" fill="none" width="192" height="192"/>
|
||||
<g id="art_layer">
|
||||
<g>
|
||||
<path d="M96,181.92L96,181.92c6.63,0,12-5.37,12-12v-104H84v104C84,176.55,89.37,181.92,96,181.92z"/>
|
||||
<g>
|
||||
<path d="M143.81,103.87C130.87,90.94,111.54,88.32,96,96l51.37,51.37c2.12,2.12,5.77,1.28,6.67-1.57
|
||||
C158.56,131.49,155.15,115.22,143.81,103.87z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M48.19,103.87C61.13,90.94,80.46,88.32,96,96l-51.37,51.37c-2.12,2.12-5.77,1.28-6.67-1.57
|
||||
C33.44,131.49,36.85,115.22,48.19,103.87z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M140,64c-20.44,0-37.79,13.4-44,32h81.24c3.33,0,5.55-3.52,4.04-6.49C173.56,74.36,157.98,64,140,64z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M104.49,42.26C90.03,56.72,87.24,78.45,96,96l57.45-57.45c2.36-2.36,1.44-6.42-1.73-7.45
|
||||
C135.54,25.85,117.2,29.55,104.49,42.26z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M87.51,42.26C101.97,56.72,104.76,78.45,96,96L38.55,38.55c-2.36-2.36-1.44-6.42,1.73-7.45
|
||||
C56.46,25.85,74.8,29.55,87.51,42.26z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M52,64c20.44,0,37.79,13.4,44,32H14.76c-3.33,0-5.55-3.52-4.04-6.49C18.44,74.36,34.02,64,52,64z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="keylines" display="none">
|
||||
|
||||
<use xlink:href="#material_x5F_product_x5F_standard_x5F_icon_x5F_keylines_00000077318920148093339210000006245950728745084294_" width="192" height="192" id="material_x5F_product_x5F_standard_x5F_icon_x5F_keylines" x="-96" y="-96" transform="matrix(1 0 0 -1 96 96)" display="inline" overflow="visible"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
3800
public/index.html
3800
public/index.html
File diff suppressed because it is too large
Load Diff
18
public/instruct/Adventure.json
Normal file
18
public/instruct/Adventure.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"system_prompt": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]",
|
||||
"input_sequence": "",
|
||||
"output_sequence": "",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence": "",
|
||||
"stop_sequence": "",
|
||||
"separator_sequence": "",
|
||||
"wrap": true,
|
||||
"macro": true,
|
||||
"names": false,
|
||||
"names_force_groups": false,
|
||||
"activation_regex": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"first_output_sequence": "",
|
||||
"name": "Adventure"
|
||||
}
|
17
public/instruct/ChatML.json
Normal file
17
public/instruct/ChatML.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"wrap": false,
|
||||
"names": true,
|
||||
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
|
||||
"system_sequence_prefix": "",
|
||||
"stop_sequence": "",
|
||||
"input_sequence": "<|im_start|>user\n",
|
||||
"output_sequence": "<|im_end|>\n<|im_start|>assistant\n",
|
||||
"separator_sequence": "<|im_end|>\n",
|
||||
"macro": true,
|
||||
"names_force_groups": true,
|
||||
"last_output_sequence": "",
|
||||
"activation_regex": "",
|
||||
"first_output_sequence": "<|im_start|>assistant\n",
|
||||
"system_sequence_suffix": "",
|
||||
"name": "ChatML"
|
||||
}
|
17
public/instruct/Libra-32B.json
Normal file
17
public/instruct/Libra-32B.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"wrap": true,
|
||||
"names": true,
|
||||
"system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
|
||||
"system_sequence_prefix": "",
|
||||
"stop_sequence": "",
|
||||
"input_sequence": "",
|
||||
"output_sequence": "",
|
||||
"separator_sequence": "",
|
||||
"macro": true,
|
||||
"names_force_groups": true,
|
||||
"last_output_sequence": "\n### Response:",
|
||||
"activation_regex": "",
|
||||
"first_output_sequence": "",
|
||||
"system_sequence_suffix": "",
|
||||
"name": "Libra-32B"
|
||||
}
|
18
public/instruct/Lightning 1.1.json
Normal file
18
public/instruct/Lightning 1.1.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"wrap": true,
|
||||
"names": false,
|
||||
"system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n",
|
||||
"system_sequence": "",
|
||||
"stop_sequence": "",
|
||||
"input_sequence": "### Instruction:",
|
||||
"output_sequence": "### Response: (length = unlimited)",
|
||||
"separator_sequence": "",
|
||||
"macro": true,
|
||||
"names_force_groups": true,
|
||||
"last_output_sequence": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"first_output_sequence": "",
|
||||
"activation_regex": "",
|
||||
"name": "Lightning 1.1"
|
||||
}
|
17
public/instruct/Mistral.json
Normal file
17
public/instruct/Mistral.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"wrap": false,
|
||||
"names": true,
|
||||
"system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
|
||||
"system_sequence_prefix": "",
|
||||
"stop_sequence": "",
|
||||
"input_sequence": "[INST] ",
|
||||
"output_sequence": " [/INST]\n",
|
||||
"separator_sequence": "\n",
|
||||
"macro": true,
|
||||
"names_force_groups": true,
|
||||
"last_output_sequence": "",
|
||||
"activation_regex": "",
|
||||
"first_output_sequence": "\n",
|
||||
"system_sequence_suffix": "",
|
||||
"name": "Mistral"
|
||||
}
|
17
public/instruct/Pygmalion.json
Normal file
17
public/instruct/Pygmalion.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Pygmalion",
|
||||
"system_prompt": "Enter RP mode. You shall reply to {{user}} while staying in character. Your responses must be detailed, creative, immersive, and drive the scenario forward. You will follow {{char}}'s persona.",
|
||||
"input_sequence": "<|user|>",
|
||||
"output_sequence": "<|model|>",
|
||||
"first_output_sequence": "",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence_prefix": "<|system|>",
|
||||
"system_sequence_suffix": "",
|
||||
"stop_sequence": "<|user|>",
|
||||
"separator_sequence": "",
|
||||
"wrap": false,
|
||||
"macro": true,
|
||||
"names": true,
|
||||
"names_force_groups": true,
|
||||
"activation_regex": ""
|
||||
}
|
18
public/instruct/Story.json
Normal file
18
public/instruct/Story.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"system_prompt": "",
|
||||
"input_sequence": "",
|
||||
"output_sequence": "",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence": "",
|
||||
"stop_sequence": "",
|
||||
"separator_sequence": "",
|
||||
"wrap": true,
|
||||
"macro": true,
|
||||
"names": false,
|
||||
"names_force_groups": false,
|
||||
"activation_regex": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"first_output_sequence": "",
|
||||
"name": "Story"
|
||||
}
|
17
public/instruct/Synthia.json
Normal file
17
public/instruct/Synthia.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"wrap": false,
|
||||
"names": false,
|
||||
"system_prompt": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation.",
|
||||
"system_sequence_prefix": "SYSTEM: ",
|
||||
"stop_sequence": "",
|
||||
"input_sequence": "USER: ",
|
||||
"output_sequence": "\nASSISTANT: ",
|
||||
"separator_sequence": "\n",
|
||||
"macro": true,
|
||||
"names_force_groups": true,
|
||||
"last_output_sequence": "",
|
||||
"activation_regex": "",
|
||||
"first_output_sequence": "ASSISTANT: ",
|
||||
"system_sequence_suffix": "",
|
||||
"name": "Synthia"
|
||||
}
|
25
public/lib/select2-search-placeholder.js
Normal file
25
public/lib/select2-search-placeholder.js
Normal file
@@ -0,0 +1,25 @@
|
||||
(function($) {
|
||||
|
||||
var Defaults = $.fn.select2.amd.require('select2/defaults');
|
||||
|
||||
$.extend(Defaults.defaults, {
|
||||
searchInputPlaceholder: '',
|
||||
searchInputCssClass: '',
|
||||
});
|
||||
|
||||
var SearchDropdown = $.fn.select2.amd.require('select2/dropdown/search');
|
||||
|
||||
var _renderSearchDropdown = SearchDropdown.prototype.render;
|
||||
|
||||
SearchDropdown.prototype.render = function(decorated) {
|
||||
|
||||
// invoke parent method
|
||||
var $rendered = _renderSearchDropdown.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
this.$search.attr('placeholder', this.options.get('searchInputPlaceholder'));
|
||||
this.$search.addClass(this.options.get('searchInputCssClass'));
|
||||
|
||||
return $rendered;
|
||||
};
|
||||
|
||||
})(window.jQuery);
|
30
public/manifest.json
Normal file
30
public/manifest.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "SillyTavern",
|
||||
"short_name": "SillyTavern",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "#202124",
|
||||
"background_color": "#202124",
|
||||
"icons": [
|
||||
{
|
||||
"src": "img/apple-icon-57x57.png",
|
||||
"sizes": "57x57",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "img/apple-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "img/apple-icon-114x114.png",
|
||||
"sizes": "114x114",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "img/apple-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
3135
public/script.js
3135
public/script.js
File diff suppressed because it is too large
Load Diff
652
public/scripts/BulkEditOverlay.js
Normal file
652
public/scripts/BulkEditOverlay.js
Normal file
@@ -0,0 +1,652 @@
|
||||
"use strict";
|
||||
|
||||
import {
|
||||
callPopup,
|
||||
characters,
|
||||
deleteCharacter,
|
||||
event_types,
|
||||
eventSource,
|
||||
getCharacters,
|
||||
getRequestHeaders,
|
||||
printCharacters,
|
||||
this_chid
|
||||
} from "../script.js";
|
||||
|
||||
import { favsToHotswap } from "./RossAscends-mods.js";
|
||||
import { hideLoader, showLoader } from "./loader.js";
|
||||
import { convertCharacterToPersona } from "./personas.js";
|
||||
import { createTagInput, getTagKeyForCharacter, tag_map } from "./tags.js";
|
||||
|
||||
// Utility object for popup messages.
|
||||
const popupMessage = {
|
||||
deleteChat(characterCount) {
|
||||
return `<h3>Delete ${characterCount} characters?</h3>
|
||||
<b>THIS IS PERMANENT!<br><br>
|
||||
<label for="del_char_checkbox" class="checkbox_label justifyCenter">
|
||||
<input type="checkbox" id="del_char_checkbox" />
|
||||
<span>Also delete the chat files</span>
|
||||
</label><br></b>`;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Static object representing the actions of the
|
||||
* character context menu override.
|
||||
*/
|
||||
class CharacterContextMenu {
|
||||
/**
|
||||
* Tag one or more characters,
|
||||
* opens a popup.
|
||||
*
|
||||
* @param selectedCharacters
|
||||
*/
|
||||
static tag = (selectedCharacters) => {
|
||||
BulkTagPopupHandler.show(selectedCharacters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate one or more characters
|
||||
*
|
||||
* @param characterId
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
static duplicate = async (characterId) => {
|
||||
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||
|
||||
return fetch('/dupecharacter', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ avatar_url: character.avatar }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Favorite a character
|
||||
* and highlight it.
|
||||
*
|
||||
* @param characterId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static favorite = async (characterId) => {
|
||||
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||
|
||||
// Only set fav for V2 spec
|
||||
const data = {
|
||||
name: character.name,
|
||||
avatar: character.avatar,
|
||||
data: {
|
||||
extensions: {
|
||||
fav: !character.data.extensions.fav
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return fetch('/v2/editcharacterattribute', {
|
||||
method: "POST",
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
const element = document.getElementById(`CharID${characterId}`);
|
||||
element.classList.toggle('is_fav');
|
||||
} else {
|
||||
response.json().then(json => toastr.error('Character not saved. Error: ' + json.message + '. Field: ' + json.error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert one or more characters to persona,
|
||||
* may open a popup for one or more characters.
|
||||
*
|
||||
* @param characterId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static persona = async (characterId) => await convertCharacterToPersona(characterId);
|
||||
|
||||
/**
|
||||
* Delete one or more characters,
|
||||
* opens a popup.
|
||||
*
|
||||
* @param characterId
|
||||
* @param deleteChats
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static delete = async (characterId, deleteChats = false) => {
|
||||
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||
|
||||
return fetch('/deletecharacter', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ avatar_url: character.avatar, delete_chats: deleteChats }),
|
||||
cache: 'no-cache',
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
deleteCharacter(character.name, character.avatar).then(() => {
|
||||
if (deleteChats) {
|
||||
fetch("/getallchatsofcharacter", {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ avatar_url: character.avatar }),
|
||||
headers: getRequestHeaders(),
|
||||
}).then((response) => {
|
||||
let data = response.json();
|
||||
data = Object.values(data);
|
||||
const pastChats = data.sort((a, b) => a["file_name"].localeCompare(b["file_name"])).reverse();
|
||||
|
||||
for (const chat of pastChats) {
|
||||
const name = chat.file_name.replace('.jsonl', '');
|
||||
eventSource.emit(event_types.CHAT_DELETED, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
eventSource.emit('characterDeleted', { id: this_chid, character: characters[this_chid] });
|
||||
});
|
||||
}
|
||||
|
||||
static #getCharacter = (characterId) => characters[characterId] ?? null;
|
||||
|
||||
/**
|
||||
* Show the context menu at the given position
|
||||
*
|
||||
* @param positionX
|
||||
* @param positionY
|
||||
*/
|
||||
static show = (positionX, positionY) => {
|
||||
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
||||
contextMenu.style.left = `${positionX}px`;
|
||||
contextMenu.style.top = `${positionY}px`;
|
||||
|
||||
document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden');
|
||||
|
||||
// Adjust position if context menu is outside of viewport
|
||||
const boundingRect = contextMenu.getBoundingClientRect();
|
||||
if (boundingRect.right > window.innerWidth) {
|
||||
contextMenu.style.left = `${positionX - (boundingRect.right - window.innerWidth)}px`;
|
||||
}
|
||||
if (boundingRect.bottom > window.innerHeight) {
|
||||
contextMenu.style.top = `${positionY - (boundingRect.bottom - window.innerHeight)}px`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the context menu
|
||||
*/
|
||||
static hide = () => document.getElementById(BulkEditOverlay.contextMenuId).classList.add('hidden');
|
||||
|
||||
/**
|
||||
* Sets up the context menu for the given overlay
|
||||
*
|
||||
* @param characterGroupOverlay
|
||||
*/
|
||||
constructor(characterGroupOverlay) {
|
||||
const contextMenuItems = [
|
||||
{ id: 'character_context_menu_favorite', callback: characterGroupOverlay.handleContextMenuFavorite },
|
||||
{ id: 'character_context_menu_duplicate', callback: characterGroupOverlay.handleContextMenuDuplicate },
|
||||
{ id: 'character_context_menu_delete', callback: characterGroupOverlay.handleContextMenuDelete },
|
||||
{ id: 'character_context_menu_persona', callback: characterGroupOverlay.handleContextMenuPersona },
|
||||
{ id: 'character_context_menu_tag', callback: characterGroupOverlay.handleContextMenuTag }
|
||||
];
|
||||
|
||||
contextMenuItems.forEach(contextMenuItem => document.getElementById(contextMenuItem.id).addEventListener('click', contextMenuItem.callback))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a tag control not bound to a single character
|
||||
*/
|
||||
class BulkTagPopupHandler {
|
||||
static #getHtml = (characterIds) => {
|
||||
const characterData = JSON.stringify({ characterIds: characterIds });
|
||||
return `<div id="bulk_tag_shadow_popup">
|
||||
<div id="bulk_tag_popup">
|
||||
<div id="bulk_tag_popup_holder">
|
||||
<h3 class="m-b-1">Add tags to ${characterIds.length} characters</h3>
|
||||
<br>
|
||||
<div id="bulk_tags_div" class="marginBot5" data-characters='${characterData}'>
|
||||
<div class="tag_controls">
|
||||
<input id="bulkTagInput" class="text_pole tag_input wide100p margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" maxlength="25" />
|
||||
<div class="tags_view menu_button fa-solid fa-tags" title="View all tags" data-i18n="[title]View all tags"></div>
|
||||
</div>
|
||||
<div id="bulkTagList" class="m-t-1 tags"></div>
|
||||
</div>
|
||||
<div id="dialogue_popup_controls" class="m-t-1">
|
||||
<div id="bulk_tag_popup_cancel" class="menu_button" data-i18n="Cancel">Close</div>
|
||||
<div id="bulk_tag_popup_reset" class="menu_button" data-i18n="Cancel">Remove all</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
|
||||
/**
|
||||
* Append and show the tag control
|
||||
*
|
||||
* @param characters - The characters assigned to this control
|
||||
*/
|
||||
static show(characters) {
|
||||
document.body.insertAdjacentHTML('beforeend', this.#getHtml(characters));
|
||||
createTagInput('#bulkTagInput', '#bulkTagList');
|
||||
document.querySelector('#bulk_tag_popup_cancel').addEventListener('click', this.hide.bind(this));
|
||||
document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this, characters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide and remove the tag control
|
||||
*/
|
||||
static hide() {
|
||||
let popupElement = document.querySelector('#bulk_tag_shadow_popup');
|
||||
if (popupElement) {
|
||||
document.body.removeChild(popupElement);
|
||||
}
|
||||
|
||||
printCharacters(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty the tag map for the given characters
|
||||
*
|
||||
* @param characterIds
|
||||
*/
|
||||
static resetTags(characterIds) {
|
||||
characterIds.forEach((characterId) => {
|
||||
const key = getTagKeyForCharacter(characterId);
|
||||
if (key) tag_map[key] = [];
|
||||
});
|
||||
|
||||
printCharacters(true);
|
||||
}
|
||||
}
|
||||
|
||||
class BulkEditOverlayState {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static browse = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static select = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement a SingletonPattern, allowing access to the group overlay instance
|
||||
* from everywhere via (new CharacterGroupOverlay())
|
||||
*
|
||||
* @type BulkEditOverlay
|
||||
*/
|
||||
let bulkEditOverlayInstance = null;
|
||||
|
||||
class BulkEditOverlay {
|
||||
static containerId = 'rm_print_characters_block';
|
||||
static contextMenuId = 'character_context_menu';
|
||||
static characterClass = 'character_select';
|
||||
static groupClass = 'group_select';
|
||||
static bogusFolderClass = 'bogus_folder_select';
|
||||
static selectModeClass = 'group_overlay_mode_select';
|
||||
static selectedClass = 'character_selected';
|
||||
static legacySelectedClass = 'bulk_select_checkbox';
|
||||
|
||||
static longPressDelay = 2500;
|
||||
|
||||
#state = BulkEditOverlayState.browse;
|
||||
#longPress = false;
|
||||
#stateChangeCallbacks = [];
|
||||
#selectedCharacters = [];
|
||||
|
||||
/**
|
||||
* Locks other pointer actions when the context menu is open
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#contextMenuOpen = false;
|
||||
|
||||
/**
|
||||
* Whether the next character select should be skipped
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#cancelNextToggle = false;
|
||||
|
||||
/**
|
||||
* @type HTMLElement
|
||||
*/
|
||||
container = null;
|
||||
|
||||
get state() {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
set state(newState) {
|
||||
if (this.#state === newState) return;
|
||||
|
||||
eventSource.emit(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_BEFORE, newState)
|
||||
.then(() => {
|
||||
this.#state = newState;
|
||||
eventSource.emit(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.state)
|
||||
});
|
||||
}
|
||||
|
||||
get isLongPress() {
|
||||
return this.#longPress;
|
||||
}
|
||||
|
||||
set isLongPress(longPress) {
|
||||
this.#longPress = longPress;
|
||||
}
|
||||
|
||||
get stateChangeCallbacks() {
|
||||
return this.#stateChangeCallbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {*[]}
|
||||
*/
|
||||
get selectedCharacters() {
|
||||
return this.#selectedCharacters;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
if (bulkEditOverlayInstance instanceof BulkEditOverlay)
|
||||
return bulkEditOverlayInstance
|
||||
|
||||
this.container = document.getElementById(BulkEditOverlay.containerId);
|
||||
|
||||
eventSource.on(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.handleStateChange);
|
||||
bulkEditOverlayInstance = Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the overlay to browse mode
|
||||
*/
|
||||
browseState = () => this.state = BulkEditOverlayState.browse;
|
||||
|
||||
/**
|
||||
* Set the overlay to select mode
|
||||
*/
|
||||
selectState = () => this.state = BulkEditOverlayState.select;
|
||||
|
||||
/**
|
||||
* Set up a Sortable grid for the loaded page
|
||||
*/
|
||||
onPageLoad = () => {
|
||||
this.browseState();
|
||||
|
||||
const elements = this.#getEnabledElements();
|
||||
elements.forEach(element => element.addEventListener('touchstart', this.handleHold));
|
||||
elements.forEach(element => element.addEventListener('mousedown', this.handleHold));
|
||||
elements.forEach(element => element.addEventListener('contextmenu', this.handleDefaultContextMenu));
|
||||
|
||||
elements.forEach(element => element.addEventListener('touchend', this.handleLongPressEnd));
|
||||
elements.forEach(element => element.addEventListener('mouseup', this.handleLongPressEnd));
|
||||
elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd));
|
||||
elements.forEach(element => element.addEventListener('touchmove', this.handleLongPressEnd));
|
||||
|
||||
// Cohee: It only triggers when clicking on a margin between the elements?
|
||||
// Feel free to fix or remove this, I'm not sure how to.
|
||||
//this.container.addEventListener('click', this.handleCancelClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle state changes
|
||||
*
|
||||
*
|
||||
*/
|
||||
handleStateChange = () => {
|
||||
switch (this.state) {
|
||||
case BulkEditOverlayState.browse:
|
||||
this.container.classList.remove(BulkEditOverlay.selectModeClass);
|
||||
this.#contextMenuOpen = false;
|
||||
this.#enableClickEventsForCharacters();
|
||||
this.#enableClickEventsForGroups();
|
||||
this.clearSelectedCharacters();
|
||||
this.disableContextMenu();
|
||||
this.#disableBulkEditButtonHighlight();
|
||||
CharacterContextMenu.hide();
|
||||
break;
|
||||
case BulkEditOverlayState.select:
|
||||
this.container.classList.add(BulkEditOverlay.selectModeClass);
|
||||
this.#disableClickEventsForCharacters();
|
||||
this.#disableClickEventsForGroups();
|
||||
this.enableContextMenu();
|
||||
this.#enableBulkEditButtonHighlight();
|
||||
break;
|
||||
}
|
||||
|
||||
this.stateChangeCallbacks.forEach(callback => callback(this.state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Block the browsers native context menu and
|
||||
* set a click event to hide the custom context menu.
|
||||
*/
|
||||
enableContextMenu = () => {
|
||||
this.container.addEventListener('contextmenu', this.handleContextMenuShow);
|
||||
document.addEventListener('click', this.handleContextMenuHide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event listeners, allowing the native browser context
|
||||
* menu to be opened.
|
||||
*/
|
||||
disableContextMenu = () => {
|
||||
this.container.removeEventListener('contextmenu', this.handleContextMenuShow);
|
||||
document.removeEventListener('click', this.handleContextMenuHide);
|
||||
}
|
||||
|
||||
handleDefaultContextMenu = (event) => {
|
||||
if (this.isLongPress) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens menu on long-press.
|
||||
*
|
||||
* @param event - Pointer event
|
||||
*/
|
||||
handleHold = (event) => {
|
||||
if (0 !== event.button && event.type !== 'touchstart') return;
|
||||
if (this.#contextMenuOpen) {
|
||||
this.#contextMenuOpen = false;
|
||||
this.#cancelNextToggle = true;
|
||||
CharacterContextMenu.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let cancel = false;
|
||||
|
||||
const cancelHold = (event) => cancel = true;
|
||||
this.container.addEventListener('mouseup', cancelHold);
|
||||
this.container.addEventListener('touchend', cancelHold);
|
||||
|
||||
this.isLongPress = true;
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.isLongPress && !cancel) {
|
||||
if (this.state === BulkEditOverlayState.browse) {
|
||||
this.selectState();
|
||||
} else if (this.state === BulkEditOverlayState.select) {
|
||||
this.#contextMenuOpen = true;
|
||||
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
||||
}
|
||||
}
|
||||
|
||||
this.container.removeEventListener('mouseup', cancelHold);
|
||||
this.container.removeEventListener('touchend', cancelHold);
|
||||
},
|
||||
BulkEditOverlay.longPressDelay);
|
||||
}
|
||||
|
||||
handleLongPressEnd = (event) => {
|
||||
this.isLongPress = false;
|
||||
if (this.#contextMenuOpen) event.stopPropagation();
|
||||
}
|
||||
|
||||
handleCancelClick = () => {
|
||||
if (false === this.#contextMenuOpen) this.state = BulkEditOverlayState.browse;
|
||||
this.#contextMenuOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the mouse/touch location
|
||||
*
|
||||
* @param event
|
||||
* @returns {(boolean|number|*)[]}
|
||||
*/
|
||||
#getContextMenuPosition = (event) => [
|
||||
event.clientX || event.touches[0].clientX,
|
||||
event.clientY || event.touches[0].clientY,
|
||||
];
|
||||
|
||||
#stopEventPropagation = (event) => {
|
||||
if (this.#contextMenuOpen) {
|
||||
this.handleContextMenuHide(event);
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
#enableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.removeEventListener('click', this.#stopEventPropagation));
|
||||
|
||||
#disableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.addEventListener('click', this.#stopEventPropagation));
|
||||
|
||||
#enableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.removeEventListener('click', this.toggleCharacterSelected));
|
||||
|
||||
#disableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.addEventListener('click', this.toggleCharacterSelected));
|
||||
|
||||
#enableBulkEditButtonHighlight = () => document.getElementById('bulkEditButton').classList.add('bulk_edit_overlay_active');
|
||||
|
||||
#disableBulkEditButtonHighlight = () => document.getElementById('bulkEditButton').classList.remove('bulk_edit_overlay_active');
|
||||
|
||||
#getEnabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)];
|
||||
|
||||
#getDisabledElements = () =>[...this.container.getElementsByClassName(BulkEditOverlay.groupClass), ...this.container.getElementsByClassName(BulkEditOverlay.bogusFolderClass)];
|
||||
|
||||
toggleCharacterSelected = event => {
|
||||
event.stopPropagation();
|
||||
|
||||
const character = event.currentTarget;
|
||||
const characterId = character.getAttribute('chid');
|
||||
|
||||
const alreadySelected = this.selectedCharacters.includes(characterId)
|
||||
|
||||
const legacyBulkEditCheckbox = character.querySelector('.' + BulkEditOverlay.legacySelectedClass);
|
||||
|
||||
// Only toggle when context menu is closed and wasn't just closed.
|
||||
if (!this.#contextMenuOpen && !this.#cancelNextToggle)
|
||||
if (alreadySelected) {
|
||||
character.classList.remove(BulkEditOverlay.selectedClass);
|
||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
|
||||
this.dismissCharacter(characterId);
|
||||
} else {
|
||||
character.classList.add(BulkEditOverlay.selectedClass)
|
||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
|
||||
this.selectCharacter(characterId);
|
||||
}
|
||||
|
||||
this.#cancelNextToggle = false;
|
||||
}
|
||||
|
||||
handleContextMenuShow = (event) => {
|
||||
event.preventDefault();
|
||||
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
||||
this.#contextMenuOpen = true;
|
||||
}
|
||||
|
||||
handleContextMenuHide = (event) => {
|
||||
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
||||
if (false === contextMenu.contains(event.target)) {
|
||||
CharacterContextMenu.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Concurrently handle character favorite requests.
|
||||
*
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
handleContextMenuFavorite = () => Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.favorite(characterId)))
|
||||
.then(() => getCharacters())
|
||||
.then(() => favsToHotswap())
|
||||
.then(() => this.browseState())
|
||||
|
||||
/**
|
||||
* Concurrently handle character duplicate requests.
|
||||
*
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
handleContextMenuDuplicate = () => Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.duplicate(characterId)))
|
||||
.then(() => getCharacters())
|
||||
.then(() => this.browseState())
|
||||
|
||||
/**
|
||||
* Sequentially handle all character-to-persona conversions.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
handleContextMenuPersona = async () => {
|
||||
for (const characterId of this.selectedCharacters) {
|
||||
await CharacterContextMenu.persona(characterId)
|
||||
}
|
||||
|
||||
this.browseState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request user input before concurrently handle deletion
|
||||
* requests.
|
||||
*
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
handleContextMenuDelete = () => {
|
||||
callPopup(
|
||||
popupMessage.deleteChat(this.selectedCharacters.length), null)
|
||||
.then((accept) => {
|
||||
if (true !== accept) return;
|
||||
|
||||
const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
|
||||
|
||||
showLoader();
|
||||
toastr.info("We're deleting your characters, please wait...", 'Working on it');
|
||||
Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
|
||||
.then(() => getCharacters())
|
||||
.then(() => this.browseState())
|
||||
.finally(() => hideLoader());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches and opens the tag menu
|
||||
*/
|
||||
handleContextMenuTag = () => {
|
||||
CharacterContextMenu.tag(this.selectedCharacters);
|
||||
}
|
||||
|
||||
addStateChangeCallback = callback => this.stateChangeCallbacks.push(callback);
|
||||
|
||||
selectCharacter = characterId => this.selectedCharacters.push(String(characterId));
|
||||
|
||||
dismissCharacter = characterId => this.#selectedCharacters = this.selectedCharacters.filter(item => String(characterId) !== item);
|
||||
|
||||
/**
|
||||
* Clears internal character storage and
|
||||
* removes visual highlight.
|
||||
*/
|
||||
clearSelectedCharacters = () => {
|
||||
document.querySelectorAll('#' + BulkEditOverlay.containerId + ' .' + BulkEditOverlay.selectedClass)
|
||||
.forEach(element => element.classList.remove(BulkEditOverlay.selectedClass));
|
||||
this.selectedCharacters.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export { BulkEditOverlayState, CharacterContextMenu, BulkEditOverlay };
|
@@ -2,7 +2,7 @@
|
||||
|
||||
import { callPopup, event_types, eventSource, is_send_press, main_api, substituteParams } from "../script.js";
|
||||
import { is_group_generating } from "./group-chats.js";
|
||||
import { TokenHandler } from "./openai.js";
|
||||
import { Message, TokenHandler } from "./openai.js";
|
||||
import { power_user } from "./power-user.js";
|
||||
import { debounce, waitUntilCondition, escapeHtml } from "./utils.js";
|
||||
|
||||
@@ -21,6 +21,16 @@ function debouncePromise(func, delay) {
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_DEPTH = 4;
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
export const INJECTION_POSITION = {
|
||||
RELATIVE: 0,
|
||||
ABSOLUTE: 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* Register migrations for the prompt manager when settings are loaded or an Open AI preset is loaded.
|
||||
*/
|
||||
@@ -60,7 +70,7 @@ const registerPromptManagerMigration = () => {
|
||||
* Represents a prompt.
|
||||
*/
|
||||
class Prompt {
|
||||
identifier; role; content; name; system_prompt;
|
||||
identifier; role; content; name; system_prompt; position; injection_position; injection_depth;
|
||||
|
||||
/**
|
||||
* Create a new Prompt instance.
|
||||
@@ -71,13 +81,19 @@ class Prompt {
|
||||
* @param {string} param0.content - The content of the prompt.
|
||||
* @param {string} param0.name - The name of the prompt.
|
||||
* @param {boolean} param0.system_prompt - Indicates if the prompt is a system prompt.
|
||||
* @param {string} param0.position - The position of the prompt in the prompt list.
|
||||
* @param {number} param0.injection_position - The insert position of the prompt.
|
||||
* @param {number} param0.injection_depth - The depth of the prompt in the chat.
|
||||
*/
|
||||
constructor({ identifier, role, content, name, system_prompt } = {}) {
|
||||
constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position } = {}) {
|
||||
this.identifier = identifier;
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
this.name = name;
|
||||
this.system_prompt = system_prompt;
|
||||
this.position = position;
|
||||
this.injection_depth = injection_depth;
|
||||
this.injection_position = injection_position;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +179,13 @@ class PromptCollection {
|
||||
}
|
||||
|
||||
function PromptManagerModule() {
|
||||
this.systemPrompts = [
|
||||
'main',
|
||||
'nsfw',
|
||||
'jailbreak',
|
||||
'enhanceDefinitions',
|
||||
];
|
||||
|
||||
this.configuration = {
|
||||
version: 1,
|
||||
prefix: '',
|
||||
@@ -273,7 +296,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
|
||||
this.serviceSettings = serviceSettings;
|
||||
this.containerElement = document.getElementById(this.configuration.containerIdentifier);
|
||||
|
||||
if ('global' === this.configuration.promptOrder.strategy) this.activeCharacter = {id: this.configuration.promptOrder.dummyId};
|
||||
if ('global' === this.configuration.promptOrder.strategy) this.activeCharacter = { id: this.configuration.promptOrder.dummyId };
|
||||
|
||||
this.sanitizeServiceSettings();
|
||||
|
||||
@@ -379,6 +402,13 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name').value = prompt.name;
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role').value = 'system';
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value = prompt.content;
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value = prompt.injection_position ?? 0;
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value = prompt.injection_depth ?? DEFAULT_DEPTH;
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
|
||||
if (!this.systemPrompts.includes(promptId)) {
|
||||
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
// Append prompt to selected character
|
||||
@@ -671,6 +701,8 @@ PromptManagerModule.prototype.updatePromptWithPromptEditForm = function (prompt)
|
||||
prompt.name = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name').value;
|
||||
prompt.role = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role').value;
|
||||
prompt.content = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value;
|
||||
prompt.injection_position = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value);
|
||||
prompt.injection_depth = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -700,6 +732,12 @@ PromptManagerModule.prototype.getTokenHandler = function () {
|
||||
return this.tokenHandler;
|
||||
}
|
||||
|
||||
PromptManagerModule.prototype.isPromptDisabledForActiveCharacter = function (identifier) {
|
||||
const promptOrderEntry = this.getPromptOrderEntry(this.activeCharacter, identifier);
|
||||
if (promptOrderEntry) return !promptOrderEntry.enabled;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a prompt to the current character's prompt list.
|
||||
* @param {object} prompt - The prompt to be added.
|
||||
@@ -838,7 +876,8 @@ PromptManagerModule.prototype.isPromptEditAllowed = function (prompt) {
|
||||
* @returns {boolean} True if the prompt can be deleted, false otherwise.
|
||||
*/
|
||||
PromptManagerModule.prototype.isPromptToggleAllowed = function (prompt) {
|
||||
return prompt.marker ? false : !this.configuration.toggleDisabled.includes(prompt.identifier);
|
||||
const forceTogglePrompts = ['charDescription', 'charPersonality', 'scenario', 'personaDescription', 'worldInfoBefore', 'worldInfoAfter'];
|
||||
return prompt.marker && !forceTogglePrompts.includes(prompt.identifier) ? false : !this.configuration.toggleDisabled.includes(prompt.identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1083,10 +1122,21 @@ PromptManagerModule.prototype.loadPromptIntoEditForm = function (prompt) {
|
||||
const nameField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name');
|
||||
const roleField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role');
|
||||
const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');
|
||||
const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');
|
||||
const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');
|
||||
const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
|
||||
|
||||
nameField.value = prompt.name ?? '';
|
||||
roleField.value = prompt.role ?? '';
|
||||
promptField.value = prompt.content ?? '';
|
||||
injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
|
||||
injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH;
|
||||
injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
injectionPositionField.removeAttribute('disabled');
|
||||
|
||||
if (this.systemPrompts.includes(prompt.identifier)) {
|
||||
injectionPositionField.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
const resetPromptButton = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_reset');
|
||||
if (true === prompt.system_prompt) {
|
||||
@@ -1096,10 +1146,23 @@ PromptManagerModule.prototype.loadPromptIntoEditForm = function (prompt) {
|
||||
resetPromptButton.style.display = 'none';
|
||||
}
|
||||
|
||||
injectionPositionField.removeEventListener('change', (e) => this.handleInjectionPositionChange(e));
|
||||
injectionPositionField.addEventListener('change', (e) => this.handleInjectionPositionChange(e));
|
||||
|
||||
const savePromptButton = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_save');
|
||||
savePromptButton.dataset.pmPrompt = prompt.identifier;
|
||||
}
|
||||
|
||||
PromptManagerModule.prototype.handleInjectionPositionChange = function (event) {
|
||||
const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
|
||||
const injectionPosition = Number(event.target.value);
|
||||
if (injectionPosition === INJECTION_POSITION.ABSOLUTE) {
|
||||
injectionDepthBlock.style.visibility = 'visible';
|
||||
} else {
|
||||
injectionDepthBlock.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a given prompt into the inspect form
|
||||
* @param {MessageCollection} messages - Prompt object with properties 'name', 'role', 'content', and 'system_prompt'
|
||||
@@ -1117,12 +1180,10 @@ PromptManagerModule.prototype.loadMessagesIntoInspectForm = function (messages)
|
||||
let drawerHTML = `
|
||||
<div class="inline-drawer ${this.configuration.prefix}prompt_manager_prompt">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<span>Name: ${title}, Role: ${role}, Tokens: ${tokens}</span>
|
||||
<span>Name: ${escapeHtml(title)}, Role: ${role}, Tokens: ${tokens}</span>
|
||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
${content}
|
||||
</div>
|
||||
<div class="inline-drawer-content" style="white-space: pre-wrap;">${escapeHtml(content)}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1133,9 +1194,11 @@ PromptManagerModule.prototype.loadMessagesIntoInspectForm = function (messages)
|
||||
|
||||
const messageList = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_inspect_list');
|
||||
|
||||
if (0 === messages.getCollection().length) messageList.innerHTML = `<span>This marker does not contain any prompts.</span>`;
|
||||
const messagesCollection = messages instanceof Message ? [messages] : messages.getCollection();
|
||||
|
||||
messages.getCollection().forEach(message => {
|
||||
if (0 === messagesCollection.length) messageList.innerHTML = `<span>This marker does not contain any prompts.</span>`;
|
||||
|
||||
messagesCollection.forEach(message => {
|
||||
messageList.append(createInlineDrawer(message));
|
||||
});
|
||||
}
|
||||
@@ -1150,10 +1213,17 @@ PromptManagerModule.prototype.clearEditForm = function () {
|
||||
const nameField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name');
|
||||
const roleField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role');
|
||||
const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');
|
||||
const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');
|
||||
const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');
|
||||
const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
|
||||
|
||||
nameField.value = '';
|
||||
roleField.selectedIndex = 0;
|
||||
promptField.value = '';
|
||||
injectionPositionField.selectedIndex = 0;
|
||||
injectionPositionField.removeAttribute('disabled');
|
||||
injectionDepthField.value = DEFAULT_DEPTH;
|
||||
injectionDepthBlock.style.visibility = 'unset';
|
||||
|
||||
roleField.disabled = false;
|
||||
}
|
||||
@@ -1433,13 +1503,18 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
|
||||
}
|
||||
|
||||
const encodedName = escapeHtml(prompt.name);
|
||||
const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
|
||||
const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
|
||||
const isInjectionPrompt = !prompt.marker && prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
|
||||
listItemHtml += `
|
||||
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass}" data-pm-identifier="${prompt.identifier}">
|
||||
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
|
||||
${prompt.marker ? '<span class="fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
|
||||
${!prompt.marker && prompt.system_prompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
|
||||
${!prompt.marker && !prompt.system_prompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
|
||||
${isSystemPrompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
|
||||
${isUserPrompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
|
||||
${isInjectionPrompt ? `<span class="fa-solid fa-syringe" title="In-Chat Injection"></span>` : ''}
|
||||
${this.isPromptInspectionAllowed(prompt) ? `<a class="prompt-manager-inspect-action">${encodedName}</a>` : encodedName}
|
||||
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${prompt.injection_depth}</small>` : ''}
|
||||
</span>
|
||||
<span>
|
||||
<span class="prompt_manager_prompt_controls">
|
||||
|
@@ -18,6 +18,8 @@ import {
|
||||
getThumbnailUrl,
|
||||
selectCharacterById,
|
||||
eventSource,
|
||||
menu_type,
|
||||
substituteParams,
|
||||
} from "../script.js";
|
||||
|
||||
import {
|
||||
@@ -31,9 +33,10 @@ import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
} from "./secrets.js";
|
||||
import { debounce, delay, getStringHash, waitUntilCondition } from "./utils.js";
|
||||
import { debounce, delay, getStringHash, isValidUrl, waitUntilCondition } from "./utils.js";
|
||||
import { chat_completion_sources, oai_settings } from "./openai.js";
|
||||
import { getTokenCount } from "./tokenizers.js";
|
||||
import { isMancer } from "./textgen-settings.js";
|
||||
|
||||
|
||||
var RPanelPin = document.getElementById("rm_button_panel_pin");
|
||||
@@ -57,9 +60,7 @@ const countTokensDebounced = debounce(RA_CountCharTokens, 1000);
|
||||
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.target.id === "online_status_text2" ||
|
||||
mutation.target.id === "online_status_text3" ||
|
||||
mutation.target.classList.contains("online_status_text4")) {
|
||||
if (mutation.target.classList.contains("online_status_text")) {
|
||||
RA_checkOnlineStatus();
|
||||
} else if (mutation.target.parentNode === SelectedCharacterTab) {
|
||||
setTimeout(RA_CountCharTokens, 200);
|
||||
@@ -171,7 +172,7 @@ export function humanizedDateTime() {
|
||||
let humanMillisecond =
|
||||
(baseDate.getMilliseconds() < 10 ? "0" : "") + baseDate.getMilliseconds();
|
||||
let HumanizedDateTime =
|
||||
humanYear + "-" + humanMonth + "-" + humanDate + " @" + humanHour + "h " + humanMinute + "m " + humanSecond + "s " + humanMillisecond + "ms";
|
||||
humanYear + "-" + humanMonth + "-" + humanDate + "@" + humanHour + "h" + humanMinute + "m" + humanSecond + "s";
|
||||
return HumanizedDateTime;
|
||||
}
|
||||
|
||||
@@ -210,10 +211,12 @@ $("#character_popup").on("input", function () { countTokensDebounced(); });
|
||||
//function:
|
||||
export function RA_CountCharTokens() {
|
||||
let total_tokens = 0;
|
||||
let permanent_tokens = 0;
|
||||
|
||||
$('[data-token-counter]').each(function () {
|
||||
const counter = $(this);
|
||||
const input = $(document.getElementById(counter.data('token-counter')));
|
||||
const isPermanent = counter.data('token-permanent') === true;
|
||||
const value = String(input.val());
|
||||
|
||||
if (input.length === 0) {
|
||||
@@ -230,10 +233,14 @@ export function RA_CountCharTokens() {
|
||||
|
||||
if (input.data('last-value-hash') === valueHash) {
|
||||
total_tokens += Number(counter.text());
|
||||
permanent_tokens += isPermanent ? Number(counter.text()) : 0;
|
||||
} else {
|
||||
const tokens = getTokenCount(value);
|
||||
// We substitute macro for existing characters, but not for the character being created
|
||||
const valueToCount = menu_type === 'create' ? value : substituteParams(value);
|
||||
const tokens = getTokenCount(valueToCount);
|
||||
counter.text(tokens);
|
||||
total_tokens += tokens;
|
||||
permanent_tokens += isPermanent ? tokens : 0;
|
||||
input.data('last-value-hash', valueHash);
|
||||
}
|
||||
});
|
||||
@@ -242,6 +249,7 @@ export function RA_CountCharTokens() {
|
||||
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
||||
const showWarning = (total_tokens > tokenLimit);
|
||||
$('#result_info_total_tokens').text(total_tokens);
|
||||
$('#result_info_permanent_tokens').text(permanent_tokens);
|
||||
$('#result_info_text').toggleClass('neutral_warning', showWarning);
|
||||
$('#chartokenwarning').toggle(showWarning);
|
||||
}
|
||||
@@ -259,11 +267,11 @@ async function RA_autoloadchat() {
|
||||
let active_character_id = Object.keys(characters).find(key => characters[key].avatar === active_character);
|
||||
|
||||
if (active_character_id !== null) {
|
||||
selectCharacterById(String(active_character_id));
|
||||
await selectCharacterById(String(active_character_id));
|
||||
}
|
||||
|
||||
if (active_group != null) {
|
||||
openGroupById(String(active_group));
|
||||
await openGroupById(String(active_group));
|
||||
}
|
||||
|
||||
// if the character list hadn't been loaded yet, try again.
|
||||
@@ -274,10 +282,16 @@ export async function favsToHotswap() {
|
||||
const entities = getEntitiesList({ doFilter: false });
|
||||
const container = $('#right-nav-panel .hotswap');
|
||||
const template = $('#hotswap_template .hotswapAvatar');
|
||||
container.empty();
|
||||
const maxCount = 6;
|
||||
const DEFAULT_COUNT = 6;
|
||||
const WIDTH_PER_ITEM = 60; // 50px + 5px gap + 5px padding
|
||||
const containerWidth = container.outerWidth();
|
||||
const maxCount = containerWidth > 0 ? Math.floor(containerWidth / WIDTH_PER_ITEM) : DEFAULT_COUNT;
|
||||
let count = 0;
|
||||
|
||||
const promises = [];
|
||||
const newContainer = container.clone();
|
||||
newContainer.empty();
|
||||
|
||||
for (const entity of entities) {
|
||||
if (count >= maxCount) {
|
||||
break;
|
||||
@@ -310,23 +324,39 @@ export async function favsToHotswap() {
|
||||
}
|
||||
|
||||
if (isCharacter) {
|
||||
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
|
||||
$(slot).find('img').attr('src', avatarUrl);
|
||||
$(slot).attr('title', entity.item.avatar);
|
||||
const imgLoadPromise = new Promise((resolve) => {
|
||||
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
|
||||
$(slot).find('img').attr('src', avatarUrl).on('load', resolve);
|
||||
$(slot).attr('title', entity.item.avatar);
|
||||
});
|
||||
|
||||
// if the image doesn't load in 500ms, resolve the promise anyway
|
||||
promises.push(Promise.race([imgLoadPromise, delay(500)]));
|
||||
}
|
||||
|
||||
$(slot).css('cursor', 'pointer');
|
||||
container.append(slot);
|
||||
newContainer.append(slot);
|
||||
count++;
|
||||
}
|
||||
|
||||
// there are 6 slots in total,
|
||||
if (count < maxCount) { //if any are left over
|
||||
// don't fill leftover spaces with avatar placeholders
|
||||
// just evenly space the selected avatars instead
|
||||
/*
|
||||
if (count < maxCount) { //if any space is left over
|
||||
let leftOverSlots = maxCount - count;
|
||||
for (let i = 1; i <= leftOverSlots; i++) {
|
||||
container.append(template.clone());
|
||||
newContainer.append(template.clone());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
//helpful instruction message if no characters are favorited
|
||||
if (count === 0) { container.html(`<small><span><i class="fa-solid fa-star"></i> Favorite characters to add them to HotSwaps</span></small>`) }
|
||||
//otherwise replace with fav'd characters
|
||||
if (count > 0) {
|
||||
container.replaceWith(newContainer);
|
||||
}
|
||||
}
|
||||
|
||||
//changes input bar and send button display depending on connection status
|
||||
@@ -341,7 +371,7 @@ function RA_checkOnlineStatus() {
|
||||
connection_made = false;
|
||||
} else {
|
||||
if (online_status !== undefined && online_status !== "no_connection") {
|
||||
$("#send_textarea").attr("placeholder", `Type a message, or /? for command list`); //on connect, placeholder tells user to type message
|
||||
$("#send_textarea").attr("placeholder", `Type a message, or /? for help`); //on connect, placeholder tells user to type message
|
||||
$('#send_form').removeClass("no-connection");
|
||||
$("#API-status-top").removeClass("fa-plug-circle-exclamation redOverlayGlow");
|
||||
$("#API-status-top").addClass("fa-plug");
|
||||
@@ -367,18 +397,21 @@ function RA_autoconnect(PrevApi) {
|
||||
if (online_status === "no_connection" && LoadLocalBool('AutoConnectEnabled')) {
|
||||
switch (main_api) {
|
||||
case 'kobold':
|
||||
if (api_server && isUrlOrAPIKey(api_server)) {
|
||||
$("#api_button").click();
|
||||
if (api_server && isValidUrl(api_server)) {
|
||||
$("#api_button").trigger('click');
|
||||
}
|
||||
break;
|
||||
case 'novel':
|
||||
if (secret_state[SECRET_KEYS.NOVEL]) {
|
||||
$("#api_button_novel").click();
|
||||
$("#api_button_novel").trigger('click');
|
||||
}
|
||||
break;
|
||||
case 'textgenerationwebui':
|
||||
if (api_server_textgenerationwebui && isUrlOrAPIKey(api_server_textgenerationwebui)) {
|
||||
$("#api_button_textgenerationwebui").click();
|
||||
if (isMancer() && secret_state[SECRET_KEYS.MANCER]) {
|
||||
$("#api_button_textgenerationwebui").trigger('click');
|
||||
}
|
||||
else if (api_server_textgenerationwebui && isValidUrl(api_server_textgenerationwebui)) {
|
||||
$("#api_button_textgenerationwebui").trigger('click');
|
||||
}
|
||||
break;
|
||||
case 'openai':
|
||||
@@ -388,8 +421,9 @@ function RA_autoconnect(PrevApi) {
|
||||
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
||||
|| (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
|
||||
|| (secret_state[SECRET_KEYS.AI21] && oai_settings.chat_completion_source == chat_completion_sources.AI21)
|
||||
|| (secret_state[SECRET_KEYS.PALM] && oai_settings.chat_completion_source == chat_completion_sources.PALM)
|
||||
) {
|
||||
$("#api_button_openai").click();
|
||||
$("#api_button_openai").trigger('click');
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -397,21 +431,12 @@ function RA_autoconnect(PrevApi) {
|
||||
if (!connection_made) {
|
||||
RA_AC_retries++;
|
||||
retry_delay = Math.min(retry_delay * 2, 30000); // double retry delay up to to 30 secs
|
||||
//console.log('connection attempts: ' + RA_AC_retries + ' delay: ' + (retry_delay / 1000) + 's');
|
||||
setTimeout(RA_autoconnect, retry_delay);
|
||||
// console.log('connection attempts: ' + RA_AC_retries + ' delay: ' + (retry_delay / 1000) + 's');
|
||||
// setTimeout(RA_autoconnect, retry_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isUrlOrAPIKey(string) {
|
||||
try {
|
||||
new URL(string);
|
||||
return true;
|
||||
} catch (_) {
|
||||
// return pattern.test(string);
|
||||
}
|
||||
}
|
||||
|
||||
function OpenNavPanels() {
|
||||
const deviceInfo = getDeviceInfo();
|
||||
if (deviceInfo && deviceInfo.device.type === 'desktop') {
|
||||
@@ -867,13 +892,23 @@ export function initRossMods() {
|
||||
|
||||
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
||||
$('#send_textarea').on('input', function () {
|
||||
const chatBlock = $('#chat');
|
||||
const originalScrollBottom = chatBlock[0].scrollHeight - (chatBlock.scrollTop() + chatBlock.outerHeight());
|
||||
this.style.height = window.getComputedStyle(this).getPropertyValue('min-height');
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
const newScrollTop = Math.round(chatBlock[0].scrollHeight - (chatBlock.outerHeight() + originalScrollBottom));
|
||||
chatBlock.scrollTop(newScrollTop);
|
||||
});
|
||||
|
||||
//Regenerate if user swipes on the last mesage in chat
|
||||
|
||||
document.addEventListener('swiped-left', function (e) {
|
||||
if (power_user.gestures === false) {
|
||||
return
|
||||
}
|
||||
if ($(".mes_edit_buttons, .drawer-content, #character_popup, #dialogue_popup, #WorldInfo, #right-nav-panel, #left-nav-panel, #select_chat_popup, #floatingPrompt").is(":visible")) {
|
||||
return
|
||||
}
|
||||
var SwipeButR = $('.swipe_right:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
@@ -883,6 +918,12 @@ export function initRossMods() {
|
||||
}
|
||||
});
|
||||
document.addEventListener('swiped-right', function (e) {
|
||||
if (power_user.gestures === false) {
|
||||
return
|
||||
}
|
||||
if ($(".mes_edit_buttons, .drawer-content, #character_popup, #dialogue_popup, #WorldInfo, #right-nav-panel, #left-nav-panel, #select_chat_popup, #floatingPrompt").is(":visible")) {
|
||||
return
|
||||
}
|
||||
var SwipeButL = $('.swipe_left:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
@@ -906,10 +947,13 @@ export function initRossMods() {
|
||||
}
|
||||
|
||||
$(document).on('keydown', function (event) {
|
||||
processHotkeys(event);
|
||||
processHotkeys(event.originalEvent);
|
||||
});
|
||||
|
||||
//Additional hotkeys CTRL+ENTER and CTRL+UPARROW
|
||||
/**
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
function processHotkeys(event) {
|
||||
//Enter to send when send_textarea in focus
|
||||
if ($(':focus').attr('id') === 'send_textarea') {
|
||||
@@ -943,6 +987,14 @@ export function initRossMods() {
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Alt+Enter or AltGr+Enter to Continue
|
||||
if ((event.altKey || (event.altKey && event.ctrlKey)) && event.key == "Enter") {
|
||||
if (is_send_press == false) {
|
||||
console.debug("Continuing with Alt+Enter");
|
||||
$('#option_continue').trigger('click');
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
|
||||
if (event.ctrlKey && event.key == "Enter") {
|
||||
const editMesDone = $(".mes_edit_done:visible");
|
||||
@@ -958,14 +1010,6 @@ export function initRossMods() {
|
||||
}
|
||||
}
|
||||
|
||||
// Alt+Enter to Continue
|
||||
if (event.altKey && event.key == "Enter") {
|
||||
if (is_send_press == false) {
|
||||
console.debug("Continuing with Alt+Enter");
|
||||
$('#option_continue').trigger('click');
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nanogallery2's lightbox is active
|
||||
function isNanogallery2LightboxActive() {
|
||||
// Check if the body has the 'nGY2On' class, adjust this based on actual behavior
|
||||
@@ -1097,13 +1141,14 @@ export function initRossMods() {
|
||||
$("#rightNavDrawerIcon").trigger('click');
|
||||
return
|
||||
}
|
||||
if ($(".draggable").is(":visible")) {
|
||||
// Remove the first matched element
|
||||
$('.draggable:first').remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($(".draggable").is(":visible")) {
|
||||
// Remove the first matched element
|
||||
$('.draggable:first').remove();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
|
||||
|
@@ -235,6 +235,7 @@ function loadSettings() {
|
||||
chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? extension_settings.note.defaultDepth ?? DEFAULT_DEPTH;
|
||||
$('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]);
|
||||
$('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]);
|
||||
$('#extension_floating_allow_wi_scan').prop('checked', extension_settings.note.allowWIScan ?? false);
|
||||
$('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
|
||||
$(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
|
||||
|
||||
@@ -389,6 +390,11 @@ function onChatChanged() {
|
||||
$('#extension_floating_default_token_counter').text(tokenCounter3);
|
||||
}
|
||||
|
||||
function onAllowWIScanCheckboxChanged() {
|
||||
extension_settings.note.allowWIScan = !!$(this).prop('checked');
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject author's note options and setup event listeners.
|
||||
*/
|
||||
@@ -402,6 +408,7 @@ export function initAuthorsNote() {
|
||||
$('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
|
||||
$('#extension_default_depth').on('input', onDefaultDepthInput);
|
||||
$('#extension_default_interval').on('input', onDefaultIntervalInput);
|
||||
$('#extension_floating_allow_wi_scan').on('input', onAllowWIScanCheckboxChanged);
|
||||
$('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
|
||||
$('input[name="extension_default_position"]').on('change', onDefaultPositionInput);
|
||||
$('input[name="extension_floating_char_position"]').on('change', onExtensionFloatingCharPositionInput);
|
||||
|
488
public/scripts/backgrounds.js
Normal file
488
public/scripts/backgrounds.js
Normal file
@@ -0,0 +1,488 @@
|
||||
import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl } from "../script.js";
|
||||
import { saveMetadataDebounced } from "./extensions.js";
|
||||
import { registerSlashCommand } from "./slash-commands.js";
|
||||
import { stringFormat } from "./utils.js";
|
||||
|
||||
const BG_METADATA_KEY = 'custom_background';
|
||||
const LIST_METADATA_KEY = 'chat_backgrounds';
|
||||
|
||||
/**
|
||||
* Sets the background for the current chat and adds it to the list of custom backgrounds.
|
||||
* @param {{url: string, path:string}} backgroundInfo
|
||||
*/
|
||||
function forceSetBackground(backgroundInfo) {
|
||||
saveBackgroundMetadata(backgroundInfo.url);
|
||||
setCustomBackground();
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const bg = backgroundInfo.path;
|
||||
list.push(bg);
|
||||
chat_metadata[LIST_METADATA_KEY] = list;
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
highlightNewBackground(bg);
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
async function onChatChanged() {
|
||||
if (hasCustomBackground()) {
|
||||
setCustomBackground();
|
||||
}
|
||||
else {
|
||||
unsetCustomBackground();
|
||||
}
|
||||
|
||||
getChatBackgroundsList();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function getChatBackgroundsList() {
|
||||
const list = chat_metadata[LIST_METADATA_KEY];
|
||||
const listEmpty = !Array.isArray(list) || list.length === 0;
|
||||
|
||||
$('#bg_custom_content').empty();
|
||||
$('#bg_chat_hint').toggle(listEmpty);
|
||||
|
||||
if (listEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bg of list) {
|
||||
const template = getBackgroundFromTemplate(bg, true);
|
||||
$('#bg_custom_content').append(template);
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundPath(fileUrl) {
|
||||
return `backgrounds/${fileUrl}`;
|
||||
}
|
||||
|
||||
function highlightLockedBackground() {
|
||||
$('.bg_example').removeClass('locked');
|
||||
|
||||
const lockedBackground = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
if (!lockedBackground) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(`.bg_example`).each(function () {
|
||||
const url = $(this).data('url');
|
||||
if (url === lockedBackground) {
|
||||
$(this).addClass('locked');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onLockBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const chatName = getCurrentChatId();
|
||||
|
||||
if (!chatName) {
|
||||
toastr.warning('Select a chat to lock the background for it');
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeBgImage = getUrlParameter(this);
|
||||
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function onUnlockBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
return chat_metadata[BG_METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
chat_metadata[BG_METADATA_KEY] = file;
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
delete chat_metadata[BG_METADATA_KEY];
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const file = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#bg_custom").css("background-image", file);
|
||||
}
|
||||
|
||||
function unsetCustomBackground() {
|
||||
$("#bg_custom").css("background-image", 'none');
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const isCustom = $(this).attr('custom') === 'true';
|
||||
const relativeBgImage = getUrlParameter(this);
|
||||
|
||||
// if clicked on upload button
|
||||
if (!relativeBgImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically lock the background if it's custom or other background is locked
|
||||
if (hasCustomBackground() || isCustom) {
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
highlightLockedBackground();
|
||||
} else {
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
const customBg = window.getComputedStyle(document.getElementById('bg_custom')).backgroundImage;
|
||||
|
||||
// Custom background is set. Do not override the layer below
|
||||
if (customBg !== 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgFile = $(this).attr("bgfile");
|
||||
const backgroundUrl = getBackgroundPath(bgFile);
|
||||
|
||||
// Fetching to browser memory to reduce flicker
|
||||
fetch(backgroundUrl).then(() => {
|
||||
$("#bg1").css("background-image", relativeBgImage);
|
||||
setBackground(bgFile);
|
||||
}).catch(() => {
|
||||
console.log('Background could not be set: ' + backgroundUrl);
|
||||
});
|
||||
}
|
||||
|
||||
async function onCopyToSystemBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgFile = await fetch(bgNames.oldBg);
|
||||
|
||||
if (!bgFile.ok) {
|
||||
toastr.warning('Failed to copy background');
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await bgFile.blob();
|
||||
const file = new File([blob], bgNames.newBg);
|
||||
const formData = new FormData();
|
||||
formData.set('avatar', file);
|
||||
|
||||
uploadBackground(formData);
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bgNames.oldBg);
|
||||
list.splice(index, 1);
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new background name from the user.
|
||||
* @param {Element} referenceElement
|
||||
* @returns {Promise<{oldBg: string, newBg: string}>}
|
||||
* */
|
||||
async function getNewBackgroundName(referenceElement) {
|
||||
const exampleBlock = $(referenceElement).closest('.bg_example');
|
||||
const isCustom = exampleBlock.attr('custom') === 'true';
|
||||
const oldBg = exampleBlock.attr('bgfile');
|
||||
|
||||
if (!oldBg) {
|
||||
console.debug('no bgfile');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileExtension = oldBg.split('.').pop();
|
||||
const fileNameBase = isCustom ? oldBg.split('/').pop() : oldBg;
|
||||
const oldBgExtensionless = fileNameBase.replace(`.${fileExtension}`, '');
|
||||
const newBgExtensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', oldBgExtensionless);
|
||||
|
||||
if (!newBgExtensionless) {
|
||||
console.debug('no new_bg_extensionless');
|
||||
return;
|
||||
}
|
||||
|
||||
const newBg = `${newBgExtensionless}.${fileExtension}`;
|
||||
|
||||
if (oldBgExtensionless === newBgExtensionless) {
|
||||
console.debug('new_bg === old_bg');
|
||||
return;
|
||||
}
|
||||
|
||||
return { oldBg, newBg };
|
||||
}
|
||||
|
||||
async function onRenameBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = { old_bg: bgNames.oldBg, new_bg: bgNames.newBg };
|
||||
const response = await fetch('/renamebackground', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bgNames.newBg);
|
||||
} else {
|
||||
toastr.warning('Failed to rename background');
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const bgToDelete = $(this).closest('.bg_example');
|
||||
const url = bgToDelete.data('url');
|
||||
const isCustom = bgToDelete.attr('custom') === 'true';
|
||||
const confirm = await callPopup("<h3>Delete the background?</h3>", 'confirm');
|
||||
const bg = bgToDelete.attr('bgfile');
|
||||
|
||||
if (confirm) {
|
||||
// If it's not custom, it's a built-in background. Delete it from the server
|
||||
if (!isCustom) {
|
||||
delBackground(bg);
|
||||
} else {
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bg);
|
||||
list.splice(index, 1);
|
||||
}
|
||||
|
||||
const siblingSelector = '.bg_example:not(#form_bg_download)';
|
||||
const nextBg = bgToDelete.next(siblingSelector);
|
||||
const prevBg = bgToDelete.prev(siblingSelector);
|
||||
const anyBg = $(siblingSelector);
|
||||
|
||||
if (nextBg.length > 0) {
|
||||
nextBg.trigger('click');
|
||||
} else if (prevBg.length > 0) {
|
||||
prevBg.trigger('click');
|
||||
} else {
|
||||
$(anyBg[Math.floor(Math.random() * anyBg.length)]).trigger('click');
|
||||
}
|
||||
|
||||
bgToDelete.remove();
|
||||
|
||||
if (url === chat_metadata[BG_METADATA_KEY]) {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
if (isCustom) {
|
||||
getChatBackgroundsList();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const autoBgPrompt = `Pause your roleplay and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}`;
|
||||
|
||||
async function autoBackgroundCommand() {
|
||||
/** @type {HTMLElement[]} */
|
||||
const bgTitles = Array.from(document.querySelectorAll('#bg_menu_content .BGSampleTitle'));
|
||||
const options = bgTitles.map(x => ({ element: x, text: x.innerText.trim() })).filter(x => x.text.length > 0);
|
||||
if (options.length == 0) {
|
||||
toastr.warning('No backgrounds to choose from. Please upload some images to the "backgrounds" folder.');
|
||||
return;
|
||||
}
|
||||
|
||||
const list = options.map(option => `- ${option.text}`).join('\n');
|
||||
const prompt = stringFormat(autoBgPrompt, list);
|
||||
const reply = await generateQuietPrompt(prompt, false, false);
|
||||
const fuse = new Fuse(options, { keys: ['text'] });
|
||||
const bestMatch = fuse.search(reply, { limit: 1 });
|
||||
|
||||
if (bestMatch.length == 0) {
|
||||
toastr.warning('No match found. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('Automatically choosing background:', bestMatch);
|
||||
bestMatch[0].item.element.click();
|
||||
}
|
||||
|
||||
export async function getBackgrounds() {
|
||||
const response = await fetch("/getbackgrounds", {
|
||||
method: "POST",
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
"": "",
|
||||
}),
|
||||
});
|
||||
if (response.ok === true) {
|
||||
const getData = await response.json();
|
||||
//background = getData;
|
||||
//console.log(getData.length);
|
||||
$("#bg_menu_content").children('div').remove();
|
||||
for (const bg of getData) {
|
||||
const template = getBackgroundFromTemplate(bg, false);
|
||||
$("#bg_menu_content").append(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the background
|
||||
* @param {Element} block
|
||||
* @returns {string} URL of the background
|
||||
*/
|
||||
function getUrlParameter(block) {
|
||||
return $(block).closest(".bg_example").data("url");
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a background template
|
||||
* @param {string} bg Path to background
|
||||
* @param {boolean} isCustom Whether the background is custom
|
||||
* @returns {JQuery<HTMLElement>} Background template
|
||||
*/
|
||||
function getBackgroundFromTemplate(bg, isCustom) {
|
||||
const template = $('#background_template .bg_example').clone();
|
||||
const thumbPath = isCustom ? bg : getThumbnailUrl('bg', bg);
|
||||
const url = isCustom ? `url("${encodeURI(bg)}")` : `url("${getBackgroundPath(bg)}")`;
|
||||
const title = isCustom ? bg.split('/').pop() : bg;
|
||||
const friendlyTitle = title.slice(0, title.lastIndexOf('.'));
|
||||
template.attr('title', title);
|
||||
template.attr('bgfile', bg);
|
||||
template.attr('custom', String(isCustom));
|
||||
template.data('url', url);
|
||||
template.css('background-image', `url('${thumbPath}')`);
|
||||
template.find('.BGSampleTitle').text(friendlyTitle);
|
||||
return template;
|
||||
}
|
||||
|
||||
async function setBackground(bg) {
|
||||
jQuery.ajax({
|
||||
type: "POST", //
|
||||
url: "/setbackground", //
|
||||
data: JSON.stringify({
|
||||
bg: bg,
|
||||
}),
|
||||
beforeSend: function () {
|
||||
|
||||
},
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
//processData: false,
|
||||
success: function (html) { },
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception);
|
||||
console.log(jqXHR);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function delBackground(bg) {
|
||||
const response = await fetch("/delbackground", {
|
||||
method: "POST",
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
bg: bg,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function onBackgroundUploadSelected() {
|
||||
const form = $("#form_bg_download").get(0);
|
||||
|
||||
if (!(form instanceof HTMLFormElement)) {
|
||||
console.error('form_bg_download is not a form');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
uploadBackground(formData);
|
||||
form.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a background to the server
|
||||
* @param {FormData} formData
|
||||
*/
|
||||
function uploadBackground(formData) {
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/downloadbackground",
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (bg) {
|
||||
setBackground(bg);
|
||||
$("#bg1").css("background-image", `url("${getBackgroundPath(bg)}"`);
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bg);
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception);
|
||||
console.log(jqXHR);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bg
|
||||
*/
|
||||
function highlightNewBackground(bg) {
|
||||
const newBg = $(`.bg_example[bgfile="${bg}"]`);
|
||||
const scrollOffset = newBg.offset().top - newBg.parent().offset().top;
|
||||
$('#Backgrounds').scrollTop(scrollOffset);
|
||||
newBg.addClass('flash animated');
|
||||
setTimeout(() => newBg.removeClass('flash animated'), 2000);
|
||||
}
|
||||
|
||||
function onBackgroundFilterInput() {
|
||||
const filterValue = String($(this).val()).toLowerCase();
|
||||
$("#bg_menu_content > div").each(function () {
|
||||
const $bgContent = $(this);
|
||||
if ($bgContent.attr("title").toLowerCase().includes(filterValue)) {
|
||||
$bgContent.show();
|
||||
} else {
|
||||
$bgContent.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function initBackgrounds() {
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||
$(document).on("click", '.bg_example', onSelectBackgroundClick);
|
||||
$(document).on('click', '.bg_example_lock', onLockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_unlock', onUnlockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_edit', onRenameBackgroundClick);
|
||||
$(document).on("click", '.bg_example_cross', onDeleteBackgroundClick);
|
||||
$(document).on("click", '.bg_example_copy', onCopyToSystemBackgroundClick);
|
||||
$('#auto_background').on("click", autoBackgroundCommand);
|
||||
$("#add_bg_button").on('change', onBackgroundUploadSelected);
|
||||
$("#bg-filter").on("input", onBackgroundFilterInput);
|
||||
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], "– locks a background for the currently selected chat", true, true);
|
||||
registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], '– unlocks a background for the currently selected chat', true, true);
|
||||
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], '– automatically changes the background based on the chat context using the AI request prompt', true, true);
|
||||
}
|
@@ -133,6 +133,39 @@ async function saveBookmarkMenu() {
|
||||
return createNewBookmark(chat.length - 1);
|
||||
}
|
||||
|
||||
export async function createBranch(mesId) {
|
||||
if (!chat.length) {
|
||||
toastr.warning('The chat is empty.', 'Branch creation failed');
|
||||
return;
|
||||
}
|
||||
|
||||
if (mesId < 0 || mesId >= chat.length) {
|
||||
toastr.warning('Invalid message ID.', 'Branch creation failed');
|
||||
return;
|
||||
}
|
||||
|
||||
const lastMes = chat[mesId];
|
||||
const mainChat = selected_group ? groups?.find(x => x.id == selected_group)?.chat_id : characters[this_chid].chat;
|
||||
const newMetadata = { main_chat: mainChat };
|
||||
let name = `Branch #${mesId} - ${humanizedDateTime()}`
|
||||
|
||||
if (selected_group) {
|
||||
await saveGroupBookmarkChat(selected_group, name, newMetadata, mesId);
|
||||
} else {
|
||||
await saveChat(name, newMetadata, mesId);
|
||||
}
|
||||
// append to branches list if it exists
|
||||
// otherwise create it
|
||||
if (typeof lastMes.extra !== 'object') {
|
||||
lastMes.extra = {};
|
||||
}
|
||||
if (typeof lastMes.extra['branches'] !== 'object') {
|
||||
lastMes.extra['branches'] = [];
|
||||
}
|
||||
lastMes.extra['branches'].push(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
async function createNewBookmark(mesId) {
|
||||
if (!chat.length) {
|
||||
toastr.warning('The chat is empty.', 'Bookmark creation failed');
|
||||
@@ -280,7 +313,6 @@ async function convertSoloToGroupChat() {
|
||||
message.name = character.name;
|
||||
message.original_avatar = character.avatar;
|
||||
message.force_avatar = getThumbnailUrl('avatar', character.avatar);
|
||||
message.is_name = true;
|
||||
|
||||
// Allow regens of a single message in group
|
||||
if (typeof message.extra !== 'object') {
|
||||
|
@@ -1,24 +1,44 @@
|
||||
import { characters, getCharacters, handleDeleteCharacter, callPopup } from "../../../script.js";
|
||||
import { characters, getCharacters, handleDeleteCharacter, callPopup } from "../script.js";
|
||||
import {BulkEditOverlay, BulkEditOverlayState} from "./BulkEditOverlay.js";
|
||||
|
||||
|
||||
let is_bulk_edit = false;
|
||||
|
||||
const enableBulkEdit = () => {
|
||||
enableBulkSelect();
|
||||
(new BulkEditOverlay()).selectState();
|
||||
// show the delete button
|
||||
$("#bulkDeleteButton").show();
|
||||
is_bulk_edit = true;
|
||||
}
|
||||
|
||||
const disableBulkEdit = () => {
|
||||
disableBulkSelect();
|
||||
(new BulkEditOverlay()).browseState();
|
||||
// hide the delete button
|
||||
$("#bulkDeleteButton").hide();
|
||||
is_bulk_edit = false;
|
||||
}
|
||||
|
||||
const toggleBulkEditMode = (isBulkEdit) => {
|
||||
if (isBulkEdit) {
|
||||
disableBulkEdit();
|
||||
} else {
|
||||
enableBulkEdit();
|
||||
}
|
||||
}
|
||||
|
||||
(new BulkEditOverlay()).addStateChangeCallback((state) => {
|
||||
if (state === BulkEditOverlayState.select) enableBulkEdit();
|
||||
if (state === BulkEditOverlayState.browse) disableBulkEdit();
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggles bulk edit mode on/off when the edit button is clicked.
|
||||
*/
|
||||
function onEditButtonClick() {
|
||||
console.log("Edit button clicked");
|
||||
// toggle bulk edit mode
|
||||
if (is_bulk_edit) {
|
||||
disableBulkSelect();
|
||||
// hide the delete button
|
||||
$("#bulkDeleteButton").hide();
|
||||
is_bulk_edit = false;
|
||||
} else {
|
||||
enableBulkSelect();
|
||||
// show the delete button
|
||||
$("#bulkDeleteButton").show();
|
||||
is_bulk_edit = true;
|
||||
}
|
||||
toggleBulkEditMode(is_bulk_edit);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,23 +84,6 @@ async function onDeleteButtonClick() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the bulk edit and delete buttons to the UI.
|
||||
*/
|
||||
function addButtons() {
|
||||
const editButton = $(
|
||||
"<i id='bulkEditButton' class='fa-solid fa-edit menu_button bulkEditButton' title='Bulk edit characters'></i>"
|
||||
);
|
||||
const deleteButton = $(
|
||||
"<i id='bulkDeleteButton' class='fa-solid fa-trash menu_button bulkDeleteButton' title='Bulk delete characters' style='display: none;'></i>"
|
||||
);
|
||||
|
||||
$("#charListGridToggle").after(editButton, deleteButton);
|
||||
|
||||
$("#bulkEditButton").on("click", onEditButtonClick);
|
||||
$("#bulkDeleteButton").on("click", onDeleteButtonClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables bulk selection by adding a checkbox next to each character.
|
||||
*/
|
||||
@@ -111,7 +114,7 @@ function disableBulkSelect() {
|
||||
/**
|
||||
* Entry point that runs on page load.
|
||||
*/
|
||||
jQuery(async () => {
|
||||
addButtons();
|
||||
// loadSettings();
|
||||
jQuery(() => {
|
||||
$("#bulkEditButton").on("click", onEditButtonClick);
|
||||
$("#bulkDeleteButton").on("click", onDeleteButtonClick);
|
||||
});
|
@@ -1,19 +1,17 @@
|
||||
import {
|
||||
chat_metadata,
|
||||
substituteParams,
|
||||
this_chid,
|
||||
eventSource,
|
||||
event_types,
|
||||
saveSettingsDebounced,
|
||||
this_chid,
|
||||
} from "../../../script.js";
|
||||
import { selected_group } from "../../group-chats.js";
|
||||
import { extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { getCharaFilename, delay } from "../../utils.js";
|
||||
import { power_user } from "../../power-user.js";
|
||||
import { metadataKeys } from "./util.js";
|
||||
} from "../script.js";
|
||||
import { extension_settings, saveMetadataDebounced } from "./extensions.js"
|
||||
import { selected_group } from "./group-chats.js";
|
||||
import { getCharaFilename, delay } from "./utils.js";
|
||||
import { power_user } from "./power-user.js";
|
||||
|
||||
// Keep track of where your extension is located, name should match repo name
|
||||
const extensionName = "cfg";
|
||||
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
||||
const extensionName = 'cfg';
|
||||
const defaultSettings = {
|
||||
global: {
|
||||
"guidance_scale": 1,
|
||||
@@ -180,7 +178,7 @@ async function modifyCharaHtml() {
|
||||
function loadSettings() {
|
||||
// Set chat CFG if it exists
|
||||
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
|
||||
$('#chat_cfg_guidance_scale_counter').text(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
||||
$('#chat_cfg_guidance_scale_counter').val(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
||||
$('#chat_cfg_negative_prompt').val(chat_metadata[metadataKeys.negative_prompt] ?? '');
|
||||
$('#chat_cfg_positive_prompt').val(chat_metadata[metadataKeys.positive_prompt] ?? '');
|
||||
$('#groupchat_cfg_use_chara').prop('checked', chat_metadata[metadataKeys.groupchat_individual_chars] ?? false);
|
||||
@@ -199,7 +197,7 @@ function loadSettings() {
|
||||
if (!promptSeparator.startsWith(`"`)) {
|
||||
promptSeparatorDisplay.unshift(`"`);
|
||||
}
|
||||
|
||||
|
||||
if (!promptSeparator.endsWith(`"`)) {
|
||||
promptSeparatorDisplay.push(`"`);
|
||||
}
|
||||
@@ -213,7 +211,7 @@ function loadSettings() {
|
||||
if (!selected_group) {
|
||||
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
||||
$('#chara_cfg_guidance_scale').val(charaCfg?.guidance_scale ?? 1.00);
|
||||
$('#chara_cfg_guidance_scale_counter').text(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
|
||||
$('#chara_cfg_guidance_scale_counter').val(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
|
||||
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
|
||||
$('#chara_cfg_positive_prompt').val(charaCfg?.positive_prompt ?? '');
|
||||
}
|
||||
@@ -230,7 +228,7 @@ async function initialLoadSettings() {
|
||||
|
||||
// Set global CFG values on load
|
||||
$('#global_cfg_guidance_scale').val(extension_settings.cfg.global.guidance_scale);
|
||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
$('#global_cfg_guidance_scale_counter').val(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
$('#global_cfg_negative_prompt').val(extension_settings.cfg.global.negative_prompt);
|
||||
$('#global_cfg_positive_prompt').val(extension_settings.cfg.global.positive_prompt);
|
||||
}
|
||||
@@ -279,14 +277,8 @@ function migrateSettings() {
|
||||
}
|
||||
|
||||
// This function is called when the extension is loaded
|
||||
jQuery(async () => {
|
||||
// This is an example of loading HTML from a file
|
||||
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
||||
|
||||
// Append settingsHtml to extensions_settings
|
||||
// extension_settings and extensions_settings2 are the left and right columns of the settings menu
|
||||
// Left should be extensions that deal with system functions and right should be visual/UI related
|
||||
windowHtml.find('#CFGClose').on('click', function () {
|
||||
export function initCfg() {
|
||||
$('#CFGClose').on('click', function () {
|
||||
$("#cfgConfig").transition({
|
||||
opacity: 0,
|
||||
duration: 200,
|
||||
@@ -295,58 +287,58 @@ jQuery(async () => {
|
||||
setTimeout(function () { $('#cfgConfig').hide() }, 200);
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_guidance_scale').on('input', function() {
|
||||
$('#chat_cfg_guidance_scale').on('input', function() {
|
||||
const numberValue = Number($(this).val());
|
||||
const success = setChatCfg(numberValue, settingType.guidance_scale);
|
||||
if (success) {
|
||||
$('#chat_cfg_guidance_scale_counter').text(numberValue.toFixed(2));
|
||||
$('#chat_cfg_guidance_scale_counter').val(numberValue.toFixed(2));
|
||||
}
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_negative_prompt').on('input', function() {
|
||||
$('#chat_cfg_negative_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_positive_prompt').on('input', function() {
|
||||
$('#chat_cfg_positive_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
||||
$('#chara_cfg_guidance_scale').on('input', function() {
|
||||
const value = $(this).val();
|
||||
const success = setCharCfg(value, settingType.guidance_scale);
|
||||
if (success) {
|
||||
$('#chara_cfg_guidance_scale_counter').text(Number(value).toFixed(2));
|
||||
$('#chara_cfg_guidance_scale_counter').val(Number(value).toFixed(2));
|
||||
}
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_negative_prompt').on('input', function() {
|
||||
$('#chara_cfg_negative_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_positive_prompt').on('input', function() {
|
||||
$('#chara_cfg_positive_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
||||
$('#global_cfg_guidance_scale').on('input', function() {
|
||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
$('#global_cfg_guidance_scale_counter').val(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_negative_prompt').on('input', function() {
|
||||
$('#global_cfg_negative_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.negative_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_positive_prompt').on('input', function() {
|
||||
$('#global_cfg_positive_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.positive_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`input[name="cfg_prompt_combine"]`).on('input', function() {
|
||||
const values = windowHtml.find(`input[name="cfg_prompt_combine"]`)
|
||||
$(`input[name="cfg_prompt_combine"]`).on('input', function() {
|
||||
const values = $('#cfgConfig').find(`input[name="cfg_prompt_combine"]`)
|
||||
.filter(":checked")
|
||||
.map(function() { return parseInt($(this).val()) })
|
||||
.map(function() { return Number($(this).val()) })
|
||||
.get()
|
||||
.filter((e) => !Number.isNaN(e)) || [];
|
||||
|
||||
@@ -354,17 +346,17 @@ jQuery(async () => {
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`#cfg_prompt_insertion_depth`).on('input', function() {
|
||||
$(`#cfg_prompt_insertion_depth`).on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_insertion_depth] = Number($(this).val());
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`#cfg_prompt_separator`).on('input', function() {
|
||||
$(`#cfg_prompt_separator`).on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_separator] = $(this).val();
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#groupchat_cfg_use_chara').on('input', function() {
|
||||
$('#groupchat_cfg_use_chara').on('input', function() {
|
||||
const checked = !!$(this).prop('checked');
|
||||
chat_metadata[metadataKeys.groupchat_individual_chars] = checked
|
||||
|
||||
@@ -375,20 +367,126 @@ jQuery(async () => {
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
$("#movingDivs").append(windowHtml);
|
||||
|
||||
initialLoadSettings();
|
||||
|
||||
if (extension_settings.cfg) {
|
||||
migrateSettings();
|
||||
}
|
||||
|
||||
const buttonHtml = $(await $.get(`${extensionFolderPath}/menuButton.html`));
|
||||
buttonHtml.on('click', onCfgMenuItemClick)
|
||||
buttonHtml.appendTo("#options_advanced");
|
||||
$('#option_toggle_CFG').on('click', onCfgMenuItemClick);
|
||||
|
||||
// Hook events
|
||||
eventSource.on(event_types.CHAT_CHANGED, async () => {
|
||||
await onChatChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const cfgType = {
|
||||
chat: 0,
|
||||
chara: 1,
|
||||
global: 2
|
||||
}
|
||||
|
||||
export const metadataKeys = {
|
||||
guidance_scale: "cfg_guidance_scale",
|
||||
negative_prompt: "cfg_negative_prompt",
|
||||
positive_prompt: "cfg_positive_prompt",
|
||||
prompt_combine: "cfg_prompt_combine",
|
||||
groupchat_individual_chars: "cfg_groupchat_individual_chars",
|
||||
prompt_insertion_depth: "cfg_prompt_insertion_depth",
|
||||
prompt_separator: "cfg_prompt_separator"
|
||||
}
|
||||
|
||||
// Gets the CFG guidance scale
|
||||
// If the guidance scale is 1, ignore the CFG prompt(s) since it won't be used anyways
|
||||
export function getGuidanceScale() {
|
||||
if (!extension_settings.cfg) {
|
||||
console.warn("CFG extension is not enabled. Skipping CFG guidance.");
|
||||
return;
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
||||
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
|
||||
|
||||
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
|
||||
return {
|
||||
type: cfgType.chat,
|
||||
value: chatGuidanceScale
|
||||
};
|
||||
}
|
||||
|
||||
if ((!selected_group && charaCfg || groupchatCharOverride) && charaCfg?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.chara,
|
||||
value: charaCfg.guidance_scale
|
||||
};
|
||||
}
|
||||
|
||||
if (extension_settings.cfg.global && extension_settings.cfg.global?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.global,
|
||||
value: extension_settings.cfg.global.guidance_scale
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CFG prompt separator.
|
||||
* @returns {string} The CFG prompt separator
|
||||
*/
|
||||
function getCustomSeparator() {
|
||||
const defaultSeparator = "\n";
|
||||
|
||||
try {
|
||||
if (chat_metadata[metadataKeys.prompt_separator]) {
|
||||
return JSON.parse(chat_metadata[metadataKeys.prompt_separator]);
|
||||
}
|
||||
|
||||
return defaultSeparator;
|
||||
} catch {
|
||||
console.warn("Invalid JSON detected for prompt separator. Using default separator.");
|
||||
return defaultSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the CFG prompt
|
||||
export function getCfgPrompt(guidanceScale, isNegative) {
|
||||
let splitCfgPrompt = [];
|
||||
|
||||
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
|
||||
if (guidanceScale.type === cfgType.chat || cfgPromptCombine.includes(cfgType.chat)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
chat_metadata[isNegative ? metadataKeys.negative_prompt : metadataKeys.positive_prompt]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
if (guidanceScale.type === cfgType.chara || cfgPromptCombine.includes(cfgType.chara)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? charaCfg.negative_prompt : charaCfg.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (guidanceScale.type === cfgType.global || cfgPromptCombine.includes(cfgType.global)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? extension_settings.cfg.global.negative_prompt : extension_settings.cfg.global.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const customSeparator = getCustomSeparator();
|
||||
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
|
||||
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
|
||||
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||
|
||||
return {
|
||||
value: combinedCfgPrompt,
|
||||
depth: insertionDepth
|
||||
};
|
||||
}
|
288
public/scripts/chats.js
Normal file
288
public/scripts/chats.js
Normal file
@@ -0,0 +1,288 @@
|
||||
// Move chat functions here from script.js (eventually)
|
||||
|
||||
import {
|
||||
addCopyToCodeBlocks,
|
||||
appendMediaToMessage,
|
||||
callPopup,
|
||||
chat,
|
||||
eventSource,
|
||||
event_types,
|
||||
getCurrentChatId,
|
||||
hideSwipeButtons,
|
||||
name2,
|
||||
saveChatDebounced,
|
||||
showSwipeButtons,
|
||||
} from "../script.js";
|
||||
import { getBase64Async, humanFileSize, saveBase64AsFile } from "./utils.js";
|
||||
|
||||
const fileSizeLimit = 1024 * 1024 * 1; // 1 MB
|
||||
|
||||
/**
|
||||
* Mark message as hidden (system message).
|
||||
* @param {number} messageId Message ID
|
||||
* @param {JQuery<Element>} messageBlock Message UI element
|
||||
* @returns
|
||||
*/
|
||||
export async function hideChatMessage(messageId, messageBlock) {
|
||||
const chatId = getCurrentChatId();
|
||||
|
||||
if (!chatId || isNaN(messageId)) return;
|
||||
|
||||
const message = chat[messageId];
|
||||
|
||||
if (!message) return;
|
||||
|
||||
message.is_system = true;
|
||||
messageBlock.attr('is_system', String(true));
|
||||
|
||||
// Reload swipes. Useful when a last message is hidden.
|
||||
hideSwipeButtons();
|
||||
showSwipeButtons();
|
||||
|
||||
saveChatDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark message as visible (non-system message).
|
||||
* @param {number} messageId Message ID
|
||||
* @param {JQuery<Element>} messageBlock Message UI element
|
||||
* @returns
|
||||
*/
|
||||
export async function unhideChatMessage(messageId, messageBlock) {
|
||||
const chatId = getCurrentChatId();
|
||||
|
||||
if (!chatId || isNaN(messageId)) return;
|
||||
|
||||
const message = chat[messageId];
|
||||
|
||||
if (!message) return;
|
||||
|
||||
message.is_system = false;
|
||||
messageBlock.attr('is_system', String(false));
|
||||
|
||||
// Reload swipes. Useful when a last message is hidden.
|
||||
hideSwipeButtons();
|
||||
showSwipeButtons();
|
||||
|
||||
saveChatDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file attachment to the message.
|
||||
* @param {object} message Message object
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function populateFileAttachment(message, inputId = 'file_form_input') {
|
||||
try {
|
||||
if (!message) return;
|
||||
if (!message.extra) message.extra = {};
|
||||
const fileInput = document.getElementById(inputId);
|
||||
if (!(fileInput instanceof HTMLInputElement)) return;
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
|
||||
// If file is image
|
||||
if (file.type.startsWith('image/')) {
|
||||
const base64Img = await getBase64Async(file);
|
||||
const base64ImgData = base64Img.split(',')[1];
|
||||
const extension = file.type.split('/')[1];
|
||||
const imageUrl = await saveBase64AsFile(base64ImgData, name2, file.name, extension);
|
||||
message.extra.image = imageUrl;
|
||||
message.extra.inline_image = true;
|
||||
} else {
|
||||
const fileText = await file.text();
|
||||
message.extra.file = {
|
||||
text: fileText,
|
||||
size: file.size,
|
||||
name: file.name,
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Could not upload file', error);
|
||||
} finally {
|
||||
$('#file_form').trigger('reset');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates file to make sure it is not binary or not image.
|
||||
* @param {File} file File object
|
||||
* @returns {Promise<boolean>} True if file is valid, false otherwise.
|
||||
*/
|
||||
async function validateFile(file) {
|
||||
const fileText = await file.text();
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);
|
||||
|
||||
if (!isImage && file.size > fileSizeLimit) {
|
||||
toastr.error(`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If file is binary
|
||||
if (isBinary && !isImage) {
|
||||
toastr.error('Binary files are not supported. Select a text file or image.');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function hasPendingFileAttachment() {
|
||||
const fileInput = document.getElementById('file_form_input');
|
||||
if (!(fileInput instanceof HTMLInputElement)) return false;
|
||||
const file = fileInput.files[0];
|
||||
return !!file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays file information in the message sending form.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function onFileAttach() {
|
||||
const fileInput = document.getElementById('file_form_input');
|
||||
if (!(fileInput instanceof HTMLInputElement)) return;
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const isValid = await validateFile(file);
|
||||
|
||||
// If file is binary
|
||||
if (!isValid) {
|
||||
$('#file_form').trigger('reset');
|
||||
return;
|
||||
}
|
||||
|
||||
$('#file_form .file_name').text(file.name);
|
||||
$('#file_form .file_size').text(humanFileSize(file.size));
|
||||
$('#file_form').removeClass('displayNone');
|
||||
|
||||
// Reset form on chat change
|
||||
eventSource.once(event_types.CHAT_CHANGED, () => {
|
||||
$('#file_form').trigger('reset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes file from message.
|
||||
* @param {number} messageId Message ID
|
||||
*/
|
||||
async function deleteMessageFile(messageId) {
|
||||
const confirm = await callPopup('Are you sure you want to delete this file?', 'confirm');
|
||||
|
||||
if (!confirm) {
|
||||
console.debug('Delete file cancelled');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = chat[messageId];
|
||||
|
||||
if (!message?.extra?.file) {
|
||||
console.debug('Message has no file');
|
||||
return;
|
||||
}
|
||||
|
||||
delete message.extra.file;
|
||||
$(`.mes[mesid="${messageId}"] .mes_file_container`).remove();
|
||||
saveChatDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens file from message in a modal.
|
||||
* @param {number} messageId Message ID
|
||||
*/
|
||||
async function viewMessageFile(messageId) {
|
||||
const messageText = chat[messageId]?.extra?.file?.text;
|
||||
|
||||
if (!messageText) {
|
||||
console.debug('Message has no file or it is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
const modalTemplate = $('<div><pre><code></code></pre></div>');
|
||||
modalTemplate.find('code').addClass('txt').text(messageText);
|
||||
modalTemplate.addClass('file_modal');
|
||||
addCopyToCodeBlocks(modalTemplate);
|
||||
|
||||
callPopup(modalTemplate, 'text');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a file embed into the message.
|
||||
* @param {number} messageId
|
||||
* @param {JQuery<HTMLElement>} messageBlock
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function embedMessageFile(messageId, messageBlock) {
|
||||
const message = chat[messageId];
|
||||
|
||||
if (!message) {
|
||||
console.warn('Failed to find message with id', messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#embed_file_input')
|
||||
.off('change')
|
||||
.on('change', parseAndUploadEmbed)
|
||||
.trigger('click');
|
||||
|
||||
async function parseAndUploadEmbed(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const isValid = await validateFile(file);
|
||||
|
||||
if (!isValid) {
|
||||
$('#file_form').trigger('reset');
|
||||
return;
|
||||
}
|
||||
|
||||
await populateFileAttachment(message, 'embed_file_input');
|
||||
appendMediaToMessage(message, messageBlock);
|
||||
saveChatDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
$(document).on('click', '.mes_hide', async function () {
|
||||
const messageBlock = $(this).closest('.mes');
|
||||
const messageId = Number(messageBlock.attr('mesid'));
|
||||
await hideChatMessage(messageId, messageBlock);
|
||||
});
|
||||
|
||||
$(document).on('click', '.mes_unhide', async function () {
|
||||
const messageBlock = $(this).closest('.mes');
|
||||
const messageId = Number(messageBlock.attr('mesid'));
|
||||
await unhideChatMessage(messageId, messageBlock);
|
||||
});
|
||||
|
||||
$(document).on('click', '.mes_file_delete', async function () {
|
||||
const messageBlock = $(this).closest('.mes');
|
||||
const messageId = Number(messageBlock.attr('mesid'));
|
||||
await deleteMessageFile(messageId);
|
||||
});
|
||||
|
||||
$(document).on('click', '.mes_file_open', async function () {
|
||||
const messageBlock = $(this).closest('.mes');
|
||||
const messageId = Number(messageBlock.attr('mesid'));
|
||||
await viewMessageFile(messageId);
|
||||
});
|
||||
|
||||
// Do not change. #attachFile is added by extension.
|
||||
$(document).on('click', '#attachFile', function () {
|
||||
$('#file_form_input').trigger('click');
|
||||
});
|
||||
|
||||
$(document).on('click', '.mes_embed', function () {
|
||||
const messageBlock = $(this).closest('.mes');
|
||||
const messageId = Number(messageBlock.attr('mesid'));
|
||||
embedMessageFile(messageId, messageBlock);
|
||||
});
|
||||
|
||||
$('#file_form_input').on('change', onFileAttach);
|
||||
$('#file_form').on('reset', function () {
|
||||
$('#file_form').addClass('displayNone');
|
||||
});
|
||||
})
|
@@ -1,4 +1,5 @@
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, substituteParams, renderTemplate } from "../script.js";
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, substituteParams, renderTemplate, animation_duration } from "../script.js";
|
||||
import { hideLoader, showLoader } from "./loader.js";
|
||||
import { isSubsetOf } from "./utils.js";
|
||||
export {
|
||||
getContext,
|
||||
@@ -11,7 +12,7 @@ export {
|
||||
ModuleWorkerWrapper,
|
||||
};
|
||||
|
||||
let extensionNames = [];
|
||||
export let extensionNames = [];
|
||||
let manifests = {};
|
||||
const defaultUrl = "http://localhost:5100";
|
||||
|
||||
@@ -102,7 +103,7 @@ class ModuleWorkerWrapper {
|
||||
}
|
||||
|
||||
// Called by the extension
|
||||
async update() {
|
||||
async update(...args) {
|
||||
// Don't touch me I'm busy...
|
||||
if (this.isBusy) {
|
||||
return;
|
||||
@@ -111,7 +112,7 @@ class ModuleWorkerWrapper {
|
||||
// I'm free. Let's update!
|
||||
try {
|
||||
this.isBusy = true;
|
||||
await this.callback();
|
||||
await this.callback(...args);
|
||||
}
|
||||
finally {
|
||||
this.isBusy = false;
|
||||
@@ -123,6 +124,7 @@ const extension_settings = {
|
||||
apiUrl: defaultUrl,
|
||||
apiKey: '',
|
||||
autoConnect: false,
|
||||
notifyUpdates: false,
|
||||
disabledExtensions: [],
|
||||
expressionOverrides: [],
|
||||
memory: {},
|
||||
@@ -134,7 +136,10 @@ const extension_settings = {
|
||||
caption: {
|
||||
refine_mode: false,
|
||||
},
|
||||
expressions: {},
|
||||
expressions: {
|
||||
/** @type {string[]} */
|
||||
custom: [],
|
||||
},
|
||||
dice: {},
|
||||
regex: [],
|
||||
tts: {},
|
||||
@@ -153,6 +158,11 @@ const extension_settings = {
|
||||
},
|
||||
speech_recognition: {},
|
||||
rvc: {},
|
||||
hypebot: {},
|
||||
vectors: {},
|
||||
variables: {
|
||||
global: {},
|
||||
},
|
||||
};
|
||||
|
||||
let modules = [];
|
||||
@@ -192,7 +202,6 @@ async function doExtrasFetch(endpoint, args) {
|
||||
}
|
||||
Object.assign(args.headers, {
|
||||
'Authorization': `Bearer ${extension_settings.apiKey}`,
|
||||
'Bypass-Tunnel-Reminder': 'bypass'
|
||||
});
|
||||
|
||||
const response = await fetch(endpoint, args);
|
||||
@@ -201,7 +210,7 @@ async function doExtrasFetch(endpoint, args) {
|
||||
|
||||
async function discoverExtensions() {
|
||||
try {
|
||||
const response = await fetch('/discover_extensions');
|
||||
const response = await fetch('/api/extensions/discover');
|
||||
|
||||
if (response.ok) {
|
||||
const extensions = await response.json();
|
||||
@@ -338,31 +347,41 @@ function addExtensionsButtonAndMenu() {
|
||||
|
||||
$(document.body).append(extensionsMenuHTML);
|
||||
|
||||
$('#send_but_sheld').prepend(buttonHTML);
|
||||
$('#leftSendForm').prepend(buttonHTML);
|
||||
|
||||
const button = $('#extensionsMenuButton');
|
||||
const dropdown = $('#extensionsMenu');
|
||||
//dropdown.hide();
|
||||
|
||||
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
|
||||
placement: 'top-end',
|
||||
placement: 'top-start',
|
||||
});
|
||||
|
||||
$(button).on('click', function () {
|
||||
popper.update()
|
||||
dropdown.fadeIn(250);
|
||||
if (!dropdown.is(':visible')) {
|
||||
dropdown.fadeIn(animation_duration);
|
||||
}
|
||||
});
|
||||
|
||||
$("html").on('touchstart mousedown', function (e) {
|
||||
let clickTarget = $(e.target);
|
||||
if (dropdown.is(':visible')
|
||||
&& clickTarget.closest(button).length == 0
|
||||
&& clickTarget.closest(dropdown).length == 0) {
|
||||
$(dropdown).fadeOut(250);
|
||||
const clickTarget = $(e.target);
|
||||
const noCloseTargets = ['#sd_gen'];
|
||||
if (dropdown.is(':visible') && !noCloseTargets.some(id => clickTarget.closest(id).length > 0)) {
|
||||
$(dropdown).fadeOut(animation_duration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function notifyUpdatesInputHandler() {
|
||||
extension_settings.notifyUpdates = !!$('#extensions_notify_updates').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
||||
if (extension_settings.notifyUpdates) {
|
||||
checkForExtensionUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* $(document).on('click', function (e) {
|
||||
const target = $(e.target);
|
||||
if (target.is(dropdown)) return;
|
||||
@@ -493,8 +512,8 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
|
||||
isUpToDate = data.isUpToDate;
|
||||
displayVersion = ` (${branch}-${commitHash.substring(0, 7)})`;
|
||||
updateButton = isUpToDate ?
|
||||
`<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Up to date"><i class="fa-solid fa-code-commit"></i></button></span>` :
|
||||
`<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Update available"><i class="fa-solid fa-download"></i></button></span>`;
|
||||
`<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Up to date"><i class="fa-solid fa-code-commit fa-fw"></i></button></span>` :
|
||||
`<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button></span>`;
|
||||
originHtml = `<a href="${origin}" target="_blank" rel="noopener noreferrer">`;
|
||||
}
|
||||
|
||||
@@ -565,7 +584,7 @@ async function getExtensionData(extension) {
|
||||
function getModuleInformation() {
|
||||
let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">Not connected to the API!</p>';
|
||||
return `
|
||||
<h3>Modules provided by your Extensions API:</h3>
|
||||
<h3>Modules provided by your Extras API:</h3>
|
||||
${moduleInfo}
|
||||
`;
|
||||
}
|
||||
@@ -574,26 +593,43 @@ function getModuleInformation() {
|
||||
* Generates the HTML strings for all extensions and displays them in a popup.
|
||||
*/
|
||||
async function showExtensionsDetails() {
|
||||
let htmlDefault = '<h3>Default Extensions:</h3>';
|
||||
let htmlExternal = '<h3>External Extensions:</h3>';
|
||||
try {
|
||||
showLoader();
|
||||
let htmlDefault = '<h3>Built-in Extensions:</h3>';
|
||||
let htmlExternal = '<h3>Installed Extensions:</h3>';
|
||||
|
||||
const extensions = Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order);
|
||||
const extensions = Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order);
|
||||
const promises = [];
|
||||
|
||||
for (const extension of extensions) {
|
||||
const { isExternal, extensionHtml } = await getExtensionData(extension);
|
||||
if (isExternal) {
|
||||
htmlExternal += extensionHtml;
|
||||
} else {
|
||||
htmlDefault += extensionHtml;
|
||||
for (const extension of extensions) {
|
||||
promises.push(getExtensionData(extension));
|
||||
}
|
||||
}
|
||||
|
||||
const html = `
|
||||
${getModuleInformation()}
|
||||
${htmlDefault}
|
||||
${htmlExternal}
|
||||
`;
|
||||
callPopup(`<div class="extensions_info">${html}</div>`, 'text');
|
||||
const settledPromises = await Promise.allSettled(promises);
|
||||
|
||||
settledPromises.forEach(promise => {
|
||||
if (promise.status === 'fulfilled') {
|
||||
const { isExternal, extensionHtml } = promise.value;
|
||||
if (isExternal) {
|
||||
htmlExternal += extensionHtml;
|
||||
} else {
|
||||
htmlDefault += extensionHtml;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const html = `
|
||||
${getModuleInformation()}
|
||||
${htmlDefault}
|
||||
${htmlExternal}
|
||||
`;
|
||||
callPopup(`<div class="extensions_info">${html}</div>`, 'text');
|
||||
} catch (error) {
|
||||
toastr.error('Error loading extensions. See browser console for details.');
|
||||
console.error(error);
|
||||
} finally {
|
||||
hideLoader();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -605,28 +641,44 @@ async function showExtensionsDetails() {
|
||||
*/
|
||||
async function onUpdateClick() {
|
||||
const extensionName = $(this).data('name');
|
||||
$(this).find('i').addClass('fa-spin');
|
||||
await updateExtension(extensionName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a third-party extension via the API.
|
||||
* @param {string} extensionName Extension folder name
|
||||
* @param {boolean} quiet If true, don't show a success message
|
||||
*/
|
||||
async function updateExtension(extensionName, quiet) {
|
||||
try {
|
||||
const response = await fetch('/update_extension', {
|
||||
const response = await fetch('/api/extensions/update', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ extensionName })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.isUpToDate) {
|
||||
toastr.success('Extension is already up to date');
|
||||
} else {
|
||||
toastr.success(`Extension updated to ${data.shortCommitHash}`);
|
||||
|
||||
if (!quiet) {
|
||||
showExtensionsDetails();
|
||||
}
|
||||
|
||||
if (data.isUpToDate) {
|
||||
if (!quiet) {
|
||||
toastr.success('Extension is already up to date');
|
||||
}
|
||||
} else {
|
||||
toastr.success(`Extension ${extensionName} updated to ${data.shortCommitHash}`);
|
||||
}
|
||||
showExtensionsDetails();
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the click event for the delete button of an extension.
|
||||
* This function makes a POST request to '/delete_extension' with the extension's name.
|
||||
* This function makes a POST request to '/api/extensions/delete' with the extension's name.
|
||||
* If the extension is deleted, it displays a success message.
|
||||
* Creates a popup for the user to confirm before delete.
|
||||
*/
|
||||
@@ -635,23 +687,26 @@ async function onDeleteClick() {
|
||||
// use callPopup to create a popup for the user to confirm before delete
|
||||
const confirmation = await callPopup(`Are you sure you want to delete ${extensionName}?`, 'delete_extension');
|
||||
if (confirmation) {
|
||||
try {
|
||||
const response = await fetch('/delete_extension', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ extensionName })
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
toastr.success(`Extension ${extensionName} deleted`);
|
||||
showExtensionsDetails();
|
||||
// reload the page to remove the extension from the list
|
||||
location.reload();
|
||||
await deleteExtension(extensionName);
|
||||
}
|
||||
};
|
||||
|
||||
export async function deleteExtension(extensionName) {
|
||||
try {
|
||||
const response = await fetch('/api/extensions/delete', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ extensionName })
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
|
||||
toastr.success(`Extension ${extensionName} deleted`);
|
||||
showExtensionsDetails();
|
||||
// reload the page to remove the extension from the list
|
||||
location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the version details of a specific extension.
|
||||
@@ -663,7 +718,7 @@ async function onDeleteClick() {
|
||||
*/
|
||||
async function getExtensionVersion(extensionName) {
|
||||
try {
|
||||
const response = await fetch('/get_extension_version', {
|
||||
const response = await fetch('/api/extensions/version', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ extensionName })
|
||||
@@ -676,9 +731,42 @@ async function getExtensionVersion(extensionName) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a third-party extension via the API.
|
||||
* @param {string} url Extension repository URL
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function installExtension(url) {
|
||||
console.debug('Extension installation started', url);
|
||||
|
||||
toastr.info('Please wait...', 'Installing extension');
|
||||
|
||||
async function loadExtensionSettings(settings) {
|
||||
const request = await fetch('/api/extensions/install', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ url }),
|
||||
});
|
||||
|
||||
if (!request.ok) {
|
||||
const text = await request.text();
|
||||
toastr.warning(text || request.statusText, 'Extension installation failed', { timeOut: 5000 });
|
||||
console.error('Extension installation failed', request.status, request.statusText, text);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await request.json();
|
||||
toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');
|
||||
console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
|
||||
await loadExtensionSettings({}, false);
|
||||
eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads extension settings from the app settings.
|
||||
* @param {object} settings App Settings
|
||||
* @param {boolean} versionChanged Is this a version change?
|
||||
*/
|
||||
async function loadExtensionSettings(settings, versionChanged) {
|
||||
if (settings.extension_settings) {
|
||||
Object.assign(extension_settings, settings.extension_settings);
|
||||
}
|
||||
@@ -686,28 +774,124 @@ async function loadExtensionSettings(settings) {
|
||||
$("#extensions_url").val(extension_settings.apiUrl);
|
||||
$("#extensions_api_key").val(extension_settings.apiKey);
|
||||
$("#extensions_autoconnect").prop('checked', extension_settings.autoConnect);
|
||||
$("#extensions_notify_updates").prop('checked', extension_settings.notifyUpdates);
|
||||
|
||||
// Activate offline extensions
|
||||
eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
|
||||
extensionNames = await discoverExtensions();
|
||||
manifests = await getManifests(extensionNames)
|
||||
|
||||
if (versionChanged) {
|
||||
await autoUpdateExtensions();
|
||||
}
|
||||
|
||||
await activateExtensions();
|
||||
if (extension_settings.autoConnect && extension_settings.apiUrl) {
|
||||
connectToApi(extension_settings.apiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
export function doDailyExtensionUpdatesCheck() {
|
||||
setTimeout(() => {
|
||||
if (extension_settings.notifyUpdates) {
|
||||
checkForExtensionUpdates(false);
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are updates available for 3rd-party extensions.
|
||||
* @param {boolean} force Skip nag check
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async function checkForExtensionUpdates(force) {
|
||||
if (!force) {
|
||||
const STORAGE_NAG_KEY = 'extension_update_nag';
|
||||
const currentDate = new Date().toDateString();
|
||||
|
||||
// Don't nag more than once a day
|
||||
if (localStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(STORAGE_NAG_KEY, currentDate);
|
||||
}
|
||||
|
||||
const updatesAvailable = [];
|
||||
const promises = [];
|
||||
|
||||
for (const [id, manifest] of Object.entries(manifests)) {
|
||||
if (manifest.auto_update && id.startsWith('third-party')) {
|
||||
const promise = new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const data = await getExtensionVersion(id.replace('third-party', ''));
|
||||
if (data.isUpToDate === false) {
|
||||
updatesAvailable.push(manifest.display_name);
|
||||
}
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error('Error checking for extension updates', error);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
|
||||
if (updatesAvailable.length > 0) {
|
||||
toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, 'Extension updates available');
|
||||
}
|
||||
}
|
||||
|
||||
async function autoUpdateExtensions() {
|
||||
if (!Object.values(manifests).some(x => x.auto_update)) {
|
||||
return;
|
||||
}
|
||||
|
||||
toastr.info('Auto-updating extensions. This may take several minutes.', 'Please wait...', { timeOut: 10000, extendedTimeOut: 20000 });
|
||||
const promises = [];
|
||||
for (const [id, manifest] of Object.entries(manifests)) {
|
||||
if (manifest.auto_update && id.startsWith('third-party')) {
|
||||
console.debug(`Auto-updating 3rd-party extension: ${manifest.display_name} (${id})`);
|
||||
promises.push(updateExtension(id.replace('third-party', ''), true));
|
||||
}
|
||||
}
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the generate interceptors for all extensions.
|
||||
* @param {any[]} chat Chat array
|
||||
* @param {number} contextSize Context size
|
||||
* @returns {Promise<boolean>} True if generation should be aborted
|
||||
*/
|
||||
async function runGenerationInterceptors(chat, contextSize) {
|
||||
let aborted = false;
|
||||
let exitImmediately = false;
|
||||
|
||||
const abort = (/** @type {boolean} */ immediately) => {
|
||||
aborted = true;
|
||||
exitImmediately = immediately;
|
||||
};
|
||||
|
||||
for (const manifest of Object.values(manifests)) {
|
||||
const interceptorKey = manifest.generate_interceptor;
|
||||
if (typeof window[interceptorKey] === 'function') {
|
||||
try {
|
||||
await window[interceptorKey](chat, contextSize);
|
||||
await window[interceptorKey](chat, contextSize, abort);
|
||||
} catch (e) {
|
||||
console.error(`Failed running interceptor for ${manifest.display_name}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (exitImmediately) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return aborted;
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
@@ -717,8 +901,36 @@ jQuery(function () {
|
||||
$("#extensions_connect").on('click', connectClickHandler);
|
||||
$("#extensions_autoconnect").on('input', autoConnectInputHandler);
|
||||
$("#extensions_details").on('click', showExtensionsDetails);
|
||||
$("#extensions_notify_updates").on('input', notifyUpdatesInputHandler);
|
||||
$(document).on('click', '.toggle_disable', onDisableExtensionClick);
|
||||
$(document).on('click', '.toggle_enable', onEnableExtensionClick);
|
||||
$(document).on('click', '.btn_update', onUpdateClick);
|
||||
$(document).on('click', '.btn_delete', onDeleteClick);
|
||||
|
||||
/**
|
||||
* Handles the click event for the third-party extension import button.
|
||||
* Prompts the user to enter the Git URL of the extension to import.
|
||||
* After obtaining the Git URL, makes a POST request to '/api/extensions/install' to import the extension.
|
||||
* If the extension is imported successfully, a success message is displayed.
|
||||
* If the extension import fails, an error message is displayed and the error is logged to the console.
|
||||
* After successfully importing the extension, the extension settings are reloaded and a 'EXTENSION_SETTINGS_LOADED' event is emitted.
|
||||
*
|
||||
* @listens #third_party_extension_button#click - The click event of the '#third_party_extension_button' element.
|
||||
*/
|
||||
$('#third_party_extension_button').on('click', async () => {
|
||||
const html = `<h3>Enter the Git URL of the extension to install</h3>
|
||||
<br>
|
||||
<p><b>Disclaimer:</b> Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.</p>
|
||||
<br>
|
||||
<p>Example: <tt> https://github.com/author/extension-name </tt></p>`
|
||||
const input = await callPopup(html, 'input');
|
||||
|
||||
if (!input) {
|
||||
console.debug('Extension install cancelled');
|
||||
return;
|
||||
}
|
||||
|
||||
const url = input.trim();
|
||||
await installExtension(url);
|
||||
});
|
||||
});
|
||||
|
9
public/scripts/extensions/assets/confirm.html
Normal file
9
public/scripts/extensions/assets/confirm.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<div class="m-b-1">
|
||||
Are you sure you want to connect to '{{url}}'?
|
||||
</div>
|
||||
<div class="flex-container justifyCenter">
|
||||
<label class="checkbox_label" for="assets-remember">
|
||||
<input type="checkbox" id="assets-remember">
|
||||
Don't ask again for this URL
|
||||
</label>
|
||||
</div>
|
@@ -1,14 +1,16 @@
|
||||
/*
|
||||
TODO:
|
||||
- Check failed install file (0kb size ?)
|
||||
*/
|
||||
//const DEBUG_TONY_SAMA_FORK_MODE = false
|
||||
//const DEBUG_TONY_SAMA_FORK_MODE = true
|
||||
|
||||
import { getRequestHeaders, callPopup } from "../../../script.js";
|
||||
import { deleteExtension, extensionNames, installExtension, renderExtensionTemplate } from "../../extensions.js";
|
||||
import { getStringHash, isValidUrl } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'Assets';
|
||||
const MODULE_NAME = 'assets';
|
||||
const DEBUG_PREFIX = "<Assets module> ";
|
||||
let previewAudio = null;
|
||||
let ASSETS_JSON_URL = "https://raw.githubusercontent.com/SillyTavern/SillyTavern-Content/main/index.json"
|
||||
|
||||
const extensionName = "assets";
|
||||
@@ -29,7 +31,7 @@ const defaultSettings = {
|
||||
|
||||
function downloadAssetsList(url) {
|
||||
updateCurrentAssets().then(function () {
|
||||
fetch(url)
|
||||
fetch(url, { cache: "no-cache" })
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
|
||||
@@ -47,19 +49,29 @@ function downloadAssetsList(url) {
|
||||
}
|
||||
|
||||
console.debug(DEBUG_PREFIX, "Updated available assets to", availableAssets);
|
||||
// First extensions, then everything else
|
||||
const assetTypes = Object.keys(availableAssets).sort((a, b) => (a === 'extension') ? -1 : (b === 'extension') ? 1 : 0);
|
||||
|
||||
for (const assetType in availableAssets) {
|
||||
for (const assetType of assetTypes) {
|
||||
let assetTypeMenu = $('<div />', { id: "assets_audio_ambient_div", class: "assets-list-div" });
|
||||
assetTypeMenu.append(`<h3>${assetType}</h3>`)
|
||||
|
||||
if (assetType == 'extension') {
|
||||
assetTypeMenu.append(`
|
||||
<div class="assets-list-git">
|
||||
To download extensions from this page, you need to have <a href="https://git-scm.com/downloads" target="_blank">Git</a> installed.
|
||||
</div>`);
|
||||
}
|
||||
|
||||
for (const i in availableAssets[assetType]) {
|
||||
const asset = availableAssets[assetType][i];
|
||||
const elemId = `assets_install_${assetType}_${i}`;
|
||||
let element = $('<button />', { id: elemId, type: "button", class: "asset-download-button menu_button" })
|
||||
const label = $("<i class=\"fa-solid fa-download fa-xl\"></i>");
|
||||
const label = $("<i class=\"fa-fw fa-solid fa-download fa-xl\"></i>");
|
||||
element.append(label);
|
||||
|
||||
//if (DEBUG_TONY_SAMA_FORK_MODE)
|
||||
// assetUrl = assetUrl.replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
|
||||
// asset["url"] = asset["url"].replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
|
||||
|
||||
console.debug(DEBUG_PREFIX, "Checking asset", asset["id"], asset["url"]);
|
||||
|
||||
@@ -71,18 +83,18 @@ function downloadAssetsList(url) {
|
||||
label.addClass("fa-check");
|
||||
this.classList.remove('asset-download-button-loading');
|
||||
element.on("click", assetDelete);
|
||||
element.on("mouseenter", function(){
|
||||
element.on("mouseenter", function () {
|
||||
label.removeClass("fa-check");
|
||||
label.addClass("fa-trash");
|
||||
label.addClass("redOverlayGlow");
|
||||
}).on("mouseleave", function(){
|
||||
}).on("mouseleave", function () {
|
||||
label.addClass("fa-check");
|
||||
label.removeClass("fa-trash");
|
||||
label.removeClass("redOverlayGlow");
|
||||
});
|
||||
};
|
||||
|
||||
const assetDelete = async function() {
|
||||
const assetDelete = async function () {
|
||||
element.off("click");
|
||||
await deleteAsset(assetType, asset["id"]);
|
||||
label.removeClass("fa-check");
|
||||
@@ -98,11 +110,11 @@ function downloadAssetsList(url) {
|
||||
label.toggleClass("fa-download");
|
||||
label.toggleClass("fa-check");
|
||||
element.on("click", assetDelete);
|
||||
element.on("mouseenter", function(){
|
||||
element.on("mouseenter", function () {
|
||||
label.removeClass("fa-check");
|
||||
label.addClass("fa-trash");
|
||||
label.addClass("redOverlayGlow");
|
||||
}).on("mouseleave", function(){
|
||||
}).on("mouseleave", function () {
|
||||
label.addClass("fa-check");
|
||||
label.removeClass("fa-trash");
|
||||
label.removeClass("redOverlayGlow");
|
||||
@@ -114,14 +126,28 @@ function downloadAssetsList(url) {
|
||||
element.on("click", assetInstall);
|
||||
}
|
||||
|
||||
console.debug(DEBUG_PREFIX, "Created element for BGM", asset["id"])
|
||||
console.debug(DEBUG_PREFIX, "Created element for ", asset["id"])
|
||||
|
||||
const displayName = DOMPurify.sanitize(asset["name"] || asset["id"]);
|
||||
const description = DOMPurify.sanitize(asset["description"] || "");
|
||||
const url = isValidUrl(asset["url"]) ? asset["url"] : "";
|
||||
const previewIcon = assetType == 'extension' ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';
|
||||
|
||||
$(`<i></i>`)
|
||||
.append(element)
|
||||
.append(`<span>${asset["id"]}</span>`)
|
||||
.append(`<div class="flex-container flexFlowColumn">
|
||||
<span class="flex-container alignitemscenter">
|
||||
<b>${displayName}</b>
|
||||
<a class="asset_preview" href="${url}" target="_blank" title="Preview in browser">
|
||||
<i class="fa-solid fa-sm ${previewIcon}"></i>
|
||||
</a>
|
||||
</span>
|
||||
<span>${description}</span>
|
||||
</div>`)
|
||||
.appendTo(assetTypeMenu);
|
||||
}
|
||||
assetTypeMenu.appendTo("#assets_menu");
|
||||
assetTypeMenu.on('click', 'a.asset_preview', previewAsset);
|
||||
}
|
||||
|
||||
$("#assets_menu").show();
|
||||
@@ -135,8 +161,37 @@ function downloadAssetsList(url) {
|
||||
});
|
||||
}
|
||||
|
||||
function previewAsset(e) {
|
||||
const href = $(this).attr('href');
|
||||
const audioExtensions = ['.mp3', '.ogg', '.wav'];
|
||||
|
||||
if (audioExtensions.some(ext => href.endsWith(ext))) {
|
||||
e.preventDefault();
|
||||
|
||||
if (previewAudio) {
|
||||
previewAudio.pause();
|
||||
|
||||
if (previewAudio.src === href) {
|
||||
previewAudio = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
previewAudio = new Audio(href);
|
||||
previewAudio.play();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function isAssetInstalled(assetType, filename) {
|
||||
for (const i of currentAssets[assetType]) {
|
||||
let assetList = currentAssets[assetType];
|
||||
|
||||
if (assetType == 'extension') {
|
||||
const thirdPartyMarker = "third-party/";
|
||||
assetList = extensionNames.filter(x => x.startsWith(thirdPartyMarker)).map(x => x.replace(thirdPartyMarker, ''));
|
||||
}
|
||||
|
||||
for (const i of assetList) {
|
||||
//console.debug(DEBUG_PREFIX,i,filename)
|
||||
if (i.includes(filename))
|
||||
return true;
|
||||
@@ -149,8 +204,15 @@ async function installAsset(url, assetType, filename) {
|
||||
console.debug(DEBUG_PREFIX, "Downloading ", url);
|
||||
const category = assetType;
|
||||
try {
|
||||
if (category === 'extension') {
|
||||
console.debug(DEBUG_PREFIX, "Installing extension ", url)
|
||||
await installExtension(url);
|
||||
console.debug(DEBUG_PREFIX, "Extension installed.")
|
||||
return;
|
||||
}
|
||||
|
||||
const body = { url, category, filename };
|
||||
const result = await fetch('/asset_download', {
|
||||
const result = await fetch('/api/assets/download', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(body),
|
||||
@@ -170,8 +232,14 @@ async function deleteAsset(assetType, filename) {
|
||||
console.debug(DEBUG_PREFIX, "Deleting ", assetType, filename);
|
||||
const category = assetType;
|
||||
try {
|
||||
if (category === 'extension') {
|
||||
console.debug(DEBUG_PREFIX, "Deleting extension ", filename)
|
||||
await deleteExtension(filename);
|
||||
console.debug(DEBUG_PREFIX, "Extension deleted.")
|
||||
}
|
||||
|
||||
const body = { category, filename };
|
||||
const result = await fetch('/asset_delete', {
|
||||
const result = await fetch('/api/assets/delete', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(body),
|
||||
@@ -194,7 +262,7 @@ async function deleteAsset(assetType, filename) {
|
||||
async function updateCurrentAssets() {
|
||||
console.debug(DEBUG_PREFIX, "Checking installed assets...")
|
||||
try {
|
||||
const result = await fetch(`/get_assets`, {
|
||||
const result = await fetch(`/api/assets/get`, {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
@@ -214,24 +282,35 @@ async function updateCurrentAssets() {
|
||||
// This function is called when the extension is loaded
|
||||
jQuery(async () => {
|
||||
// This is an example of loading HTML from a file
|
||||
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
||||
const windowHtml = $(renderExtensionTemplate(MODULE_NAME, 'window', {}));
|
||||
|
||||
const assetsJsonUrl = windowHtml.find('#assets-json-url-field');
|
||||
assetsJsonUrl.val(ASSETS_JSON_URL);
|
||||
|
||||
const connectButton = windowHtml.find('#assets-connect-button');
|
||||
connectButton.on("click", async function () {
|
||||
const confirmation = await callPopup(`Are you sure you want to connect to '${assetsJsonUrl.val()}'?`, 'confirm')
|
||||
const url = String(assetsJsonUrl.val());
|
||||
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
|
||||
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
|
||||
|
||||
const template = renderExtensionTemplate(MODULE_NAME, 'confirm', { url });
|
||||
const confirmation = skipConfirm || await callPopup(template, 'confirm');
|
||||
|
||||
if (confirmation) {
|
||||
try {
|
||||
if (!skipConfirm) {
|
||||
const rememberValue = Boolean($('#assets-remember').prop('checked'));
|
||||
localStorage.setItem(rememberKey, String(rememberValue));
|
||||
}
|
||||
|
||||
console.debug(DEBUG_PREFIX, "Confimation, loading assets...");
|
||||
downloadAssetsList(assetsJsonUrl.val());
|
||||
downloadAssetsList(url);
|
||||
connectButton.removeClass("fa-plug-circle-exclamation");
|
||||
connectButton.removeClass("redOverlayGlow");
|
||||
connectButton.addClass("fa-plug-circle-check");
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
toastr.error(`Cannot get assets list from ${assetsJsonUrl.val()}`);
|
||||
toastr.error(`Cannot get assets list from ${url}`);
|
||||
connectButton.removeClass("fa-plug-circle-check");
|
||||
connectButton.addClass("fa-plug-circle-exclamation");
|
||||
connectButton.removeClass("redOverlayGlow");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user