mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
303 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c9c4f30637 | ||
|
1c095415a4 | ||
|
19acc05516 | ||
|
2cbde8b55c | ||
|
15cf87ef01 | ||
|
57bf499ce9 | ||
|
4fbbd34db9 | ||
|
9b04e43b4c | ||
|
5848ec498b | ||
|
5a570ff283 | ||
|
1dd6fa4b6a | ||
|
32377fd131 | ||
|
2f294b9f2b | ||
|
0b8295926e | ||
|
27ce0b5eb7 | ||
|
2797b4bd89 | ||
|
e9ba20f13c | ||
|
1ef68a34fe | ||
|
4189da19b1 | ||
|
ad3f8e7bf4 | ||
|
0dfe46a162 | ||
|
edeb804128 | ||
|
b49f234185 | ||
|
85a63b3bb3 | ||
|
04a0d012fc | ||
|
eaa33f2544 | ||
|
d5004d79bd | ||
|
96be2ebf35 | ||
|
fc90843207 | ||
|
679d40b3ce | ||
|
5307ee5d17 | ||
|
dd753498fa | ||
|
30e5a31591 | ||
|
2befd69c31 | ||
|
995b4d07ff | ||
|
788bbe969f | ||
|
99a89a7329 | ||
|
f8b5fe8b1e | ||
|
368cba1149 | ||
|
dac6639cc1 | ||
|
345a0e04fc | ||
|
82182015e2 | ||
|
384e8d43a6 | ||
|
b203b22d4d | ||
|
b9ea0061d5 | ||
|
e9afb39a4a | ||
|
df55a66153 | ||
|
6df0c2acf3 | ||
|
046bf20592 | ||
|
2879e2701f | ||
|
7771ecb0d0 | ||
|
cc9f452699 | ||
|
a8fb2d9bee | ||
|
165997c791 | ||
|
22e3c25e12 | ||
|
495b7eebbf | ||
|
4c39a32be1 | ||
|
584d7961eb | ||
|
977421edb7 | ||
|
f0b48aabff | ||
|
191c180946 | ||
|
03fe8f88f9 | ||
|
bb47712696 | ||
|
f466ff3243 | ||
|
306cf51da4 | ||
|
5857823c3b | ||
|
02418d2d97 | ||
|
0569b706dc | ||
|
bfda5a5492 | ||
|
03e5ca054d | ||
|
3e29d39f05 | ||
|
01f14b461f | ||
|
edb79d8c53 | ||
|
db71b87309 | ||
|
8f426a0184 | ||
|
773d42b886 | ||
|
1b7c51ebde | ||
|
e2d4f9dc8c | ||
|
0a9b595f80 | ||
|
86e9974410 | ||
|
423f88a458 | ||
|
9bc3e4e47c | ||
|
1838ca1994 | ||
|
2411006fdb | ||
|
5421925d6c | ||
|
d9b2b9f753 | ||
|
a081f78bd8 | ||
|
5275e0c904 | ||
|
2903167fad | ||
|
a25cb024fe | ||
|
6203d82f48 | ||
|
21ebe0c148 | ||
|
43510d5626 | ||
|
3cd97b0772 | ||
|
bd72f0eeb0 | ||
|
565327fe1e | ||
|
2e5bbf0445 | ||
|
ec6b6ab8d4 | ||
|
654a34f932 | ||
|
3d1312c13a | ||
|
52cf684444 | ||
|
54c37e945b | ||
|
ecab8a6cb4 | ||
|
b1ab1451ec | ||
|
b84fbed800 | ||
|
ccf66e6343 | ||
|
5e8fc39735 | ||
|
cd1a8c9224 | ||
|
ae4a9a7b14 | ||
|
6ae1b7a72b | ||
|
d8380a390a | ||
|
5fbb232d69 | ||
|
25d818ecbd | ||
|
5dd9a87dc9 | ||
|
857ce2c577 | ||
|
902acc44a2 | ||
|
22a0bf9451 | ||
|
ad95be2500 | ||
|
071b901f87 | ||
|
ad9382a98c | ||
|
49c26f3810 | ||
|
86c7a7a058 | ||
|
0178c95f6f | ||
|
440ecfc991 | ||
|
e0b5df97c4 | ||
|
e3f760a9dd | ||
|
c9783640c0 | ||
|
f4d1e2a46e | ||
|
b52f71dcce | ||
|
158aa79aed | ||
|
3fe2b21686 | ||
|
dfbeb41afa | ||
|
3d4054f10e | ||
|
6c6f914655 | ||
|
3de5cdd7e8 | ||
|
eb6e987f55 | ||
|
10f27f41d1 | ||
|
3d83d1d5b7 | ||
|
cfd6a26881 | ||
|
e92d4a3dbf | ||
|
1189734c62 | ||
|
a78bb82b44 | ||
|
3a8383ab79 | ||
|
dc1c477d62 | ||
|
e9c459690f | ||
|
43de36b331 | ||
|
39567cf278 | ||
|
0033090a93 | ||
|
ef8c347a95 | ||
|
535ec8c42d | ||
|
66911160c0 | ||
|
d861c59f27 | ||
|
938f89cd1a | ||
|
acbd01407d | ||
|
254339af34 | ||
|
6c9cabfb57 | ||
|
359277deb5 | ||
|
11e7ca76e1 | ||
|
dc1121b72a | ||
|
323493962a | ||
|
751c0723dc | ||
|
23b08173ff | ||
|
9f15e67856 | ||
|
2c84c93f3d | ||
|
bfdd071001 | ||
|
ab9aa28fe4 | ||
|
61995bb33f | ||
|
38b63b07f5 | ||
|
d185e143a8 | ||
|
4e1630c17d | ||
|
2214f284fa | ||
|
6e562bd1ff | ||
|
2d774f32b2 | ||
|
5ab449d8a1 | ||
|
124658a006 | ||
|
57de6229f9 | ||
|
e162df67fa | ||
|
dae09d58d7 | ||
|
6dd09858d4 | ||
|
985c2bcfb1 | ||
|
fc7a4538e9 | ||
|
72c672c2c2 | ||
|
77c8bc8eb5 | ||
|
1edc2b08f2 | ||
|
d34f7d3e1a | ||
|
eaca6ddaf0 | ||
|
aa89a74901 | ||
|
599904d589 | ||
|
ba302e4aa0 | ||
|
0f1a0963fd | ||
|
2b3055a84a | ||
|
1fed8ba4f7 | ||
|
5827f9638f | ||
|
6ad786f348 | ||
|
0cc048cb64 | ||
|
cb8d9ac71b | ||
|
b24509ef43 | ||
|
7553efc308 | ||
|
9fb4b3425e | ||
|
182216e711 | ||
|
52891898d2 | ||
|
f6c29c61df | ||
|
17a5d629ea | ||
|
688551ffa6 | ||
|
ece34dc337 | ||
|
179de92231 | ||
|
c4c962aeb9 | ||
|
d0182c47de | ||
|
0d30d8244f | ||
|
2e67ebd881 | ||
|
d62cdffcc0 | ||
|
77a28c7131 | ||
|
c60d4e5bb9 | ||
|
990e08ba2d | ||
|
bf0cf10403 | ||
|
7a3869c476 | ||
|
e74090139c | ||
|
09fc42a787 | ||
|
c0e5d7efae | ||
|
66ec17620f | ||
|
51e2a3afcf | ||
|
abc1555c19 | ||
|
6c29879f12 | ||
|
2f8f6844fe | ||
|
dc4a6e862b | ||
|
4bf91c7772 | ||
|
004baf7b87 | ||
|
6c8bd06308 | ||
|
65b4551864 | ||
|
7f55d108cf | ||
|
c9a9dab523 | ||
|
f149fc9aaa | ||
|
7aeb098212 | ||
|
c76c76410c | ||
|
5cc6a2dca6 | ||
|
0bdd350b8d | ||
|
9a5e667674 | ||
|
c9d8d7ba64 | ||
|
17367f2b17 | ||
|
6362f76812 | ||
|
74d627f674 | ||
|
599261dc31 | ||
|
d19c151669 | ||
|
b30d7ad51c | ||
|
4fdc533bd7 | ||
|
d17ac770e6 | ||
|
70071312d3 | ||
|
ec23356c99 | ||
|
98cc969d18 | ||
|
dc5deaf47c | ||
|
d81c94de0b | ||
|
e2e32da4e6 | ||
|
0480acebcd | ||
|
dbac2704f3 | ||
|
f8d90c1933 | ||
|
3dbdd1258e | ||
|
f92249790f | ||
|
23951b8c8a | ||
|
af38971a01 | ||
|
ed6417ebcd | ||
|
2fa038f91d | ||
|
31beb05aa1 | ||
|
4cf6a1f7da | ||
|
180dcefe40 | ||
|
307e666c27 | ||
|
b605b940eb | ||
|
967a084aad | ||
|
4d08e3e9be | ||
|
da34517943 | ||
|
7ffe3d21f8 | ||
|
ea01247bcf | ||
|
786b87952e | ||
|
26ddfd1a08 | ||
|
6f3947226f | ||
|
42fd317188 | ||
|
2411b17279 | ||
|
ab460199ab | ||
|
3a3ff89047 | ||
|
a5acc7872d | ||
|
2688d980c1 | ||
|
3abee9e37a | ||
|
5b63d0ff40 | ||
|
a96aad6073 | ||
|
02bdd56e20 | ||
|
b5a6257352 | ||
|
408a1fe846 | ||
|
44ba1cba59 | ||
|
96df705409 | ||
|
40f95bf842 | ||
|
9d45c0a018 | ||
|
470da71b3b | ||
|
92ab17b58b | ||
|
8c00f38a1f | ||
|
89705391d1 | ||
|
bbe52886da | ||
|
ef68dd07ac | ||
|
f2cae64b0d | ||
|
868778b079 | ||
|
e681f1f36f | ||
|
1832145645 | ||
|
af4f60a4af | ||
|
fa147f71a3 | ||
|
902676262a |
90
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
90
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
name: Bug Report 🐛
|
||||||
|
description: Report something that's not working the intended way. Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!
|
||||||
|
title: '[BUG] <title>'
|
||||||
|
labels: ['bug']
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Where are you running SillyTavern?
|
||||||
|
options:
|
||||||
|
- Self-Hosted (Bare Metal)
|
||||||
|
- Self-Hosted (Docker)
|
||||||
|
- Android (Termux)
|
||||||
|
- Cloud Service (Static)
|
||||||
|
- Other (Specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: system
|
||||||
|
attributes:
|
||||||
|
label: System
|
||||||
|
description: >-
|
||||||
|
For deployment issues, specify your [distro or OS](https://whatsmyos.com/) and/ or Docker version.
|
||||||
|
For client-side issues, include your [browser version](https://www.whatsmybrowser.org/)
|
||||||
|
placeholder: e.g. Firefox 101, Manjaro Linux 21.3.0, Docker 20.10.16
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: What version of SillyTavern are you running?
|
||||||
|
placeholder: (check User Settings to see the version)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: desktop
|
||||||
|
attributes:
|
||||||
|
label: Desktop Information
|
||||||
|
description: Please provide details about your desktop environment.
|
||||||
|
placeholder: |
|
||||||
|
- Node.js version (if applicable): [run `node --version` in cmd]
|
||||||
|
- Generation API [e.g. KoboldAI, OpenAI]
|
||||||
|
- Branch [staging, release]
|
||||||
|
- Model [e.g. Pygmalion 6b, LLaMa 13b]
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: repro
|
||||||
|
attributes:
|
||||||
|
label: Describe the problem
|
||||||
|
description: Please describe exactly what is not working, include the steps to reproduce, actual result and expected result
|
||||||
|
placeholder: When doing ABC then DEF, I expect to see XYZ, but I actually see ZYX
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Additional info
|
||||||
|
description: Logs? Screenshots? Yes, please.
|
||||||
|
placeholder: If the issue happens during build-time, include terminal logs. For run-time errors, include browser logs which you can view in the Dev Tools (F12), under the Console tab. Take care to blank out any personal info.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: idiot-check
|
||||||
|
attributes:
|
||||||
|
label: Please tick the boxes
|
||||||
|
description: Before submitting, please ensure that
|
||||||
|
options:
|
||||||
|
- label: You have explained the issue clearly, and included all relevant info
|
||||||
|
required: true
|
||||||
|
- label: You've checked that this [issue hasn't already been raised](https://github.com/SillyTavern/SillyTavern/issues?q=is%3Aissue)
|
||||||
|
required: true
|
||||||
|
- label: You've checked the [docs](https://docs.sillytavern.app/) 
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## Thanks 🙏
|
||||||
|
Thank you for raising this ticket - in doing so you are helping to make SillyTavern better for everyone.
|
||||||
|
validations:
|
||||||
|
required: false
|
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: "Create a report to help us improve. PAY ATTENTION: Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!"
|
|
||||||
title: "[BUG]"
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> **Warning**. Complete **all** the fields below. Otherwise, your bug report will be **ignored**!
|
|
||||||
|
|
||||||
**Have you searched for similar [bugs](https://github.com/SillyTavern/SillyTavern/issues?q=)?**
|
|
||||||
Yes/No
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Logs**
|
|
||||||
|
|
||||||
Providing the logs from the browser DevTools console (opened by pressing the F12 key) or SillyTavern command line window will be highly appreciated.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
- OS/Device: [e.g. Windows 11]
|
|
||||||
- Environment: [cloud, local]
|
|
||||||
- Node.js version (if applicable): [run `node --version` in cmd]
|
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Generation API [e.g. KoboldAI, OpenAI]
|
|
||||||
- Branch [staging, release]
|
|
||||||
- Model [e.g. Pygmalion 6b, LLaMa 13b]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
91
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
91
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
name: Feature Request ✨
|
||||||
|
description: Suggest an idea for future development of this project
|
||||||
|
title: '[FEATURE_REQUEST] <title>'
|
||||||
|
labels: ['enhancement']
|
||||||
|
|
||||||
|
body:
|
||||||
|
|
||||||
|
# Field 1 - Did the user searched for similar requests
|
||||||
|
- type: dropdown
|
||||||
|
id: similarRequest
|
||||||
|
attributes:
|
||||||
|
label: Have you searched for similar requests?
|
||||||
|
description:
|
||||||
|
options:
|
||||||
|
- 'No'
|
||||||
|
- 'Yes'
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# Field 2 - Is it bug-related
|
||||||
|
- type: textarea
|
||||||
|
id: issue
|
||||||
|
attributes:
|
||||||
|
label: Is your feature request related to a problem? If so, please describe.
|
||||||
|
description:
|
||||||
|
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# Field 3 - Describe feature
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Describe the solution you'd like
|
||||||
|
placeholder: An outline of how you would like this to be implemented, include as much details as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
# Field 4 - Describe alternatives
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Describe alternatives you've considered
|
||||||
|
placeholder: A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# Field 5 - Additional context
|
||||||
|
- type: textarea
|
||||||
|
id: addcontext
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
placeholder: Add any other context or screenshots about the feature request here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# Field 6 - Priority
|
||||||
|
- type: dropdown
|
||||||
|
id: priority
|
||||||
|
attributes:
|
||||||
|
label: Priority
|
||||||
|
description: How urgent is the development of this feature
|
||||||
|
options:
|
||||||
|
- Low (Nice-to-have)
|
||||||
|
- Medium (Would be very useful)
|
||||||
|
- High (The app does not function without it)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
# Field 7 - Can the user implement
|
||||||
|
- type: dropdown
|
||||||
|
id: canImplement
|
||||||
|
attributes:
|
||||||
|
label: Is this something you would be keen to implement?
|
||||||
|
description: Are you raising this ticket in order to get an issue number for your PR?
|
||||||
|
options:
|
||||||
|
- 'No'
|
||||||
|
- 'Maybe'
|
||||||
|
- 'Yes!'
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# Final text
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## Thanks 🙏
|
||||||
|
Thank you for your feature suggestion.
|
||||||
|
Please note that there is no guarantee that your idea will be implemented.
|
||||||
|
validations:
|
||||||
|
required: false
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: "[Feature Request] "
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Have you searched for similar [requests](https://github.com/SillyTavern/SillyTavern/issues?q=)?**
|
|
||||||
Yes/No
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
2
.github/readme-zh_cn.md
vendored
2
.github/readme-zh_cn.md
vendored
@@ -41,8 +41,6 @@ SillyTavern 本身并无用处,因为它只是一个用户聊天界面。你
|
|||||||
|
|
||||||
<https://rentry.org/STAI-Termux>
|
<https://rentry.org/STAI-Termux>
|
||||||
|
|
||||||
Termux 不支持**.Webp 字符卡的导入/导出。请使用 JSON 或 PNG 格式**。
|
|
||||||
|
|
||||||
## 有问题或建议?
|
## 有问题或建议?
|
||||||
|
|
||||||
### 我们现在有了 Discord 社区
|
### 我们现在有了 Discord 社区
|
||||||
|
4
.github/readme.md
vendored
4
.github/readme.md
vendored
@@ -41,8 +41,6 @@ Since Tavern is only a user interface, it has tiny hardware requirements, it wil
|
|||||||
|
|
||||||
<https://rentry.org/STAI-Termux>
|
<https://rentry.org/STAI-Termux>
|
||||||
|
|
||||||
**.webp character cards import/export is not supported in Termux. Use either JSON or PNG formats instead.**
|
|
||||||
|
|
||||||
## Questions or suggestions?
|
## Questions or suggestions?
|
||||||
|
|
||||||
### We now have a community Discord server
|
### We now have a community Discord server
|
||||||
@@ -71,7 +69,6 @@ Get in touch with the developers directly:
|
|||||||
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
||||||
* [AI Horde](https://horde.koboldai.net/) connection
|
* [AI Horde](https://horde.koboldai.net/) connection
|
||||||
* Prompt generation formatting tweaking
|
* Prompt generation formatting tweaking
|
||||||
* webp character card interoperability (PNG is still an internal format)
|
|
||||||
|
|
||||||
## Extensions
|
## Extensions
|
||||||
|
|
||||||
@@ -295,6 +292,7 @@ GNU Affero General Public License for more details.**
|
|||||||
* RossAscends' additions: AGPL v3
|
* RossAscends' additions: AGPL v3
|
||||||
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
||||||
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
||||||
|
* city_unit's extensions and various QoL features (<https://github.com/city-unit>)
|
||||||
* StefanDanielSchwarz's various commits and bug reports (<https://github.com/StefanDanielSchwarz>)
|
* StefanDanielSchwarz's various commits and bug reports (<https://github.com/StefanDanielSchwarz>)
|
||||||
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
||||||
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
||||||
|
45
.github/workflows/docker-publish.yml
vendored
Normal file
45
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# This workflow will publish a docker image for every full release to the GitHub package repository
|
||||||
|
|
||||||
|
name: Create Docker Image on Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
# Only runs on full releases not pre releases
|
||||||
|
types: [released]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# This should allow creation of docker images even in forked repositories
|
||||||
|
# Image name may not contain uppercase characters, so we can not use the repository name
|
||||||
|
# Creates a string like: ghcr.io/SillyTavern/sillytavern
|
||||||
|
image_name: ghcr.io/sillytavern/sillytavern
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Build docker image using dockerfile and tag it with branch name
|
||||||
|
# Assumes branch name is the version number
|
||||||
|
- name: Build the Docker image
|
||||||
|
run: |
|
||||||
|
docker build . --file Dockerfile --tag $image_name:${{ github.ref_name }}
|
||||||
|
|
||||||
|
# Login into package repository as the person who created the release
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Assumes release is the latest and marks image as such
|
||||||
|
- name: Docker Tag and Push
|
||||||
|
run: |
|
||||||
|
docker tag $image_name:${{ github.ref_name }} $image_name:latest
|
||||||
|
docker push $image_name:${{ github.ref_name }}
|
||||||
|
docker push $image_name:latest
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,6 +26,7 @@ public/settings.json
|
|||||||
/thumbnails
|
/thumbnails
|
||||||
whitelist.txt
|
whitelist.txt
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea/
|
||||||
secrets.json
|
secrets.json
|
||||||
/dist
|
/dist
|
||||||
/backups/
|
/backups/
|
||||||
@@ -35,3 +36,5 @@ content.log
|
|||||||
cloudflared.exe
|
cloudflared.exe
|
||||||
public/assets/
|
public/assets/
|
||||||
access.log
|
access.log
|
||||||
|
/vectors/
|
||||||
|
/cache/
|
||||||
|
@@ -13,7 +13,7 @@ ENTRYPOINT [ "tini", "--" ]
|
|||||||
WORKDIR ${APP_HOME}
|
WORKDIR ${APP_HOME}
|
||||||
|
|
||||||
# Install app dependencies
|
# Install app dependencies
|
||||||
COPY package*.json ./
|
COPY package*.json post-install.js ./
|
||||||
RUN \
|
RUN \
|
||||||
echo "*** Install npm packages ***" && \
|
echo "*** Install npm packages ***" && \
|
||||||
npm install && npm cache clean --force
|
npm install && npm cache clean --force
|
||||||
@@ -23,7 +23,7 @@ COPY . ./
|
|||||||
|
|
||||||
# Copy default chats, characters and user avatars to <folder>.default folder
|
# Copy default chats, characters and user avatars to <folder>.default folder
|
||||||
RUN \
|
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 ***" && \
|
echo "*** Store default $RESOURCES in <folder>.default ***" && \
|
||||||
for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done && \
|
for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done && \
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
"#@markdown Enables ChromaDB for Infinity Context plugin\n",
|
"#@markdown Enables ChromaDB for Infinity Context plugin\n",
|
||||||
"\n",
|
"\n",
|
||||||
"import subprocess\n",
|
"import subprocess\n",
|
||||||
|
"import secrets\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# ---\n",
|
"# ---\n",
|
||||||
"# SillyTavern extras\n",
|
"# SillyTavern extras\n",
|
||||||
@@ -116,6 +117,7 @@
|
|||||||
"!npm install -g localtunnel\n",
|
"!npm install -g localtunnel\n",
|
||||||
"!pip install -r requirements-complete.txt\n",
|
"!pip install -r requirements-complete.txt\n",
|
||||||
"!pip install tensorflow==2.12\n",
|
"!pip install tensorflow==2.12\n",
|
||||||
|
"!pip install colorama\n",
|
||||||
"!pip install Flask-Cors\n",
|
"!pip install Flask-Cors\n",
|
||||||
"!pip install Flask-Compress\n",
|
"!pip install Flask-Compress\n",
|
||||||
"!pip install transformers\n",
|
"!pip install transformers\n",
|
||||||
@@ -130,6 +132,13 @@
|
|||||||
"!wget https://github.com/cloudflare/cloudflared/releases/download/2023.5.0/cloudflared-linux-amd64 -O /tmp/cloudflared-linux-amd64\n",
|
"!wget https://github.com/cloudflare/cloudflared/releases/download/2023.5.0/cloudflared-linux-amd64 -O /tmp/cloudflared-linux-amd64\n",
|
||||||
"!chmod +x /tmp/cloudflared-linux-amd64\n",
|
"!chmod +x /tmp/cloudflared-linux-amd64\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"# Generate a random API key\n",
|
||||||
|
"api_key = secrets.token_hex(5)\n",
|
||||||
|
"\n",
|
||||||
|
"# Write the API key to api_key.txt\n",
|
||||||
|
"with open('./api_key.txt', 'w') as f:\n",
|
||||||
|
" f.write(api_key)\n",
|
||||||
|
"print(f\"API Key generated: {api_key}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
||||||
"print(cmd)\n",
|
"print(cmd)\n",
|
||||||
|
@@ -9,13 +9,28 @@ const enableExtensions = true; //Enables support for TavernAI-extras project
|
|||||||
const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine.
|
const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine.
|
||||||
const allowKeysExposure = false; // If true, private API keys could be fetched to the frontend.
|
const 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 skipContentCheck = false; // If true, no new default content will be delivered to you.
|
||||||
|
const thumbnailsQuality = 95; // Quality of thumbnails. 0-100
|
||||||
|
|
||||||
// If true, Allows insecure settings for listen, whitelist, and authentication.
|
// 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.
|
// Change this setting only on "trusted networks". Do not change this value unless you are aware of the issues that can arise from changing this setting and configuring a insecure setting.
|
||||||
const securityOverride = false;
|
const securityOverride = false;
|
||||||
|
|
||||||
|
// Additional settings for extra modules / extensions
|
||||||
|
const extras = {
|
||||||
|
// Disables auto-download of models from the HuggingFace Hub.
|
||||||
|
// You will need to manually download the models and put them into the /cache folder.
|
||||||
|
disableAutoDownload: false,
|
||||||
|
// Text classification model for sentiment analysis. HuggingFace ID of a model in ONNX format.
|
||||||
|
classificationModel: 'Cohee/distilbert-base-uncased-go-emotions-onnx',
|
||||||
|
// Image captioning model. HuggingFace ID of a model in ONNX format.
|
||||||
|
captioningModel: 'Xenova/vit-gpt2-image-captioning',
|
||||||
|
// Feature extraction model. HuggingFace ID of a model in ONNX format.
|
||||||
|
embeddingModel: 'Xenova/all-mpnet-base-v2',
|
||||||
|
};
|
||||||
|
|
||||||
// Request overrides for additional headers
|
// Request overrides for additional headers
|
||||||
|
// Format is an array of objects:
|
||||||
|
// { hosts: [ "<url>" ], headers: { <header>: "<value>" } }
|
||||||
const requestOverrides = [];
|
const requestOverrides = [];
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -32,4 +47,6 @@ module.exports = {
|
|||||||
securityOverride,
|
securityOverride,
|
||||||
skipContentCheck,
|
skipContentCheck,
|
||||||
requestOverrides,
|
requestOverrides,
|
||||||
|
thumbnailsQuality,
|
||||||
|
extras,
|
||||||
};
|
};
|
||||||
|
@@ -75,9 +75,6 @@
|
|||||||
"always_force_name2": true,
|
"always_force_name2": true,
|
||||||
"user_prompt_bias": "",
|
"user_prompt_bias": "",
|
||||||
"show_user_prompt_bias": true,
|
"show_user_prompt_bias": true,
|
||||||
"multigen": false,
|
|
||||||
"multigen_first_chunk": 50,
|
|
||||||
"multigen_next_chunks": 30,
|
|
||||||
"markdown_escape_strings": "",
|
"markdown_escape_strings": "",
|
||||||
"fast_ui_mode": false,
|
"fast_ui_mode": false,
|
||||||
"avatar_style": 0,
|
"avatar_style": 0,
|
||||||
@@ -167,7 +164,6 @@
|
|||||||
"custom_stopping_strings_macro": true,
|
"custom_stopping_strings_macro": true,
|
||||||
"fuzzy_search": true,
|
"fuzzy_search": true,
|
||||||
"encode_tags": false,
|
"encode_tags": false,
|
||||||
"lazy_load": 100,
|
|
||||||
"ui_mode": 1
|
"ui_mode": 1
|
||||||
},
|
},
|
||||||
"extension_settings": {
|
"extension_settings": {
|
||||||
@@ -410,7 +406,6 @@
|
|||||||
"tfs": 1,
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
"single_line": false,
|
||||||
"use_stop_sequence": false,
|
|
||||||
"streaming_kobold": false,
|
"streaming_kobold": false,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -420,7 +415,12 @@
|
|||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"use_default_badwordsids": false,
|
||||||
|
"grammar": ""
|
||||||
},
|
},
|
||||||
"oai_settings": {
|
"oai_settings": {
|
||||||
"preset_settings_openai": "Default",
|
"preset_settings_openai": "Default",
|
||||||
|
@@ -9,4 +9,5 @@ services:
|
|||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- "./config:/home/node/app/config"
|
- "./config:/home/node/app/config"
|
||||||
|
- "./user:/home/node/app/public/user"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Initialize missing user files
|
# 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
|
for R in $RESOURCES; do
|
||||||
if [ ! -e "config/$R" ]; then
|
if [ ! -e "config/$R" ]; then
|
||||||
echo "Resource not found, copying from defaults: $R"
|
echo "Resource not found, copying from defaults: $R"
|
||||||
|
1197
package-lock.json
generated
1197
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -9,29 +9,27 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"csrf-csrf": "^2.2.3",
|
"csrf-csrf": "^2.2.3",
|
||||||
"device-detector-js": "^3.0.3",
|
"device-detector-js": "^3.0.3",
|
||||||
"exifreader": "^4.12.0",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"google-translate-api-browser": "^3.0.1",
|
"google-translate-api-browser": "^3.0.1",
|
||||||
|
"bing-translate-api": "^2.9.1",
|
||||||
"gpt3-tokenizer": "^1.1.5",
|
"gpt3-tokenizer": "^1.1.5",
|
||||||
"ip-matching": "^2.1.2",
|
"ip-matching": "^2.1.2",
|
||||||
"ipaddr.js": "^2.0.1",
|
"ipaddr.js": "^2.0.1",
|
||||||
"jimp": "^0.22.7",
|
"jimp": "^0.22.10",
|
||||||
"jquery": "^3.6.4",
|
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"node-fetch": "^2.6.11",
|
"node-fetch": "^2.6.11",
|
||||||
"open": "^8.4.2",
|
"open": "^8.4.2",
|
||||||
"piexifjs": "^1.0.6",
|
|
||||||
"png-chunk-text": "^1.0.0",
|
"png-chunk-text": "^1.0.0",
|
||||||
"png-chunks-encode": "^1.0.0",
|
"png-chunks-encode": "^1.0.0",
|
||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
"response-time": "^2.3.2",
|
"response-time": "^2.3.2",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
|
"sillytavern-transformers": "^2.7.3",
|
||||||
"simple-git": "^3.19.1",
|
"simple-git": "^3.19.1",
|
||||||
"uniqolor": "^1.1.0",
|
"vectra": "^0.2.2",
|
||||||
"webp-converter": "2.3.2",
|
|
||||||
"write-file-atomic": "^5.0.1",
|
"write-file-atomic": "^5.0.1",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0",
|
||||||
"yargs": "^17.7.1",
|
"yargs": "^17.7.1",
|
||||||
@@ -49,11 +47,12 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||||
},
|
},
|
||||||
"version": "1.10.2",
|
"version": "1.10.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"start-multi": "node server.js --disableCsrf",
|
"start-multi": "node server.js --disableCsrf",
|
||||||
"pkg": "pkg --compress Gzip --no-bytecode --public ."
|
"pkg": "pkg --compress Gzip --no-bytecode --public .",
|
||||||
|
"postinstall": "node post-install.js"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"sillytavern": "./server.js"
|
"sillytavern": "./server.js"
|
||||||
@@ -78,6 +77,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"jquery": "^3.6.4",
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
"pkg-fetch": "^3.5.2"
|
"pkg-fetch": "^3.5.2"
|
||||||
}
|
}
|
||||||
|
81
post-install.js
Normal file
81
post-install.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Scripts to be done before starting the server for the first time.
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the default config files if they don't exist yet.
|
||||||
|
*/
|
||||||
|
function createDefaultFiles() {
|
||||||
|
const files = {
|
||||||
|
settings: './public/settings.json',
|
||||||
|
bg_load: './public/css/bg_load.css',
|
||||||
|
config: './config.conf',
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const file of Object.values(files)) {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
const defaultFilePath = path.join('./default', path.parse(file).base);
|
||||||
|
fs.copyFileSync(defaultFilePath, file);
|
||||||
|
console.log(`Created default file: ${file}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`FATAL: Could not write default file: ${file}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MD5 hash of the given data.
|
||||||
|
* @param {Buffer} data Input data
|
||||||
|
* @returns {string} MD5 hash of the input data
|
||||||
|
*/
|
||||||
|
function getMd5Hash(data) {
|
||||||
|
return crypto
|
||||||
|
.createHash('md5')
|
||||||
|
.update(data)
|
||||||
|
.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the WASM binaries from the sillytavern-transformers package to the dist folder.
|
||||||
|
*/
|
||||||
|
function copyWasmFiles() {
|
||||||
|
if (!fs.existsSync('./dist')) {
|
||||||
|
fs.mkdirSync('./dist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const listDir = fs.readdirSync('./node_modules/sillytavern-transformers/dist');
|
||||||
|
|
||||||
|
for (const file of listDir) {
|
||||||
|
if (file.endsWith('.wasm')) {
|
||||||
|
const sourcePath = `./node_modules/sillytavern-transformers/dist/${file}`;
|
||||||
|
const targetPath = `./dist/${file}`;
|
||||||
|
|
||||||
|
// Don't copy if the file already exists and is the same checksum
|
||||||
|
if (fs.existsSync(targetPath)) {
|
||||||
|
const sourceChecksum = getMd5Hash(fs.readFileSync(sourcePath));
|
||||||
|
const targetChecksum = getMd5Hash(fs.readFileSync(targetPath));
|
||||||
|
|
||||||
|
if (sourceChecksum === targetChecksum) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(sourcePath, targetPath);
|
||||||
|
console.log(`${file} successfully copied to ./dist/${file}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Create default config files
|
||||||
|
createDefaultFiles();
|
||||||
|
// 2. Copy transformers WASM binaries from node_modules
|
||||||
|
copyWasmFiles();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1.15,
|
"temp": 1.15,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 0.95,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.8,
|
|
||||||
"rep_pen": 1.05,
|
"rep_pen": 1.05,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.8,
|
||||||
"rep_pen_slope": 7,
|
"rep_pen_slope": 7,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
5,
|
5,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.59,
|
"temp": 0.59,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.87,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.87,
|
||||||
"rep_pen_slope": 0.3,
|
"rep_pen_slope": 0.3,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.8,
|
"temp": 0.8,
|
||||||
"top_k": 100,
|
|
||||||
"top_p": 0.9,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 1,
|
|
||||||
"rep_pen": 1.15,
|
"rep_pen": 1.15,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 0.9,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 100,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 3.4,
|
"rep_pen_slope": 3.4,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.51,
|
"temp": 0.51,
|
||||||
"top_p": 1,
|
|
||||||
"top_k": 0,
|
|
||||||
"tfs": 0.99,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"rep_pen": 1.2,
|
"rep_pen": 1.2,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.99,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -8,7 +8,6 @@
|
|||||||
"typical": 1,
|
"typical": 1,
|
||||||
"tfs": 1,
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
0,
|
0,
|
||||||
@@ -17,5 +16,9 @@
|
|||||||
4,
|
4,
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.63,
|
"temp": 0.63,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 0.98,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.98,
|
|
||||||
"rep_pen": 1.05,
|
"rep_pen": 1.05,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 0.98,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.98,
|
||||||
"rep_pen_slope": 0.1,
|
"rep_pen_slope": 0.1,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
5,
|
5,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.7,
|
"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": 1.1,
|
||||||
"rep_pen_range": 1024,
|
"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,
|
"rep_pen_slope": 0.7,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
0
|
0
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.7,
|
"temp": 0.7,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.9,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 1024,
|
"rep_pen_range": 1024,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.9,
|
||||||
"rep_pen_slope": 0.7,
|
"rep_pen_slope": 0.7,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.66,
|
"temp": 0.66,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0.96,
|
|
||||||
"typical": 0.6,
|
|
||||||
"tfs": 1,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 1024,
|
"rep_pen_range": 1024,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0.96,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 0.6,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0.7,
|
"rep_pen_slope": 0.7,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
0,
|
0,
|
||||||
2,
|
2,
|
||||||
3
|
3
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.94,
|
"temp": 0.94,
|
||||||
"top_k": 12,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.94,
|
|
||||||
"rep_pen": 1.05,
|
"rep_pen": 1.05,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 12,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.94,
|
||||||
"rep_pen_slope": 0.2,
|
"rep_pen_slope": 0.2,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1.5,
|
"temp": 1.5,
|
||||||
"top_k": 85,
|
|
||||||
"top_p": 0.24,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 1,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 0.24,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 85,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1.05,
|
"temp": 1.05,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 0.95,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 1,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 1024,
|
"rep_pen_range": 1024,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0.7,
|
"rep_pen_slope": 0.7,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
24
public/KoboldAI Settings/Miro Bronze.settings
Normal file
24
public/KoboldAI Settings/Miro Bronze.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.06,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
|
"rep_pen_slope": 0.9,
|
||||||
|
"sampler_order": [
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"mirostat": 2,
|
||||||
|
"mirostat_tau": 9.61,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"grammar": ""
|
||||||
|
}
|
24
public/KoboldAI Settings/Miro Gold.settings
Normal file
24
public/KoboldAI Settings/Miro Gold.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.17,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
|
"rep_pen_slope": 0.9,
|
||||||
|
"sampler_order": [
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"mirostat": 2,
|
||||||
|
"mirostat_tau": 9.91,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"grammar": ""
|
||||||
|
}
|
24
public/KoboldAI Settings/Miro Silver.settings
Normal file
24
public/KoboldAI Settings/Miro Silver.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.17,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
|
"rep_pen_slope": 0.9,
|
||||||
|
"sampler_order": [
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"mirostat": 2,
|
||||||
|
"mirostat_tau": 9.62,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"grammar": ""
|
||||||
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1.07,
|
"temp": 1.07,
|
||||||
"top_k": 100,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.93,
|
|
||||||
"rep_pen": 1.05,
|
"rep_pen": 1.05,
|
||||||
"rep_pen_range": 404,
|
"rep_pen_range": 404,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 100,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.93,
|
||||||
"rep_pen_slope": 0.8,
|
"rep_pen_slope": 0.8,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.44,
|
"temp": 0.44,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.9,
|
|
||||||
"rep_pen": 1.15,
|
"rep_pen": 1.15,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.9,
|
||||||
"rep_pen_slope": 6.8,
|
"rep_pen_slope": 6.8,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1.35,
|
"temp": 1.35,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 0.69,
|
|
||||||
"rep_pen": 1.15,
|
"rep_pen": 1.15,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 0.69,
|
||||||
"rep_pen_slope": 0.1,
|
"rep_pen_slope": 0.1,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 1,
|
"temp": 1,
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 0.95,
|
|
||||||
"top_a": 0,
|
|
||||||
"typical": 1,
|
|
||||||
"tfs": 1,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 600,
|
"rep_pen_range": 600,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -8,7 +8,6 @@
|
|||||||
"typical": 1,
|
"typical": 1,
|
||||||
"tfs": 1,
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
0,
|
0,
|
||||||
@@ -17,5 +16,9 @@
|
|||||||
4,
|
4,
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"temp": 0.72,
|
"temp": 0.72,
|
||||||
"tfs": 1,
|
|
||||||
"top_a": 0,
|
|
||||||
"top_k": 0,
|
|
||||||
"top_p": 0.73,
|
|
||||||
"typical": 1,
|
|
||||||
"rep_pen": 1.1,
|
"rep_pen": 1.1,
|
||||||
"rep_pen_range": 2048,
|
"rep_pen_range": 2048,
|
||||||
|
"top_p": 0.73,
|
||||||
|
"top_a": 0,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical": 1,
|
||||||
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0.2,
|
"rep_pen_slope": 0.2,
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
@@ -16,5 +16,9 @@
|
|||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
4
|
4
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -8,7 +8,6 @@
|
|||||||
"typical": 1,
|
"typical": 1,
|
||||||
"tfs": 0.95,
|
"tfs": 0.95,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
0,
|
0,
|
||||||
@@ -17,5 +16,9 @@
|
|||||||
4,
|
4,
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -8,7 +8,6 @@
|
|||||||
"typical": 1,
|
"typical": 1,
|
||||||
"tfs": 1,
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
0,
|
0,
|
||||||
@@ -17,5 +16,9 @@
|
|||||||
4,
|
4,
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
@@ -8,7 +8,6 @@
|
|||||||
"typical": 1,
|
"typical": 1,
|
||||||
"tfs": 1,
|
"tfs": 1,
|
||||||
"rep_pen_slope": 0,
|
"rep_pen_slope": 0,
|
||||||
"single_line": false,
|
|
||||||
"sampler_order": [
|
"sampler_order": [
|
||||||
6,
|
6,
|
||||||
0,
|
0,
|
||||||
@@ -17,5 +16,9 @@
|
|||||||
4,
|
4,
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
]
|
],
|
||||||
|
"mirostat": 0,
|
||||||
|
"mirostat_tau": 5,
|
||||||
|
"mirostat_eta": 0.1,
|
||||||
|
"grammar": ""
|
||||||
}
|
}
|
24
public/TextGen Settings/Miro Bronze.settings
Normal file
24
public/TextGen Settings/Miro Bronze.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.06,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"tfs": 1,
|
||||||
|
"epsilon_cutoff": 0,
|
||||||
|
"eta_cutoff": 0,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"no_repeat_ngram_size": 0,
|
||||||
|
"penalty_alpha": 0,
|
||||||
|
"num_beams": 1,
|
||||||
|
"length_penalty": 1,
|
||||||
|
"min_length": 0,
|
||||||
|
"encoder_rep_pen": 1,
|
||||||
|
"do_sample": true,
|
||||||
|
"early_stopping": false,
|
||||||
|
"mirostat_mode": 2,
|
||||||
|
"mirostat_tau": 9.61,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"rep_pen_size": 0
|
||||||
|
}
|
24
public/TextGen Settings/Miro Gold.settings
Normal file
24
public/TextGen Settings/Miro Gold.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.17,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"tfs": 1,
|
||||||
|
"epsilon_cutoff": 0,
|
||||||
|
"eta_cutoff": 0,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"no_repeat_ngram_size": 0,
|
||||||
|
"penalty_alpha": 0,
|
||||||
|
"num_beams": 1,
|
||||||
|
"length_penalty": 1,
|
||||||
|
"min_length": 0,
|
||||||
|
"encoder_rep_pen": 1,
|
||||||
|
"do_sample": true,
|
||||||
|
"early_stopping": false,
|
||||||
|
"mirostat_mode": 2,
|
||||||
|
"mirostat_tau": 9.91,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"rep_pen_size": 0
|
||||||
|
}
|
24
public/TextGen Settings/Miro Silver.settings
Normal file
24
public/TextGen Settings/Miro Silver.settings
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"temp": 1.17,
|
||||||
|
"top_p": 1,
|
||||||
|
"top_k": 0,
|
||||||
|
"typical_p": 1,
|
||||||
|
"top_a": 0,
|
||||||
|
"tfs": 1,
|
||||||
|
"epsilon_cutoff": 0,
|
||||||
|
"eta_cutoff": 0,
|
||||||
|
"rep_pen": 1,
|
||||||
|
"rep_pen_range": 0,
|
||||||
|
"no_repeat_ngram_size": 0,
|
||||||
|
"penalty_alpha": 0,
|
||||||
|
"num_beams": 1,
|
||||||
|
"length_penalty": 1,
|
||||||
|
"min_length": 0,
|
||||||
|
"encoder_rep_pen": 1,
|
||||||
|
"do_sample": true,
|
||||||
|
"early_stopping": false,
|
||||||
|
"mirostat_mode": 2,
|
||||||
|
"mirostat_tau": 9.62,
|
||||||
|
"mirostat_eta": 1,
|
||||||
|
"rep_pen_size": 0
|
||||||
|
}
|
6
public/context/Adventure.json
Normal file
6
public/context/Adventure.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": "Adventure"
|
||||||
|
}
|
6
public/context/ChatML.json
Normal file
6
public/context/ChatML.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "<|im_start|>system\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}<|im_end|>",
|
||||||
|
"chat_start": "",
|
||||||
|
"example_separator": "",
|
||||||
|
"name": "ChatML"
|
||||||
|
}
|
6
public/context/Libra-32B.json
Normal file
6
public/context/Libra-32B.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "### Instruction:\nWrite {{char}}'s next reply in this roleplay with {{user}}. Use the provided character sheet and example dialogue for formatting direction and character speech patterns.\n\n{{#if system}}{{system}}\n\n{{/if}}### Character Sheet:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||||
|
"chat_start": "### START ROLEPLAY:",
|
||||||
|
"example_separator": "### Example:",
|
||||||
|
"name": "Libra-32B"
|
||||||
|
}
|
6
public/context/Lightning 1.1.json
Normal file
6
public/context/Lightning 1.1.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}",
|
||||||
|
"chat_start": "This is the history of the roleplay:",
|
||||||
|
"example_separator": "Example of an interaction:",
|
||||||
|
"name": "Lightning 1.1"
|
||||||
|
}
|
6
public/context/Mistral.json
Normal file
6
public/context/Mistral.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "[INST] {{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}[/INST]",
|
||||||
|
"chat_start": "",
|
||||||
|
"example_separator": "Examples:",
|
||||||
|
"name": "Mistral"
|
||||||
|
}
|
6
public/context/OldDefault.json
Normal file
6
public/context/OldDefault.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Circumstances and context of the dialogue: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||||
|
"chat_start": "\nThen the roleplay chat between {{user}} and {{char}} begins.\n",
|
||||||
|
"example_separator": "This is how {{char}} should talk",
|
||||||
|
"name": "OldDefault"
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Pygmalion",
|
"name": "Pygmalion",
|
||||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{{char}}}'s Persona: {{description}}\n{{/if}}{{#if personality}}Personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||||
"chat_start": "<START>",
|
"chat_start": "",
|
||||||
"example_separator": "<START>"
|
"example_separator": ""
|
||||||
}
|
}
|
||||||
|
6
public/context/Story.json
Normal file
6
public/context/Story.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||||
|
"chat_start": "",
|
||||||
|
"example_separator": "",
|
||||||
|
"name": "Story"
|
||||||
|
}
|
@@ -94,3 +94,117 @@ input.extension_missing[type="checkbox"] {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fixes order of settings for extensions */
|
||||||
|
#extensions_settings,
|
||||||
|
#extensions_settings2 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** LEFT COLUMN **/
|
||||||
|
#extensions_settings>.expression_settings {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>.background_settings {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>.sd_settings {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>#tts_settings {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>#rvc_settings {
|
||||||
|
order: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>.objective-settings {
|
||||||
|
order: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>#speech_recognition_settings {
|
||||||
|
order: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>#audio_settings {
|
||||||
|
order: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings>#assets_ui {
|
||||||
|
order: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RIGHT COLUMN **/
|
||||||
|
#extensions_settings2>.translation_settings {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.caption_settings {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.quickReplySettings {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.idle-settings {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>#memory_settings {
|
||||||
|
order: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.hypebot_settings {
|
||||||
|
order: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.regex_settings {
|
||||||
|
order: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.vectors_settings {
|
||||||
|
order: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.chromadb_settings {
|
||||||
|
order: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensions_settings2>.randomizer_settings {
|
||||||
|
order: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** WAND MENU **/
|
||||||
|
#extensionsMenu>#ttsExtensionMenuItem {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#sd_gen {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#send_picture {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#token_counter {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#objective-task-manual-check-menu-item {
|
||||||
|
order: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#roll_dice {
|
||||||
|
order: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extensionsMenu>#translate_chat {
|
||||||
|
order: 7;
|
||||||
|
}
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
max-width: 90svw;
|
max-width: 90svw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.world_entry_thin_controls,
|
/* .world_entry_thin_controls, */
|
||||||
#persona-management-block,
|
#persona-management-block,
|
||||||
#character_popup .flex-container {
|
#character_popup .flex-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -63,6 +63,15 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.world_entry .inline-drawer-toggle {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#worldInfoScanningCheckboxes {
|
||||||
|
flex-flow: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -70,6 +79,10 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.world_entry_form_control {
|
||||||
|
/* width: 100%; */
|
||||||
|
}
|
||||||
|
|
||||||
.drawer-content {
|
.drawer-content {
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -78,7 +91,7 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
border: 1px solid var(--grey30);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#select_chat_popup {
|
#select_chat_popup {
|
||||||
@@ -91,7 +104,6 @@
|
|||||||
#top-settings-holder,
|
#top-settings-holder,
|
||||||
#top-bar {
|
#top-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding-top: 3px;
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
width: 100svw;
|
width: 100svw;
|
||||||
}
|
}
|
||||||
@@ -114,18 +126,32 @@
|
|||||||
/* ,
|
/* ,
|
||||||
#world_popup */
|
#world_popup */
|
||||||
{
|
{
|
||||||
max-height: calc(100vh - 36px);
|
/*max-height: calc(100vh - 36px);
|
||||||
max-height: calc(100svh - 36px);
|
max-height: calc(100svh - 36px);*/
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
resize: none !important;
|
resize: none !important;
|
||||||
top: 36px;
|
top: var(--topBarBlockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wi-settings {
|
.wi-settings {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WIEntryTitleAndStatus,
|
||||||
|
.WIEntryHeaderControls {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#WIEntryHeaderTitlesPC {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WIEntryHeaderTitleMobile {
|
||||||
|
display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#character_popup,
|
#character_popup,
|
||||||
@@ -135,15 +161,15 @@
|
|||||||
|
|
||||||
#character_popup,
|
#character_popup,
|
||||||
#send_form {
|
#send_form {
|
||||||
border: 1px solid var(--grey30);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||||
max-width: 100dvw;
|
max-width: 100dvw;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat {
|
#chat {
|
||||||
border-left: 1px solid var(--grey30);
|
border-left: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-right: 1px solid var(--grey30);
|
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-bottom: 1px solid var(--grey30);
|
border-bottom: 1px solid var(--SmartThemeBorderColor);
|
||||||
align-items: start;
|
align-items: start;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -161,6 +187,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#showRawPrompt,
|
#showRawPrompt,
|
||||||
|
#copyPromptToClipboard,
|
||||||
#groupCurrentMemberPopoutButton {
|
#groupCurrentMemberPopoutButton {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -175,11 +202,11 @@
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
border-left: 1px solid var(--grey30);
|
border-left: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-right: 1px solid var(--grey30);
|
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-bottom: 1px solid var(--grey30);
|
border-bottom: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-radius: 0 0 20px 20px;
|
border-radius: 0 0 20px 20px;
|
||||||
top: 36px !important;
|
top: var(--topBarBlockSize) !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||||
}
|
}
|
||||||
@@ -271,6 +298,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1001px) {
|
@media screen and (min-width: 1001px) {
|
||||||
|
|
||||||
#PygOverrides,
|
#PygOverrides,
|
||||||
#ContextFormatting,
|
#ContextFormatting,
|
||||||
#UI-Theme-Block,
|
#UI-Theme-Block,
|
||||||
@@ -310,8 +338,7 @@
|
|||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
max-width: 50vh;
|
max-width: 90vw;
|
||||||
width: 50vw;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
filter: drop-shadow(2px 2px 2px #51515199);
|
filter: drop-shadow(2px 2px 2px #51515199);
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
grid-column-end: 4;
|
grid-column-end: 4;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
background-image: linear-gradient(90deg, var(--transparent), var(--white30a), var(--transparent));
|
background-image: linear-gradient(90deg, var(--transparent), var(--SmartThemeBorderColor), var(--transparent));
|
||||||
min-height: 1px;
|
min-height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt {
|
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt .prompt_manager_prompt_controls {
|
#completion_prompt_manager #completion_prompt_manager_list li.completion_prompt_manager_prompt .prompt_manager_prompt_controls {
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
#completion_prompt_manager_popup .completion_prompt_manager_prompt {
|
#completion_prompt_manager_popup .completion_prompt_manager_prompt {
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#completion_prompt_manager_popup .completion_prompt_manager_popup_header {
|
#completion_prompt_manager_popup .completion_prompt_manager_popup_header {
|
||||||
@@ -265,7 +265,7 @@
|
|||||||
top: var(--topBarBlockSize);
|
top: var(--topBarBlockSize);
|
||||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border: 1px solid #333333;
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
z-index: 3010 !important;
|
z-index: 3010 !important;
|
||||||
border-radius: 0 0 20px 20px;
|
border-radius: 0 0 20px 20px;
|
||||||
|
@@ -14,7 +14,8 @@
|
|||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
color: rgb(188, 193, 200, 1);
|
color: rgb(188, 193, 200, 1);
|
||||||
border: 1px solid #333;
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
|
;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -61,7 +62,8 @@
|
|||||||
#rm_group_add_members {
|
#rm_group_add_members {
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
border: 1px solid grey;
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
|
;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: var(--black30a);
|
background-color: var(--black30a);
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
/* Customize the dropdown */
|
/* Customize the dropdown */
|
||||||
.select2-dropdown {
|
.select2-dropdown {
|
||||||
background-color: var(--SmartThemeBlurTintColor);
|
background-color: var(--SmartThemeBlurTintColor);
|
||||||
border: 1px solid var(--white30a) !important;
|
border: 1px solid var(--SmartThemeBorderColor) !important;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 0 5px black;
|
box-shadow: 0 0 5px black;
|
||||||
text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
|
text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
|
||||||
@@ -19,11 +19,24 @@
|
|||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-search__field {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
line-height: revert;
|
||||||
|
padding-left: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-results>.select2-results__options {
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
.select2-container .select2-selection--multiple .select2-selection__choice__remove {
|
.select2-container .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
padding: revert;
|
padding: revert;
|
||||||
border-right: 1px solid var(--white30a);
|
border-right: 1px solid var(--SmartThemeBorderColor);
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container .select2-selection--multiple .select2-selection__choice__display {
|
.select2-container .select2-selection--multiple .select2-selection__choice__display {
|
||||||
@@ -34,7 +47,7 @@
|
|||||||
.select2-search__field {
|
.select2-search__field {
|
||||||
background-color: var(--black30a);
|
background-color: var(--black30a);
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
@@ -58,27 +71,30 @@
|
|||||||
background-color: var(--SmartThemeBodyColor);
|
background-color: var(--SmartThemeBodyColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container .select2-selection--multiple {
|
.select2-container .select2-selection--multiple,
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
background-color: var(--black30a);
|
background-color: var(--black30a);
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container.select2-container--focus .select2-selection--multiple {
|
.select2-container.select2-container--focus .select2-selection--multiple,
|
||||||
border: 1px solid var(--white30a);
|
.select2-container.select2-container--focus .select2-selection--single {
|
||||||
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container .select2-selection--multiple .select2-selection__choice {
|
.select2-container .select2-selection--multiple .select2-selection__choice,
|
||||||
|
.select2-container .select2-selection--single .select2-selection__choice {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
background-color: var(--black30a);
|
background-color: var(--black30a);
|
||||||
border-color: var(--white30a);
|
border-color: var(--SmartThemeBorderColor);
|
||||||
font-size: calc(var(--mainFontSize) - 5%);
|
font-size: calc(var(--mainFontSize) - 5%);
|
||||||
text-shadow: none !important;
|
text-shadow: none !important;
|
||||||
}
|
}
|
||||||
@@ -114,12 +130,13 @@
|
|||||||
margin-top: -7px;
|
margin-top: -7px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
background-color: var(--SmartThemeBlurTintColor);
|
background-color: var(--SmartThemeBlurTintColor);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container .select2-selection--multiple .select2-selection__choice__remove {
|
.select2-container .select2-selection--multiple .select2-selection__choice__remove,
|
||||||
|
.select2-container .select2-selection--single .select2-selection__choice__remove {
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,10 @@
|
|||||||
color: var(--fullred);
|
color: var(--fullred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-t-0 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.m-t-1 {
|
.m-t-1 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
@@ -99,6 +103,10 @@
|
|||||||
align-self: start;
|
align-self: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap3px {
|
||||||
|
gap: 3px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.gap5px {
|
.gap5px {
|
||||||
gap: 5px !important;
|
gap: 5px !important;
|
||||||
}
|
}
|
||||||
@@ -120,6 +128,10 @@
|
|||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.width100px {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.widthUnset {
|
.widthUnset {
|
||||||
width: unset;
|
width: unset;
|
||||||
}
|
}
|
||||||
@@ -147,6 +159,10 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textAlignCenter {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.margin-right-10px {
|
.margin-right-10px {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
@@ -230,6 +246,10 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.padding0 {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.padding5 {
|
.padding5 {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
@@ -262,6 +282,10 @@
|
|||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flexFlowRow {
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
.wideMinContent {
|
.wideMinContent {
|
||||||
width: min-content;
|
width: min-content;
|
||||||
}
|
}
|
||||||
@@ -362,6 +386,11 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:disabled,
|
||||||
|
textarea:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.debug-red {
|
.debug-red {
|
||||||
border: 1px solid red !important;
|
border: 1px solid red !important;
|
||||||
}
|
}
|
||||||
@@ -390,6 +419,11 @@
|
|||||||
font-size: calc(var(--mainFontSize) * 0.6) !important;
|
font-size: calc(var(--mainFontSize) * 0.6) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paddingBottom5px {
|
||||||
|
padding: unset;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.paddingTopBot5 {
|
.paddingTopBot5 {
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
@@ -109,6 +109,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags_inline .tag {
|
.tags_inline .tag {
|
||||||
@@ -128,6 +129,13 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
filter: brightness(0.8);
|
filter: brightness(0.8);
|
||||||
|
transition: opacity 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rm_tag_filter .tag:hover {
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
filter: brightness(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags_view,
|
.tags_view,
|
||||||
|
@@ -117,7 +117,7 @@ body.big-avatars .avatar img {
|
|||||||
height: 90px;
|
height: 90px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
object-position: center;
|
||||||
border: 1px solid var(--black30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ body.bubblechat .mes {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: var(--SmartThemeBotMesBlurTintColor);
|
background-color: var(--SmartThemeBotMesBlurTintColor);
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
border: 1px solid var(--white30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.bubblechat .mes[is_user="true"] {
|
body.bubblechat .mes[is_user="true"] {
|
||||||
@@ -257,31 +257,16 @@ body.no-blur #bg_custom {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not(.bubblechat).no-blur #chat,
|
|
||||||
body.no-blur #top-bar,
|
body.no-blur #top-bar,
|
||||||
body.no-blur #send_form {
|
body.no-blur #send_form {
|
||||||
background-color: var(--SmartThemeBlurTintColor) !important;
|
background-color: var(--SmartThemeBlurTintColor) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.no-blur #options,
|
|
||||||
body.no-blur .ui-widget-content,
|
|
||||||
body.no-blur #floatingPrompt,
|
|
||||||
body.no-blur #extensionsMenu,
|
|
||||||
body.no-blur .list-group,
|
|
||||||
body.no-blur #character_popup,
|
|
||||||
body.no-blur #world_popup,
|
|
||||||
body.no-blur #dialogue_popup,
|
|
||||||
body.no-blur #select_chat_popup,
|
|
||||||
body.no-blur .drawer-content,
|
|
||||||
body.no-blur .select2-results__options {
|
|
||||||
background-color: black !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wAIfu mode*/
|
/* wAIfu mode*/
|
||||||
|
|
||||||
body.waifuMode #top-bar {
|
body.waifuMode #top-bar {
|
||||||
border-radius: 0 0 20px 20px;
|
border-radius: 0 0 20px 20px;
|
||||||
border: 1px solid var(--grey30a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.waifuMode #sheld {
|
body.waifuMode #sheld {
|
||||||
@@ -292,7 +277,7 @@ body.waifuMode #sheld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.waifuMode #chat {
|
body.waifuMode #chat {
|
||||||
border-top: 1px solid var(--grey30a);
|
border-top: 1px solid var(--SmartThemeBorderColor);
|
||||||
border-radius: 20px 20px 0 0;
|
border-radius: 20px 20px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,6 +338,7 @@ body.movingUI #groupMemberListPopout {
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*No Text Shadows Mode*/
|
/*No Text Shadows Mode*/
|
||||||
@@ -360,3 +346,11 @@ body.movingUI #groupMemberListPopout {
|
|||||||
body.noShadows * {
|
body.noShadows * {
|
||||||
text-shadow: none !important;
|
text-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.expandMessageActions .mes .mes_buttons .extraMesButtons {
|
||||||
|
display: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.expandMessageActions .mes .mes_buttons .extraMesButtonsHint {
|
||||||
|
display: none !important;
|
||||||
|
}
|
@@ -101,7 +101,7 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
min-height: 32px;
|
min-height: calc(var(--mainFontSize) + 13px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete_entry_button {
|
.delete_entry_button {
|
||||||
@@ -157,6 +157,37 @@
|
|||||||
width: 10em;
|
width: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world_info_search {
|
#world_info_search,
|
||||||
width: 10em;
|
#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;
|
||||||
}
|
}
|
603
public/i18n.json
603
public/i18n.json
File diff suppressed because it is too large
Load Diff
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 |
1272
public/index.html
1272
public/index.html
File diff suppressed because it is too large
Load Diff
18
public/instruct/Adventure.json
Normal file
18
public/instruct/Adventure.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"system_prompt": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]",
|
||||||
|
"input_sequence": "",
|
||||||
|
"output_sequence": "",
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"system_sequence": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"separator_sequence": "",
|
||||||
|
"wrap": true,
|
||||||
|
"macro": true,
|
||||||
|
"names": false,
|
||||||
|
"names_force_groups": false,
|
||||||
|
"activation_regex": "",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"first_output_sequence": "",
|
||||||
|
"name": "Adventure"
|
||||||
|
}
|
17
public/instruct/ChatML.json
Normal file
17
public/instruct/ChatML.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"wrap": false,
|
||||||
|
"names": true,
|
||||||
|
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"input_sequence": "<|im_start|>user\n",
|
||||||
|
"output_sequence": "<|im_end|>\n<|im_start|>assistant\n",
|
||||||
|
"separator_sequence": "<|im_end|>\n",
|
||||||
|
"macro": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"activation_regex": "",
|
||||||
|
"first_output_sequence": "<|im_start|>assistant\n",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"name": "ChatML"
|
||||||
|
}
|
17
public/instruct/Libra-32B.json
Normal file
17
public/instruct/Libra-32B.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"wrap": true,
|
||||||
|
"names": true,
|
||||||
|
"system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"input_sequence": "",
|
||||||
|
"output_sequence": "",
|
||||||
|
"separator_sequence": "",
|
||||||
|
"macro": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"last_output_sequence": "\n### Response:",
|
||||||
|
"activation_regex": "",
|
||||||
|
"first_output_sequence": "",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"name": "Libra-32B"
|
||||||
|
}
|
18
public/instruct/Lightning 1.1.json
Normal file
18
public/instruct/Lightning 1.1.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"wrap": true,
|
||||||
|
"names": false,
|
||||||
|
"system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n",
|
||||||
|
"system_sequence": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"input_sequence": "### Instruction:",
|
||||||
|
"output_sequence": "### Response: (length = unlimited)",
|
||||||
|
"separator_sequence": "",
|
||||||
|
"macro": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"first_output_sequence": "",
|
||||||
|
"activation_regex": "",
|
||||||
|
"name": "Lightning 1.1"
|
||||||
|
}
|
17
public/instruct/Mistral.json
Normal file
17
public/instruct/Mistral.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"wrap": false,
|
||||||
|
"names": true,
|
||||||
|
"system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"input_sequence": "[INST] ",
|
||||||
|
"output_sequence": " [/INST]\n",
|
||||||
|
"separator_sequence": "\n",
|
||||||
|
"macro": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"activation_regex": "",
|
||||||
|
"first_output_sequence": "\n",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"name": "Mistral"
|
||||||
|
}
|
17
public/instruct/Pygmalion.json
Normal file
17
public/instruct/Pygmalion.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "Pygmalion",
|
||||||
|
"system_prompt": "Enter RP mode. You shall reply to {{user}} while staying in character. Your responses must be detailed, creative, immersive, and drive the scenario forward. You will follow {{char}}'s persona.",
|
||||||
|
"input_sequence": "<|user|>",
|
||||||
|
"output_sequence": "<|model|>",
|
||||||
|
"first_output_sequence": "",
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"system_sequence_prefix": "<|system|>",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"stop_sequence": "<|user|>",
|
||||||
|
"separator_sequence": "",
|
||||||
|
"wrap": false,
|
||||||
|
"macro": true,
|
||||||
|
"names": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"activation_regex": ""
|
||||||
|
}
|
18
public/instruct/Story.json
Normal file
18
public/instruct/Story.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"system_prompt": "",
|
||||||
|
"input_sequence": "",
|
||||||
|
"output_sequence": "",
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"system_sequence": "",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"separator_sequence": "",
|
||||||
|
"wrap": true,
|
||||||
|
"macro": true,
|
||||||
|
"names": false,
|
||||||
|
"names_force_groups": false,
|
||||||
|
"activation_regex": "",
|
||||||
|
"system_sequence_prefix": "",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"first_output_sequence": "",
|
||||||
|
"name": "Story"
|
||||||
|
}
|
17
public/instruct/Synthia.json
Normal file
17
public/instruct/Synthia.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"wrap": false,
|
||||||
|
"names": false,
|
||||||
|
"system_prompt": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation.",
|
||||||
|
"system_sequence_prefix": "SYSTEM: ",
|
||||||
|
"stop_sequence": "",
|
||||||
|
"input_sequence": "USER: ",
|
||||||
|
"output_sequence": "\nASSISTANT: ",
|
||||||
|
"separator_sequence": "\n",
|
||||||
|
"macro": true,
|
||||||
|
"names_force_groups": true,
|
||||||
|
"last_output_sequence": "",
|
||||||
|
"activation_regex": "",
|
||||||
|
"first_output_sequence": "ASSISTANT: ",
|
||||||
|
"system_sequence_suffix": "",
|
||||||
|
"name": "Synthia"
|
||||||
|
}
|
25
public/lib/select2-search-placeholder.js
Normal file
25
public/lib/select2-search-placeholder.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
(function($) {
|
||||||
|
|
||||||
|
var Defaults = $.fn.select2.amd.require('select2/defaults');
|
||||||
|
|
||||||
|
$.extend(Defaults.defaults, {
|
||||||
|
searchInputPlaceholder: '',
|
||||||
|
searchInputCssClass: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
var SearchDropdown = $.fn.select2.amd.require('select2/dropdown/search');
|
||||||
|
|
||||||
|
var _renderSearchDropdown = SearchDropdown.prototype.render;
|
||||||
|
|
||||||
|
SearchDropdown.prototype.render = function(decorated) {
|
||||||
|
|
||||||
|
// invoke parent method
|
||||||
|
var $rendered = _renderSearchDropdown.apply(this, Array.prototype.slice.apply(arguments));
|
||||||
|
|
||||||
|
this.$search.attr('placeholder', this.options.get('searchInputPlaceholder'));
|
||||||
|
this.$search.addClass(this.options.get('searchInputCssClass'));
|
||||||
|
|
||||||
|
return $rendered;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(window.jQuery);
|
939
public/script.js
939
public/script.js
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ const registerPromptManagerMigration = () => {
|
|||||||
* Represents a prompt.
|
* Represents a prompt.
|
||||||
*/
|
*/
|
||||||
class Prompt {
|
class Prompt {
|
||||||
identifier; role; content; name; system_prompt;
|
identifier; role; content; name; system_prompt; position;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Prompt instance.
|
* Create a new Prompt instance.
|
||||||
@@ -71,13 +71,15 @@ class Prompt {
|
|||||||
* @param {string} param0.content - The content of the prompt.
|
* @param {string} param0.content - The content of the prompt.
|
||||||
* @param {string} param0.name - The name of the prompt.
|
* @param {string} param0.name - The name of the prompt.
|
||||||
* @param {boolean} param0.system_prompt - Indicates if the prompt is a system prompt.
|
* @param {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.
|
||||||
*/
|
*/
|
||||||
constructor({ identifier, role, content, name, system_prompt } = {}) {
|
constructor({ identifier, role, content, name, system_prompt, position } = {}) {
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.system_prompt = system_prompt;
|
this.system_prompt = system_prompt;
|
||||||
|
this.position = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ import {
|
|||||||
SECRET_KEYS,
|
SECRET_KEYS,
|
||||||
secret_state,
|
secret_state,
|
||||||
} from "./secrets.js";
|
} from "./secrets.js";
|
||||||
import { debounce, delay, getStringHash, waitUntilCondition } from "./utils.js";
|
import { debounce, delay, getStringHash, isUrlOrAPIKey, waitUntilCondition } from "./utils.js";
|
||||||
import { chat_completion_sources, oai_settings } from "./openai.js";
|
import { chat_completion_sources, oai_settings } from "./openai.js";
|
||||||
import { getTokenCount } from "./tokenizers.js";
|
import { getTokenCount } from "./tokenizers.js";
|
||||||
|
|
||||||
@@ -210,10 +210,12 @@ $("#character_popup").on("input", function () { countTokensDebounced(); });
|
|||||||
//function:
|
//function:
|
||||||
export function RA_CountCharTokens() {
|
export function RA_CountCharTokens() {
|
||||||
let total_tokens = 0;
|
let total_tokens = 0;
|
||||||
|
let permanent_tokens = 0;
|
||||||
|
|
||||||
$('[data-token-counter]').each(function () {
|
$('[data-token-counter]').each(function () {
|
||||||
const counter = $(this);
|
const counter = $(this);
|
||||||
const input = $(document.getElementById(counter.data('token-counter')));
|
const input = $(document.getElementById(counter.data('token-counter')));
|
||||||
|
const isPermanent = counter.data('token-permanent') === true;
|
||||||
const value = String(input.val());
|
const value = String(input.val());
|
||||||
|
|
||||||
if (input.length === 0) {
|
if (input.length === 0) {
|
||||||
@@ -230,10 +232,12 @@ export function RA_CountCharTokens() {
|
|||||||
|
|
||||||
if (input.data('last-value-hash') === valueHash) {
|
if (input.data('last-value-hash') === valueHash) {
|
||||||
total_tokens += Number(counter.text());
|
total_tokens += Number(counter.text());
|
||||||
|
permanent_tokens += isPermanent ? Number(counter.text()) : 0;
|
||||||
} else {
|
} else {
|
||||||
const tokens = getTokenCount(value);
|
const tokens = getTokenCount(value);
|
||||||
counter.text(tokens);
|
counter.text(tokens);
|
||||||
total_tokens += tokens;
|
total_tokens += tokens;
|
||||||
|
permanent_tokens += isPermanent ? tokens : 0;
|
||||||
input.data('last-value-hash', valueHash);
|
input.data('last-value-hash', valueHash);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -242,6 +246,7 @@ export function RA_CountCharTokens() {
|
|||||||
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
||||||
const showWarning = (total_tokens > tokenLimit);
|
const showWarning = (total_tokens > tokenLimit);
|
||||||
$('#result_info_total_tokens').text(total_tokens);
|
$('#result_info_total_tokens').text(total_tokens);
|
||||||
|
$('#result_info_permanent_tokens').text(permanent_tokens);
|
||||||
$('#result_info_text').toggleClass('neutral_warning', showWarning);
|
$('#result_info_text').toggleClass('neutral_warning', showWarning);
|
||||||
$('#chartokenwarning').toggle(showWarning);
|
$('#chartokenwarning').toggle(showWarning);
|
||||||
}
|
}
|
||||||
@@ -274,10 +279,16 @@ export async function favsToHotswap() {
|
|||||||
const entities = getEntitiesList({ doFilter: false });
|
const entities = getEntitiesList({ doFilter: false });
|
||||||
const container = $('#right-nav-panel .hotswap');
|
const container = $('#right-nav-panel .hotswap');
|
||||||
const template = $('#hotswap_template .hotswapAvatar');
|
const template = $('#hotswap_template .hotswapAvatar');
|
||||||
container.empty();
|
const DEFAULT_COUNT = 6;
|
||||||
const maxCount = 6;
|
const WIDTH_PER_ITEM = 60; // 50px + 5px gap + 5px padding
|
||||||
|
const containerWidth = container.outerWidth();
|
||||||
|
const maxCount = containerWidth > 0 ? Math.floor(containerWidth / WIDTH_PER_ITEM) : DEFAULT_COUNT;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
const newContainer = container.clone();
|
||||||
|
newContainer.empty();
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (count >= maxCount) {
|
if (count >= maxCount) {
|
||||||
break;
|
break;
|
||||||
@@ -310,23 +321,39 @@ export async function favsToHotswap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCharacter) {
|
if (isCharacter) {
|
||||||
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
|
const imgLoadPromise = new Promise((resolve) => {
|
||||||
$(slot).find('img').attr('src', avatarUrl);
|
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
|
||||||
$(slot).attr('title', entity.item.avatar);
|
$(slot).find('img').attr('src', avatarUrl).on('load', resolve);
|
||||||
|
$(slot).attr('title', entity.item.avatar);
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the image doesn't load in 500ms, resolve the promise anyway
|
||||||
|
promises.push(Promise.race([imgLoadPromise, delay(500)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$(slot).css('cursor', 'pointer');
|
$(slot).css('cursor', 'pointer');
|
||||||
container.append(slot);
|
newContainer.append(slot);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// there are 6 slots in total,
|
// don't fill leftover spaces with avatar placeholders
|
||||||
if (count < maxCount) { //if any are left over
|
// just evenly space the selected avatars instead
|
||||||
|
/*
|
||||||
|
if (count < maxCount) { //if any space is left over
|
||||||
let leftOverSlots = maxCount - count;
|
let leftOverSlots = maxCount - count;
|
||||||
for (let i = 1; i <= leftOverSlots; i++) {
|
for (let i = 1; i <= leftOverSlots; i++) {
|
||||||
container.append(template.clone());
|
newContainer.append(template.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
//helpful instruction message if no characters are favorited
|
||||||
|
if (count === 0) { container.html(`<small><span><i class="fa-solid fa-star"></i> Favorite characters to add them to HotSwaps</span></small>`) }
|
||||||
|
//otherwise replace with fav'd characters
|
||||||
|
if (count > 0) {
|
||||||
|
container.replaceWith(newContainer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//changes input bar and send button display depending on connection status
|
//changes input bar and send button display depending on connection status
|
||||||
@@ -388,6 +415,7 @@ function RA_autoconnect(PrevApi) {
|
|||||||
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
|| (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.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.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").click();
|
||||||
}
|
}
|
||||||
@@ -403,15 +431,6 @@ function RA_autoconnect(PrevApi) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUrlOrAPIKey(string) {
|
|
||||||
try {
|
|
||||||
new URL(string);
|
|
||||||
return true;
|
|
||||||
} catch (_) {
|
|
||||||
// return pattern.test(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function OpenNavPanels() {
|
function OpenNavPanels() {
|
||||||
const deviceInfo = getDeviceInfo();
|
const deviceInfo = getDeviceInfo();
|
||||||
if (deviceInfo && deviceInfo.device.type === 'desktop') {
|
if (deviceInfo && deviceInfo.device.type === 'desktop') {
|
||||||
@@ -867,8 +886,12 @@ export function initRossMods() {
|
|||||||
|
|
||||||
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
||||||
$('#send_textarea').on('input', function () {
|
$('#send_textarea').on('input', function () {
|
||||||
|
const chatBlock = $('#chat');
|
||||||
|
const originalScrollBottom = chatBlock[0].scrollHeight - (chatBlock.scrollTop() + chatBlock.outerHeight());
|
||||||
this.style.height = window.getComputedStyle(this).getPropertyValue('min-height');
|
this.style.height = window.getComputedStyle(this).getPropertyValue('min-height');
|
||||||
this.style.height = (this.scrollHeight) + 'px';
|
this.style.height = (this.scrollHeight) + 'px';
|
||||||
|
const newScrollTop = chatBlock[0].scrollHeight - (chatBlock.outerHeight() + originalScrollBottom);
|
||||||
|
chatBlock.scrollTop(newScrollTop);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Regenerate if user swipes on the last mesage in chat
|
//Regenerate if user swipes on the last mesage in chat
|
||||||
@@ -906,10 +929,13 @@ export function initRossMods() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('keydown', function (event) {
|
$(document).on('keydown', function (event) {
|
||||||
processHotkeys(event);
|
processHotkeys(event.originalEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Additional hotkeys CTRL+ENTER and CTRL+UPARROW
|
//Additional hotkeys CTRL+ENTER and CTRL+UPARROW
|
||||||
|
/**
|
||||||
|
* @param {KeyboardEvent} event
|
||||||
|
*/
|
||||||
function processHotkeys(event) {
|
function processHotkeys(event) {
|
||||||
//Enter to send when send_textarea in focus
|
//Enter to send when send_textarea in focus
|
||||||
if ($(':focus').attr('id') === 'send_textarea') {
|
if ($(':focus').attr('id') === 'send_textarea') {
|
||||||
@@ -943,6 +969,14 @@ export function initRossMods() {
|
|||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alt+Enter or AltGr+Enter to Continue
|
||||||
|
if ((event.altKey || (event.altKey && event.ctrlKey)) && event.key == "Enter") {
|
||||||
|
if (is_send_press == false) {
|
||||||
|
console.debug("Continuing with Alt+Enter");
|
||||||
|
$('#option_continue').trigger('click');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
|
// Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
|
||||||
if (event.ctrlKey && event.key == "Enter") {
|
if (event.ctrlKey && event.key == "Enter") {
|
||||||
const editMesDone = $(".mes_edit_done:visible");
|
const editMesDone = $(".mes_edit_done:visible");
|
||||||
@@ -958,14 +992,6 @@ export function initRossMods() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alt+Enter to Continue
|
|
||||||
if (event.altKey && event.key == "Enter") {
|
|
||||||
if (is_send_press == false) {
|
|
||||||
console.debug("Continuing with Alt+Enter");
|
|
||||||
$('#option_continue').trigger('click');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to check if nanogallery2's lightbox is active
|
// Helper function to check if nanogallery2's lightbox is active
|
||||||
function isNanogallery2LightboxActive() {
|
function isNanogallery2LightboxActive() {
|
||||||
// Check if the body has the 'nGY2On' class, adjust this based on actual behavior
|
// Check if the body has the 'nGY2On' class, adjust this based on actual behavior
|
||||||
@@ -1097,13 +1123,14 @@ export function initRossMods() {
|
|||||||
$("#rightNavDrawerIcon").trigger('click');
|
$("#rightNavDrawerIcon").trigger('click');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if ($(".draggable").is(":visible")) {
|
||||||
|
// Remove the first matched element
|
||||||
|
$('.draggable:first').remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($(".draggable").is(":visible")) {
|
|
||||||
// Remove the first matched element
|
|
||||||
$('.draggable:first').remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
|
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
|
||||||
|
@@ -133,6 +133,39 @@ async function saveBookmarkMenu() {
|
|||||||
return createNewBookmark(chat.length - 1);
|
return createNewBookmark(chat.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createBranch(mesId) {
|
||||||
|
if (!chat.length) {
|
||||||
|
toastr.warning('The chat is empty.', 'Branch creation failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesId < 0 || mesId >= chat.length) {
|
||||||
|
toastr.warning('Invalid message ID.', 'Branch creation failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastMes = chat[mesId];
|
||||||
|
const mainChat = selected_group ? groups?.find(x => x.id == selected_group)?.chat_id : characters[this_chid].chat;
|
||||||
|
const newMetadata = { main_chat: mainChat };
|
||||||
|
let name = `Branch #${mesId} - ${humanizedDateTime()}`
|
||||||
|
|
||||||
|
if (selected_group) {
|
||||||
|
await saveGroupBookmarkChat(selected_group, name, newMetadata, mesId);
|
||||||
|
} else {
|
||||||
|
await saveChat(name, newMetadata, mesId);
|
||||||
|
}
|
||||||
|
// append to branches list if it exists
|
||||||
|
// otherwise create it
|
||||||
|
if (typeof lastMes.extra !== 'object') {
|
||||||
|
lastMes.extra = {};
|
||||||
|
}
|
||||||
|
if (typeof lastMes.extra['branches'] !== 'object') {
|
||||||
|
lastMes.extra['branches'] = [];
|
||||||
|
}
|
||||||
|
lastMes.extra['branches'].push(name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
async function createNewBookmark(mesId) {
|
async function createNewBookmark(mesId) {
|
||||||
if (!chat.length) {
|
if (!chat.length) {
|
||||||
toastr.warning('The chat is empty.', 'Bookmark creation failed');
|
toastr.warning('The chat is empty.', 'Bookmark creation failed');
|
||||||
@@ -280,7 +313,6 @@ async function convertSoloToGroupChat() {
|
|||||||
message.name = character.name;
|
message.name = character.name;
|
||||||
message.original_avatar = character.avatar;
|
message.original_avatar = character.avatar;
|
||||||
message.force_avatar = getThumbnailUrl('avatar', character.avatar);
|
message.force_avatar = getThumbnailUrl('avatar', character.avatar);
|
||||||
message.is_name = true;
|
|
||||||
|
|
||||||
// Allow regens of a single message in group
|
// Allow regens of a single message in group
|
||||||
if (typeof message.extra !== 'object') {
|
if (typeof message.extra !== 'object') {
|
||||||
|
@@ -134,7 +134,10 @@ const extension_settings = {
|
|||||||
caption: {
|
caption: {
|
||||||
refine_mode: false,
|
refine_mode: false,
|
||||||
},
|
},
|
||||||
expressions: {},
|
expressions: {
|
||||||
|
/** @type {string[]} */
|
||||||
|
custom: [],
|
||||||
|
},
|
||||||
dice: {},
|
dice: {},
|
||||||
regex: [],
|
regex: [],
|
||||||
tts: {},
|
tts: {},
|
||||||
@@ -153,6 +156,8 @@ const extension_settings = {
|
|||||||
},
|
},
|
||||||
speech_recognition: {},
|
speech_recognition: {},
|
||||||
rvc: {},
|
rvc: {},
|
||||||
|
hypebot: {},
|
||||||
|
vectors: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let modules = [];
|
let modules = [];
|
||||||
@@ -192,7 +197,6 @@ async function doExtrasFetch(endpoint, args) {
|
|||||||
}
|
}
|
||||||
Object.assign(args.headers, {
|
Object.assign(args.headers, {
|
||||||
'Authorization': `Bearer ${extension_settings.apiKey}`,
|
'Authorization': `Bearer ${extension_settings.apiKey}`,
|
||||||
'Bypass-Tunnel-Reminder': 'bypass'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(endpoint, args);
|
const response = await fetch(endpoint, args);
|
||||||
@@ -201,7 +205,7 @@ async function doExtrasFetch(endpoint, args) {
|
|||||||
|
|
||||||
async function discoverExtensions() {
|
async function discoverExtensions() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/discover_extensions');
|
const response = await fetch('/api/extensions/discover');
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const extensions = await response.json();
|
const extensions = await response.json();
|
||||||
@@ -606,7 +610,7 @@ async function showExtensionsDetails() {
|
|||||||
async function onUpdateClick() {
|
async function onUpdateClick() {
|
||||||
const extensionName = $(this).data('name');
|
const extensionName = $(this).data('name');
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/update_extension', {
|
const response = await fetch('/api/extensions/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ extensionName })
|
body: JSON.stringify({ extensionName })
|
||||||
@@ -626,7 +630,7 @@ async function onUpdateClick() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the click event for the delete button of an extension.
|
* Handles the click event for the delete button of an extension.
|
||||||
* This function makes a POST request to '/delete_extension' with the extension's name.
|
* This function makes a POST request to '/api/extensions/delete' with the extension's name.
|
||||||
* If the extension is deleted, it displays a success message.
|
* If the extension is deleted, it displays a success message.
|
||||||
* Creates a popup for the user to confirm before delete.
|
* Creates a popup for the user to confirm before delete.
|
||||||
*/
|
*/
|
||||||
@@ -636,7 +640,7 @@ async function onDeleteClick() {
|
|||||||
const confirmation = await callPopup(`Are you sure you want to delete ${extensionName}?`, 'delete_extension');
|
const confirmation = await callPopup(`Are you sure you want to delete ${extensionName}?`, 'delete_extension');
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/delete_extension', {
|
const response = await fetch('/api/extensions/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ extensionName })
|
body: JSON.stringify({ extensionName })
|
||||||
@@ -663,7 +667,7 @@ async function onDeleteClick() {
|
|||||||
*/
|
*/
|
||||||
async function getExtensionVersion(extensionName) {
|
async function getExtensionVersion(extensionName) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/get_extension_version', {
|
const response = await fetch('/api/extensions/version', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ extensionName })
|
body: JSON.stringify({ extensionName })
|
||||||
|
@@ -150,7 +150,7 @@ async function installAsset(url, assetType, filename) {
|
|||||||
const category = assetType;
|
const category = assetType;
|
||||||
try {
|
try {
|
||||||
const body = { url, category, filename };
|
const body = { url, category, filename };
|
||||||
const result = await fetch('/asset_download', {
|
const result = await fetch('/api/assets/download', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
@@ -171,7 +171,7 @@ async function deleteAsset(assetType, filename) {
|
|||||||
const category = assetType;
|
const category = assetType;
|
||||||
try {
|
try {
|
||||||
const body = { category, filename };
|
const body = { category, filename };
|
||||||
const result = await fetch('/asset_delete', {
|
const result = await fetch('/api/assets/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
@@ -194,7 +194,7 @@ async function deleteAsset(assetType, filename) {
|
|||||||
async function updateCurrentAssets() {
|
async function updateCurrentAssets() {
|
||||||
console.debug(DEBUG_PREFIX, "Checking installed assets...")
|
console.debug(DEBUG_PREFIX, "Checking installed assets...")
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`/get_assets`, {
|
const result = await fetch(`/api/assets/get`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Ideas:
|
Ideas:
|
||||||
|
- Clean design of new ui
|
||||||
|
- change select text versus options for playing: audio
|
||||||
- cross fading between bgm / start a different time
|
- cross fading between bgm / start a different time
|
||||||
|
- fading should appear before end when switching randomly
|
||||||
- Background based ambient sounds
|
- Background based ambient sounds
|
||||||
- import option on background UI ?
|
- import option on background UI ?
|
||||||
- Allow background music edition using background menu
|
- Allow background music edition using background menu
|
||||||
@@ -59,6 +62,8 @@ const DEFAULT_EXPRESSIONS = [
|
|||||||
];
|
];
|
||||||
const SPRITE_DOM_ID = "#expression-image";
|
const SPRITE_DOM_ID = "#expression-image";
|
||||||
|
|
||||||
|
let current_chat_id = null
|
||||||
|
|
||||||
let fallback_BGMS = null; // Initialized only once with module workers
|
let fallback_BGMS = null; // Initialized only once with module workers
|
||||||
let ambients = null; // Initialized only once with module workers
|
let ambients = null; // Initialized only once with module workers
|
||||||
let characterMusics = {}; // Updated with module workers
|
let characterMusics = {}; // Updated with module workers
|
||||||
@@ -69,16 +74,27 @@ let currentBackground = null;
|
|||||||
|
|
||||||
let cooldownBGM = 0;
|
let cooldownBGM = 0;
|
||||||
|
|
||||||
|
let bgmEnded = true;
|
||||||
|
|
||||||
//#############################//
|
//#############################//
|
||||||
// Extension UI and Settings //
|
// Extension UI and Settings //
|
||||||
//#############################//
|
//#############################//
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
dynamic_bgm_enabled: false,
|
||||||
|
//dynamic_ambient_enabled: false,
|
||||||
|
|
||||||
|
bgm_locked: true,
|
||||||
bgm_muted: true,
|
bgm_muted: true,
|
||||||
ambient_muted: true,
|
|
||||||
bgm_volume: 50,
|
bgm_volume: 50,
|
||||||
|
bgm_selected: null,
|
||||||
|
|
||||||
|
ambient_locked: true,
|
||||||
|
ambient_muted: true,
|
||||||
ambient_volume: 50,
|
ambient_volume: 50,
|
||||||
|
ambient_selected: null,
|
||||||
|
|
||||||
bgm_cooldown: 30
|
bgm_cooldown: 30
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +106,8 @@ function loadSettings() {
|
|||||||
Object.assign(extension_settings.audio, defaultSettings)
|
Object.assign(extension_settings.audio, defaultSettings)
|
||||||
}
|
}
|
||||||
$("#audio_enabled").prop('checked', extension_settings.audio.enabled);
|
$("#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_bgm_volume").text(extension_settings.audio.bgm_volume);
|
||||||
$("#audio_ambient_volume").text(extension_settings.audio.ambient_volume);
|
$("#audio_ambient_volume").text(extension_settings.audio.ambient_volume);
|
||||||
@@ -103,20 +121,55 @@ function loadSettings() {
|
|||||||
$("#audio_bgm_mute").addClass("redOverlayGlow");
|
$("#audio_bgm_mute").addClass("redOverlayGlow");
|
||||||
$("#audio_bgm").prop("muted", true);
|
$("#audio_bgm").prop("muted", true);
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$("#audio_bgm_mute_icon").addClass("fa-volume-high");
|
$("#audio_bgm_mute_icon").addClass("fa-volume-high");
|
||||||
$("#audio_bgm_mute_icon").removeClass("fa-volume-mute");
|
$("#audio_bgm_mute_icon").removeClass("fa-volume-mute");
|
||||||
$("#audio_bgm_mute").removeClass("redOverlayGlow");
|
$("#audio_bgm_mute").removeClass("redOverlayGlow");
|
||||||
$("#audio_bgm").prop("muted", false);
|
$("#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) {
|
if (extension_settings.audio.ambient_muted) {
|
||||||
$("#audio_ambient_mute_icon").removeClass("fa-volume-high");
|
$("#audio_ambient_mute_icon").removeClass("fa-volume-high");
|
||||||
$("#audio_ambient_mute_icon").addClass("fa-volume-mute");
|
$("#audio_ambient_mute_icon").addClass("fa-volume-mute");
|
||||||
$("#audio_ambient_mute").addClass("redOverlayGlow");
|
$("#audio_ambient_mute").addClass("redOverlayGlow");
|
||||||
$("#audio_ambient").prop("muted", true);
|
$("#audio_ambient").prop("muted", true);
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
$("#audio_ambient_mute_icon").addClass("fa-volume-high");
|
$("#audio_ambient_mute_icon").addClass("fa-volume-high");
|
||||||
$("#audio_ambient_mute_icon").removeClass("fa-volume-mute");
|
$("#audio_ambient_mute_icon").removeClass("fa-volume-mute");
|
||||||
$("#audio_ambient_mute").removeClass("redOverlayGlow");
|
$("#audio_ambient_mute").removeClass("redOverlayGlow");
|
||||||
@@ -125,7 +178,7 @@ function loadSettings() {
|
|||||||
|
|
||||||
$("#audio_bgm_cooldown").val(extension_settings.audio.bgm_cooldown);
|
$("#audio_bgm_cooldown").val(extension_settings.audio.bgm_cooldown);
|
||||||
|
|
||||||
$("#audio_debug_div").hide(); // DBG
|
$("#audio_debug_div").hide(); // DBG: comment to see debug mode
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onEnabledClick() {
|
async function onEnabledClick() {
|
||||||
@@ -142,6 +195,51 @@ async function onEnabledClick() {
|
|||||||
saveSettingsDebounced();
|
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() {
|
async function onBGMMuteClick() {
|
||||||
extension_settings.audio.bgm_muted = !extension_settings.audio.bgm_muted;
|
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-high");
|
||||||
@@ -151,6 +249,20 @@ async function onBGMMuteClick() {
|
|||||||
saveSettingsDebounced();
|
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() {
|
async function onAmbientMuteClick() {
|
||||||
extension_settings.audio.ambient_muted = !extension_settings.audio.ambient_muted;
|
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-high");
|
||||||
@@ -176,6 +288,20 @@ async function onAmbientVolumeChange() {
|
|||||||
//console.debug(DEBUG_PREFIX,"UPDATED Ambient MAX TO",extension_settings.audio.ambient_volume);
|
//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() {
|
async function onBGMCooldownInput() {
|
||||||
extension_settings.audio.bgm_cooldown = ~~($("#audio_bgm_cooldown").val());
|
extension_settings.audio.bgm_cooldown = ~~($("#audio_bgm_cooldown").val());
|
||||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||||
@@ -191,7 +317,7 @@ async function getAssetsList(type) {
|
|||||||
console.debug(DEBUG_PREFIX, "getting assets of type", type);
|
console.debug(DEBUG_PREFIX, "getting assets of type", type);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`/get_assets`, {
|
const result = await fetch(`/api/assets/get`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
});
|
});
|
||||||
@@ -209,7 +335,7 @@ async function getCharacterBgmList(name) {
|
|||||||
console.debug(DEBUG_PREFIX, "getting bgm list for", name);
|
console.debug(DEBUG_PREFIX, "getting bgm list for", name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`/get_character_assets_list?name=${encodeURIComponent(name)}&category=${CHARACTER_BGM_FOLDER}`, {
|
const result = await fetch(`/api/assets/character?name=${encodeURIComponent(name)}&category=${CHARACTER_BGM_FOLDER}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
});
|
});
|
||||||
@@ -222,11 +348,42 @@ async function getCharacterBgmList(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//#############################//
|
//#############################//
|
||||||
// Module Worker //
|
// 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 ambient sound
|
||||||
- Update character BGM
|
- Update character BGM
|
||||||
@@ -246,6 +403,8 @@ async function moduleWorker() {
|
|||||||
fallback_BGMS = await getAssetsList(ASSETS_BGM_FOLDER);
|
fallback_BGMS = await getAssetsList(ASSETS_BGM_FOLDER);
|
||||||
fallback_BGMS = fallback_BGMS.filter((filename) => filename != ".placeholder")
|
fallback_BGMS = fallback_BGMS.filter((filename) => filename != ".placeholder")
|
||||||
console.debug(DEBUG_PREFIX, "Detected assets:", fallback_BGMS);
|
console.debug(DEBUG_PREFIX, "Detected assets:", fallback_BGMS);
|
||||||
|
|
||||||
|
fillBGMSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ambients == null) {
|
if (ambients == null) {
|
||||||
@@ -253,10 +412,33 @@ async function moduleWorker() {
|
|||||||
ambients = await getAssetsList(ASSETS_AMBIENT_FOLDER);
|
ambients = await getAssetsList(ASSETS_AMBIENT_FOLDER);
|
||||||
ambients = ambients.filter((filename) => filename != ".placeholder")
|
ambients = ambients.filter((filename) => filename != ".placeholder")
|
||||||
console.debug(DEBUG_PREFIX, "Detected assets:", ambients);
|
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
|
// 1) Update ambient audio
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
//if (extension_settings.audio.dynamic_ambient_enabled) {
|
||||||
let newBackground = $("#bg1").css("background-image");
|
let newBackground = $("#bg1").css("background-image");
|
||||||
const custom_background = getContext()["chatMetadata"]["custom_background"];
|
const custom_background = getContext()["chatMetadata"]["custom_background"];
|
||||||
|
|
||||||
@@ -275,6 +457,7 @@ async function moduleWorker() {
|
|||||||
updateAmbient();
|
updateAmbient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
//console.debug(DEBUG_PREFIX,context);
|
//console.debug(DEBUG_PREFIX,context);
|
||||||
@@ -288,6 +471,14 @@ async function moduleWorker() {
|
|||||||
// 1) Update BGM (single chat)
|
// 1) Update BGM (single chat)
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
if (!chatIsGroup) {
|
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;
|
newCharacter = context.name2;
|
||||||
|
|
||||||
//console.log(DEBUG_PREFIX,"SOLO CHAT MODE"); // DBG
|
//console.log(DEBUG_PREFIX,"SOLO CHAT MODE"); // DBG
|
||||||
@@ -307,7 +498,7 @@ async function moduleWorker() {
|
|||||||
if (currentCharacterBGM !== newCharacter) {
|
if (currentCharacterBGM !== newCharacter) {
|
||||||
currentCharacterBGM = newCharacter;
|
currentCharacterBGM = newCharacter;
|
||||||
try {
|
try {
|
||||||
await updateBGM();
|
await updateBGM(false, true);
|
||||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -329,9 +520,9 @@ async function moduleWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
currentExpressionBGM = newExpression;
|
||||||
await updateBGM();
|
await updateBGM();
|
||||||
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
|
||||||
currentExpressionBGM = newExpression;
|
|
||||||
console.debug(DEBUG_PREFIX, "(SOLO) Updated current character expression to", currentExpressionBGM, "cooldown", cooldownBGM);
|
console.debug(DEBUG_PREFIX, "(SOLO) Updated current character expression to", currentExpressionBGM, "cooldown", cooldownBGM);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -346,6 +537,34 @@ async function moduleWorker() {
|
|||||||
|
|
||||||
// 2) Update BGM (group chat)
|
// 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;
|
newCharacter = context.chat[context.chat.length - 1].name;
|
||||||
const userName = context.name1;
|
const userName = context.name1;
|
||||||
|
|
||||||
@@ -353,17 +572,17 @@ async function moduleWorker() {
|
|||||||
|
|
||||||
//console.log(DEBUG_PREFIX,"GROUP CHAT MODE"); // DBG
|
//console.log(DEBUG_PREFIX,"GROUP CHAT MODE"); // DBG
|
||||||
|
|
||||||
// 2.1) First time character appear
|
// 2.1) New character appear
|
||||||
if (characterMusics[newCharacter] === undefined) {
|
if (characterMusics[newCharacter] === undefined) {
|
||||||
await loadCharacterBGM(newCharacter);
|
await loadCharacterBGM(newCharacter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2) Switched chat
|
// 2.2) Switched char
|
||||||
if (currentCharacterBGM !== newCharacter) {
|
if (currentCharacterBGM !== newCharacter) {
|
||||||
// Check cooldown
|
// Check cooldown
|
||||||
if (cooldownBGM > 0) {
|
if (cooldownBGM > 0) {
|
||||||
//console.debug(DEBUG_PREFIX,"(GROUP) BGM switch on cooldown:",cooldownBGM);
|
console.debug(DEBUG_PREFIX, "(GROUP) BGM switch on cooldown:", cooldownBGM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +648,8 @@ async function loadCharacterBGM(newCharacter) {
|
|||||||
characterMusics[newCharacter][e].push(i);
|
characterMusics[newCharacter][e].push(i);
|
||||||
}
|
}
|
||||||
console.debug(DEBUG_PREFIX, "Updated BGM map of", newCharacter, "to", characterMusics[newCharacter]);
|
console.debug(DEBUG_PREFIX, "Updated BGM map of", newCharacter, "to", characterMusics[newCharacter]);
|
||||||
|
|
||||||
|
fillBGMSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNewExpression() {
|
function getNewExpression() {
|
||||||
@@ -458,24 +679,52 @@ function getNewExpression() {
|
|||||||
return newExpression;
|
return newExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateBGM() {
|
async function updateBGM(isUserInput = false, newChat = false) {
|
||||||
let audio_files = characterMusics[currentCharacterBGM][currentExpressionBGM];// Try char expression BGM
|
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");
|
||||||
if (audio_files === undefined || audio_files.length == 0) {
|
return;
|
||||||
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) {
|
let audio_file_path = ""
|
||||||
console.debug(DEBUG_PREFIX, "No default BGM for", currentCharacterBGM, FALLBACK_EXPRESSION, "switch to ST BGM");
|
if (isUserInput || (extension_settings.audio.bgm_locked && extension_settings.audio.bgm_selected !== null)) {
|
||||||
audio_files = fallback_BGMS; // ST FALLBACK BGM
|
audio_file_path = extension_settings.audio.bgm_selected;
|
||||||
|
|
||||||
if (audio_files.length == 0) {
|
if (isUserInput)
|
||||||
console.debug(DEBUG_PREFIX, "No default BGM file found, bgm folder may be empty.");
|
console.debug(DEBUG_PREFIX, "User selected BGM", audio_file_path);
|
||||||
return;
|
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)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const audio_file_path = audio_files[Math.floor(Math.random() * audio_files.length)];
|
|
||||||
console.log(DEBUG_PREFIX, "Updating BGM");
|
console.log(DEBUG_PREFIX, "Updating BGM");
|
||||||
console.log(DEBUG_PREFIX, "Checking file", audio_file_path);
|
console.log(DEBUG_PREFIX, "Checking file", audio_file_path);
|
||||||
try {
|
try {
|
||||||
@@ -485,20 +734,30 @@ async function updateBGM() {
|
|||||||
console.log(DEBUG_PREFIX, "File not found!")
|
console.log(DEBUG_PREFIX, "File not found!")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(DEBUG_PREFIX, "Switching BGM to", currentExpressionBGM)
|
console.log(DEBUG_PREFIX, "Switching BGM to", currentExpressionBGM);
|
||||||
|
$("#audio_bgm_select").val(audio_file_path);
|
||||||
const audio = $("#audio_bgm");
|
const audio = $("#audio_bgm");
|
||||||
|
|
||||||
if (audio.attr("src") == audio_file_path) {
|
if (audio.attr("src") == audio_file_path && !bgmEnded) {
|
||||||
console.log(DEBUG_PREFIX, "Already playing, ignored");
|
console.log(DEBUG_PREFIX, "Already playing, ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio.animate({ volume: 0.0 }, 2000, function () {
|
let fade_time = 2000;
|
||||||
|
bgmEnded = false;
|
||||||
|
|
||||||
|
if (isUserInput || extension_settings.audio.bgm_locked) {
|
||||||
audio.attr("src", audio_file_path);
|
audio.attr("src", audio_file_path);
|
||||||
audio[0].play();
|
audio[0].play();
|
||||||
audio.volume = extension_settings.audio.bgm_volume * 0.01;
|
}
|
||||||
audio.animate({ volume: extension_settings.audio.bgm_volume * 0.01 }, 2000);
|
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) {
|
} catch (error) {
|
||||||
@@ -506,18 +765,30 @@ async function updateBGM() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateAmbient() {
|
async function updateAmbient(isUserInput = false) {
|
||||||
let audio_file_path = null;
|
let audio_file_path = null;
|
||||||
for (const i of ambients) {
|
|
||||||
console.debug(i)
|
if (isUserInput || extension_settings.audio.ambient_locked) {
|
||||||
if (i.includes(currentBackground)) {
|
audio_file_path = extension_settings.audio.ambient_selected;
|
||||||
audio_file_path = i;
|
|
||||||
break;
|
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) {
|
if (audio_file_path === null) {
|
||||||
console.debug(DEBUG_PREFIX, "No ambient file found for background", currentBackground);
|
console.debug(DEBUG_PREFIX, "No bgm file found for background", currentBackground);
|
||||||
const audio = $("#audio_ambient");
|
const audio = $("#audio_ambient");
|
||||||
audio.attr("src", "");
|
audio.attr("src", "");
|
||||||
audio[0].pause();
|
audio[0].pause();
|
||||||
@@ -527,45 +798,87 @@ async function updateAmbient() {
|
|||||||
//const audio_file_path = AMBIENT_FOLDER+currentBackground+".mp3";
|
//const audio_file_path = AMBIENT_FOLDER+currentBackground+".mp3";
|
||||||
console.log(DEBUG_PREFIX, "Updating ambient");
|
console.log(DEBUG_PREFIX, "Updating ambient");
|
||||||
console.log(DEBUG_PREFIX, "Checking file", audio_file_path);
|
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");
|
const audio = $("#audio_ambient");
|
||||||
audio.animate({ volume: 0.0 }, 2000, function () {
|
|
||||||
|
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.attr("src", audio_file_path);
|
||||||
audio[0].play();
|
audio[0].play();
|
||||||
audio.volume = extension_settings.audio.ambient_volume * 0.01;
|
audio.volume = extension_settings.audio.ambient_volume * 0.01;
|
||||||
audio.animate({ volume: extension_settings.audio.ambient_volume * 0.01 }, 2000);
|
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 //
|
// Extension load //
|
||||||
//#############################//
|
//#############################//
|
||||||
|
|
||||||
// This function is called when the extension is loaded
|
// This function is called when the extension is loaded
|
||||||
jQuery(async () => {
|
jQuery(async () => {
|
||||||
// This is an example of loading HTML from a file
|
|
||||||
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
||||||
|
|
||||||
$('#extensions_settings').append(windowHtml);
|
$('#extensions_settings').append(windowHtml);
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
$("#audio_bgm").attr("loop", true);
|
$("#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_ambient").attr("loop", true);
|
||||||
|
|
||||||
$("#audio_bgm").hide();
|
$("#audio_bgm").hide();
|
||||||
$("#audio_ambient").hide();
|
$("#audio_bgm_lock").on("click", onBGMLockClick);
|
||||||
$("#audio_bgm_mute").on("click", onBGMMuteClick);
|
$("#audio_bgm_mute").on("click", onBGMMuteClick);
|
||||||
$("#audio_ambient_mute").on("click", onAmbientMuteClick);
|
|
||||||
|
|
||||||
$("#audio_enabled").on("click", onEnabledClick);
|
|
||||||
$("#audio_bgm_volume_slider").on("input", onBGMVolumeChange);
|
$("#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);
|
$("#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);
|
$("#audio_bgm_cooldown").on("input", onBGMCooldownInput);
|
||||||
|
|
||||||
// Reset assets container, will be redected like if ST restarted
|
// Reset assets container, will be redected like if ST restarted
|
||||||
$("#audio_refresh_assets").on("click", function(){
|
$("#audio_refresh_assets").on("click", function () {
|
||||||
console.debug(DEBUG_PREFIX,"Refreshing audio assets");
|
console.debug(DEBUG_PREFIX, "Refreshing audio assets");
|
||||||
|
current_chat_id = null
|
||||||
fallback_BGMS = null;
|
fallback_BGMS = null;
|
||||||
ambients = null;
|
ambients = null;
|
||||||
characterMusics = {};
|
characterMusics = {};
|
||||||
@@ -574,6 +887,9 @@ jQuery(async () => {
|
|||||||
currentBackground = null;
|
currentBackground = null;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$("#audio_bgm_select").on("change", onBGMSelectChange);
|
||||||
|
$("#audio_ambient_select").on("change", onAmbientSelectChange);
|
||||||
|
|
||||||
// DBG
|
// DBG
|
||||||
$("#audio_debug").on("click", function () {
|
$("#audio_debug").on("click", function () {
|
||||||
if ($("#audio_debug").is(':checked')) {
|
if ($("#audio_debug").is(':checked')) {
|
||||||
@@ -587,6 +903,14 @@ jQuery(async () => {
|
|||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
|
||||||
|
$("#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);
|
const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
|
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
|
||||||
moduleWorker();
|
moduleWorker();
|
||||||
|
@@ -1,15 +1,46 @@
|
|||||||
.mixer-div {
|
.audio-ui-block {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-mixer-div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: rgba(38, 38, 38, 0.5);
|
||||||
|
border: 1px rgb(75, 75, 75) solid;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-label {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-volume-div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 5px;
|
}
|
||||||
|
|
||||||
|
.audio-lock-button {
|
||||||
|
width: 100%;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-random-button {
|
||||||
|
width: 100%;
|
||||||
|
height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-mute-button {
|
.audio-mute-button {
|
||||||
padding: 5px;
|
width: 100%;
|
||||||
width: 50px;
|
height: 2em;
|
||||||
height: 30px;
|
}
|
||||||
|
|
||||||
|
.audio-slider {
|
||||||
|
width: 100% !important;
|
||||||
|
vertical-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-mute-button-muted {
|
.audio-mute-button-muted {
|
||||||
@@ -20,3 +51,42 @@
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.audio-mixer-mute {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-mixer-lock {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-mixer-random {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container>.vol {
|
||||||
|
width: 100px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container>.vol>input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container>.playlist {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container>.playlist>select {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
@@ -10,6 +10,12 @@
|
|||||||
<input type="checkbox" id="audio_enabled" name="audio_enabled">
|
<input type="checkbox" id="audio_enabled" name="audio_enabled">
|
||||||
<small>Enabled</small>
|
<small>Enabled</small>
|
||||||
</label>
|
</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_debug_div">
|
<div id="audio_debug_div">
|
||||||
<label class="checkbox_label" for="audio_debug">
|
<label class="checkbox_label" for="audio_debug">
|
||||||
<input type="checkbox" id="audio_debug" name="audio_debug">
|
<input type="checkbox" id="audio_debug" name="audio_debug">
|
||||||
@@ -24,23 +30,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div class="audio-ui-block">
|
||||||
<label for="audio_bgm_volume_slider">Music <span id="audio_bgm_volume"></span></label>
|
<label for="audio_bgm_volume_slider">Music</label>
|
||||||
<div class="mixer-div">
|
<div class="audio-mixer-div audio-container">
|
||||||
<div id="audio_bgm_mute" class="menu_button audio-mute-button">
|
<div class="audio-mixer-element audio-mixer-mute">
|
||||||
<i class="fa-solid fa-volume-high fa-lg" id="audio_bgm_mute_icon"></i>
|
<div id="audio_bgm_mute" class="menu_button audio-mute-button">
|
||||||
|
<i class="fa-solid fa-volume-high fa-lg fa-fw" id="audio_bgm_mute_icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element vol audio-mixer-volume">
|
||||||
|
<input type="range" class ="audio-slider" id ="audio_bgm_volume_slider" value = "0" maxlength ="100">
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element playlist audio-mixer-playlist">
|
||||||
|
<select id="audio_bgm_select">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element audio-mixer-lock">
|
||||||
|
<div id="audio_bgm_lock" class="menu_button audio-lock-button">
|
||||||
|
<i class="fa-solid fa-repeat fa-lg fa-fw" id="audio_bgm_lock_icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element audio-mixer-random">
|
||||||
|
<div id="audio_bgm_random" class="menu_button audio-random-button">
|
||||||
|
<i class="fa-solid fa-random fa-lg fa-fw" id="audio_bgm_random_icon"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="range" class ="slider" id ="audio_bgm_volume_slider" value = "0" maxlength ="100">
|
|
||||||
</div>
|
</div>
|
||||||
<audio id="audio_bgm" controls src="">
|
<audio id="audio_bgm" controls src="">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="audio_ambient_volume_slider">Ambient <span id="audio_ambient_volume"></span></label>
|
<label for="audio_ambient_volume_slider">Ambient</label>
|
||||||
<div class="mixer-div">
|
<div class="audio-mixer-div audio-container">
|
||||||
<div id="audio_ambient_mute" class="menu_button audio-mute-button">
|
<div class="audio-mixer-element audio-mixer-mute">
|
||||||
<i class="fa-solid fa-volume-high fa-lg" id="audio_ambient_mute_icon"></i>
|
<div id="audio_ambient_mute" class="menu_button audio-mute-button">
|
||||||
|
<i class="fa-solid fa-volume-high fa-lg fa-fw" id="audio_ambient_mute_icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element vol audio-mixer-volume">
|
||||||
|
<input type="range" class ="audio-slider" id ="audio_ambient_volume_slider" value = "0" maxlength ="100">
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element playlist audio-mixer-playlist">
|
||||||
|
<select id="audio_ambient_select">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="audio-mixer-element">
|
||||||
|
<div id="audio_ambient_lock" class="menu_button audio-lock-button">
|
||||||
|
<i class="fa-solid fa-lock-open fa-lg fa-fw" id="audio_ambient_lock_icon"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="range" class ="slider" id ="audio_ambient_volume_slider" value = "0" maxlength ="100">
|
|
||||||
</div>
|
</div>
|
||||||
<audio id="audio_ambient" controls src="">
|
<audio id="audio_ambient" controls src="">
|
||||||
</div>
|
</div>
|
||||||
|
@@ -170,9 +170,9 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addSettings();
|
addSettings();
|
||||||
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], " – locks a background for the currently selected chat", true, true);
|
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('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);
|
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.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||||
eventSource.on(event_types.CHAT_CHANGED, moduleWorker);
|
eventSource.on(event_types.CHAT_CHANGED, moduleWorker);
|
||||||
});
|
});
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 1px solid var(--black50a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
box-shadow: 0 0 7px var(--black50a);
|
box-shadow: 0 0 7px var(--black50a);
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { getBase64Async } from "../../utils.js";
|
import { getBase64Async, saveBase64AsFile } from "../../utils.js";
|
||||||
import { getContext, getApiUrl, doExtrasFetch, extension_settings } from "../../extensions.js";
|
import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules } from "../../extensions.js";
|
||||||
import { callPopup, saveSettingsDebounced } from "../../../script.js";
|
import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
|
||||||
import { getMessageTimeStamp } from "../../RossAscends-mods.js";
|
import { getMessageTimeStamp } from "../../RossAscends-mods.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
@@ -8,7 +8,8 @@ const MODULE_NAME = 'caption';
|
|||||||
const UPDATE_INTERVAL = 1000;
|
const UPDATE_INTERVAL = 1000;
|
||||||
|
|
||||||
async function moduleWorker() {
|
async function moduleWorker() {
|
||||||
$('#send_picture').toggle(getContext().onlineStatus !== 'no_connection');
|
const hasConnection = getContext().onlineStatus !== 'no_connection';
|
||||||
|
$('#send_picture').toggle(hasConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setImageIcon() {
|
async function setImageIcon() {
|
||||||
@@ -52,7 +53,6 @@ async function sendCaptionedMessage(caption, image) {
|
|||||||
const message = {
|
const message = {
|
||||||
name: context.name1,
|
name: context.name1,
|
||||||
is_user: true,
|
is_user: true,
|
||||||
is_name: true,
|
|
||||||
send_date: getMessageTimeStamp(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: messageText,
|
mes: messageText,
|
||||||
extra: {
|
extra: {
|
||||||
@@ -65,16 +65,21 @@ async function sendCaptionedMessage(caption, image) {
|
|||||||
await context.generate('caption');
|
await context.generate('caption');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSelectImage(e) {
|
async function doCaptionRequest(base64Img) {
|
||||||
setSpinnerIcon();
|
if (extension_settings.caption.local) {
|
||||||
const file = e.target.files[0];
|
const apiResult = await fetch('/api/extra/caption', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({ image: base64Img })
|
||||||
|
});
|
||||||
|
|
||||||
if (!file) {
|
if (!apiResult.ok) {
|
||||||
return;
|
throw new Error('Failed to caption image via local pipeline.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const data = await apiResult.json();
|
||||||
const base64Img = await getBase64Async(file);
|
return data;
|
||||||
|
} else if (modules.includes('caption')) {
|
||||||
const url = new URL(getApiUrl());
|
const url = new URL(getApiUrl());
|
||||||
url.pathname = '/api/caption';
|
url.pathname = '/api/caption';
|
||||||
|
|
||||||
@@ -84,17 +89,42 @@ async function onSelectImage(e) {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Bypass-Tunnel-Reminder': 'bypass',
|
'Bypass-Tunnel-Reminder': 'bypass',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ image: base64Img.split(',')[1] })
|
body: JSON.stringify({ image: base64Img })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiResult.ok) {
|
if (!apiResult.ok) {
|
||||||
const data = await apiResult.json();
|
throw new Error('Failed to caption image via Extras.');
|
||||||
const caption = data.caption;
|
|
||||||
const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img;
|
|
||||||
await sendCaptionedMessage(caption, imageToSave);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = await apiResult.json();
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error('No captioning module is available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSelectImage(e) {
|
||||||
|
setSpinnerIcon();
|
||||||
|
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 data = await doCaptionRequest(base64Data);
|
||||||
|
const caption = data.caption;
|
||||||
|
const imageToSave = data.thumbnail ? data.thumbnail : base64Data;
|
||||||
|
const format = data.thumbnail ? 'jpeg' : base64Format;
|
||||||
|
const imagePath = await saveBase64AsFile(imageToSave, context.name2, '', format);
|
||||||
|
await sendCaptionedMessage(caption, imagePath);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
toastr.error('Failed to caption image.');
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@@ -113,12 +143,21 @@ jQuery(function () {
|
|||||||
const sendButton = $(`
|
const sendButton = $(`
|
||||||
<div id="send_picture" class="list-group-item flex-container flexGap5">
|
<div id="send_picture" class="list-group-item flex-container flexGap5">
|
||||||
<div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
|
<div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
|
||||||
Send a picture
|
Send a Picture
|
||||||
</div>`);
|
</div>`);
|
||||||
|
|
||||||
$('#extensionsMenu').prepend(sendButton);
|
$('#extensionsMenu').prepend(sendButton);
|
||||||
$(sendButton).hide();
|
$(sendButton).hide();
|
||||||
$(sendButton).on('click', () => $('#img_file').trigger('click'));
|
$(sendButton).on('click', () => {
|
||||||
|
const hasCaptionModule = modules.includes('caption') || extension_settings.caption.local;
|
||||||
|
|
||||||
|
if (!hasCaptionModule) {
|
||||||
|
toastr.error('No captioning module is available. Either enable the local captioning pipeline or connect to Extras.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#img_file').trigger('click');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function addPictureSendForm() {
|
function addPictureSendForm() {
|
||||||
const inputHtml = `<input id="img_file" type="file" accept="image/*">`;
|
const inputHtml = `<input id="img_file" type="file" accept="image/*">`;
|
||||||
@@ -131,13 +170,17 @@ jQuery(function () {
|
|||||||
}
|
}
|
||||||
function addSettings() {
|
function addSettings() {
|
||||||
const html = `
|
const html = `
|
||||||
<div class="background_settings">
|
<div class="caption_settings">
|
||||||
<div class="inline-drawer">
|
<div class="inline-drawer">
|
||||||
<div class="inline-drawer-toggle inline-drawer-header">
|
<div class="inline-drawer-toggle inline-drawer-header">
|
||||||
<b>Image Captioning</b>
|
<b>Image Captioning</b>
|
||||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-drawer-content">
|
<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 class="checkbox_label" for="caption_refine_mode">
|
||||||
<input id="caption_refine_mode" type="checkbox" class="checkbox">
|
<input id="caption_refine_mode" type="checkbox" class="checkbox">
|
||||||
Edit captions before generation
|
Edit captions before generation
|
||||||
@@ -155,6 +198,11 @@ jQuery(function () {
|
|||||||
setImageIcon();
|
setImageIcon();
|
||||||
moduleWorker();
|
moduleWorker();
|
||||||
$('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode));
|
$('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode));
|
||||||
|
$('#caption_local').prop('checked', !!(extension_settings.caption.local));
|
||||||
$('#caption_refine_mode').on('input', onRefineModeInput);
|
$('#caption_refine_mode').on('input', onRefineModeInput);
|
||||||
|
$('#caption_local').on('input', () => {
|
||||||
|
extension_settings.caption.local = !!$('#caption_local').prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"display_name": "Image Captioning",
|
"display_name": "Image Captioning",
|
||||||
"loading_order": 4,
|
"loading_order": 4,
|
||||||
"requires": [
|
"requires": [],
|
||||||
|
"optional": [
|
||||||
"caption"
|
"caption"
|
||||||
],
|
],
|
||||||
"optional": [],
|
|
||||||
"js": "index.js",
|
"js": "index.js",
|
||||||
"css": "style.css",
|
"css": "style.css",
|
||||||
"author": "Cohee#1207",
|
"author": "Cohee#1207",
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
<h3>
|
||||||
|
Enter a name for the custom expression:
|
||||||
|
</h3>
|
||||||
|
<h4>
|
||||||
|
Requirements:
|
||||||
|
</h4>
|
||||||
|
<ol class="justifyLeft">
|
||||||
|
<li>
|
||||||
|
The name must be unique and not already in use by the default expression.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The name must contain only letters, numbers, dashes and underscores. Don't include any file extensions.
|
||||||
|
</li>
|
||||||
|
</ol>
|
@@ -2,11 +2,13 @@ import { callPopup, eventSource, event_types, getRequestHeaders, saveSettingsDeb
|
|||||||
import { dragElement, isMobile } from "../../RossAscends-mods.js";
|
import { dragElement, isMobile } from "../../RossAscends-mods.js";
|
||||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js";
|
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js";
|
||||||
import { loadMovingUIState, power_user } from "../../power-user.js";
|
import { loadMovingUIState, power_user } from "../../power-user.js";
|
||||||
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
|
import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
|
import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence } from "../../utils.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = 'expressions';
|
const MODULE_NAME = 'expressions';
|
||||||
const UPDATE_INTERVAL = 2000;
|
const UPDATE_INTERVAL = 2000;
|
||||||
|
const STREAMING_UPDATE_INTERVAL = 6000;
|
||||||
const FALLBACK_EXPRESSION = 'joy';
|
const FALLBACK_EXPRESSION = 'joy';
|
||||||
const DEFAULT_EXPRESSIONS = [
|
const DEFAULT_EXPRESSIONS = [
|
||||||
"talkinghead",
|
"talkinghead",
|
||||||
@@ -45,6 +47,7 @@ let lastCharacter = undefined;
|
|||||||
let lastMessage = null;
|
let lastMessage = null;
|
||||||
let spriteCache = {};
|
let spriteCache = {};
|
||||||
let inApiCall = false;
|
let inApiCall = false;
|
||||||
|
let lastServerResponseTime = 0;
|
||||||
|
|
||||||
function isVisualNovelMode() {
|
function isVisualNovelMode() {
|
||||||
return Boolean(!isMobile() && power_user.waifuMode && getContext().groupId);
|
return Boolean(!isMobile() && power_user.waifuMode && getContext().groupId);
|
||||||
@@ -125,15 +128,7 @@ async function visualNovelSetCharacterSprites(container, name, expression) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let spriteFolderName = character.name;
|
const spriteFolderName = getSpriteFolderName({ original_avatar: character.avatar }, character.name);
|
||||||
const avatarFileName = getSpriteFolderName({ original_avatar: character.avatar });
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// download images if not downloaded yet
|
// download images if not downloaded yet
|
||||||
if (spriteCache[spriteFolderName] === undefined) {
|
if (spriteCache[spriteFolderName] === undefined) {
|
||||||
@@ -270,16 +265,7 @@ async function setLastMessageSprite(img, avatar, labels) {
|
|||||||
|
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
const text = lastMessage.mes || '';
|
const text = lastMessage.mes || '';
|
||||||
let spriteFolderName = lastMessage.name;
|
const spriteFolderName = getSpriteFolderName(lastMessage, lastMessage.name);
|
||||||
const avatarFileName = getSpriteFolderName(lastMessage);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sprites = spriteCache[spriteFolderName] || [];
|
const sprites = spriteCache[spriteFolderName] || [];
|
||||||
const label = await getExpressionLabel(text);
|
const label = await getExpressionLabel(text);
|
||||||
const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : '';
|
const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : '';
|
||||||
@@ -365,7 +351,7 @@ async function setImage(img, path) {
|
|||||||
expressionClone.removeClass('default');
|
expressionClone.removeClass('default');
|
||||||
expressionClone.off('error');
|
expressionClone.off('error');
|
||||||
expressionClone.on('error', function () {
|
expressionClone.on('error', function () {
|
||||||
console.debug('Expression image error', sprite.path);
|
console.debug('Expression image error', path);
|
||||||
$(this).attr('src', '');
|
$(this).attr('src', '');
|
||||||
$(this).off('error');
|
$(this).off('error');
|
||||||
resolve();
|
resolve();
|
||||||
@@ -419,17 +405,7 @@ async function loadLiveChar() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = getContext();
|
const spriteFolderName = getSpriteFolderName();
|
||||||
let spriteFolderName = context.name2;
|
|
||||||
const message = getLastCharacterMessage();
|
|
||||||
const avatarFileName = getSpriteFolderName(message);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
|
const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
|
||||||
|
|
||||||
@@ -468,19 +444,19 @@ async function loadLiveChar() {
|
|||||||
function handleImageChange() {
|
function handleImageChange() {
|
||||||
const imgElement = document.querySelector('img#expression-image.expression');
|
const imgElement = document.querySelector('img#expression-image.expression');
|
||||||
|
|
||||||
if (!imgElement) {
|
if (!imgElement || !(imgElement instanceof HTMLImageElement)) {
|
||||||
console.log("Cannot find addExpressionImage()");
|
console.log("Cannot find addExpressionImage()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extension_settings.expressions.talkinghead) {
|
if (extension_settings.expressions.talkinghead && !extension_settings.expressions.local) {
|
||||||
// Method get IP of endpoint
|
// Method get IP of endpoint
|
||||||
const talkingheadResultFeedSrc = `${getApiUrl()}/api/talkinghead/result_feed`;
|
const talkingheadResultFeedSrc = `${getApiUrl()}/api/talkinghead/result_feed`;
|
||||||
$('#expression-holder').css({ display: '' });
|
$('#expression-holder').css({ display: '' });
|
||||||
if (imgElement.src !== talkingheadResultFeedSrc) {
|
if (imgElement.src !== talkingheadResultFeedSrc) {
|
||||||
const expressionImageElement = document.querySelector('.expression_list_image');
|
const expressionImageElement = document.querySelector('.expression_list_image');
|
||||||
|
|
||||||
if (expressionImageElement) {
|
if (expressionImageElement && expressionImageElement instanceof HTMLImageElement) {
|
||||||
doExtrasFetch(expressionImageElement.src, {
|
doExtrasFetch(expressionImageElement.src, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
})
|
})
|
||||||
@@ -503,6 +479,14 @@ function handleImageChange() {
|
|||||||
async function moduleWorker() {
|
async function moduleWorker() {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
|
|
||||||
|
// Hide and disable talkinghead while in local mode
|
||||||
|
$('#image_type_block').toggle(!extension_settings.expressions.local);
|
||||||
|
|
||||||
|
if (extension_settings.expressions.local && extension_settings.expressions.talkinghead) {
|
||||||
|
$('#image_type_toggle').prop('checked', false);
|
||||||
|
setTalkingHeadState(false);
|
||||||
|
}
|
||||||
|
|
||||||
// non-characters not supported
|
// non-characters not supported
|
||||||
if (!context.groupId && (context.characterId === undefined || context.characterId === 'invalid-safety-id')) {
|
if (!context.groupId && (context.characterId === undefined || context.characterId === 'invalid-safety-id')) {
|
||||||
removeExpression();
|
removeExpression();
|
||||||
@@ -516,12 +500,14 @@ async function moduleWorker() {
|
|||||||
|
|
||||||
//clear expression
|
//clear expression
|
||||||
let imgElement = document.getElementById('expression-image');
|
let imgElement = document.getElementById('expression-image');
|
||||||
imgElement.src = "";
|
if (imgElement && imgElement instanceof HTMLImageElement) {
|
||||||
|
imgElement.src = "";
|
||||||
|
}
|
||||||
|
|
||||||
//set checkbox to global var
|
//set checkbox to global var
|
||||||
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
|
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
|
||||||
if (extension_settings.expressions.talkinghead) {
|
if (extension_settings.expressions.talkinghead) {
|
||||||
settalkingheadState(extension_settings.expressions.talkinghead);
|
setTalkingHeadState(extension_settings.expressions.talkinghead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,15 +531,7 @@ async function moduleWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
let spriteFolderName = currentLastMessage.name;
|
let spriteFolderName = getSpriteFolderName(currentLastMessage, currentLastMessage.name);
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// character has no expressions or it is not loaded
|
// character has no expressions or it is not loaded
|
||||||
if (Object.keys(spriteCache).length === 0) {
|
if (Object.keys(spriteCache).length === 0) {
|
||||||
@@ -562,8 +540,9 @@ async function moduleWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const offlineMode = $('.expression_settings .offline_mode');
|
const offlineMode = $('.expression_settings .offline_mode');
|
||||||
if (!modules.includes('classify')) {
|
if (!modules.includes('classify') && !extension_settings.expressions.local) {
|
||||||
$('.expression_settings').show();
|
$('#open_chat_expressions').show();
|
||||||
|
$('#no_chat_expressions').hide();
|
||||||
offlineMode.css('display', 'block');
|
offlineMode.css('display', 'block');
|
||||||
lastCharacter = context.groupId || context.characterId;
|
lastCharacter = context.groupId || context.characterId;
|
||||||
|
|
||||||
@@ -584,9 +563,19 @@ async function moduleWorker() {
|
|||||||
await forceUpdateVisualNovelMode();
|
await forceUpdateVisualNovelMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.groupId && !Array.isArray(spriteCache[spriteFolderName])) {
|
||||||
|
await validateImages(spriteFolderName, true);
|
||||||
|
await forceUpdateVisualNovelMode();
|
||||||
|
}
|
||||||
|
|
||||||
offlineMode.css('display', 'none');
|
offlineMode.css('display', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't bother classifying if current char has no sprites and no default expressions are enabled
|
||||||
|
if ((!Array.isArray(spriteCache[spriteFolderName]) || spriteCache[spriteFolderName].length === 0) && !extension_settings.expressions.showDefault) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// check if last message changed
|
// check if last message changed
|
||||||
if ((lastCharacter === context.characterId || lastCharacter === context.groupId)
|
if ((lastCharacter === context.characterId || lastCharacter === context.groupId)
|
||||||
&& lastMessage === currentLastMessage.mes) {
|
&& lastMessage === currentLastMessage.mes) {
|
||||||
@@ -595,9 +584,21 @@ async function moduleWorker() {
|
|||||||
|
|
||||||
// API is busy
|
// API is busy
|
||||||
if (inApiCall) {
|
if (inApiCall) {
|
||||||
|
console.debug('Classification API is busy');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throttle classification requests during streaming
|
||||||
|
if (!context.groupId && context.streamingProcessor && !context.streamingProcessor.isFinished) {
|
||||||
|
const now = Date.now();
|
||||||
|
const timeSinceLastServerResponse = now - lastServerResponseTime;
|
||||||
|
|
||||||
|
if (timeSinceLastServerResponse < STREAMING_UPDATE_INTERVAL) {
|
||||||
|
console.log('Streaming in progress: throttling expression update. Next update at ' + new Date(lastServerResponseTime + STREAMING_UPDATE_INTERVAL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inApiCall = true;
|
inApiCall = true;
|
||||||
let expression = await getExpressionLabel(currentLastMessage.mes);
|
let expression = await getExpressionLabel(currentLastMessage.mes);
|
||||||
@@ -615,7 +616,6 @@ async function moduleWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sendExpressionCall(spriteFolderName, expression, force, vnMode);
|
await sendExpressionCall(spriteFolderName, expression, force, vnMode);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -624,21 +624,12 @@ async function moduleWorker() {
|
|||||||
inApiCall = false;
|
inApiCall = false;
|
||||||
lastCharacter = context.groupId || context.characterId;
|
lastCharacter = context.groupId || context.characterId;
|
||||||
lastMessage = currentLastMessage.mes;
|
lastMessage = currentLastMessage.mes;
|
||||||
|
lastServerResponseTime = Date.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function talkingheadcheck() {
|
async function talkingHeadCheck() {
|
||||||
const context = getContext();
|
let spriteFolderName = getSpriteFolderName();
|
||||||
let spriteFolderName = context.name2;
|
|
||||||
const message = getLastCharacterMessage();
|
|
||||||
const avatarFileName = getSpriteFolderName(message);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await validateImages(spriteFolderName);
|
await validateImages(spriteFolderName);
|
||||||
@@ -659,11 +650,29 @@ async function talkingheadcheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function settalkingheadState(switch_var) {
|
function getSpriteFolderName(characterMessage = null, characterName = null) {
|
||||||
|
const context = getContext();
|
||||||
|
let spriteFolderName = characterName ?? context.name2;
|
||||||
|
const message = characterMessage ?? getLastCharacterMessage();
|
||||||
|
const avatarFileName = getFolderNameByMessage(message);
|
||||||
|
const expressionOverride = extension_settings.expressionOverrides.find(e => e.name == avatarFileName);
|
||||||
|
|
||||||
|
if (expressionOverride && expressionOverride.path) {
|
||||||
|
spriteFolderName = expressionOverride.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return spriteFolderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTalkingHeadState(switch_var) {
|
||||||
extension_settings.expressions.talkinghead = switch_var; // Store setting
|
extension_settings.expressions.talkinghead = switch_var; // Store setting
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
|
||||||
talkingheadcheck().then(result => {
|
if (extension_settings.expressions.local) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
talkingHeadCheck().then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
//console.log("talkinghead exists!");
|
//console.log("talkinghead exists!");
|
||||||
|
|
||||||
@@ -672,7 +681,7 @@ function settalkingheadState(switch_var) {
|
|||||||
} else {
|
} else {
|
||||||
unloadLiveChar();
|
unloadLiveChar();
|
||||||
}
|
}
|
||||||
handleImageChange(switch_var); // Change image as needed
|
handleImageChange(); // Change image as needed
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -681,7 +690,7 @@ function settalkingheadState(switch_var) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSpriteFolderName(message) {
|
function getFolderNameByMessage(message) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
let avatarPath = '';
|
let avatarPath = '';
|
||||||
|
|
||||||
@@ -712,27 +721,122 @@ async function sendExpressionCall(name, expression, force, vnMode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setSpriteSetCommand(_, folder) {
|
||||||
|
if (!folder) {
|
||||||
|
console.log('Clearing sprite set');
|
||||||
|
folder = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folder.startsWith('/') || folder.startsWith('\\')) {
|
||||||
|
folder = folder.slice(1);
|
||||||
|
|
||||||
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
|
folder = `${currentLastMessage.name}/${folder}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#expression_override").val(folder.trim());
|
||||||
|
onClickExpressionOverrideButton();
|
||||||
|
removeExpression();
|
||||||
|
moduleWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setSpriteSlashCommand(_, spriteId) {
|
||||||
|
if (!spriteId) {
|
||||||
|
console.log('No sprite id provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spriteId = spriteId.trim().toLowerCase();
|
||||||
|
|
||||||
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
|
const spriteFolderName = getSpriteFolderName(currentLastMessage, currentLastMessage.name);
|
||||||
|
await validateImages(spriteFolderName);
|
||||||
|
|
||||||
|
// Fuzzy search for sprite
|
||||||
|
const fuse = new Fuse(spriteCache[spriteFolderName], { keys: ['label'] });
|
||||||
|
const results = fuse.search(spriteId);
|
||||||
|
const spriteItem = results[0]?.item;
|
||||||
|
|
||||||
|
if (!spriteItem) {
|
||||||
|
console.log('No sprite found for search term ' + spriteId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vnMode = isVisualNovelMode();
|
||||||
|
await sendExpressionCall(spriteFolderName, spriteItem.label, true, vnMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the classification text to reduce the amount of text sent to the API.
|
||||||
|
* Quotes and asterisks are to be removed. If the text is less than 300 characters, it is returned as is.
|
||||||
|
* If the text is more than 300 characters, the first and last 150 characters are returned.
|
||||||
|
* The result is trimmed to the end of sentence.
|
||||||
|
* @param {string} text The text to process.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function sampleClassifyText(text) {
|
||||||
|
if (!text) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove asterisks and quotes
|
||||||
|
let result = text.replace(/[\*\"]/g, '');
|
||||||
|
|
||||||
|
const SAMPLE_THRESHOLD = 300;
|
||||||
|
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
|
||||||
|
|
||||||
|
if (text.length < SAMPLE_THRESHOLD) {
|
||||||
|
result = trimToEndSentence(result);
|
||||||
|
} else {
|
||||||
|
result = trimToEndSentence(result.slice(0, HALF_SAMPLE_THRESHOLD)) + ' ' + trimToStartSentence(result.slice(-HALF_SAMPLE_THRESHOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
async function getExpressionLabel(text) {
|
async function getExpressionLabel(text) {
|
||||||
// Return if text is undefined, saving a costly fetch request
|
// Return if text is undefined, saving a costly fetch request
|
||||||
if (!modules.includes('classify') || !text) {
|
if ((!modules.includes('classify') && !extension_settings.expressions.local) || !text) {
|
||||||
return FALLBACK_EXPRESSION;
|
return FALLBACK_EXPRESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(getApiUrl());
|
text = sampleClassifyText(text);
|
||||||
url.pathname = '/api/classify';
|
|
||||||
|
|
||||||
const apiResult = await doExtrasFetch(url, {
|
try {
|
||||||
method: 'POST',
|
if (extension_settings.expressions.local) {
|
||||||
headers: {
|
// Local transformers pipeline
|
||||||
'Content-Type': 'application/json',
|
const apiResult = await fetch('/api/extra/classify', {
|
||||||
'Bypass-Tunnel-Reminder': 'bypass',
|
method: 'POST',
|
||||||
},
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ text: text }),
|
body: JSON.stringify({ text: text }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiResult.ok) {
|
if (apiResult.ok) {
|
||||||
const data = await apiResult.json();
|
const data = await apiResult.json();
|
||||||
return data.classification[0].label;
|
return data.classification[0].label;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Extras
|
||||||
|
const url = new URL(getApiUrl());
|
||||||
|
url.pathname = '/api/classify';
|
||||||
|
|
||||||
|
const apiResult = await doExtrasFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Bypass-Tunnel-Reminder': 'bypass',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ text: text }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (apiResult.ok) {
|
||||||
|
const data = await apiResult.json();
|
||||||
|
return data.classification[0].label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return FALLBACK_EXPRESSION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,7 +860,8 @@ function removeExpression() {
|
|||||||
$('img.expression').off('error');
|
$('img.expression').off('error');
|
||||||
$('img.expression').prop('src', '');
|
$('img.expression').prop('src', '');
|
||||||
$('img.expression').removeClass('default');
|
$('img.expression').removeClass('default');
|
||||||
$('.expression_settings').hide();
|
$('#open_chat_expressions').hide();
|
||||||
|
$('#no_chat_expressions').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateImages(character, forceRedrawCached) {
|
async function validateImages(character, forceRedrawCached) {
|
||||||
@@ -782,9 +887,11 @@ async function validateImages(character, forceRedrawCached) {
|
|||||||
|
|
||||||
function drawSpritesList(character, labels, sprites) {
|
function drawSpritesList(character, labels, sprites) {
|
||||||
let validExpressions = [];
|
let validExpressions = [];
|
||||||
$('.expression_settings').show();
|
$('#no_chat_expressions').hide();
|
||||||
|
$('#open_chat_expressions').show();
|
||||||
$('#image_list').empty();
|
$('#image_list').empty();
|
||||||
$('#image_list').data('name', character);
|
$('#image_list').data('name', character);
|
||||||
|
$('#image_list_header_name').text(character);
|
||||||
|
|
||||||
if (!Array.isArray(labels)) {
|
if (!Array.isArray(labels)) {
|
||||||
return [];
|
return [];
|
||||||
@@ -792,27 +899,36 @@ function drawSpritesList(character, labels, sprites) {
|
|||||||
|
|
||||||
labels.sort().forEach((item) => {
|
labels.sort().forEach((item) => {
|
||||||
const sprite = sprites.find(x => x.label == item);
|
const sprite = sprites.find(x => x.label == item);
|
||||||
|
const isCustom = extension_settings.expressions.custom.includes(item);
|
||||||
|
|
||||||
if (sprite) {
|
if (sprite) {
|
||||||
validExpressions.push(sprite);
|
validExpressions.push(sprite);
|
||||||
$('#image_list').append(getListItem(item, sprite.path, 'success'));
|
$('#image_list').append(getListItem(item, sprite.path, 'success', isCustom));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure'));
|
$('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure', isCustom));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return validExpressions;
|
return validExpressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getListItem(item, imageSrc, textClass) {
|
/**
|
||||||
return renderExtensionTemplate(MODULE_NAME, 'list-item', { item, imageSrc, textClass });
|
* Renders a list item template for the expressions list.
|
||||||
|
* @param {string} item Expression name
|
||||||
|
* @param {string} imageSrc Path to image
|
||||||
|
* @param {'success' | 'failure'} textClass 'success' or 'failure'
|
||||||
|
* @param {boolean} isCustom If expression is added by user
|
||||||
|
* @returns {string} Rendered list item template
|
||||||
|
*/
|
||||||
|
function getListItem(item, imageSrc, textClass, isCustom) {
|
||||||
|
return renderExtensionTemplate(MODULE_NAME, 'list-item', { item, imageSrc, textClass, isCustom });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSpritesList(name) {
|
async function getSpritesList(name) {
|
||||||
console.debug('getting sprites list');
|
console.debug('getting sprites list');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`/get_sprites?name=${encodeURIComponent(name)}`);
|
const result = await fetch(`/api/sprites/get?name=${encodeURIComponent(name)}`);
|
||||||
let sprites = result.ok ? (await result.json()) : [];
|
let sprites = result.ok ? (await result.json()) : [];
|
||||||
return sprites;
|
return sprites;
|
||||||
}
|
}
|
||||||
@@ -822,40 +938,84 @@ async function getSpritesList(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getExpressionsList() {
|
function renderCustomExpressions() {
|
||||||
// get something for offline mode (default images)
|
if (!Array.isArray(extension_settings.expressions.custom)) {
|
||||||
if (!modules.includes('classify')) {
|
extension_settings.expressions.custom = [];
|
||||||
return DEFAULT_EXPRESSIONS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customExpressions = extension_settings.expressions.custom.sort((a, b) => a.localeCompare(b));
|
||||||
|
$('#expression_custom').empty();
|
||||||
|
|
||||||
|
for (const expression of customExpressions) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = expression;
|
||||||
|
option.text = expression;
|
||||||
|
$('#expression_custom').append(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customExpressions.length === 0) {
|
||||||
|
$('#expression_custom').append('<option value="" disabled selected>[ No custom expressions ]</option>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getExpressionsList() {
|
||||||
|
// Return cached list if available
|
||||||
if (Array.isArray(expressionsList)) {
|
if (Array.isArray(expressionsList)) {
|
||||||
return expressionsList;
|
return expressionsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(getApiUrl());
|
/**
|
||||||
url.pathname = '/api/classify/labels';
|
* Returns the list of expressions from the API or fallback in offline mode.
|
||||||
|
* @returns {Promise<string[]>}
|
||||||
|
*/
|
||||||
|
async function resolveExpressionsList() {
|
||||||
|
// get something for offline mode (default images)
|
||||||
|
if (!modules.includes('classify') && !extension_settings.expressions.local) {
|
||||||
|
return DEFAULT_EXPRESSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const apiResult = await doExtrasFetch(url, {
|
if (extension_settings.expressions.local) {
|
||||||
method: 'GET',
|
const apiResult = await fetch('/api/extra/classify/labels', {
|
||||||
headers: { 'Bypass-Tunnel-Reminder': 'bypass' },
|
method: 'POST',
|
||||||
});
|
headers: getRequestHeaders(),
|
||||||
|
});
|
||||||
|
|
||||||
if (apiResult.ok) {
|
if (apiResult.ok) {
|
||||||
|
const data = await apiResult.json();
|
||||||
|
expressionsList = data.labels;
|
||||||
|
return expressionsList;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const url = new URL(getApiUrl());
|
||||||
|
url.pathname = '/api/classify/labels';
|
||||||
|
|
||||||
const data = await apiResult.json();
|
const apiResult = await doExtrasFetch(url, {
|
||||||
expressionsList = data.labels;
|
method: 'GET',
|
||||||
return expressionsList;
|
headers: { 'Bypass-Tunnel-Reminder': 'bypass' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (apiResult.ok) {
|
||||||
|
|
||||||
|
const data = await apiResult.json();
|
||||||
|
expressionsList = data.labels;
|
||||||
|
return expressionsList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
|
||||||
console.log(error);
|
const result = await resolveExpressionsList();
|
||||||
return [];
|
result.push(...extension_settings.expressions.custom);
|
||||||
}
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setExpression(character, expression, force) {
|
async function setExpression(character, expression, force) {
|
||||||
if (!extension_settings.expressions.talkinghead) {
|
if (extension_settings.expressions.local || !extension_settings.expressions.talkinghead) {
|
||||||
console.debug('entered setExpressions');
|
console.debug('entered setExpressions');
|
||||||
await validateImages(character);
|
await validateImages(character);
|
||||||
const img = $('img.expression');
|
const img = $('img.expression');
|
||||||
@@ -968,12 +1128,12 @@ async function setExpression(character, expression, force) {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
||||||
talkingheadcheck().then(result => {
|
talkingHeadCheck().then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Find the <img> element with id="expression-image" and class="expression"
|
// Find the <img> element with id="expression-image" and class="expression"
|
||||||
const imgElement = document.querySelector('img#expression-image.expression');
|
const imgElement = document.querySelector('img#expression-image.expression');
|
||||||
//console.log("searching");
|
//console.log("searching");
|
||||||
if (imgElement) {
|
if (imgElement && imgElement instanceof HTMLImageElement) {
|
||||||
//console.log("setting value");
|
//console.log("setting value");
|
||||||
imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
|
imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
|
||||||
}
|
}
|
||||||
@@ -988,18 +1148,76 @@ async function setExpression(character, expression, force) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onClickExpressionImage() {
|
function onClickExpressionImage() {
|
||||||
// online mode doesn't need force set
|
const expression = $(this).attr('id');
|
||||||
if (modules.includes('classify')) {
|
setSpriteSlashCommand({}, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onClickExpressionAddCustom() {
|
||||||
|
let expressionName = await callPopup(renderExtensionTemplate(MODULE_NAME, 'add-custom-expression'), 'input');
|
||||||
|
|
||||||
|
if (!expressionName) {
|
||||||
|
console.debug('No custom expression name provided');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expression = $(this).attr('id');
|
expressionName = expressionName.trim().toLowerCase();
|
||||||
const name = getLastCharacterMessage().name;
|
|
||||||
|
|
||||||
if ($(this).find('.failure').length === 0) {
|
// a-z, 0-9, dashes and underscores only
|
||||||
setExpression(name, expression, true);
|
if (!/^[a-z0-9-_]+$/.test(expressionName)) {
|
||||||
|
toastr.info('Invalid custom expression name provided');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if expression name already exists in default expressions
|
||||||
|
if (DEFAULT_EXPRESSIONS.includes(expressionName)) {
|
||||||
|
toastr.info('Expression name already exists');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if expression name already exists in custom expressions
|
||||||
|
if (extension_settings.expressions.custom.includes(expressionName)) {
|
||||||
|
toastr.info('Custom expression already exists');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom expression into settings
|
||||||
|
extension_settings.expressions.custom.push(expressionName);
|
||||||
|
renderCustomExpressions();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
|
||||||
|
// Force refresh sprites list
|
||||||
|
expressionsList = null;
|
||||||
|
spriteCache = {};
|
||||||
|
moduleWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onClickExpressionRemoveCustom() {
|
||||||
|
const selectedExpression = $('#expression_custom').val();
|
||||||
|
|
||||||
|
if (!selectedExpression) {
|
||||||
|
console.debug('No custom expression selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmation = await callPopup(renderExtensionTemplate(MODULE_NAME, 'remove-custom-expression', { expression: selectedExpression }), 'confirm');
|
||||||
|
|
||||||
|
if (!confirmation) {
|
||||||
|
console.debug('Custom expression removal cancelled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove custom expression from settings
|
||||||
|
const index = extension_settings.expressions.custom.indexOf(selectedExpression);
|
||||||
|
extension_settings.expressions.custom.splice(index, 1);
|
||||||
|
renderCustomExpressions();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
|
||||||
|
// Force refresh sprites list
|
||||||
|
expressionsList = null;
|
||||||
|
spriteCache = {};
|
||||||
|
moduleWorker();
|
||||||
|
}
|
||||||
|
|
||||||
async function handleFileUpload(url, formData) {
|
async function handleFileUpload(url, formData) {
|
||||||
try {
|
try {
|
||||||
const data = await jQuery.ajax({
|
const data = await jQuery.ajax({
|
||||||
@@ -1042,7 +1260,7 @@ async function onClickExpressionUpload(event) {
|
|||||||
formData.append('label', id);
|
formData.append('label', id);
|
||||||
formData.append('avatar', file);
|
formData.append('avatar', file);
|
||||||
|
|
||||||
await handleFileUpload('/upload_sprite', formData);
|
await handleFileUpload('/api/sprites/upload', formData);
|
||||||
|
|
||||||
// Reset the input
|
// Reset the input
|
||||||
e.target.form.reset();
|
e.target.form.reset();
|
||||||
@@ -1057,7 +1275,7 @@ async function onClickExpressionUpload(event) {
|
|||||||
async function onClickExpressionOverrideButton() {
|
async function onClickExpressionOverrideButton() {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
const avatarFileName = getFolderNameByMessage(currentLastMessage);
|
||||||
|
|
||||||
// If the avatar name couldn't be found, abort.
|
// If the avatar name couldn't be found, abort.
|
||||||
if (!avatarFileName) {
|
if (!avatarFileName) {
|
||||||
@@ -1066,7 +1284,7 @@ async function onClickExpressionOverrideButton() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const overridePath = $("#expression_override").val();
|
const overridePath = String($("#expression_override").val());
|
||||||
const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) =>
|
const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) =>
|
||||||
e.name == avatarFileName
|
e.name == avatarFileName
|
||||||
);
|
);
|
||||||
@@ -1148,7 +1366,7 @@ async function onClickExpressionUploadPackButton() {
|
|||||||
formData.append('name', name);
|
formData.append('name', name);
|
||||||
formData.append('avatar', file);
|
formData.append('avatar', file);
|
||||||
|
|
||||||
const { count } = await handleFileUpload('/upload_sprite_pack', formData);
|
const { count } = await handleFileUpload('/api/sprites/upload-zip', formData);
|
||||||
toastr.success(`Uploaded ${count} image(s) for ${name}`);
|
toastr.success(`Uploaded ${count} image(s) for ${name}`);
|
||||||
|
|
||||||
// Reset the input
|
// Reset the input
|
||||||
@@ -1175,7 +1393,7 @@ async function onClickExpressionDelete(event) {
|
|||||||
const name = $('#image_list').data('name');
|
const name = $('#image_list').data('name');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetch('/delete_sprite', {
|
await fetch('/api/sprites/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ name, label: id }),
|
body: JSON.stringify({ name, label: id }),
|
||||||
@@ -1191,7 +1409,7 @@ async function onClickExpressionDelete(event) {
|
|||||||
|
|
||||||
function setExpressionOverrideHtml(forceClear = false) {
|
function setExpressionOverrideHtml(forceClear = false) {
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
const avatarFileName = getFolderNameByMessage(currentLastMessage);
|
||||||
if (!avatarFileName) {
|
if (!avatarFileName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1237,6 +1455,11 @@ function setExpressionOverrideHtml(forceClear = false) {
|
|||||||
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput);
|
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput);
|
||||||
$('#expression_upload_pack_button').on('click', onClickExpressionUploadPackButton);
|
$('#expression_upload_pack_button').on('click', onClickExpressionUploadPackButton);
|
||||||
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input');
|
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input');
|
||||||
|
$('#expression_local').prop('checked', extension_settings.expressions.local).on('input', function () {
|
||||||
|
extension_settings.expressions.local = !!$(this).prop('checked');
|
||||||
|
moduleWorker();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
$('#expression_override_cleanup_button').on('click', onClickExpressionOverrideRemoveAllButton);
|
$('#expression_override_cleanup_button').on('click', onClickExpressionOverrideRemoveAllButton);
|
||||||
$(document).on('dragstart', '.expression', (e) => {
|
$(document).on('dragstart', '.expression', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -1246,11 +1469,18 @@ function setExpressionOverrideHtml(forceClear = false) {
|
|||||||
$(document).on('click', '.expression_list_upload', onClickExpressionUpload);
|
$(document).on('click', '.expression_list_upload', onClickExpressionUpload);
|
||||||
$(document).on('click', '.expression_list_delete', onClickExpressionDelete);
|
$(document).on('click', '.expression_list_delete', onClickExpressionDelete);
|
||||||
$(window).on("resize", updateVisualNovelModeDebounced);
|
$(window).on("resize", updateVisualNovelModeDebounced);
|
||||||
$('.expression_settings').hide();
|
$("#open_chat_expressions").hide();
|
||||||
|
|
||||||
$('#image_type_toggle').on('click', function () {
|
$('#image_type_toggle').on('click', function () {
|
||||||
settalkingheadState(this.checked);
|
if (this instanceof HTMLInputElement) {
|
||||||
|
setTalkingHeadState(this.checked);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
renderCustomExpressions();
|
||||||
|
|
||||||
|
$('#expression_custom_add').on('click', onClickExpressionAddCustom);
|
||||||
|
$('#expression_custom_remove').on('click', onClickExpressionRemoveCustom);
|
||||||
}
|
}
|
||||||
|
|
||||||
addExpressionImage();
|
addExpressionImage();
|
||||||
@@ -1270,4 +1500,6 @@ function setExpressionOverrideHtml(forceClear = false) {
|
|||||||
});
|
});
|
||||||
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
|
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
|
||||||
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
|
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
|
||||||
|
registerSlashCommand('sprite', setSpriteSlashCommand, ['emote'], '<span class="monospace">(spriteId)</span> – force sets the sprite for the current character', true, true);
|
||||||
|
registerSlashCommand('spriteoverride', setSpriteSetCommand, ['costume'], '<span class="monospace">(optional folder)</span> – sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.', true, true);
|
||||||
})();
|
})();
|
||||||
|
@@ -7,6 +7,11 @@
|
|||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="expression_list_title {{textClass}}">{{item}}</span>
|
<div class="expression_list_title {{textClass}}">
|
||||||
|
<span>{{item}}</span>
|
||||||
|
{{#if isCustom}}
|
||||||
|
<small class="expression_list_custom">(custom)</small>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
<img class="expression_list_image" src="{{imageSrc}}" />
|
<img class="expression_list_image" src="{{imageSrc}}" />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
<h3>
|
||||||
|
Are you sure you want to remove the expression <tt>"{{expression}}"</tt>?
|
||||||
|
</h3>
|
||||||
|
<div>
|
||||||
|
Uploaded images will not be deleted, but will no longer be used by the extension.
|
||||||
|
</div>
|
||||||
|
<br>
|
@@ -6,35 +6,57 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<!-- Toggle button for aituber/static images -->
|
<label class="checkbox_label" for="expression_local" title="Use classification model without the Extras server.">
|
||||||
<div class="toggle_button">
|
<input id="expression_local" type="checkbox" />
|
||||||
<label class="switch">
|
<span data-i18n="Local server classification">Local server classification</span>
|
||||||
<input id="image_type_toggle" type="checkbox">
|
</label>
|
||||||
<span class="slider round"></span>
|
<label class="checkbox_label" for="expressions_show_default">
|
||||||
<label for="image_type_toggle">Image Type - talkinghead (extras)</label>
|
<input id="expressions_show_default" type="checkbox">
|
||||||
</label>
|
<span>Show default images (emojis) if sprite missing</span>
|
||||||
</div>
|
</label>
|
||||||
<div class="offline_mode">
|
<label id="image_type_block" class="checkbox_label" for="image_type_toggle">
|
||||||
<small>You are in offline mode. Click on the image below to set the expression.</small>
|
<input id="image_type_toggle" type="checkbox">
|
||||||
</div>
|
<span>Image Type - talkinghead (extras)</span>
|
||||||
<div class="flex-container flexnowrap">
|
</label>
|
||||||
<input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
|
<div class="expression_custom_block m-b-1 m-t-1">
|
||||||
<input id="expression_override_button" class="menu_button" type="submit" value="Submit" />
|
<label for="expression_custom">Custom Expressions</label>
|
||||||
</div>
|
<small>Can be set manually or with an <tt>/emote</tt> slash command.</small>
|
||||||
<div id="image_list"></div>
|
<div class="flex-container">
|
||||||
<div class="expression_buttons flex-container spaceEvenly">
|
<select id="expression_custom" class="flex1 margin0"><select>
|
||||||
<div id="expression_upload_pack_button" class="menu_button">
|
<i id="expression_custom_add" class="menu_button fa-solid fa-plus margin0" title="Add"></i>
|
||||||
<i class="fa-solid fa-file-zipper"></i>
|
<i id="expression_custom_remove" class="menu_button fa-solid fa-xmark margin0" title="Remove"></i>
|
||||||
<span>Upload sprite pack (ZIP)</span>
|
|
||||||
</div>
|
|
||||||
<div id="expression_override_cleanup_button" class="menu_button">
|
|
||||||
<i class="fa-solid fa-trash-can"></i>
|
|
||||||
<span>Remove all image overrides</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character.
|
<div id="no_chat_expressions">
|
||||||
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
|
Open a chat to see the character expressions.
|
||||||
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
|
</div>
|
||||||
|
<div id="open_chat_expressions">
|
||||||
|
<div class="offline_mode">
|
||||||
|
<small>You are in offline mode. Click on the image below to set the expression.</small>
|
||||||
|
</div>
|
||||||
|
<label for="expression_override">Sprite Folder Override</label>
|
||||||
|
<small>Use a forward slash to specify a subfolder. Example: <tt>Bob/formal</tt></small>
|
||||||
|
<div class="flex-container flexnowrap">
|
||||||
|
<input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
|
||||||
|
<input id="expression_override_button" class="menu_button" type="submit" value="Submit" />
|
||||||
|
</div>
|
||||||
|
<h3 id="image_list_header">
|
||||||
|
<strong>Sprite set:</strong> <span id="image_list_header_name"></span>
|
||||||
|
</h3>
|
||||||
|
<div id="image_list"></div>
|
||||||
|
<div class="expression_buttons flex-container spaceEvenly">
|
||||||
|
<div id="expression_upload_pack_button" class="menu_button">
|
||||||
|
<i class="fa-solid fa-file-zipper"></i>
|
||||||
|
<span>Upload sprite pack (ZIP)</span>
|
||||||
|
</div>
|
||||||
|
<div id="expression_override_cleanup_button" class="menu_button">
|
||||||
|
<i class="fa-solid fa-trash-can"></i>
|
||||||
|
<span>Remove all image overrides</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character.
|
||||||
|
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form>
|
<form>
|
||||||
|
@@ -49,6 +49,18 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
resize: both;
|
resize: both;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#no_chat_expressions {
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image_list_header_name {
|
||||||
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.expression {
|
img.expression {
|
||||||
@@ -112,6 +124,8 @@ img.expression.default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expression_list_buttons {
|
.expression_list_buttons {
|
||||||
|
@@ -7,7 +7,8 @@ import {
|
|||||||
import { selected_group } from "../../group-chats.js";
|
import { selected_group } from "../../group-chats.js";
|
||||||
import { loadFileToDocument } from "../../utils.js";
|
import { loadFileToDocument } from "../../utils.js";
|
||||||
import { loadMovingUIState } from '../../power-user.js';
|
import { loadMovingUIState } from '../../power-user.js';
|
||||||
import { dragElement } from '../../RossAscends-mods.js'
|
import { dragElement } from '../../RossAscends-mods.js';
|
||||||
|
import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
|
|
||||||
const extensionName = "gallery";
|
const extensionName = "gallery";
|
||||||
const extensionFolderPath = `scripts/extensions/${extensionName}/`;
|
const extensionFolderPath = `scripts/extensions/${extensionName}/`;
|
||||||
@@ -367,6 +368,21 @@ function makeDragImg(id, url) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a given ID to ensure it can be used as an HTML ID.
|
||||||
|
* This function replaces spaces and non-word characters with dashes.
|
||||||
|
* It also removes any non-ASCII characters.
|
||||||
|
* @param {string} id - The ID to be sanitized.
|
||||||
|
* @returns {string} - The sanitized ID.
|
||||||
|
*/
|
||||||
|
function sanitizeHTMLId(id){
|
||||||
|
// Replace spaces and non-word characters
|
||||||
|
id = id.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\x00-\x7F]/g, '-')
|
||||||
|
.replace(/\W/g, '');
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a list of items (containing URLs) and creates a draggable box for the first item.
|
* Processes a list of items (containing URLs) and creates a draggable box for the first item.
|
||||||
@@ -379,10 +395,17 @@ function makeDragImg(id, url) {
|
|||||||
*/
|
*/
|
||||||
function viewWithDragbox(items) {
|
function viewWithDragbox(items) {
|
||||||
if (items && items.length > 0) {
|
if (items && items.length > 0) {
|
||||||
var url = items[0].responsiveURL(); // Get the URL of the clicked image/video
|
const url = items[0].responsiveURL(); // Get the URL of the clicked image/video
|
||||||
// ID should just be the last part of the URL, removing the extension
|
// ID should just be the last part of the URL, removing the extension
|
||||||
var id = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
|
const id = sanitizeHTMLId(url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.')));
|
||||||
makeDragImg(id, url);
|
makeDragImg(id, url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Registers a simple command for opening the char gallery.
|
||||||
|
registerSlashCommand("show-gallery", showGalleryCommand, ["sg"], "– shows the gallery", true, true);
|
||||||
|
|
||||||
|
function showGalleryCommand(args) {
|
||||||
|
showCharGallery();
|
||||||
|
}
|
||||||
|
@@ -62,11 +62,11 @@ const generateDebounced = debounce(() => generateHypeBot(), 500);
|
|||||||
* @param {string} text Text to set
|
* @param {string} text Text to set
|
||||||
*/
|
*/
|
||||||
function setHypeBotText(text) {
|
function setHypeBotText(text) {
|
||||||
const blockA = $('#chat');
|
const chatBlock = $('#chat');
|
||||||
var originalScrollBottom = blockA[0].scrollHeight - (blockA.scrollTop() + blockA.outerHeight());
|
const originalScrollBottom = chatBlock[0].scrollHeight - (chatBlock.scrollTop() + chatBlock.outerHeight());
|
||||||
hypeBotBar.html(DOMPurify.sanitize(text));
|
hypeBotBar.html(DOMPurify.sanitize(text));
|
||||||
var newScrollTop = blockA[0].scrollHeight - (blockA.outerHeight() + originalScrollBottom);
|
const newScrollTop = chatBlock[0].scrollHeight - (chatBlock.outerHeight() + originalScrollBottom);
|
||||||
blockA.scrollTop(newScrollTop);
|
chatBlock.scrollTop(newScrollTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +159,7 @@ async function generateHypeBot() {
|
|||||||
|
|
||||||
abortController = new AbortController();
|
abortController = new AbortController();
|
||||||
|
|
||||||
const response = await fetch('/generate_novelai', {
|
const response = await fetch('/api/novelai/generate', {
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify(parameters),
|
body: JSON.stringify(parameters),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -191,11 +191,13 @@ jQuery(() => {
|
|||||||
settings.enabled = $('#hypebot_enabled').prop('checked');
|
settings.enabled = $('#hypebot_enabled').prop('checked');
|
||||||
hypeBotBar.toggle(settings.enabled);
|
hypeBotBar.toggle(settings.enabled);
|
||||||
abortController?.abort();
|
abortController?.abort();
|
||||||
|
Object.assign(extension_settings.hypebot, settings);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#hypebot_name').val(settings.name).on('input', () => {
|
$('#hypebot_name').val(settings.name).on('input', () => {
|
||||||
settings.name = String($('#hypebot_name').val());
|
settings.name = String($('#hypebot_name').val());
|
||||||
|
Object.assign(extension_settings.hypebot, settings);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
54
public/scripts/extensions/idle/dropdown.html
Normal file
54
public/scripts/extensions/idle/dropdown.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="idle-settings">
|
||||||
|
<div class="inline-drawer">
|
||||||
|
<div class="inline-drawer-toggle inline-drawer-header" title="Indicates the settings for the idle feature.">
|
||||||
|
<b>Idle</b>
|
||||||
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-drawer-content">
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_enabled" type="checkbox" title="Toggle to enable or disable the idle feature." />
|
||||||
|
<label for="idle_enabled">Enabled</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_repeats" class="text_pole widthUnset" type="number" min="0" max="100000" step="1" title="The number of times the idle action will be prompted." />
|
||||||
|
<label for="idle_repeats">Idle Prompt Count</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container" style="display: none;">
|
||||||
|
<input id="idle_timer_min" class="text_pole widthUnset" type="number" min="0" max="600000" step="1" title="The minimum amount of time in seconds before the idle action is triggered." />
|
||||||
|
<label for="idle_timer_min">Idle Timer Minimum (seconds)</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_timer" class="text_pole widthUnset" type="number" min="0" max="600000" step="1" title="The amount of time in seconds before the idle action is triggered." />
|
||||||
|
<label for="idle_timer">Idle Timer (seconds)</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<label for="idle_prompts">Idle Prompts</label>
|
||||||
|
<textarea id="idle_prompts" class="text_pole textarea_compact" rows="6" title="The prompts to be sent to initial the idle reply (newline seperated)."></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_use_continuation" type="checkbox" title="Indicates whether the idle action will just use the 'Continue' function instead of a prompt." />
|
||||||
|
<label for="idle_use_continuation">Use Continuation</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_random_time" type="checkbox" title="Indicates if the idle time should be randomized between a min/max value." />
|
||||||
|
<label for="idle_random_time">Randomize Time</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<input id="idle_include_prompt" type="checkbox" title="Indicates if the idle prompting should be included in context. (Sends as user)" />
|
||||||
|
<label for="idle_include_prompt">Include Idle Prompt</label>
|
||||||
|
</div>
|
||||||
|
<div class="idle_block flex-container">
|
||||||
|
<label for="idle_sendAs">Send As</label>
|
||||||
|
<select id="idle_sendAs" class="text_pole" title="Determines how the idle message prompting is sent; as a user, character, system, or raw message.">
|
||||||
|
<option value="user">User</option>
|
||||||
|
<option value="char">Character</option>
|
||||||
|
<option value="sys">System</option>
|
||||||
|
<option value="raw">Raw</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="sysHR" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
329
public/scripts/extensions/idle/index.js
Normal file
329
public/scripts/extensions/idle/index.js
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
import {
|
||||||
|
saveSettingsDebounced,
|
||||||
|
substituteParams
|
||||||
|
} from "../../../script.js";
|
||||||
|
import { debounce } from "../../utils.js";
|
||||||
|
import { promptQuietForLoudResponse, sendMessageAs, sendNarratorMessage } from "../../slash-commands.js";
|
||||||
|
import { extension_settings, getContext, renderExtensionTemplate } from "../../extensions.js";
|
||||||
|
import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
|
const extensionName = "idle";
|
||||||
|
|
||||||
|
let idleTimer = null;
|
||||||
|
let repeatCount = 0;
|
||||||
|
|
||||||
|
let defaultSettings = {
|
||||||
|
enabled: false,
|
||||||
|
timer: 120,
|
||||||
|
prompts: [
|
||||||
|
"*stands silently, looking deep in thought*",
|
||||||
|
"*pauses, eyes wandering over the surroundings*",
|
||||||
|
"*hesitates, appearing lost for a moment*",
|
||||||
|
"*takes a deep breath, collecting their thoughts*",
|
||||||
|
"*gazes into the distance, seemingly distracted*",
|
||||||
|
"*remains still, absorbing the ambiance*",
|
||||||
|
"*lingers in silence, a contemplative look on their face*",
|
||||||
|
"*stops, fingers brushing against an old memory*",
|
||||||
|
"*seems to drift into a momentary daydream*",
|
||||||
|
"*waits quietly, allowing the weight of the moment to settle*",
|
||||||
|
],
|
||||||
|
useContinuation: true,
|
||||||
|
repeats: 2, // 0 = infinite
|
||||||
|
sendAs: "user",
|
||||||
|
randomTime: false,
|
||||||
|
timeMin: 60,
|
||||||
|
includePrompt: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: Can we make this a generic function?
|
||||||
|
/**
|
||||||
|
* Load the extension settings and set defaults if they don't exist.
|
||||||
|
*/
|
||||||
|
async function loadSettings() {
|
||||||
|
if (!extension_settings.idle) {
|
||||||
|
console.log("Creating extension_settings.idle");
|
||||||
|
extension_settings.idle = {};
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(defaultSettings)) {
|
||||||
|
if (!extension_settings.idle.hasOwnProperty(key)) {
|
||||||
|
console.log(`Setting default for: ${key}`);
|
||||||
|
extension_settings.idle[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
populateUIWithSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Can we make this a generic function too?
|
||||||
|
/**
|
||||||
|
* Populate the UI components with values from the extension settings.
|
||||||
|
*/
|
||||||
|
function populateUIWithSettings() {
|
||||||
|
$("#idle_timer").val(extension_settings.idle.timer).trigger("input");
|
||||||
|
$("#idle_prompts").val(extension_settings.idle.prompts.join("\n")).trigger("input");
|
||||||
|
$("#idle_use_continuation").prop("checked", extension_settings.idle.useContinuation).trigger("input");
|
||||||
|
$("#idle_enabled").prop("checked", extension_settings.idle.enabled).trigger("input");
|
||||||
|
$("#idle_repeats").val(extension_settings.idle.repeats).trigger("input");
|
||||||
|
$("#idle_sendAs").val(extension_settings.idle.sendAs).trigger("input");
|
||||||
|
$("#idle_random_time").prop("checked", extension_settings.idle.randomTime).trigger("input");
|
||||||
|
$("#idle_timer_min").val(extension_settings.idle.timerMin).trigger("input");
|
||||||
|
$("#idle_include_prompt").prop("checked", extension_settings.idle.includePrompt).trigger("input");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the idle timer based on the extension settings and context.
|
||||||
|
*/
|
||||||
|
function resetIdleTimer() {
|
||||||
|
console.debug("Resetting idle timer");
|
||||||
|
if (idleTimer) clearTimeout(idleTimer);
|
||||||
|
let context = getContext();
|
||||||
|
if (!context.characterId && !context.groupID) return;
|
||||||
|
if (!extension_settings.idle.enabled) return;
|
||||||
|
if (extension_settings.idle.randomTime) {
|
||||||
|
// ensure these are ints
|
||||||
|
let min = extension_settings.idle.timerMin;
|
||||||
|
let max = extension_settings.idle.timer;
|
||||||
|
min = parseInt(min);
|
||||||
|
max = parseInt(max);
|
||||||
|
let randomTime = (Math.random() * (max - min + 1)) + min;
|
||||||
|
idleTimer = setTimeout(sendIdlePrompt, 1000 * randomTime);
|
||||||
|
} else {
|
||||||
|
idleTimer = setTimeout(sendIdlePrompt, 1000 * extension_settings.idle.timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a random idle prompt to the AI based on the extension settings.
|
||||||
|
* Checks conditions like if the extension is enabled and repeat conditions.
|
||||||
|
*/
|
||||||
|
async function sendIdlePrompt() {
|
||||||
|
if (!extension_settings.idle.enabled) return;
|
||||||
|
|
||||||
|
// Check repeat conditions and waiting for a response
|
||||||
|
if (repeatCount >= extension_settings.idle.repeats || $('#mes_stop').is(':visible')) {
|
||||||
|
//console.debug("Not sending idle prompt due to repeat conditions or waiting for a response.");
|
||||||
|
resetIdleTimer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomPrompt = extension_settings.idle.prompts[
|
||||||
|
Math.floor(Math.random() * extension_settings.idle.prompts.length)
|
||||||
|
];
|
||||||
|
|
||||||
|
sendPrompt(randomPrompt);
|
||||||
|
repeatCount++;
|
||||||
|
resetIdleTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add our prompt to the chat and then send the chat to the backend.
|
||||||
|
* @param {string} sendAs - The type of message to send. "user", "char", or "sys".
|
||||||
|
* @param {string} prompt - The prompt text to send to the AI.
|
||||||
|
*/
|
||||||
|
function sendLoud(sendAs, prompt) {
|
||||||
|
if (sendAs === "user") {
|
||||||
|
prompt = substituteParams(prompt);
|
||||||
|
|
||||||
|
$("#send_textarea").val(prompt);
|
||||||
|
|
||||||
|
// Set the focus back to the textarea
|
||||||
|
$("#send_textarea").focus();
|
||||||
|
|
||||||
|
$("#send_but").trigger('click');
|
||||||
|
} else if (sendAs === "char") {
|
||||||
|
sendMessageAs("", `${getContext().name2}\n${prompt}`);
|
||||||
|
promptQuietForLoudResponse(sendAs, "");
|
||||||
|
} else if (sendAs === "sys") {
|
||||||
|
sendNarratorMessage("", prompt);
|
||||||
|
promptQuietForLoudResponse(sendAs, "");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(`Unknown sendAs value: ${sendAs}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the provided prompt to the AI. Determines method based on continuation setting.
|
||||||
|
* @param {string} prompt - The prompt text to send to the AI.
|
||||||
|
*/
|
||||||
|
function sendPrompt(prompt) {
|
||||||
|
clearTimeout(idleTimer);
|
||||||
|
$("#send_textarea").off("input");
|
||||||
|
|
||||||
|
if (extension_settings.idle.useContinuation) {
|
||||||
|
$('#option_continue').trigger('click');
|
||||||
|
console.debug("Sending idle prompt with continuation");
|
||||||
|
} else {
|
||||||
|
console.debug("Sending idle prompt");
|
||||||
|
console.log(extension_settings.idle);
|
||||||
|
if (extension_settings.idle.includePrompt) {
|
||||||
|
sendLoud(extension_settings.idle.sendAs, prompt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
promptQuietForLoudResponse(extension_settings.idle.sendAs, prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the settings HTML and append to the designated area.
|
||||||
|
*/
|
||||||
|
async function loadSettingsHTML() {
|
||||||
|
const settingsHtml = renderExtensionTemplate(extensionName, "dropdown");
|
||||||
|
$("#extensions_settings2").append(settingsHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a specific setting based on user input.
|
||||||
|
* @param {string} elementId - The HTML element ID tied to the setting.
|
||||||
|
* @param {string} property - The property name in the settings object.
|
||||||
|
* @param {boolean} [isCheckbox=false] - Whether the setting is a checkbox.
|
||||||
|
*/
|
||||||
|
function updateSetting(elementId, property, isCheckbox = false) {
|
||||||
|
let value = $(`#${elementId}`).val();
|
||||||
|
if (isCheckbox) {
|
||||||
|
value = $(`#${elementId}`).prop('checked');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property === "prompts") {
|
||||||
|
value = value.split("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_settings.idle[property] = value;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach an input listener to a UI component to update the corresponding setting.
|
||||||
|
* @param {string} elementId - The HTML element ID tied to the setting.
|
||||||
|
* @param {string} property - The property name in the settings object.
|
||||||
|
* @param {boolean} [isCheckbox=false] - Whether the setting is a checkbox.
|
||||||
|
*/
|
||||||
|
function attachUpdateListener(elementId, property, isCheckbox = false) {
|
||||||
|
$(`#${elementId}`).on('input', debounce(() => {
|
||||||
|
updateSetting(elementId, property, isCheckbox);
|
||||||
|
}, 250));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the enabling or disabling of the idle extension.
|
||||||
|
* Adds or removes the idle listeners based on the checkbox's state.
|
||||||
|
*/
|
||||||
|
function handleIdleEnabled() {
|
||||||
|
if (!extension_settings.idle.enabled) {
|
||||||
|
clearTimeout(idleTimer);
|
||||||
|
removeIdleListeners();
|
||||||
|
} else {
|
||||||
|
resetIdleTimer();
|
||||||
|
attachIdleListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup input listeners for the various settings and actions related to the idle extension.
|
||||||
|
*/
|
||||||
|
function setupListeners() {
|
||||||
|
const settingsToWatch = [
|
||||||
|
['idle_timer', 'timer'],
|
||||||
|
['idle_prompts', 'prompts'],
|
||||||
|
['idle_use_continuation', 'useContinuation', true],
|
||||||
|
['idle_enabled', 'enabled', true],
|
||||||
|
['idle_repeats', 'repeats'],
|
||||||
|
['idle_sendAs', 'sendAs'],
|
||||||
|
['idle_random_time', 'randomTime', true],
|
||||||
|
['idle_timer_min', 'timerMin'],
|
||||||
|
['idle_include_prompt', 'includePrompt', true]
|
||||||
|
];
|
||||||
|
settingsToWatch.forEach(setting => {
|
||||||
|
attachUpdateListener(...setting);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Idleness listeners, could be made better
|
||||||
|
$('#idle_enabled').on('input', debounce(handleIdleEnabled, 250));
|
||||||
|
|
||||||
|
// Add the idle listeners initially if the idle feature is enabled
|
||||||
|
if (extension_settings.idle.enabled) {
|
||||||
|
attachIdleListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
//show/hide timer min parent div
|
||||||
|
$('#idle_random_time').on('input', function () {
|
||||||
|
if ($(this).prop('checked')) {
|
||||||
|
$('#idle_timer_min').parent().show();
|
||||||
|
} else {
|
||||||
|
$('#idle_timer_min').parent().hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#idle_timer').trigger('input');
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we're including the prompt, hide raw from the sendAs dropdown
|
||||||
|
$('#idle_include_prompt').on('input', function () {
|
||||||
|
if ($(this).prop('checked')) {
|
||||||
|
$('#idle_sendAs option[value="raw"]').hide();
|
||||||
|
} else {
|
||||||
|
$('#idle_sendAs option[value="raw"]').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//make sure timer min is less than timer
|
||||||
|
$('#idle_timer').on('input', function () {
|
||||||
|
if ($('#idle_random_time').prop('checked')) {
|
||||||
|
if ($(this).val() < $('#idle_timer_min').val()) {
|
||||||
|
$('#idle_timer_min').val($(this).val());
|
||||||
|
$('#idle_timer_min').trigger('input');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const debouncedActivityHandler = debounce((event) => {
|
||||||
|
// Check if the event target (or any of its parents) has the id "option_continue"
|
||||||
|
if ($(event.target).closest('#option_continue').length) {
|
||||||
|
return; // Do not proceed if the click was on (or inside) an element with id "option_continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug("Activity detected, resetting idle timer");
|
||||||
|
resetIdleTimer();
|
||||||
|
repeatCount = 0;
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
function attachIdleListeners() {
|
||||||
|
$(document).on("click keypress", debouncedActivityHandler);
|
||||||
|
document.addEventListener('keydown', debouncedActivityHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove idle-specific listeners.
|
||||||
|
*/
|
||||||
|
function removeIdleListeners() {
|
||||||
|
$(document).off("click keypress", debouncedActivityHandler);
|
||||||
|
document.removeEventListener('keydown', debouncedActivityHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleIdle() {
|
||||||
|
extension_settings.idle.enabled = !extension_settings.idle.enabled;
|
||||||
|
$('#idle_enabled').prop('checked', extension_settings.idle.enabled);
|
||||||
|
$('#idle_enabled').trigger('input');
|
||||||
|
toastr.info(`Idle mode ${extension_settings.idle.enabled ? "enabled" : "disabled"}.`);
|
||||||
|
resetIdleTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jQuery(async () => {
|
||||||
|
await loadSettingsHTML();
|
||||||
|
loadSettings();
|
||||||
|
setupListeners();
|
||||||
|
if (extension_settings.idle.enabled) {
|
||||||
|
resetIdleTimer();
|
||||||
|
}
|
||||||
|
// once the doc is ready, check if random time is checked and hide/show timer min
|
||||||
|
if ($('#idle_random_time').prop('checked')) {
|
||||||
|
$('#idle_timer_min').parent().show();
|
||||||
|
}
|
||||||
|
registerSlashCommand('idle', toggleIdle, [], '– toggles idle mode', true, true);
|
||||||
|
});
|
12
public/scripts/extensions/idle/manifest.json
Normal file
12
public/scripts/extensions/idle/manifest.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"display_name": "Idle",
|
||||||
|
"loading_order": 6,
|
||||||
|
"requires": [],
|
||||||
|
"optional": [
|
||||||
|
],
|
||||||
|
"js": "index.js",
|
||||||
|
"css": "style.css",
|
||||||
|
"author": "City-Unit",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||||
|
}
|
3
public/scripts/extensions/idle/style.css
Normal file
3
public/scripts/extensions/idle/style.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idle_block {
|
||||||
|
align-items: center;
|
||||||
|
}
|
@@ -508,7 +508,6 @@ async function onSelectInjectFile(e) {
|
|||||||
meta: JSON.stringify({
|
meta: JSON.stringify({
|
||||||
name: file.name,
|
name: file.name,
|
||||||
is_user: false,
|
is_user: false,
|
||||||
is_name: false,
|
|
||||||
is_system: false,
|
is_system: false,
|
||||||
send_date: humanizedDateTime(),
|
send_date: humanizedDateTime(),
|
||||||
mes: m,
|
mes: m,
|
||||||
@@ -686,7 +685,6 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
|||||||
const charname = context.name2;
|
const charname = context.name2;
|
||||||
newChat.push(
|
newChat.push(
|
||||||
{
|
{
|
||||||
is_name: false,
|
|
||||||
is_user: false,
|
is_user: false,
|
||||||
mes: `[Use these past chat exchanges to inform ${charname}'s next response:`,
|
mes: `[Use these past chat exchanges to inform ${charname}'s next response:`,
|
||||||
name: "system",
|
name: "system",
|
||||||
@@ -696,7 +694,6 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
|||||||
newChat.push(...queriedMessages.map(m => m.meta).filter(onlyUnique).map(JSON.parse));
|
newChat.push(...queriedMessages.map(m => m.meta).filter(onlyUnique).map(JSON.parse));
|
||||||
newChat.push(
|
newChat.push(
|
||||||
{
|
{
|
||||||
is_name: false,
|
|
||||||
is_user: false,
|
is_user: false,
|
||||||
mes: `]\n`,
|
mes: `]\n`,
|
||||||
name: "system",
|
name: "system",
|
||||||
@@ -739,7 +736,7 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
|||||||
// No memories? No prompt.
|
// No memories? No prompt.
|
||||||
const promptBlob = (tokenApprox == 0) ? "" : wrapperMsg.replace('{{memories}}', allMemoryBlob);
|
const promptBlob = (tokenApprox == 0) ? "" : wrapperMsg.replace('{{memories}}', allMemoryBlob);
|
||||||
console.debug("CHROMADB: prompt blob: %o", promptBlob);
|
console.debug("CHROMADB: prompt blob: %o", promptBlob);
|
||||||
context.setExtensionPrompt(MODULE_NAME, promptBlob, extension_prompt_types.AFTER_SCENARIO);
|
context.setExtensionPrompt(MODULE_NAME, promptBlob, extension_prompt_types.IN_PROMPT);
|
||||||
}
|
}
|
||||||
if (selectedStrategy === 'custom') {
|
if (selectedStrategy === 'custom') {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
@@ -752,7 +749,6 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
|||||||
|
|
||||||
newChat.push(
|
newChat.push(
|
||||||
{
|
{
|
||||||
is_name: false,
|
|
||||||
is_user: false,
|
is_user: false,
|
||||||
mes: recallStart,
|
mes: recallStart,
|
||||||
name: "system",
|
name: "system",
|
||||||
@@ -762,7 +758,6 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
|||||||
newChat.push(...queriedMessages.map(m => m.meta).filter(onlyUnique).map(JSON.parse));
|
newChat.push(...queriedMessages.map(m => m.meta).filter(onlyUnique).map(JSON.parse));
|
||||||
newChat.push(
|
newChat.push(
|
||||||
{
|
{
|
||||||
is_name: false,
|
|
||||||
is_user: false,
|
is_user: false,
|
||||||
mes: recallEnd + `\n`,
|
mes: recallEnd + `\n`,
|
||||||
name: "system",
|
name: "system",
|
||||||
|
@@ -2,6 +2,7 @@ import { getStringHash, debounce, waitUntilCondition, extractAllWords } from "..
|
|||||||
import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
||||||
import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js";
|
import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js";
|
||||||
import { is_group_generating, selected_group } from "../../group-chats.js";
|
import { is_group_generating, selected_group } from "../../group-chats.js";
|
||||||
|
import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = '1_memory';
|
const MODULE_NAME = '1_memory';
|
||||||
@@ -63,7 +64,7 @@ const defaultSettings = {
|
|||||||
source: summary_sources.extras,
|
source: summary_sources.extras,
|
||||||
prompt: defaultPrompt,
|
prompt: defaultPrompt,
|
||||||
template: defaultTemplate,
|
template: defaultTemplate,
|
||||||
position: extension_prompt_types.AFTER_SCENARIO,
|
position: extension_prompt_types.IN_PROMPT,
|
||||||
depth: 2,
|
depth: 2,
|
||||||
promptWords: 200,
|
promptWords: 200,
|
||||||
promptMinWords: 25,
|
promptMinWords: 25,
|
||||||
@@ -190,18 +191,21 @@ function onMemoryPromptInput() {
|
|||||||
function onMemoryTemplateInput() {
|
function onMemoryTemplateInput() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
extension_settings.memory.template = value;
|
extension_settings.memory.template = value;
|
||||||
|
reinsertMemory();
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMemoryDepthInput() {
|
function onMemoryDepthInput() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
extension_settings.memory.depth = Number(value);
|
extension_settings.memory.depth = Number(value);
|
||||||
|
reinsertMemory();
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMemoryPositionChange(e) {
|
function onMemoryPositionChange(e) {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
extension_settings.memory.position = value;
|
extension_settings.memory.position = value;
|
||||||
|
reinsertMemory();
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +396,7 @@ async function summarizeChatMain(context, force) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = await generateQuietPrompt(prompt);
|
const summary = await generateQuietPrompt(prompt, false);
|
||||||
const newContext = getContext();
|
const newContext = getContext();
|
||||||
|
|
||||||
// something changed during summarization request
|
// something changed during summarization request
|
||||||
@@ -517,6 +521,11 @@ function onMemoryContentInput() {
|
|||||||
setMemoryContext(value, true);
|
setMemoryContext(value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reinsertMemory() {
|
||||||
|
const existingValue = $('#memory_contents').val();
|
||||||
|
setMemoryContext(existingValue, false);
|
||||||
|
}
|
||||||
|
|
||||||
function setMemoryContext(value, saveToMessage) {
|
function setMemoryContext(value, saveToMessage) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_settings.memory.position, extension_settings.memory.depth);
|
context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_settings.memory.position, extension_settings.memory.depth);
|
||||||
@@ -565,13 +574,17 @@ jQuery(function () {
|
|||||||
</div>
|
</div>
|
||||||
<label for="memory_position">Injection position:</label>
|
<label for="memory_position">Injection position:</label>
|
||||||
<div class="radio_group">
|
<div class="radio_group">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="memory_position" value="2" />
|
||||||
|
Before Main Prompt / Story String
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="memory_position" value="0" />
|
<input type="radio" name="memory_position" value="0" />
|
||||||
After Main Prompt / Story String
|
After Main Prompt / Story String
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="memory_position" value="1" />
|
<input type="radio" name="memory_position" value="1" />
|
||||||
In-chat @ Depth <input id="memory_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
In-chat @ Depth <input id="memory_depth" class="text_pole widthUnset" type="number" min="0" max="999" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div data-source="main" class="memory_contents_controls">
|
<div data-source="main" class="memory_contents_controls">
|
||||||
@@ -637,4 +650,5 @@ jQuery(function () {
|
|||||||
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
|
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
|
||||||
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
|
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
|
||||||
eventSource.on(event_types.CHAT_CHANGED, onChatEvent);
|
eventSource.on(event_types.CHAT_CHANGED, onChatEvent);
|
||||||
|
registerSlashCommand('summarize', forceSummarizeChat, [], '– forces the summarization of the current chat using the Main API', true, true);
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user