mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
585 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7bf62b3dad | ||
|
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 |
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug Report 🐛
|
||||
description: Report something that's not working the way it's (probably) intended to. PAY ATTENTION, Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!"
|
||||
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:
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -9,7 +9,7 @@ body:
|
||||
- type: dropdown
|
||||
id: similarRequest
|
||||
attributes:
|
||||
label: Have you searched for similar [requests](https://github.com/SillyTavern/SillyTavern/issues?q=)?
|
||||
label: Have you searched for similar requests?
|
||||
description:
|
||||
options:
|
||||
- 'No'
|
||||
|
5
.github/readme-zh_cn.md
vendored
5
.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 的分叉版本
|
||||
|
||||
@@ -81,7 +81,6 @@ SillyTavern 支持扩展服务,一些额外的人工智能模块可通过 [Sil
|
||||
* 在聊天窗口发送图片,并由人工智能解释图片内容
|
||||
* 文本图像生成(5 预设,以及 "自由模式")
|
||||
* 聊天信息的文字转语音(通过 ElevenLabs、Silero 或操作系统的语音生成)
|
||||
* ChromaDB 向量数据库,用于更智能的聊天 Prompt
|
||||
|
||||
扩展服务的完整功能介绍和使用教程,请参阅 [Docs](https://docs.sillytavern.app/extras/extensions/)。
|
||||
|
||||
|
15
.github/readme.md
vendored
15
.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.
|
||||
@@ -80,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/).
|
||||
|
||||
|
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -11,7 +11,7 @@ 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/${{ github.repository_owner }}/sillytavern
|
||||
image_name: ghcr.io/sillytavern/sillytavern
|
||||
|
||||
jobs:
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -38,3 +38,4 @@ public/assets/
|
||||
access.log
|
||||
/vectors/
|
||||
/cache/
|
||||
public/css/user.css
|
||||
|
@@ -23,7 +23,7 @@ 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="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 && \
|
||||
|
@@ -70,7 +70,7 @@
|
||||
"#@markdown * ckpt/sd15 - base SD 1.5\n",
|
||||
"#@markdown * stabilityai/stable-diffusion-2-1-base - base SD 2.1\n",
|
||||
"extras_enable_chromadb = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables ChromaDB for Infinity Context plugin\n",
|
||||
"#@markdown Enables ChromaDB module\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import secrets\n",
|
||||
@@ -116,7 +116,7 @@
|
||||
"!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 tensorflow==2.14\n",
|
||||
"!pip install colorama\n",
|
||||
"!pip install Flask-Cors\n",
|
||||
"!pip install Flask-Compress\n",
|
||||
|
@@ -10,6 +10,7 @@ const listen = true; // If true, Can be access from other device or PC. otherwis
|
||||
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.
|
||||
const thumbnailsQuality = 95; // Quality of thumbnails. 0-100
|
||||
const disableChatBackup = false; // Disables the backup of chat logs to the /backups folder
|
||||
|
||||
// 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.
|
||||
@@ -26,6 +27,8 @@ const extras = {
|
||||
captioningModel: 'Xenova/vit-gpt2-image-captioning',
|
||||
// Feature extraction model. HuggingFace ID of a model in ONNX format.
|
||||
embeddingModel: 'Xenova/all-mpnet-base-v2',
|
||||
// GPT-2 text generation model. HuggingFace ID of a model in ONNX format.
|
||||
promptExpansionModel: 'Cohee/fooocus_expansion-onnx',
|
||||
};
|
||||
|
||||
// Request overrides for additional headers
|
||||
@@ -49,4 +52,5 @@ module.exports = {
|
||||
requestOverrides,
|
||||
thumbnailsQuality,
|
||||
extras,
|
||||
disableChatBackup,
|
||||
};
|
||||
|
@@ -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,
|
||||
@@ -164,6 +163,8 @@
|
||||
"custom_stopping_strings_macro": true,
|
||||
"fuzzy_search": true,
|
||||
"encode_tags": false,
|
||||
"enableLabMode": false,
|
||||
"enableZenSliders": false,
|
||||
"ui_mode": 1
|
||||
},
|
||||
"extension_settings": {
|
||||
@@ -405,8 +406,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0,
|
||||
"single_line": false,
|
||||
"use_stop_sequence": false,
|
||||
"streaming_kobold": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
@@ -416,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. */
|
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Initialize missing user files
|
||||
IFS="," RESOURCES="characters,groups,group chats,chats,User Avatars,worlds"
|
||||
IFS="," RESOURCES="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"
|
||||
|
433
package-lock.json
generated
433
package-lock.json
generated
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.10.4",
|
||||
"version": "1.10.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.10.4",
|
||||
"version": "1.10.9",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@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",
|
||||
@@ -694,11 +695,57 @@
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
|
||||
"dependencies": {
|
||||
"defer-to-connect": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tokenizer/token": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||
},
|
||||
"node_modules/@types/cacheable-request": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
||||
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
|
||||
"dependencies": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "^3.1.4",
|
||||
"@types/node": "*",
|
||||
"@types/responselike": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-cache-semantics": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz",
|
||||
"integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw=="
|
||||
},
|
||||
"node_modules/@types/keyv": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
|
||||
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
@@ -709,6 +756,34 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
|
||||
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||
"integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/responselike": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz",
|
||||
"integrity": "sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -756,6 +831,17 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/agentkeepalive": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
|
||||
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
|
||||
"dependencies": {
|
||||
"humanize-ms": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
@@ -830,15 +916,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
|
||||
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
|
||||
"integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base-64": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
|
||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -858,6 +949,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/bing-translate-api": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz",
|
||||
"integrity": "sha512-DaYqa7iupfj+fj/KeaeZSp5FUY/ZR4sZ6jqoTP0RHkYOUfo7wwoxlhYDkh4VcvBBzuVORnBEgdXBVQrfM4kk7g==",
|
||||
"dependencies": {
|
||||
"got": "^11.8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
@@ -999,6 +1098,31 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
|
||||
"engines": {
|
||||
"node": ">=10.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-request": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
|
||||
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
|
||||
"dependencies": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
"http-cache-semantics": "^4.0.0",
|
||||
"keyv": "^4.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"normalize-url": "^6.0.1",
|
||||
"responselike": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
@@ -1027,6 +1151,14 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cheerio": {
|
||||
"version": "1.0.0-rc.12",
|
||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
|
||||
@@ -1082,6 +1214,25 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-response": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
|
||||
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
|
||||
"dependencies": {
|
||||
"mimic-response": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-response/node_modules/mimic-response": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -1244,6 +1395,14 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/csrf-csrf": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/csrf-csrf/-/csrf-csrf-2.2.4.tgz",
|
||||
@@ -1290,7 +1449,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
@@ -1310,6 +1468,14 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/defer-to-connect": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
|
||||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
@@ -1360,6 +1526,15 @@
|
||||
"node": ">= 8.11.4"
|
||||
}
|
||||
},
|
||||
"node_modules/digest-fetch": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz",
|
||||
"integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==",
|
||||
"dependencies": {
|
||||
"base-64": "^0.1.0",
|
||||
"md5": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@@ -1458,7 +1633,6 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -1503,6 +1677,14 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/exif-parser": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
||||
@@ -1700,6 +1882,31 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data-encoder": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
|
||||
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
|
||||
},
|
||||
"node_modules/formdata-node": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
||||
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
|
||||
"dependencies": {
|
||||
"node-domexception": "1.0.0",
|
||||
"web-streams-polyfill": "4.0.0-beta.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-node/node_modules/web-streams-polyfill": {
|
||||
"version": "4.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -1774,6 +1981,20 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gifwrap": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz",
|
||||
@@ -1835,6 +2056,30 @@
|
||||
"resolved": "https://registry.npmjs.org/google-translate-api-browser/-/google-translate-api-browser-3.0.1.tgz",
|
||||
"integrity": "sha512-KTLodkyGBWMK9IW6QIeJ2zCuju4Z0CLpbkADKo+yLhbSTD4l+CXXpQ/xaynGVAzeBezzJG6qn8MLeqOq3SmW0A=="
|
||||
},
|
||||
"node_modules/got": {
|
||||
"version": "11.8.6",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
|
||||
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.2",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/got?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/gpt-3-encoder": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/gpt-3-encoder/-/gpt-3-encoder-1.1.4.tgz",
|
||||
@@ -1922,6 +2167,11 @@
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
@@ -1937,6 +2187,18 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-wrapper": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
|
||||
"dependencies": {
|
||||
"quick-lru": "^5.1.1",
|
||||
"resolve-alpn": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
@@ -1973,6 +2235,14 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
|
||||
"dependencies": {
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@@ -2068,6 +2338,11 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
|
||||
@@ -2196,6 +2471,11 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
},
|
||||
"node_modules/json-colorizer": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/json-colorizer/-/json-colorizer-2.2.2.tgz",
|
||||
@@ -2284,6 +2564,14 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
|
||||
"integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/load-bmfont": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz",
|
||||
@@ -2314,6 +2602,14 @@
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"node_modules/lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -2326,6 +2622,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"dependencies": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -2403,7 +2709,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -2530,6 +2835,24 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||
@@ -2549,6 +2872,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
@@ -2604,7 +2938,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2666,20 +2999,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz",
|
||||
"integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==",
|
||||
"version": "4.17.4",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.17.4.tgz",
|
||||
"integrity": "sha512-ThRFkl6snLbcAKS58St7N3CaKuI5WdYUvIjPvf4s+8SdymgNtOfzmZcZXVcCefx04oKFnvZJvIcTh3eAFUUhAQ==",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"form-data": "^4.0.0"
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"abort-controller": "^3.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"digest-fetch": "^1.3.0",
|
||||
"form-data-encoder": "1.7.2",
|
||||
"formdata-node": "^4.3.2",
|
||||
"node-fetch": "^2.6.7",
|
||||
"web-streams-polyfill": "^3.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
}
|
||||
},
|
||||
"node_modules/openai/node_modules/axios": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
||||
"node_modules/openai/node_modules/@types/node": {
|
||||
"version": "18.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz",
|
||||
"integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.8"
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/p-cancelable": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-is-promise": {
|
||||
@@ -3108,7 +3459,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -3148,6 +3498,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -3265,6 +3626,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
|
||||
},
|
||||
"node_modules/resolve/node_modules/is-core-module": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
|
||||
@@ -3297,6 +3663,17 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/responselike": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
|
||||
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
|
||||
"dependencies": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
@@ -3790,6 +4167,11 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
@@ -3867,6 +4249,14 @@
|
||||
"vectra": "bin/vectra.js"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
@@ -3905,8 +4295,7 @@
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/write-file-atomic": {
|
||||
"version": "5.0.1",
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"device-detector-js": "^3.0.3",
|
||||
"express": "^4.18.2",
|
||||
"google-translate-api-browser": "^3.0.1",
|
||||
"bing-translate-api": "^2.9.1",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
"ip-matching": "^2.1.2",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
@@ -37,6 +38,9 @@
|
||||
"overrides": {
|
||||
"parse-bmfont-xml": {
|
||||
"xml2js": "^0.5.0"
|
||||
},
|
||||
"vectra": {
|
||||
"openai": "^4.17.0"
|
||||
}
|
||||
},
|
||||
"name": "sillytavern",
|
||||
@@ -46,7 +50,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.10.4",
|
||||
"version": "1.10.9",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start-multi": "node server.js --disableCsrf",
|
||||
|
@@ -13,6 +13,7 @@ function createDefaultFiles() {
|
||||
settings: './public/settings.json',
|
||||
bg_load: './public/css/bg_load.css',
|
||||
config: './config.conf',
|
||||
user: './public/css/user.css',
|
||||
};
|
||||
|
||||
for (const file of Object.values(files)) {
|
||||
|
@@ -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": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -21,5 +20,5 @@
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.61,
|
||||
"mirostat_eta": 1,
|
||||
"use_default_badwordsids": true
|
||||
}
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -21,5 +20,5 @@
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.91,
|
||||
"mirostat_eta": 1,
|
||||
"use_default_badwordsids": true
|
||||
}
|
||||
"grammar": ""
|
||||
}
|
@@ -8,7 +8,6 @@
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"single_line": false,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -21,5 +20,5 @@
|
||||
"mirostat": 2,
|
||||
"mirostat_tau": 9.62,
|
||||
"mirostat_eta": 1,
|
||||
"use_default_badwordsids": true
|
||||
}
|
||||
"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": ""
|
||||
}
|
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/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;
|
||||
}
|
||||
@@ -103,39 +98,40 @@ input.extension_missing[type="checkbox"] {
|
||||
}
|
||||
|
||||
/** LEFT COLUMN **/
|
||||
#extensions_settings>.expression_settings {
|
||||
order: 1;
|
||||
/* Must be always on top */
|
||||
#extensions_settings>#assets_ui {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
#extensions_settings>.background_settings {
|
||||
#extensions_settings>.expression_settings {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#extensions_settings>.sd_settings {
|
||||
#extensions_settings>.background_settings {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
#extensions_settings>#tts_settings {
|
||||
#extensions_settings>.sd_settings {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
#extensions_settings>#rvc_settings {
|
||||
#extensions_settings>#tts_settings {
|
||||
order: 5;
|
||||
}
|
||||
|
||||
#extensions_settings>.objective-settings {
|
||||
#extensions_settings>#rvc_settings {
|
||||
order: 6;
|
||||
}
|
||||
|
||||
#extensions_settings>#speech_recognition_settings {
|
||||
#extensions_settings>.objective-settings {
|
||||
order: 7;
|
||||
}
|
||||
|
||||
#extensions_settings>#audio_settings {
|
||||
#extensions_settings>#speech_recognition_settings {
|
||||
order: 8;
|
||||
}
|
||||
|
||||
#extensions_settings>#assets_ui {
|
||||
#extensions_settings>#audio_settings {
|
||||
order: 9;
|
||||
}
|
||||
|
||||
|
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%;
|
||||
@@ -125,6 +138,20 @@
|
||||
|
||||
.wi-settings {
|
||||
flex-direction: column;
|
||||
gap: 5px !important;
|
||||
}
|
||||
|
||||
.WIEntryTitleAndStatus,
|
||||
.WIEntryHeaderControls {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#WIEntryHeaderTitlesPC {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.WIEntryHeaderTitleMobile {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
#character_popup,
|
||||
@@ -161,7 +188,8 @@
|
||||
|
||||
#showRawPrompt,
|
||||
#copyPromptToClipboard,
|
||||
#groupCurrentMemberPopoutButton {
|
||||
#groupCurrentMemberPopoutButton,
|
||||
#summaryExtensionPopoutButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -265,7 +293,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#bg_menu_content {
|
||||
.bg_list {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
@@ -311,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);
|
||||
@@ -418,4 +445,4 @@
|
||||
#horde_model {
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -78,6 +78,7 @@
|
||||
|
||||
#rm_group_members:empty {
|
||||
width: 100%;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
#rm_group_members:empty::before {
|
||||
@@ -226,4 +227,5 @@
|
||||
|
||||
.group_member .avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
flex-basis: auto;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -230,6 +301,10 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.padding0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.padding5 {
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -274,10 +349,6 @@
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.wide50p {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.wide25p {
|
||||
width: 25%;
|
||||
}
|
||||
@@ -361,11 +432,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;
|
||||
}
|
||||
@@ -386,6 +466,10 @@
|
||||
border: 1px solid purple !important;
|
||||
}
|
||||
|
||||
.fontsize120p {
|
||||
font-size: calc(var(--mainFontSize) * 1.2) !important;
|
||||
}
|
||||
|
||||
.fontsize80p {
|
||||
font-size: calc(var(--mainFontSize) * 0.8) !important;
|
||||
}
|
||||
@@ -394,6 +478,11 @@
|
||||
font-size: calc(var(--mainFontSize) * 0.6) !important;
|
||||
}
|
||||
|
||||
.paddingBottom5px {
|
||||
padding: unset;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.paddingTopBot5 {
|
||||
padding: 5px 0;
|
||||
}
|
||||
@@ -419,6 +508,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;
|
||||
}
|
||||
@@ -138,8 +141,7 @@
|
||||
filter: brightness(1);
|
||||
}
|
||||
|
||||
.tags_view,
|
||||
.open_alternate_greetings {
|
||||
.tags_view {
|
||||
margin: 0;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
@@ -171,4 +173,4 @@
|
||||
-1px 1px 0px black,
|
||||
1px -1px 0px black;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,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 +38,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 +47,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 +72,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,
|
||||
@@ -328,8 +333,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,4 +357,12 @@ body.expandMessageActions .mes .mes_buttons .extraMesButtons {
|
||||
|
||||
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;
|
||||
}
|
||||
|
657
public/i18n.json
657
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 |
3008
public/index.html
3008
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"
|
||||
}
|
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"
|
||||
}
|
2091
public/script.js
2091
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; position;
|
||||
identifier; role; content; name; system_prompt; position; injection_position; injection_depth;
|
||||
|
||||
/**
|
||||
* Create a new Prompt instance.
|
||||
@@ -72,14 +82,18 @@ class 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, position } = {}) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +289,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();
|
||||
|
||||
@@ -381,6 +395,9 @@ 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';
|
||||
}
|
||||
|
||||
// Append prompt to selected character
|
||||
@@ -673,6 +690,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1085,10 +1104,16 @@ 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';
|
||||
|
||||
const resetPromptButton = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_reset');
|
||||
if (true === prompt.system_prompt) {
|
||||
@@ -1098,10 +1123,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'
|
||||
@@ -1119,12 +1157,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>
|
||||
`;
|
||||
|
||||
@@ -1135,9 +1171,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));
|
||||
});
|
||||
}
|
||||
@@ -1152,10 +1190,16 @@ 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;
|
||||
injectionDepthField.value = DEFAULT_DEPTH;
|
||||
injectionDepthBlock.style.visibility = 'unset';
|
||||
|
||||
roleField.disabled = false;
|
||||
}
|
||||
@@ -1435,13 +1479,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, isUrlOrAPIKey, 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;
|
||||
}
|
||||
|
||||
@@ -234,7 +235,9 @@ export function RA_CountCharTokens() {
|
||||
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;
|
||||
@@ -264,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.
|
||||
@@ -368,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");
|
||||
@@ -394,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':
|
||||
@@ -415,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;
|
||||
}
|
||||
@@ -424,8 +431,8 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -889,13 +896,19 @@ export function initRossMods() {
|
||||
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 = chatBlock[0].scrollHeight - (chatBlock.outerHeight() + originalScrollBottom);
|
||||
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, #character_popup, #dialogue_popup, #WorldInfo").is(":visible")) {
|
||||
return
|
||||
}
|
||||
var SwipeButR = $('.swipe_right:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
@@ -905,6 +918,12 @@ export function initRossMods() {
|
||||
}
|
||||
});
|
||||
document.addEventListener('swiped-right', function (e) {
|
||||
if (power_user.gestures === false) {
|
||||
return
|
||||
}
|
||||
if ($(".mes_edit_buttons, #character_popup, #dialogue_popup, #WorldInfo").is(":visible")) {
|
||||
return
|
||||
}
|
||||
var SwipeButL = $('.swipe_left:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
|
@@ -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);
|
||||
}
|
@@ -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
|
||||
};
|
||||
}
|
73
public/scripts/chats.js
Normal file
73
public/scripts/chats.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// Move chat functions here from script.js (eventually)
|
||||
|
||||
import {
|
||||
chat,
|
||||
getCurrentChatId,
|
||||
hideSwipeButtons,
|
||||
saveChatConditional,
|
||||
showSwipeButtons,
|
||||
} from "../script.js";
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
await saveChatConditional();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
await saveChatConditional();
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
})
|
@@ -1,4 +1,5 @@
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, substituteParams, renderTemplate } 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";
|
||||
|
||||
@@ -123,6 +124,7 @@ const extension_settings = {
|
||||
apiUrl: defaultUrl,
|
||||
apiKey: '',
|
||||
autoConnect: false,
|
||||
notifyUpdates: false,
|
||||
disabledExtensions: [],
|
||||
expressionOverrides: [],
|
||||
memory: {},
|
||||
@@ -158,6 +160,9 @@ const extension_settings = {
|
||||
rvc: {},
|
||||
hypebot: {},
|
||||
vectors: {},
|
||||
variables: {
|
||||
global: {},
|
||||
},
|
||||
};
|
||||
|
||||
let modules = [];
|
||||
@@ -367,6 +372,15 @@ function addExtensionsButtonAndMenu() {
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -569,7 +583,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}
|
||||
`;
|
||||
}
|
||||
@@ -578,26 +592,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -609,6 +640,15 @@ async function showExtensionsDetails() {
|
||||
*/
|
||||
async function onUpdateClick() {
|
||||
const extensionName = $(this).data('name');
|
||||
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('/api/extensions/update', {
|
||||
method: 'POST',
|
||||
@@ -618,15 +658,20 @@ async function onUpdateClick() {
|
||||
|
||||
const data = await response.json();
|
||||
if (data.isUpToDate) {
|
||||
toastr.success('Extension is already up to date');
|
||||
if (!quiet) {
|
||||
toastr.success('Extension is already up to date');
|
||||
}
|
||||
} else {
|
||||
toastr.success(`Extension updated to ${data.shortCommitHash}`);
|
||||
toastr.success(`Extension ${extensionName} updated to ${data.shortCommitHash}`);
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
showExtensionsDetails();
|
||||
}
|
||||
showExtensionsDetails();
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the click event for the delete button of an extension.
|
||||
@@ -639,23 +684,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('/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();
|
||||
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.
|
||||
@@ -680,9 +728,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);
|
||||
}
|
||||
@@ -690,28 +771,117 @@ 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() {
|
||||
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})`);
|
||||
await updateExtension(id.replace('third-party', ''), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 () {
|
||||
@@ -721,8 +891,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,10 +49,20 @@ 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}`;
|
||||
@@ -59,7 +71,7 @@ function downloadAssetsList(url) {
|
||||
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,6 +204,13 @@ 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('/api/assets/download', {
|
||||
method: 'POST',
|
||||
@@ -170,6 +232,12 @@ 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('/api/assets/delete', {
|
||||
method: 'POST',
|
||||
@@ -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");
|
||||
|
@@ -13,18 +13,37 @@
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.assets-list-git {
|
||||
font-size: calc(var(--mainFontSize) * 0.8);
|
||||
opacity: 0.8;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.assets-list-div h3 {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.assets-list-div i a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.assets-list-div i {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
padding: 5px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.assets-list-div i span{
|
||||
.assets-list-div i span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.assets-list-div i span:first-of-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.asset-download-button {
|
||||
position: relative;
|
||||
width: 50px;
|
||||
@@ -33,8 +52,8 @@
|
||||
outline: none;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.asset-download-button:active {
|
||||
background: #007a63;
|
||||
}
|
||||
@@ -67,13 +86,11 @@
|
||||
}
|
||||
|
||||
@keyframes asset-download-button-loading-spinner {
|
||||
from {
|
||||
transform: rotate(0turn);
|
||||
}
|
||||
from {
|
||||
transform: rotate(0turn);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<div id="assets_ui">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Assets</b>
|
||||
<b>Download Extensions & Assets</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
|
@@ -1,917 +0,0 @@
|
||||
/*
|
||||
Ideas:
|
||||
- Clean design of new ui
|
||||
- change select text versus options for playing: audio
|
||||
- cross fading between bgm / start a different time
|
||||
- fading should appear before end when switching randomly
|
||||
- Background based ambient sounds
|
||||
- import option on background UI ?
|
||||
- Allow background music edition using background menu
|
||||
- https://fontawesome.com/icons/music?f=classic&s=solid
|
||||
- https://codepen.io/noirsociety/pen/rNQxQwm
|
||||
- https://codepen.io/xrocker/pen/abdKVGy
|
||||
*/
|
||||
|
||||
import { saveSettingsDebounced, getRequestHeaders } from "../../../script.js";
|
||||
import { getContext, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
|
||||
import { isDataURL } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const extensionName = "audio";
|
||||
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
||||
|
||||
const MODULE_NAME = 'Audio';
|
||||
const DEBUG_PREFIX = "<Audio module> ";
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
|
||||
const ASSETS_BGM_FOLDER = "bgm";
|
||||
const ASSETS_AMBIENT_FOLDER = "ambient";
|
||||
const CHARACTER_BGM_FOLDER = "bgm"
|
||||
|
||||
const FALLBACK_EXPRESSION = "neutral";
|
||||
const DEFAULT_EXPRESSIONS = [
|
||||
//"talkinghead",
|
||||
"admiration",
|
||||
"amusement",
|
||||
"anger",
|
||||
"annoyance",
|
||||
"approval",
|
||||
"caring",
|
||||
"confusion",
|
||||
"curiosity",
|
||||
"desire",
|
||||
"disappointment",
|
||||
"disapproval",
|
||||
"disgust",
|
||||
"embarrassment",
|
||||
"excitement",
|
||||
"fear",
|
||||
"gratitude",
|
||||
"grief",
|
||||
"joy",
|
||||
"love",
|
||||
"nervousness",
|
||||
"optimism",
|
||||
"pride",
|
||||
"realization",
|
||||
"relief",
|
||||
"remorse",
|
||||
"sadness",
|
||||
"surprise",
|
||||
"neutral"
|
||||
];
|
||||
const SPRITE_DOM_ID = "#expression-image";
|
||||
|
||||
let current_chat_id = null
|
||||
|
||||
let fallback_BGMS = null; // Initialized only once with module workers
|
||||
let ambients = null; // Initialized only once with module workers
|
||||
let characterMusics = {}; // Updated with module workers
|
||||
|
||||
let currentCharacterBGM = null;
|
||||
let currentExpressionBGM = null;
|
||||
let currentBackground = null;
|
||||
|
||||
let cooldownBGM = 0;
|
||||
|
||||
let bgmEnded = true;
|
||||
|
||||
//#############################//
|
||||
// Extension UI and Settings //
|
||||
//#############################//
|
||||
|
||||
const defaultSettings = {
|
||||
enabled: false,
|
||||
dynamic_bgm_enabled: false,
|
||||
//dynamic_ambient_enabled: false,
|
||||
|
||||
bgm_locked: true,
|
||||
bgm_muted: true,
|
||||
bgm_volume: 50,
|
||||
bgm_selected: null,
|
||||
|
||||
ambient_locked: true,
|
||||
ambient_muted: true,
|
||||
ambient_volume: 50,
|
||||
ambient_selected: null,
|
||||
|
||||
bgm_cooldown: 30
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
if (extension_settings.audio === undefined)
|
||||
extension_settings.audio = {};
|
||||
|
||||
if (Object.keys(extension_settings.audio).length === 0) {
|
||||
Object.assign(extension_settings.audio, defaultSettings)
|
||||
}
|
||||
$("#audio_enabled").prop('checked', extension_settings.audio.enabled);
|
||||
$("#audio_dynamic_bgm_enabled").prop('checked', extension_settings.audio.dynamic_bgm_enabled);
|
||||
//$("#audio_dynamic_ambient_enabled").prop('checked', extension_settings.audio.dynamic_ambient_enabled);
|
||||
|
||||
$("#audio_bgm_volume").text(extension_settings.audio.bgm_volume);
|
||||
$("#audio_ambient_volume").text(extension_settings.audio.ambient_volume);
|
||||
|
||||
$("#audio_bgm_volume_slider").val(extension_settings.audio.bgm_volume);
|
||||
$("#audio_ambient_volume_slider").val(extension_settings.audio.ambient_volume);
|
||||
|
||||
if (extension_settings.audio.bgm_muted) {
|
||||
$("#audio_bgm_mute_icon").removeClass("fa-volume-high");
|
||||
$("#audio_bgm_mute_icon").addClass("fa-volume-mute");
|
||||
$("#audio_bgm_mute").addClass("redOverlayGlow");
|
||||
$("#audio_bgm").prop("muted", true);
|
||||
}
|
||||
else {
|
||||
$("#audio_bgm_mute_icon").addClass("fa-volume-high");
|
||||
$("#audio_bgm_mute_icon").removeClass("fa-volume-mute");
|
||||
$("#audio_bgm_mute").removeClass("redOverlayGlow");
|
||||
$("#audio_bgm").prop("muted", false);
|
||||
}
|
||||
|
||||
if (extension_settings.audio.bgm_locked) {
|
||||
//$("#audio_bgm_lock_icon").removeClass("fa-lock-open");
|
||||
//$("#audio_bgm_lock_icon").addClass("fa-lock");
|
||||
$("#audio_bgm").attr("loop", true);
|
||||
$("#audio_bgm_lock").addClass("redOverlayGlow");
|
||||
}
|
||||
else {
|
||||
//$("#audio_bgm_lock_icon").removeClass("fa-lock");
|
||||
//$("#audio_bgm_lock_icon").addClass("fa-lock-open");
|
||||
$("#audio_bgm").attr("loop", false);
|
||||
$("#audio_bgm_lock").removeClass("redOverlayGlow");
|
||||
}
|
||||
|
||||
/*
|
||||
if (extension_settings.audio.bgm_selected !== null) {
|
||||
$("#audio_bgm_select").append(new Option(extension_settings.audio.bgm_selected, extension_settings.audio.bgm_selected));
|
||||
$("#audio_bgm_select").val(extension_settings.audio.bgm_selected);
|
||||
}*/
|
||||
|
||||
if (extension_settings.audio.ambient_locked) {
|
||||
$("#audio_ambient_lock_icon").removeClass("fa-lock-open");
|
||||
$("#audio_ambient_lock_icon").addClass("fa-lock");
|
||||
$("#audio_ambient_lock").addClass("redOverlayGlow");
|
||||
}
|
||||
else {
|
||||
$("#audio_ambient_lock_icon").removeClass("fa-lock");
|
||||
$("#audio_ambient_lock_icon").addClass("fa-lock-open");
|
||||
}
|
||||
|
||||
/*
|
||||
if (extension_settings.audio.ambient_selected !== null) {
|
||||
$("#audio_ambient_select").append(new Option(extension_settings.audio.ambient_selected, extension_settings.audio.ambient_selected));
|
||||
$("#audio_ambient_select").val(extension_settings.audio.ambient_selected);
|
||||
}*/
|
||||
|
||||
if (extension_settings.audio.ambient_muted) {
|
||||
$("#audio_ambient_mute_icon").removeClass("fa-volume-high");
|
||||
$("#audio_ambient_mute_icon").addClass("fa-volume-mute");
|
||||
$("#audio_ambient_mute").addClass("redOverlayGlow");
|
||||
$("#audio_ambient").prop("muted", true);
|
||||
}
|
||||
else {
|
||||
$("#audio_ambient_mute_icon").addClass("fa-volume-high");
|
||||
$("#audio_ambient_mute_icon").removeClass("fa-volume-mute");
|
||||
$("#audio_ambient_mute").removeClass("redOverlayGlow");
|
||||
$("#audio_ambient").prop("muted", false);
|
||||
}
|
||||
|
||||
$("#audio_bgm_cooldown").val(extension_settings.audio.bgm_cooldown);
|
||||
|
||||
$("#audio_debug_div").hide(); // DBG: comment to see debug mode
|
||||
}
|
||||
|
||||
async function onEnabledClick() {
|
||||
extension_settings.audio.enabled = $('#audio_enabled').is(':checked');
|
||||
if (extension_settings.audio.enabled) {
|
||||
if ($("#audio_bgm").attr("src") != "")
|
||||
$("#audio_bgm")[0].play();
|
||||
if ($("#audio_ambient").attr("src") != "")
|
||||
$("#audio_ambient")[0].play();
|
||||
} else {
|
||||
$("#audio_bgm")[0].pause();
|
||||
$("#audio_ambient")[0].pause();
|
||||
}
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onDynamicBGMEnabledClick() {
|
||||
extension_settings.audio.dynamic_bgm_enabled = $('#audio_dynamic_bgm_enabled').is(':checked');
|
||||
currentCharacterBGM = null;
|
||||
currentExpressionBGM = null;
|
||||
cooldownBGM = 0;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
/*
|
||||
async function onDynamicAmbientEnabledClick() {
|
||||
extension_settings.audio.dynamic_ambient_enabled = $('#audio_dynamic_ambient_enabled').is(':checked');
|
||||
currentBackground = null;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
*/
|
||||
async function onBGMLockClick() {
|
||||
extension_settings.audio.bgm_locked = !extension_settings.audio.bgm_locked;
|
||||
if (extension_settings.audio.bgm_locked) {
|
||||
extension_settings.audio.bgm_selected = $("#audio_bgm_select").val();
|
||||
$("#audio_bgm").attr("loop", true);
|
||||
}
|
||||
else {
|
||||
$("#audio_bgm").attr("loop", false);
|
||||
}
|
||||
//$("#audio_bgm_lock_icon").toggleClass("fa-lock");
|
||||
//$("#audio_bgm_lock_icon").toggleClass("fa-lock-open");
|
||||
$("#audio_bgm_lock").toggleClass("redOverlayGlow");
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onBGMRandomClick() {
|
||||
var select = document.getElementById('audio_bgm_select');
|
||||
var items = select.getElementsByTagName('option');
|
||||
|
||||
if (items.length < 2)
|
||||
return;
|
||||
|
||||
var index;
|
||||
do {
|
||||
index = Math.floor(Math.random() * items.length);
|
||||
} while (index == select.selectedIndex);
|
||||
|
||||
select.selectedIndex = index;
|
||||
onBGMSelectChange();
|
||||
}
|
||||
|
||||
async function onBGMMuteClick() {
|
||||
extension_settings.audio.bgm_muted = !extension_settings.audio.bgm_muted;
|
||||
$("#audio_bgm_mute_icon").toggleClass("fa-volume-high");
|
||||
$("#audio_bgm_mute_icon").toggleClass("fa-volume-mute");
|
||||
$("#audio_bgm").prop("muted", !$("#audio_bgm").prop("muted"));
|
||||
$("#audio_bgm_mute").toggleClass("redOverlayGlow");
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onAmbientLockClick() {
|
||||
extension_settings.audio.ambient_locked = !extension_settings.audio.ambient_locked;
|
||||
if (extension_settings.audio.ambient_locked)
|
||||
extension_settings.audio.ambient_selected = $("#audio_ambient_select").val();
|
||||
else {
|
||||
extension_settings.audio.ambient_selected = null;
|
||||
currentBackground = null;
|
||||
}
|
||||
$("#audio_ambient_lock_icon").toggleClass("fa-lock");
|
||||
$("#audio_ambient_lock_icon").toggleClass("fa-lock-open");
|
||||
$("#audio_ambient_lock").toggleClass("redOverlayGlow");
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onAmbientMuteClick() {
|
||||
extension_settings.audio.ambient_muted = !extension_settings.audio.ambient_muted;
|
||||
$("#audio_ambient_mute_icon").toggleClass("fa-volume-high");
|
||||
$("#audio_ambient_mute_icon").toggleClass("fa-volume-mute");
|
||||
$("#audio_ambient").prop("muted", !$("#audio_ambient").prop("muted"));
|
||||
$("#audio_ambient_mute").toggleClass("redOverlayGlow");
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onBGMVolumeChange() {
|
||||
extension_settings.audio.bgm_volume = ~~($("#audio_bgm_volume_slider").val());
|
||||
$("#audio_bgm").prop("volume", extension_settings.audio.bgm_volume * 0.01);
|
||||
$("#audio_bgm_volume").text(extension_settings.audio.bgm_volume);
|
||||
saveSettingsDebounced();
|
||||
//console.debug(DEBUG_PREFIX,"UPDATED BGM MAX TO",extension_settings.audio.bgm_volume);
|
||||
}
|
||||
|
||||
async function onAmbientVolumeChange() {
|
||||
extension_settings.audio.ambient_volume = ~~($("#audio_ambient_volume_slider").val());
|
||||
$("#audio_ambient").prop("volume", extension_settings.audio.ambient_volume * 0.01);
|
||||
$("#audio_ambient_volume").text(extension_settings.audio.ambient_volume);
|
||||
saveSettingsDebounced();
|
||||
//console.debug(DEBUG_PREFIX,"UPDATED Ambient MAX TO",extension_settings.audio.ambient_volume);
|
||||
}
|
||||
|
||||
async function onBGMSelectChange() {
|
||||
extension_settings.audio.bgm_selected = $("#audio_bgm_select").val();
|
||||
updateBGM(true);
|
||||
saveSettingsDebounced();
|
||||
//console.debug(DEBUG_PREFIX,"UPDATED BGM MAX TO",extension_settings.audio.bgm_volume);
|
||||
}
|
||||
|
||||
async function onAmbientSelectChange() {
|
||||
extension_settings.audio.ambient_selected = $("#audio_ambient_select").val();
|
||||
updateAmbient(true);
|
||||
saveSettingsDebounced();
|
||||
//console.debug(DEBUG_PREFIX,"UPDATED BGM MAX TO",extension_settings.audio.bgm_volume);
|
||||
}
|
||||
|
||||
async function onBGMCooldownInput() {
|
||||
extension_settings.audio.bgm_cooldown = ~~($("#audio_bgm_cooldown").val());
|
||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||
saveSettingsDebounced();
|
||||
console.debug(DEBUG_PREFIX, "UPDATED BGM cooldown to", extension_settings.audio.bgm_cooldown);
|
||||
}
|
||||
|
||||
//#############################//
|
||||
// API Calls //
|
||||
//#############################//
|
||||
|
||||
async function getAssetsList(type) {
|
||||
console.debug(DEBUG_PREFIX, "getting assets of type", type);
|
||||
|
||||
try {
|
||||
const result = await fetch(`/api/assets/get`, {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
const assets = result.ok ? (await result.json()) : { type: [] };
|
||||
console.debug(DEBUG_PREFIX, "Found assets:", assets);
|
||||
return assets[type];
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getCharacterBgmList(name) {
|
||||
console.debug(DEBUG_PREFIX, "getting bgm list for", name);
|
||||
|
||||
try {
|
||||
const result = await fetch(`/api/assets/character?name=${encodeURIComponent(name)}&category=${CHARACTER_BGM_FOLDER}`, {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
let musics = result.ok ? (await result.json()) : [];
|
||||
return musics;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
//#############################//
|
||||
// Module Worker //
|
||||
//#############################//
|
||||
|
||||
function fillBGMSelect() {
|
||||
let found_last_selected_bgm = false;
|
||||
// Update bgm list in UI
|
||||
$("#audio_bgm_select")
|
||||
.find('option')
|
||||
.remove();
|
||||
|
||||
for (const file of fallback_BGMS) {
|
||||
$('#audio_bgm_select').append(new Option("asset: " + file.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, ""), file));
|
||||
if (file === extension_settings.audio.bgm_selected) {
|
||||
$('#audio_bgm_select').val(extension_settings.audio.bgm_selected);
|
||||
found_last_selected_bgm = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update bgm list in UI
|
||||
for (const char in characterMusics)
|
||||
for (const e in characterMusics[char])
|
||||
for (const file of characterMusics[char][e]) {
|
||||
$('#audio_bgm_select').append(new Option(char + ": " + file.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, ""), file));
|
||||
if (file === extension_settings.audio.bgm_selected) {
|
||||
$('#audio_bgm_select').val(extension_settings.audio.bgm_selected);
|
||||
found_last_selected_bgm = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_last_selected_bgm) {
|
||||
$('#audio_bgm_select').val($("#audio_bgm_select option:first").val());
|
||||
extension_settings.audio.bgm_selected = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- Update ambient sound
|
||||
- Update character BGM
|
||||
- Solo dynamique expression
|
||||
- Group only neutral bgm
|
||||
*/
|
||||
async function moduleWorker() {
|
||||
const moduleEnabled = extension_settings.audio.enabled;
|
||||
|
||||
if (moduleEnabled) {
|
||||
|
||||
if (cooldownBGM > 0)
|
||||
cooldownBGM -= UPDATE_INTERVAL;
|
||||
|
||||
if (fallback_BGMS == null) {
|
||||
console.debug(DEBUG_PREFIX, "Updating audio bgm assets...");
|
||||
fallback_BGMS = await getAssetsList(ASSETS_BGM_FOLDER);
|
||||
fallback_BGMS = fallback_BGMS.filter((filename) => filename != ".placeholder")
|
||||
console.debug(DEBUG_PREFIX, "Detected assets:", fallback_BGMS);
|
||||
|
||||
fillBGMSelect();
|
||||
}
|
||||
|
||||
if (ambients == null) {
|
||||
console.debug(DEBUG_PREFIX, "Updating audio ambient assets...");
|
||||
ambients = await getAssetsList(ASSETS_AMBIENT_FOLDER);
|
||||
ambients = ambients.filter((filename) => filename != ".placeholder")
|
||||
console.debug(DEBUG_PREFIX, "Detected assets:", ambients);
|
||||
|
||||
// Update bgm list in UI
|
||||
$("#audio_ambient_select")
|
||||
.find('option')
|
||||
.remove();
|
||||
|
||||
if (extension_settings.audio.ambient_selected !== null) {
|
||||
let ambient_label = extension_settings.audio.ambient_selected;
|
||||
if (ambient_label.includes("assets"))
|
||||
ambient_label = "asset: " + ambient_label.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "");
|
||||
else {
|
||||
ambient_label = ambient_label.substring("/characters/".length);
|
||||
ambient_label = ambient_label.substring(0, ambient_label.indexOf("/")) + ": " + ambient_label.substring(ambient_label.indexOf("/") + "/bgm/".length);
|
||||
ambient_label = ambient_label.replace(/\.[^/.]+$/, "");
|
||||
}
|
||||
$('#audio_ambient_select').append(new Option(ambient_label, extension_settings.audio.ambient_selected));
|
||||
}
|
||||
|
||||
for (const file of ambients) {
|
||||
if (file !== extension_settings.audio.ambient_selected)
|
||||
$("#audio_ambient_select").append(new Option("asset: " + file.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, ""), file));
|
||||
}
|
||||
}
|
||||
|
||||
// 1) Update ambient audio
|
||||
// ---------------------------
|
||||
//if (extension_settings.audio.dynamic_ambient_enabled) {
|
||||
let newBackground = $("#bg1").css("background-image");
|
||||
const custom_background = getContext()["chatMetadata"]["custom_background"];
|
||||
|
||||
if (custom_background !== undefined)
|
||||
newBackground = custom_background
|
||||
|
||||
if (!isDataURL(newBackground)) {
|
||||
newBackground = newBackground.substring(newBackground.lastIndexOf("/") + 1).replace(/\.[^/.]+$/, "").replaceAll("%20", "-").replaceAll(" ", "-"); // remove path and spaces
|
||||
|
||||
//console.debug(DEBUG_PREFIX,"Current backgroung:",newBackground);
|
||||
|
||||
if (currentBackground !== newBackground) {
|
||||
currentBackground = newBackground;
|
||||
|
||||
console.debug(DEBUG_PREFIX, "Changing ambient audio for", currentBackground);
|
||||
updateAmbient();
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
const context = getContext();
|
||||
//console.debug(DEBUG_PREFIX,context);
|
||||
|
||||
if (context.chat.length == 0)
|
||||
return;
|
||||
|
||||
let chatIsGroup = context.chat[0].is_group;
|
||||
let newCharacter = null;
|
||||
|
||||
// 1) Update BGM (single chat)
|
||||
// -----------------------------
|
||||
if (!chatIsGroup) {
|
||||
|
||||
// Reset bgm list on new chat
|
||||
if (context.chatId != current_chat_id) {
|
||||
current_chat_id = context.chatId;
|
||||
characterMusics = {};
|
||||
cooldownBGM = 0;
|
||||
}
|
||||
|
||||
newCharacter = context.name2;
|
||||
|
||||
//console.log(DEBUG_PREFIX,"SOLO CHAT MODE"); // DBG
|
||||
|
||||
// 1.1) First time loading chat
|
||||
if (characterMusics[newCharacter] === undefined) {
|
||||
await loadCharacterBGM(newCharacter);
|
||||
currentExpressionBGM = FALLBACK_EXPRESSION;
|
||||
//currentCharacterBGM = newCharacter;
|
||||
|
||||
//updateBGM();
|
||||
//cooldownBGM = BGM_UPDATE_COOLDOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1.2) Switched chat
|
||||
if (currentCharacterBGM !== newCharacter) {
|
||||
currentCharacterBGM = newCharacter;
|
||||
try {
|
||||
await updateBGM(false, true);
|
||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(DEBUG_PREFIX, "Error while trying to update BGM character, will try again");
|
||||
currentCharacterBGM = null
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const newExpression = getNewExpression();
|
||||
|
||||
// 1.3) Same character but different expression
|
||||
if (currentExpressionBGM !== newExpression) {
|
||||
|
||||
// Check cooldown
|
||||
if (cooldownBGM > 0) {
|
||||
//console.debug(DEBUG_PREFIX,"(SOLO) BGM switch on cooldown:",cooldownBGM);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentExpressionBGM = newExpression;
|
||||
await updateBGM();
|
||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||
console.debug(DEBUG_PREFIX, "(SOLO) Updated current character expression to", currentExpressionBGM, "cooldown", cooldownBGM);
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(DEBUG_PREFIX, "Error while trying to update BGM expression, will try again");
|
||||
currentCharacterBGM = null
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) Update BGM (group chat)
|
||||
// -----------------------------
|
||||
|
||||
// Load current chat character bgms
|
||||
// Reset bgm list on new chat
|
||||
if (context.chatId != current_chat_id) {
|
||||
current_chat_id = context.chatId;
|
||||
characterMusics = {};
|
||||
cooldownBGM = 0;
|
||||
for (const message of context.chat) {
|
||||
if (characterMusics[message.name] === undefined)
|
||||
await loadCharacterBGM(message.name);
|
||||
}
|
||||
|
||||
try {
|
||||
newCharacter = context.chat[context.chat.length - 1].name;
|
||||
currentCharacterBGM = newCharacter;
|
||||
await updateBGM(false, true);
|
||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||
currentCharacterBGM = newCharacter;
|
||||
currentExpressionBGM = FALLBACK_EXPRESSION;
|
||||
console.debug(DEBUG_PREFIX, "(GROUP) Updated current character BGM to", currentExpressionBGM, "cooldown", cooldownBGM);
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(DEBUG_PREFIX, "Error while trying to update BGM group, will try again");
|
||||
currentCharacterBGM = null
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
newCharacter = context.chat[context.chat.length - 1].name;
|
||||
const userName = context.name1;
|
||||
|
||||
if (newCharacter !== undefined && newCharacter != userName) {
|
||||
|
||||
//console.log(DEBUG_PREFIX,"GROUP CHAT MODE"); // DBG
|
||||
|
||||
// 2.1) New character appear
|
||||
if (characterMusics[newCharacter] === undefined) {
|
||||
await loadCharacterBGM(newCharacter);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.2) Switched char
|
||||
if (currentCharacterBGM !== newCharacter) {
|
||||
// Check cooldown
|
||||
if (cooldownBGM > 0) {
|
||||
console.debug(DEBUG_PREFIX, "(GROUP) BGM switch on cooldown:", cooldownBGM);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentCharacterBGM = newCharacter;
|
||||
await updateBGM();
|
||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||
currentCharacterBGM = newCharacter;
|
||||
currentExpressionBGM = FALLBACK_EXPRESSION;
|
||||
console.debug(DEBUG_PREFIX, "(GROUP) Updated current character BGM to", currentExpressionBGM, "cooldown", cooldownBGM);
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(DEBUG_PREFIX, "Error while trying to update BGM group, will try again");
|
||||
currentCharacterBGM = null
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
const newExpression = getNewExpression();
|
||||
|
||||
// 1.3) Same character but different expression
|
||||
if (currentExpressionBGM !== newExpression) {
|
||||
|
||||
// Check cooldown
|
||||
if (cooldownBGM > 0) {
|
||||
console.debug(DEBUG_PREFIX,"BGM switch on cooldown:",cooldownBGM);
|
||||
return;
|
||||
}
|
||||
|
||||
cooldownBGM = BGM_UPDATE_COOLDOWN;
|
||||
currentExpressionBGM = newExpression;
|
||||
console.debug(DEBUG_PREFIX,"Updated current character expression to",currentExpressionBGM);
|
||||
updateBGM();
|
||||
return;
|
||||
}
|
||||
|
||||
return;*/
|
||||
|
||||
}
|
||||
|
||||
// Case 3: Same character/expression or BGM switch on cooldown keep playing same BGM
|
||||
//console.debug(DEBUG_PREFIX,"Nothing to do for",currentCharacterBGM, newCharacter, currentExpressionBGM, cooldownBGM);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCharacterBGM(newCharacter) {
|
||||
console.debug(DEBUG_PREFIX, "New character detected, loading BGM folder of", newCharacter);
|
||||
|
||||
// 1.1) First time character appear, load its music folder
|
||||
const audio_file_paths = await getCharacterBgmList(newCharacter);
|
||||
//console.debug(DEBUG_PREFIX, "Recieved", audio_file_paths);
|
||||
|
||||
// Initialise expression/files mapping
|
||||
characterMusics[newCharacter] = {};
|
||||
for (const e of DEFAULT_EXPRESSIONS)
|
||||
characterMusics[newCharacter][e] = [];
|
||||
|
||||
for (const i of audio_file_paths) {
|
||||
//console.debug(DEBUG_PREFIX,"File found:",i);
|
||||
for (const e of DEFAULT_EXPRESSIONS)
|
||||
if (i.includes(e))
|
||||
characterMusics[newCharacter][e].push(i);
|
||||
}
|
||||
console.debug(DEBUG_PREFIX, "Updated BGM map of", newCharacter, "to", characterMusics[newCharacter]);
|
||||
|
||||
fillBGMSelect();
|
||||
}
|
||||
|
||||
function getNewExpression() {
|
||||
let newExpression;
|
||||
|
||||
// HACK: use sprite file name as expression detection
|
||||
if (!$(SPRITE_DOM_ID).length) {
|
||||
console.error(DEBUG_PREFIX, "ERROR: expression sprite does not exist, cannot extract expression from ", SPRITE_DOM_ID)
|
||||
return FALLBACK_EXPRESSION;
|
||||
}
|
||||
|
||||
const spriteFile = $("#expression-image").attr("src");
|
||||
newExpression = spriteFile.substring(spriteFile.lastIndexOf("/") + 1).replace(/\.[^/.]+$/, "");
|
||||
//
|
||||
|
||||
// No sprite to detect expression
|
||||
if (newExpression == "") {
|
||||
//console.info(DEBUG_PREFIX,"Warning: no expression extracted from sprite, switch to",FALLBACK_EXPRESSION);
|
||||
newExpression = FALLBACK_EXPRESSION;
|
||||
}
|
||||
|
||||
if (!DEFAULT_EXPRESSIONS.includes(newExpression)) {
|
||||
console.info(DEBUG_PREFIX, "Warning:", newExpression, " is not a handled expression, expected one of", FALLBACK_EXPRESSION);
|
||||
return FALLBACK_EXPRESSION;
|
||||
}
|
||||
|
||||
return newExpression;
|
||||
}
|
||||
|
||||
async function updateBGM(isUserInput = false, newChat = false) {
|
||||
if (!isUserInput && !extension_settings.audio.dynamic_bgm_enabled && $("#audio_bgm").attr("src") != "" && !bgmEnded && !newChat) {
|
||||
console.debug(DEBUG_PREFIX, "BGM already playing and dynamic switch disabled, no update done");
|
||||
return;
|
||||
}
|
||||
|
||||
let audio_file_path = ""
|
||||
if (isUserInput || (extension_settings.audio.bgm_locked && extension_settings.audio.bgm_selected !== null)) {
|
||||
audio_file_path = extension_settings.audio.bgm_selected;
|
||||
|
||||
if (isUserInput)
|
||||
console.debug(DEBUG_PREFIX, "User selected BGM", audio_file_path);
|
||||
if (extension_settings.audio.bgm_locked)
|
||||
console.debug(DEBUG_PREFIX, "BGM locked keeping current audio", audio_file_path);
|
||||
}
|
||||
else {
|
||||
|
||||
let audio_files = null;
|
||||
|
||||
if (extension_settings.audio.dynamic_bgm_enabled) {
|
||||
extension_settings.audio.bgm_selected = null;
|
||||
saveSettingsDebounced();
|
||||
audio_files = characterMusics[currentCharacterBGM][currentExpressionBGM];// Try char expression BGM
|
||||
|
||||
if (audio_files === undefined || audio_files.length == 0) {
|
||||
console.debug(DEBUG_PREFIX, "No BGM for", currentCharacterBGM, currentExpressionBGM);
|
||||
audio_files = characterMusics[currentCharacterBGM][FALLBACK_EXPRESSION]; // Try char FALLBACK BGM
|
||||
if (audio_files === undefined || audio_files.length == 0) {
|
||||
console.debug(DEBUG_PREFIX, "No default BGM for", currentCharacterBGM, FALLBACK_EXPRESSION, "switch to ST BGM");
|
||||
audio_files = fallback_BGMS; // ST FALLBACK BGM
|
||||
|
||||
if (audio_files.length == 0) {
|
||||
console.debug(DEBUG_PREFIX, "No default BGM file found, bgm folder may be empty.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
audio_files = [];
|
||||
$("#audio_bgm_select option").each(function () { audio_files.push($(this).val()); });
|
||||
}
|
||||
|
||||
audio_file_path = audio_files[Math.floor(Math.random() * audio_files.length)];
|
||||
}
|
||||
|
||||
console.log(DEBUG_PREFIX, "Updating BGM");
|
||||
console.log(DEBUG_PREFIX, "Checking file", audio_file_path);
|
||||
try {
|
||||
const response = await fetch(audio_file_path);
|
||||
|
||||
if (!response.ok) {
|
||||
console.log(DEBUG_PREFIX, "File not found!")
|
||||
}
|
||||
else {
|
||||
console.log(DEBUG_PREFIX, "Switching BGM to", currentExpressionBGM);
|
||||
$("#audio_bgm_select").val(audio_file_path);
|
||||
const audio = $("#audio_bgm");
|
||||
|
||||
if (audio.attr("src") == audio_file_path && !bgmEnded) {
|
||||
console.log(DEBUG_PREFIX, "Already playing, ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
let fade_time = 2000;
|
||||
bgmEnded = false;
|
||||
|
||||
if (isUserInput || extension_settings.audio.bgm_locked) {
|
||||
audio.attr("src", audio_file_path);
|
||||
audio[0].play();
|
||||
}
|
||||
else {
|
||||
audio.animate({ volume: 0.0 }, fade_time, function () {
|
||||
audio.attr("src", audio_file_path);
|
||||
audio[0].play();
|
||||
audio.volume = extension_settings.audio.bgm_volume * 0.01;
|
||||
audio.animate({ volume: extension_settings.audio.bgm_volume * 0.01 }, fade_time);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(DEBUG_PREFIX, "Error while trying to fetch", audio_file_path, ":", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAmbient(isUserInput = false) {
|
||||
let audio_file_path = null;
|
||||
|
||||
if (isUserInput || extension_settings.audio.ambient_locked) {
|
||||
audio_file_path = extension_settings.audio.ambient_selected;
|
||||
|
||||
if (isUserInput)
|
||||
console.debug(DEBUG_PREFIX, "User selected Ambient", audio_file_path);
|
||||
if (extension_settings.audio.bgm_locked)
|
||||
console.debug(DEBUG_PREFIX, "Ambient locked keeping current audio", audio_file_path);
|
||||
}
|
||||
else {
|
||||
extension_settings.audio.ambient_selected = null;
|
||||
for (const i of ambients) {
|
||||
console.debug(i)
|
||||
if (i.includes(currentBackground)) {
|
||||
audio_file_path = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_file_path === null) {
|
||||
console.debug(DEBUG_PREFIX, "No bgm file found for background", currentBackground);
|
||||
const audio = $("#audio_ambient");
|
||||
audio.attr("src", "");
|
||||
audio[0].pause();
|
||||
return;
|
||||
}
|
||||
|
||||
//const audio_file_path = AMBIENT_FOLDER+currentBackground+".mp3";
|
||||
console.log(DEBUG_PREFIX, "Updating ambient");
|
||||
console.log(DEBUG_PREFIX, "Checking file", audio_file_path);
|
||||
$("#audio_ambient_select").val(audio_file_path);
|
||||
|
||||
let fade_time = 2000;
|
||||
if (isUserInput)
|
||||
fade_time = 0;
|
||||
|
||||
const audio = $("#audio_ambient");
|
||||
|
||||
if (audio.attr("src") == audio_file_path) {
|
||||
console.log(DEBUG_PREFIX, "Already playing, ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
audio.animate({ volume: 0.0 }, fade_time, function () {
|
||||
audio.attr("src", audio_file_path);
|
||||
audio[0].play();
|
||||
audio.volume = extension_settings.audio.ambient_volume * 0.01;
|
||||
audio.animate({ volume: extension_settings.audio.ambient_volume * 0.01 }, fade_time);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles wheel events on volume sliders.
|
||||
* @param {WheelEvent} e Event
|
||||
*/
|
||||
function onVolumeSliderWheelEvent(e) {
|
||||
const slider = $(this);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const delta = e.deltaY / 20;
|
||||
const sliderVal = Number(slider.val());
|
||||
|
||||
let newVal = sliderVal - delta;
|
||||
if (newVal < 0) {
|
||||
newVal = 0;
|
||||
} else if (newVal > 100) {
|
||||
newVal = 100;
|
||||
}
|
||||
|
||||
slider.val(newVal).trigger('input');
|
||||
}
|
||||
|
||||
//#############################//
|
||||
// Extension load //
|
||||
//#############################//
|
||||
|
||||
// This function is called when the extension is loaded
|
||||
jQuery(async () => {
|
||||
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
||||
|
||||
$('#extensions_settings').append(windowHtml);
|
||||
loadSettings();
|
||||
|
||||
$("#audio_enabled").on("click", onEnabledClick);
|
||||
$("#audio_dynamic_bgm_enabled").on("click", onDynamicBGMEnabledClick);
|
||||
//$("#audio_dynamic_ambient_enabled").on("click", onDynamicAmbientEnabledClick);
|
||||
|
||||
//$("#audio_bgm").attr("loop", false);
|
||||
$("#audio_ambient").attr("loop", true);
|
||||
|
||||
$("#audio_bgm").hide();
|
||||
$("#audio_bgm_lock").on("click", onBGMLockClick);
|
||||
$("#audio_bgm_mute").on("click", onBGMMuteClick);
|
||||
$("#audio_bgm_volume_slider").on("input", onBGMVolumeChange);
|
||||
$("#audio_bgm_random").on("click", onBGMRandomClick);
|
||||
|
||||
$("#audio_ambient").hide();
|
||||
$("#audio_ambient_lock").on("click", onAmbientLockClick);
|
||||
$("#audio_ambient_mute").on("click", onAmbientMuteClick);
|
||||
$("#audio_ambient_volume_slider").on("input", onAmbientVolumeChange);
|
||||
|
||||
document.getElementById('audio_ambient_volume_slider').addEventListener('wheel', onVolumeSliderWheelEvent, { passive: false });
|
||||
document.getElementById('audio_bgm_volume_slider').addEventListener('wheel', onVolumeSliderWheelEvent, { passive: false });
|
||||
|
||||
$("#audio_bgm_cooldown").on("input", onBGMCooldownInput);
|
||||
|
||||
// Reset assets container, will be redected like if ST restarted
|
||||
$("#audio_refresh_assets").on("click", function () {
|
||||
console.debug(DEBUG_PREFIX, "Refreshing audio assets");
|
||||
current_chat_id = null
|
||||
fallback_BGMS = null;
|
||||
ambients = null;
|
||||
characterMusics = {};
|
||||
currentCharacterBGM = null;
|
||||
currentExpressionBGM = null;
|
||||
currentBackground = null;
|
||||
})
|
||||
|
||||
$("#audio_bgm_select").on("change", onBGMSelectChange);
|
||||
$("#audio_ambient_select").on("change", onAmbientSelectChange);
|
||||
|
||||
// DBG
|
||||
$("#audio_debug").on("click", function () {
|
||||
if ($("#audio_debug").is(':checked')) {
|
||||
$("#audio_bgm").show();
|
||||
$("#audio_ambient").show();
|
||||
}
|
||||
else {
|
||||
$("#audio_bgm").hide();
|
||||
$("#audio_ambient").hide();
|
||||
}
|
||||
});
|
||||
//
|
||||
|
||||
$("#audio_bgm").on("ended", function () {
|
||||
console.debug(DEBUG_PREFIX, "END OF BGM")
|
||||
if (!extension_settings.audio.bgm_locked) {
|
||||
bgmEnded = true;
|
||||
updateBGM();
|
||||
}
|
||||
});
|
||||
|
||||
const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
|
||||
moduleWorker();
|
||||
});
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"display_name": "Dynamic Audio",
|
||||
"loading_order": 14,
|
||||
"requires": [],
|
||||
"optional": ["classify"],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Keij#6799 and Deffcolony",
|
||||
"version": "0.1.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
.audio-ui-block {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.audio-mixer-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 5px;
|
||||
background-color: rgba(38, 38, 38, 0.5);
|
||||
border: 1px rgb(75, 75, 75) solid;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.audio-mixer-element {
|
||||
height: 60px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
label {
|
||||
text-align: top;
|
||||
}
|
||||
input {
|
||||
margin-top: 15px;
|
||||
}
|
||||
select {
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-label {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.audio-volume-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.audio-lock-button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.audio-random-button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.audio-mute-button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.audio-slider {
|
||||
width: 100% !important;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.audio-mute-button-muted {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#audio_refresh_assets {
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.audio-mixer-mute {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.audio-mixer-volume {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.audio-mixer-playlist {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.audio-mixer-lock {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.audio-mixer-random {
|
||||
width: 10%;
|
||||
}
|
@@ -1,120 +0,0 @@
|
||||
<div id="audio_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Dynamic Audio</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div>
|
||||
<label class="checkbox_label" for="audio_enabled">
|
||||
<input type="checkbox" id="audio_enabled" name="audio_enabled">
|
||||
<small>Enabled</small>
|
||||
</label>
|
||||
<div id="audio_bgm_dynamic_enable_div">
|
||||
<label class="checkbox_label" for="audio_dynamic_bgm_enabled">
|
||||
<input type="checkbox" id="audio_dynamic_bgm_enabled" name="audio_dynamic_bgm_enabled">
|
||||
<small>Enable expression BGM switch (req. character expression)</small>
|
||||
</label>
|
||||
</div>
|
||||
<!--
|
||||
<div id="audio_ambient_dynamic_enable_div">
|
||||
<label class="checkbox_label" for="audio_dynamic_ambient_enabled">
|
||||
<input type="checkbox" id="audio_dynamic_ambient_enabled" name="audio_dynamic_ambient_enabled">
|
||||
<small>Enable ambient background switch (req. chat background)</small>
|
||||
</label>
|
||||
</div>
|
||||
-->
|
||||
<div id="audio_debug_div">
|
||||
<label class="checkbox_label" for="audio_debug">
|
||||
<input type="checkbox" id="audio_debug" name="audio_debug">
|
||||
<small>Debug</small>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="audio_refresh_assets">Refresh assets</label>
|
||||
<div id="audio_refresh_assets" class="menu_button">
|
||||
<i class="fa-solid fa-refresh fa-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="audio-ui-block">
|
||||
<label for="audio_bgm_volume_slider">Music</label>
|
||||
<div class="audio-mixer-div">
|
||||
<div class="audio-mixer-element audio-mixer-mute">
|
||||
<label for="audio_bgm_lock" class="audio-label">Mute</label>
|
||||
<div id="audio_bgm_mute" class="menu_button audio-mute-button">
|
||||
<i class="fa-solid fa-volume-high fa-lg" id="audio_bgm_mute_icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-volume">
|
||||
<label for="audio_bgm_lock" class="audio-label">Vol <span id="audio_bgm_volume"></span></label>
|
||||
<input type="range" class ="audio-slider" id ="audio_bgm_volume_slider" value = "0" maxlength ="100">
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-playlist">
|
||||
<label for="audio_bgm_lock" class="audio-label">Playlist</label>
|
||||
<select id="audio_bgm_select">
|
||||
</select>
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-lock">
|
||||
<label for="audio_bgm_lock" class="audio-label">Loop</label>
|
||||
<div id="audio_bgm_lock" class="menu_button audio-lock-button">
|
||||
<i class="fa-solid fa-repeat fa-lg" id="audio_bgm_lock_icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-random">
|
||||
<label for="audio_bgm_random" class="audio-label">Roll</label>
|
||||
<div id="audio_bgm_random" class="menu_button audio-random-button">
|
||||
<i class="fa-solid fa-random fa-lg" id="audio_bgm_random_icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio id="audio_bgm" controls src="">
|
||||
</div>
|
||||
<div>
|
||||
<label for="audio_ambient_volume_slider">Ambient</label>
|
||||
<div class="audio-mixer-div">
|
||||
<div class="audio-mixer-element audio-mixer-mute">
|
||||
<label for="audio_ambient_lock" class="audio-label">Mute</label>
|
||||
<div id="audio_ambient_mute" class="menu_button audio-mute-button">
|
||||
<i class="fa-solid fa-volume-high fa-lg" id="audio_ambient_mute_icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-volume">
|
||||
<label for="audio_ambient_lock" class="audio-label">Vol <span id="audio_ambient_volume"></span></label>
|
||||
<input type="range" class ="audio-slider" id ="audio_ambient_volume_slider" value = "0" maxlength ="100">
|
||||
</div>
|
||||
<div class="audio-mixer-element audio-mixer-playlist">
|
||||
<label for="audio_ambient_lock" class="audio-label">Playlist</label>
|
||||
<select id="audio_ambient_select">
|
||||
</select>
|
||||
</div>
|
||||
<div class="audio-mixer-element">
|
||||
<label for="audio_ambient_lock audio-mixer-lock" class="audio-label">Lock</label>
|
||||
<div id="audio_ambient_lock" class="menu_button audio-lock-button">
|
||||
<i class="fa-solid fa-lock-open fa-lg" id="audio_ambient_lock_icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio id="audio_ambient" controls src="">
|
||||
</div>
|
||||
<div>
|
||||
<label for="audio_bgm_cooldown">Music update cooldown (in seconds)</label>
|
||||
<input id="audio_bgm_cooldown" class="text_pole wide30p">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<b>Hint:</b>
|
||||
<i>
|
||||
Create new folder in the
|
||||
<b>public/characters/</b>
|
||||
folder and name it as the name of the character.
|
||||
Create a folder name <b>bgm</b> inside of it.
|
||||
Put bgm music with expressions there. File names should follow the pattern:
|
||||
<it>[expression_label]_[number].mp3</it>
|
||||
By default one of the <it>neutral_[number].mp3</it> will play if classify module is not active.
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,178 +0,0 @@
|
||||
import { eventSource, event_types, generateQuietPrompt } from "../../../script.js";
|
||||
import { getContext, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { stringFormat } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'backgrounds';
|
||||
const METADATA_KEY = 'custom_background';
|
||||
|
||||
/**
|
||||
* @param {string} background
|
||||
*/
|
||||
function forceSetBackground(background) {
|
||||
saveBackgroundMetadata(background);
|
||||
setCustomBackground();
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
if (hasCustomBackground()) {
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
setCustomBackground();
|
||||
}
|
||||
else {
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
unsetCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
function onLockBackgroundClick() {
|
||||
const bgImage = window.getComputedStyle(document.getElementById('bg1')).backgroundImage;
|
||||
|
||||
// Extract the URL from the CSS string
|
||||
const urlRegex = /url\((['"])?(.*?)\1\)/;
|
||||
const matches = bgImage.match(urlRegex);
|
||||
const url = matches[2];
|
||||
|
||||
// Remove the protocol and host, leaving the relative URL
|
||||
const relativeUrl = new URL(url).pathname;
|
||||
const relativeBgImage = `url("${relativeUrl}")`
|
||||
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
}
|
||||
|
||||
function onUnlockBackgroundClick() {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
const context = getContext();
|
||||
return !!context.chatMetadata[METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
const context = getContext();
|
||||
context.chatMetadata[METADATA_KEY] = file;
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
const context = getContext();
|
||||
delete context.chatMetadata[METADATA_KEY];
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const context = getContext();
|
||||
const file = context.chatMetadata[METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#bg_custom").css("background-image", file);
|
||||
$("#custom_bg_preview").css("background-image", file);
|
||||
}
|
||||
|
||||
function unsetCustomBackground() {
|
||||
$("#bg_custom").css("background-image", 'none');
|
||||
$("#custom_bg_preview").css("background-image", 'none');
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const bgfile = $(this).attr("bgfile");
|
||||
|
||||
if (hasCustomBackground()) {
|
||||
saveBackgroundMetadata(`url("backgrounds/${bgfile}")`);
|
||||
setCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
const options = Array.from(document.querySelectorAll('.BGSampleTitle')).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);
|
||||
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();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
function addSettings() {
|
||||
const html = `
|
||||
<div class="background_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat Backgrounds</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="background_controls">
|
||||
<div id="lock_background" class="menu_button">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
Lock
|
||||
</div>
|
||||
<div id="unlock_background" class="menu_button">
|
||||
<i class="fa-solid fa-unlock"></i>
|
||||
Unlock
|
||||
</div>
|
||||
<small>
|
||||
Press "Lock" to assign a currently selected background to a character or group chat.<br>
|
||||
Any background image selected while lock is engaged will be saved automatically.
|
||||
</small>
|
||||
</div>
|
||||
<div class="background_controls">
|
||||
<div id="auto_background" class="menu_button">
|
||||
<i class="fa-solid fa-wand-magic"></i>
|
||||
Auto
|
||||
</div>
|
||||
<small>
|
||||
Automatically select a background based on the chat context.<br>
|
||||
Respects the "Lock" setting state.
|
||||
</small>
|
||||
</div>
|
||||
<div>Preview</div>
|
||||
<div id="custom_bg_preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(html);
|
||||
$('#lock_background').on('click', onLockBackgroundClick);
|
||||
$('#unlock_background').on('click', onUnlockBackgroundClick);
|
||||
$(document).on("click", ".bg_example", onSelectBackgroundClick);
|
||||
$('#auto_background').on("click", autoBackgroundCommand);
|
||||
}
|
||||
|
||||
addSettings();
|
||||
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);
|
||||
eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||
eventSource.on(event_types.CHAT_CHANGED, moduleWorker);
|
||||
});
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"display_name": "Chat Backgrounds",
|
||||
"loading_order": 7,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
#custom_bg_preview {
|
||||
width: 160px;
|
||||
height: 90px;
|
||||
background-color: var(--grey30a);
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
border-radius: 20px;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
box-shadow: 0 0 7px var(--black50a);
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#custom_bg_preview::before {
|
||||
content: 'No Background';
|
||||
color: white;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#custom_bg_preview:not([style*="background-image: none"])::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background_controls .menu_button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls small {
|
||||
flex-grow: 1;
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"display_name": "Bulk Card Editor",
|
||||
"loading_order": 9,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "city-unit",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/city-unit"
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
.bulk_select_checkbox {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
#rm_print_characters_block.bulk_select .wide100pLess70px {
|
||||
width: calc(100% - 85px);
|
||||
}
|
@@ -1,17 +1,42 @@
|
||||
import { getBase64Async, saveBase64AsFile } from "../../utils.js";
|
||||
import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules } from "../../extensions.js";
|
||||
import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
|
||||
import { appendImageToMessage, callPopup, getRequestHeaders, saveSettingsDebounced, substituteParams } from "../../../script.js";
|
||||
import { getMessageTimeStamp } from "../../RossAscends-mods.js";
|
||||
import { SECRET_KEYS, secret_state } from "../../secrets.js";
|
||||
import { isImageInliningSupported } from "../../openai.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'caption';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
|
||||
const PROMPT_DEFAULT = 'What’s in this image?';
|
||||
const TEMPLATE_DEFAULT = '[{{user}} sends {{char}} a picture that contains: {{caption}}]';
|
||||
|
||||
async function moduleWorker() {
|
||||
const hasConnection = getContext().onlineStatus !== 'no_connection';
|
||||
$('#send_picture').toggle(hasConnection);
|
||||
}
|
||||
|
||||
function migrateSettings() {
|
||||
if (extension_settings.caption.local !== undefined) {
|
||||
extension_settings.caption.source = extension_settings.caption.local ? 'local' : 'extras';
|
||||
}
|
||||
|
||||
delete extension_settings.caption.local;
|
||||
|
||||
if (!extension_settings.caption.source) {
|
||||
extension_settings.caption.source = 'extras';
|
||||
}
|
||||
|
||||
if (!extension_settings.caption.prompt) {
|
||||
extension_settings.caption.prompt = PROMPT_DEFAULT;
|
||||
}
|
||||
|
||||
if (!extension_settings.caption.template) {
|
||||
extension_settings.caption.template = TEMPLATE_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
async function setImageIcon() {
|
||||
try {
|
||||
const sendButton = $('#send_picture .extensionsMenuExtensionButton');
|
||||
@@ -36,7 +61,14 @@ async function setSpinnerIcon() {
|
||||
|
||||
async function sendCaptionedMessage(caption, image) {
|
||||
const context = getContext();
|
||||
let messageText = `[${context.name1} sends ${context.name2 ?? ''} a picture that contains: ${caption}]`;
|
||||
let template = extension_settings.caption.template || TEMPLATE_DEFAULT;
|
||||
|
||||
if (!/{{caption}}/i.test(template)) {
|
||||
console.warn('Poka-yoke: Caption template does not contain {{caption}}. Appending it.')
|
||||
template += ' {{caption}}';
|
||||
}
|
||||
|
||||
let messageText = substituteParams(template).replace(/{{caption}}/i, caption);
|
||||
|
||||
if (extension_settings.caption.refine_mode) {
|
||||
messageText = await callPopup(
|
||||
@@ -65,42 +97,96 @@ async function sendCaptionedMessage(caption, image) {
|
||||
await context.generate('caption');
|
||||
}
|
||||
|
||||
async function doCaptionRequest(base64Img) {
|
||||
if (extension_settings.caption.local) {
|
||||
const apiResult = await fetch('/api/extra/caption', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ image: base64Img })
|
||||
});
|
||||
/**
|
||||
*
|
||||
* @param {string} base64Img Base64 encoded image without the data:image/...;base64, prefix
|
||||
* @param {string} fileData Base64 encoded image with the data:image/...;base64, prefix
|
||||
* @returns
|
||||
*/
|
||||
async function doCaptionRequest(base64Img, fileData) {
|
||||
switch (extension_settings.caption.source) {
|
||||
case 'local':
|
||||
return await captionLocal(base64Img);
|
||||
case 'extras':
|
||||
return await captionExtras(base64Img);
|
||||
case 'horde':
|
||||
return await captionHorde(base64Img);
|
||||
case 'openai':
|
||||
return await captionOpenAI(fileData);
|
||||
default:
|
||||
throw new Error('Unknown caption source.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via local pipeline.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
} else if (modules.includes('caption')) {
|
||||
const url = new URL(getApiUrl());
|
||||
url.pathname = '/api/caption';
|
||||
|
||||
const apiResult = await doExtrasFetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Bypass-Tunnel-Reminder': 'bypass',
|
||||
},
|
||||
body: JSON.stringify({ image: base64Img })
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via Extras.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
} else {
|
||||
async function captionExtras(base64Img) {
|
||||
if (!modules.includes('caption')) {
|
||||
throw new Error('No captioning module is available.');
|
||||
}
|
||||
|
||||
const url = new URL(getApiUrl());
|
||||
url.pathname = '/api/caption';
|
||||
|
||||
const apiResult = await doExtrasFetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Bypass-Tunnel-Reminder': 'bypass',
|
||||
},
|
||||
body: JSON.stringify({ image: base64Img })
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via Extras.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function captionLocal(base64Img) {
|
||||
const apiResult = await fetch('/api/extra/caption', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ image: base64Img })
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via local pipeline.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function captionHorde(base64Img) {
|
||||
const apiResult = await fetch('/api/horde/caption-image', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ image: base64Img })
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via Horde.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function captionOpenAI(base64Img) {
|
||||
const prompt = extension_settings.caption.prompt || PROMPT_DEFAULT;
|
||||
const apiResult = await fetch('/api/openai/caption-image', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ image: base64Img, prompt: prompt }),
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
throw new Error('Failed to caption image via OpenAI.');
|
||||
}
|
||||
|
||||
const data = await apiResult.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function onSelectImage(e) {
|
||||
@@ -116,7 +202,7 @@ async function onSelectImage(e) {
|
||||
const fileData = await getBase64Async(file);
|
||||
const base64Format = fileData.split(',')[0].split(';')[0].split('/')[1];
|
||||
const base64Data = fileData.split(',')[1];
|
||||
const data = await doCaptionRequest(base64Data);
|
||||
const data = await doCaptionRequest(base64Data, fileData);
|
||||
const caption = data.caption;
|
||||
const imageToSave = data.thumbnail ? data.thumbnail : base64Data;
|
||||
const format = data.thumbnail ? 'jpeg' : base64Format;
|
||||
@@ -138,6 +224,83 @@ function onRefineModeInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function sendEmbeddedImage(e) {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file || !(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const context = getContext();
|
||||
const fileData = await getBase64Async(file);
|
||||
const base64Format = fileData.split(',')[0].split(';')[0].split('/')[1];
|
||||
const base64Data = fileData.split(',')[1];
|
||||
const caption = await callPopup('<h3>Enter a comment or question (optional)</h3>', 'input', 'What is this?', { okButton: 'Send', rows: 2 });
|
||||
const imagePath = await saveBase64AsFile(base64Data, context.name2, '', base64Format);
|
||||
const message = {
|
||||
name: context.name1,
|
||||
is_user: true,
|
||||
send_date: getMessageTimeStamp(),
|
||||
mes: caption || `[${context.name1} sends ${context.name2} a picture]`,
|
||||
extra: {
|
||||
image: imagePath,
|
||||
inline_image: !!caption,
|
||||
title: caption || '',
|
||||
},
|
||||
};
|
||||
context.chat.push(message);
|
||||
context.addOneMessage(message);
|
||||
await context.generate('caption');
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
finally {
|
||||
e.target.form.reset();
|
||||
setImageIcon();
|
||||
}
|
||||
}
|
||||
|
||||
function onImageEmbedClicked() {
|
||||
const context = getContext();
|
||||
const messageElement = $(this).closest('.mes');
|
||||
const messageId = messageElement.attr('mesid');
|
||||
const message = context.chat[messageId];
|
||||
|
||||
if (!message) {
|
||||
console.warn('Failed to find message with id', messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#embed_img_file')
|
||||
.off('change')
|
||||
.on('change', parseAndUploadEmbed)
|
||||
.trigger('click');
|
||||
|
||||
async function parseAndUploadEmbed(e) {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file || !(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
const fileData = await getBase64Async(file);
|
||||
const base64Data = fileData.split(',')[1];
|
||||
const base64Format = fileData.split(',')[0].split(';')[0].split('/')[1];
|
||||
const imagePath = await saveBase64AsFile(base64Data, context.name2, '', base64Format);
|
||||
|
||||
if (!message.extra) {
|
||||
message.extra = {};
|
||||
}
|
||||
|
||||
message.extra.image = imagePath;
|
||||
message.extra.inline_image = true;
|
||||
message.extra.title = '';
|
||||
appendImageToMessage(message, messageElement);
|
||||
await context.saveChat();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
function addSendPictureButton() {
|
||||
const sendButton = $(`
|
||||
@@ -149,10 +312,20 @@ jQuery(function () {
|
||||
$('#extensionsMenu').prepend(sendButton);
|
||||
$(sendButton).hide();
|
||||
$(sendButton).on('click', () => {
|
||||
const hasCaptionModule = modules.includes('caption') || extension_settings.caption.local;
|
||||
if (isImageInliningSupported()) {
|
||||
console.log('Native image inlining is supported. Skipping captioning.');
|
||||
$('#embed_img_file').off('change').on('change', sendEmbeddedImage).trigger('click');
|
||||
return;
|
||||
}
|
||||
|
||||
const hasCaptionModule =
|
||||
(modules.includes('caption') && extension_settings.caption.source === 'extras') ||
|
||||
(extension_settings.caption.source === 'openai' && secret_state[SECRET_KEYS.OPENAI]) ||
|
||||
extension_settings.caption.source === 'local' ||
|
||||
extension_settings.caption.source === 'horde';
|
||||
|
||||
if (!hasCaptionModule) {
|
||||
toastr.error('No captioning module is available. Either enable the local captioning pipeline or connect to Extras.');
|
||||
toastr.error('No captioning module is available. Choose other captioning source in the extension settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,10 +333,12 @@ jQuery(function () {
|
||||
});
|
||||
}
|
||||
function addPictureSendForm() {
|
||||
const inputHtml = `<input id="img_file" type="file" accept="image/*">`;
|
||||
const inputHtml = `<input id="img_file" type="file" hidden accept="image/*">`;
|
||||
const embedInputHtml = `<input id="embed_img_file" type="file" hidden accept="image/*">`;
|
||||
const imgForm = document.createElement('form');
|
||||
imgForm.id = 'img_form';
|
||||
$(imgForm).append(inputHtml);
|
||||
$(imgForm).append(embedInputHtml);
|
||||
$(imgForm).hide();
|
||||
$('#form_sheld').append(imgForm);
|
||||
$('#img_file').on('change', onSelectImage);
|
||||
@@ -177,11 +352,18 @@ jQuery(function () {
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label class="checkbox_label" for="caption_local">
|
||||
<input id="caption_local" type="checkbox" class="checkbox">
|
||||
Use local captioning pipeline
|
||||
</label>
|
||||
<label class="checkbox_label" for="caption_refine_mode">
|
||||
<label for="caption_source">Source:</label>
|
||||
<select id="caption_source" class="text_pole">
|
||||
<option value="local">Local</option>
|
||||
<option value="extras">Extras</option>
|
||||
<option value="horde">Horde</option>
|
||||
<option value="openai">OpenAI</option>
|
||||
</select>
|
||||
<label for="caption_prompt">Caption Prompt (OpenAI):</label>
|
||||
<textarea id="caption_prompt" class="text_pole" rows="1" placeholder="< Use default >">${PROMPT_DEFAULT}</textarea>
|
||||
<label for="caption_template">Message Template: <small>(use <tt>{{caption}}</tt> macro)</small></label>
|
||||
<textarea id="caption_template" class="text_pole" rows="2" placeholder="< Use default >">${TEMPLATE_DEFAULT}</textarea>
|
||||
<label class="checkbox_label margin-bot-10px" for="caption_refine_mode">
|
||||
<input id="caption_refine_mode" type="checkbox" class="checkbox">
|
||||
Edit captions before generation
|
||||
</label>
|
||||
@@ -196,13 +378,26 @@ jQuery(function () {
|
||||
addPictureSendForm();
|
||||
addSendPictureButton();
|
||||
setImageIcon();
|
||||
migrateSettings();
|
||||
moduleWorker();
|
||||
|
||||
$('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode));
|
||||
$('#caption_local').prop('checked', !!(extension_settings.caption.local));
|
||||
$('#caption_source').val(extension_settings.caption.source);
|
||||
$('#caption_prompt').val(extension_settings.caption.prompt);
|
||||
$('#caption_template').val(extension_settings.caption.template);
|
||||
$('#caption_refine_mode').on('input', onRefineModeInput);
|
||||
$('#caption_local').on('input', () => {
|
||||
extension_settings.caption.local = !!$('#caption_local').prop('checked');
|
||||
$('#caption_source').on('change', () => {
|
||||
extension_settings.caption.source = String($('#caption_source').val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#caption_prompt').on('input', () => {
|
||||
extension_settings.caption.prompt = String($('#caption_prompt').val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#caption_template').on('input', () => {
|
||||
extension_settings.caption.template = String($('#caption_template').val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$(document).on('click', '.mes_embed', onImageEmbedClicked);
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
});
|
||||
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"display_name": "CFG",
|
||||
"loading_order": 1,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "kingbri",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
<a id="option_toggle_CFG">
|
||||
<i class="fa-lg fa-solid fa-scale-balanced"></i>
|
||||
<span data-i18n="CFG Scale">CFG Scale</span>
|
||||
</a>
|
@@ -1,90 +0,0 @@
|
||||
import { chat_metadata, substituteParams, this_chid } from "../../../script.js";
|
||||
import { extension_settings, getContext } from "../../extensions.js"
|
||||
import { selected_group } from "../../group-chats.js";
|
||||
import { getCharaFilename } from "../../utils.js";
|
||||
|
||||
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() {
|
||||
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
|
||||
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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// This line is a bit hacky with a JSON.stringify and JSON.parse. Fix this if possible.
|
||||
const customSeparator = JSON.parse(chat_metadata[metadataKeys.prompt_separator] || JSON.stringify("\n")) ?? "\n";
|
||||
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
|
||||
};
|
||||
}
|
@@ -1,172 +0,0 @@
|
||||
<div id="cfgConfig" class="drawer-content flexGap5">
|
||||
<div class="panelControlBar flex-container">
|
||||
<div id="cfgConfigHeader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
<div id="CFGClose" class="fa-solid fa-circle-xmark"></div>
|
||||
</div>
|
||||
<div name="cfgConfigHolder" class="scrollY">
|
||||
<div id="chat_cfg_container">
|
||||
<div class="inline-drawer">
|
||||
<div id="CFGBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small>
|
||||
<b>Unique to this chat.</b><br>
|
||||
</small>
|
||||
<label for="chat_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chat_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chat_cfg_guidance_scale" id="chat_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chat_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chat_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
<div id="groupchat_cfg_use_chara_container">
|
||||
<label class="checkbox_label" for="groupchat_cfg_use_chara">
|
||||
<input type="checkbox" id="groupchat_cfg_use_chara" />
|
||||
<span data-i18n="Use character CFG scales">Use character CFG scales</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chara_cfg_container" style="display: none;">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Character CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be automatically added as the CFG for this character.</b></small>
|
||||
<br />
|
||||
<label for="chara_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chara_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chara_cfg_guidance_scale" id="chara_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chara_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chara_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="global_cfg_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Global CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be used as the default CFG options for every chat unless overridden.</b></small>
|
||||
<br />
|
||||
<label for="global_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="global_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="global_cfg_guidance_scale" id="global_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="global_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="global_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cfg_prompt_combine_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>CFG Prompt Cascading</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<small>
|
||||
<b>Combine positive/negative prompts from other boxes.</b>
|
||||
<br />
|
||||
For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
||||
</small>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label for="cfg_prompt_combine">
|
||||
<span data-i18n="Scale">Always Include</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="0" />
|
||||
<span data-i18n="Chat Negatives">Chat Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="1" />
|
||||
<span data-i18n="Character Negatives">Character Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="2" />
|
||||
<span data-i18n="Global Negatives">Global Negatives</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label>
|
||||
Custom Separator: <input id="cfg_prompt_separator" class="text_pole textarea_compact widthUnset" placeholder=""\n"" type="text" />
|
||||
</label>
|
||||
<label>
|
||||
Insertion Depth: <input id="cfg_prompt_insertion_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,88 +0,0 @@
|
||||
import { callPopup } from "../../../script.js";
|
||||
import { getContext } from "../../extensions.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'dice';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
|
||||
async function doDiceRoll(customDiceFormula) {
|
||||
let value = typeof customDiceFormula === 'string' ? customDiceFormula.trim() : $(this).data('value');
|
||||
|
||||
if (value == 'custom') {
|
||||
value = await callPopup('Enter the dice formula:<br><i>(for example, <tt>2d6</tt>)</i>', 'input');
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isValid = droll.validate(value);
|
||||
|
||||
if (isValid) {
|
||||
const result = droll.roll(value);
|
||||
const context = getContext();
|
||||
context.sendSystemMessage('generic', `${context.name1} rolls a ${value}. The result is: ${result.total} (${result.rolls})`, { isSmallSys: true });
|
||||
} else {
|
||||
toastr.warning('Invalid dice formula');
|
||||
}
|
||||
}
|
||||
|
||||
function addDiceRollButton() {
|
||||
const buttonHtml = `
|
||||
<div id="roll_dice" class="list-group-item flex-container flexGap5">
|
||||
<div class="fa-solid fa-dice extensionsMenuExtensionButton" title="Roll Dice" /></div>
|
||||
Roll Dice
|
||||
</div>
|
||||
`;
|
||||
const dropdownHtml = `
|
||||
<div id="dice_dropdown">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" data-value="d4">d4</li>
|
||||
<li class="list-group-item" data-value="d6">d6</li>
|
||||
<li class="list-group-item" data-value="d8">d8</li>
|
||||
<li class="list-group-item" data-value="d10">d10</li>
|
||||
<li class="list-group-item" data-value="d12">d12</li>
|
||||
<li class="list-group-item" data-value="d20">d20</li>
|
||||
<li class="list-group-item" data-value="d100">d100</li>
|
||||
<li class="list-group-item" data-value="custom">...</li>
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
$('#extensionsMenu').prepend(buttonHtml);
|
||||
|
||||
$(document.body).append(dropdownHtml)
|
||||
$('#dice_dropdown li').on('click', doDiceRoll);
|
||||
const button = $('#roll_dice');
|
||||
const dropdown = $('#dice_dropdown');
|
||||
dropdown.hide();
|
||||
button.hide();
|
||||
|
||||
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
|
||||
placement: 'top',
|
||||
});
|
||||
|
||||
$(document).on('click touchend', function (e) {
|
||||
const target = $(e.target);
|
||||
if (target.is(dropdown)) return;
|
||||
if (target.is(button) && !dropdown.is(":visible")) {
|
||||
e.preventDefault();
|
||||
|
||||
dropdown.fadeIn(250);
|
||||
popper.update();
|
||||
} else {
|
||||
dropdown.fadeOut(250);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
$('#roll_dice').toggle(getContext().onlineStatus !== 'no_connection');
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
addDiceRollButton();
|
||||
moduleWorker();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
registerSlashCommand('roll', (_, value) => doDiceRoll(value), ['r'], "<span class='monospace'>(dice formula)</span> – roll the dice. For example, /roll 2d6", false, true);
|
||||
});
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"display_name": "D&D Dice",
|
||||
"loading_order": 5,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user