mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
1 Commits
1.12.14
...
mordred-te
Author | SHA1 | Date | |
---|---|---|---|
|
c2be2e5521 |
@@ -3,9 +3,6 @@ module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
],
|
||||
plugins: [
|
||||
'jsdoc',
|
||||
],
|
||||
env: {
|
||||
es6: true,
|
||||
},
|
||||
@@ -78,10 +75,8 @@ module.exports = {
|
||||
'plugins/**',
|
||||
'**/*.min.js',
|
||||
'public/scripts/extensions/quick-reply/lib/**',
|
||||
'public/scripts/extensions/tts/lib/**',
|
||||
],
|
||||
rules: {
|
||||
'jsdoc/no-undefined-types': ['warn', { disableReporting: true, markVariablesAsUsed: true }],
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-control-regex': 'off',
|
||||
'no-constant-condition': ['error', { checkLoops: false }],
|
||||
|
3
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: Bug Report 🐛
|
||||
type: Bug
|
||||
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! Please use English only.
|
||||
title: '[BUG] <title>'
|
||||
labels: ['🐛 Bug']
|
||||
@@ -81,8 +80,6 @@ body:
|
||||
required: true
|
||||
- label: I have checked the [docs](https://docs.sillytavern.app/) 
|
||||
required: true
|
||||
- label: I confirm that my issue is not related to third-party content, unofficial extension or patch. If in doubt, check with a new [user account](https://docs.sillytavern.app/administration/multi-user/) and with extensions disabled
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
3
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
3
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: Feature Request ✨
|
||||
type: Feature
|
||||
description: Suggest an idea for future development of this project. Please use English only.
|
||||
title: '[FEATURE_REQUEST] <title>'
|
||||
labels: ['🦄 Feature Request']
|
||||
@@ -33,7 +32,7 @@ body:
|
||||
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
|
||||
placeholder: An outline of how you would like this to be implemented, include as much details as possible
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
2
.github/close-label.yml
vendored
Normal file
2
.github/close-label.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
🐛 Bug: ✅ Fixed
|
||||
🦄 Feature Request: ✅ Implemented
|
62
.github/issue-auto-comments.yml
vendored
Normal file
62
.github/issue-auto-comments.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
comment:
|
||||
footer: |
|
||||
---
|
||||
> I am a bot, and this is an automated message 🤖
|
||||
labels:
|
||||
- name: ✖️ Invalid
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
body: >
|
||||
Hello @{{ issue.user.login }} your ticket has been marked as invalid.
|
||||
Please ensure you follow the issue template, provide all requested info,
|
||||
and be sure to check the docs + previous issues prior to raising tickets.
|
||||
pr:
|
||||
body: Thank you @{{ pull_request.user.login }} for suggesting this. Please follow the pull request templates.
|
||||
action: close
|
||||
|
||||
- name: 👩💻 Good First Issue
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
This issue has been marked as a good first issue for first-time contributors to implement!
|
||||
This is a great way to support the project, while also improving your skills, you'll also be credited as a contributor once your PR is merged.
|
||||
If you're new to SillyTavern [here are a collection of resources](https://docs.sillytavern.app/)
|
||||
If you need any support at all, feel free to reach out via [Discord](https://discord.gg/sillytavern).
|
||||
|
||||
- name: ❌ wontfix
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
body: >
|
||||
This ticked has been marked as 'wontfix', which usually means it is out-of-scope, or not feasible at this time.
|
||||
You can still fork the project and make the changes yourself.
|
||||
|
||||
- name: ✅ Fixed
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
Hello @{{ issue.user.login }}! It looks like all or part of this issue has now been implemented.
|
||||
|
||||
|
||||
- name: ‼️ High Priority
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
This ticket has been marked as high priority, and has been bumped to the top of the priority list.
|
||||
You should expect an implementation to be pushed out soon. Thank you for your patience.
|
||||
|
||||
- name: 💀 Spam
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
locking: lock
|
||||
lock_reason: spam
|
||||
body: >
|
||||
This issue has been identified as spam, and is now locked.
|
||||
Users who repeatedly raise spam issues may be blocked or reported.
|
||||
|
||||
- name: ⛔ Don't Merge
|
||||
labeled:
|
||||
pr:
|
||||
body: This PR has been temporarily blocked from merging.
|
69
.github/issues-auto-comments.yml
vendored
69
.github/issues-auto-comments.yml
vendored
@@ -1,69 +0,0 @@
|
||||
labels:
|
||||
- name: ✖️ Invalid
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
body: >
|
||||
Hey @{{ issue.user.login }}, this issue has been marked as invalid.
|
||||
|
||||
Please double-check that you've followed the issue template, included all necessary details, and reviewed the docs & previous issues before submitting.
|
||||
If provided, follow the instructions given by maintainers.
|
||||
|
||||
- name: 👩💻 Good First Issue
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
🏆 This issue has been marked as a good first issue for contributors to implement!
|
||||
This is a great way to support the project. While also improving your skills, you'll also be credited as a contributor once your PR is merged.
|
||||
|
||||
If you're new to SillyTavern [here is the official documentation](https://docs.sillytavern.app/). The official contribution guide can be found [here](https://github.com/SillyTavern/SillyTavern/blob/release/CONTRIBUTING.md).
|
||||
If you need any support, feel free to reach out via [Discord](https://discord.gg/sillytavern), or let us know in this issue or via [discussions](https://github.com/SillyTavern/SillyTavern/discussions).
|
||||
|
||||
- name: ❌ wontfix
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
body: >
|
||||
❌ This issue has been marked as 'wontfix', which usually means it is out-of-scope, not feasible at this time or will not be implemented for various reasons.
|
||||
If you have any questions about this, feel free to reach out.
|
||||
|
||||
- name: 🛑 Out of Scope
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
body: >
|
||||
🛑 This issue has been marked as 'out of scope', as this can't or won't be implemented.
|
||||
If you have any questions about this, feel free to reach out.
|
||||
|
||||
- name: ✅ Done (staging)
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
✅ It looks like all or part of this issue has now been implemented as part of the `staging` branch.
|
||||
If you currently are on the `release` branch, you can switch to `staging` to test this right away.
|
||||
|
||||
Note that `staging` is considered less stable than the official releases. To switch, follow existing instructions,
|
||||
or simply enter the following command: `git switch staging`
|
||||
|
||||
- name: ✅ Done
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
✅ It looks like all or part of this issue has now been implemented as part of the latest release.
|
||||
|
||||
- name: ‼️ High Priority
|
||||
labeled:
|
||||
issue:
|
||||
body: >
|
||||
🚨 This issue has been marked high priority, meaning it's important to the maintainers or community.
|
||||
While we can't promise immediate changes, it is on our radar and will be addressed whenever possible. Thanks for your patience!
|
||||
|
||||
- name: 💀 Spam
|
||||
labeled:
|
||||
issue:
|
||||
action: close
|
||||
locking: lock
|
||||
lock_reason: spam
|
||||
body: >
|
||||
💀 This issue has been flagged as spam and is now locked.
|
||||
Please avoid posting spam - it disrupts the community and wastes everyone's time.
|
@@ -1,3 +1,7 @@
|
||||
# Add/remove 'critical' label if issue contains the words 'urgent' or 'critical'
|
||||
#critical:
|
||||
# - '(critical|urgent)'
|
||||
|
||||
🪟 Windows:
|
||||
- '(🪟 Windows)'
|
||||
|
||||
@@ -11,10 +15,4 @@
|
||||
- '(📱 Termux)'
|
||||
|
||||
🐧 Linux:
|
||||
- '(🐧 Linux)'
|
||||
|
||||
🦊 Firefox:
|
||||
- '\b(firefox|mozilla)\b'
|
||||
|
||||
📱 Mobile:
|
||||
- '\b(iphone|ios|android|📱 Termux)\b'
|
||||
- '(🐧 Linux)'
|
51
.github/pr-auto-comments.yml
vendored
51
.github/pr-auto-comments.yml
vendored
@@ -1,51 +0,0 @@
|
||||
labels:
|
||||
- name: ✖️ Invalid
|
||||
labeled:
|
||||
pr:
|
||||
action: close
|
||||
body: >
|
||||
Hey @{{ pull_request.user.login }}, thanks for your contribution!
|
||||
Unfortunately, this PR has been marked as invalid.
|
||||
|
||||
Please check that you've followed the PR template, included all relevant details, and are targeting the correct branch (`staging` for regular contributions, `release` only for hotfixes).
|
||||
|
||||
If you need help, feel free to ask!
|
||||
|
||||
- name: ⛔ Don't Merge
|
||||
labeled:
|
||||
pr:
|
||||
body: >
|
||||
🚨 This PR has been temporarily blocked from merging.
|
||||
|
||||
- name: 💥💣 Breaking Changes
|
||||
labeled:
|
||||
pr:
|
||||
body: >
|
||||
⚠️ Heads up! This PR introduces breaking changes.
|
||||
|
||||
Make sure these changes are well-documented and that users will be properly informed when this is released.
|
||||
|
||||
- name: ⛔ Waiting For External/Upstream
|
||||
labeled:
|
||||
pr:
|
||||
body: >
|
||||
⛔ This PR is awaiting external or upstream changes or approval.
|
||||
It can only be merged once those changes have been implemented and approved.
|
||||
|
||||
Please inform us of any progress on the upstream changes or approval.
|
||||
|
||||
- name: 🔬 Needs Testing
|
||||
labeled:
|
||||
pr:
|
||||
body: >
|
||||
🔬 This PR needs testing!
|
||||
Any contributor can test and leave reviews, so feel free to help us out!
|
||||
|
||||
- name: 🟥 ⬤⬤⬤⬤⬤
|
||||
labeled:
|
||||
pr:
|
||||
body: >
|
||||
⚠️ This PR is over 1000 lines, which is larger than recommended.
|
||||
|
||||
Please make sure that it only addresses a single issue - PRs this large are hard to test and may be rejected.
|
||||
|
83
.github/pr-auto-labels-by-branch.yml
vendored
83
.github/pr-auto-labels-by-branch.yml
vendored
@@ -1,83 +0,0 @@
|
||||
####################################
|
||||
# Label PRs against 'release' #
|
||||
####################################
|
||||
❗ Against Release Branch:
|
||||
- base-branch: 'release'
|
||||
|
||||
####################################
|
||||
# Labels based on PR branch name #
|
||||
####################################
|
||||
🦋 Bug Fix:
|
||||
- head-branch: ['^fix[/-]', '\bfixes\b']
|
||||
|
||||
🚑 Hot Fix:
|
||||
- head-branch: ['^hotfix[/-]']
|
||||
|
||||
✨ New Feature:
|
||||
- head-branch: ['^feat(ure)?[/-].*?\badd', '^add-']
|
||||
|
||||
✨ Feature Changes:
|
||||
- head-branch: ['^feat(ure)?[/-](?!.*\badd\b)', '\bchanges?\b']
|
||||
|
||||
🤖 API / Model:
|
||||
- head-branch: ['\bapi\b', '\bmodels?\b']
|
||||
|
||||
🏭 Backend Changes:
|
||||
- head-branch: ['\bbackend\b', '\bendpoints?\b']
|
||||
|
||||
🐋 Docker:
|
||||
- head-branch: ['\bdocker\b']
|
||||
|
||||
➕ Extension:
|
||||
- head-branch: ['\bextension\b', '\bext\b']
|
||||
|
||||
🦊 Firefox:
|
||||
- head-branch: ['\bfirefox\b']
|
||||
|
||||
🧑🤝🧑 Group Chat:
|
||||
- head-branch: ['\bgroups?\b']
|
||||
|
||||
🖼️ Image Gen:
|
||||
- head-branch: ['\bimage-gen\b']
|
||||
|
||||
🌐 Language:
|
||||
- head-branch: ['\btranslations?\b', '\blanguages?\b']
|
||||
|
||||
🐧 Linux:
|
||||
- head-branch: ['\blinux\b']
|
||||
|
||||
🧩 Macros:
|
||||
- head-branch: ['\bmacros?\b']
|
||||
|
||||
📱 Mobile:
|
||||
- head-branch: ['\bmobile\b', '\bios\b', '\bandroid\b']
|
||||
|
||||
🚄 Performance:
|
||||
- head-branch: ['\bperformance\b']
|
||||
|
||||
⚙️ Preset:
|
||||
- head-branch: ['\bpresets?\b']
|
||||
|
||||
📜 Prompt:
|
||||
- head-branch: ['\bprompt\b']
|
||||
|
||||
🧠 Reasoning:
|
||||
- head-branch: ['\breasoning\b', '\breason\b', '\bthinking\b']
|
||||
|
||||
🚚 Refactor:
|
||||
- head-branch: ['\brefactor(s|ed)?\b']
|
||||
|
||||
📜 STscript:
|
||||
- head-branch: ['\bstscript\b', '\bslash-commands\b']
|
||||
|
||||
🏷️ Tags / Folders:
|
||||
- head-branch: ['\btags\b']
|
||||
|
||||
🎙️ TTS / Voice:
|
||||
- head-branch: ['\btts\b', '\bvoice\b']
|
||||
|
||||
🌟 UX:
|
||||
- head-branch: ['\bux\b']
|
||||
|
||||
🗺️ World Info:
|
||||
- head-branch: ['\bworld-info\b', '\bwi\b']
|
46
.github/pr-auto-labels-by-files.yml
vendored
46
.github/pr-auto-labels-by-files.yml
vendored
@@ -1,46 +0,0 @@
|
||||
####################################
|
||||
# Labels based on changed files #
|
||||
####################################
|
||||
🏭 Backend Changes:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "src/**"
|
||||
- "default/config.yaml"
|
||||
- "server.js"
|
||||
- "plugins.js"
|
||||
- "recover.js"
|
||||
- "webpack.config.js"
|
||||
- "Start.bat"
|
||||
- "start.sh"
|
||||
- "UpdateAndStart.bat"
|
||||
- "UpdateForkAndStart.bat"
|
||||
|
||||
⚙️ config.yaml:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "default/config.yaml"
|
||||
|
||||
🛠️ Build Changes:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ".github/workflows/**"
|
||||
- "docker/**"
|
||||
- ".dockerignore"
|
||||
- "Dockerfile"
|
||||
- "webpack.config.js"
|
||||
|
||||
🌐 Language:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "public/locales/**"
|
||||
|
||||
📥 Dependencies:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "public/lib/**" # Every frontend lib counts as a dependency as well
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "tests/package.json"
|
||||
- "tests/package-lock.json"
|
||||
- "src/electron/package.json"
|
||||
- "src/electron/package-lock.json"
|
190
.github/readme.md
vendored
190
.github/readme.md
vendored
@@ -23,7 +23,7 @@ We have a [Documentation website](https://docs.sillytavern.app/) to answer most
|
||||
|
||||
SillyTavern (or ST for short) is a locally installed user interface that allows you to interact with text generation LLMs, image generation engines, and TTS voice models.
|
||||
|
||||
Beginning in February 2023 as a fork of TavernAI 1.2.8, SillyTavern now has over 200 contributors and 2 years of independent development under its belt, and continues to serve as a leading software for savvy AI hobbyists.
|
||||
Beginning in February 2023 as a fork of TavernAI 1.2.8, SillyTavern now has over 100 contributors and 2 years of independent development under its belt, and continues to serve as a leading software for savvy AI hobbyists.
|
||||
|
||||
## Our Vision
|
||||
|
||||
@@ -113,9 +113,7 @@ SillyTavern has extensibility support.
|
||||
|
||||
Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern.app/).
|
||||
|
||||
## ⌛ Installation
|
||||
|
||||
### 🪟 Windows
|
||||
# ⌛ Installation
|
||||
|
||||
> \[!WARNING]
|
||||
>
|
||||
@@ -123,7 +121,9 @@ Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern
|
||||
> * DO NOT RUN START.BAT WITH ADMIN PERMISSIONS
|
||||
> * INSTALLATION ON WINDOWS 7 IS IMPOSSIBLE AS IT CAN NOT RUN NODEJS 18.16
|
||||
|
||||
#### Installing via Git (recommended)
|
||||
## 🪟 Windows
|
||||
|
||||
### Installing via Git
|
||||
|
||||
1. Install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended)
|
||||
2. Install [Git for Windows](https://gitforwindows.org/)
|
||||
@@ -138,7 +138,7 @@ Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern
|
||||
7. Once everything is cloned, double-click `Start.bat` to make NodeJS install its requirements.
|
||||
8. The server will then start, and SillyTavern will pop up in your browser.
|
||||
|
||||
#### Installing via GitHub Desktop
|
||||
### Installing via GitHub Desktop
|
||||
|
||||
(This allows git usage **only** in GitHub Desktop, if you want to use `git` on the command line too, you also need to install [Git for Windows](https://gitforwindows.org/))
|
||||
|
||||
@@ -152,7 +152,7 @@ Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern
|
||||
9. After the installation process, if everything is working, the command console window should look like this and a SillyTavern tab should be open in your browser:
|
||||
10. Connect to any of the [supported APIs](https://docs.sillytavern.app/usage/api-connections/) and start chatting!
|
||||
|
||||
### 🐧 Linux & 🍎 MacOS
|
||||
## 🐧 Linux & 🍎 MacOS
|
||||
|
||||
For MacOS / Linux all of these will be done in a Terminal.
|
||||
|
||||
@@ -168,72 +168,6 @@ For MacOS / Linux all of these will be done in a Terminal.
|
||||
* `./start.sh`
|
||||
* `bash start.sh`
|
||||
|
||||
## 🐋 Installing via Docker
|
||||
|
||||
These instructions assume you have installed Docker, are able to access your command line for the installation of containers, and familiar with their general operation.
|
||||
|
||||
### Using the GitHub Container Registry
|
||||
|
||||
#### Docker Compose (easiest)
|
||||
|
||||
Grab the `docker-compose.yml` file from the [GitHub Repository](https://github.com/SillyTavern/SillyTavern/blob/release/docker/docker-compose.yml) and run the following command in the directory where the file is located. This will pull the latest release image from the GitHub Container Registry and start the container, automatically creating the necessary volumes.
|
||||
|
||||
```shell
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
Customize the `docker-compose.yml` file to your needs. The default port is 8000. If you want to adjust the server configuration using environment variables, read the documentation [here](https://docs.sillytavern.app/administration/config-yaml/#environment-variables).
|
||||
|
||||
#### Docker CLI (advanced)
|
||||
|
||||
You will need two mandatory directory mappings and a port mapping to allow SillyTavern to function. In the command, replace your selections in the following places:
|
||||
|
||||
#### Container Variables
|
||||
|
||||
##### Volume Mappings
|
||||
|
||||
* `CONFIG_PATH` - The directory where SillyTavern configuration files will be stored on your host machine
|
||||
* `DATA_PATH` - The directory where SillyTavern user data (including characters) will be stored on your host machine
|
||||
* `PLUGINS_PATH` - (optional) The directory where SillyTavern server plugins will be stored on your host machine
|
||||
* `EXTENSIONS_PATH` - (optional) The directory where global UI extensions will be stored on your host machine
|
||||
|
||||
##### Port Mappings
|
||||
|
||||
* `PUBLIC_PORT` - The port to expose the traffic on. This is mandatory, as you will be accessing the instance from outside of its virtual machine container. DO NOT expose this to the internet without implementing a separate service for security.
|
||||
|
||||
##### Additional Settings
|
||||
|
||||
* `SILLYTAVERN_VERSION` - On the right-hand side of this GitHub page, you'll see "Packages". Select the "sillytavern" package and you'll see the image versions. The image tag "latest" will keep you up-to-date with the current release. You can also utilize "staging" that points to the nightly image of the respective branch.
|
||||
|
||||
#### Running the container
|
||||
|
||||
1. Open your Command Line
|
||||
2. Run the following command in a folder where you want to store the configuration and data files:
|
||||
|
||||
```bash
|
||||
SILLYTAVERN_VERSION="latest"
|
||||
PUBLIC_PORT="8000"
|
||||
CONFIG_PATH="./config"
|
||||
DATA_PATH="./data"
|
||||
PLUGINS_PATH="./plugins"
|
||||
EXTENSIONS_PATH="./extensions"
|
||||
|
||||
docker run \
|
||||
--name="sillytavern" \
|
||||
-p "$PUBLIC_PORT:8000/tcp" \
|
||||
-v "$CONFIG_PATH:/home/node/app/config:rw" \
|
||||
-v "$DATA_PATH:/home/node/app/data:rw" \
|
||||
-v "$EXTENSIONS_PATH:/home/node/app/public/scripts/extensions/third-party:rw" \
|
||||
-v "$PLUGINS_PATH:/home/node/app/plugins:rw" \
|
||||
ghcr.io/sillytavern/sillytavern:"$SILLYTAVERN_VERSION"
|
||||
```
|
||||
|
||||
> By default the container will run in the foreground. If you want to run it in the background, add the `-d` flag to the `docker run` command.
|
||||
|
||||
### Building the image yourself
|
||||
|
||||
We have a comprehensive guide on using SillyTavern in Docker [here](http://docs.sillytavern.app/installation/docker/) which covers installations on Windows, macOS and Linux! Give it a read if you wish to build the image yourself.
|
||||
|
||||
## ⚡ Installing via SillyTavern Launcher
|
||||
|
||||
SillyTavern Launcher is an installation wizard that will help you get setup with many options, including installing a backend for local inference.
|
||||
@@ -305,6 +239,45 @@ chmod +x install.sh && ./install.sh
|
||||
chmod +x launcher.sh && ./launcher.sh
|
||||
```
|
||||
|
||||
## 🐋 Installing via Docker
|
||||
|
||||
These instructions assume you have installed Docker, are able to access your command line for the installation of containers, and familiar with their general operation.
|
||||
|
||||
### Building the image yourself
|
||||
|
||||
We have a comprehensive guide on using SillyTavern in Docker [here](http://docs.sillytavern.app/installation/docker/) which covers installations on Windows, macOS and Linux! Give it a read if you wish to build the image yourself.
|
||||
|
||||
### Using the GitHub Container Registry (easiest)
|
||||
|
||||
You will need two mandatory directory mappings and a port mapping to allow SillyTavern to function. In the command, replace your selections in the following places:
|
||||
|
||||
#### Container Variables
|
||||
|
||||
##### Volume Mappings
|
||||
|
||||
* [config] - The directory where SillyTavern configuration files will be stored on your host machine
|
||||
* [data] - The directory where SillyTavern user data (including characters) will be stored on your host machine
|
||||
* [plugins] - (optional) The directory where SillyTavern server plugins will be stored on your host machine
|
||||
* [extensions] - (optional) The directory where global UI extensions will be stored on your host machine
|
||||
|
||||
##### Port Mappings
|
||||
|
||||
* [PublicPort] - The port to expose the traffic on. This is mandatory, as you will be accessing the instance from outside of its virtual machine container. DO NOT expose this to the internet without implementing a separate service for security.
|
||||
|
||||
##### Additional Settings
|
||||
|
||||
* [DockerNet] - The docker network that the container should be created with a connection to. If you don't know what it is, see the [official Docker documentation](https://docs.docker.com/reference/cli/docker/network/).
|
||||
* [version] - On the right-hand side of this GitHub page, you'll see "Packages". Select the "sillytavern" package and you'll see the image versions. The image tag "latest" will keep you up-to-date with the current release. You can also utilize "staging" and "release" tags that point to the nightly images of the respective branches, but this may not be appropriate, if you are utilizing extensions that could be broken, and may need time to update.
|
||||
|
||||
#### Install command
|
||||
|
||||
1. Open your Command Line
|
||||
2. Run the following command
|
||||
|
||||
`docker run --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||
|
||||
> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
|
||||
|
||||
## 📱 Installing via Termux on Android OS
|
||||
|
||||
> \[!NOTE]
|
||||
@@ -344,34 +317,29 @@ Start.bat --port 8000 --listen false
|
||||
|
||||
### Supported arguments
|
||||
|
||||
> \[!TIP]
|
||||
> None of the arguments are required. If you don't provide them, SillyTavern will use the settings in `config.yaml`.
|
||||
|
||||
| Option | Description | Type |
|
||||
|-------------------------|----------------------------------------------------------------------|----------|
|
||||
| `--version` | Show version number | boolean |
|
||||
| `--dataRoot` | Root directory for data storage | string |
|
||||
| `--port` | Sets the port under which SillyTavern will run | number |
|
||||
| `--listen` | SillyTavern will listen on all network interfaces | boolean |
|
||||
| `--whitelist` | Enables whitelist mode | boolean |
|
||||
| `--basicAuthMode` | Enables basic authentication | boolean |
|
||||
| `--enableIPv4` | Enables IPv4 protocol | boolean |
|
||||
| `--enableIPv6` | Enables IPv6 protocol | boolean |
|
||||
| `--listenAddressIPv4` | Specific IPv4 address to listen to | string |
|
||||
| `--listenAddressIPv6` | Specific IPv6 address to listen to | string |
|
||||
| `--dnsPreferIPv6` | Prefers IPv6 for DNS | boolean |
|
||||
| `--ssl` | Enables SSL | boolean |
|
||||
| `--certPath` | Path to your certificate file | string |
|
||||
| `--keyPath` | Path to your private key file | string |
|
||||
| `--autorun` | Automatically launch SillyTavern in the browser | boolean |
|
||||
| `--autorunHostname` | Autorun hostname | string |
|
||||
| `--autorunPortOverride` | Overrides the port for autorun | string |
|
||||
| `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode | boolean |
|
||||
| `--corsProxy` | Enables CORS proxy | boolean |
|
||||
| `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean |
|
||||
| `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string |
|
||||
| `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array |
|
||||
| `--disableCsrf` | Disables CSRF protection (NOT RECOMMENDED) | boolean |
|
||||
| Option | Description | Type |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------------|----------|
|
||||
| `--version` | Show version number | boolean |
|
||||
| `--enableIPv6` | Enables IPv6. | boolean |
|
||||
| `--enableIPv4` | Enables IPv4. | boolean |
|
||||
| `--port` | Sets the port under which SillyTavern will run. If not provided falls back to yaml config 'port'. | number |
|
||||
| `--dnsPreferIPv6` | Prefers IPv6 for dns. If not provided falls back to yaml config 'preferIPv6'. | boolean |
|
||||
| `--autorun` | Automatically launch SillyTavern in the browser. If not provided falls back to yaml config 'autorun'.| boolean |
|
||||
| `--autorunHostname` | The autorun hostname, probably best left on 'auto'. | string |
|
||||
| `--autorunPortOverride` | Overrides the port for autorun. | string |
|
||||
| `--listen` | SillyTavern is listening on all network interfaces. If not provided falls back to yaml config 'listen'.| boolean |
|
||||
| `--corsProxy` | Enables CORS proxy. If not provided falls back to yaml config 'enableCorsProxy'. | boolean |
|
||||
| `--disableCsrf` | Disables CSRF protection | boolean |
|
||||
| `--ssl` | Enables SSL | boolean |
|
||||
| `--certPath` | Path to your certificate file. | string |
|
||||
| `--keyPath` | Path to your private key file. | string |
|
||||
| `--whitelist` | Enables whitelist mode | boolean |
|
||||
| `--dataRoot` | Root directory for data storage | string |
|
||||
| `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode. | boolean |
|
||||
| `--basicAuthMode` | Enables basic authentication | boolean |
|
||||
| `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean |
|
||||
| `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string |
|
||||
| `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array |
|
||||
|
||||
## Remote connections
|
||||
|
||||
@@ -383,29 +351,10 @@ You may also want to configure SillyTavern user profiles with (optional) passwor
|
||||
|
||||
## Performance issues?
|
||||
|
||||
### General tips
|
||||
|
||||
1. Disable the Blur Effect and enable Reduced Motion on the User Settings panel (UI Theme toggles category).
|
||||
2. If using response streaming, set the streaming FPS to a lower value (10-15 FPS is recommended).
|
||||
3. Make sure the browser is enabled to use GPU acceleration for rendering.
|
||||
|
||||
### Input lag
|
||||
|
||||
Performance degradation, particularly input lag, is most commonly attributed to browser extensions. Known problematic extensions include:
|
||||
|
||||
* iCloud Password Manager
|
||||
* DeepL Translation
|
||||
* AI-based grammar correction tools
|
||||
* Various ad-blocking extensions
|
||||
|
||||
If you experience performance issues and cannot identify the cause, or suspect an issue with SillyTavern itself, please:
|
||||
|
||||
1. [Record a performance profile](https://developer.chrome.com/docs/devtools/performance/reference)
|
||||
2. Export the profile as a JSON file
|
||||
3. Submit it to the development team for analysis
|
||||
|
||||
We recommend first testing with all browser extensions and third-party SillyTavern extensions disabled to isolate the source of the performance degradation.
|
||||
|
||||
## License and credits
|
||||
|
||||
**This program is distributed in the hope that it will be useful,
|
||||
@@ -420,7 +369,6 @@ GNU Affero General Public License for more details.**
|
||||
* Icon theme by Font Awesome <https://fontawesome.com> (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Default content by @OtisAlejandro (Seraphina character and lorebook) and @kallmeflocc (10K Discord Users Celebratory Background)
|
||||
* Docker guide by [@mrguymiah](https://github.com/mrguymiah) and [@Bronya-Rand](https://github.com/Bronya-Rand)
|
||||
* kokoro-js library by [@hexgrad](https://github.com/hexgrad) (Apache-2.0 License)
|
||||
|
||||
## Top Contributors
|
||||
|
||||
|
28
.github/workflows/add-comment-from-tag.yml
vendored
Normal file
28
.github/workflows/add-comment-from-tag.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Based on a label applied to an issue, the bot will add a comment with some additional info
|
||||
|
||||
name: 🎯 Auto-Reply to Labeled Tickets
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
- unlabeled
|
||||
pull_request_target:
|
||||
types:
|
||||
- labeled
|
||||
- unlabeled
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Label Commenter
|
||||
uses: peaceiris/actions-label-commenter@v1
|
||||
with:
|
||||
config_file: .github/issue-auto-comments.yml
|
||||
github_token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
17
.github/workflows/check-merge-conflicts.yml
vendored
Normal file
17
.github/workflows/check-merge-conflicts.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Detect and label pull requests that have merge conflicts
|
||||
name: 🏗️ Check Merge Conflicts
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- staging
|
||||
jobs:
|
||||
check-conflicts:
|
||||
if: github.repository == 'SillyTavern/SillyTavern'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: mschilde/auto-label-merge-conflicts@master
|
||||
with:
|
||||
CONFLICT_LABEL_NAME: "🚫 Merge Conflicts"
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
MAX_RETRIES: 5
|
||||
WAIT_MS: 5000
|
82
.github/workflows/close-stale-issues.yml
vendored
Normal file
82
.github/workflows/close-stale-issues.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Closes any issues that no longer have user interaction
|
||||
name: 🎯 Close Stale Issues
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs every day at midnight UTC
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Comment on, then close issues that haven't been updated for ages
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 183
|
||||
days-before-close: 7
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
enable-statistics: true
|
||||
stale-issue-message: >
|
||||
This issue has gone 6 months without an update. To keep the ticket open, please indicate that it is still relevant in a comment below.
|
||||
Otherwise it will be closed in 7 days.
|
||||
stale-pr-message: >
|
||||
This PR is stale because it has been open 6 months with no activity. Either remove the stale label or comment below with a short update,
|
||||
otherwise this PR will be closed in 7 days.
|
||||
close-issue-message: >
|
||||
This issue was automatically closed because it has been stalled for over 6 months with no activity.
|
||||
close-pr-message: >
|
||||
This pull request was automatically closed because it has been stalled for over 6 months with no activity.
|
||||
stale-issue-label: '⚰️ Stale'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
stale-pr-label: '⚰️ Stale'
|
||||
close-pr-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '📌 Keep Open'
|
||||
exempt-pr-labels: '📌 Keep Open'
|
||||
labels-to-add-when-unstale: '📌 Keep Open'
|
||||
|
||||
# Comment on, then close issues that required a response from the user, but didn't get one
|
||||
- name: Close Issues without Response
|
||||
uses: actions/stale@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 7
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-message: >
|
||||
Hi! Looks like additional info is required for this issue to be addressed.
|
||||
Don't forget to provide this within the next few days to keep your ticket open.
|
||||
close-issue-message: 'Issue closed due to no response from user.'
|
||||
only-labels: '🚏 Awaiting User Response'
|
||||
labels-to-remove-when-unstale: '🚏 Awaiting User Response, 🛑 No Response'
|
||||
stale-issue-label: '🛑 No Response'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '📌 Keep Open'
|
||||
exempt-pr-labels: '📌 Keep Open'
|
||||
|
||||
# Comment on issues that we should have replied to
|
||||
- name: Notify Repo Owner to Respond
|
||||
uses: actions/stale@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 183
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-message: Hey SillyTavern, - Don't forget to respond!
|
||||
stale-pr-message: Hey SillyTavern, - Don't forget to respond!
|
||||
only-labels: '👤 Awaiting Maintainer Response'
|
||||
labels-to-remove-when-unstale: '👤 Awaiting Maintainer Response'
|
||||
close-issue-message: 'Closed due to no response from repo author for over a year'
|
||||
close-pr-message: 'Closed due to no response from repo author for over a year'
|
||||
stale-issue-label: '👤 Awaiting Maintainer Response'
|
||||
stale-pr-label: '👤 Awaiting Maintainer Response'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
close-pr-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '📌 Keep Open'
|
||||
exempt-pr-labels: '📌 Keep Open'
|
39
.github/workflows/get-pr-size.yml
vendored
Normal file
39
.github/workflows/get-pr-size.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Adds a comment to new PRs, showing the compressed size and size difference of new code
|
||||
# And also labels the PR based on the number of lines changes
|
||||
|
||||
name: 🌈 Check PR Size
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
# Find and comment with compressed size
|
||||
- name: Get Compressed Size
|
||||
uses: preactjs/compressed-size-action@v2
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
pattern: './dist/**/*.{js,css,html}'
|
||||
strip-hash: '\\b\\w{8}\\.'
|
||||
exclude: '**/node_modules/**'
|
||||
minimum-change-threshold: 100
|
||||
# Check number of lines of code added
|
||||
- name: Label based on Lines of Code
|
||||
uses: codelytv/pr-size-labeler@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
xs_max_size: '10'
|
||||
s_max_size: '100'
|
||||
m_max_size: '500'
|
||||
l_max_size: '1000'
|
||||
s_label: '🟩 PR - Small'
|
||||
m_label: '🟨 PR - Medium'
|
||||
l_label: '🟧 PR - Large'
|
||||
xl_label: '🟥 PR - XL'
|
||||
fail_if_xl: 'false'
|
||||
message_if_xl: >
|
||||
It looks like this PR is very large (over 1000 lines).
|
||||
Try to avoid addressing multiple issues in a single PR, and
|
||||
in the future consider breaking large tasks down into smaller steps.
|
||||
This it to make reviewing, testing, reverting and general quality management easier.
|
116
.github/workflows/issues-auto-manager.yml
vendored
116
.github/workflows/issues-auto-manager.yml
vendored
@@ -1,116 +0,0 @@
|
||||
name: 🛠️ Issues Manager
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, labeled, unlabeled]
|
||||
# Re also listen to comments, to remove stale labels right away
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
label-on-content:
|
||||
name: 🏷️ Label Issues by Content
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Auto-Label Issues (Based on Issue Content)
|
||||
# only auto label based on issue content once, on open (to prevent re-labeling removed labels)
|
||||
if: github.event.action == 'opened'
|
||||
|
||||
# Issue Labeler
|
||||
# https://github.com/marketplace/actions/regex-issue-labeler
|
||||
uses: github/issue-labeler@v3.4
|
||||
with:
|
||||
configuration-path: .github/issues-auto-labels.yml
|
||||
enable-versioned-regex: 0
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
label-on-labels:
|
||||
name: 🏷️ Label Issues by Labels
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ✅ Add "👍 Approved" for relevant labels
|
||||
if: contains(fromJSON('["👩💻 Good First Issue", "🙏 Help Wanted", "🪲 Confirmed", "⚠️ High Priority", "❕ Medium Priority", "💤 Low Priority"]'), github.event.label.name)
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: '👍 Approved'
|
||||
|
||||
- name: ❌ Remove progress labels when issue is marked done or stale
|
||||
if: contains(fromJSON('["✅ Done", "✅ Done (staging)", "⚰️ Stale", "❌ wontfix"]'), github.event.label.name)
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: '🧑💻 In Progress,🤔 Unsure,🤔 Under Consideration'
|
||||
|
||||
- name: ❌ Remove temporary labels when confirmed labels are added
|
||||
if: contains(fromJSON('["❌ wontfix","👍 Approved","👩💻 Good First Issue"]'), github.event.label.name)
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: '🤔 Unsure,🤔 Under Consideration'
|
||||
|
||||
- name: ❌ Remove no bug labels when "🪲 Confirmed" is added
|
||||
if: github.event.label.name == '🪲 Confirmed'
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: '✖️ Not Reproducible,✖️ Not A Bug'
|
||||
|
||||
remove-stale-label:
|
||||
name: 🗑️ Remove Stale Label on Comment
|
||||
runs-on: ubuntu-latest
|
||||
# Only run this on new comments, to automatically remove the stale label
|
||||
if: github.event_name == 'issue_comment' && github.actor != 'github-actions[bot]'
|
||||
|
||||
steps:
|
||||
- name: Remove Stale Label
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: '⚰️ Stale,🕸️ Inactive,🚏 Awaiting User Response,🛑 No Response'
|
||||
|
||||
write-auto-comments:
|
||||
name: 💬 Post Issue Comments Based on Labels
|
||||
needs: [label-on-content, label-on-labels]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Post Issue Comments Based on Labels
|
||||
# Label Commenter
|
||||
# https://github.com/marketplace/actions/label-commenter
|
||||
uses: peaceiris/actions-label-commenter@v1.10.0
|
||||
with:
|
||||
config_file: .github/issues-auto-comments.yml
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
45
.github/workflows/issues-updates-on-merge.yml
vendored
45
.github/workflows/issues-updates-on-merge.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: 🔄 Update Issues on Push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- staging
|
||||
- release
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
# This runs commits to staging/release, reading the commit messages. Check `pr-auto-manager.yml`:`update-linked-issues` for PR-linked updates.
|
||||
update-linked-issues:
|
||||
name: 🔗 Mark Linked Issues Done on Push
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Extract Linked Issues from Commit Message
|
||||
id: extract_issues
|
||||
run: |
|
||||
ISSUES=$(git log ${{ github.event.before }}..${{ github.event.after }} --pretty=%B | grep -oiE '(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #([0-9]+)' | awk '{print $2}' | tr -d '#' | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo "issues=$ISSUES" >> $GITHUB_ENV
|
||||
|
||||
- name: Label Linked Issues
|
||||
id: label_linked_issues
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
for ISSUE in $(echo $issues | jq -r '.[]'); do
|
||||
if [ "${{ github.ref }}" == "refs/heads/staging" ]; then
|
||||
LABEL="✅ Done (staging)"
|
||||
gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" --remove-label "🧑💻 In Progress"
|
||||
elif [ "${{ github.ref }}" == "refs/heads/release" ]; then
|
||||
LABEL="✅ Done"
|
||||
gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" --remove-label "🧑💻 In Progress"
|
||||
fi
|
||||
echo "Added label '$LABEL' (and removed '🧑💻 In Progress' if present) in issue #$ISSUE"
|
||||
done
|
100
.github/workflows/job-close-stale.yml
vendored
100
.github/workflows/job-close-stale.yml
vendored
@@ -1,100 +0,0 @@
|
||||
name: 🕒 Close Stale Issues/PRs Workflow
|
||||
|
||||
on:
|
||||
# Run the workflow every day
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs every day at midnight UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
mark-inactivity:
|
||||
name: ⏳ Mark Issues/PRs without Activity
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Mark Issues/PRs without Activity
|
||||
# Close Stale Issues and PRs
|
||||
# https://github.com/marketplace/actions/close-stale-issues
|
||||
uses: actions/stale@v9.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 183
|
||||
days-before-close: 7
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
enable-statistics: true
|
||||
stale-issue-message: >
|
||||
⏳ This issue has been inactive for 6 months. If it's still relevant, drop a comment below to keep it open.
|
||||
Otherwise, it will be auto-closed in 7 days.
|
||||
stale-pr-message: >
|
||||
⏳ This PR has been inactive for 6 months. If it's still relevant, update it or remove the stale label.
|
||||
Otherwise, it will be auto-closed in 7 days.
|
||||
close-issue-message: >
|
||||
🔒 This issue was auto-closed due to inactivity for over 6 months.
|
||||
close-pr-message: >
|
||||
🔒 This PR was auto-closed due to inactivity for over 6 months.
|
||||
stale-issue-label: '⚰️ Stale'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
stale-pr-label: '⚰️ Stale'
|
||||
close-pr-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '📌 Keep Open'
|
||||
exempt-pr-labels: '📌 Keep Open'
|
||||
|
||||
await-user-response:
|
||||
name: ⚠️ Mark Issues/PRs Awaiting User Response
|
||||
runs-on: ubuntu-latest
|
||||
needs: mark-inactivity
|
||||
|
||||
steps:
|
||||
- name: Mark Issues/PRs Awaiting User Response
|
||||
# Close Stale Issues and PRs
|
||||
# https://github.com/marketplace/actions/close-stale-issues
|
||||
uses: actions/stale@v9.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 7
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-message: >
|
||||
⚠️ Hey! We need some more info to move forward with this issue.
|
||||
Please provide the requested details in the next few days to keep this ticket open.
|
||||
close-issue-message: >
|
||||
🔒 This issue was auto-closed due to no response from user.
|
||||
only-labels: '🚏 Awaiting User Response'
|
||||
labels-to-remove-when-unstale: '🚏 Awaiting User Response'
|
||||
stale-issue-label: '🛑 No Response'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '🚧 Alternative Exists'
|
||||
|
||||
alternative-exists:
|
||||
name: 🔄 Mark Issues with Alternative Exists
|
||||
runs-on: ubuntu-latest
|
||||
needs: await-user-response
|
||||
|
||||
steps:
|
||||
- name: Mark Issues with Alternative Exists
|
||||
# Close Stale Issues and PRs
|
||||
# https://github.com/marketplace/actions/close-stale-issues
|
||||
uses: actions/stale@v9.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 7
|
||||
operations-per-run: 30
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-message: >
|
||||
🔄 An alternative solution has been provided for this issue.
|
||||
Did this solve your problem? If so, we'll go ahead and close it.
|
||||
If you still need help, drop a comment within the next 7 days to keep this open.
|
||||
close-issue-message: >
|
||||
✅ Closing this issue due to no confirmation on the alternative solution.
|
||||
only-labels: '🚧 Alternative Exists'
|
||||
stale-issue-label: '🚏 Awaiting User Response'
|
||||
close-issue-label: '🕸️ Inactive'
|
||||
exempt-issue-labels: '📌 Keep Open'
|
19
.github/workflows/labeler.yml
vendored
Normal file
19
.github/workflows/labeler.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: "Issue Labeler"
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.4
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
# not-before: 2020-01-15T02:54:32Z # optional and will result in any issues prior to this timestamp to be ignored.
|
||||
enable-versioned-regex: 0
|
||||
repo-token: ${{ github.token }}
|
17
.github/workflows/manage-pending-labels-closed.yml
vendored
Normal file
17
.github/workflows/manage-pending-labels-closed.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# When a new comment is added to an issue, if it had the Stale or Awaiting User Response labels, then those labels will be removed
|
||||
|
||||
name: 🎯 Remove Pending Labels on Close
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
jobs:
|
||||
remove-labels:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Remove Labels when Closed
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: remove-labels
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: '🚏 Awaiting User Response,⚰️ Stale,👤 Awaiting Maintainer Response'
|
42
.github/workflows/manage-pending-labels.yml
vendored
Normal file
42
.github/workflows/manage-pending-labels.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# When a new comment is added to an issue, if it had the Stale or Awaiting User Response labels, then those labels will be removed
|
||||
|
||||
name: 🎯 Add/ Remove Awaiting Response Labels
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
jobs:
|
||||
remove-stale:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' }}
|
||||
steps:
|
||||
- name: Remove Stale labels when Updated
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: remove-labels
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: '🚏 Awaiting User Response,⚰️ Stale'
|
||||
|
||||
add-awaiting-author:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{!github.event.issue.pull_request && github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' && github.event.issue.state == 'open' }}
|
||||
steps:
|
||||
- name: Add Awaiting Author labels when Updated
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: add-labels
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: '👤 Awaiting Maintainer Response'
|
||||
|
||||
remove-awaiting-author:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.comment.author_association == 'OWNER' }}
|
||||
steps:
|
||||
- name: Remove Awaiting Author labels when Updated
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: remove-labels
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: '👤 Awaiting Maintainer Response'
|
28
.github/workflows/on-close-handler.yml
vendored
28
.github/workflows/on-close-handler.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: 🚪 Issues/PRs On Close Handler
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
remove-labels:
|
||||
name: 🗑️ Remove Pending Labels on Close
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Remove Pending Labels on Close
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: remove-labels
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
labels: '🚏 Awaiting User Response,🧑💻 In Progress,📌 Keep Open,🚫 Merge Conflicts,🔬 Needs Testing,🔨 Needs Work,⚰️ Stale,⛔ Waiting For External/Upstream'
|
29
.github/workflows/on-open-handler.yml
vendored
29
.github/workflows/on-open-handler.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: 📨 Issues/PRs Open Handler
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label-maintainer:
|
||||
name: 🏷️ Label if Author is a Repo Maintainer
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(fromJson('["Cohee1207", "RossAscends", "Wolfsblvt"]'), github.actor)
|
||||
|
||||
steps:
|
||||
- name: Label if Author is a Repo Maintainer
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
labels: '👷 Maintainer'
|
270
.github/workflows/pr-auto-manager.yml
vendored
270
.github/workflows/pr-auto-manager.yml
vendored
@@ -1,270 +0,0 @@
|
||||
name: 🔀 Pull Request Manager
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow to manually call this workflow
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened, edited, labeled, unlabeled, closed]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
run-eslint:
|
||||
name: ✅ Check ESLint on PR
|
||||
runs-on: ubuntu-latest
|
||||
# Only needs to run when code is changed
|
||||
if: github.event.action == 'opened' || github.event.action == 'synchronize'
|
||||
|
||||
# Override permissions, linter likely needs write access to issues
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- name: Setup Node.js
|
||||
# Setup Node.js environment
|
||||
# https://github.com/marketplace/actions/setup-node-js-environment
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run ESLint
|
||||
# Action ESLint
|
||||
# https://github.com/marketplace/actions/action-eslint
|
||||
uses: sibiraj-s/action-eslint@v3.0.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
eslint-args: '--ignore-path=.gitignore --quiet'
|
||||
extensions: 'js'
|
||||
annotations: true
|
||||
ignore-patterns: |
|
||||
dist/
|
||||
lib/
|
||||
|
||||
label-by-size:
|
||||
name: 🏷️ Label PR by Size
|
||||
# This job should run after all others, to prevent possible concurrency issues
|
||||
needs: [label-by-branches, label-by-files, remove-stale-label, check-merge-blocking-labels, write-auto-comments]
|
||||
runs-on: ubuntu-latest
|
||||
# Only needs to run when code is changed
|
||||
if: always() && (github.event.action == 'opened' || github.event.action == 'synchronize')
|
||||
|
||||
# Override permissions, the labeler needs issues write access
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Label PR Size
|
||||
# Pull Request Size Labeler
|
||||
# https://github.com/marketplace/actions/pull-request-size-labeler
|
||||
uses: codelytv/pr-size-labeler@v1.10.2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
xs_label: '🟩 ⬤○○○○'
|
||||
xs_max_size: '20'
|
||||
s_label: '🟩 ⬤⬤○○○'
|
||||
s_max_size: '100'
|
||||
m_label: '🟨 ⬤⬤⬤○○'
|
||||
m_max_size: '500'
|
||||
l_label: '🟧 ⬤⬤⬤⬤○'
|
||||
l_max_size: '1000'
|
||||
xl_label: '🟥 ⬤⬤⬤⬤⬤'
|
||||
fail_if_xl: 'false'
|
||||
files_to_ignore: |
|
||||
"package-lock.json"
|
||||
"public/lib/*"
|
||||
|
||||
label-by-branches:
|
||||
name: 🏷️ Label PR by Branches
|
||||
runs-on: ubuntu-latest
|
||||
# Only label once when PR is created or when base branch is changed, to allow manual label removal
|
||||
if: github.event.action == 'opened' || (github.event.action == 'synchronize' && github.event.changes.base)
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Apply Labels Based on Branch Name and Target Branch
|
||||
# Pull Request Labeler
|
||||
# https://github.com/marketplace/actions/labeler
|
||||
uses: actions/labeler@v5.0.0
|
||||
with:
|
||||
configuration-path: .github/pr-auto-labels-by-branch.yml
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
label-by-files:
|
||||
name: 🏷️ Label PR by Files
|
||||
runs-on: ubuntu-latest
|
||||
# Only needs to run when code is changed
|
||||
if: github.event.action == 'opened' || github.event.action == 'synchronize'
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Apply Labels Based on Changed Files
|
||||
# Pull Request Labeler
|
||||
# https://github.com/marketplace/actions/labeler
|
||||
uses: actions/labeler@v5.0.0
|
||||
with:
|
||||
configuration-path: .github/pr-auto-labels-by-files.yml
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
remove-stale-label:
|
||||
name: 🗑️ Remove Stale Label on Comment
|
||||
runs-on: ubuntu-latest
|
||||
# Only runs on comments not done by the github actions bot
|
||||
if: github.event_name == 'pull_request_review_comment' && github.actor != 'github-actions[bot]'
|
||||
|
||||
# Override permissions, issue labeler needs issues write access
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Remove Stale Label
|
||||
# 🤖 Issues Helper
|
||||
# https://github.com/marketplace/actions/issues-helper
|
||||
uses: actions-cool/issues-helper@v3.6.0
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
labels: '⚰️ Stale'
|
||||
|
||||
check-merge-blocking-labels:
|
||||
name: 🚫 Check Merge Blocking Labels
|
||||
needs: [label-by-branches, label-by-files]
|
||||
runs-on: ubuntu-latest
|
||||
# Run, even if the previous jobs were skipped/failed
|
||||
if: always()
|
||||
|
||||
# Override permissions, as this needs to write a check
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Check Merge Blocking
|
||||
# GitHub Script
|
||||
# https://github.com/marketplace/actions/github-script
|
||||
id: label-check
|
||||
uses: actions/github-script@v7.0.1
|
||||
with:
|
||||
script: |
|
||||
const prLabels = context.payload.pull_request.labels.map(label => label.name);
|
||||
const blockingLabels = [
|
||||
"⛔ Don't Merge",
|
||||
"🔨 Needs Work",
|
||||
"🔬 Needs Testing",
|
||||
"⛔ Waiting For External/Upstream",
|
||||
"❗ Against Release Branch",
|
||||
"💥💣 Breaking Changes"
|
||||
];
|
||||
const hasBlockingLabel = prLabels.some(label => blockingLabels.includes(label));
|
||||
|
||||
if (hasBlockingLabel) {
|
||||
console.log("Blocking label detected. Setting warning status.");
|
||||
await github.rest.checks.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: "PR Label Warning",
|
||||
head_sha: context.payload.pull_request.head.sha,
|
||||
status: "completed",
|
||||
conclusion: "neutral",
|
||||
output: {
|
||||
title: "Potential Merge Issue",
|
||||
summary: "This PR has a merge-blocking label. Proceed with caution."
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("No merge-blocking labels found.");
|
||||
}
|
||||
|
||||
write-auto-comments:
|
||||
name: 💬 Post PR Comments Based on Labels
|
||||
needs: [label-by-branches, label-by-files]
|
||||
runs-on: ubuntu-latest
|
||||
# Run, even if the previous jobs were skipped/failed
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
# Checkout
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Post PR Comments Based on Labels
|
||||
# Label Commenter for PRs
|
||||
# https://github.com/marketplace/actions/label-commenter
|
||||
uses: peaceiris/actions-label-commenter@v1.10.0
|
||||
with:
|
||||
config_file: .github/pr-auto-comments.yml
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# This runs on merged PRs to staging, reading the PR body and directly linked issues. Check `issues-updates-on-merge.yml`:`update-linked-issues` for commit-based updates.
|
||||
update-linked-issues:
|
||||
name: 🔗 Mark Linked Issues Done on Staging Merge
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'staging'
|
||||
|
||||
# Override permissions, We need to be able to write to issues
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Extract Linked Issues From PR Description
|
||||
id: extract_issues
|
||||
run: |
|
||||
ISSUES=$(jq -r '.pull_request.body' "$GITHUB_EVENT_PATH" | grep -oiE '(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #([0-9]+)' | awk '{print $2}' | tr -d '#' | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo "issues=$ISSUES" >> $GITHUB_ENV
|
||||
|
||||
- name: Fetch Directly Linked Issues
|
||||
id: fetch_linked_issues
|
||||
run: |
|
||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||
REPO=${{ github.repository }}
|
||||
API_URL="https://api.github.com/repos/$REPO/pulls/$PR_NUMBER/issues"
|
||||
ISSUES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$API_URL" | jq -r '.[].number' | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo "linked_issues=$ISSUES" >> $GITHUB_ENV
|
||||
|
||||
- name: Merge Issue Lists
|
||||
id: merge_issues
|
||||
run: |
|
||||
ISSUES=$(jq -c -n --argjson a "$issues" --argjson b "$linked_issues" '$a + $b | unique')
|
||||
echo "final_issues=$ISSUES" >> $GITHUB_ENV
|
||||
|
||||
- name: Label Linked Issues
|
||||
id: label_linked_issues
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
for ISSUE in $(echo $final_issues | jq -r '.[]'); do
|
||||
gh issue edit $ISSUE -R ${{ github.repository }} --add-label "✅ Done (staging)" --remove-label "🧑💻 In Progress"
|
||||
echo "Added label '✅ Done (staging)' (and removed '🧑💻 In Progress' if present) in issue #$ISSUE"
|
||||
done
|
28
.github/workflows/pr-check-merge-conflicts.yaml
vendored
28
.github/workflows/pr-check-merge-conflicts.yaml
vendored
@@ -1,28 +0,0 @@
|
||||
name: ⚔️ Check Merge Conflicts
|
||||
|
||||
on:
|
||||
# So that PRs touching the same files as the push are updated
|
||||
push:
|
||||
# So that the `dirtyLabel` is removed if conflicts are resolved
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check-merge-conflicts:
|
||||
name: ⚔️ Check Merge Conflicts
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check Merge Conflicts
|
||||
# Label Conflicting Pull Requests
|
||||
# https://github.com/marketplace/actions/label-conflicting-pull-requests
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.3
|
||||
with:
|
||||
dirtyLabel: '🚫 Merge Conflicts'
|
||||
repoToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
commentOnDirty: >
|
||||
⚠️ This PR has conflicts that need to be resolved before it can be merged.
|
@@ -4,7 +4,7 @@ FROM node:lts-alpine3.19
|
||||
ARG APP_HOME=/home/node/app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache gcompat tini git
|
||||
RUN apk add gcompat tini git
|
||||
|
||||
# Create app directory
|
||||
WORKDIR ${APP_HOME}
|
||||
|
@@ -1,13 +0,0 @@
|
||||
These are master copies of the default content files and are managed by SillyTavern.
|
||||
|
||||
Editing any of these files would not only have no effect, but will also cause merge conflicts during update pulls.
|
||||
|
||||
You should edit their respective copies instead, for example:
|
||||
|
||||
1. /default/config.yaml => /config.yaml
|
||||
2. /default/public/css/user.css => /public/css/user.css
|
||||
etc.
|
||||
|
||||
Any questions? You're always welcome at our official documentation website:
|
||||
|
||||
https://docs.sillytavern.app/
|
@@ -1,16 +1,12 @@
|
||||
# -- DATA CONFIGURATION --
|
||||
# Root directory for user data storage
|
||||
dataRoot: ./data
|
||||
# The maximum amount of memory that parsed character cards can use in MB
|
||||
cardsCacheCapacity: 100
|
||||
# -- SERVER CONFIGURATION --
|
||||
# Listen for incoming connections
|
||||
listen: false
|
||||
# Listen on a specific address, supports IPv4 and IPv6
|
||||
listenAddress:
|
||||
ipv4: 0.0.0.0
|
||||
ipv6: '[::]'
|
||||
# Enables IPv6 and/or IPv4 protocols. Need to have at least one enabled!
|
||||
# - Use option "auto" to automatically detect support
|
||||
# - Use true or false (no qoutes) to enable or disable each protocol
|
||||
protocol:
|
||||
ipv4: true
|
||||
ipv6: false
|
||||
@@ -26,11 +22,6 @@ port: 8000
|
||||
# - Use -1 to use the server port.
|
||||
# - Specify a port to override the default.
|
||||
autorunPortOverride: -1
|
||||
# -- SSL options --
|
||||
ssl:
|
||||
enabled: false
|
||||
certPath: "./certs/cert.pem"
|
||||
keyPath: "./certs/privkey.pem"
|
||||
# -- SECURITY CONFIGURATION --
|
||||
# Toggle whitelist mode
|
||||
whitelistMode: true
|
||||
@@ -40,8 +31,6 @@ enableForwardedWhitelist: true
|
||||
whitelist:
|
||||
- ::1
|
||||
- 127.0.0.1
|
||||
# Automatically whitelist Docker host and gateway IPs
|
||||
whitelistDockerHosts: true
|
||||
# Toggle basic authentication for endpoints
|
||||
basicAuthMode: false
|
||||
# Basic authentication credentials
|
||||
@@ -76,28 +65,20 @@ autheliaAuth: false
|
||||
# the username and passwords for basic auth are the same as those
|
||||
# for the individual accounts
|
||||
perUserBasicAuth: false
|
||||
# Minimum log level to display in the terminal (DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3)
|
||||
minLogLevel: 0
|
||||
|
||||
# User session timeout *in seconds* (defaults to 24 hours).
|
||||
## Set to a positive number to expire session after a certain time of inactivity
|
||||
## Set to 0 to expire session when the browser is closed
|
||||
## Set to a negative number to disable session expiration
|
||||
sessionTimeout: -1
|
||||
# Used to sign session cookies. Will be auto-generated if not set
|
||||
cookieSecret: ''
|
||||
# Disable CSRF protection - NOT RECOMMENDED
|
||||
disableCsrfProtection: false
|
||||
# Disable startup security checks - NOT RECOMMENDED
|
||||
securityOverride: false
|
||||
# -- LOGGING CONFIGURATION --
|
||||
logging:
|
||||
# Enable access logging to access.log file
|
||||
# Records new connections with timestamp, IP address and user agent
|
||||
enableAccessLog: true
|
||||
# Minimum log level to display in the terminal (DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3)
|
||||
minLogLevel: 0
|
||||
# -- RATE LIMITING CONFIGURATION --
|
||||
rateLimiting:
|
||||
# Use X-Real-IP header instead of socket IP for rate limiting
|
||||
# Only enable this if you are using a properly configured reverse proxy (like Nginx/traefik/Caddy)
|
||||
preferRealIpHeader: false
|
||||
# -- ADVANCED CONFIGURATION --
|
||||
# Open the browser automatically
|
||||
autorun: true
|
||||
@@ -114,8 +95,6 @@ backups:
|
||||
chat:
|
||||
# Enable automatic chat backups
|
||||
enabled: true
|
||||
# Verify integrity of chat files before saving
|
||||
checkIntegrity: true
|
||||
# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
|
||||
maxTotalBackups: -1
|
||||
# Interval in milliseconds to throttle chat backups per user
|
||||
@@ -135,16 +114,6 @@ thumbnails:
|
||||
# Maximum thumbnail dimensions per type [width, height]
|
||||
dimensions: { 'bg': [160, 90], 'avatar': [96, 144] }
|
||||
|
||||
# PERFORMANCE-RELATED CONFIGURATION
|
||||
performance:
|
||||
# Enables lazy loading of character cards. Improves performances with large card libraries.
|
||||
# May have compatibility issues with some extensions.
|
||||
lazyLoadCharacters: false
|
||||
# The maximum amount of memory that parsed character cards can use. Set to 0 to disable memory caching.
|
||||
memoryCacheCapacity: '100mb'
|
||||
# Enables disk caching for character cards. Improves performances with large card libraries.
|
||||
useDiskCache: true
|
||||
|
||||
# Allow secret keys exposure via API
|
||||
allowKeysExposure: false
|
||||
# Skip new default content checks
|
||||
@@ -155,7 +124,6 @@ whitelistImportDomains:
|
||||
- cdn.discordapp.com
|
||||
- files.catbox.moe
|
||||
- raw.githubusercontent.com
|
||||
- char-archive.evulid.cc
|
||||
# API request overrides (for KoboldAI and Text Completion APIs)
|
||||
## Note: host includes the port number if it's not the default (80 or 443)
|
||||
## Format is an array of objects:
|
||||
@@ -213,10 +181,6 @@ ollama:
|
||||
# * 0: Unload the model immediately after the request
|
||||
# * N (any positive number): Keep the model loaded for N seconds after the request.
|
||||
keepAlive: -1
|
||||
# Controls the "num_batch" (batch size) parameter of the generation request
|
||||
# * -1: Use the default value of the model
|
||||
# * N (positive number): Use the specified value. Must be a power of 2, e.g. 128, 256, 512, etc.
|
||||
batchSize: -1
|
||||
# -- ANTHROPIC CLAUDE API CONFIGURATION --
|
||||
claude:
|
||||
# Enables caching of the system prompt (if supported).
|
||||
@@ -236,5 +200,3 @@ claude:
|
||||
cachingAtDepth: -1
|
||||
# -- SERVER PLUGIN CONFIGURATION --
|
||||
enableServerPlugins: false
|
||||
# Attempt to automatically update server plugins on startup
|
||||
enableServerPluginsAutoUpdate: true
|
||||
|
@@ -563,10 +563,6 @@
|
||||
"filename": "presets/context/Llama 3 Instruct.json",
|
||||
"type": "context"
|
||||
},
|
||||
{
|
||||
"filename": "presets/context/Llama 4 Instruct.json",
|
||||
"type": "context"
|
||||
},
|
||||
{
|
||||
"filename": "presets/context/Phi.json",
|
||||
"type": "context"
|
||||
@@ -667,10 +663,6 @@
|
||||
"filename": "presets/instruct/Llama 3 Instruct.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Llama 4 Instruct.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Phi.json",
|
||||
"type": "instruct"
|
||||
@@ -679,6 +671,10 @@
|
||||
"filename": "presets/moving-ui/Default.json",
|
||||
"type": "moving_ui"
|
||||
},
|
||||
{
|
||||
"filename": "presets/moving-ui/Black Magic Time.json",
|
||||
"type": "moving_ui"
|
||||
},
|
||||
{
|
||||
"filename": "presets/quick-replies/Default.json",
|
||||
"type": "quick_replies"
|
||||
@@ -794,13 +790,5 @@
|
||||
{
|
||||
"filename": "presets/context/DeepSeek-V2.5.json",
|
||||
"type": "context"
|
||||
},
|
||||
{
|
||||
"filename": "presets/reasoning/DeepSeek.json",
|
||||
"type": "reasoning"
|
||||
},
|
||||
{
|
||||
"filename": "presets/reasoning/Blank.json",
|
||||
"type": "reasoning"
|
||||
}
|
||||
]
|
||||
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"story_string": "<|begin_of_text|><|header_start|>system<|header_end|>\n\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}}{{trim}}<|eot|>",
|
||||
"example_separator": "",
|
||||
"chat_start": "",
|
||||
"use_stop_strings": false,
|
||||
"allow_jailbreak": false,
|
||||
"always_force_name2": true,
|
||||
"trim_sentences": false,
|
||||
"single_line": false,
|
||||
"name": "Llama 4 Instruct"
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
"input_suffix": "<|eot_id|>",
|
||||
"system_suffix": "<|eot_id|>",
|
||||
"user_alignment_message": "",
|
||||
"system_same_as_user": false,
|
||||
"system_same_as_user": true,
|
||||
"last_system_sequence": "",
|
||||
"name": "Llama 3 Instruct"
|
||||
}
|
||||
|
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"input_sequence": "<|header_start|>user<|header_end|>\n\n",
|
||||
"output_sequence": "<|header_start|>assistant<|header_end|>\n\n",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence": "<|header_start|>system<|header_end|>\n\n",
|
||||
"stop_sequence": "<|eot|>",
|
||||
"wrap": false,
|
||||
"macro": true,
|
||||
"names_behavior": "always",
|
||||
"activation_regex": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"first_output_sequence": "",
|
||||
"skip_examples": false,
|
||||
"output_suffix": "<|eot|>",
|
||||
"input_suffix": "<|eot|>",
|
||||
"system_suffix": "<|eot|>",
|
||||
"user_alignment_message": "",
|
||||
"system_same_as_user": false,
|
||||
"last_system_sequence": "",
|
||||
"name": "Llama 4 Instruct"
|
||||
}
|
45
default/content/presets/moving-ui/Black Magic Time.json
Normal file
45
default/content/presets/moving-ui/Black Magic Time.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "Black Magic Time",
|
||||
"movingUIState": {
|
||||
"sheld": {
|
||||
"top": 488,
|
||||
"left": 1407,
|
||||
"right": 1,
|
||||
"bottom": 4,
|
||||
"margin": "unset",
|
||||
"width": 471,
|
||||
"height": 439
|
||||
},
|
||||
"floatingPrompt": {
|
||||
"width": 369,
|
||||
"height": 441
|
||||
},
|
||||
"right-nav-panel": {
|
||||
"top": 0,
|
||||
"left": 1400,
|
||||
"right": 111,
|
||||
"bottom": 446,
|
||||
"margin": "unset",
|
||||
"width": 479,
|
||||
"height": 487
|
||||
},
|
||||
"WorldInfo": {
|
||||
"top": 41,
|
||||
"left": 369,
|
||||
"right": 642,
|
||||
"bottom": 51,
|
||||
"margin": "unset",
|
||||
"width": 1034,
|
||||
"height": 858
|
||||
},
|
||||
"left-nav-panel": {
|
||||
"top": 442,
|
||||
"left": 0,
|
||||
"right": 1546,
|
||||
"bottom": 25,
|
||||
"margin": "unset",
|
||||
"width": 368,
|
||||
"height": 483
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,11 +28,12 @@
|
||||
"wrap_in_quotes": false,
|
||||
"names_behavior": 0,
|
||||
"send_if_empty": "",
|
||||
"jailbreak_system": false,
|
||||
"impersonation_prompt": "[Write your next reply from the point of view of {{user}}, using the chat history so far as a guideline for the writing style of {{user}}. Don't write as {{char}} or system. Don't describe actions of {{char}}.]",
|
||||
"new_chat_prompt": "[Start a new Chat]",
|
||||
"new_group_chat_prompt": "[Start a new group chat. Group members: {{group}}]",
|
||||
"new_example_chat_prompt": "[Example Chat]",
|
||||
"continue_nudge_prompt": "[Continue your last message without repeating its original content.]",
|
||||
"continue_nudge_prompt": "[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]",
|
||||
"bias_preset_selected": "Default (none)",
|
||||
"reverse_proxy": "",
|
||||
"proxy_password": "",
|
||||
|
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Blank",
|
||||
"prefix": "",
|
||||
"suffix": "",
|
||||
"separator": ""
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "DeepSeek",
|
||||
"prefix": "<think>\n",
|
||||
"suffix": "\n</think>",
|
||||
"separator": "\n\n"
|
||||
}
|
@@ -593,7 +593,7 @@
|
||||
"new_chat_prompt": "[Start a new Chat]",
|
||||
"new_group_chat_prompt": "[Start a new group chat. Group members: {{group}}]",
|
||||
"new_example_chat_prompt": "[Example Chat]",
|
||||
"continue_nudge_prompt": "[Continue your last message without repeating its original content.]",
|
||||
"continue_nudge_prompt": "[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]",
|
||||
"bias_preset_selected": "Default (none)",
|
||||
"bias_presets": {
|
||||
"Default (none)": [],
|
||||
@@ -626,6 +626,7 @@
|
||||
"ai21_model": "jamba-1.5-large",
|
||||
"windowai_model": "",
|
||||
"openrouter_model": "OR_Website",
|
||||
"jailbreak_system": true,
|
||||
"reverse_proxy": "",
|
||||
"chat_completion_source": "openai",
|
||||
"max_context_unlocked": false,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import getWebpackServeMiddleware from '../src/middleware/webpack-serve.js';
|
||||
|
||||
const middleware = getWebpackServeMiddleware();
|
||||
await middleware.runWebpackCompiler({ forceDist: true });
|
||||
await middleware.runWebpackCompiler();
|
||||
|
@@ -1,12 +1,10 @@
|
||||
version: "3"
|
||||
services:
|
||||
sillytavern:
|
||||
build: ..
|
||||
container_name: sillytavern
|
||||
hostname: sillytavern
|
||||
image: ghcr.io/sillytavern/sillytavern:latest
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- FORCE_COLOR=1
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
|
@@ -5,8 +5,5 @@ if [ ! -e "config/config.yaml" ]; then
|
||||
cp -r "default/config.yaml" "config/config.yaml"
|
||||
fi
|
||||
|
||||
# Execute postinstall to auto-populate config.yaml with missing values
|
||||
npm run postinstall
|
||||
|
||||
# Start the server
|
||||
exec node server.js --listen "$@"
|
||||
|
38
index.d.ts
vendored
38
index.d.ts
vendored
@@ -1,36 +1,7 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { CsrfSyncedToken } from 'csrf-sync';
|
||||
import { UserDirectoryList, User } from './src/users.js';
|
||||
import { CommandLineArguments } from './src/command-line.js';
|
||||
import { EVENT_NAMES } from './src/server-events.js';
|
||||
|
||||
/**
|
||||
* Event payload for SERVER_STARTED event.
|
||||
*/
|
||||
export interface ServerStartedEvent {
|
||||
/**
|
||||
* The URL the server is listening on.
|
||||
*/
|
||||
url: URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of all server events to their payload types.
|
||||
*/
|
||||
export interface ServerEventMap {
|
||||
[EVENT_NAMES.SERVER_STARTED]: [ServerStartedEvent];
|
||||
}
|
||||
import { UserDirectoryList, User } from "./src/users";
|
||||
import { CsrfSyncedToken } from "csrf-sync";
|
||||
|
||||
declare global {
|
||||
declare namespace NodeJS {
|
||||
export interface Process {
|
||||
/**
|
||||
* A global instance of the server events emitter.
|
||||
*/
|
||||
serverEvents: EventEmitter<ServerEventMap>;
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace CookieSessionInterfaces {
|
||||
export interface CookieSessionObject {
|
||||
/**
|
||||
@@ -61,9 +32,4 @@ declare global {
|
||||
* The root directory for user data.
|
||||
*/
|
||||
var DATA_ROOT: string;
|
||||
|
||||
/**
|
||||
* Parsed command line arguments.
|
||||
*/
|
||||
var COMMAND_LINE_ARGS: CommandLineArguments;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ES2023",
|
||||
"moduleResolution": "Bundler",
|
||||
"moduleResolution": "Node",
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"checkJs": true,
|
||||
|
2565
package-lock.json
generated
2565
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
90
package.json
90
package.json
@@ -1,62 +1,38 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@adobe/css-tools": "^4.4.2",
|
||||
"@adobe/css-tools": "^4.4.0",
|
||||
"@agnai/sentencepiece-js": "^1.1.1",
|
||||
"@agnai/web-tokenizers": "^0.1.3",
|
||||
"@iconfu/svg-inject": "^1.2.3",
|
||||
"@jimp/core": "^1.6.0",
|
||||
"@jimp/js-bmp": "^1.6.0",
|
||||
"@jimp/js-gif": "^1.6.0",
|
||||
"@jimp/js-tiff": "^1.6.0",
|
||||
"@jimp/plugin-circle": "^1.6.0",
|
||||
"@jimp/plugin-color": "^1.6.0",
|
||||
"@jimp/plugin-contain": "^1.6.0",
|
||||
"@jimp/plugin-cover": "^1.6.0",
|
||||
"@jimp/plugin-crop": "^1.6.0",
|
||||
"@jimp/plugin-displace": "^1.6.0",
|
||||
"@jimp/plugin-fisheye": "^1.6.0",
|
||||
"@jimp/plugin-flip": "^1.6.0",
|
||||
"@jimp/plugin-mask": "^1.6.0",
|
||||
"@jimp/plugin-quantize": "^1.6.0",
|
||||
"@jimp/plugin-rotate": "^1.6.0",
|
||||
"@jimp/plugin-threshold": "^1.6.0",
|
||||
"@jimp/wasm-avif": "^1.6.0",
|
||||
"@jimp/wasm-jpeg": "^1.6.0",
|
||||
"@jimp/wasm-png": "^1.6.0",
|
||||
"@jimp/wasm-webp": "^1.6.0",
|
||||
"@mozilla/readability": "^0.6.0",
|
||||
"@mozilla/readability": "^0.5.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zeldafan0225/ai_horde": "^5.2.0",
|
||||
"@zeldafan0225/ai_horde": "^5.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"bing-translate-api": "^4.0.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"bowser": "^2.11.0",
|
||||
"bytes": "^3.1.2",
|
||||
"chalk": "^5.4.1",
|
||||
"command-exists": "^1.2.9",
|
||||
"compression": "^1.8.0",
|
||||
"compression": "^1",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cookie-session": "^2.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"crc": "^4.3.2",
|
||||
"csrf-sync": "^4.0.3",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"dompurify": "^3.2.4",
|
||||
"dompurify": "^3.1.7",
|
||||
"droll": "^0.2.1",
|
||||
"express": "^4.21.0",
|
||||
"form-data": "^4.0.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"google-translate-api-browser": "^3.0.1",
|
||||
"google-translate-api-x": "^10.7.2",
|
||||
"google-translate-api-x": "^10.7.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^7.2.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"helmet": "^7.1.0",
|
||||
"highlight.js": "^11.10.0",
|
||||
"html-entities": "^2.5.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ip-matching": "^2.1.2",
|
||||
"ip-regex": "^5.0.0",
|
||||
"ipaddr.js": "^2.2.0",
|
||||
"is-docker": "^3.0.0",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"jimp": "^0.22.10",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
@@ -64,27 +40,28 @@
|
||||
"morphdom": "^2.7.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"node-persist": "^4.0.4",
|
||||
"node-persist": "^4.0.1",
|
||||
"open": "^8.4.2",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"proxy-agent": "^6.5.0",
|
||||
"rate-limiter-flexible": "^5.0.5",
|
||||
"response-time": "^2.3.3",
|
||||
"proxy-agent": "^6.4.0",
|
||||
"rate-limiter-flexible": "^5.0.0",
|
||||
"response-time": "^2.3.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"seedrandom": "^3.0.5",
|
||||
"showdown": "^2.1.0",
|
||||
"sillytavern-transformers": "2.14.6",
|
||||
"simple-git": "^3.27.0",
|
||||
"simple-git": "^3.19.1",
|
||||
"slidetoggle": "^4.0.0",
|
||||
"tiktoken": "^1.0.20",
|
||||
"tiktoken": "^1.0.16",
|
||||
"url-join": "^5.0.0",
|
||||
"vectra": "^0.2.2",
|
||||
"wavefile": "^11.0.0",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack": "^5.95.0",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.18.1",
|
||||
"yaml": "^2.7.0",
|
||||
"ws": "^8.17.1",
|
||||
"yaml": "^2.3.4",
|
||||
"yargs": "^17.7.1",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
@@ -109,11 +86,9 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.12.14",
|
||||
"version": "1.12.11",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"debug": "node --inspect server.js",
|
||||
"start:electron": "cd ./src/electron && npm run start",
|
||||
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
|
||||
"start:bun": "bun server.js",
|
||||
"start:no-csrf": "node server.js --disableCsrf",
|
||||
@@ -132,25 +107,25 @@
|
||||
},
|
||||
"main": "server.js",
|
||||
"devDependencies": {
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/bytes": "^3.1.5",
|
||||
"@types/archiver": "^6.0.2",
|
||||
"@types/command-exists": "^1.2.3",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/cookie-parser": "^1.4.8",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
"@types/cookie-session": "^2.0.49",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/deno": "^2.2.0",
|
||||
"@types/deno": "^2.0.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jquery": "^3.5.32",
|
||||
"@types/jquery": "^3.5.29",
|
||||
"@types/jquery-cropper": "^1.0.4",
|
||||
"@types/jquery.transit": "^0.9.33",
|
||||
"@types/jqueryui": "^1.12.24",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/jqueryui": "^1.12.23",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/multer": "^1.4.12",
|
||||
"@types/node": "^18.19.80",
|
||||
"@types/node": "^18.19.55",
|
||||
"@types/node-persist": "^3.1.8",
|
||||
"@types/png-chunk-text": "^1.0.3",
|
||||
"@types/png-chunks-encode": "^1.0.2",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/response-time": "^2.3.8",
|
||||
"@types/select2": "^4.0.63",
|
||||
@@ -158,7 +133,6 @@
|
||||
"@types/write-file-atomic": "^4.0.3",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-jsdoc": "^48.10.0"
|
||||
"eslint": "^8.57.0"
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { default as git, CheckRepoActions } from 'simple-git';
|
||||
import { default as git } from 'simple-git';
|
||||
import { color } from './src/util.js';
|
||||
|
||||
const __dirname = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -48,13 +48,6 @@ async function updatePlugins() {
|
||||
console.log(`Updating plugin ${color.green(directory)}...`);
|
||||
const pluginPath = path.join(pluginsPath, directory);
|
||||
const pluginRepo = git(pluginPath);
|
||||
|
||||
const isRepo = await pluginRepo.checkIsRepo(CheckRepoActions.IS_REPO_ROOT);
|
||||
if (!isRepo) {
|
||||
console.log(`Directory ${color.yellow(directory)} is not a Git repository`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await pluginRepo.fetch();
|
||||
const commitHash = await pluginRepo.revparse(['HEAD']);
|
||||
const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
|
||||
|
@@ -7,13 +7,26 @@ import crypto from 'node:crypto';
|
||||
import process from 'node:process';
|
||||
import yaml from 'yaml';
|
||||
import _ from 'lodash';
|
||||
import chalk from 'chalk';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
/**
|
||||
* Colorizes console output.
|
||||
*/
|
||||
const color = chalk;
|
||||
const color = {
|
||||
byNum: (mess, fgNum) => {
|
||||
mess = mess || '';
|
||||
fgNum = fgNum === undefined ? 31 : fgNum;
|
||||
return '\u001b[' + fgNum + 'm' + mess + '\u001b[39m';
|
||||
},
|
||||
black: (mess) => color.byNum(mess, 30),
|
||||
red: (mess) => color.byNum(mess, 31),
|
||||
green: (mess) => color.byNum(mess, 32),
|
||||
yellow: (mess) => color.byNum(mess, 33),
|
||||
blue: (mess) => color.byNum(mess, 34),
|
||||
magenta: (mess) => color.byNum(mess, 35),
|
||||
cyan: (mess) => color.byNum(mess, 36),
|
||||
white: (mess) => color.byNum(mess, 37),
|
||||
};
|
||||
|
||||
const keyMigrationMap = [
|
||||
{
|
||||
@@ -91,22 +104,6 @@ const keyMigrationMap = [
|
||||
newKey: 'extensions.models.textToSpeech',
|
||||
migrate: (value) => value,
|
||||
},
|
||||
{
|
||||
oldKey: 'minLogLevel',
|
||||
newKey: 'logging.minLogLevel',
|
||||
migrate: (value) => value,
|
||||
},
|
||||
{
|
||||
oldKey: 'cardsCacheCapacity',
|
||||
newKey: 'performance.memoryCacheCapacity',
|
||||
migrate: (value) => `${value}mb`,
|
||||
},
|
||||
{
|
||||
oldKey: 'cookieSecret',
|
||||
newKey: 'cookieSecret',
|
||||
migrate: () => void 0,
|
||||
remove: true,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -166,17 +163,8 @@ function addMissingConfigValues() {
|
||||
|
||||
// Migrate old keys to new keys
|
||||
const migratedKeys = [];
|
||||
for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) {
|
||||
for (const { oldKey, newKey, migrate } of keyMigrationMap) {
|
||||
if (_.has(config, oldKey)) {
|
||||
if (remove) {
|
||||
_.unset(config, oldKey);
|
||||
migratedKeys.push({
|
||||
oldKey,
|
||||
newValue: void 0,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const oldValue = _.get(config, oldKey);
|
||||
const newValue = migrate(oldValue);
|
||||
_.set(config, newKey, newValue);
|
||||
|
@@ -146,15 +146,3 @@ input.extension_missing[type="checkbox"] {
|
||||
.extensions_info .extension_actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.extensions_toolbar {
|
||||
top: 0;
|
||||
position: sticky;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
gap: 5px;
|
||||
z-index: 1;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
@@ -216,6 +216,8 @@
|
||||
|
||||
}
|
||||
|
||||
#showRawPrompt,
|
||||
#copyPromptToClipboard,
|
||||
#groupCurrentMemberPopoutButton,
|
||||
#summaryExtensionPopoutButton {
|
||||
display: none;
|
||||
|
@@ -72,10 +72,6 @@ dialog {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.popup.left_aligned_dialogue_popup .popup-content {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* Opening animation */
|
||||
.popup[opening] {
|
||||
animation: pop-in var(--popup-animation-speed) ease-in-out;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
.scrollable-buttons-container {
|
||||
max-height: 50vh; /* Use viewport height instead of fixed pixels */
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch; /* Momentum scrolling on iOS */
|
||||
margin-top: 1rem; /* m-t-1 is equivalent to margin-top: 1rem; */
|
||||
flex-shrink: 1;
|
||||
|
@@ -13,7 +13,6 @@
|
||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)*2));
|
||||
color: var(--SmartThemeBodyColor);
|
||||
z-index: 40000;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection .select2-selection__clear {
|
||||
|
@@ -96,6 +96,11 @@ body.charListGrid #rm_print_characters_block .group_select .group_name_block,
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#user_avatar_block.gridView .avatar-container .avatar-buttons {
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
body.charListGrid #rm_print_characters_block .bogus_folder_select .character_select_container,
|
||||
body.charListGrid #rm_print_characters_block .character_select .character_select_container,
|
||||
body.charListGrid #rm_print_characters_block .group_select .group_select_container,
|
||||
@@ -226,16 +231,10 @@ body.big-avatars .avatars_inline_small .avatar img {
|
||||
body.big-avatars .avatars_inline {
|
||||
max-height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) + 2 * var(--avatar-base-border-radius));
|
||||
}
|
||||
body.big-avatars .avatars_inline.avatars_multiline {
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
body.big-avatars .avatars_inline.avatars_inline_small {
|
||||
height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-small-factor) + 2 * var(--avatar-base-border-radius));
|
||||
}
|
||||
body.big-avatars .avatars_inline.avatars_inline_small.avatars_multiline {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
body:not(.big-avatars) .avatars_inline_small .avatar_collage {
|
||||
min-width: calc(var(--avatar-base-width) * var(--inline-avatar-small-factor));
|
||||
@@ -494,7 +493,3 @@ label[for="trim_spaces"]:not(:has(input:checked)) small {
|
||||
#mistralai_other_models:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba {
|
||||
filter: brightness(0.5);
|
||||
}
|
||||
|
37
public/global.d.ts
vendored
37
public/global.d.ts
vendored
@@ -1,25 +1,18 @@
|
||||
import libs from './lib';
|
||||
import getContext from './scripts/st-context';
|
||||
import { power_user } from './scripts/power-user';
|
||||
|
||||
// Global namespace modules
|
||||
declare var ai;
|
||||
declare var pdfjsLib;
|
||||
declare var ePub;
|
||||
|
||||
declare var SillyTavern: {
|
||||
getContext(): typeof getContext;
|
||||
llm: any;
|
||||
libs: typeof libs;
|
||||
};
|
||||
|
||||
declare global {
|
||||
// Custom types
|
||||
declare type InstructSettings = typeof power_user.instruct;
|
||||
|
||||
// Global namespace modules
|
||||
interface Window {
|
||||
ai: any;
|
||||
}
|
||||
|
||||
declare var pdfjsLib;
|
||||
declare var ePub;
|
||||
|
||||
declare var SillyTavern: {
|
||||
getContext(): typeof getContext;
|
||||
llm: any;
|
||||
libs: typeof libs;
|
||||
};
|
||||
|
||||
// Jquery plugins
|
||||
interface JQuery {
|
||||
nanogallery2(options?: any): JQuery;
|
||||
@@ -47,12 +40,4 @@ declare global {
|
||||
searchInputCssClass?: string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a text to a target language using a translation provider.
|
||||
* @param text Text to translate
|
||||
* @param lang Target language
|
||||
* @param provider Translation provider
|
||||
*/
|
||||
async function translate(text: string, lang: string, provider: string = null): Promise<string>;
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 27.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="katman_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 438.67001 481.44999"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="XAI_Logo.svg"
|
||||
width="438.67001"
|
||||
height="481.45001"
|
||||
inkscape:version="1.3 (0e150ed, 2023-07-21)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs4" /><sodipodi:namedview
|
||||
id="namedview4"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.39645207"
|
||||
inkscape:cx="219.44645"
|
||||
inkscape:cy="238.36425"
|
||||
inkscape:window-width="1512"
|
||||
inkscape:window-height="856"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="38"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="katman_1" /> <g
|
||||
id="g4"
|
||||
transform="translate(-201.61,-56.91)"> <polygon
|
||||
points="631.96,538.36 640.28,93.18 557.09,211.99 565.4,538.36 "
|
||||
id="polygon1" /> <polygon
|
||||
points="379.35,284.53 430.13,357.05 640.28,56.91 538.72,56.91 "
|
||||
id="polygon2" /> <polygon
|
||||
points="353.96,465.84 303.17,393.31 201.61,538.36 303.17,538.36 "
|
||||
id="polygon3" /> <polygon
|
||||
points="531.69,538.36 303.17,211.99 201.61,211.99 430.13,538.36 "
|
||||
id="polygon4" /> </g> </svg>
|
Before Width: | Height: | Size: 1.6 KiB |
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,7 @@
|
||||
"**/.git/**",
|
||||
"lib/**",
|
||||
"**/*.min.js",
|
||||
"scripts/extensions/quick-reply/lib/**",
|
||||
"scripts/extensions/tts/lib/**"
|
||||
"scripts/extensions/quick-reply/lib/**"
|
||||
],
|
||||
"typeAcquisition": {
|
||||
"include": []
|
||||
|
@@ -20,7 +20,6 @@ import * as Popper from '@popperjs/core';
|
||||
import droll from 'droll';
|
||||
import morphdom from 'morphdom';
|
||||
import { toggle as slideToggle } from 'slidetoggle';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Expose the libraries to the 'window' object.
|
||||
@@ -97,7 +96,6 @@ export default {
|
||||
droll,
|
||||
morphdom,
|
||||
slideToggle,
|
||||
chalk,
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -120,5 +118,4 @@ export {
|
||||
droll,
|
||||
morphdom,
|
||||
slideToggle,
|
||||
chalk,
|
||||
};
|
||||
|
@@ -24,22 +24,10 @@ if (typeof Array.prototype.indexOf === 'function') {
|
||||
|
||||
|
||||
/* Polyfill EventEmitter. */
|
||||
/**
|
||||
* Creates an event emitter.
|
||||
* @param {string[]} autoFireAfterEmit Auto-fire event names
|
||||
*/
|
||||
var EventEmitter = function (autoFireAfterEmit = []) {
|
||||
var EventEmitter = function () {
|
||||
this.events = {};
|
||||
this.autoFireLastArgs = new Map();
|
||||
this.autoFireAfterEmit = new Set(autoFireAfterEmit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a listener to an event.
|
||||
* @param {string} event Event name
|
||||
* @param {function} listener Event listener
|
||||
* @returns
|
||||
*/
|
||||
EventEmitter.prototype.on = function (event, listener) {
|
||||
// Unknown event used by external libraries?
|
||||
if (event === undefined) {
|
||||
@@ -52,10 +40,6 @@ EventEmitter.prototype.on = function (event, listener) {
|
||||
}
|
||||
|
||||
this.events[event].push(listener);
|
||||
|
||||
if (this.autoFireAfterEmit.has(event) && this.autoFireLastArgs.has(event)) {
|
||||
listener.apply(this, this.autoFireLastArgs.get(event));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -76,10 +60,6 @@ EventEmitter.prototype.makeLast = function (event, listener) {
|
||||
}
|
||||
|
||||
events.push(listener);
|
||||
|
||||
if (this.autoFireAfterEmit.has(event) && this.autoFireLastArgs.has(event)) {
|
||||
listener.apply(this, this.autoFireLastArgs.get(event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,17 +80,8 @@ EventEmitter.prototype.makeFirst = function (event, listener) {
|
||||
}
|
||||
|
||||
events.unshift(listener);
|
||||
|
||||
if (this.autoFireAfterEmit.has(event) && this.autoFireLastArgs.has(event)) {
|
||||
listener.apply(this, this.autoFireLastArgs.get(event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from an event.
|
||||
* @param {string} event Event name
|
||||
* @param {function} listener Event listener
|
||||
*/
|
||||
EventEmitter.prototype.removeListener = function (event, listener) {
|
||||
var idx;
|
||||
|
||||
@@ -123,10 +94,6 @@ EventEmitter.prototype.removeListener = function (event, listener) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Emits an event with optional arguments.
|
||||
* @param {string} event Event name
|
||||
*/
|
||||
EventEmitter.prototype.emit = async function (event) {
|
||||
let args = [].slice.call(arguments, 1);
|
||||
if (localStorage.getItem('eventTracing') === 'true') {
|
||||
@@ -151,10 +118,6 @@ EventEmitter.prototype.emit = async function (event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.autoFireAfterEmit.has(event)) {
|
||||
this.autoFireLastArgs.set(event, args);
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.emitAndWait = function (event) {
|
||||
@@ -181,14 +144,10 @@ EventEmitter.prototype.emitAndWait = function (event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.autoFireAfterEmit.has(event)) {
|
||||
this.autoFireLastArgs.set(event, args);
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.once = function (event, listener) {
|
||||
this.on(event, function g() {
|
||||
this.on(event, function g () {
|
||||
this.removeListener(event, g);
|
||||
listener.apply(this, arguments);
|
||||
});
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "وضع علامة",
|
||||
"API key (optional)": "مفتاح API (اختياري)",
|
||||
"Server url": "رابط الخادم",
|
||||
"Example: http://127.0.0.1:5000": "مثال: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "مثال: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "نموذج مخصص (اختياري)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (وضع غلاف OpenAI API)",
|
||||
"vLLM API key": "مفتاح واجهة برمجة التطبيقات vLLM",
|
||||
"Example: http://127.0.0.1:8000": "مثال: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "مثال: http://127.0.0.1:8000",
|
||||
"vLLM Model": "نموذج vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (وضع التغليف لواجهة برمجة التطبيقات OpenAI)",
|
||||
"Aphrodite API key": "مفتاح واجهة برمجة التطبيقات Aphrodite",
|
||||
"Aphrodite Model": "نموذج أفروديت",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (خادم إخراج)",
|
||||
"Example: http://127.0.0.1:8080": "مثال: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "مثال: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "مثال: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "مثال: 127.0.0.1:11434",
|
||||
"Ollama Model": "نموذج Ollama",
|
||||
"Download": "تحميل",
|
||||
"Tabby API key": "مفتاح API لـ Tabby",
|
||||
"koboldcpp API key (optional)": "مفتاح koboldcpp API (اختياري)",
|
||||
"Example: http://127.0.0.1:5001": "مثال: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "مثال: 127.0.0.1:5001",
|
||||
"Authorize": "تفويض",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "احصل على رمز واجهة برمجة التطبيقات الخاص بك لموزع الاتصالات باستخدام تدفق OAuth. سيتم توجيهك إلى openrouter.ai",
|
||||
"Bypass status check": "تجاوز فحص الحالة",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "فصل بفواصل دون مسافة بينها",
|
||||
"Custom Stopping Strings": "سلاسل توقف مخصصة",
|
||||
"JSON serialized array of strings": "مصفوفة سلسلة JSON متسلسلة",
|
||||
"Replace Macro in Stop Strings": "استبدال الماكرو في سلاسل التوقف المخصصة",
|
||||
"Replace Macro in Custom Stopping Strings": "استبدال الماكرو في سلاسل التوقف المخصصة",
|
||||
"Auto-Continue": "المتابعة التلقائية",
|
||||
"Allow for Chat Completion APIs": "السماح بواجهات برمجة التطبيقات لإكمال الدردشة",
|
||||
"Target length (tokens)": "الطول المستهدف (رموز)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "حذف موضوع",
|
||||
"Update a theme file": "تحديث ملف السمة",
|
||||
"Save as a new theme": "حفظ كسمة جديدة",
|
||||
"Avatar Style:": "نمط الصورة الرمزية",
|
||||
"Avatar Style": "نمط الصورة الرمزية",
|
||||
"Circle": "دائرة",
|
||||
"Square": "مربع",
|
||||
"Rectangle": "مستطيل",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "تفضيل التعليمات من بطاقة الشخصية",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "إذا تم التحقق وكانت بطاقة الشخصية تحتوي على تجاوز للكسر (تعليمات تاريخ المشاركة)، استخدم ذلك بدلاً من ذلك",
|
||||
"Prefer Character Card Jailbreak": "تفضيل كسر الحصار من بطاقة الشخصية",
|
||||
"never_resize_avatars_tooltip": "تجنب اقتصاص صور الأحرف المستوردة وتغيير حجمها. عند إيقاف التشغيل، قم بالقص/تغيير الحجم إلى 512 × 768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "تجنب اقتصاص صور الأحرف المستوردة وتغيير حجمها. عند إيقاف التشغيل، قم بالقص/تغيير الحجم إلى 512 × 768.",
|
||||
"Never resize avatars": "لا تغيير حجم الصور الرمزية أبدًا",
|
||||
"Show actual file names on the disk, in the characters list display only": "عرض الأسماء الفعلية للملفات على القرص، في عرض قائمة الشخصيات فقط",
|
||||
"Show avatar filenames": "عرض أسماء ملفات الصور الرمزية",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "السحب التلقائي",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "تمكين وظيفة السحب التلقائي. الإعدادات في هذا القسم تؤثر فقط عند تمكين السحب التلقائي",
|
||||
"Minimum generated message length": "الحد الأدنى لطول الرسالة المولدة",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "إذا كانت الرسالة المولدة أقصر من هذا، فتحريض السحب التلقائي",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "إذا كانت الرسالة المولدة أقصر من هذا، فتحريض السحب التلقائي",
|
||||
"Blacklisted words": "الكلمات الممنوعة",
|
||||
"words you dont want generated separated by comma ','": "الكلمات التي لا تريد توليدها مفصولة بفاصلة ','",
|
||||
"Blacklisted word count to swipe": "عدد الكلمات الممنوعة للسحب",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "Flagge",
|
||||
"API key (optional)": "API-Schlüssel (optional)",
|
||||
"Server url": "Server-URL",
|
||||
"Example: http://127.0.0.1:5000": "Beispiel: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Beispiel: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Benutzerdefiniertes Modell (optional)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (OpenAI API-Wrappermodus)",
|
||||
"vLLM API key": "vLLM-API-Schlüssel",
|
||||
"Example: http://127.0.0.1:8000": "Beispiel: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Beispiel: http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM-Modell",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Wrappermodus für OpenAI API)",
|
||||
"Aphrodite API key": "Aphrodite API-Schlüssel",
|
||||
"Aphrodite Model": "Aphrodite-Modell",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (Output-Server)",
|
||||
"Example: http://127.0.0.1:8080": "Beispiel: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Beispiel: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Beispiel: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Beispiel: 127.0.0.1:11434",
|
||||
"Ollama Model": "Ollama-Modell",
|
||||
"Download": "Herunterladen",
|
||||
"Tabby API key": "Tabby API-Schlüssel",
|
||||
"koboldcpp API key (optional)": "koboldcpp API-Schlüssel (optional)",
|
||||
"Example: http://127.0.0.1:5001": "Beispiel: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Beispiel: 127.0.0.1:5001",
|
||||
"Authorize": "Autorisieren",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Hole dein OpenRouter-API-Token mit OAuth-Fluss. Du wirst zu openrouter.ai weitergeleitet",
|
||||
"Bypass status check": "Umgehe Statusüberprüfung",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "getrennt durch Kommas ohne Leerzeichen dazwischen",
|
||||
"Custom Stopping Strings": "Benutzerdefinierte Stoppzeichenfolgen",
|
||||
"JSON serialized array of strings": "JSON serialisierte Reihe von Zeichenfolgen",
|
||||
"Replace Macro in Stop Strings": "Makro in benutzerdefinierten Stoppzeichenfolgen ersetzen",
|
||||
"Replace Macro in Custom Stopping Strings": "Makro in benutzerdefinierten Stoppzeichenfolgen ersetzen",
|
||||
"Auto-Continue": "Automatisch fortsetzen",
|
||||
"Allow for Chat Completion APIs": "Erlaube Chat-Vervollständigungs-APIs",
|
||||
"Target length (tokens)": "Ziel-Länge (Tokens)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Löschen eines Designs",
|
||||
"Update a theme file": "Ein Theme-Datei aktualisieren",
|
||||
"Save as a new theme": "Als neues Theme speichern",
|
||||
"Avatar Style:": "Avatar-Stil",
|
||||
"Avatar Style": "Avatar-Stil",
|
||||
"Circle": "Kreis",
|
||||
"Square": "Quadrat",
|
||||
"Rectangle": "Rechteck",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Bevorzuge Charakterkarten-Prompt",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Wenn aktiviert und die Charakterkarte eine Jailbreak-Überschreibung enthält (Post-History-Instruction), verwende stattdessen diese",
|
||||
"Prefer Character Card Jailbreak": "Bevorzuge Charakterkarten-Jailbreak",
|
||||
"never_resize_avatars_tooltip": "Vermeiden Sie das Zuschneiden und Ändern der Größe importierter Zeichenbilder. Wenn deaktiviert, wird die Größe auf 512 x 768 zugeschnitten/angepasst.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Vermeiden Sie das Zuschneiden und Ändern der Größe importierter Zeichenbilder. Wenn deaktiviert, wird die Größe auf 512 x 768 zugeschnitten/angepasst.",
|
||||
"Never resize avatars": "Avatare niemals verkleinern",
|
||||
"Show actual file names on the disk, in the characters list display only": "Zeige tatsächliche Dateinamen auf der Festplatte, nur in der Anzeige der Charakterliste",
|
||||
"Show avatar filenames": "Avatar-Dateinamen anzeigen",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Automatisches Wischen",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Aktiviere die Auto-Wisch-Funktion. Einstellungen in diesem Abschnitt haben nur dann Auswirkungen, wenn das automatische Wischen aktiviert ist",
|
||||
"Minimum generated message length": "Minimale generierte Nachrichtenlänge",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Wenn die generierte Nachricht kürzer ist als diese, löse automatisches Wischen aus",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Wenn die generierte Nachricht kürzer ist als diese, löse automatisches Wischen aus",
|
||||
"Blacklisted words": "Verbotene Wörter",
|
||||
"words you dont want generated separated by comma ','": "Wörter, die du nicht generiert haben möchtest, durch Komma ',' getrennt",
|
||||
"Blacklisted word count to swipe": "Anzahl der verbotenen Wörter, um zu wischen",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "bandera",
|
||||
"API key (optional)": "Clave API (opcional)",
|
||||
"Server url": "URL del servidor",
|
||||
"Example: http://127.0.0.1:5000": "Ejemplo: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Ejemplo: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Modelo personalizado (opcional)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (modo contenedor de API OpenAI)",
|
||||
"vLLM API key": "Clave API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Ejemplo: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Ejemplo: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Modelo vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Modo envolvente para API de OpenAI)",
|
||||
"Aphrodite API key": "Clave de API de Aphrodite",
|
||||
"Aphrodite Model": "Modelo Afrodita",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (Servidor de salida)",
|
||||
"Example: http://127.0.0.1:8080": "Ejemplo: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Ejemplo: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Ejemplo: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Ejemplo: 127.0.0.1:11434",
|
||||
"Ollama Model": "Modelo Ollama",
|
||||
"Download": "Descargar",
|
||||
"Tabby API key": "Clave API de Tabby",
|
||||
"koboldcpp API key (optional)": "Clave API de koboldcpp (opcional)",
|
||||
"Example: http://127.0.0.1:5001": "Ejemplo: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Ejemplo: 127.0.0.1:5001",
|
||||
"Authorize": "Autorizar",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Obtenga su token de API de OpenRouter utilizando el flujo OAuth. Será redirigido a openrouter.ai",
|
||||
"Bypass status check": "Saltar la verificación del estado",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "separe con comas sin espacio entre ellas",
|
||||
"Custom Stopping Strings": "Cadenas de Detención Personalizadas",
|
||||
"JSON serialized array of strings": "Arreglo de cadenas serializado en JSON",
|
||||
"Replace Macro in Stop Strings": "Reemplazar macro en Cadenas de Detención Personalizadas",
|
||||
"Replace Macro in Custom Stopping Strings": "Reemplazar macro en Cadenas de Detención Personalizadas",
|
||||
"Auto-Continue": "Autocontinuar",
|
||||
"Allow for Chat Completion APIs": "Permitir para APIs de Completado de Chat",
|
||||
"Target length (tokens)": "Longitud objetivo (tokens)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Eliminar un tema",
|
||||
"Update a theme file": "Actualizar un archivo de tema",
|
||||
"Save as a new theme": "Guardar como nuevo tema",
|
||||
"Avatar Style:": "Estilo de Avatar",
|
||||
"Avatar Style": "Estilo de Avatar",
|
||||
"Circle": "Círculo",
|
||||
"Square": "Cuadrado",
|
||||
"Rectangle": "Rectángulo",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferir Indicaciones en Tarjeta de Personaje",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Si está marcado y la tarjeta de personaje contiene una anulación de jailbreak (Instrucciones Post Historial), usar eso en su lugar",
|
||||
"Prefer Character Card Jailbreak": "Preferir Jailbreak en Tarjeta de Personaje",
|
||||
"never_resize_avatars_tooltip": "Evite recortar y cambiar el tamaño de las imágenes de personajes importados. Cuando esté desactivado, recorte/cambie el tamaño a 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evite recortar y cambiar el tamaño de las imágenes de personajes importados. Cuando esté desactivado, recorte/cambie el tamaño a 512x768.",
|
||||
"Never resize avatars": "Nunca redimensionar avatares",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostrar nombres de archivo reales en el disco, solo en la visualización de la lista de personajes",
|
||||
"Show avatar filenames": "Mostrar nombres de archivo de avatares",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Deslizamiento automático",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Habilitar la función de deslizamiento automático. La configuración en esta sección solo tiene efecto cuando el deslizamiento automático está habilitado",
|
||||
"Minimum generated message length": "Longitud mínima del mensaje generado",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Si el mensaje generado es más corto que esto, activar un deslizamiento automático",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Si el mensaje generado es más corto que esto, activar un deslizamiento automático",
|
||||
"Blacklisted words": "Palabras prohibidas",
|
||||
"words you dont want generated separated by comma ','": "palabras que no desea generar separadas por coma ','",
|
||||
"Blacklisted word count to swipe": "Número de palabras prohibidas para deslizar",
|
||||
|
@@ -301,23 +301,23 @@
|
||||
"flag": "fanion",
|
||||
"API key (optional)": "Clé API (optionnelle)",
|
||||
"Server url": "URL du serveur",
|
||||
"Example: http://127.0.0.1:5000": "Exemple : http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Exemple : 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Modèle personnalisé (optionnel)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (mode wrapper de l'API OpenAI)",
|
||||
"vLLM API key": "Clé API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Exemple : http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Exemple : http://127.0.0.1:8000",
|
||||
"vLLM Model": "Modèle vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (mode wrapper pour l'API OpenAI)",
|
||||
"Aphrodite API key": "Clé API Aphrodite",
|
||||
"Aphrodite Model": "Modèle Aphrodite",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp",
|
||||
"Example: http://127.0.0.1:8080": "Exemple : http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Exemple : http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Exemple : 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Exemple : 127.0.0.1:11434",
|
||||
"Ollama Model": "Modèle Ollama",
|
||||
"Download": "Télécharger",
|
||||
"Tabby API key": "Clé API de Tabby",
|
||||
"koboldcpp API key (optional)": "Clé API koboldcpp (facultatif)",
|
||||
"Example: http://127.0.0.1:5001": "Exemple : http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Exemple : 127.0.0.1:5001",
|
||||
"Authorize": "Autoriser",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Obtenez votre jeton API OpenRouter en utilisant le flux OAuth. Vous serez redirigé vers openrouter.ai",
|
||||
"Bypass status check": "Contourner la vérification de l'état",
|
||||
@@ -434,7 +434,7 @@
|
||||
"Non-markdown strings": "Chaînes non Markdown",
|
||||
"Custom Stopping Strings": "Chaînes d'arrêt personnalisées",
|
||||
"JSON serialized array of strings": "Tableau de chaînes sérialisé JSON",
|
||||
"Replace Macro in Stop Strings": "Remplacer les macro dans les chaînes d'arrêt personnalisées",
|
||||
"Replace Macro in Custom Stopping Strings": "Remplacer les macro dans les chaînes d'arrêt personnalisées",
|
||||
"Auto-Continue": "Auto-Continue",
|
||||
"Allow for Chat Completion APIs": "Autoriser les APIs de complétion de chat",
|
||||
"Target length (tokens)": "Longueur cible (tokens)",
|
||||
@@ -508,7 +508,7 @@
|
||||
"Delete a theme": "Supprimer un thème",
|
||||
"Update a theme file": "Mettre à jour un fichier de thème",
|
||||
"Save as a new theme": "Enregistrer en tant que nouveau thème",
|
||||
"Avatar Style:": "Style d'avatar",
|
||||
"Avatar Style": "Style d'avatar",
|
||||
"Circle": "Cercle",
|
||||
"Square": "Carré",
|
||||
"Rectangle": "Rectangle",
|
||||
@@ -581,7 +581,7 @@
|
||||
"Advanced Character Search": "Recherche de personnage avancée",
|
||||
"If checked and the character card contains a prompt override (System Prompt), use that instead": "Si cochée et si la carte de personnage contient un prompt de remplacement (prompt système), l'utiliser à la place",
|
||||
"Prefer Character Card Prompt": "Préférer le prompt du personnage",
|
||||
"never_resize_avatars_tooltip": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",
|
||||
"Never resize avatars": "Ne jamais redimensionner les avatars",
|
||||
"Show actual file names on the disk, in the characters list display only": "Afficher les noms de fichier réels sur le disque, dans l'affichage de la liste de personnages uniquement",
|
||||
"Show avatar filenames": "Afficher les noms de fichier des avatars",
|
||||
@@ -656,7 +656,7 @@
|
||||
"Auto-swipe": "Balayage automatique",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Activer la fonction de balayage automatique. Les paramètres de cette section n'ont d'effet que lorsque le balayage automatique est activé",
|
||||
"Minimum generated message length": "Longueur minimale du message généré",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Si le message généré est plus court que cela, déclenchez un balayage automatique",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Si le message généré est plus court que cela, déclenchez un balayage automatique",
|
||||
"Blacklisted words": "Mots en liste noire",
|
||||
"words you dont want generated separated by comma ','": "mots que vous ne voulez pas générer séparés par des virgules ','",
|
||||
"Blacklisted word count to swipe": "Nombre de mots en liste noire pour balayer",
|
||||
@@ -1420,6 +1420,7 @@
|
||||
"Block Entropy API Key": "Clé API Block Entropy",
|
||||
"Select a Model": "Sélectionner un modèle",
|
||||
"Example: http://localhost:1234/v1": "Exemple: http://localhost:1234/v1",
|
||||
"at the end of the URL!": "à la fin de l'URL !",
|
||||
"(Optional)": "(Optionnel)",
|
||||
"Enter a Model ID": "Saisir un ID de modèle",
|
||||
"Example: gpt-3.5-turbo": "Exemple: gpt-3.5-turbo",
|
||||
@@ -1484,7 +1485,7 @@
|
||||
"(disabled when max recursion steps are used)": "(désactivé lorsque le nombre maximum de pas de récursivité est utilisé)",
|
||||
"Cap the number of entry activation recursions": "Plafonner le nombre de récursions d'activation d'entrée",
|
||||
"Max Recursion Steps": "Nombre maximal d'étapes de récursivité",
|
||||
"0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc": "0 = illimité, 1 = scanne une fois et ne récure pas, 2 = scanne une fois et récure une fois, etc.\n(désactivé lorsque des activations minimales sont utilisées)",
|
||||
"0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\\n(disabled when min activations are used)": "0 = illimité, 1 = scanne une fois et ne récure pas, 2 = scanne une fois et récure une fois, etc.\n(désactivé lorsque des activations minimales sont utilisées)",
|
||||
"Include names with each message into the context for scanning": "Inclure les noms dans chaque message dans le contexte pour l'analyse.",
|
||||
"Apply current sorting as Order": "Appliquer le tri actuel comme ordre",
|
||||
"Display swipe numbers for all messages, not just the last.": "Afficher le nombre de balayage sur tous les messages, et pas seulement le dernier.",
|
||||
@@ -1493,7 +1494,7 @@
|
||||
"Ask": "Demander",
|
||||
"tag_import_none": "Aucun",
|
||||
"tag_import_all": "Tous",
|
||||
"tag_import_existing": "Existant",
|
||||
"Existing": "Existant",
|
||||
"If checked and the character card contains a Post-History Instructions override, use that instead": "Si cette case est cochée et que la carte de personnage contient une instruction de remplacement pour le post-histoire, utilisez-la à la place.",
|
||||
"Prefer Character Card Instructions": "Préférer les instructions du personnage",
|
||||
"Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "Active la sélection automatique du texte saisi dans certains champs de texte lorsque l'on clique dessus ou qu'on les sélectionne. S'applique aux zones de texte de type popup et, éventuellement, à d'autres champs de saisie personnalisés.",
|
||||
@@ -1601,6 +1602,7 @@
|
||||
"Character Expressions": "Expressions de personnages",
|
||||
"Translate text to English before classification": "Traduire le texte en anglais avant de le classer",
|
||||
"Show default images (emojis) if sprite missing": "Afficher les images par défaut (emojis) si le sprite est manquant",
|
||||
"Image Type - talkinghead (extras)": "Type d'image - talkinghead (extras)",
|
||||
"Classifier API": "API de classification",
|
||||
"Select the API for classifying expressions.": "Sélectionnez l'API pour classer les expressions.",
|
||||
"Main API": "API principale",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "merki",
|
||||
"API key (optional)": "API lykill (valkvæmt)",
|
||||
"Server url": "URL þjóns",
|
||||
"Example: http://127.0.0.1:5000": "Dæmi: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Dæmi: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Sérsniðið módel (valkvæmt)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (OpenAI API umbúðastilling)",
|
||||
"vLLM API key": "vLLM API lykill",
|
||||
"Example: http://127.0.0.1:8000": "Dæmi: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Dæmi: http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM líkan",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (OpenAI forritunargrensl)",
|
||||
"Aphrodite API key": "Aphrodite API lykill",
|
||||
"Aphrodite Model": "Afródíta fyrirmynd",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (úttak þjónn)",
|
||||
"Example: http://127.0.0.1:8080": "Dæmi: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Dæmi: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Dæmi: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Dæmi: 127.0.0.1:11434",
|
||||
"Ollama Model": "Ollama módel",
|
||||
"Download": "Niðurhal",
|
||||
"Tabby API key": "Tabby API lykill",
|
||||
"koboldcpp API key (optional)": "koboldcpp API lykill (valfrjálst)",
|
||||
"Example: http://127.0.0.1:5001": "Dæmi: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Dæmi: 127.0.0.1:5001",
|
||||
"Authorize": "Heimild",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Fáðu API lykilinn þinn fyrir OpenRouter með því að nota OAuth strauminn. Þú verður endurvísað(ð/ur) á openrouter.ai",
|
||||
"Bypass status check": "Hlaupa framhjá stöðutík",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "aðskilið með kommum án bila milli",
|
||||
"Custom Stopping Strings": "Eigin stopp-strengir",
|
||||
"JSON serialized array of strings": "JSON raðað fylki af strengjum",
|
||||
"Replace Macro in Stop Strings": "Skiptu út í macro í sérsniðnum stoppa strengjum",
|
||||
"Replace Macro in Custom Stopping Strings": "Skiptu út í macro í sérsniðnum stoppa strengjum",
|
||||
"Auto-Continue": "Sjálfvirk Forná",
|
||||
"Allow for Chat Completion APIs": "Leyfa fyrir spjall Loka APIs",
|
||||
"Target length (tokens)": "Markaðarlengd (texti)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Eyða þema",
|
||||
"Update a theme file": "Uppfæra þemu skrá",
|
||||
"Save as a new theme": "Vista sem nýja þemu",
|
||||
"Avatar Style:": "Avatar Stíll",
|
||||
"Avatar Style": "Avatar Stíll",
|
||||
"Circle": "Hring",
|
||||
"Square": "Reitur",
|
||||
"Rectangle": "Ferhyrningur",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Kosstu kvenkortu fyrirspurn",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Ef merkt er og kortið inniheldur fangabrotsskil, notaðu það í staðinn",
|
||||
"Prefer Character Card Jailbreak": "Kosstu kvenkortu fangabrot",
|
||||
"never_resize_avatars_tooltip": "Forðastu að klippa og breyta stærð innfluttra stafamynda. Þegar slökkt er á því skaltu skera/breyta stærð í 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Forðastu að klippa og breyta stærð innfluttra stafamynda. Þegar slökkt er á því skaltu skera/breyta stærð í 512x768.",
|
||||
"Never resize avatars": "Aldrei breyta stærðinni á merkjum",
|
||||
"Show actual file names on the disk, in the characters list display only": "Sýna raunveruleg nöfn skráa á diskinum, í lista yfir persónur sýna aðeins",
|
||||
"Show avatar filenames": "Sýna nöfn merkja",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Sjálfvirkur sveip",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Virkjaðu sjálfvirka sveiflugerð. Stillingar í þessum hluta hafa aðeins áhrif þegar sjálfvirkur sveiflugerð er virk",
|
||||
"Minimum generated message length": "Lágmarks lengd á mynduðum skilaboðum",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Ef mynduðu skilaboðin eru styttri en þessi, kallaðu fram sjálfvirkar sveiflugerðar",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Ef mynduðu skilaboðin eru styttri en þessi, kallaðu fram sjálfvirkar sveiflugerðar",
|
||||
"Blacklisted words": "Svört orð",
|
||||
"words you dont want generated separated by comma ','": "orð sem þú vilt ekki að framleiða aðskilin með kommu ','",
|
||||
"Blacklisted word count to swipe": "Fjöldi svörtra orða til að sveipa",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "bandiera",
|
||||
"API key (optional)": "Chiave API (opzionale)",
|
||||
"Server url": "URL del server",
|
||||
"Example: http://127.0.0.1:5000": "Esempio: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Esempio: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Modello personalizzato (opzionale)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (modalità wrapper API OpenAI)",
|
||||
"vLLM API key": "Chiave API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Esempio: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Esempio: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Modello vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Modalità wrapper per l'API OpenAI)",
|
||||
"Aphrodite API key": "Chiave API di Aphrodite",
|
||||
"Aphrodite Model": "Modello di Afrodite",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (Server di output)",
|
||||
"Example: http://127.0.0.1:8080": "Esempio: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Esempio: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Esempio: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Esempio: 127.0.0.1:11434",
|
||||
"Ollama Model": "Modello Ollama",
|
||||
"Download": "Scarica",
|
||||
"Tabby API key": "Chiave API di Tabby",
|
||||
"koboldcpp API key (optional)": "Chiave API koboldcpp (opzionale)",
|
||||
"Example: http://127.0.0.1:5001": "Esempio: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Esempio: 127.0.0.1:5001",
|
||||
"Authorize": "Autorizzare",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Ottieni il tuo token API di OpenRouter utilizzando il flusso OAuth. Sarai reindirizzato su openrouter.ai",
|
||||
"Bypass status check": "Ignora controllo stato",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "separati con virgole senza spazio tra loro",
|
||||
"Custom Stopping Strings": "Stringhe di Stop Personalizzate",
|
||||
"JSON serialized array of strings": "Matrice serializzata JSON di stringhe",
|
||||
"Replace Macro in Stop Strings": "Sostituisci Macro in Stringhe di Arresto Personalizzate",
|
||||
"Replace Macro in Custom Stopping Strings": "Sostituisci Macro in Stringhe di Arresto Personalizzate",
|
||||
"Auto-Continue": "Auto-continua",
|
||||
"Allow for Chat Completion APIs": "Consenti per API di completamento chat",
|
||||
"Target length (tokens)": "Lunghezza obiettivo (token)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Elimina un tema",
|
||||
"Update a theme file": "Aggiorna un file di tema",
|
||||
"Save as a new theme": "Salva come nuovo tema",
|
||||
"Avatar Style:": "Stile avatar",
|
||||
"Avatar Style": "Stile avatar",
|
||||
"Circle": "Cerchio",
|
||||
"Square": "Quadrato",
|
||||
"Rectangle": "Rettangolo",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferisci Prompt della Scheda Personaggio",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Se selezionato e la scheda del personaggio contiene una sovrascrittura jailbreak (Istruzione Storico Post), usalo invece",
|
||||
"Prefer Character Card Jailbreak": "Preferisci Jailbreak della Scheda Personaggio",
|
||||
"never_resize_avatars_tooltip": "Evita di ritagliare e ridimensionare le immagini dei personaggi importati. Quando è disattivato, ritaglia/ridimensiona a 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evita di ritagliare e ridimensionare le immagini dei personaggi importati. Quando è disattivato, ritaglia/ridimensiona a 512x768.",
|
||||
"Never resize avatars": "Non ridimensionare mai gli avatar",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostra i nomi file effettivi sul disco, solo nella visualizzazione dell'elenco dei personaggi",
|
||||
"Show avatar filenames": "Mostra nomi file avatar",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Auto-swipe",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Abilita la funzione di auto-swipe. Le impostazioni in questa sezione hanno effetto solo quando l'auto-swipe è abilitato",
|
||||
"Minimum generated message length": "Lunghezza minima del messaggio generato",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Se il messaggio generato è più breve di questo, attiva un'automatica rimozione",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Se il messaggio generato è più breve di questo, attiva un'automatica rimozione",
|
||||
"Blacklisted words": "Parole in blacklist",
|
||||
"words you dont want generated separated by comma ','": "parole che non vuoi generate separate da virgola ','",
|
||||
"Blacklisted word count to swipe": "Numero di parole in blacklist per attivare un'automatica rimozione",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "フラグ",
|
||||
"API key (optional)": "APIキー(オプション)",
|
||||
"Server url": "サーバーURL",
|
||||
"Example: http://127.0.0.1:5000": "例: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "例: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "カスタムモデル(オプション)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (OpenAI API ラッパーモード)",
|
||||
"vLLM API key": "vLLM API キー",
|
||||
"Example: http://127.0.0.1:8000": "例: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "例: http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM モデル",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine(OpenAI APIエンドポイントのパッケージングモード)",
|
||||
"Aphrodite API key": "アフロディーテAPIキー",
|
||||
"Aphrodite Model": "アフロディーテモデル",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp(出力サーバー)",
|
||||
"Example: http://127.0.0.1:8080": "例: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "例: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "例: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "例: 127.0.0.1:11434",
|
||||
"Ollama Model": "Ollamaモデル",
|
||||
"Download": "ダウンロード",
|
||||
"Tabby API key": "TabbyのAPIキー",
|
||||
"koboldcpp API key (optional)": "koboldcpp API キー (オプション)",
|
||||
"Example: http://127.0.0.1:5001": "例: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "例: 127.0.0.1:5001",
|
||||
"Authorize": "承認",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "OAuthフローを使用してOpenRouter APIトークンを取得します。 openrouter.aiにリダイレクトされます",
|
||||
"Bypass status check": "ステータスのチェックをバイパスする",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "間にスペースのないカンマで区切ります",
|
||||
"Custom Stopping Strings": "カスタム停止文字列",
|
||||
"JSON serialized array of strings": "文字列のJSONシリアル化配列",
|
||||
"Replace Macro in Stop Strings": "カスタム停止文字列内のマクロを置換する",
|
||||
"Replace Macro in Custom Stopping Strings": "カスタム停止文字列内のマクロを置換する",
|
||||
"Auto-Continue": "自動継続",
|
||||
"Allow for Chat Completion APIs": "チャット補完APIを許可",
|
||||
"Target length (tokens)": "ターゲット長さ(トークン)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "テーマを削除する",
|
||||
"Update a theme file": "テーマファイルを更新",
|
||||
"Save as a new theme": "新しいテーマとして保存",
|
||||
"Avatar Style:": "アバタースタイル",
|
||||
"Avatar Style": "アバタースタイル",
|
||||
"Circle": "円",
|
||||
"Square": "正方形",
|
||||
"Rectangle": "長方形",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "キャラクターカードのプロンプトを優先",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "チェックされていてキャラクターカードにジェイルブレイクオーバーライド(投稿履歴指示)が含まれている場合、それを代わりに使用します",
|
||||
"Prefer Character Card Jailbreak": "キャラクターカードのJailbreakを優先",
|
||||
"never_resize_avatars_tooltip": "インポートした文字画像の切り取りやサイズ変更を避けます。オフにすると、512x768 に切り取り/サイズ変更されます。",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "インポートした文字画像の切り取りやサイズ変更を避けます。オフにすると、512x768 に切り取り/サイズ変更されます。",
|
||||
"Never resize avatars": "アバターを常にリサイズしない",
|
||||
"Show actual file names on the disk, in the characters list display only": "ディスク上の実際のファイル名を表示します。キャラクターリストの表示にのみ",
|
||||
"Show avatar filenames": "アバターのファイル名を表示",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "オートスワイプ",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "自動スワイプ機能を有効にします。このセクションの設定は、自動スワイプが有効になっている場合にのみ効果があります",
|
||||
"Minimum generated message length": "生成されたメッセージの最小長",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "生成されたメッセージがこれよりも短い場合、自動スワイプをトリガーします",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "生成されたメッセージがこれよりも短い場合、自動スワイプをトリガーします",
|
||||
"Blacklisted words": "ブラックリストされた単語",
|
||||
"words you dont want generated separated by comma ','": "コンマ ',' で区切られた生成したくない単語",
|
||||
"Blacklisted word count to swipe": "スワイプするブラックリストされた単語の数",
|
||||
|
@@ -211,7 +211,7 @@
|
||||
"Sampler Priority": "샘플러 우선 순위",
|
||||
"Ooba only. Determines the order of samplers.": "Ooba 전용. 샘플러의 순서를 결정합니다.",
|
||||
"Character Names Behavior": "캐릭터 이름 동작",
|
||||
"character_names_none": "캐릭터 이름 접두사를 추가하지 않습니다. 그룹 채팅에서는 좋지 않을 수 있으므로, 이 설정을 선택할 때는 주의해야 합니다.",
|
||||
"[title]character_names_none": "캐릭터 이름 접두사를 추가하지 않습니다. 그룹 채팅에서는 좋지 않을 수 있으므로, 이 설정을 선택할 때는 주의해야 합니다.",
|
||||
"Helps the model to associate messages with characters.": "모델이 메시지를 캐릭터와 연관시키는 데 도움이 됩니다.",
|
||||
"None": "없음",
|
||||
"None (not injected)": "없음 (삽입되지 않음)",
|
||||
@@ -320,23 +320,23 @@
|
||||
"flag": "깃발",
|
||||
"API key (optional)": "API 키 (선택 사항)",
|
||||
"Server url": "서버 URL",
|
||||
"Example: http://127.0.0.1:5000": "예시: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "예시: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "사용자 정의 모델 (선택 사항)",
|
||||
"vllm-project/vllm": "vllm-project/vllm(OpenAI API 래퍼 모드)",
|
||||
"vLLM API key": "vLLM API 키",
|
||||
"Example: http://127.0.0.1:8000": "예: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "예: http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM 모델",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (OpenAI API의 래퍼 모드)",
|
||||
"Aphrodite API key": "Aphrodite API 키",
|
||||
"Aphrodite Model": "Aphrodite 모델",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (출력 서버)",
|
||||
"Example: http://127.0.0.1:8080": "예: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "예: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "예: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "예: 127.0.0.1:11434",
|
||||
"Ollama Model": "Ollama 모델",
|
||||
"Download": "다운로드",
|
||||
"Tabby API key": "Tabby API 키",
|
||||
"koboldcpp API key (optional)": "koboldcpp API 키(선택사항)",
|
||||
"Example: http://127.0.0.1:5001": "예: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "예: 127.0.0.1:5001",
|
||||
"Authorize": "승인하기",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "OAuth 플로우를 사용하여 OpenRouter API 토큰을 가져옵니다. openrouter.ai로 리디렉션됩니다.",
|
||||
"Legacy API (pre-OAI, no streaming)": "레거시 API (OAI 이전, 스트리밍 없음)",
|
||||
@@ -404,7 +404,7 @@
|
||||
"Custom API Key": "커스텀 API 키",
|
||||
"Available Models": "사용 가능한 모델",
|
||||
"Prompt Post-Processing": "신속한 후처리",
|
||||
"api_no_connection": "연결이 되지 않았습니다...",
|
||||
"[title]API Connections;[no_connection_text]api_no_connection": "연결이 되지 않았습니다...",
|
||||
"Applies additional processing to the prompt before sending it to the API.": "API로 보내기 전에 프롬프트에 추가 처리를 적용합니다.",
|
||||
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "짧은 테스트 메시지를 보내어 API 연결을 확인합니다. 이에 대해 유료 크레딧이 지불될 수 있음을 인식하세요!",
|
||||
"Test Message": "테스트 메시지",
|
||||
@@ -492,7 +492,7 @@
|
||||
"separate with commas w/o space between": "쉼표로 구분 (공백 없이)",
|
||||
"Custom Stopping Strings": "사용자 정의 중지 문자열",
|
||||
"JSON serialized array of strings": "문자열의 JSON 직렬화된 배열",
|
||||
"Replace Macro in Stop Strings": "사용자 정의 중단 문자열에서 매크로 교체",
|
||||
"Replace Macro in Custom Stopping Strings": "사용자 정의 중단 문자열에서 매크로 교체",
|
||||
"Auto-Continue": "자동 계속하기",
|
||||
"Allow for Chat Completion APIs": "채팅 완성 API 허용",
|
||||
"Target length (tokens)": "대상 길이 (토큰)",
|
||||
@@ -568,7 +568,7 @@
|
||||
"Delete a theme": "테마 삭제",
|
||||
"Update a theme file": "테마 파일 업데이트",
|
||||
"Save as a new theme": "새 테마로 저장",
|
||||
"Avatar Style:": "캐릭터 프로필 스타일",
|
||||
"Avatar Style": "캐릭터 프로필 스타일",
|
||||
"Circle": "원",
|
||||
"Square": "정사각형",
|
||||
"Rectangle": "사각형",
|
||||
@@ -625,7 +625,7 @@
|
||||
"Single-row message input area. Mobile only, no effect on PC": "한 줄짜리 메시지 입력 영역. 모바일 전용, PC에는 영향 없음",
|
||||
"Compact Input Area (Mobile)": "조그마한 입력 영역 (모바일)",
|
||||
"Swipe # for All Messages": "모든 스와이프 메시지에 대해 번호 매기기",
|
||||
"Display swipe numbers for all messages, not just the last.": "마지막 메시지만이 아니라 모든 메시지에 대한 스와이프 번호를 표시합니다.",
|
||||
"[title]Display swipe numbers for all messages, not just the last.": "마지막 메시지만이 아니라 모든 메시지에 대한 스와이프 번호를 표시합니다.",
|
||||
"In the Character Management panel, show quick selection buttons for favorited characters": "캐릭터 관리 패널에서 즐겨찾는 캐릭터에 대한 빠른 선택 버튼을 표시합니다",
|
||||
"Characters Hotswap": "캐릭터 핫스왑",
|
||||
"Enable magnification for zoomed avatar display.": "마우스 포인터를 아바타 위에 올려두면 아바타가 확대 됩니다.",
|
||||
@@ -646,7 +646,7 @@
|
||||
"Prefer Character Card Prompt": "캐릭터 카드 프롬프트 선호",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "선택되어 있고 캐릭터 카드에 (Post-History 지시)탈옥 재정의가 포함 된 경우, 그것을 대신 사용합니다.",
|
||||
"Prefer Character Card Jailbreak": "캐릭터 카드 탈옥 선호",
|
||||
"never_resize_avatars_tooltip": "가져온 캐릭터 이미지를 자르거나 크기를 조정하지 마세요. 꺼져 있으면 512x768로 자르거나 크기를 조정합니다.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "가져온 캐릭터 이미지를 자르거나 크기를 조정하지 마세요. 꺼져 있으면 512x768로 자르거나 크기를 조정합니다.",
|
||||
"Never resize avatars": "아바타 크기 변경하지 않음",
|
||||
"Show actual file names on the disk, in the characters list display only": "실제 파일 이름을 디스크에 표시하며 캐릭터 목록 디스플레이에만",
|
||||
"Show avatar filenames": "아바타 파일 이름 표시",
|
||||
@@ -724,7 +724,7 @@
|
||||
"Auto-swipe": "자동 스와이프",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "자동 스와이프 기능을 활성화합니다. 이 섹션의 설정은 자동 스와이프가 활성화되었을 때만 영향을 미칩니다",
|
||||
"Minimum generated message length": "생성된 메시지 최소 길이",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "생성된 메시지가이보다 짧으면 자동 스와이프를 트리거합니다",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "생성된 메시지가이보다 짧으면 자동 스와이프를 트리거합니다",
|
||||
"Blacklisted words": "금지어",
|
||||
"words you dont want generated separated by comma ','": "쉼표로 구분된 생성하지 않으려는 단어",
|
||||
"Blacklisted word count to swipe": "스와이프할 금지어 개수",
|
||||
@@ -1467,6 +1467,7 @@
|
||||
"menu within": "내의 메뉴",
|
||||
"Translate text to English before classification": "분류 전에 텍스트를 영어로 번역합니다.",
|
||||
"Show default images (emojis) if sprite missing": "해당하는 스프라이트가 없으면 기본 이미지 (이모지들)을 표시합니다.",
|
||||
"Image Type - talkinghead (extras)": "이미지 유형 - 토킹 헤드 (부가 사항)",
|
||||
"Classifier API": "분류를 위한 API",
|
||||
"Select the API for classifying expressions.": "감정 이미지들을 분류할 API를 선택하세요.",
|
||||
"Local": "로컬",
|
||||
@@ -1537,7 +1538,7 @@
|
||||
"Only apply color as accent": "색상은 오직 강조로써만 적용됩니다",
|
||||
"qr--colorClear": "색상 지우기",
|
||||
"Color": "색상",
|
||||
"world_button_title": "캐릭터 로어. 클릭하여 로드하세요. Shift를 클릭하면 '월드 인포 링크' 팝업이 열립니다.",
|
||||
"[title]world_button_title": "캐릭터 로어. 클릭하여 로드하세요. Shift를 클릭하면 '월드 인포 링크' 팝업이 열립니다.",
|
||||
"Select TTS Provider": "TTS 공급자 선택",
|
||||
"tts_enabled": "활성화",
|
||||
"Narrate user messages": "사용자 메시지 나레이션",
|
||||
@@ -1582,15 +1583,15 @@
|
||||
"Prompt Content": "프롬프트 내용",
|
||||
"Instruct Sequences": "지시 시퀀스",
|
||||
"Prefer Character Card Instructions": "캐릭터 카드의 지시사항을 선호",
|
||||
"If checked and the character card contains a Post-History Instructions override, use that instead": "활성화 된 경우, 캐릭터 카드에 Post-History 지시 무시 항목이 포함되어 있으면, 카드 지시사항의 내용으로 대신 사용합니다.",
|
||||
"[title]If checked and the character card contains a Post-History Instructions override, use that instead": "활성화 된 경우, 캐릭터 카드에 Post-History 지시 무시 항목이 포함되어 있으면, 카드 지시사항의 내용으로 대신 사용합니다.",
|
||||
"Auto-select Input Text": "입력 텍스트 자동 선택",
|
||||
"Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "일부 텍스트 필드를 클릭하거나 선택할 때 자동으로 입력된 텍스트가 선택되도록 설정합니다. 팝업 입력창과 기타 커스텀 입력 필드에 적용됩니다.",
|
||||
"[title]Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "일부 텍스트 필드를 클릭하거나 선택할 때 자동으로 입력된 텍스트가 선택되도록 설정합니다. 팝업 입력창과 기타 커스텀 입력 필드에 적용됩니다.",
|
||||
"Markdown Hotkeys": "마크다운 입력 단축키",
|
||||
"markdown_hotkeys_desc": "특정 텍스트 입력창에서 마크다운 형식 문자를 입력하기 위한 단축키를 활성화합니다. '/help hotkeys'를 참고하세요.",
|
||||
"[title]markdown_hotkeys_desc": "특정 텍스트 입력창에서 마크다운 형식 문자를 입력하기 위한 단축키를 활성화합니다. '/help hotkeys'를 참고하세요.",
|
||||
"Show group chat queue": "그룹 채팅 대기열 표시",
|
||||
"In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "그룹 채팅에서 응답을 생성하기 위해 현재 대기 중인 캐릭터와 응답할 순서를 강조 표시합니다.",
|
||||
"[title]In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "그룹 채팅에서 응답을 생성하기 위해 현재 대기 중인 캐릭터와 응답할 순서를 강조 표시합니다.",
|
||||
"Quick 'Impersonate' button": "빠른 '사칭' 버튼",
|
||||
"Show a button in the input area to ask the AI to impersonate your character for a single message": "입력 영역에 AI에게 한 메시지 동안 당신의 캐릭터 연기를 사칭하도록 요청하는 버튼을 표시합니다.",
|
||||
"[title]Show a button in the input area to ask the AI to impersonate your character for a single message": "입력 영역에 AI에게 한 메시지 동안 당신의 캐릭터 연기를 사칭하도록 요청하는 버튼을 표시합니다.",
|
||||
"Injection Template": "삽입 템플릿",
|
||||
"Query messages": "쿼리 메시지 수",
|
||||
"Score threshold": "점수 임계값",
|
||||
@@ -1613,7 +1614,7 @@
|
||||
"Ask": "묻기",
|
||||
"tag_import_none": "불러오지 않음",
|
||||
"tag_import_all": "전부",
|
||||
"tag_import_existing": "기존 태그 참조",
|
||||
"Existing": "기존 태그 참조",
|
||||
"You can add more": "원한다면",
|
||||
"or_welcome": "또는",
|
||||
"from other websites": "를 통해 다른 웹사이트들로부터 불러올 수 있습니다.",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "vlag",
|
||||
"API key (optional)": "API-sleutel (optioneel)",
|
||||
"Server url": "Server-URL",
|
||||
"Example: http://127.0.0.1:5000": "Voorbeeld: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Voorbeeld: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Aangepast model (optioneel)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (OpenAI API-wrappermodus)",
|
||||
"vLLM API key": "vLLM API-sleutel",
|
||||
"Example: http://127.0.0.1:8000": "Voorbeeld: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Voorbeeld: http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM-model",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Wrappermodus voor OpenAI API)",
|
||||
"Aphrodite API key": "Aphrodite API-sleutel",
|
||||
"Aphrodite Model": "Aphrodite-model",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (Output-server)",
|
||||
"Example: http://127.0.0.1:8080": "Voorbeeld: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Voorbeeld: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Voorbeeld: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Voorbeeld: 127.0.0.1:11434",
|
||||
"Ollama Model": "Ollama-model",
|
||||
"Download": "Downloaden",
|
||||
"Tabby API key": "Tabby API-sleutel",
|
||||
"koboldcpp API key (optional)": "koboldcpp API-sleutel (optioneel)",
|
||||
"Example: http://127.0.0.1:5001": "Voorbeeld: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Voorbeeld: 127.0.0.1:5001",
|
||||
"Authorize": "Toestemming geven",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Haal uw OpenRouter API-token op met behulp van OAuth-flow. U wordt doorgestuurd naar openrouter.ai",
|
||||
"Bypass status check": "Omzeil statuscontrole",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "gescheiden met komma's zonder spatie ertussen",
|
||||
"Custom Stopping Strings": "Aangepaste Stopreeksen",
|
||||
"JSON serialized array of strings": "JSON geserialiseerde reeks van strings",
|
||||
"Replace Macro in Stop Strings": "Macro vervangen in aangepaste stopreeksen",
|
||||
"Replace Macro in Custom Stopping Strings": "Macro vervangen in aangepaste stopreeksen",
|
||||
"Auto-Continue": "Automatisch doorgaan",
|
||||
"Allow for Chat Completion APIs": "Chatvervolledigings-API's toestaan",
|
||||
"Target length (tokens)": "Doellengte (tokens)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Verwijder een thema",
|
||||
"Update a theme file": "Werk een themabestand bij",
|
||||
"Save as a new theme": "Opslaan als nieuw thema",
|
||||
"Avatar Style:": "Avatarstijl",
|
||||
"Avatar Style": "Avatarstijl",
|
||||
"Circle": "Cirkel",
|
||||
"Square": "Vierkant",
|
||||
"Rectangle": "Rechthoek",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Voorkeur karakterkaart prompt",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Als aangevinkt en de karakterkaart bevat een jailbreak-override (Post History Instruction), gebruik die in plaats daarvan",
|
||||
"Prefer Character Card Jailbreak": "Voorkeur karakterkaart jailbreak",
|
||||
"never_resize_avatars_tooltip": "Vermijd het bijsnijden en vergroten/verkleinen van geïmporteerde karakterafbeeldingen. Indien uitgeschakeld, bijsnijden/formaat wijzigen naar 512 x 768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Vermijd het bijsnijden en vergroten/verkleinen van geïmporteerde karakterafbeeldingen. Indien uitgeschakeld, bijsnijden/formaat wijzigen naar 512 x 768.",
|
||||
"Never resize avatars": "Avatars nooit verkleinen",
|
||||
"Show actual file names on the disk, in the characters list display only": "Toon de werkelijke bestandsnamen op de schijf, alleen in de weergave van de lijst met personages",
|
||||
"Show avatar filenames": "Toon avatar bestandsnamen",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Automatisch vegen",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Schakel de automatische-vegen functie in. Instellingen in dit gedeelte hebben alleen effect wanneer automatisch vegen is ingeschakeld",
|
||||
"Minimum generated message length": "Minimale gegenereerde berichtlengte",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Als het gegenereerde bericht korter is dan dit, activeer dan een automatische veeg",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Als het gegenereerde bericht korter is dan dit, activeer dan een automatische veeg",
|
||||
"Blacklisted words": "Verboden woorden",
|
||||
"words you dont want generated separated by comma ','": "woorden die je niet gegenereerd wilt hebben gescheiden door komma ','",
|
||||
"Blacklisted word count to swipe": "Aantal verboden woorden om te vegen",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "bandeira",
|
||||
"API key (optional)": "Chave da API (opcional)",
|
||||
"Server url": "URL do servidor",
|
||||
"Example: http://127.0.0.1:5000": "Exemplo: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Exemplo: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Modelo personalizado (opcional)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (modo wrapper da API OpenAI)",
|
||||
"vLLM API key": "Chave de API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Exemplo: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Exemplo: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Modelo vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Modo Wrapper para API OpenAI)",
|
||||
"Aphrodite API key": "Chave da API Aphrodite",
|
||||
"Aphrodite Model": "Modelo Afrodite",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (Servidor de Saída)",
|
||||
"Example: http://127.0.0.1:8080": "Exemplo: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Exemplo: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Exemplo: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Exemplo: 127.0.0.1:11434",
|
||||
"Ollama Model": "Modelo Ollama",
|
||||
"Download": "Baixar",
|
||||
"Tabby API key": "Chave da API do Tabby",
|
||||
"koboldcpp API key (optional)": "Chave API koboldcpp (opcional)",
|
||||
"Example: http://127.0.0.1:5001": "Exemplo: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Exemplo: 127.0.0.1:5001",
|
||||
"Authorize": "Autorizar",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Obtenha seu token da API do OpenRouter usando o fluxo OAuth. Você será redirecionado para openrouter.ai",
|
||||
"Bypass status check": "Ignorar verificação de status",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "separe com vírgulas sem espaço entre",
|
||||
"Custom Stopping Strings": "Cadeias de parada personalizadas",
|
||||
"JSON serialized array of strings": "Matriz de strings serializada em JSON",
|
||||
"Replace Macro in Stop Strings": "Substituir Macro em Strings de Parada Personalizadas",
|
||||
"Replace Macro in Custom Stopping Strings": "Substituir Macro em Strings de Parada Personalizadas",
|
||||
"Auto-Continue": "Auto-Continuar",
|
||||
"Allow for Chat Completion APIs": "Permitir APIs de Completar Chat",
|
||||
"Target length (tokens)": "Comprimento alvo (tokens)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Excluir um tema",
|
||||
"Update a theme file": "Atualizar um arquivo de tema",
|
||||
"Save as a new theme": "Salvar como um novo tema",
|
||||
"Avatar Style:": "Estilo de Avatar",
|
||||
"Avatar Style": "Estilo de Avatar",
|
||||
"Circle": "Círculo",
|
||||
"Square": "Quadrado",
|
||||
"Rectangle": "Retângulo",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferir Prompt do Cartão de Personagem",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Se marcado e o cartão de personagem contiver uma substituição de jailbreak (Instrução de Histórico de Postagens), use isso em vez disso",
|
||||
"Prefer Character Card Jailbreak": "Preferir Jailbreak do Cartão de Personagem",
|
||||
"never_resize_avatars_tooltip": "Evite cortar e redimensionar imagens de personagens importados. Quando desativado, corte/redimensione para 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evite cortar e redimensionar imagens de personagens importados. Quando desativado, corte/redimensione para 512x768.",
|
||||
"Never resize avatars": "Nunca redimensionar avatares",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostrar nomes de arquivo reais no disco, apenas na exibição da lista de personagens",
|
||||
"Show avatar filenames": "Mostrar nomes de arquivo de avatar",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Auto-swipe",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Ativar a função de auto-swipe. As configurações nesta seção só têm efeito quando o auto-swipe está ativado",
|
||||
"Minimum generated message length": "Comprimento mínimo da mensagem gerada",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Se a mensagem gerada for mais curta que isso, acione um auto-swipe",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Se a mensagem gerada for mais curta que isso, acione um auto-swipe",
|
||||
"Blacklisted words": "Palavras proibidas",
|
||||
"words you dont want generated separated by comma ','": "palavras que você não quer geradas separadas por vírgula ','",
|
||||
"Blacklisted word count to swipe": "Contagem de palavras proibidas para swipe",
|
||||
|
@@ -23,8 +23,9 @@
|
||||
"Mirostat Mode": "Режим",
|
||||
"Mirostat Tau": "Tau",
|
||||
"Mirostat Eta": "Eta",
|
||||
"Variability parameter for Mirostat outputs": "Вариативность для выходных данных Mirostat.",
|
||||
"Variability parameter for Mirostat outputs": "Параметр изменчивости для выходных данных Mirostat.",
|
||||
"Learning rate of Mirostat": "Скорость обучения Mirostat.",
|
||||
"Strength of the Contrastive Search regularization term. Set to 0 to disable CS": "Сила условия регуляризации контрастивного поиска. Установите значение 0, чтобы отключить CS.",
|
||||
"Temperature Last": "Температура последней",
|
||||
"LLaMA / Mistral / Yi models only": "Только для моделей LLaMA / Mistral / Yi. Перед этим обязательно выберите подходящий токенизатор.\nПоследовательности, которых не должно быть на выходе.\nОдна на строку. Текст или [идентификаторы токенов].\nМногие токены имеют пробел впереди. Используйте счетчик токенов, если не уверены.",
|
||||
"Example: some text [42, 69, 1337]": "Пример:\nкакой-то текст\n[42, 69, 1337]",
|
||||
@@ -59,11 +60,13 @@
|
||||
"Add BOS Token": "Добавлять BOS-токен",
|
||||
"Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Добавлять BOS-токен в начале промпта. Если выключить, ответы могут стать более креативными.",
|
||||
"Ban EOS Token": "Запретить EOS-токен",
|
||||
"Ban the eos_token. This forces the model to never end the generation prematurely": "Запрет EOS-токена не позволит модели завершить генерацию самостоятельно (только при достижении лимита токенов)",
|
||||
"Ban the eos_token. This forces the model to never end the generation prematurely": "Запрет EOS-токена не позволит модели завершить генерацию преждевременно",
|
||||
"Skip Special Tokens": "Пропускать спец. токены",
|
||||
"Beam search": "Beam Search",
|
||||
"Beam search": "Поиск Beam",
|
||||
"Number of Beams": "Количество Beam",
|
||||
"Length Penalty": "Штраф за длину",
|
||||
"Early Stopping": "Прекращать сразу",
|
||||
"Early Stopping": "Преждевременная остановка",
|
||||
"Contrastive search": "Контрастный поиск",
|
||||
"Penalty Alpha": "Penalty Alpha",
|
||||
"Seed": "Зерно",
|
||||
"Epsilon Cutoff": "Epsilon Cutoff",
|
||||
@@ -86,7 +89,7 @@
|
||||
"Text Completion presets": "Пресеты для Text Completion",
|
||||
"Documentation on sampling parameters": "Документация по параметрам сэмплеров",
|
||||
"Set all samplers to their neutral/disabled state.": "Установить все сэмплеры в нейтральное/отключенное состояние.",
|
||||
"Only enable this if your model supports context sizes greater than 8192 tokens": "Включайте эту опцию, только если ваша модель поддерживает размер контекста более 8192 токенов.\nУвеличивайте только если вы понимаете, что делаете.",
|
||||
"Only enable this if your model supports context sizes greater than 8192 tokens": "Включайте эту опцию, только если ваша модель поддерживает размер контекста более 8192 токенов.\nУвеличивайте только если вы знаете, что делаете.",
|
||||
"Wrap in Quotes": "Заключать в кавычки",
|
||||
"Wrap entire user message in quotes before sending.": "Перед отправкой заключать всё сообщение пользователя в кавычки.",
|
||||
"Leave off if you use quotes manually for speech.": "Оставьте выключенным, если вручную выставляете кавычки для прямой речи.",
|
||||
@@ -106,13 +109,13 @@
|
||||
"Adjust response length to worker capabilities": "Подстраивать длину ответа под возможности рабочих машин",
|
||||
"API key": "API-ключ",
|
||||
"Tabby API key": "Tabby API-ключ",
|
||||
"Get it here:": "Получите здесь:",
|
||||
"Get it here:": "Получить здесь:",
|
||||
"Register": "Зарегистрироваться",
|
||||
"TogetherAI Model": "Модель TogetherAI",
|
||||
"Example: http://127.0.0.1:5001": "Пример: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Пример: http://127.0.0.1:5001",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (сервер вывода)",
|
||||
"Example: http://127.0.0.1:8080": "Пример: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Пример: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Пример: http://127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Пример: http://127.0.0.1:11434",
|
||||
"Ollama Model": "Модель Ollama",
|
||||
"Download": "Скачать",
|
||||
"TogetherAI API Key": "TogetherAI API-ключ",
|
||||
@@ -133,7 +136,7 @@
|
||||
"Server url": "URL-адрес сервера",
|
||||
"Custom model (optional)": "Пользовательская модель (необязательно)",
|
||||
"Bypass API status check": "Обход проверки статуса API",
|
||||
"Example: http://127.0.0.1:5000": "Пример: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Пример: http://127.0.0.1:5000",
|
||||
"Bypass status check": "Обход проверки статуса",
|
||||
"Mancer API key": "Ключ от Mancer API",
|
||||
"to get your OpenAI API key.": "для получения ключа от API OpenAI",
|
||||
@@ -158,7 +161,7 @@
|
||||
"View hidden API keys": "Посмотреть скрытые API-ключи",
|
||||
"Advanced Formatting": "Расширенное форматирование",
|
||||
"Context Template": "Шаблон контекста",
|
||||
"Replace Macro in Stop Strings": "Заменять макросы в пользовательских стоп-строках",
|
||||
"Replace Macro in Custom Stopping Strings": "Заменять макросы в пользовательских стоп-строках",
|
||||
"Story String": "Строка истории",
|
||||
"Example Separator": "Разделитель примеров сообщений",
|
||||
"Chat Start": "Начало чата",
|
||||
@@ -192,12 +195,12 @@
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"Context %": "Процент контекста",
|
||||
"Budget Cap": "Лимит бюджета",
|
||||
"Budget Cap": "Бюджетный лимит",
|
||||
"(0 = disabled)": "(0 = отключено)",
|
||||
"None": "Отсутствует",
|
||||
"User Settings": "Настройки пользователя",
|
||||
"UI Language": "Язык интерфейса",
|
||||
"Avatar Style:": "Аватарки",
|
||||
"Avatar Style": "Аватарки",
|
||||
"Circle": "Круглые",
|
||||
"Rectangle": "Прямоугольные",
|
||||
"Square": "Квадратные",
|
||||
@@ -286,10 +289,10 @@
|
||||
"Author's Note": "Заметки автора",
|
||||
"Replace empty message": "Заменять пустые сообщения",
|
||||
"Send this text instead of nothing when the text box is empty.": "Этот текст будет отправлен в случае отсутствия текста на отправку.",
|
||||
"Unrestricted maximum value for the context slider": "Убрать потолок для ползунка контекста. Включайте только если точно понимаете, что делаете",
|
||||
"Unrestricted maximum value for the context slider": "Убрать потолок для ползунка контекста. Включайте только если точно знаете, что делаете",
|
||||
"Chat Completion Source": "Источник для Chat Completion",
|
||||
"Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde.",
|
||||
"Review the Privacy statement": "Ознакомьтесь с заявлением о конфиденциальности",
|
||||
"Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde",
|
||||
"Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности",
|
||||
"Trusted workers only": "Только доверенные рабочие машины",
|
||||
"For privacy reasons, your API key will be hidden after you reload the page.": "Из соображений безопасности ваш API-ключ будет скрыт после перезагрузки страницы.",
|
||||
"-- Horde models not loaded --": "--Модель Horde не загружена--",
|
||||
@@ -423,7 +426,7 @@
|
||||
"Requests logprobs from the API for the Token Probabilities feature": "Запросить логпробы из API для функции Token Probabilities.",
|
||||
"Automatically reject and re-generate AI message based on configurable criteria": "Автоматическое отклонение и повторная генерация сообщений AI на основе настраиваемых критериев.",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Включить авто-свайп. Настройки в этом разделе действуют только при включенном авто-свайпе.",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Если сгенерированное сообщение короче этого значения, срабатывает авто-свайп.",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Если сгенерированное сообщение короче этого значения, срабатывает авто-свайп.",
|
||||
"Reload and redraw the currently open chat": "Перезагрузить и перерисовать открытый в данный момент чат.",
|
||||
"Auto-Expand Message Actions": "Развернуть действия",
|
||||
"Persona Management": "Управление персоной",
|
||||
@@ -572,10 +575,10 @@
|
||||
"Characters sorting order": "Порядок сортировки персонажей",
|
||||
"Remove": "Убрать",
|
||||
"Select a World Info file for": "Выбрать файл с миром для",
|
||||
"Primary Lorebook": "Основной лорбук",
|
||||
"A selected World Info will be bound to this character as its own Lorebook.": "Информация о мире будет привязана к персонажу как его собственный лорбук.",
|
||||
"When generating an AI reply, it will be combined with the entries from a global World Info selector.": "Когда ИИ генерирует ответ, он будет совмещён с записями из глобально выбранного мира.",
|
||||
"Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "При экспорте персонажа вместе с ним также выгрузится выбранный лорбук в виде JSON.",
|
||||
"Primary Lorebook": "Основного лорбука",
|
||||
"A selected World Info will be bound to this character as its own Lorebook.": "Информация о мире будет привязана к персонажу как его собственный лорбук",
|
||||
"When generating an AI reply, it will be combined with the entries from a global World Info selector.": "Когда ИИ генерирует ответ, он будет совмещён с записями из глобально выбранного мира",
|
||||
"Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "При экспорте персонажа вместе с ним также выгрузится выбранный лорбук в виде JSON",
|
||||
"Additional Lorebooks": "Вспомогательные лорбуки",
|
||||
"Associate one or more auxillary Lorebooks with this character.": "Привязать к этому персонажу один или больше вспомогательных лорбуков",
|
||||
"NOTE: These choices are optional and won't be preserved on character export!": "ВНИМАНИЕ: эти выборы необязательные и не будут сохранены при экспорте персонажа!",
|
||||
@@ -590,7 +593,7 @@
|
||||
"Prompt": "Промпт",
|
||||
"Copy": "Скопировать",
|
||||
"Confirm": "Подтвердить",
|
||||
"Copy this message": "Продублировать сообщение",
|
||||
"Copy this message": "Скопировать сообщение",
|
||||
"Delete this message": "Удалить сообщение",
|
||||
"Move message up": "Переместить сообщение вверх",
|
||||
"Move message down": "Переместить сообщение вниз",
|
||||
@@ -609,7 +612,7 @@
|
||||
"Ask AI to write your message for you": "Попросить ИИ написать сообщение за вас",
|
||||
"Continue the last message": "Продолжить текущее сообщение",
|
||||
"Bind user name to that avatar": "Закрепить имя за этим аватаром",
|
||||
"Select this as default persona for the new chats.": "Выбирать эту персону по умолчанию для всех новых чатов.",
|
||||
"Select this as default persona for the new chats.": "Выберать эту Персону в качестве персоны по умолчанию для новых чатов.",
|
||||
"Change persona image": "Сменить аватар персоны",
|
||||
"Delete persona": "Удалить персону",
|
||||
"Reduced Motion": "Сокращение анимаций",
|
||||
@@ -637,7 +640,7 @@
|
||||
"Token Probabilities": "Вероятности токенов",
|
||||
"Close chat": "Закрыть чат",
|
||||
"Manage chat files": "Все чаты",
|
||||
"Import Extension From Git Repo": "Импортировать расширение из Git-репозитория.",
|
||||
"Import Extension From Git Repo": "Импортировать расширение из Git Repository",
|
||||
"Install extension": "Установить расширение",
|
||||
"Manage extensions": "Управление расширениями",
|
||||
"Tokens persona description": "Токенов",
|
||||
@@ -696,7 +699,7 @@
|
||||
"Aggressive": "Агрессивный",
|
||||
"Very aggressive": "Очень агрессивный",
|
||||
"Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling. В единицах 1e-4; разумное значение - 3. Установите в 0, чтобы отключить. См. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.",
|
||||
"Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde",
|
||||
"Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как внести свой вклад в свои свободные GPU-циклы в орду",
|
||||
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.",
|
||||
"Load koboldcpp order": "Загрузить порядок из koboldcpp",
|
||||
"Use Google Tokenizer": "Использовать токенизатор Google",
|
||||
@@ -741,7 +744,7 @@
|
||||
"Last Assistant Prefix": "Последний префикс ассистента",
|
||||
"System Instruction Prefix": "Префикс системной инструкции",
|
||||
"User Filler Message": "Принудительное сообщение пользователя",
|
||||
"Permanent": "постоянных",
|
||||
"Permanent": "перманентных",
|
||||
"Alt. Greetings": "Др. варианты",
|
||||
"Smooth Streaming": "Плавный стриминг",
|
||||
"Save checkpoint": "Сохранить чекпоинт",
|
||||
@@ -992,7 +995,7 @@
|
||||
"Set your custom avatar.": "Установить аватарку",
|
||||
"Remove your custom avatar.": "Сбросить аватарку",
|
||||
"Make a Snapshot": "Сделать снимок",
|
||||
"never_resize_avatars_tooltip": "Не менять размер картинок у импортируемых персонажей. При отключении все картинки будут приводиться к размеру 512х768",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Не менять размер картинок у импортируемых персонажей. При отключении все картинки будут приводиться к размеру 512х768",
|
||||
"Char List Subheader": "Доп. заголовок в списке персонажей",
|
||||
"# Messages to Load": "Сколько сообщений загружать",
|
||||
"(0 = All)": "(0 = все)",
|
||||
@@ -1119,11 +1122,12 @@
|
||||
"help_hotkeys_0": "Горячие клавиши",
|
||||
"You can browse a list of bundled characters in the": "Комплектных персонажей можно найти в меню",
|
||||
"Download Extensions & Assets": "Загрузить расширения и ресурсы",
|
||||
"menu within": "в меню",
|
||||
"menu within": "внутри этих кубиков",
|
||||
"Assets URL": "URL с описанием ресурсов",
|
||||
"Custom (OpenAI-compatible)": "Кастомный (совместимый с OpenAI)",
|
||||
"Custom Endpoint (Base URL)": "Кастомный эндпоинт (базовый URL)",
|
||||
"Example: http://localhost:1234/v1": "Пример: http://localhost:1234/v1",
|
||||
"at the end of the URL!": "!",
|
||||
"Custom API Key": "Ключ от кастомного API",
|
||||
"(Optional)": "(необязательно)",
|
||||
"Enter a Model ID": "Введите идентификатор модели",
|
||||
@@ -1224,6 +1228,7 @@
|
||||
"JSON-serialized array of strings.": "Список строк в формате JSON.",
|
||||
"Mirostat_desc": "Mirostat - своего рода термометр, измеряющий перплексию для выводимого текста.\nMirostat подгоняет перплексию генерируемого текста к перплексии входного текста, что позволяет избежать повторов.\n(когда по мере генерации текста авторегрессионным инференсом, перплексия всё больше приближается к нулю)\n а также ловушки перплексии (когда перплексия начинает уходить в сторону)\nБолее подробное описание в статье Mirostat: A Neural Text Decoding Algorithm that Directly Controls Perplexity by Basu et al. (2020).\nРежим выбирает версию Mirostat. 0=отключить, 1=Mirostat 1.0 (только llama.cpp), 2=Mirostat 2.0.",
|
||||
"Helpful tip coming soon.": "Подсказку скоро добавим.",
|
||||
"Temperature_Last_desc": "Использовать Temperature сэмплер в последнюю очередь. Это почти всегда разумно.\nПри включении: сначала выборка набора правдоподобных токенов, затем применение Temperature для корректировки их относительных вероятностей (технически, логитов).\nПри отключении: сначала применение Temperature для корректировки относительных вероятностей ВСЕХ токенов, затем выборка правдоподобных токенов из этого.\nОтключение Temperature Last увеличивает вероятности в хвосте распределения, что увеличивает шансы получить несогласованный ответ.",
|
||||
"Speculative Ngram": "Speculative Ngram",
|
||||
"Use a different speculative decoding method without a draft model": "Use a different speculative decoding method without a draft model.\rUsing a draft model is preferred. Speculative ngram is not as effective.",
|
||||
"Spaces Between Special Tokens": "Spaces Between Special Tokens",
|
||||
@@ -1252,6 +1257,7 @@
|
||||
"DreamGen Model": "Модель DreamGen",
|
||||
"vllm-project/vllm": "vllm-project/vllm (режим враппера OpenAI API)",
|
||||
"vLLM API key": "Ключ от API vLLM",
|
||||
"Example: 127.0.0.1:8000": "Example: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Модель vLLM",
|
||||
"Aphrodite Model": "Модель Aphrodite",
|
||||
"Peek a password": "Посмотреть пароль",
|
||||
@@ -1616,7 +1622,7 @@
|
||||
"Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog.": "Выберите, какие действия следует предпринять по отношению к тегам импортируемой карточки. При выборе опции \"Спрашивать\" вы будете решать это индивидуально для каждой карточки.",
|
||||
"Ask": "Спрашивать",
|
||||
"tag_import_all": "Все",
|
||||
"tag_import_existing": "Только существующие",
|
||||
"Existing": "Только существующие",
|
||||
"tag_import_none": "Не импортировать",
|
||||
"Using a proxy that you're not running yourself is a risk to your data privacy.": "Помните, что используя чужую прокси, вы подвергаете риску конфиденциальность своих данных.",
|
||||
"ANY support requests will be REFUSED if you are using a proxy.": "НЕ РАССЧИТЫВАЙТЕ на нашу поддержку, если используете прокси.",
|
||||
@@ -1729,7 +1735,7 @@
|
||||
"markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",
|
||||
"Save and Update": "Сохранить и обновить",
|
||||
"Profile name:": "Название профиля:",
|
||||
"API returned an error": "API ответило ошибкой",
|
||||
"API returned an error": "API вернуло ошибку",
|
||||
"Failed to save preset": "Не удалось сохранить пресет",
|
||||
"Preset name should be unique.": "Название пресета должно быть уникальным.",
|
||||
"Invalid file": "Невалидный файл",
|
||||
@@ -1751,7 +1757,8 @@
|
||||
"dot quota_error": "имеется достаточно кредитов.",
|
||||
"If you have sufficient credits, please try again later.": "Если кредитов достаточно, то повторите попытку позднее.",
|
||||
"Proxy preset '${0}' not found": "Пресет '${0}' не найден",
|
||||
"Window.ai returned an error": "Window.ai ответил ошибкой",
|
||||
"Window.ai returned an error": "Window.ai вернул ошибку",
|
||||
"Get it here:": "Загрузите здесь:",
|
||||
"Extension is not installed": "Расширение не установлено",
|
||||
"Update or remove your reverse proxy settings.": "Измените или удалите ваши настройки прокси.",
|
||||
"An error occurred while importing prompts. More info available in console.": "В процессе импорта произошла ошибка. Подробную информацию см. в консоли.",
|
||||
@@ -1860,7 +1867,7 @@
|
||||
"Group Chat could not be saved": "Не удалось сохранить групповой чат",
|
||||
"Deleted group member swiped. To get a reply, add them back to the group.": "Вы пытаетесь свайпнуть удалённого члена группы. Чтобы получить ответ, добавьте этого персонажа обратно в группу.",
|
||||
"Currently no group selected.": "В данный момент не выбрано ни одной группы.",
|
||||
"Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонажи закончат печатать.",
|
||||
"Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонаж закончит печатать.",
|
||||
"Delete the group?": "Удалить группу?",
|
||||
"This will also delete all your chats with that group. If you want to delete a single conversation, select a \"View past chats\" option in the lower left menu.": "Вместе с ней будут удалены и все её чаты. Если требуется удалить только один чат, воспользуйтесь кнопкой \"Все чаты\" в меню в левом нижнем углу.",
|
||||
"Can't peek a character while group reply is being generated": "Невозможно открыть карточку персонажа во время генерации ответа",
|
||||
@@ -1936,7 +1943,7 @@
|
||||
"and connect to an": "и подключитесь к",
|
||||
"You can add more": "Можете добавить больше",
|
||||
"from other websites": "с других сайтов.",
|
||||
"Go to the": "Заходите в",
|
||||
"Go to the": "Загляните в",
|
||||
"to install additional features.": ", чтобы установить разные дополнительные ресурсы.",
|
||||
"or_welcome": "; также доступен",
|
||||
"Claude API Key": "Ключ от API Claude",
|
||||
@@ -1951,7 +1958,7 @@
|
||||
"Save": "Сохранить",
|
||||
"Chat Lorebook": "Лорбук для чата",
|
||||
"chat_world_template_txt": "Выбранный мир будет привязан к этому чату. Будет добавляться в промпт наряду с глобальным лорбуком и лором персонажа.",
|
||||
"world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + ЛКМ, чтобы открыть диалог привязки мира",
|
||||
"world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + клик, чтобы открыть диалог привязки мира",
|
||||
"No auxillary Lorebooks set. Click here to select.": "Вспомогательный лорбук не выбран. Нажмите, чтобы выбрать.",
|
||||
"ext_regex_user_input_desc": "Отправленные вами сообщения.",
|
||||
"ext_regex_ai_input_desc": "Полученные от API ответы.",
|
||||
@@ -1991,7 +1998,7 @@
|
||||
"Default persona deleted": "Удалена персона по умолчанию",
|
||||
"The locked persona was deleted. You will need to set a new persona for this chat.": "Удалена привязанная к чату персона. Вам будет необходимо выбрать новую фиксированную персону для этого чата.",
|
||||
"Persona deleted": "Персона удалена",
|
||||
"You must bind a name to this persona before you can set it as the default.": "Прежде чем установить эту персону в качестве персоны по умолчанию, ей необходимо присвоить имя.",
|
||||
"You must bind a name to this persona before you can set it as the default.": "Прежде чем установить эту персону в качестве персоны по умолчанию, ей необходимо задать имя.",
|
||||
"Persona name not set": "У персоны отсутствует имя",
|
||||
"Are you sure you want to remove the default persona?": "Вы точно хотите снять статус персоны по умолчанию?",
|
||||
"This persona will no longer be used by default when you open a new chat.": "Эта персона больше не будет автоматически выбираться при старте нового чата",
|
||||
@@ -2032,7 +2039,7 @@
|
||||
"[Currently loaded]": "[Загруженная сейчас]",
|
||||
"Search providers...": "Искать по провайдерам...",
|
||||
"Automatically chooses an alternative provider if chosen providers can't serve your request.": "Автоматически переключаться на другого провайдера, если текущий не может обслужить запрос.",
|
||||
"Example: http://127.0.0.1:8000": "Пример: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Пример: 127.0.0.1:8000",
|
||||
"Edit a connection profile": "Редактировать профиль соединения",
|
||||
"System Prompt Name": "Название системного промпта",
|
||||
"Use System Prompt": "Использовать системный промпт",
|
||||
@@ -2137,206 +2144,5 @@
|
||||
"Not connected to the API!": "Нет соединения с API!",
|
||||
"ext_type_system": "Это комплектное расширение. Его нельзя удалить, а обновляется оно вместе со всей системой.",
|
||||
"Update all": "Обновить все",
|
||||
"Close": "Закрыть",
|
||||
"Optional modules:": "Необязательные модули:",
|
||||
"Sort: Display Name": "Сортировать: по названию",
|
||||
"Sort: Loading Order": "Сортировать: в порядке загрузки",
|
||||
"Click to toggle": "Нажмите, чтобы включить или выключить",
|
||||
"Loading Asset List": "Загрузить список ресурсов",
|
||||
"Don't ask again for this URL": "Запомнить выбор для этого адреса",
|
||||
"Are you sure you want to connect to the following url?": "Вы точно хотите подключиться к этому адресу?",
|
||||
"All": "Всё",
|
||||
"Characters": "Персонажи",
|
||||
"Ambient sounds": "Звуковой эмбиент",
|
||||
"Blip sounds": "Звуки уведомлений",
|
||||
"Background music": "Фоновая музыка",
|
||||
"Search": "Поиск",
|
||||
"extension_install_1": "Чтобы загружать расширения из этого списка, у вас должен быть установлен ",
|
||||
"extension_install_2": ".",
|
||||
"extension_install_3": "Нажмите на иконку ",
|
||||
"extension_install_4": ", чтобы перейти в репозиторий расширения и получить более подробную информацию о нём.",
|
||||
"Extension repo/guide:": "Репозиторий расширения:",
|
||||
"Preview in browser": "Предпросмотр",
|
||||
"Adds a function tool": "Частично или полностью работает через вызов функций",
|
||||
"Tool": "Функции",
|
||||
"Move extension": "Переместить расширение",
|
||||
"ext_type_local": "Это локальное расширение, доступно только вам",
|
||||
"ext_type_global": "Это глобальное расширение, доступно всем пользователям",
|
||||
"Move": "Переместить",
|
||||
"Enter the Git URL of the extension to install": "Введите Git-адрес расширения",
|
||||
"Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.": "помните, что используя расширения от сторонних авторов, вы можете подвергать систему опасности. Устанавливайте расширения только от проверенных разработчиков. Мы не несём ответственности за любой ущерб, причинённый сторонними расширениями.",
|
||||
"Disclaimer:": "Внимание:",
|
||||
"Example:": "Пример:",
|
||||
"context_derived": "Считывать из метаданных модели (по возможности)",
|
||||
"instruct_derived": "Считывать из метаданных модели (по возможности)",
|
||||
"Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте",
|
||||
"Reasoning Effort": "Рассуждения",
|
||||
"Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей которые поддерживают эту возможность.\nНа данный момент поддерживаются три значения: Подробные, Обычные, Поверхностные.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.",
|
||||
"openai_reasoning_effort_low": "Поверхностные",
|
||||
"openai_reasoning_effort_medium": "Обычные",
|
||||
"openai_reasoning_effort_high": "Подробные",
|
||||
"Persona Lore Alt+Click to open the lorebook": "Лорбук данной персоны\nAlt + ЛКМ чтобы открыть лорбук",
|
||||
"Persona Lorebook for": "Лорбук для персоны",
|
||||
"persona_world_template_txt": "Выбранная Информация о мире будет привязана к этой персоне. Информация будет добавляться в каждом промпте вместе с глобальным лорбуком и лорбуками персонажа и чата.",
|
||||
"Global list": "Глобальный список",
|
||||
"Preset-specific list": "Список для данного пресета",
|
||||
"Banned tokens/strings are being sent in the request.": "Запрещённые токены и строки отсылаются в запросе.",
|
||||
"Banned tokens/strings are NOT being sent in the request.": "Запрещённые токены и строки НЕ отсылаются в запросе.",
|
||||
"Add a reasoning block": "Добавить блок рассуждений",
|
||||
"Create a copy of this message?": "Продублировать это сообщение?",
|
||||
"Max Recursion Steps": "Макс. глубина рекурсии",
|
||||
"0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc": "0 = неограничено, 1 = сканировать единожды, 2 = сканировать единожды и сделать один повторный проход, и т.д.\n(неактивно при указанном мин. числе активаций)",
|
||||
"(disabled when max recursion steps are used)": "(неактивно при указанной макс. глубине рекурсии)",
|
||||
"Enter a valid API URL": "Введите корректный адрес API",
|
||||
"No Ollama model selected.": "Не выбрана модель Ollama",
|
||||
"Background Fitting": "Способ подгонки фона под разрешение",
|
||||
"Chat Lore Alt+Click to open the lorebook": "Лорбук данного чата\nAlt + ЛКМ чтобы открыть лорбук",
|
||||
"Token Counter": "Подсчитать токены",
|
||||
"Type / paste in the box below to see the number of tokens in the text.": "Введите или вставьте текст в окошко ниже, чтобы подсчитать количество токенов в нём.",
|
||||
"Selected tokenizer:": "Выбранный токенайзер:",
|
||||
"Input:": "Входные данные:",
|
||||
"Tokenized text:": "Токенизированный текст:",
|
||||
"Token IDs:": "Идентификаторы токенов:",
|
||||
"Tokens:": "Токенов:",
|
||||
"Max prompt cost:": "Макс. стоимость промпта:",
|
||||
"Reset custom sampler selection": "Сбросить подборку семплеров",
|
||||
"Here you can toggle the display of individual samplers. (WIP)": "Здесь можно включить или выключить отображение каждого из сэмплеров отдельно. (WIP)",
|
||||
"Request Model Reasoning": "Запрашивать цепочку рассуждений",
|
||||
"Reasoning": "Рассуждения / Reasoning",
|
||||
"Auto-Parse": "Авто-парсинг",
|
||||
"reasoning_auto_parse": "Автоматически считывать блоки рассуждений, расположенные между префиксом и суффиксом рассуждений. Для работы должно быть указано и то, и другое.",
|
||||
"Auto-Expand": "Разворачивать",
|
||||
"reasoning_auto_expand": "Автоматически разворачивать блоки рассуждений.",
|
||||
"Show Hidden": "Показывать время",
|
||||
"reasoning_show_hidden": "Отображать затраченное на рассуждения время для моделей со скрытой цепочкой рассуждений",
|
||||
"Add to Prompts": "Добавлять в промпт",
|
||||
"reasoning_add_to_prompts": "Добавлять существующие блоки рассуждений в промпт. Для добавления новых используйте меню редактирования сообщений.",
|
||||
"reasoning_max_additions": "Макс. кол-во блоков рассуждений в промпте, считается от последнего сообщения",
|
||||
"Max": "Макс.",
|
||||
"Reasoning Formatting": "Форматирование рассуждений",
|
||||
"Prefix": "Префикс",
|
||||
"Suffix": "Постфикс",
|
||||
"Separator": "Разделитель",
|
||||
"reasoning_separator": "Вставляется между рассуждениями и содержанием самого сообщения.",
|
||||
"reasoning_prefix": "Вставляется перед рассуждениями.",
|
||||
"reasoning_suffix": "Вставляется после рассуждений.",
|
||||
"Seed_desc": "Фиксированное значение зерна позволяет получать предсказуемые, одинаковые результаты на одинаковых настройках. Поставьте -1 для рандомного зерна.",
|
||||
"# of Beams": "Кол-во лучей",
|
||||
"The number of sequences generated at each step with Beam Search.": "Кол-во вариантов, генерируемых Beam Search на каждом шаге работы.",
|
||||
"Penalize sequences based on their length.": "Штрафует строки в зависимости от длины",
|
||||
"Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates.": "Определяет, когда останавливать работу Beam Search. Поставив галочку, вы укажете поиску остановиться тогда, когда будет достигнуто кол-во лучей из соответствующего поля. Если галочку не отмечать, то генерация остановится тогда, когда сочтёт, что дальше найти лучших кандидатов слишком маловероятно.",
|
||||
"A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "Жадный алгоритм LLM-сэмплинга, подбирающий наиболее вероятную последовательность слов или токенов путём исследования и расширения сразу нескольких вариантов. На каждом шаге он удерживает фиксированное кол-во самых подходящих вариантов (ширина луча).",
|
||||
"Smooth_Sampling_desc": "Изменяет распределение с помощью квадратичных и кубических преобразований. Снижение Коэффициента сглаживания даёт более креативные ответы, обычно идеальное значение находится в диапазоне 0.2-0.3 (при кривой сглаживания=1.0). Повышение значения Кривой сглаживания сделает кривую круче, что приведёт к более агрессивной фильтрации маловероятных вариантов. Установив Кривую сглаживания = 1.0, вы фактически нейтрализуете этот параметр и будете работать только с Коэффициентом",
|
||||
"Temperature_Last_desc": "Применять сэмплер Температуры в последнюю очередь. Почти всегда оправдано.\nПри включении: сначала все токены семплируются, и затем температура регулирует распределение у оставшихся (технически, у оставшихся логитов).\nПри выключении: сначала температура настраивает распределение ВСЕХ токенов, и потом они семплируются уже с этим обновлённым распределением.\nПри отключении этой опции токены в хвосте получают больше шансов попасть в итоговую последовательность, что может привести к менее связным и логичным ответам.",
|
||||
"Swipe # for All Messages": "Номер свайпа на всех сообщениях",
|
||||
"Display swipe numbers for all messages, not just the last.": "Отображать номер свайпа для всех сообщений, а не только для последнего.",
|
||||
"Penalty Range": "Окно для штрафа",
|
||||
"Never": "Никогда",
|
||||
"Groups and Past Personas": "Для групп и прошлых персон",
|
||||
"Always": "Всегда",
|
||||
"Request model reasoning": "Запрашивать рассуждения",
|
||||
"Allows the model to return its thinking process.": "Позволяет модели высылать в ответе свою цепочку рассуждений.",
|
||||
"Rename Persona": "Переименовать персону",
|
||||
"Change Persona Image": "Изменить изображение персоны",
|
||||
"Duplicate Persona": "Клонировать персону",
|
||||
"Delete Persona": "Удалить персону",
|
||||
"Enter a new name for this persona:": "Введите новое имя персоны:",
|
||||
"Connections": "Связи",
|
||||
"Click to select this as default persona for the new chats. Click again to remove it.": "Нажмите, чтобы установить эту персону стандартной для всех новых чатов. Нажмите ещё раз, чтобы отключить.",
|
||||
"Character": "Персонаж",
|
||||
"Click to lock your selected persona to the current character. Click again to remove the lock.": "Нажмите, чтобы закрепить эту персону для текущего персонажа. Нажмите ещё раз, чтобы открепить.",
|
||||
"Chat": "Чат",
|
||||
"[No character connections. Click one of the buttons above to connect this persona.]": "[Связи отсутствуют. Нажмите на одну из кнопок выше, чтобы создать.]",
|
||||
"Global Settings": "Общие настройки",
|
||||
"Allow multiple persona connections per character": "Разрешить привязывать несколько персон к одному персонажу",
|
||||
"When multiple personas are connected to a character, a popup will appear to select which one to use": "При связывании нескольких персон с персонажем, будет появляться окошко с предложением выбрать нужную.",
|
||||
"Auto-lock a chosen persona to the chat": "Автоматически привязывать выбранную персону к чату",
|
||||
"Whenever a persona is selected, it will be locked to the current chat and automatically selected when the chat is opened.": "При выборе новой персоны она автоматически будет привязана к текущему чату, и будет выбираться при его открытии.",
|
||||
"Current Persona": "Текущая персона",
|
||||
"The chat has been successfully converted!": "Чат успешно преобразован!",
|
||||
"Manual": "Когда вы скажете",
|
||||
"Auto Mode delay": "Задержка авто-режима",
|
||||
"Use tag as folder": "Тег-папка",
|
||||
"All connections to ${0} have been removed.": "Все связи с персонажем ${0} были удалены.",
|
||||
"Personas Unlocked": "Персоны отвязаны",
|
||||
"Remove All Connections": "Удалить все связи",
|
||||
"Persona ${0} selected and auto-locked to current chat": "Персона ${0} выбрана и автоматически закреплена за этим чатом",
|
||||
"This persona is only temporarily chosen. Click for more info.": "Данная персона выбрана лишь временно. Нажмите, чтобы узнать больше.",
|
||||
"Temporary Persona": "Временная персона",
|
||||
"A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.": "К этому чату уже привязана иная персона, либо у вас выбрана иная персона по-умолчанию. Выбранная в данный момент персона будет временной, и сбросится после перезагрузки. Если хотите всегда использовать её в этом чате, советуем её прикрепить.",
|
||||
"Current Persona: ${0}": "Выбранная персона: ${0}",
|
||||
"Chat persona: ${0}": "Персона для этого чата: ${0}",
|
||||
"Default persona: ${0}": "Персона по умолчанию (стандартная): ${0}",
|
||||
"Persona ${0} is now unlocked from this chat.": "Персона ${0} отвязана от этого чата.",
|
||||
"Persona Unlocked": "Персона отвязана",
|
||||
"Persona ${0} is now unlocked from character ${1}.": "Персона ${0} отвязана от персонажа ${1}.",
|
||||
"Persona Not Found": "Персона не найдена",
|
||||
"Persona Locked": "Персона закреплена",
|
||||
"User persona ${0} is locked to character ${1}${2}": "Персона ${0} прикреплена к персонажу ${1}${2}",
|
||||
"Persona Name Not Set": "У персоны отсутствует имя",
|
||||
"You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.",
|
||||
"Default Persona Removed": "Персона по умолчанию снята",
|
||||
"Persona is locked to the current character": "Персона закреплена за этим персонажем",
|
||||
"Persona is locked to the current chat": "Персона закреплена за этим чатом",
|
||||
"characters": "перс.",
|
||||
"character": "персонаж",
|
||||
"in this group": "в группе",
|
||||
"Chatting Since": "Первая беседа",
|
||||
"Context": "Контекст",
|
||||
"Response": "Ответ",
|
||||
"Connected": "Подключено",
|
||||
"Enter new background name:": "Введите новое название для фона:",
|
||||
"AI Horde Website": "Сайт AI Horde",
|
||||
"Enable web search": "Включить поиск в Интернете",
|
||||
"Use search capabilities provided by the backend.": "Разрешить использование предоставляемых бэкендом функций поиска.",
|
||||
"Request inline images": "Запрашивать inline-изображения",
|
||||
"Allows the model to return image attachments.": "Разрешить модели отправлять вложения в виде картинок.",
|
||||
"Request inline images_desc_2": "Не совместимо со следующим функционалом: вызов функций, поиск в Интернете, системный промпт.",
|
||||
"Connected Personas": "Связанные персоны",
|
||||
"[Currently no personas connected]": "[Связанных персон нет]",
|
||||
"The following personas are connected to the current character.\n\nClick on a persona to select it for the current character.\nShift + Click to unlink the persona from the character.": "С этим персонажем связаны следующие персоны.\n\nНажмите на персону, чтобы выбрать её для данного персонажа.\nShift + ЛКМ, чтобы её отвязать.",
|
||||
"Persona Connections": "Связи с персонами",
|
||||
"Pooled order": "Если уже давно не отвечали",
|
||||
"Attach a File": "Приложить файл",
|
||||
"Attach a file or image to a current chat.": "Приложить файл или изображение к текущему чату",
|
||||
"Remove the file": "Удалить файл",
|
||||
"Delete the Chat File?": "Удалить чат?",
|
||||
"Forbidden": "Доступ запрещён",
|
||||
"To view your API keys here, set the value of allowKeysExposure to true in config.yaml file and restart the SillyTavern server.": "Чтобы видеть здесь ваши API-ключи, установите параметр allowKeysExposure в config.yaml в положение true, после чего перезапустите сервер SillyTavern.",
|
||||
"Invalid endpoint URL. Requests may fail.": "Некорректный адрес эндпоинта. Запросы могут не проходить.",
|
||||
"How to install extensions?": "Как устанавливать расширения?",
|
||||
"Click the flashing button to install extensions.": "Чтобы их установить, нажмите на мигающую кнопку.",
|
||||
"ext_regex_reasoning_desc": "Содержимое блоков рассуждений. При отмеченной галочке \"Только промпт\" будут также обработаны добавленные в промпт рассуждения.",
|
||||
"Macro in Find Regex": "Макросы в рег. выражении",
|
||||
"Don't substitute": "Не заменять",
|
||||
"Substitute (raw)": "Заменять в \"чистом\" виде",
|
||||
"Substitute (escaped)": "Заменять после экранирования",
|
||||
"ext_regex_other_options_desc": "По умолчанию, расширение вносит изменения в сам файл чата.\nПри включении одной из опций (или обеих), файл чата останется нетронутым, при этом сами изменения по-прежнему будут действовать.",
|
||||
"ext_regex_flags_help": "Нажмите, чтобы узнать больше о флагах в рег. выражениях.",
|
||||
"Applies to all matches": "Заменяет все вхождения",
|
||||
"Applies to the first match": "Заменяет первое вхождение",
|
||||
"Case insensitive": "Не чувствительно к регистру",
|
||||
"Case sensitive": "Чувствительно к регистру",
|
||||
"Find Regex is empty": "Рег. выражение не указано",
|
||||
"Click the button to save it as a file.": "Нажмите на кнопку справа, чтобы сохранить его в файл.",
|
||||
"Export as JSONL": "Экспорт в формате JSONL",
|
||||
"Thought for some time": "Какое-то время заняли размышления",
|
||||
"Thinking...": "В раздумьях...",
|
||||
"Thought for ${0}": "Размышления заняли ${0}",
|
||||
"Hidden reasoning - Add reasoning block": "Рассуждения скрыты - Добавить блок рассуждений",
|
||||
"Add reasoning block": "Добавить блок рассуждений",
|
||||
"Edit reasoning": "Редактировать рассуждения",
|
||||
"Copy reasoning": "Скопировать рассуждения",
|
||||
"Confirm Edit": "Подтвердить",
|
||||
"Remove reasoning": "Удалить рассуждения",
|
||||
"Cancel edit": "Отменить редактирование",
|
||||
"Remove Reasoning": "Удалить рассуждения",
|
||||
"Are you sure you want to clear the reasoning?<br />Visible message contents will stay intact.": "Вы точно хотите удалить блок рассуждений?<br />Основное сообщение останется на месте.",
|
||||
"Reasoning Parse": "Парсинг рассуждений",
|
||||
"Both prefix and suffix must be set in the Reasoning Formatting settings.": "В настройках форматирования рассуждений должны быть заданы префикс и суффикс.",
|
||||
"Invalid return type '${0}', defaulting to 'reasoning'.": "Некорректный возвращаемый тип, используем стандартный 'reasoning'.",
|
||||
"Reasoning already exists.": "Рассуждения уже присутствуют.",
|
||||
"Edit Message": "Редактирование",
|
||||
"Status check bypassed": "Проверка статуса отключена",
|
||||
"Valid": "Работает"
|
||||
"Close": "Закрыть"
|
||||
}
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "прапорцем",
|
||||
"API key (optional)": "Ключ API (необов'язково)",
|
||||
"Server url": "URL-адреса сервера",
|
||||
"Example: http://127.0.0.1:5000": "Приклад: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Приклад: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Власна модель (необов'язково)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (режим оболонки OpenAI API)",
|
||||
"vLLM API key": "Ключ API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Приклад: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Приклад: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Модель vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (режим OpenAI API)",
|
||||
"Aphrodite API key": "Ключ API для Aphrodite",
|
||||
"Aphrodite Model": "Модель Афродіта",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp (сервер виведення)",
|
||||
"Example: http://127.0.0.1:8080": "Приклад: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Приклад: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Приклад: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Приклад: 127.0.0.1:11434",
|
||||
"Ollama Model": "Модель Ollama",
|
||||
"Download": "Завантажити",
|
||||
"Tabby API key": "Ключ API для Tabby",
|
||||
"koboldcpp API key (optional)": "API-ключ koboldcpp (необов’язково)",
|
||||
"Example: http://127.0.0.1:5001": "Приклад: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Приклад: 127.0.0.1:5001",
|
||||
"Authorize": "Авторизувати",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Отримайте свій токен API OpenRouter за допомогою OAuth. Вас буде перенаправлено на openrouter.ai",
|
||||
"Bypass status check": "Обійти перевірку статусу",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "розділяйте комами без пропусків між ними",
|
||||
"Custom Stopping Strings": "Власні рядки зупинки",
|
||||
"JSON serialized array of strings": "JSON-серіалізований масив рядків",
|
||||
"Replace Macro in Stop Strings": "Замінювати макроси у власних рядках зупинки",
|
||||
"Replace Macro in Custom Stopping Strings": "Замінювати макроси у власних рядках зупинки",
|
||||
"Auto-Continue": "Автоматичне продовження",
|
||||
"Allow for Chat Completion APIs": "Дозволити для Chat Completion API",
|
||||
"Target length (tokens)": "Цільова довжина (токени)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Видалити тему",
|
||||
"Update a theme file": "Оновити файл теми",
|
||||
"Save as a new theme": "Зберегти як нову тему",
|
||||
"Avatar Style:": "Стиль аватара",
|
||||
"Avatar Style": "Стиль аватара",
|
||||
"Circle": "Коло",
|
||||
"Square": "Квадрат",
|
||||
"Rectangle": "Прямокутник",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Перевага запиту персонажа",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Якщо відмічено і картка персонажа містить заміну джейлбрейку (Інструкцію), використовуйте її замість цього",
|
||||
"Prefer Character Card Jailbreak": "Перевага джейлбрейку персонажа",
|
||||
"never_resize_avatars_tooltip": "Уникайте обрізання та зміни розміру імпортованих зображень символів. Коли вимкнено, обрізати/змінити розмір до 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Уникайте обрізання та зміни розміру імпортованих зображень символів. Коли вимкнено, обрізати/змінити розмір до 512x768.",
|
||||
"Never resize avatars": "Ніколи не змінювати розмір аватарів",
|
||||
"Show actual file names on the disk, in the characters list display only": "Показувати фактичні назви файлів на диску, тільки у відображенні списку персонажів",
|
||||
"Show avatar filenames": "Показувати імена файлів аватарів",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Автоматичний змах",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Вмикає функцію автоматичного змаху. Налаштування в цьому розділі діють лише тоді, коли увімкнено автоматичний змах",
|
||||
"Minimum generated message length": "Мінімальна довжина згенерованого повідомлення",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Якщо згенероване повідомлення коротше за це, викликайте автоматичний змаху",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Якщо згенероване повідомлення коротше за це, викликайте автоматичний змаху",
|
||||
"Blacklisted words": "Список заборонених слів",
|
||||
"words you dont want generated separated by comma ','": "слова, які ви не хочете генерувати, розділені комою ','",
|
||||
"Blacklisted word count to swipe": "Кількість заборонених слів для змаху",
|
||||
|
@@ -318,23 +318,23 @@
|
||||
"flag": "cờ",
|
||||
"API key (optional)": "Key API (tùy chọn)",
|
||||
"Server url": "URL máy chủ",
|
||||
"Example: http://127.0.0.1:5000": "Ví dụ: http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "Ví dụ: 127.0.0.1:5000",
|
||||
"Custom model (optional)": "Model tùy chỉnh (tùy chọn)",
|
||||
"vllm-project/vllm": "vllm-project/vllm (Chế độ trình bao bọc API OpenAI)",
|
||||
"vLLM API key": "Key API vLLM",
|
||||
"Example: http://127.0.0.1:8000": "Ví dụ: http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "Ví dụ: http://127.0.0.1:8000",
|
||||
"vLLM Model": "Model vLLM",
|
||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine (Chế độ đóng gói cho Giao diện lập trình ứng dụng OpenAI)",
|
||||
"Aphrodite API key": "Key API Aphrodite",
|
||||
"Aphrodite Model": "Moddel cho Aphrodite",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp",
|
||||
"Example: http://127.0.0.1:8080": "Ví dụ: http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "Ví dụ: http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "Ví dụ: 127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "Ví dụ: 127.0.0.1:11434",
|
||||
"Ollama Model": "Model Ollama",
|
||||
"Download": "Tải xuống",
|
||||
"Tabby API key": "Key API Tabby",
|
||||
"koboldcpp API key (optional)": "Key API koboldcpp (tùy chọn)",
|
||||
"Example: http://127.0.0.1:5001": "Ví dụ: http://127.0.0.1:5001",
|
||||
"Example: 127.0.0.1:5001": "Ví dụ: 127.0.0.1:5001",
|
||||
"Cho phép": "Ủy quyền",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Nhận mã thông báo API OpenRouter của bạn bằng cách sử dụng luồng OAuth. Bạn sẽ được chuyển hướng đến openrouter.ai",
|
||||
"Bypass status check": "Bỏ qua check trạng thái",
|
||||
@@ -482,7 +482,7 @@
|
||||
"separate with commas w/o space between": "phân tách bằng dấu phẩy không có khoảng trắng giữa",
|
||||
"Custom Stopping Strings": "Chuỗi dừng tùy chỉnh",
|
||||
"JSON serialized array of strings": "Mảng chuỗi được tuần tự hóa JSON",
|
||||
"Replace Macro in Stop Strings": "Thay thế Macro trong Chuỗi Dừng Tùy chỉnh",
|
||||
"Replace Macro in Custom Stopping Strings": "Thay thế Macro trong Chuỗi Dừng Tùy chỉnh",
|
||||
"Auto-Continue": "Tự động Tiếp tục",
|
||||
"Allow for Chat Completion APIs": "Cho phép các API hoàn thành Trò chuyện",
|
||||
"Target length (tokens)": "Độ dài mục tiêu (token)",
|
||||
@@ -558,7 +558,7 @@
|
||||
"Delete a theme": "Xóa một chủ đề",
|
||||
"Update a theme file": "Cập nhật một tập tin chủ đề",
|
||||
"Save as a new theme": "Lưu dưới dạng chủ đề mới",
|
||||
"Avatar Style:": "Kiểu hình đại diện",
|
||||
"Avatar Style": "Kiểu hình đại diện",
|
||||
"Circle": "Hình tròn",
|
||||
"Square": "Hình vuông",
|
||||
"Rectangle": "Hình chữ nhật",
|
||||
@@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Ưu tiên Gợi ý từ Card",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Nếu được kiểm tra và thẻ nhân vật chứa một lệnh phá vỡ giam giữ (Hướng dẫn Lịch sử Bài viết), hãy sử dụng thay vào đó",
|
||||
"Prefer Character Card Jailbreak": "Ưu tiên Jailbreak từ Card",
|
||||
"never_resize_avatars_tooltip": "Tránh cắt xén và thay đổi kích thước hình ảnh ký tự đã nhập. Khi tắt, hãy cắt/thay đổi kích thước thành 512x768.",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Tránh cắt xén và thay đổi kích thước hình ảnh ký tự đã nhập. Khi tắt, hãy cắt/thay đổi kích thước thành 512x768.",
|
||||
"Never resize avatars": "Không bao giờ thay đổi kích thước hình đại diện",
|
||||
"Show actual file names on the disk, in the characters list display only": "Hiển thị tên tệp thực tế trên đĩa, chỉ trong danh sách nhân vật",
|
||||
"Show avatar filenames": "Hiển thị tên tệp hình đại diện",
|
||||
@@ -709,7 +709,7 @@
|
||||
"Auto-swipe": "Tự động vuốt",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Bật chức năng tự động vuốt. Các cài đặt trong phần này chỉ có tác dụng khi tự động vuốt được bật",
|
||||
"Minimum generated message length": "Độ dài tối thiểu của tin nhắn được tạo",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "Nếu tin nhắn được tạo ra ngắn hơn điều này, kích hoạt tự động vuốt",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "Nếu tin nhắn được tạo ra ngắn hơn điều này, kích hoạt tự động vuốt",
|
||||
"Blacklisted words": "Từ trong danh sách đen",
|
||||
"words you dont want generated separated by comma ','": "các từ bạn không muốn được tạo ra được phân tách bằng dấu phẩy ','",
|
||||
"Blacklisted word count to swipe": "Số từ trong danh sách đen để vuốt",
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"Favorite": "星标",
|
||||
"Tag": "标签",
|
||||
"Duplicate": "复制",
|
||||
"Persona": "用户角色",
|
||||
"Persona": "角色",
|
||||
"Delete": "删除",
|
||||
"AI Response Configuration": "AI响应配置",
|
||||
"AI Configuration panel will stay open": "AI配置面板将保持打开",
|
||||
@@ -203,7 +203,6 @@
|
||||
"Ignore EOS Token": "忽略序列结束词符",
|
||||
"Ignore the EOS Token even if it generates.": "即使生成了序列结束词符,也忽略它。",
|
||||
"Skip Special Tokens": "跳过特殊词符",
|
||||
"Request Model Reasoning": "Request Model Reasoning",
|
||||
"Temperature Last": "温度放最后",
|
||||
"Temperature_Last_desc": "温度采样器放到最后使用。这通常是合理的。\n当启用时:首先进行潜在词符的选择,然后应用温度来修正它们的相对概率(技术上是对数似然)。\n当禁用时:首先应用温度来修正所有词符的相对概率,然后从中选择潜在词符。\n禁用此项可以增大分布在尾部的词符概率,这可能加大得到不相关回复的几率。",
|
||||
"Speculative Ngram": "推测性 Ngram",
|
||||
@@ -211,14 +210,12 @@
|
||||
"Spaces Between Special Tokens": "特殊词符之间的空格",
|
||||
"Seed_desc": "一个用于生成确定性和可复现的输出的随机种子。设置为 -1 时会使用随机种子。",
|
||||
"LLaMA / Mistral / Yi models only": "LLaMA / Mistral / Yi模型专用。首先确保您选择了适当的词符化器。\n这项设置决定了你不想在结果中看到的字符串。\n每行一个字符串。可以是文本或者[词符id]。\n许多词符以空格开头。如果不确定,请使用词符计数器。",
|
||||
"Global list": "Global list",
|
||||
"Example: some text [42, 69, 1337]": "例如:\n一些文本\n[42, 69, 1337]",
|
||||
"Preset-specific list": "Preset-specific list",
|
||||
"CFG": "CFG",
|
||||
"Classifier Free Guidance. More helpful tip coming soon": "无分类器指导(CFG)。更多有用的提示敬请期待。",
|
||||
"Scale": "缩放比例",
|
||||
"Negative Prompt": "负面提示词",
|
||||
"Used if CFG Scale is unset globally, per chat or character": "如果CFG缩放比例未被全局设置,它将作用于所有聊天或角色",
|
||||
"Used if CFG Scale is unset globally, per chat or character": "如果无分类器指导(CFG)缩放比例未在全局设置,它将作用于每个聊天或每个角色",
|
||||
"Add text here that would make the AI generate things you don't want in your outputs.": "请在此处添加文本,以避免生成您不希望出现在输出中的内容。",
|
||||
"Grammar String": "语法字符串",
|
||||
"GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF 或 EBNF,取决于使用的后端。如果您使用这个,您应该知道该用哪一个。",
|
||||
@@ -258,11 +255,11 @@
|
||||
"enable_functions_desc_1": "允许使用",
|
||||
"enable_functions_desc_2": "功能工具",
|
||||
"enable_functions_desc_3": "可以被各种扩展利用来提供附加功能。",
|
||||
"Send inline images": "发送图片",
|
||||
"image_inlining_hint_1": "如果模型支持,就可以在提示词中发送图片(例如 GPT-4V、Claude 3 或 Llava 13B)。\n发送消息时,点击",
|
||||
"image_inlining_hint_2": "在这里(",
|
||||
"image_inlining_hint_3": ")将图片添加到消息中。",
|
||||
"Inline Image Quality": "图片画质",
|
||||
"Send inline images": "发送内联图像",
|
||||
"image_inlining_hint_1": "如果模型支持,则在提示词中发送图像(例如 GPT-4V、Claude 3 或 Llava 13B)。\n对任何消息使用",
|
||||
"image_inlining_hint_2": "或",
|
||||
"image_inlining_hint_3": "菜单将图像文件附加到聊天中。",
|
||||
"Inline Image Quality": "内联图像质量",
|
||||
"openai_inline_image_quality_auto": "自动",
|
||||
"openai_inline_image_quality_low": "低",
|
||||
"openai_inline_image_quality_high": "高",
|
||||
@@ -271,11 +268,6 @@
|
||||
"Merges_all_system_messages_desc_2": "字段发送。",
|
||||
"Request model reasoning": "请求思维链",
|
||||
"Allows the model to return its thinking process.": "允许模型返回其思维过程。",
|
||||
"Constrains effort on reasoning for reasoning models.": "限定模型推理的强度。\n当前支持低、中、高三种强度。\n降低推理强度可以让模型更快回复,并节省推理所用的词符数。。",
|
||||
"Reasoning Effort": "推理强度",
|
||||
"openai_reasoning_effort_low": "低",
|
||||
"openai_reasoning_effort_medium": "中",
|
||||
"openai_reasoning_effort_high": "高",
|
||||
"Assistant Prefill": "AI预填",
|
||||
"Expand the editor": "展开编辑器",
|
||||
"Start Claude's answer with...": "以如下内容开始Claude的回答...",
|
||||
@@ -336,8 +328,7 @@
|
||||
"Click Authorize below or get the key from": "点击下方授权或从以下位置获取密钥",
|
||||
"View Remaining Credits": "查看剩余额度",
|
||||
"OpenRouter Model": "OpenRouter 模型",
|
||||
"Model Providers": "模型提供商",
|
||||
"Automatically chooses an alternative provider if chosen providers can't serve your request.": "在当前选择的模型提供商无效时,自动选择备用的提供商。",
|
||||
"Model Providers": "模型提供者",
|
||||
"Allow fallback providers": "允许后备提供者",
|
||||
"InfermaticAI API Key": "InfermaticAI API 密钥",
|
||||
"InfermaticAI Model": "InfermaticAI 模型",
|
||||
@@ -347,7 +338,7 @@
|
||||
"Mancer Model": "Mancer 模型",
|
||||
"API key (optional)": "API密钥(可选)",
|
||||
"Server url": "服务器URL",
|
||||
"Example: http://127.0.0.1:5000": "示例:http://127.0.0.1:5000",
|
||||
"Example: 127.0.0.1:5000": "示例:127.0.0.1:5000",
|
||||
"Model ID (optional)": "模型 ID(可选)",
|
||||
"Make sure you run it with": "确保您在运行时加上",
|
||||
"flag": "标志",
|
||||
@@ -364,7 +355,7 @@
|
||||
"No model description": "[无描述]",
|
||||
"vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",
|
||||
"vLLM API key": "vLLM API 密钥",
|
||||
"Example: http://127.0.0.1:8000": "示例:http://127.0.0.1:8000",
|
||||
"Example: 127.0.0.1:8000": "示例:http://127.0.0.1:8000",
|
||||
"vLLM Model": "vLLM 模型",
|
||||
"HuggingFace Token": "HuggingFace 代币",
|
||||
"Endpoint URL": "端点 URL",
|
||||
@@ -373,8 +364,8 @@
|
||||
"Aphrodite API key": "Aphrodite API 密钥",
|
||||
"Aphrodite Model": "Aphrodite 模型",
|
||||
"ggerganov/llama.cpp": "ggerganov/llama.cpp",
|
||||
"Example: http://127.0.0.1:8080": "示例:http://127.0.0.1:8080",
|
||||
"Example: http://127.0.0.1:11434": "示例:http://127.0.0.1:11434",
|
||||
"Example: 127.0.0.1:8080": "示例:127.0.0.1:8080",
|
||||
"Example: 127.0.0.1:11434": "示例:127.0.0.1:11434",
|
||||
"Ollama Model": "Ollama 模型",
|
||||
"Download": "下载",
|
||||
"Tabby API key": "Tabby API 密钥",
|
||||
@@ -382,8 +373,8 @@
|
||||
"must be set in Tabby's config.yml to switch models.": "必须在Tabby的config.yml内设置以切换模型",
|
||||
"Use an admin API key.": "使用管理员API密钥。",
|
||||
"koboldcpp API key (optional)": "koboldcpp API 密钥(可选)",
|
||||
"Example: http://127.0.0.1:5001": "示例:http://127.0.0.1:5001",
|
||||
"Bypass status check": "跳过状态检查",
|
||||
"Example: 127.0.0.1:5001": "示例:127.0.0.1:5001",
|
||||
"Bypass status check": "绕过状态检查",
|
||||
"Derive context size from backend": "从后端获取上下文长度",
|
||||
"Authorize": "授权",
|
||||
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "使用OAuth流程获取您的OpenRouter API令牌。您将被重定向到openrouter.ai",
|
||||
@@ -398,8 +389,8 @@
|
||||
"This will show up as your saved preset.": "这将显示为您保存的预设。",
|
||||
"Proxy Server URL": "代理服务器 URL",
|
||||
"Alternative server URL (leave empty to use the default value).": "备用服务器 URL(留空以使用默认值)。",
|
||||
"Doesn't work? Try adding": "不起作用?在末尾添加",
|
||||
"at the end!": "试试!",
|
||||
"Doesn't work? Try adding": "不起作用?尝试添加",
|
||||
"at the end!": "!",
|
||||
"Proxy Password": "代理密码",
|
||||
"Will be used as a password for the proxy instead of API key.": "将用作代理的密码,而不是 API 密钥。",
|
||||
"Peek a password": "查看密码",
|
||||
@@ -420,7 +411,6 @@
|
||||
"Anthropic's developer console": "Anthropic 开发者控制台",
|
||||
"Claude Model": "Claude 模型",
|
||||
"Window AI Model": "Window AI 模型",
|
||||
"Use extension settings": "使用扩展程序中的设定",
|
||||
"Allow fallback routes Description": "如果所选模型无法响应您的请求,则自动选择备用模型。",
|
||||
"Allow fallback models": "允许后备模型",
|
||||
"Model Order": "OpenRouter 模型顺序",
|
||||
@@ -429,7 +419,6 @@
|
||||
"Context Size": "上下文长度",
|
||||
"Group by vendors": "按厂商分组",
|
||||
"Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。",
|
||||
"To use instruct formatting, switch to OpenRouter under Text Completion API.": "To use instruct formatting, switch to OpenRouter under Text Completion API.",
|
||||
"Scale API Key": "Scale API密钥",
|
||||
"Clear your cookie": "清除你的 Cookie",
|
||||
"Alt Method": "备用方法",
|
||||
@@ -453,6 +442,7 @@
|
||||
"Select a Model": "选择一个模型",
|
||||
"Custom Endpoint (Base URL)": "自定义端点(基础 URL)",
|
||||
"Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
|
||||
"at the end of the URL!": "到 URL 的末尾!",
|
||||
"Custom API Key": "自定义 API 密钥",
|
||||
"(Optional)": "(可选)",
|
||||
"Enter a Model ID": "输入模型名",
|
||||
@@ -476,9 +466,9 @@
|
||||
"AI Response Formatting": "AI回复格式化",
|
||||
"Advanced Formatting": "高级格式化设置",
|
||||
"Import Advanced Formatting settings": "导入高级格式化设置\n\n对于指导和上下文模板,你也可以提供旧版文件。",
|
||||
"Master Import": "全局导入",
|
||||
"Master Import": "Master Import",
|
||||
"Export Advanced Formatting settings": "导出高级格式化设置",
|
||||
"Master Export": "全局导出",
|
||||
"Master Export": "Master Export",
|
||||
"Context Template": "上下文模板",
|
||||
"context_derived": "若可能,从模型的元数据获取。",
|
||||
"Select your current Context Template": "选择你当前的上下文模板",
|
||||
@@ -518,7 +508,7 @@
|
||||
"Skip Example Dialogues Formatting": "跳过示例对话格式化",
|
||||
"Include Names": "包括名称",
|
||||
"Never": "永不",
|
||||
"Groups and Past Personas": "群聊和过去的用户角色",
|
||||
"Groups and Past Personas": "Groups and Past Personas",
|
||||
"Always": "永远",
|
||||
"Instruct Sequences": "指令序列",
|
||||
"User Message Sequences": "用户消息序列",
|
||||
@@ -569,26 +559,8 @@
|
||||
"Prompt Content": "提示词内容",
|
||||
"Custom Stopping Strings": "自定义停止字符串",
|
||||
"JSON serialized array of strings": "JSON序列化的字符串数组",
|
||||
"Replace Macro in Stop Strings": "替换自定义停止字符串中的宏",
|
||||
"Replace Macro in Custom Stopping Strings": "替换自定义停止字符串中的宏",
|
||||
"Token Padding": "词符填充",
|
||||
"Reasoning": "推理",
|
||||
"reasoning_auto_parse": "Automatically parse reasoning blocks from main content between the reasoning prefix/suffix. Both fields must be defined and non-empty.",
|
||||
"Auto-Parse": "自动解析",
|
||||
"reasoning_auto_expand": "自动展开推理内容块。",
|
||||
"Auto-Expand": "自动展开",
|
||||
"reasoning_show_hidden": "对于隐藏推理内容的模型,展示其推理用时。",
|
||||
"Show Hidden": "显示隐藏内容",
|
||||
"reasoning_add_to_prompts": "将已有的推理块添加到提示词。若需新增一个推理块,请使用消息编辑菜单。",
|
||||
"Add to Prompts": "添加到提示词",
|
||||
"reasoning_max_additions": "Maximum number of reasoning blocks to be added per prompt, counting from the last message.",
|
||||
"Max": "最大值",
|
||||
"Reasoning Formatting": "推理内容格式化",
|
||||
"reasoning_prefix": "插入在推理内容之前。",
|
||||
"Prefix": "前缀",
|
||||
"reasoning_suffix": "插入在推理内容之后。",
|
||||
"Suffix": "后缀",
|
||||
"reasoning_separator": "插入在推理内容和消息内容之间。",
|
||||
"Separator": "分隔符",
|
||||
"Miscellaneous": "杂项",
|
||||
"Non-markdown strings": "非 Markdown 字符串",
|
||||
"comma delimited,no spaces between": "以逗号分隔,无需空格",
|
||||
@@ -612,7 +584,7 @@
|
||||
"(0 = unlimited, use budget)": "(“0”为无限制,使用预算)",
|
||||
"Cap the number of entry activation recursions": "限制条目激活递归的次数",
|
||||
"Max Recursion Steps": "最大递归深度",
|
||||
"0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc": "“0”为无限制,“1”为扫描一次且不递归,“2”为扫描一次且递归一次,依此类推\n(当使用最小激活次数时,此功能被禁用)",
|
||||
"0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\\n(disabled when min activations are used)": "“0”为无限制,“1”为扫描一次且不递归,“2”为扫描一次且递归一次,依此类推\n(当使用最小激活次数时,此功能被禁用)",
|
||||
"Insertion Strategy": "插入策略",
|
||||
"Sorted Evenly": "均匀排序",
|
||||
"Character Lore First": "角色世界书优先",
|
||||
@@ -667,7 +639,7 @@
|
||||
"Delete a theme": "删除主题",
|
||||
"Update a theme file": "更新主题文件",
|
||||
"Save as a new theme": "另存为新主题",
|
||||
"Avatar Style:": "头像样式:",
|
||||
"Avatar Style": "头像样式",
|
||||
"Circle": "圆形",
|
||||
"Square": "正方形",
|
||||
"Rectangle": "矩形",
|
||||
@@ -741,16 +713,16 @@
|
||||
"Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog.": "定义在导入卡片时应选择哪种操作来导入其列出的标签。“询问”将始终显示对话框。",
|
||||
"Import Card Tags": "导入卡片标签",
|
||||
"Ask": "询问",
|
||||
"tag_import_none": "不导入",
|
||||
"tag_import_all": "导入全部",
|
||||
"tag_import_existing": "仅导入现有的",
|
||||
"tag_import_none": "无",
|
||||
"tag_import_all": "全部",
|
||||
"Existing": "现存的",
|
||||
"Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "使用模糊匹配,在列表中通过所有数据字段搜索角色,而不仅仅是名称子字符串",
|
||||
"Advanced Character Search": "高级角色搜索",
|
||||
"If checked and the character card contains a prompt override (System Prompt), use that instead": "开启后,如果角色卡已包含系统提示词,则覆盖当前的系统提示词。",
|
||||
"Prefer Character Card Prompt": "角色卡提示词优先",
|
||||
"If checked and the character card contains a Post-History Instructions override, use that instead": "开启后,如果角色卡包含后历史指令覆盖,则使用它。",
|
||||
"Prefer Character Card Instructions": "首选角色卡说明",
|
||||
"never_resize_avatars_tooltip": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
|
||||
"Never resize avatars": "永不调整头像大小",
|
||||
"Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",
|
||||
"Show avatar filenames": "显示头像文件名",
|
||||
@@ -779,7 +751,7 @@
|
||||
"Restore User Input": "恢复用户输入",
|
||||
"Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "允许通过拖动重新定位某些UI元素。仅适用于PC,对移动设备无影响",
|
||||
"Movable UI Panels": "可移动 UI 面板",
|
||||
"Reset MovingUI panel sizes/locations.": "重置 可移动UI 面板大小/位置。",
|
||||
"Reset MovingUI panel sizes/locations.": "重置 MovingUI 面板大小/位置。",
|
||||
"mui_reset": "Reset",
|
||||
"MovingUI preset. Predefined/saved draggable positions": "可移动UI预设。预定义/保存的可拖动位置",
|
||||
"MUI Preset": "可移动 UI 预设",
|
||||
@@ -787,7 +759,7 @@
|
||||
"Apply a custom CSS style to all of the ST GUI": "将自定义CSS样式应用于所有ST GUI",
|
||||
"Custom CSS": "自定义 CSS",
|
||||
"Chat/Message Handling": "聊天/消息处理",
|
||||
"# Messages to Load": "要加载 # 条消息",
|
||||
"# Messages to Load": "# 要加载的消息",
|
||||
"The number of chat history messages to load before pagination.": "分页前要加载的聊天历史消息数。",
|
||||
"(0 = All)": "(“0”为全部)",
|
||||
"Streaming FPS": "流式传输帧速率",
|
||||
@@ -832,12 +804,11 @@
|
||||
"Auto-swipe": "自动滑动",
|
||||
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "启用自动滑动功能。仅当启用自动滑动时,本节中的设置才会生效",
|
||||
"Minimum generated message length": "生成的消息的最小长度",
|
||||
"If the generated message is shorter than these many characters, trigger an auto-swipe": "如果生成的消息短于此长度,则触发自动滑动",
|
||||
"If the generated message is shorter than this, trigger an auto-swipe": "如果生成的消息短于此长度,则触发自动滑动",
|
||||
"Blacklisted words": "屏蔽词",
|
||||
"words you dont want generated separated by comma ','": "不想生成的词语,用半角逗号“,”分隔",
|
||||
"Blacklisted word count to swipe": "触发滑动的黑名单词语数量",
|
||||
"Minimum number of blacklisted words detected to trigger an auto-swipe": "触发自动滑动刷新回复所需检测到的最少违禁词数量。",
|
||||
"Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.": "当回复没有达到特定词符数时,自动让模型“继续”这个回复。",
|
||||
"Auto-Continue": "自动继续",
|
||||
"Allow for Chat Completion APIs": "允许使用聊天补全API",
|
||||
"Target length (tokens)": "目标长度(以词符数计)",
|
||||
@@ -872,12 +843,12 @@
|
||||
"Change Background Image": "更改背景图片",
|
||||
"Background Image": "背景图片",
|
||||
"Filter": "搜索",
|
||||
"Background Fitting": "背景图片尺寸",
|
||||
"Classic": "经典",
|
||||
"Cover": "填充",
|
||||
"Contain": "不变换",
|
||||
"Stretch": "拉伸",
|
||||
"Center": "居中",
|
||||
"Background Fitting": "Background Fitting",
|
||||
"Classic": "Classic",
|
||||
"Cover": "Cover",
|
||||
"Contain": "Contain",
|
||||
"Stretch": "Stretch",
|
||||
"Center": "Center",
|
||||
"Automatically select a background based on the chat context": "根据聊天上下文自动选择背景",
|
||||
"Auto-select": "自动选择",
|
||||
"System Backgrounds": "系统背景",
|
||||
@@ -959,7 +930,6 @@
|
||||
"Search / Create Tags": "搜索/创建标签",
|
||||
"View all tags": "查看所有标签",
|
||||
"Creator's Notes": "创作者的注释",
|
||||
"Character details are hidden.": "角色详情已隐藏。",
|
||||
"Show / Hide Description and First Message": "显示/隐藏描述和第一条消息",
|
||||
"Character Description": "角色描述",
|
||||
"Click to allow/forbid the use of external media for this character.": "单击以允许/禁止此角色使用外部媒体。",
|
||||
@@ -1084,7 +1054,6 @@
|
||||
"Drag to reorder tag": "拖动以排序",
|
||||
"Use tag as folder": "标记为文件夹",
|
||||
"Delete tag": "删除标签",
|
||||
"Toggle entry's active state.": "切换条目激活状态。",
|
||||
"Entry Title/Memo": "条目标题/备忘录",
|
||||
"WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "世界书条目状态:\r🔵 永久\r🟢 关键词\r🔗 向量化",
|
||||
"WI_Entry_Status_Constant": "永久",
|
||||
@@ -1103,7 +1072,7 @@
|
||||
"Depth": "深度",
|
||||
"Order:": "顺序:",
|
||||
"Order": "顺序",
|
||||
"Trigger %:": "触发 %:",
|
||||
"Trigger %:": "触发 %:",
|
||||
"Duplicate world info entry": "重复的世界信息条目",
|
||||
"Delete world info entry": "删除世界信息条目",
|
||||
"Comma separated (required)": "逗号分隔(必填)",
|
||||
@@ -1185,7 +1154,7 @@
|
||||
"Message Actions": "消息操作",
|
||||
"Translate message": "翻译消息",
|
||||
"Generate Image": "生成图片",
|
||||
"Narrate": "朗读",
|
||||
"Narrate": "叙述",
|
||||
"Exclude message from prompts": "从提示词中排除消息",
|
||||
"Include message in prompts": "将消息包含在提示词中",
|
||||
"Embed file or image": "嵌入文件或图像",
|
||||
@@ -1196,16 +1165,9 @@
|
||||
"Edit": "编辑",
|
||||
"Confirm": "确认",
|
||||
"Copy this message": "复制此消息",
|
||||
"Add a reasoning block": "添加一个推理块",
|
||||
"Delete this message": "删除此消息",
|
||||
"Move message up": "将消息上移",
|
||||
"Move message down": "将消息下移",
|
||||
"Thought for some time": "思考了一会",
|
||||
"Confirm Edit": "确认",
|
||||
"Remove reasoning": "删除推理内容",
|
||||
"Cancel edit": "Cancel edit",
|
||||
"Copy reasoning": "复制推理内容",
|
||||
"Edit reasoning": "编辑推理内容",
|
||||
"Enlarge": "放大",
|
||||
"Caption": "标题",
|
||||
"Swipe left": "Swipe left",
|
||||
@@ -1246,7 +1208,7 @@
|
||||
"View contents": "查看内容",
|
||||
"Remove the file": "删除文件",
|
||||
"Author's Note": "作者注释",
|
||||
"Unique to this chat": "仅对此聊天生效",
|
||||
"Unique to this chat": "此聊天独有",
|
||||
"Checkpoints inherit the Note from their parent, and can be changed individually after that.": "检查点从其父级继承注释,之后可以单独更改。",
|
||||
"Include in World Info Scanning": "纳入世界信息扫描",
|
||||
"Before Main Prompt / Story String": "主提示词/故事线之前",
|
||||
@@ -1262,13 +1224,13 @@
|
||||
"Replace Author's Note": "替换作者注",
|
||||
"Default Author's Note": "默认作者注",
|
||||
"Will be automatically added as the Author's Note for all new chats.": "将自动添加为所有新聊天的作者注释。",
|
||||
"Chat CFG": "本聊天的CFG缩放",
|
||||
"1 = disabled": "“1”为禁用",
|
||||
"Chat CFG": "聊天CFG",
|
||||
"1 = disabled": "“1”为已禁用",
|
||||
"write short replies, write replies using past tense": "写简短的回复,用过去时写回复",
|
||||
"Positive Prompt": "正面提示词",
|
||||
"Use character CFG scales": "单独为各个角色设置CFG缩放",
|
||||
"Character CFG": "角色CFG配置",
|
||||
"Will be automatically added as the CFG for this character.": "将自动添加到该角色的CFG设置中。",
|
||||
"Will be automatically added as the CFG for this character.": "将自动添加为该角色的 CFG。",
|
||||
"Global CFG": "全局CFG",
|
||||
"Will be used as the default CFG options for every chat unless overridden.": "除非被覆盖,否则将用作每次聊天的默认 CFG 选项。",
|
||||
"CFG Prompt Cascading": "CFG 提示词级联",
|
||||
@@ -1304,10 +1266,6 @@
|
||||
"Regenerate": "重新生成",
|
||||
"Impersonate": "AI 帮答",
|
||||
"Continue": "继续",
|
||||
"extension_install_1": "若想从此页安装扩展程序,你需要提前安装",
|
||||
"extension_install_2": "。",
|
||||
"extension_install_3": "点这个图标(",
|
||||
"extension_install_4": ")前往扩展程序的代码仓库以了解如何使用它。",
|
||||
"These characters are the winners of character design contests and have outstandable quality.": "这些角色都是角色设计大赛的获奖者,品质非常出色。",
|
||||
"Contest Winners": "比赛获胜者",
|
||||
"These characters are the finalists of character design contests and have remarkable quality.": "这些角色都是角色设计大赛的入围作品,品质十分出色。",
|
||||
@@ -1376,11 +1334,9 @@
|
||||
"macro)": "宏指令)",
|
||||
"Automatically caption images": "自动为图像添加标题",
|
||||
"Edit captions before saving": "保存前编辑标题",
|
||||
"Included settings:": "包含的设置:",
|
||||
"{{@key}}": "{{@key}}:",
|
||||
"Profile name:": "配置名称:",
|
||||
"Creating a Connection Profile": "新建API连接配置",
|
||||
"Click on the setting name to omit it from the profile.": "点击设置名称以将其从连接配置中删除。",
|
||||
"{{@key}}": "{{@key}}:",
|
||||
"Enter a name:": "输入名字:",
|
||||
"Connection Profile": "API连接配置",
|
||||
"View connection profile details": "查看API连接配置详情",
|
||||
@@ -1389,17 +1345,14 @@
|
||||
"Edit a connection profile": "编辑API连接配置",
|
||||
"Reload a connection profile": "重载API连接配置",
|
||||
"Delete a connection profile": "删除API连接配置",
|
||||
"Omitted Settings:": "排除的设置:",
|
||||
"Omitted Settings:": "Omitted Settings:",
|
||||
"Character Expressions": "角色表情",
|
||||
"Use the selected API from Chat Translation extension settings.": "使用聊天翻译扩展程序中已选择的API。",
|
||||
"Translate text to English before classification": "分类之前将文本翻译成英文",
|
||||
"A single expression can have multiple sprites. Whenever the expression is chosen, a random sprite for this expression will be selected.": "A single expression can have multiple sprites. Whenever the expression is chosen, a random sprite for this expression will be selected.",
|
||||
"Allow multiple sprites per expression": "Allow multiple sprites per expression",
|
||||
"If the same expression is used again, re-roll the sprite. This only applies to expressions that have multiple available sprites assigned.": "If the same expression is used again, re-roll the sprite. This only applies to expressions that have multiple available sprites assigned.",
|
||||
"Re-roll if same expression is used again": "Re-roll if same sprite is used again",
|
||||
"Show default images (emojis) if sprite missing": "如果表情包缺失,则显示默认图像(表情符号)",
|
||||
"Image Type - talkinghead (extras)": "图像类型 - 说话头像(附加内容)",
|
||||
"Classifier API": "分类器 API",
|
||||
"Select the API for classifying expressions.": "选择用于对表达式进行分类的API。",
|
||||
"Main API": "当前连接的 API",
|
||||
"Main API": "主要 API",
|
||||
"WebLLM Extension": "WebLLM Extension",
|
||||
"LLM Prompt": "大语言模型提示词",
|
||||
"Will be used if the API doesn't support JSON schemas or function calling.": "如果 API 不支持 JSON 模式或函数调用,则会使用它。",
|
||||
@@ -1419,10 +1372,6 @@
|
||||
"Put images with expressions there. File names should follow the pattern:": "将带有表情的图像放在那里。文件名应遵循以下模式:",
|
||||
"expression_label_pattern": "[表达式标签].[图像格式]",
|
||||
"Sprite set:": "表情集:",
|
||||
"upload_expression_request": "请输入表情名称(不用加后缀)。",
|
||||
"upload_expression_naming_1": "素材名称必须符合所选表情 {{expression}} 的命名规范",
|
||||
"upload_expression_naming_2": "当存在多个表情时,名称应由表情名称与合法后缀构成,允许使用横杠'-'或英文句号'.'作为分隔符。",
|
||||
"upload_expression_replace": "点击“替换”以替换当前表情:",
|
||||
"Show Gallery": "展示图库",
|
||||
"ext_sum_title": "总结",
|
||||
"ext_sum_with": "总结如下:",
|
||||
@@ -1537,7 +1486,7 @@
|
||||
"ext_regex_replace_string_placeholder": "使用 {{match}} 包含来自“查找正则表达式”或“$1”、“$2”等的匹配文本作为捕获组。",
|
||||
"Trim Out": "修剪掉",
|
||||
"ext_regex_trim_placeholder": "在替换之前全局修剪正则表达式匹配中任何不需要的部分。用回车键分隔每个元素。",
|
||||
"ext_regex_affects": "作用范围",
|
||||
"ext_regex_affects": "影响",
|
||||
"ext_regex_user_input_desc": "用户发送的消息",
|
||||
"ext_regex_user_input": "用户输入",
|
||||
"ext_regex_ai_input_desc": "从生成式API中获取的信息。",
|
||||
@@ -1545,7 +1494,6 @@
|
||||
"ext_regex_slash_desc": "通过 STscript 命令发送的消息。",
|
||||
"Slash Commands": "快捷命令",
|
||||
"ext_regex_wi_desc": "知识书/世界书 条目的内容。需要勾选“仅格式提示词”!",
|
||||
"ext_regex_reasoning_desc": "推理块内容。当'仅格式提示词'被选中时,它会影响提示词里的推理内容。",
|
||||
"ext_regex_min_depth_desc": "当应用于提示或显示时,仅影响深度至少为 N 级的消息。“0”为最后一条消息,“1”为倒数第二条消息等。仅计算 WI 条目 @Depth 和可用消息,即非隐藏或系统消息。",
|
||||
"Min Depth": "最小深度",
|
||||
"ext_regex_min_depth_placeholder": "无限",
|
||||
@@ -1673,13 +1621,6 @@
|
||||
"Interactive Mode": "交互模式",
|
||||
"Function Tool": "Function Tool",
|
||||
"Image Prompt Templates": "图像提示模板",
|
||||
"Token Counter": "词符计数器",
|
||||
"Type / paste in the box below to see the number of tokens in the text.": "在下方框中输入或粘贴你想要统计词符数量的文本。",
|
||||
"Selected tokenizer:": "已选分词器:",
|
||||
"Input:": "输入:",
|
||||
"Tokens:": "词符:",
|
||||
"Tokenized text:": "词符化文本:",
|
||||
"Token IDs:": "词符ID:",
|
||||
"ext_translate_btn_chat": "翻译聊天",
|
||||
"ext_translate_btn_input": "翻译输入",
|
||||
"ext_translate_delete_confirm_1": "你确定吗?",
|
||||
@@ -1699,12 +1640,11 @@
|
||||
"Auto Generation": "自动生成",
|
||||
"Requires auto generation to be enabled.": "需要启用自动生成功能。",
|
||||
"Narrate by paragraphs (when streaming)": "按段朗读(流式播放时)",
|
||||
"Narrate by paragraphs (when not streaming)": "按段朗读(非流式播放时)",
|
||||
"Only narrate quotes": "只朗读引号内文本",
|
||||
"Ignore text, even quotes, inside asterisk": "不朗读所有*星号内文本*,即使其被引号包裹",
|
||||
"Narrate only the translated text": "只朗读翻译后文本",
|
||||
"Skip codeblocks": "跳过代码块",
|
||||
"Skip tagged blocks": "跳过标签块里的内容(<标签>跳过这里</标签>)",
|
||||
"Skip tagged blocks": "跳过标签化的块(<tagged>)",
|
||||
"Pass Asterisks to TTS Engine": "将星号传递给文本转语音服务",
|
||||
"Audio Playback Speed": "音频播放速度",
|
||||
"Vector Storage": "向量存储",
|
||||
@@ -1726,7 +1666,6 @@
|
||||
"Max Entries": "最大条目数",
|
||||
"File vectorization settings": "文件向量化设置",
|
||||
"Enable for files": "为文件启用",
|
||||
"Only chunk on custom boundary": "仅按自定义边界分块",
|
||||
"Translate files into English before processing": "处理之前将文件翻译成英文",
|
||||
"Message attachments": "消息附件",
|
||||
"Size threshold (KB)": "大小阈值(KB)",
|
||||
@@ -1781,9 +1720,9 @@
|
||||
"Chat Lorebook for": "聊天知识书",
|
||||
"chat_world_template_txt": "选定的世界信息将绑定到此聊天。生成 AI 回复时,\n它将与全球和角色传说书中的条目相结合。",
|
||||
"chat_rename_1": "输入聊天的新名称:",
|
||||
"chat_rename_2": "注意!!与其他文件重名会导致错误!!",
|
||||
"chat_rename_3": "此举会将此聊天与标记为“检查点”的聊天解绑。",
|
||||
"chat_rename_4": "(不需要在结尾添加 '.JSONL' 后缀)",
|
||||
"chat_rename_2": "注意!!使用已有文件名会导致错误!!",
|
||||
"chat_rename_3": "此举会将次聊天与标记为“检查点”的聊天解绑。",
|
||||
"chat_rename_4": "不需要在结尾添加 '.JSONL'",
|
||||
"Enter Checkpoint Name:": "输入检查点名称:",
|
||||
"(Leave empty to auto-generate)": "(留空以自动生成)",
|
||||
"The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.": "当前检查点将会被解绑并替换为新的检查点,但仍可在聊天管理中找到。",
|
||||
@@ -1916,6 +1855,7 @@
|
||||
"Total Tokens in Prompt:": "提示词的总Token数量:",
|
||||
"Max Context": "最大上下文:",
|
||||
"(Context Size - Response Length)": "(上下文长度 - 回复长度)",
|
||||
":": ":",
|
||||
"System-wide Replacement Macros (in order of evaluation):": "系统范围的替换宏(按评估顺序):",
|
||||
"help_macros_1": "仅适用于斜线命令批处理。替换为上一个命令的返回结果。",
|
||||
"help_macros_2": "仅插入一个换行符。",
|
||||
@@ -1933,19 +1873,18 @@
|
||||
"help_macros_14": "未格式化的对话示例",
|
||||
"(only for Story String)": "(仅适用于故事字符串)",
|
||||
"help_macros_summary": "“Summarize”扩展生成的最新聊天摘要(如果有)。",
|
||||
"help_macros_15": "您当前的用户角色名称",
|
||||
"help_macros_15": "您当前的 Persona 用户名",
|
||||
"help_macros_16": "角色的名字",
|
||||
"help_macros_17": "角色的版本号",
|
||||
"help_macros_18": "以逗号分隔的群成员名称列表或单人聊天中的角色名称。别名:{{charIfNotGroup}}",
|
||||
"help_groupNotMuted": "与 {{group}} 相同,但排除被禁言的成员",
|
||||
"help_macros_19": "当前选定的 API 的文本生成模型名称。",
|
||||
"Can be inaccurate!": "不一定准确!",
|
||||
"Can be inaccurate!": "可能不准确!",
|
||||
"help_macros_20": "最新聊天消息的文本。",
|
||||
"help_macros_lastUser": "最后的用户聊天消息文本。",
|
||||
"help_macros_lastChar": "最后的角色聊天消息文本。",
|
||||
"help_macros_21": "最新聊天消息的索引号。对于斜线命令批处理很有用。",
|
||||
"help_macros_22": "上下文中包含的第一条消息的 ID。要求在当前会话中至少运行一次生成。",
|
||||
"help_macros_firstDisplayedMessageId": "第一条载入可见聊天的消息的ID",
|
||||
"help_macros_23": "最后一条聊天消息中当前滑动的 ID(以 1 为基数)。如果最后一条消息是用户或提示隐藏的,则为空字符串。",
|
||||
"help_macros_24": "最后一条聊天消息中的滑动次数。如果最后一条消息是用户隐藏或提示隐藏的,则为空字符串。",
|
||||
"help_macros_reverse": "反转宏的内容。",
|
||||
@@ -2036,7 +1975,7 @@
|
||||
"Enter your password below to confirm:": "输入您的密码以确认:",
|
||||
"Chat Scenario Override": "聊天场景覆盖",
|
||||
"Remove": "移除",
|
||||
"Unique to this chat.": "仅对此聊天生效。",
|
||||
"Unique to this chat.": "Unique to this chat.",
|
||||
"All group members will use the following scenario text instead of what is specified in their character cards.": "All group members will use the following scenario text instead of what is specified in their character cards.",
|
||||
"The following scenario text will be used instead of the value set in the character card.": "The following scenario text will be used instead of the value set in the character card.",
|
||||
"Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.",
|
||||
@@ -2102,7 +2041,7 @@
|
||||
"in the chat bar": "至聊天框",
|
||||
"SillyTavern Documentation Site": "访问 SillyTavern 帮助文档",
|
||||
"Still have questions?": "仍有疑问?",
|
||||
"Join the SillyTavern Discord": "加入 SillyTavern 的 Discord群组",
|
||||
"Join the SillyTavern Discord": "加入 SillyTavern Discord群组",
|
||||
"Post a GitHub issue": "在 GitHub 发布问题",
|
||||
"Contact the developers": "联系开发者",
|
||||
"If you're connected to an API, try asking me something!": "若您已经配置好API,尝试发送些什么吧!",
|
||||
|
File diff suppressed because it is too large
Load Diff
1462
public/script.js
1462
public/script.js
File diff suppressed because it is too large
Load Diff
@@ -395,7 +395,7 @@ class BulkEditOverlay {
|
||||
|
||||
/**
|
||||
* @typedef {object} LastSelected - An object noting the last selected character and its state.
|
||||
* @property {number} [characterId] - The character id of the last selected character.
|
||||
* @property {string} [characterId] - The character id of the last selected character.
|
||||
* @property {boolean} [select] - The selected state of the last selected character. <c>true</c> if it was selected, <c>false</c> if it was deselected.
|
||||
*/
|
||||
|
||||
@@ -672,10 +672,10 @@ class BulkEditOverlay {
|
||||
* @param {HTMLElement} currentCharacter - The html element of the currently toggled character
|
||||
*/
|
||||
handleShiftClick = (currentCharacter) => {
|
||||
const characterId = Number(currentCharacter.getAttribute('data-chid'));
|
||||
const characterId = currentCharacter.getAttribute('chid');
|
||||
const select = !this.selectedCharacters.includes(characterId);
|
||||
|
||||
if (this.lastSelected.characterId >= 0 && this.lastSelected.select !== undefined) {
|
||||
if (this.lastSelected.characterId && this.lastSelected.select !== undefined) {
|
||||
// Only if select state and the last select state match we execute the range select
|
||||
if (select === this.lastSelected.select) {
|
||||
this.toggleCharactersInRange(currentCharacter, select);
|
||||
@@ -691,7 +691,7 @@ class BulkEditOverlay {
|
||||
* @param {boolean} [param1.markState] - Whether the toggle of this character should be remembered as the last done toggle
|
||||
*/
|
||||
toggleSingleCharacter = (character, { markState = true } = {}) => {
|
||||
const characterId = Number(character.getAttribute('data-chid'));
|
||||
const characterId = character.getAttribute('chid');
|
||||
|
||||
const select = !this.selectedCharacters.includes(characterId);
|
||||
const legacyBulkEditCheckbox = character.querySelector('.' + BulkEditOverlay.legacySelectedClass);
|
||||
@@ -699,11 +699,11 @@ class BulkEditOverlay {
|
||||
if (select) {
|
||||
character.classList.add(BulkEditOverlay.selectedClass);
|
||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
|
||||
this.#selectedCharacters.push(characterId);
|
||||
this.#selectedCharacters.push(String(characterId));
|
||||
} else {
|
||||
character.classList.remove(BulkEditOverlay.selectedClass);
|
||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
|
||||
this.#selectedCharacters = this.#selectedCharacters.filter(item => characterId !== item);
|
||||
this.#selectedCharacters = this.#selectedCharacters.filter(item => String(characterId) !== item);
|
||||
}
|
||||
|
||||
this.updateSelectedCount();
|
||||
@@ -732,15 +732,15 @@ class BulkEditOverlay {
|
||||
* @param {boolean} select - <c>true</c> if the characters in the range are to be selected, <c>false</c> if deselected
|
||||
*/
|
||||
toggleCharactersInRange = (currentCharacter, select) => {
|
||||
const currentCharacterId = Number(currentCharacter.getAttribute('data-chid'));
|
||||
const currentCharacterId = currentCharacter.getAttribute('chid');
|
||||
const characters = Array.from(document.querySelectorAll('#' + BulkEditOverlay.containerId + ' .' + BulkEditOverlay.characterClass));
|
||||
|
||||
const startIndex = characters.findIndex(c => Number(c.getAttribute('data-chid')) === Number(this.lastSelected.characterId));
|
||||
const endIndex = characters.findIndex(c => Number(c.getAttribute('data-chid')) === currentCharacterId);
|
||||
const startIndex = characters.findIndex(c => c.getAttribute('chid') === this.lastSelected.characterId);
|
||||
const endIndex = characters.findIndex(c => c.getAttribute('chid') === currentCharacterId);
|
||||
|
||||
for (let i = Math.min(startIndex, endIndex); i <= Math.max(startIndex, endIndex); i++) {
|
||||
const character = characters[i];
|
||||
const characterId = Number(character.getAttribute('data-chid'));
|
||||
const characterId = character.getAttribute('chid');
|
||||
const isCharacterSelected = this.selectedCharacters.includes(characterId);
|
||||
|
||||
// Only toggle the character if it wasn't on the state we have are toggling towards.
|
||||
|
@@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
import { DOMPurify } from '../lib.js';
|
||||
import { DOMPurify, Popper } from '../lib.js';
|
||||
|
||||
import { event_types, eventSource, is_send_press, main_api, substituteParams } from '../script.js';
|
||||
import { is_group_generating } from './group-chats.js';
|
||||
import { Message, MessageCollection, TokenHandler } from './openai.js';
|
||||
import { Message, TokenHandler } from './openai.js';
|
||||
import { power_user } from './power-user.js';
|
||||
import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
|
||||
import { debounce_timeout } from './constants.js';
|
||||
@@ -1440,8 +1440,36 @@ class PromptManager {
|
||||
footerDiv.querySelector('select').selectedIndex = selectedPromptIndex;
|
||||
|
||||
// Add prompt export dialogue and options
|
||||
|
||||
const exportForCharacter = await renderTemplateAsync('promptManagerExportForCharacter');
|
||||
const exportPopup = await renderTemplateAsync('promptManagerExportPopup', { isGlobalStrategy: 'global' === this.configuration.promptOrder.strategy, exportForCharacter });
|
||||
rangeBlockDiv.insertAdjacentHTML('beforeend', exportPopup);
|
||||
|
||||
// Destroy previous popper instance if it exists
|
||||
if (this.exportPopper) {
|
||||
this.exportPopper.destroy();
|
||||
}
|
||||
|
||||
this.exportPopper = Popper.createPopper(
|
||||
document.getElementById('prompt-manager-export'),
|
||||
document.getElementById('prompt-manager-export-format-popup'),
|
||||
{ placement: 'bottom' },
|
||||
);
|
||||
|
||||
const showExportSelection = () => {
|
||||
const popup = document.getElementById('prompt-manager-export-format-popup');
|
||||
const show = popup.hasAttribute('data-show');
|
||||
|
||||
if (show) popup.removeAttribute('data-show');
|
||||
else popup.setAttribute('data-show', '');
|
||||
|
||||
this.exportPopper.update();
|
||||
};
|
||||
|
||||
footerDiv.querySelector('#prompt-manager-import').addEventListener('click', this.handleImport);
|
||||
footerDiv.querySelector('#prompt-manager-export').addEventListener('click', this.handleFullExport);
|
||||
footerDiv.querySelector('#prompt-manager-export').addEventListener('click', showExportSelection);
|
||||
rangeBlockDiv.querySelector('.export-promptmanager-prompts-full').addEventListener('click', this.handleFullExport);
|
||||
rangeBlockDiv.querySelector('.export-promptmanager-prompts-character')?.addEventListener('click', this.handleCharacterExport);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,7 @@ import {
|
||||
send_on_enter_options,
|
||||
} from './power-user.js';
|
||||
|
||||
import { LoadLocal, SaveLocal, LoadLocalBool } from './f-localStorage.js';
|
||||
import { selected_group, is_group_generating, openGroupById } from './group-chats.js';
|
||||
import { getTagKeyForEntity, applyTagsOnCharacterSelect } from './tags.js';
|
||||
import {
|
||||
@@ -40,8 +41,6 @@ import { textgen_types, textgenerationwebui_settings as textgen_settings, getTex
|
||||
import { debounce_timeout } from './constants.js';
|
||||
|
||||
import { Popup } from './popup.js';
|
||||
import { accountStorage } from './util/AccountStorage.js';
|
||||
import { getCurrentUserHandle } from './user.js';
|
||||
|
||||
var RPanelPin = document.getElementById('rm_button_panel_pin');
|
||||
var LPanelPin = document.getElementById('lm_button_panel_pin');
|
||||
@@ -280,32 +279,17 @@ async function RA_autoloadchat() {
|
||||
// active character is the name, we should look it up in the character list and get the id
|
||||
if (active_character !== null && active_character !== undefined) {
|
||||
const active_character_id = characters.findIndex(x => getTagKeyForEntity(x) === active_character);
|
||||
if (active_character_id !== -1) {
|
||||
await selectCharacterById(active_character_id);
|
||||
if (active_character_id !== null) {
|
||||
await selectCharacterById(String(active_character_id));
|
||||
|
||||
// Do a little tomfoolery to spoof the tag selector
|
||||
const selectedCharElement = $(`#rm_print_characters_block .character_select[chid="${active_character_id}"]`);
|
||||
applyTagsOnCharacterSelect.call(selectedCharElement);
|
||||
} else {
|
||||
setActiveCharacter(null);
|
||||
saveSettingsDebounced();
|
||||
console.warn(`Currently active character with ID ${active_character} not found. Resetting to no active character.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (active_group !== null && active_group !== undefined) {
|
||||
if (active_character) {
|
||||
console.warn('Active character and active group are both set. Only active character will be loaded. Resetting active group.');
|
||||
setActiveGroup(null);
|
||||
saveSettingsDebounced();
|
||||
} else {
|
||||
const result = await openGroupById(String(active_group));
|
||||
if (!result) {
|
||||
setActiveGroup(null);
|
||||
saveSettingsDebounced();
|
||||
console.warn(`Currently active group with ID ${active_group} not found. Resetting to no active group.`);
|
||||
}
|
||||
}
|
||||
await openGroupById(String(active_group));
|
||||
}
|
||||
|
||||
// if the character list hadn't been loaded yet, try again.
|
||||
@@ -316,10 +300,7 @@ export async function favsToHotswap() {
|
||||
const entities = getEntitiesList({ doFilter: false });
|
||||
const container = $('#right-nav-panel .hotswap');
|
||||
|
||||
// Hard limit is required because even if all hotswaps don't fit the screen, their images would still be loaded
|
||||
// 25 is roughly calculated as the maximum number of favs that can fit an ultrawide monitor with the default theme
|
||||
const FAVS_LIMIT = 25;
|
||||
const favs = entities.filter(x => x.item.fav || x.item.fav == 'true').slice(0, FAVS_LIMIT);
|
||||
const favs = entities.filter(x => x.item.fav || x.item.fav == 'true');
|
||||
|
||||
//helpful instruction message if no characters are favorited
|
||||
if (favs.length == 0) {
|
||||
@@ -407,9 +388,9 @@ function RA_autoconnect(PrevApi) {
|
||||
|| (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)
|
||||
|| (secret_state[SECRET_KEYS.GROQ] && oai_settings.chat_completion_source == chat_completion_sources.GROQ)
|
||||
|| (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
|
||||
|| (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
|
||||
|| (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
|
||||
|| (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
|
||||
|| (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
|
||||
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
|
||||
) {
|
||||
$('#api_button_openai').trigger('click');
|
||||
@@ -428,34 +409,32 @@ function RA_autoconnect(PrevApi) {
|
||||
function OpenNavPanels() {
|
||||
if (!isMobile()) {
|
||||
//auto-open R nav if locked and previously open
|
||||
if (accountStorage.getItem('NavLockOn') == 'true' && accountStorage.getItem('NavOpened') == 'true') {
|
||||
if (LoadLocalBool('NavLockOn') == true && LoadLocalBool('NavOpened') == true) {
|
||||
//console.log("RA -- clicking right nav to open");
|
||||
$('#rightNavDrawerIcon').click();
|
||||
}
|
||||
|
||||
//auto-open L nav if locked and previously open
|
||||
if (accountStorage.getItem('LNavLockOn') == 'true' && accountStorage.getItem('LNavOpened') == 'true') {
|
||||
if (LoadLocalBool('LNavLockOn') == true && LoadLocalBool('LNavOpened') == true) {
|
||||
console.debug('RA -- clicking left nav to open');
|
||||
$('#leftNavDrawerIcon').click();
|
||||
}
|
||||
|
||||
//auto-open WI if locked and previously open
|
||||
if (accountStorage.getItem('WINavLockOn') == 'true' && accountStorage.getItem('WINavOpened') == 'true') {
|
||||
if (LoadLocalBool('WINavLockOn') == true && LoadLocalBool('WINavOpened') == true) {
|
||||
console.debug('RA -- clicking WI to open');
|
||||
$('#WIDrawerIcon').click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getUserInputKey = () => getCurrentUserHandle() + '_userInput';
|
||||
|
||||
function restoreUserInput() {
|
||||
if (!power_user.restore_user_input) {
|
||||
console.debug('restoreUserInput disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
const userInput = localStorage.getItem(getUserInputKey());
|
||||
const userInput = LoadLocal('userInput');
|
||||
if (userInput) {
|
||||
$('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
@@ -463,8 +442,7 @@ function restoreUserInput() {
|
||||
|
||||
function saveUserInput() {
|
||||
const userInput = String($('#send_textarea').val());
|
||||
localStorage.setItem(getUserInputKey(), userInput);
|
||||
console.debug('User Input -- ', userInput);
|
||||
SaveLocal('userInput', userInput);
|
||||
}
|
||||
const saveUserInputDebounced = debounce(saveUserInput);
|
||||
|
||||
@@ -761,7 +739,7 @@ export function initRossMods() {
|
||||
|
||||
//toggle pin class when lock toggle clicked
|
||||
$(RPanelPin).on('click', function () {
|
||||
accountStorage.setItem('NavLockOn', $(RPanelPin).prop('checked'));
|
||||
SaveLocal('NavLockOn', $(RPanelPin).prop('checked'));
|
||||
if ($(RPanelPin).prop('checked') == true) {
|
||||
//console.log('adding pin class to right nav');
|
||||
$(RightNavPanel).addClass('pinnedOpen');
|
||||
@@ -779,7 +757,7 @@ export function initRossMods() {
|
||||
}
|
||||
});
|
||||
$(LPanelPin).on('click', function () {
|
||||
accountStorage.setItem('LNavLockOn', $(LPanelPin).prop('checked'));
|
||||
SaveLocal('LNavLockOn', $(LPanelPin).prop('checked'));
|
||||
if ($(LPanelPin).prop('checked') == true) {
|
||||
//console.log('adding pin class to Left nav');
|
||||
$(LeftNavPanel).addClass('pinnedOpen');
|
||||
@@ -798,7 +776,7 @@ export function initRossMods() {
|
||||
});
|
||||
|
||||
$(WIPanelPin).on('click', function () {
|
||||
accountStorage.setItem('WINavLockOn', $(WIPanelPin).prop('checked'));
|
||||
SaveLocal('WINavLockOn', $(WIPanelPin).prop('checked'));
|
||||
if ($(WIPanelPin).prop('checked') == true) {
|
||||
console.debug('adding pin class to WI');
|
||||
$(WorldInfo).addClass('pinnedOpen');
|
||||
@@ -818,8 +796,8 @@ export function initRossMods() {
|
||||
});
|
||||
|
||||
// read the state of right Nav Lock and apply to rightnav classlist
|
||||
$(RPanelPin).prop('checked', accountStorage.getItem('NavLockOn') == 'true');
|
||||
if (accountStorage.getItem('NavLockOn') == 'true') {
|
||||
$(RPanelPin).prop('checked', LoadLocalBool('NavLockOn'));
|
||||
if (LoadLocalBool('NavLockOn') == true) {
|
||||
//console.log('setting pin class via local var');
|
||||
$(RightNavPanel).addClass('pinnedOpen');
|
||||
$(RightNavDrawerIcon).addClass('drawerPinnedOpen');
|
||||
@@ -830,8 +808,8 @@ export function initRossMods() {
|
||||
$(RightNavDrawerIcon).addClass('drawerPinnedOpen');
|
||||
}
|
||||
// read the state of left Nav Lock and apply to leftnav classlist
|
||||
$(LPanelPin).prop('checked', accountStorage.getItem('LNavLockOn') === 'true');
|
||||
if (accountStorage.getItem('LNavLockOn') == 'true') {
|
||||
$(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));
|
||||
if (LoadLocalBool('LNavLockOn') == true) {
|
||||
//console.log('setting pin class via local var');
|
||||
$(LeftNavPanel).addClass('pinnedOpen');
|
||||
$(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
|
||||
@@ -843,8 +821,8 @@ export function initRossMods() {
|
||||
}
|
||||
|
||||
// read the state of left Nav Lock and apply to leftnav classlist
|
||||
$(WIPanelPin).prop('checked', accountStorage.getItem('WINavLockOn') === 'true');
|
||||
if (accountStorage.getItem('WINavLockOn') == 'true') {
|
||||
$(WIPanelPin).prop('checked', LoadLocalBool('WINavLockOn'));
|
||||
if (LoadLocalBool('WINavLockOn') == true) {
|
||||
//console.log('setting pin class via local var');
|
||||
$(WorldInfo).addClass('pinnedOpen');
|
||||
$(WIDrawerIcon).addClass('drawerPinnedOpen');
|
||||
@@ -859,22 +837,22 @@ export function initRossMods() {
|
||||
//save state of Right nav being open or closed
|
||||
$('#rightNavDrawerIcon').on('click', function () {
|
||||
if (!$('#rightNavDrawerIcon').hasClass('openIcon')) {
|
||||
accountStorage.setItem('NavOpened', 'true');
|
||||
} else { accountStorage.setItem('NavOpened', 'false'); }
|
||||
SaveLocal('NavOpened', 'true');
|
||||
} else { SaveLocal('NavOpened', 'false'); }
|
||||
});
|
||||
|
||||
//save state of Left nav being open or closed
|
||||
$('#leftNavDrawerIcon').on('click', function () {
|
||||
if (!$('#leftNavDrawerIcon').hasClass('openIcon')) {
|
||||
accountStorage.setItem('LNavOpened', 'true');
|
||||
} else { accountStorage.setItem('LNavOpened', 'false'); }
|
||||
SaveLocal('LNavOpened', 'true');
|
||||
} else { SaveLocal('LNavOpened', 'false'); }
|
||||
});
|
||||
|
||||
//save state of Left nav being open or closed
|
||||
$('#WorldInfo').on('click', function () {
|
||||
if (!$('#WorldInfo').hasClass('openIcon')) {
|
||||
accountStorage.setItem('WINavOpened', 'true');
|
||||
} else { accountStorage.setItem('WINavOpened', 'false'); }
|
||||
SaveLocal('WINavOpened', 'true');
|
||||
} else { SaveLocal('WINavOpened', 'false'); }
|
||||
});
|
||||
|
||||
var chatbarInFocus = false;
|
||||
@@ -890,21 +868,21 @@ export function initRossMods() {
|
||||
OpenNavPanels();
|
||||
}, 300);
|
||||
|
||||
$(SelectedCharacterTab).click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_selected_ch'); });
|
||||
$('#rm_button_characters').click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_characters'); });
|
||||
$(SelectedCharacterTab).click(function () { SaveLocal('SelectedNavTab', 'rm_button_selected_ch'); });
|
||||
$('#rm_button_characters').click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); });
|
||||
|
||||
// when a char is selected from the list, save them as the auto-load character for next page load
|
||||
|
||||
// when a char is selected from the list, save their name as the auto-load character for next page load
|
||||
$(document).on('click', '.character_select', function () {
|
||||
const characterId = $(this).attr('data-chid');
|
||||
const characterId = $(this).attr('chid') || $(this).data('id');
|
||||
setActiveCharacter(characterId);
|
||||
setActiveGroup(null);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$(document).on('click', '.group_select', function () {
|
||||
const groupId = $(this).attr('data-chid') || $(this).attr('data-grid');
|
||||
const groupId = $(this).attr('chid') || $(this).attr('grid') || $(this).data('id');
|
||||
setActiveCharacter(null);
|
||||
setActiveGroup(groupId);
|
||||
saveSettingsDebounced();
|
||||
@@ -1018,14 +996,6 @@ export function initRossMods() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isModifiedKeyboardEvent(event) {
|
||||
return (event instanceof KeyboardEvent &&
|
||||
event.shiftKey ||
|
||||
event.ctrlKey ||
|
||||
event.altKey ||
|
||||
event.metaKey);
|
||||
}
|
||||
|
||||
$(document).on('keydown', async function (event) {
|
||||
await processHotkeys(event.originalEvent);
|
||||
});
|
||||
@@ -1107,7 +1077,7 @@ export function initRossMods() {
|
||||
}
|
||||
else if (is_send_press == false) {
|
||||
const skipConfirmKey = 'RegenerateWithCtrlEnter';
|
||||
const skipConfirm = accountStorage.getItem(skipConfirmKey) === 'true';
|
||||
const skipConfirm = LoadLocalBool(skipConfirmKey);
|
||||
function doRegenerate() {
|
||||
console.debug('Regenerating with Ctrl+Enter');
|
||||
$('#option_regenerate').trigger('click');
|
||||
@@ -1127,7 +1097,7 @@ export function initRossMods() {
|
||||
return;
|
||||
}
|
||||
|
||||
accountStorage.setItem(skipConfirmKey, String(regenerateWithCtrlEnter));
|
||||
SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);
|
||||
doRegenerate();
|
||||
}
|
||||
return;
|
||||
@@ -1149,10 +1119,9 @@ export function initRossMods() {
|
||||
$('#send_textarea').val() === '' &&
|
||||
$('#character_popup').css('display') === 'none' &&
|
||||
$('#shadow_select_chat_popup').css('display') === 'none' &&
|
||||
!isInputElementInFocus() &&
|
||||
!isModifiedKeyboardEvent(event)
|
||||
!isInputElementInFocus()
|
||||
) {
|
||||
$('.swipe_left:last').trigger('click', { source: 'keyboard', repeated: event.repeat });
|
||||
$('.swipe_left:last').click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1163,10 +1132,9 @@ export function initRossMods() {
|
||||
$('#send_textarea').val() === '' &&
|
||||
$('#character_popup').css('display') === 'none' &&
|
||||
$('#shadow_select_chat_popup').css('display') === 'none' &&
|
||||
!isInputElementInFocus() &&
|
||||
!isModifiedKeyboardEvent(event)
|
||||
!isInputElementInFocus()
|
||||
) {
|
||||
$('.swipe_right:last').trigger('click', { source: 'keyboard', repeated: event.repeat });
|
||||
$('.swipe_right:last').click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
|
||||
export { MODULE_NAME as NOTE_MODULE_NAME };
|
||||
import { t } from './i18n.js';
|
||||
import { MacrosParser } from './macros.js';
|
||||
|
||||
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
|
||||
|
||||
@@ -300,7 +299,7 @@ function loadSettings() {
|
||||
$('#extension_floating_role').val(chat_metadata[metadata_keys.role]);
|
||||
$(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
|
||||
|
||||
if (extension_settings.note.chara && getContext().characterId !== undefined) {
|
||||
if (extension_settings.note.chara && getContext().characterId) {
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
$('#extension_floating_chara').val(charaNote ? charaNote.prompt : '');
|
||||
@@ -390,49 +389,49 @@ export function setFloatingPrompt() {
|
||||
}
|
||||
|
||||
function onANMenuItemClick() {
|
||||
if (!selected_group && this_chid === undefined) {
|
||||
toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 });
|
||||
return;
|
||||
}
|
||||
|
||||
//show AN if it's hidden
|
||||
if ($('#floatingPrompt').css('display') !== 'flex') {
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#floatingPrompt').css('display', 'flex');
|
||||
$('#floatingPrompt').css('opacity', 0.0);
|
||||
$('#floatingPrompt').transition({
|
||||
opacity: 1.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#floatingPrompt').removeClass('resizing');
|
||||
});
|
||||
|
||||
//auto-open the main AN inline drawer
|
||||
if ($('#ANBlockToggle')
|
||||
.siblings('.inline-drawer-content')
|
||||
.css('display') !== 'block') {
|
||||
if (selected_group || this_chid) {
|
||||
//show AN if it's hidden
|
||||
if ($('#floatingPrompt').css('display') !== 'flex') {
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#ANBlockToggle').click();
|
||||
}
|
||||
} else {
|
||||
//hide AN if it's already displayed
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#floatingPrompt').transition({
|
||||
opacity: 0.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#floatingPrompt').removeClass('resizing');
|
||||
});
|
||||
setTimeout(function () {
|
||||
$('#floatingPrompt').hide();
|
||||
}, animation_duration);
|
||||
}
|
||||
$('#floatingPrompt').css('display', 'flex');
|
||||
$('#floatingPrompt').css('opacity', 0.0);
|
||||
$('#floatingPrompt').transition({
|
||||
opacity: 1.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#floatingPrompt').removeClass('resizing');
|
||||
});
|
||||
|
||||
//duplicate options menu close handler from script.js
|
||||
//because this listener takes priority
|
||||
$('#options').stop().fadeOut(animation_duration);
|
||||
//auto-open the main AN inline drawer
|
||||
if ($('#ANBlockToggle')
|
||||
.siblings('.inline-drawer-content')
|
||||
.css('display') !== 'block') {
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#ANBlockToggle').click();
|
||||
}
|
||||
} else {
|
||||
//hide AN if it's already displayed
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#floatingPrompt').transition({
|
||||
opacity: 0.0,
|
||||
duration: animation_duration,
|
||||
},
|
||||
async function () {
|
||||
await delay(50);
|
||||
$('#floatingPrompt').removeClass('resizing');
|
||||
});
|
||||
setTimeout(function () {
|
||||
$('#floatingPrompt').hide();
|
||||
}, animation_duration);
|
||||
|
||||
}
|
||||
//duplicate options menu close handler from script.js
|
||||
//because this listener takes priority
|
||||
$('#options').stop().fadeOut(animation_duration);
|
||||
} else {
|
||||
toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function onChatChanged() {
|
||||
@@ -447,7 +446,7 @@ async function onChatChanged() {
|
||||
$('#extension_floating_prompt_token_counter').text(tokenCounter1);
|
||||
|
||||
let tokenCounter2;
|
||||
if (extension_settings.note.chara && context.characterId !== undefined) {
|
||||
if (extension_settings.note.chara && context.characterId) {
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
if (charaNote) {
|
||||
@@ -577,8 +576,4 @@ export function initAuthorsNote() {
|
||||
`,
|
||||
}));
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
|
||||
MacrosParser.registerMacro('authorsNote', () => chat_metadata[metadata_keys.prompt] ?? '', t`The contents of the Author's Note`);
|
||||
MacrosParser.registerMacro('charAuthorsNote', () => this_chid !== undefined ? (extension_settings.note.chara.find((e) => e.name === getCharaFilename())?.prompt ?? '') : '', t`The contents of the Character Author's Note`);
|
||||
MacrosParser.registerMacro('defaultAuthorsNote', () => extension_settings.note.default ?? '', t`The contents of the Default Author's Note`);
|
||||
}
|
||||
|
@@ -3,8 +3,10 @@ import { debounce, escapeRegex } from '../utils.js';
|
||||
import { AutoCompleteOption } from './AutoCompleteOption.js';
|
||||
import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
|
||||
import { BlankAutoCompleteOption } from './BlankAutoCompleteOption.js';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { AutoCompleteNameResult } from './AutoCompleteNameResult.js';
|
||||
import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';
|
||||
import { Popup, getTopmostModalLayer } from '../popup.js';
|
||||
|
||||
/**@readonly*/
|
||||
/**@enum {Number}*/
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { SlashCommandNamedArgumentAutoCompleteOption } from '../slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js';
|
||||
import { AutoCompleteOption } from './AutoCompleteOption.js';
|
||||
|
||||
|
||||
|
@@ -1,18 +1,19 @@
|
||||
import { SlashCommand } from '../slash-commands/SlashCommand.js';
|
||||
import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
|
||||
|
||||
|
||||
|
||||
export class AutoCompleteOption {
|
||||
/** @type {string} */ name;
|
||||
/** @type {string} */ typeIcon;
|
||||
/** @type {string} */ type;
|
||||
/** @type {number} */ nameOffset = 0;
|
||||
/** @type {AutoCompleteFuzzyScore} */ score;
|
||||
/** @type {string} */ replacer;
|
||||
/** @type {HTMLElement} */ dom;
|
||||
/** @type {(input:string)=>boolean} */ matchProvider;
|
||||
/** @type {(input:string)=>string} */ valueProvider;
|
||||
/** @type {boolean} */ makeSelectable = false;
|
||||
/**@type {string}*/ name;
|
||||
/**@type {string}*/ typeIcon;
|
||||
/**@type {string}*/ type;
|
||||
/**@type {number}*/ nameOffset = 0;
|
||||
/**@type {AutoCompleteFuzzyScore}*/ score;
|
||||
/**@type {string}*/ replacer;
|
||||
/**@type {HTMLElement}*/ dom;
|
||||
/**@type {(input:string)=>boolean}*/ matchProvider;
|
||||
/**@type {(input:string)=>string}*/ valueProvider;
|
||||
/**@type {boolean}*/ makeSelectable = false;
|
||||
|
||||
|
||||
/**
|
||||
|
@@ -5,7 +5,6 @@ import { saveMetadataDebounced } from './extensions.js';
|
||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { flashHighlight, stringFormat } from './utils.js';
|
||||
import { t } from './i18n.js';
|
||||
|
||||
const BG_METADATA_KEY = 'custom_background';
|
||||
const LIST_METADATA_KEY = 'chat_backgrounds';
|
||||
@@ -244,7 +243,7 @@ async function getNewBackgroundName(referenceElement) {
|
||||
const fileExtension = oldBg.split('.').pop();
|
||||
const fileNameBase = isCustom ? oldBg.split('/').pop() : oldBg;
|
||||
const oldBgExtensionless = fileNameBase.replace(`.${fileExtension}`, '');
|
||||
const newBgExtensionless = await callPopup('<h3>' + t`Enter new background name:` + '</h3>', 'input', oldBgExtensionless);
|
||||
const newBgExtensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', oldBgExtensionless);
|
||||
|
||||
if (!newBgExtensionless) {
|
||||
console.debug('no new_bg_extensionless');
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
characters,
|
||||
saveChat,
|
||||
system_messages,
|
||||
system_message_types,
|
||||
this_chid,
|
||||
openCharacterChat,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
saveChatConditional,
|
||||
saveItemizedPrompts,
|
||||
} from '../script.js';
|
||||
import { humanizedDateTime } from './RossAscends-mods.js';
|
||||
import { humanizedDateTime, getMessageTimeStamp } from './RossAscends-mods.js';
|
||||
import {
|
||||
getGroupPastChats,
|
||||
group_activation_strategy,
|
||||
@@ -155,7 +156,7 @@ export async function createBranch(mesId) {
|
||||
if (selected_group) {
|
||||
await saveGroupBookmarkChat(selected_group, name, newMetadata, mesId);
|
||||
} else {
|
||||
await saveChat({ chatName: name, withMetadata: newMetadata, mesId });
|
||||
await saveChat(name, newMetadata, mesId);
|
||||
}
|
||||
// append to branches list if it exists
|
||||
// otherwise create it
|
||||
@@ -211,7 +212,7 @@ export async function createNewBookmark(mesId, { forceName = null } = {}) {
|
||||
if (selected_group) {
|
||||
await saveGroupBookmarkChat(selected_group, name, newMetadata, mesId);
|
||||
} else {
|
||||
await saveChat({ chatName: name, withMetadata: newMetadata, mesId });
|
||||
await saveChat(name, newMetadata, mesId);
|
||||
}
|
||||
|
||||
lastMes.extra['bookmark_link'] = name;
|
||||
@@ -317,6 +318,16 @@ export async function convertSoloToGroupChat() {
|
||||
const groupChat = chat.slice();
|
||||
const genIdFirst = Date.now();
|
||||
|
||||
// Add something if the chat is empty
|
||||
if (groupChat.length === 0) {
|
||||
const newMessage = {
|
||||
...system_messages[system_message_types.GROUP],
|
||||
send_date: getMessageTimeStamp(),
|
||||
extra: { type: system_message_types.GROUP },
|
||||
};
|
||||
groupChat.push(newMessage);
|
||||
}
|
||||
|
||||
for (let index = 0; index < groupChat.length; index++) {
|
||||
const message = groupChat[index];
|
||||
|
||||
@@ -357,7 +368,7 @@ export async function convertSoloToGroupChat() {
|
||||
// Click on the freshly selected group to open it
|
||||
await openGroupById(group.id);
|
||||
|
||||
toastr.success(t`The chat has been successfully converted!`);
|
||||
toastr.success('The chat has been successfully converted!');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -40,7 +40,7 @@ function setCharCfg(tempValue, setting) {
|
||||
name: avatarName,
|
||||
};
|
||||
|
||||
switch (setting) {
|
||||
switch(setting) {
|
||||
case settingType.guidance_scale:
|
||||
tempCharaCfg['guidance_scale'] = Number(tempValue);
|
||||
break;
|
||||
@@ -69,7 +69,8 @@ function setCharCfg(tempValue, setting) {
|
||||
if (!existingCharaCfg.useChara &&
|
||||
(tempAssign.guidance_scale ?? 1.00) === 1.00 &&
|
||||
(tempAssign.negative_prompt?.length ?? 0) === 0 &&
|
||||
(tempAssign.positive_prompt?.length ?? 0) === 0) {
|
||||
(tempAssign.positive_prompt?.length ?? 0) === 0)
|
||||
{
|
||||
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
||||
}
|
||||
} else if (avatarName && tempValue.length > 0) {
|
||||
@@ -91,7 +92,7 @@ function setCharCfg(tempValue, setting) {
|
||||
}
|
||||
|
||||
function setChatCfg(tempValue, setting) {
|
||||
switch (setting) {
|
||||
switch(setting) {
|
||||
case settingType.guidance_scale:
|
||||
chat_metadata[metadataKeys.guidance_scale] = tempValue;
|
||||
break;
|
||||
@@ -112,49 +113,49 @@ function setChatCfg(tempValue, setting) {
|
||||
|
||||
// TODO: Only change CFG when character is selected
|
||||
function onCfgMenuItemClick() {
|
||||
if (!selected_group && this_chid === undefined) {
|
||||
toastr.warning('Select a character before trying to configure CFG', '', { timeOut: 2000 });
|
||||
return;
|
||||
}
|
||||
if (selected_group || this_chid) {
|
||||
//show CFG config if it's hidden
|
||||
if ($('#cfgConfig').css('display') !== 'flex') {
|
||||
$('#cfgConfig').addClass('resizing');
|
||||
$('#cfgConfig').css('display', 'flex');
|
||||
$('#cfgConfig').css('opacity', 0.0);
|
||||
$('#cfgConfig').transition({
|
||||
opacity: 1.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#cfgConfig').removeClass('resizing');
|
||||
});
|
||||
|
||||
//show CFG config if it's hidden
|
||||
if ($('#cfgConfig').css('display') !== 'flex') {
|
||||
$('#cfgConfig').addClass('resizing');
|
||||
$('#cfgConfig').css('display', 'flex');
|
||||
$('#cfgConfig').css('opacity', 0.0);
|
||||
$('#cfgConfig').transition({
|
||||
opacity: 1.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#cfgConfig').removeClass('resizing');
|
||||
});
|
||||
//auto-open the main AN inline drawer
|
||||
if ($('#CFGBlockToggle')
|
||||
.siblings('.inline-drawer-content')
|
||||
.css('display') !== 'block') {
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#CFGBlockToggle').click();
|
||||
}
|
||||
} else {
|
||||
//hide AN if it's already displayed
|
||||
$('#cfgConfig').addClass('resizing');
|
||||
$('#cfgConfig').transition({
|
||||
opacity: 0.0,
|
||||
duration: animation_duration,
|
||||
},
|
||||
async function () {
|
||||
await delay(50);
|
||||
$('#cfgConfig').removeClass('resizing');
|
||||
});
|
||||
setTimeout(function () {
|
||||
$('#cfgConfig').hide();
|
||||
}, animation_duration);
|
||||
|
||||
//auto-open the main AN inline drawer
|
||||
if ($('#CFGBlockToggle')
|
||||
.siblings('.inline-drawer-content')
|
||||
.css('display') !== 'block') {
|
||||
$('#floatingPrompt').addClass('resizing');
|
||||
$('#CFGBlockToggle').click();
|
||||
}
|
||||
//duplicate options menu close handler from script.js
|
||||
//because this listener takes priority
|
||||
$('#options').stop().fadeOut(animation_duration);
|
||||
} else {
|
||||
//hide AN if it's already displayed
|
||||
$('#cfgConfig').addClass('resizing');
|
||||
$('#cfgConfig').transition({
|
||||
opacity: 0.0,
|
||||
duration: animation_duration,
|
||||
}, async function () {
|
||||
await delay(50);
|
||||
$('#cfgConfig').removeClass('resizing');
|
||||
});
|
||||
setTimeout(function () {
|
||||
$('#cfgConfig').hide();
|
||||
}, animation_duration);
|
||||
|
||||
toastr.warning('Select a character before trying to configure CFG', '', { timeOut: 2000 });
|
||||
}
|
||||
//duplicate options menu close handler from script.js
|
||||
//because this listener takes priority
|
||||
$('#options').stop().fadeOut(animation_duration);
|
||||
}
|
||||
|
||||
async function onChatChanged() {
|
||||
@@ -287,7 +288,7 @@ export function initCfg() {
|
||||
setTimeout(function () { $('#cfgConfig').hide(); }, animation_duration);
|
||||
});
|
||||
|
||||
$('#chat_cfg_guidance_scale').on('input', function () {
|
||||
$('#chat_cfg_guidance_scale').on('input', function() {
|
||||
const numberValue = Number($(this).val());
|
||||
const success = setChatCfg(numberValue, settingType.guidance_scale);
|
||||
if (success) {
|
||||
@@ -295,15 +296,15 @@ export function initCfg() {
|
||||
}
|
||||
});
|
||||
|
||||
$('#chat_cfg_negative_prompt').on('input', function () {
|
||||
$('#chat_cfg_negative_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
$('#chat_cfg_positive_prompt').on('input', function () {
|
||||
$('#chat_cfg_positive_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
$('#chara_cfg_guidance_scale').on('input', function () {
|
||||
$('#chara_cfg_guidance_scale').on('input', function() {
|
||||
const value = $(this).val();
|
||||
const success = setCharCfg(value, settingType.guidance_scale);
|
||||
if (success) {
|
||||
@@ -311,34 +312,34 @@ export function initCfg() {
|
||||
}
|
||||
});
|
||||
|
||||
$('#chara_cfg_negative_prompt').on('input', function () {
|
||||
$('#chara_cfg_negative_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
$('#chara_cfg_positive_prompt').on('input', function () {
|
||||
$('#chara_cfg_positive_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
$('#global_cfg_guidance_scale').on('input', function () {
|
||||
$('#global_cfg_guidance_scale').on('input', function() {
|
||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||
$('#global_cfg_guidance_scale_counter').val(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#global_cfg_negative_prompt').on('input', function () {
|
||||
$('#global_cfg_negative_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.negative_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#global_cfg_positive_prompt').on('input', function () {
|
||||
$('#global_cfg_positive_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.positive_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('input[name="cfg_prompt_combine"]').on('input', function () {
|
||||
$('input[name="cfg_prompt_combine"]').on('input', function() {
|
||||
const values = $('#cfgConfig').find('input[name="cfg_prompt_combine"]')
|
||||
.filter(':checked')
|
||||
.map(function () { return Number($(this).val()); })
|
||||
.map(function() { return Number($(this).val()); })
|
||||
.get()
|
||||
.filter((e) => !Number.isNaN(e)) || [];
|
||||
|
||||
@@ -346,17 +347,17 @@ export function initCfg() {
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
$('#cfg_prompt_insertion_depth').on('input', function () {
|
||||
$('#cfg_prompt_insertion_depth').on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_insertion_depth] = Number($(this).val());
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
$('#cfg_prompt_separator').on('input', function () {
|
||||
$('#cfg_prompt_separator').on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_separator] = $(this).val();
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
$('#groupchat_cfg_use_chara').on('input', function () {
|
||||
$('#groupchat_cfg_use_chara').on('input', function() {
|
||||
const checked = !!$(this).prop('checked');
|
||||
chat_metadata[metadataKeys.groupchat_individual_chars] = checked;
|
||||
|
||||
|
@@ -113,6 +113,5 @@
|
||||
* @property {string} chat - name of the current chat file chat
|
||||
* @property {string} avatar - file name of the avatar image (acts as a unique identifier)
|
||||
* @property {string} json_data - the full raw JSON data of the character
|
||||
* @property {boolean?} shallow - if the data is shallow (lazy-loaded)
|
||||
*/
|
||||
export default 0;// now this file is a module
|
||||
|
@@ -47,10 +47,6 @@ const hash_derivations = {
|
||||
// gemma-2-2b-it
|
||||
'Gemma 2'
|
||||
,
|
||||
'7de1c58e208eda46e9c7f86397df37ec49883aeece39fb961e0a6b24088dd3c4':
|
||||
// gemma-3
|
||||
'Gemma 2'
|
||||
,
|
||||
|
||||
// Cohere
|
||||
'3b54f5c219ae1caa5c0bb2cdc7c001863ca6807cf888e4240e8739fa7eb9e02e':
|
||||
|
@@ -45,7 +45,6 @@ import { DragAndDropHandler } from './dragdrop.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
import { t } from './i18n.js';
|
||||
import { humanizedDateTime } from './RossAscends-mods.js';
|
||||
import { accountStorage } from './util/AccountStorage.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileAttachment
|
||||
@@ -130,10 +129,9 @@ function getConverter(type) {
|
||||
* @param {number} start Starting message ID
|
||||
* @param {number} end Ending message ID (inclusive)
|
||||
* @param {boolean} unhide If true, unhide the messages instead.
|
||||
* @param {string} nameFitler Optional name filter
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function hideChatMessageRange(start, end, unhide, nameFitler = null) {
|
||||
export async function hideChatMessageRange(start, end, unhide) {
|
||||
if (isNaN(start)) return;
|
||||
if (!end) end = start;
|
||||
const hide = !unhide;
|
||||
@@ -141,7 +139,6 @@ export async function hideChatMessageRange(start, end, unhide, nameFitler = null
|
||||
for (let messageId = start; messageId <= end; messageId++) {
|
||||
const message = chat[messageId];
|
||||
if (!message) continue;
|
||||
if (nameFitler && message.name !== nameFitler) continue;
|
||||
|
||||
message.is_system = hide;
|
||||
|
||||
@@ -459,7 +456,7 @@ export async function appendFileContent(message, messageText) {
|
||||
* @copyright https://github.com/kwaroran/risuAI
|
||||
*/
|
||||
export function encodeStyleTags(text) {
|
||||
const styleRegex = /<style>(.+?)<\/style>/gims;
|
||||
const styleRegex = /<style>(.+?)<\/style>/gms;
|
||||
return text.replaceAll(styleRegex, (_, match) => {
|
||||
return `<custom-style>${escape(match)}</custom-style>`;
|
||||
});
|
||||
@@ -575,8 +572,8 @@ export function isExternalMediaAllowed() {
|
||||
return !power_user.forbid_external_media;
|
||||
}
|
||||
|
||||
function expandMessageImage(event) {
|
||||
const mesBlock = $(event.currentTarget).closest('.mes');
|
||||
async function enlargeMessageImage() {
|
||||
const mesBlock = $(this).closest('.mes');
|
||||
const mesId = mesBlock.attr('mesid');
|
||||
const message = chat[mesId];
|
||||
const imgSrc = message?.extra?.image;
|
||||
@@ -620,65 +617,25 @@ function expandMessageImage(event) {
|
||||
popup.completeCancelled();
|
||||
});
|
||||
|
||||
popup.show();
|
||||
return img;
|
||||
}
|
||||
|
||||
function expandAndZoomMessageImage(event) {
|
||||
expandMessageImage(event).click();
|
||||
await popup.show();
|
||||
}
|
||||
|
||||
async function deleteMessageImage() {
|
||||
const value = await callGenericPopup('<h3>Delete image from message?<br>This action can\'t be undone.</h3>', POPUP_TYPE.TEXT, '', {
|
||||
okButton: t`Delete one`,
|
||||
customButtons: [
|
||||
{
|
||||
text: t`Delete all`,
|
||||
appendAtEnd: true,
|
||||
result: POPUP_RESULT.CUSTOM1,
|
||||
},
|
||||
{
|
||||
text: t`Cancel`,
|
||||
appendAtEnd: true,
|
||||
result: POPUP_RESULT.CANCELLED,
|
||||
},
|
||||
],
|
||||
});
|
||||
const value = await callGenericPopup('<h3>Delete image from message?<br>This action can\'t be undone.</h3>', POPUP_TYPE.CONFIRM);
|
||||
|
||||
if (!value) {
|
||||
if (value !== POPUP_RESULT.AFFIRMATIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mesBlock = $(this).closest('.mes');
|
||||
const mesId = mesBlock.attr('mesid');
|
||||
const message = chat[mesId];
|
||||
|
||||
let isLastImage = true;
|
||||
|
||||
if (Array.isArray(message.extra.image_swipes)) {
|
||||
const indexOf = message.extra.image_swipes.indexOf(message.extra.image);
|
||||
if (indexOf > -1) {
|
||||
message.extra.image_swipes.splice(indexOf, 1);
|
||||
isLastImage = message.extra.image_swipes.length === 0;
|
||||
if (!isLastImage) {
|
||||
const newIndex = Math.min(indexOf, message.extra.image_swipes.length - 1);
|
||||
message.extra.image = message.extra.image_swipes[newIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLastImage || value === POPUP_RESULT.CUSTOM1) {
|
||||
delete message.extra.image;
|
||||
delete message.extra.inline_image;
|
||||
delete message.extra.title;
|
||||
delete message.extra.append_title;
|
||||
delete message.extra.image_swipes;
|
||||
mesBlock.find('.mes_img_container').removeClass('img_extra');
|
||||
mesBlock.find('.mes_img').attr('src', '');
|
||||
} else {
|
||||
appendMediaToMessage(message, mesBlock);
|
||||
}
|
||||
|
||||
delete message.extra.image;
|
||||
delete message.extra.inline_image;
|
||||
delete message.extra.title;
|
||||
delete message.extra.append_title;
|
||||
mesBlock.find('.mes_img_container').removeClass('img_extra');
|
||||
mesBlock.find('.mes_img').attr('src', '');
|
||||
await saveChatConditional();
|
||||
}
|
||||
|
||||
@@ -1086,8 +1043,8 @@ async function openAttachmentManager() {
|
||||
renderAttachments();
|
||||
});
|
||||
|
||||
let sortField = accountStorage.getItem('DataBank_sortField') || 'created';
|
||||
let sortOrder = accountStorage.getItem('DataBank_sortOrder') || 'desc';
|
||||
let sortField = localStorage.getItem('DataBank_sortField') || 'created';
|
||||
let sortOrder = localStorage.getItem('DataBank_sortOrder') || 'desc';
|
||||
let filterString = '';
|
||||
|
||||
const template = $(await renderExtensionTemplateAsync('attachments', 'manager', {}));
|
||||
@@ -1103,8 +1060,8 @@ async function openAttachmentManager() {
|
||||
|
||||
sortField = this.selectedOptions[0].dataset.sortField;
|
||||
sortOrder = this.selectedOptions[0].dataset.sortOrder;
|
||||
accountStorage.setItem('DataBank_sortField', sortField);
|
||||
accountStorage.setItem('DataBank_sortOrder', sortOrder);
|
||||
localStorage.setItem('DataBank_sortField', sortField);
|
||||
localStorage.setItem('DataBank_sortOrder', sortOrder);
|
||||
renderAttachments();
|
||||
});
|
||||
function handleBulkAction(action) {
|
||||
@@ -1494,7 +1451,7 @@ jQuery(function () {
|
||||
...chat.filter(x => x?.extra?.type !== system_message_types.ASSISTANT_NOTE),
|
||||
];
|
||||
|
||||
download(chatToSave.map((m) => JSON.stringify(m)).join('\n'), `Assistant - ${humanizedDateTime()}.jsonl`, 'application/json');
|
||||
download(JSON.stringify(chatToSave, null, 4), `Assistant - ${humanizedDateTime()}.json`, 'application/json');
|
||||
});
|
||||
|
||||
// Do not change. #attachFile is added by extension.
|
||||
@@ -1513,7 +1470,7 @@ jQuery(function () {
|
||||
embedMessageFile(messageId, messageBlock);
|
||||
});
|
||||
|
||||
$(document).on('click', '.editor_maximize', async function () {
|
||||
$(document).on('click', '.editor_maximize', function () {
|
||||
const broId = $(this).attr('data-for');
|
||||
const bro = $(`#${broId}`);
|
||||
const contentEditable = bro.is('[contenteditable]');
|
||||
@@ -1532,7 +1489,6 @@ jQuery(function () {
|
||||
textarea.value = String(contentEditable ? bro[0].innerText : bro.val());
|
||||
textarea.classList.add('height100p', 'wide100p', 'maximized_textarea');
|
||||
bro.hasClass('monospace') && textarea.classList.add('monospace');
|
||||
bro.hasClass('mdHotkeys') && textarea.classList.add('mdHotkeys');
|
||||
textarea.addEventListener('input', function () {
|
||||
if (contentEditable) {
|
||||
bro[0].innerText = textarea.value;
|
||||
@@ -1573,7 +1529,7 @@ jQuery(function () {
|
||||
});
|
||||
}
|
||||
|
||||
await callGenericPopup(wrapper, POPUP_TYPE.TEXT, '', { wide: true, large: true });
|
||||
callGenericPopup(wrapper, POPUP_TYPE.TEXT, '', { wide: true, large: true });
|
||||
});
|
||||
|
||||
$(document).on('click', 'body.documentstyle .mes .mes_text', function () {
|
||||
@@ -1608,8 +1564,7 @@ jQuery(function () {
|
||||
reloadCurrentChat();
|
||||
});
|
||||
|
||||
$(document).on('click', '.mes_img', expandMessageImage);
|
||||
$(document).on('click', '.mes_img_enlarge', expandAndZoomMessageImage);
|
||||
$(document).on('click', '.mes_img_enlarge', enlargeMessageImage);
|
||||
$(document).on('click', '.mes_img_delete', deleteMessageImage);
|
||||
|
||||
$('#file_form_input').on('change', async () => {
|
||||
|
@@ -14,11 +14,3 @@ export const debounce_timeout = {
|
||||
/** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
|
||||
extended: 5000,
|
||||
};
|
||||
|
||||
/**
|
||||
* Used as an ephemeral key in message extra metadata.
|
||||
* When set, the message will be excluded from generation
|
||||
* prompts without affecting the number of chat messages,
|
||||
* which is needed to preserve world info timed effects.
|
||||
*/
|
||||
export const IGNORE_SYMBOL = Symbol.for('ignore');
|
||||
|
@@ -1,558 +0,0 @@
|
||||
import { getPresetManager } from './preset-manager.js';
|
||||
import { extractMessageFromData, getGenerateUrl, getRequestHeaders } from '../script.js';
|
||||
import { getTextGenServer } from './textgen-settings.js';
|
||||
import { extractReasoningFromData } from './reasoning.js';
|
||||
import { formatInstructModeChat, formatInstructModePrompt, getInstructStoppingSequences, names_behavior_types } from './instruct-mode.js';
|
||||
import { getStreamingReply, tryParseStreamingError } from './openai.js';
|
||||
import EventSourceStream from './sse-stream.js';
|
||||
|
||||
// #region Type Definitions
|
||||
/**
|
||||
* @typedef {Object} TextCompletionRequestBase
|
||||
* @property {boolean?} [stream=false] - Whether to stream the response
|
||||
* @property {number} max_tokens - Maximum number of tokens to generate
|
||||
* @property {string} [model] - Optional model name
|
||||
* @property {string} api_type - Type of API to use
|
||||
* @property {string} [api_server] - Optional API server URL
|
||||
* @property {number} [temperature] - Optional temperature parameter
|
||||
* @property {number} [min_p] - Optional min_p parameter
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextCompletionPayloadBase
|
||||
* @property {boolean?} [stream=false] - Whether to stream the response
|
||||
* @property {string} prompt - The text prompt for completion
|
||||
* @property {number} max_tokens - Maximum number of tokens to generate
|
||||
* @property {number} max_new_tokens - Alias for max_tokens
|
||||
* @property {string} [model] - Optional model name
|
||||
* @property {string} api_type - Type of API to use
|
||||
* @property {string} api_server - API server URL
|
||||
* @property {number} [temperature] - Optional temperature parameter
|
||||
*/
|
||||
|
||||
/** @typedef {Record<string, any> & TextCompletionPayloadBase} TextCompletionPayload */
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChatCompletionMessage
|
||||
* @property {string} role - The role of the message author (e.g., "user", "assistant", "system")
|
||||
* @property {string} content - The content of the message
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChatCompletionPayloadBase
|
||||
* @property {boolean?} [stream=false] - Whether to stream the response
|
||||
* @property {ChatCompletionMessage[]} messages - Array of chat messages
|
||||
* @property {string} [model] - Optional model name to use for completion
|
||||
* @property {string} chat_completion_source - Source provider
|
||||
* @property {number} max_tokens - Maximum number of tokens to generate
|
||||
* @property {number} [temperature] - Optional temperature parameter for response randomness
|
||||
* @property {string} [custom_url] - Optional custom URL
|
||||
* @property {string} [reverse_proxy] - Optional reverse proxy URL
|
||||
* @property {string} [proxy_password] - Optional proxy password
|
||||
*/
|
||||
|
||||
/** @typedef {Record<string, any> & ChatCompletionPayloadBase} ChatCompletionPayload */
|
||||
|
||||
/**
|
||||
* @typedef {Object} ExtractedData
|
||||
* @property {string} content - Extracted content.
|
||||
* @property {string} reasoning - Extracted reasoning.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} StreamResponse
|
||||
* @property {string} text - Generated text.
|
||||
* @property {string[]} swipes - Generated swipes
|
||||
* @property {Object} state - Generated state
|
||||
* @property {string?} [state.reasoning] - Generated reasoning
|
||||
* @property {string?} [state.image] - Generated image
|
||||
*/
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* Creates & sends a text completion request.
|
||||
*/
|
||||
export class TextCompletionService {
|
||||
static TYPE = 'textgenerationwebui';
|
||||
|
||||
/**
|
||||
* @param {Record<string, any> & TextCompletionRequestBase & {prompt: string}} custom
|
||||
* @returns {TextCompletionPayload}
|
||||
*/
|
||||
static createRequestData({ stream = false, prompt, max_tokens, model, api_type, api_server, temperature, min_p, ...props }) {
|
||||
const payload = {
|
||||
stream,
|
||||
prompt,
|
||||
max_tokens,
|
||||
max_new_tokens: max_tokens,
|
||||
model,
|
||||
api_type,
|
||||
api_server: api_server ?? getTextGenServer(api_type),
|
||||
temperature,
|
||||
min_p,
|
||||
...props,
|
||||
};
|
||||
|
||||
// Remove undefined values to avoid API errors
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (payload[key] === undefined) {
|
||||
delete payload[key];
|
||||
}
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a text completion request to the specified server
|
||||
* @param {TextCompletionPayload} data Request data
|
||||
* @param {boolean?} extractData Extract message from the response. Default true
|
||||
* @param {AbortSignal?} signal
|
||||
* @returns {Promise<ExtractedData | (() => AsyncGenerator<StreamResponse>)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator
|
||||
* @throws {Error}
|
||||
*/
|
||||
static async sendRequest(data, extractData = true, signal = null) {
|
||||
if (!data.stream) {
|
||||
const response = await fetch(getGenerateUrl(this.TYPE), {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify(data),
|
||||
signal: signal ?? new AbortController().signal,
|
||||
});
|
||||
|
||||
const json = await response.json();
|
||||
if (!response.ok || json.error) {
|
||||
throw json;
|
||||
}
|
||||
|
||||
if (!extractData) {
|
||||
return json;
|
||||
}
|
||||
|
||||
return {
|
||||
content: extractMessageFromData(json, this.TYPE),
|
||||
reasoning: extractReasoningFromData(json, {
|
||||
mainApi: this.TYPE,
|
||||
textGenType: data.api_type,
|
||||
ignoreShowThoughts: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch('/api/backends/text-completions/generate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify(data),
|
||||
signal: signal ?? new AbortController().signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
tryParseStreamingError(response, text, { quiet: true });
|
||||
|
||||
throw new Error(`Got response status ${response.status}`);
|
||||
}
|
||||
|
||||
const eventStream = new EventSourceStream();
|
||||
response.body.pipeThrough(eventStream);
|
||||
const reader = eventStream.readable.getReader();
|
||||
return async function* streamData() {
|
||||
let text = '';
|
||||
const swipes = [];
|
||||
const state = { reasoning: '' };
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) return;
|
||||
if (value.data === '[DONE]') return;
|
||||
|
||||
tryParseStreamingError(response, value.data, { quiet: true });
|
||||
|
||||
let data = JSON.parse(value.data);
|
||||
|
||||
if (data?.choices?.[0]?.index > 0) {
|
||||
const swipeIndex = data.choices[0].index - 1;
|
||||
swipes[swipeIndex] = (swipes[swipeIndex] || '') + data.choices[0].text;
|
||||
} else {
|
||||
const newText = data?.choices?.[0]?.text || data?.content || '';
|
||||
text += newText;
|
||||
state.reasoning += data?.choices?.[0]?.reasoning ?? '';
|
||||
}
|
||||
|
||||
yield { text, swipes, state };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and send a text completion request with optional preset & instruct
|
||||
* @param {Record<string, any> & TextCompletionRequestBase & {prompt: (ChatCompletionMessage & {ignoreInstruct?: boolean})[] |string}} custom
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {string?} [options.presetName] - Name of the preset to use for generation settings
|
||||
* @param {string?} [options.instructName] - Name of instruct preset for message formatting
|
||||
* @param {Partial<InstructSettings>?} [options.instructSettings] - Override instruct settings
|
||||
* @param {boolean} extractData - Whether to extract structured data from response
|
||||
* @param {AbortSignal?} [signal]
|
||||
* @returns {Promise<ExtractedData | (() => AsyncGenerator<StreamResponse>)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator
|
||||
* @throws {Error}
|
||||
*/
|
||||
static async processRequest(
|
||||
custom,
|
||||
options = {},
|
||||
extractData = true,
|
||||
signal = null,
|
||||
) {
|
||||
const { presetName, instructName } = options;
|
||||
let requestData = { ...custom };
|
||||
const prompt = custom.prompt;
|
||||
|
||||
// Apply generation preset if specified
|
||||
if (presetName) {
|
||||
const presetManager = getPresetManager(this.TYPE);
|
||||
if (presetManager) {
|
||||
const preset = presetManager.getCompletionPresetByName(presetName);
|
||||
if (preset) {
|
||||
// Convert preset to payload and merge with custom parameters
|
||||
const presetPayload = this.presetToGeneratePayload(preset, {});
|
||||
requestData = { ...presetPayload, ...requestData };
|
||||
} else {
|
||||
console.warn(`Preset "${presetName}" not found, continuing with default settings`);
|
||||
}
|
||||
} else {
|
||||
console.warn('Preset manager not found, continuing with default settings');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @type {InstructSettings | undefined} */
|
||||
let instructPreset;
|
||||
// Handle instruct formatting if requested
|
||||
if (Array.isArray(prompt) && instructName) {
|
||||
const instructPresetManager = getPresetManager('instruct');
|
||||
instructPreset = instructPresetManager?.getCompletionPresetByName(instructName);
|
||||
if (instructPreset) {
|
||||
// Clone the preset to avoid modifying the original
|
||||
instructPreset = structuredClone(instructPreset);
|
||||
instructPreset.names_behavior = names_behavior_types.NONE;
|
||||
if (options.instructSettings) {
|
||||
Object.assign(instructPreset, options.instructSettings);
|
||||
}
|
||||
|
||||
// Format messages using instruct formatting
|
||||
const formattedMessages = [];
|
||||
for (const message of prompt) {
|
||||
let messageContent = message.content;
|
||||
if (!message.ignoreInstruct) {
|
||||
messageContent = formatInstructModeChat(
|
||||
message.role,
|
||||
message.content,
|
||||
message.role === 'user',
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
instructPreset,
|
||||
);
|
||||
|
||||
// Add prompt formatting for the last message
|
||||
if (message === prompt[prompt.length - 1]) {
|
||||
messageContent += formatInstructModePrompt(
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
instructPreset,
|
||||
);
|
||||
}
|
||||
}
|
||||
formattedMessages.push(messageContent);
|
||||
}
|
||||
requestData.prompt = formattedMessages.join('');
|
||||
const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopStrings: false });
|
||||
requestData.stop = stoppingStrings;
|
||||
requestData.stopping_strings = stoppingStrings;
|
||||
} else {
|
||||
console.warn(`Instruct preset "${instructName}" not found, using basic formatting`);
|
||||
requestData.prompt = prompt.map(x => x.content).join('\n\n');
|
||||
}
|
||||
} else if (typeof prompt === 'string') {
|
||||
requestData.prompt = prompt;
|
||||
} else {
|
||||
requestData.prompt = prompt.map(x => x.content).join('\n\n');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const data = this.createRequestData(requestData);
|
||||
|
||||
const response = await this.sendRequest(data, extractData, signal);
|
||||
// Remove stopping strings from the end
|
||||
if (!data.stream && extractData) {
|
||||
/** @type {ExtractedData} */
|
||||
// @ts-ignore
|
||||
const extractedData = response;
|
||||
|
||||
let message = extractedData.content;
|
||||
|
||||
message = message.replace(/[^\S\r\n]+$/gm, '');
|
||||
|
||||
if (requestData.stopping_strings) {
|
||||
for (const stoppingString of requestData.stopping_strings) {
|
||||
if (stoppingString.length) {
|
||||
for (let j = stoppingString.length; j > 0; j--) {
|
||||
if (message.slice(-j) === stoppingString.slice(0, j)) {
|
||||
message = message.slice(0, -j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instructPreset) {
|
||||
[
|
||||
instructPreset.stop_sequence,
|
||||
instructPreset.input_sequence,
|
||||
].forEach(sequence => {
|
||||
if (sequence?.trim()) {
|
||||
const index = message.indexOf(sequence);
|
||||
if (index !== -1) {
|
||||
message = message.substring(0, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
[
|
||||
instructPreset.output_sequence,
|
||||
instructPreset.last_output_sequence,
|
||||
].forEach(sequences => {
|
||||
if (sequences) {
|
||||
sequences.split('\n')
|
||||
.filter(line => line.trim() !== '')
|
||||
.forEach(line => {
|
||||
message = message.replaceAll(line, '');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
extractedData.content = message;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a preset to a valid text completion payload.
|
||||
* Only supports temperature.
|
||||
* @param {Object} preset - The preset configuration
|
||||
* @param {Object} customPreset - Additional parameters to override preset values
|
||||
* @returns {Object} - Formatted payload for text completion API
|
||||
*/
|
||||
static presetToGeneratePayload(preset, customPreset = {}) {
|
||||
if (!preset || typeof preset !== 'object') {
|
||||
throw new Error('Invalid preset: must be an object');
|
||||
}
|
||||
|
||||
// Merge preset with custom parameters
|
||||
const settings = { ...preset, ...customPreset };
|
||||
|
||||
// Initialize base payload with common parameters
|
||||
let payload = {
|
||||
'temperature': settings.temp ? Number(settings.temp) : undefined,
|
||||
'min_p': settings.min_p ? Number(settings.min_p) : undefined,
|
||||
};
|
||||
|
||||
// Remove undefined values to avoid API errors
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (payload[key] === undefined) {
|
||||
delete payload[key];
|
||||
}
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates & sends a chat completion request.
|
||||
*/
|
||||
export class ChatCompletionService {
|
||||
static TYPE = 'openai';
|
||||
|
||||
/**
|
||||
* @param {ChatCompletionPayload} custom
|
||||
* @returns {ChatCompletionPayload}
|
||||
*/
|
||||
static createRequestData({ stream = false, messages, model, chat_completion_source, max_tokens, temperature, custom_url, reverse_proxy, proxy_password, ...props }) {
|
||||
const payload = {
|
||||
stream,
|
||||
messages,
|
||||
model,
|
||||
chat_completion_source,
|
||||
max_tokens,
|
||||
temperature,
|
||||
custom_url,
|
||||
reverse_proxy,
|
||||
proxy_password,
|
||||
use_makersuite_sysprompt: true,
|
||||
claude_use_sysprompt: true,
|
||||
...props,
|
||||
};
|
||||
|
||||
// Remove undefined values to avoid API errors
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (payload[key] === undefined) {
|
||||
delete payload[key];
|
||||
}
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a chat completion request
|
||||
* @param {ChatCompletionPayload} data Request data
|
||||
* @param {boolean?} extractData Extract message from the response. Default true
|
||||
* @param {AbortSignal?} signal Abort signal
|
||||
* @returns {Promise<ExtractedData | (() => AsyncGenerator<StreamResponse>)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator
|
||||
* @throws {Error}
|
||||
*/
|
||||
static async sendRequest(data, extractData = true, signal = null) {
|
||||
const response = await fetch('/api/backends/chat-completions/generate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify(data),
|
||||
signal: signal ?? new AbortController().signal,
|
||||
});
|
||||
|
||||
if (!data.stream) {
|
||||
const json = await response.json();
|
||||
if (!response.ok || json.error) {
|
||||
throw json;
|
||||
}
|
||||
|
||||
if (!extractData) {
|
||||
return json;
|
||||
}
|
||||
|
||||
return {
|
||||
content: extractMessageFromData(json, this.TYPE),
|
||||
reasoning: extractReasoningFromData(json, {
|
||||
mainApi: this.TYPE,
|
||||
textGenType: data.chat_completion_source,
|
||||
ignoreShowThoughts: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
tryParseStreamingError(response, text, { quiet: true });
|
||||
|
||||
throw new Error(`Got response status ${response.status}`);
|
||||
}
|
||||
|
||||
const eventStream = new EventSourceStream();
|
||||
response.body.pipeThrough(eventStream);
|
||||
const reader = eventStream.readable.getReader();
|
||||
return async function* streamData() {
|
||||
let text = '';
|
||||
const swipes = [];
|
||||
const state = { reasoning: '', image: '' };
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) return;
|
||||
const rawData = value.data;
|
||||
if (rawData === '[DONE]') return;
|
||||
tryParseStreamingError(response, rawData, { quiet: true });
|
||||
const parsed = JSON.parse(rawData);
|
||||
|
||||
const reply = getStreamingReply(parsed, state, {
|
||||
chatCompletionSource: data.chat_completion_source,
|
||||
overrideShowThoughts: true,
|
||||
});
|
||||
if (Array.isArray(parsed?.choices) && parsed?.choices?.[0]?.index > 0) {
|
||||
const swipeIndex = parsed.choices[0].index - 1;
|
||||
swipes[swipeIndex] = (swipes[swipeIndex] || '') + reply;
|
||||
} else {
|
||||
text += reply;
|
||||
}
|
||||
|
||||
yield { text, swipes: swipes, state };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and send a chat completion request with optional preset
|
||||
* @param {ChatCompletionPayload} custom
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {string?} [options.presetName] - Name of the preset to use for generation settings
|
||||
* @param {boolean} [extractData=true] - Whether to extract structured data from response
|
||||
* @param {AbortSignal?} [signal] - Abort signal
|
||||
* @returns {Promise<ExtractedData | (() => AsyncGenerator<StreamResponse>)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator
|
||||
* @throws {Error}
|
||||
*/
|
||||
static async processRequest(custom, options, extractData = true, signal = null) {
|
||||
const { presetName } = options;
|
||||
let requestData = { ...custom };
|
||||
|
||||
// Apply generation preset if specified
|
||||
if (presetName) {
|
||||
const presetManager = getPresetManager(this.TYPE);
|
||||
if (presetManager) {
|
||||
const preset = presetManager.getCompletionPresetByName(presetName);
|
||||
if (preset) {
|
||||
// Convert preset to payload and merge with custom parameters
|
||||
const presetPayload = this.presetToGeneratePayload(preset, {});
|
||||
requestData = { ...presetPayload, ...requestData };
|
||||
} else {
|
||||
console.warn(`Preset "${presetName}" not found, continuing with default settings`);
|
||||
}
|
||||
} else {
|
||||
console.warn('Preset manager not found, continuing with default settings');
|
||||
}
|
||||
}
|
||||
|
||||
const data = this.createRequestData(requestData);
|
||||
|
||||
return await this.sendRequest(data, extractData, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a preset to a valid chat completion payload
|
||||
* Only supports temperature.
|
||||
* @param {Object} preset - The preset configuration
|
||||
* @param {Object} customParams - Additional parameters to override preset values
|
||||
* @returns {Object} - Formatted payload for chat completion API
|
||||
*/
|
||||
static presetToGeneratePayload(preset, customParams = {}) {
|
||||
if (!preset || typeof preset !== 'object') {
|
||||
throw new Error('Invalid preset: must be an object');
|
||||
}
|
||||
|
||||
// Merge preset with custom parameters
|
||||
const settings = { ...preset, ...customParams };
|
||||
|
||||
// Initialize base payload with common parameters
|
||||
const payload = {
|
||||
temperature: settings.temperature ? Number(settings.temperature) : undefined,
|
||||
};
|
||||
|
||||
// Remove undefined values to avoid API errors
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (payload[key] === undefined) {
|
||||
delete payload[key];
|
||||
}
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
@@ -7,9 +7,8 @@ import { renderTemplate, renderTemplateAsync } from './templates.js';
|
||||
import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js';
|
||||
import { getContext } from './st-context.js';
|
||||
import { isAdmin } from './user.js';
|
||||
import { addLocaleData, getCurrentLocale, t } from './i18n.js';
|
||||
import { t } from './i18n.js';
|
||||
import { debounce_timeout } from './constants.js';
|
||||
import { accountStorage } from './util/AccountStorage.js';
|
||||
|
||||
export {
|
||||
getContext,
|
||||
@@ -154,18 +153,8 @@ export const extension_settings = {
|
||||
refine_mode: false,
|
||||
},
|
||||
expressions: {
|
||||
/** @type {number} see `EXPRESSION_API` */
|
||||
api: undefined,
|
||||
/** @type {string[]} */
|
||||
custom: [],
|
||||
showDefault: false,
|
||||
translate: false,
|
||||
/** @type {string} */
|
||||
fallback_expression: undefined,
|
||||
/** @type {string} */
|
||||
llmPrompt: undefined,
|
||||
allowMultiple: true,
|
||||
rerollIfSame: false,
|
||||
},
|
||||
connectionManager: {
|
||||
selectedProfile: '',
|
||||
@@ -210,12 +199,6 @@ export const extension_settings = {
|
||||
* @type {string[]}
|
||||
*/
|
||||
disabled_attachments: [],
|
||||
gallery: {
|
||||
/** @type {{[characterKey: string]: string}} */
|
||||
folders: {},
|
||||
/** @type {string} */
|
||||
sort: 'dateAsc',
|
||||
},
|
||||
};
|
||||
|
||||
function showHideExtensionsMenu() {
|
||||
@@ -390,7 +373,7 @@ async function activateExtensions() {
|
||||
if (meetsModuleRequirements && !isDisabled) {
|
||||
try {
|
||||
console.debug('Activating extension', name);
|
||||
const promise = addExtensionLocale(name, manifest).finally(() => Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]));
|
||||
const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);
|
||||
await promise
|
||||
.then(() => activeExtensions.add(name))
|
||||
.catch(err => console.log('Could not activate extension', name, err));
|
||||
@@ -582,42 +565,6 @@ function addExtensionScript(name, manifest) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a localization data for an extension.
|
||||
* @param {string} name Extension name
|
||||
* @param {object} manifest Manifest object
|
||||
*/
|
||||
function addExtensionLocale(name, manifest) {
|
||||
// No i18n data in the manifest
|
||||
if (!manifest.i18n || typeof manifest.i18n !== 'object') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const currentLocale = getCurrentLocale();
|
||||
const localeFile = manifest.i18n[currentLocale];
|
||||
|
||||
// Manifest doesn't provide a locale file for the current locale
|
||||
if (!localeFile) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return fetch(`/scripts/extensions/${name}/${localeFile}`)
|
||||
.then(async response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
addLocaleData(currentLocale, data);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Could not load extension locale data for ' + name, err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates HTML string for displaying an extension in the UI.
|
||||
*
|
||||
@@ -655,12 +602,12 @@ function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal,
|
||||
}
|
||||
|
||||
let toggleElement = isActive || isDisabled ?
|
||||
'<input type="checkbox" title="' + t`Click to toggle` + `" data-name="${name}" class="${isActive ? 'toggle_disable' : 'toggle_enable'} ${checkboxClass}" ${isActive ? 'checked' : ''}>` :
|
||||
`<input type="checkbox" title="Click to toggle" data-name="${name}" class="${isActive ? 'toggle_disable' : 'toggle_enable'} ${checkboxClass}" ${isActive ? 'checked' : ''}>` :
|
||||
`<input type="checkbox" title="Cannot enable extension" data-name="${name}" class="extension_missing ${checkboxClass}" disabled>`;
|
||||
|
||||
let deleteButton = isExternal ? `<button class="btn_delete menu_button" data-name="${externalId}" data-i18n="[title]Delete" title="Delete"><i class="fa-fw fa-solid fa-trash-can"></i></button>` : '';
|
||||
let deleteButton = isExternal ? `<button class="btn_delete menu_button" data-name="${externalId}" title="Delete"><i class="fa-fw fa-solid fa-trash-can"></i></button>` : '';
|
||||
let updateButton = isExternal ? `<button class="btn_update menu_button displayNone" data-name="${externalId}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button>` : '';
|
||||
let moveButton = isExternal && isUserAdmin ? `<button class="btn_move menu_button" data-name="${externalId}" data-i18n="[title]Move" title="Move"><i class="fa-solid fa-folder-tree fa-fw"></i></button>` : '';
|
||||
let moveButton = isExternal && isUserAdmin ? `<button class="btn_move menu_button" data-name="${externalId}" title="Move"><i class="fa-solid fa-folder-tree fa-fw"></i></button>` : '';
|
||||
let modulesInfo = '';
|
||||
|
||||
if (isActive && Array.isArray(manifest.optional)) {
|
||||
@@ -668,7 +615,7 @@ function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal,
|
||||
modules.forEach(x => optional.delete(x));
|
||||
if (optional.size > 0) {
|
||||
const optionalString = DOMPurify.sanitize([...optional].join(', '));
|
||||
modulesInfo = '<div class="extension_modules">' + t`Optional modules:` + ` <span class="optional">${optionalString}</span></div>`;
|
||||
modulesInfo = `<div class="extension_modules">Optional modules: <span class="optional">${optionalString}</span></div>`;
|
||||
}
|
||||
} else if (!isDisabled) { // Neither active nor disabled
|
||||
const requirements = new Set(manifest.requires);
|
||||
@@ -767,7 +714,7 @@ async function showExtensionsDetails() {
|
||||
htmlExternal.append(htmlLoading);
|
||||
|
||||
const sortOrderKey = 'extensions_sortByName';
|
||||
const sortByName = accountStorage.getItem(sortOrderKey) === 'true';
|
||||
const sortByName = localStorage.getItem(sortOrderKey) === 'true';
|
||||
const sortFn = sortByName ? sortManifestsByName : sortManifestsByOrder;
|
||||
const extensions = Object.entries(manifests).sort((a, b) => sortFn(a[1], b[1])).map(getExtensionData);
|
||||
|
||||
@@ -783,41 +730,25 @@ async function showExtensionsDetails() {
|
||||
.append(htmlExternal)
|
||||
.append(getModuleInformation());
|
||||
|
||||
{
|
||||
const updateAction = async (force) => {
|
||||
/** @type {import('./popup.js').CustomPopupButton} */
|
||||
const updateAllButton = {
|
||||
text: t`Update all`,
|
||||
action: async () => {
|
||||
requiresReload = true;
|
||||
await autoUpdateExtensions(force);
|
||||
await autoUpdateExtensions(true);
|
||||
await popup.complete(POPUP_RESULT.AFFIRMATIVE);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.classList.add('extensions_toolbar');
|
||||
|
||||
const updateAllButton = document.createElement('button');
|
||||
updateAllButton.classList.add('menu_button', 'menu_button_icon');
|
||||
updateAllButton.textContent = t`Update all`;
|
||||
updateAllButton.addEventListener('click', () => updateAction(true));
|
||||
|
||||
const updateEnabledOnlyButton = document.createElement('button');
|
||||
updateEnabledOnlyButton.classList.add('menu_button', 'menu_button_icon');
|
||||
updateEnabledOnlyButton.textContent = t`Update enabled`;
|
||||
updateEnabledOnlyButton.addEventListener('click', () => updateAction(false));
|
||||
|
||||
const flexExpander = document.createElement('div');
|
||||
flexExpander.classList.add('expander');
|
||||
|
||||
const sortOrderButton = document.createElement('button');
|
||||
sortOrderButton.classList.add('menu_button', 'menu_button_icon');
|
||||
sortOrderButton.textContent = sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`;
|
||||
sortOrderButton.addEventListener('click', async () => {
|
||||
/** @type {import('./popup.js').CustomPopupButton} */
|
||||
const sortOrderButton = {
|
||||
text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,
|
||||
action: async () => {
|
||||
abortController.abort();
|
||||
accountStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
|
||||
localStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
|
||||
await showExtensionsDetails();
|
||||
});
|
||||
|
||||
toolbar.append(updateAllButton, updateEnabledOnlyButton, flexExpander, sortOrderButton);
|
||||
html.prepend(toolbar);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let waitingForSave = false;
|
||||
|
||||
@@ -825,7 +756,7 @@ async function showExtensionsDetails() {
|
||||
okButton: t`Close`,
|
||||
wide: true,
|
||||
large: true,
|
||||
customButtons: [],
|
||||
customButtons: [sortOrderButton, updateAllButton],
|
||||
allowVerticalScrolling: true,
|
||||
onClosing: async () => {
|
||||
if (waitingForSave) {
|
||||
@@ -1086,7 +1017,7 @@ export async function installExtension(url, global) {
|
||||
toastr.success(t`Extension '${response.display_name}' by ${response.author} (version ${response.version}) has been installed successfully!`, t`Extension installation successful`);
|
||||
console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
|
||||
await loadExtensionSettings({}, false, false);
|
||||
await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED, response);
|
||||
await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1212,7 +1143,7 @@ async function checkForUpdatesManual(sortFn, abortSignal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are updates available for enabled 3rd-party extensions.
|
||||
* Checks if there are updates available for 3rd-party extensions.
|
||||
* @param {boolean} force Skip nag check
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
@@ -1222,11 +1153,11 @@ async function checkForExtensionUpdates(force) {
|
||||
const currentDate = new Date().toDateString();
|
||||
|
||||
// Don't nag more than once a day
|
||||
if (accountStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
|
||||
if (localStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
accountStorage.setItem(STORAGE_NAG_KEY, currentDate);
|
||||
localStorage.setItem(STORAGE_NAG_KEY, currentDate);
|
||||
}
|
||||
|
||||
const isCurrentUserAdmin = isAdmin();
|
||||
@@ -1234,11 +1165,6 @@ async function checkForExtensionUpdates(force) {
|
||||
const promises = [];
|
||||
|
||||
for (const [id, manifest] of Object.entries(manifests)) {
|
||||
const isDisabled = extension_settings.disabledExtensions.includes(id);
|
||||
if (isDisabled) {
|
||||
console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||
continue;
|
||||
}
|
||||
const isGlobal = getExtensionType(id) === 'global';
|
||||
if (isGlobal && !isCurrentUserAdmin) {
|
||||
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||
@@ -1268,8 +1194,8 @@ async function checkForExtensionUpdates(force) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all enabled 3rd-party extensions that have auto-update enabled.
|
||||
* @param {boolean} forceAll Include disabled and not auto-updating
|
||||
* Updates all 3rd-party extensions that have auto-update enabled.
|
||||
* @param {boolean} forceAll Force update all even if not auto-updating
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function autoUpdateExtensions(forceAll) {
|
||||
@@ -1281,11 +1207,6 @@ async function autoUpdateExtensions(forceAll) {
|
||||
const isCurrentUserAdmin = isAdmin();
|
||||
const promises = [];
|
||||
for (const [id, manifest] of Object.entries(manifests)) {
|
||||
const isDisabled = extension_settings.disabledExtensions.includes(id);
|
||||
if (!forceAll && isDisabled) {
|
||||
console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||
continue;
|
||||
}
|
||||
const isGlobal = getExtensionType(id) === 'global';
|
||||
if (isGlobal && !isCurrentUserAdmin) {
|
||||
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||
|
@@ -8,9 +8,7 @@ import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from
|
||||
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
|
||||
import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
|
||||
import { executeSlashCommands } from '../../slash-commands.js';
|
||||
import { accountStorage } from '../../util/AccountStorage.js';
|
||||
import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
|
||||
import { t } from '../../i18n.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'assets';
|
||||
@@ -60,11 +58,11 @@ const KNOWN_TYPES = {
|
||||
'blip': 'Blip sounds',
|
||||
};
|
||||
|
||||
async function downloadAssetsList(url) {
|
||||
updateCurrentAssets().then(async function () {
|
||||
function downloadAssetsList(url) {
|
||||
updateCurrentAssets().then(function () {
|
||||
fetch(url, { cache: 'no-cache' })
|
||||
.then(response => response.json())
|
||||
.then(async function(json) {
|
||||
.then(json => {
|
||||
|
||||
availableAssets = {};
|
||||
$('#assets_menu').empty();
|
||||
@@ -85,10 +83,10 @@ async function downloadAssetsList(url) {
|
||||
|
||||
$('#assets_type_select').empty();
|
||||
$('#assets_search').val('');
|
||||
$('#assets_type_select').append($('<option />', { value: '', text: t`All` }));
|
||||
$('#assets_type_select').append($('<option />', { value: '', text: 'All' }));
|
||||
|
||||
for (const type of assetTypes) {
|
||||
const option = $('<option />', { value: type, text: t([KNOWN_TYPES[type] || type]) });
|
||||
const option = $('<option />', { value: type, text: KNOWN_TYPES[type] || type });
|
||||
$('#assets_type_select').append(option);
|
||||
}
|
||||
|
||||
@@ -105,7 +103,11 @@ async function downloadAssetsList(url) {
|
||||
assetTypeMenu.append(`<h3>${KNOWN_TYPES[assetType] || assetType}</h3>`).hide();
|
||||
|
||||
if (assetType == 'extension') {
|
||||
assetTypeMenu.append(await renderExtensionTemplateAsync('assets', 'installation'));
|
||||
assetTypeMenu.append(`
|
||||
<div class="assets-list-git">
|
||||
To download extensions from this page, you need to have <a href="https://git-scm.com/downloads" target="_blank">Git</a> installed.<br>
|
||||
Click the <i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i> icon to visit the Extension's repo for tips on how to use it.
|
||||
</div>`);
|
||||
}
|
||||
|
||||
for (const i in availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['name'].localeCompare(b['name']))) {
|
||||
@@ -181,7 +183,7 @@ async function downloadAssetsList(url) {
|
||||
const displayName = DOMPurify.sanitize(asset['name'] || asset['id']);
|
||||
const description = DOMPurify.sanitize(asset['description'] || '');
|
||||
const url = isValidUrl(asset['url']) ? asset['url'] : '';
|
||||
const title = assetType === 'extension' ? t`Extension repo/guide:` + ` ${url}` : t`Preview in browser`;
|
||||
const title = assetType === 'extension' ? `Extension repo/guide: ${url}` : 'Preview in browser';
|
||||
const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';
|
||||
const toolTag = assetType === 'extension' && asset['tool'];
|
||||
|
||||
@@ -192,10 +194,9 @@ async function downloadAssetsList(url) {
|
||||
<b>${displayName}</b>
|
||||
<a class="asset_preview" href="${url}" target="_blank" title="${title}">
|
||||
<i class="fa-solid fa-sm ${previewIcon}"></i>
|
||||
</a>` +
|
||||
(toolTag ? '<span class="tag" title="' + t`Adds a function tool` + '"><i class="fa-solid fa-sm fa-wrench"></i> ' +
|
||||
t`Tool` + '</span>' : '') +
|
||||
`</span>
|
||||
</a>
|
||||
${toolTag ? '<span class="tag" title="Adds a function tool"><i class="fa-solid fa-sm fa-wrench"></i> Tool</span>' : ''}
|
||||
</span>
|
||||
<small class="asset-description">
|
||||
${description}
|
||||
</small>
|
||||
@@ -424,21 +425,21 @@ jQuery(async () => {
|
||||
installHintButton.on('click', async function () {
|
||||
const installButton = $('#third_party_extension_button');
|
||||
flashHighlight(installButton, 5000);
|
||||
toastr.info(t`Click the flashing button to install extensions.`, t`How to install extensions?`);
|
||||
toastr.info('Click the flashing button to install extensions.', 'How to install extensions?');
|
||||
});
|
||||
|
||||
const connectButton = windowHtml.find('#assets-connect-button');
|
||||
connectButton.on('click', async function () {
|
||||
const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
|
||||
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
|
||||
const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
|
||||
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
|
||||
|
||||
const confirmation = skipConfirm || await Popup.show.confirm(t`Loading Asset List`, '<span>' + t`Are you sure you want to connect to the following url?` + `</span><var>${url}</var>`, {
|
||||
const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {
|
||||
customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
|
||||
onClose: popup => {
|
||||
if (popup.result) {
|
||||
const rememberValue = popup.inputResults.get('assets-remember');
|
||||
accountStorage.setItem(rememberKey, String(rememberValue));
|
||||
localStorage.setItem(rememberKey, String(rememberValue));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@@ -1,4 +0,0 @@
|
||||
<div class="assets-list-git">
|
||||
<span data-i18n="extension_install_1">To download extensions from this page, you need to have </span><a href="https://git-scm.com/downloads" target="_blank">Git</a><span data-i18n="extension_install_2"> installed.</span><br>
|
||||
<span data-i18n="extension_install_3">Click the </span><i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i><span data-i18n="extension_install_4"> icon to visit the Extension's repo for tips on how to use it.</span>
|
||||
</div>
|
@@ -33,13 +33,13 @@ To install a single 3rd party extension, use the "Install Extensions"
|
||||
<div id="assets_filters" class="flex-container">
|
||||
<select id="assets_type_select" class="text_pole flex1">
|
||||
</select>
|
||||
<input id="assets_search" class="text_pole flex1" data-i18n="[placeholder]Search" placeholder="Search" type="search">
|
||||
<input id="assets_search" class="text_pole flex1" placeholder="Search" type="search">
|
||||
<div id="assets-characters-button" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-image-portrait"></i>
|
||||
<span data-i18n="Characters">Characters</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="assets_menu">
|
||||
<div class="inline-drawer-content" id="assets_menu">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div id="attachFile" class="list-group-item flex-container flexGap5" data-i18n="[title]Attach a file or image to a current chat." title="Attach a file or image to a current chat.">
|
||||
<div id="attachFile" class="list-group-item flex-container flexGap5" title="Attach a file or image to a current chat.">
|
||||
<div class="fa-fw fa-solid fa-paperclip extensionsMenuExtensionButton"></div>
|
||||
<span data-i18n="Attach a File">Attach a File</span>
|
||||
</div>
|
||||
|
@@ -398,63 +398,23 @@ jQuery(async function () {
|
||||
|
||||
$('#caption_wand_container').append(sendButton);
|
||||
$(sendButton).on('click', () => {
|
||||
const hasCaptionModule = (() => {
|
||||
const settings = extension_settings.caption;
|
||||
|
||||
// Handle non-multimodal sources
|
||||
if (settings.source === 'extras' && modules.includes('caption')) return true;
|
||||
if (settings.source === 'local' || settings.source === 'horde') return true;
|
||||
|
||||
// Handle multimodal sources
|
||||
if (settings.source === 'multimodal') {
|
||||
const api = settings.multimodal_api;
|
||||
|
||||
// APIs that support reverse proxy
|
||||
const reverseProxyApis = {
|
||||
'openai': SECRET_KEYS.OPENAI,
|
||||
'mistral': SECRET_KEYS.MISTRALAI,
|
||||
'google': SECRET_KEYS.MAKERSUITE,
|
||||
'anthropic': SECRET_KEYS.CLAUDE,
|
||||
};
|
||||
|
||||
if (reverseProxyApis[api]) {
|
||||
if (secret_state[reverseProxyApis[api]] || settings.allow_reverse_proxy) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const chatCompletionApis = {
|
||||
'openrouter': SECRET_KEYS.OPENROUTER,
|
||||
'zerooneai': SECRET_KEYS.ZEROONEAI,
|
||||
'groq': SECRET_KEYS.GROQ,
|
||||
'cohere': SECRET_KEYS.COHERE,
|
||||
'xai': SECRET_KEYS.XAI,
|
||||
};
|
||||
|
||||
if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const textCompletionApis = {
|
||||
'ollama': textgen_types.OLLAMA,
|
||||
'llamacpp': textgen_types.LLAMACPP,
|
||||
'ooba': textgen_types.OOBA,
|
||||
'koboldcpp': textgen_types.KOBOLDCPP,
|
||||
'vllm': textgen_types.VLLM,
|
||||
};
|
||||
|
||||
if (textCompletionApis[api] && textgenerationwebui_settings.server_urls[textCompletionApis[api]]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Custom API doesn't need additional checks
|
||||
if (api === 'custom') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})();
|
||||
const hasCaptionModule =
|
||||
(modules.includes('caption') && extension_settings.caption.source === 'extras') ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'zerooneai' && secret_state[SECRET_KEYS.ZEROONEAI]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'groq' && secret_state[SECRET_KEYS.GROQ]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'mistral' && (secret_state[SECRET_KEYS.MISTRALAI] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && (secret_state[SECRET_KEYS.MAKERSUITE] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && (secret_state[SECRET_KEYS.CLAUDE] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'llamacpp' && textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ooba' && textgenerationwebui_settings.server_urls[textgen_types.OOBA]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'koboldcpp' && textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'vllm' && textgenerationwebui_settings.server_urls[textgen_types.VLLM]) ||
|
||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'custom') ||
|
||||
extension_settings.caption.source === 'local' ||
|
||||
extension_settings.caption.source === 'horde';
|
||||
|
||||
if (!hasCaptionModule) {
|
||||
toastr.error('Choose other captioning source in the extension settings.', 'Captioning is not available');
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<select id="caption_source" class="text_pole">
|
||||
<option value="local" data-i18n="Local">Local</option>
|
||||
<option value="multimodal" data-i18n="Multimodal (OpenAI / Anthropic / llama / Google)">Multimodal (OpenAI / Anthropic / llama / Google)</option>
|
||||
<option value="extras" data-i18n="Extras">Extras (deprecated)</option>
|
||||
<option value="extras" data-i18n="Extras">Extras</option>
|
||||
<option value="horde" data-i18n="Horde">Horde</option>
|
||||
</select>
|
||||
<div id="caption_multimodal_block" class="flex-container wide100p">
|
||||
@@ -19,7 +19,6 @@
|
||||
<select id="caption_multimodal_api" class="flex1 text_pole">
|
||||
<option value="zerooneai">01.AI (Yi)</option>
|
||||
<option value="anthropic">Anthropic</option>
|
||||
<option value="cohere">Cohere</option>
|
||||
<option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
|
||||
<option value="google">Google AI Studio</option>
|
||||
<option value="groq">Groq</option>
|
||||
@@ -31,43 +30,21 @@
|
||||
<option value="openrouter">OpenRouter</option>
|
||||
<option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option>
|
||||
<option value="vllm">vLLM</option>
|
||||
<option value="xai">xAI (Grok)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex1 flex-container flexFlowColumn flexNoGap">
|
||||
<label for="caption_multimodal_model" data-i18n="Model">Model</label>
|
||||
<select id="caption_multimodal_model" class="flex1 text_pole">
|
||||
<option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option>
|
||||
<option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option>
|
||||
<option data-type="mistral" value="pixtral-12b-latest">pixtral-12b-latest</option>
|
||||
<option data-type="mistral" value="pixtral-12b-2409">pixtral-12b-2409</option>
|
||||
<option data-type="mistral" value="pixtral-large-latest">pixtral-large-latest</option>
|
||||
<option data-type="mistral" value="pixtral-large-2411">pixtral-large-2411</option>
|
||||
<option data-type="mistral" value="mistral-large-pixtral-2411">mistral-large-pixtral-2411</option>
|
||||
<option data-type="mistral" value="mistral-small-2503">mistral-small-2503</option>
|
||||
<option data-type="mistral" value="mistral-small-latest">mistral-small-latest</option>
|
||||
<option data-type="zerooneai" value="yi-vision">yi-vision</option>
|
||||
<option data-type="openai" value="gpt-4.1">gpt-4.1</option>
|
||||
<option data-type="openai" value="gpt-4.1-2025-04-14">gpt-4.1-2025-04-14</option>
|
||||
<option data-type="openai" value="gpt-4.1-mini">gpt-4.1-mini</option>
|
||||
<option data-type="openai" value="gpt-4.1-mini-2025-04-14">gpt-4.1-mini-2025-04-14</option>
|
||||
<option data-type="openai" value="gpt-4.1-nano">gpt-4.1-nano</option>
|
||||
<option data-type="openai" value="gpt-4.1-nano-2025-04-14">gpt-4.1-nano-2025-04-14</option>
|
||||
<option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option>
|
||||
<option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option>
|
||||
<option data-type="openai" value="gpt-4o">gpt-4o</option>
|
||||
<option data-type="openai" value="gpt-4o-mini">gpt-4o-mini</option>
|
||||
<option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option>
|
||||
<option data-type="openai" value="o1">o1</option>
|
||||
<option data-type="openai" value="o1-2024-12-17">o1-2024-12-17</option>
|
||||
<option data-type="openai" value="o3">o3</option>
|
||||
<option data-type="openai" value="o3-2025-04-16">o3-2025-04-16</option>
|
||||
<option data-type="openai" value="o4-mini">o4-mini</option>
|
||||
<option data-type="openai" value="o4-mini-2025-04-16">o4-mini-2025-04-16</option>
|
||||
<option data-type="openai" value="gpt-4.5-preview">gpt-4.5-preview</option>
|
||||
<option data-type="openai" value="gpt-4.5-preview-2025-02-27">gpt-4.5-preview-2025-02-27</option>
|
||||
<option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
|
||||
<option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-20241022">claude-3-5-sonnet-20241022</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option>
|
||||
@@ -76,17 +53,7 @@
|
||||
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
|
||||
<option data-type="google" value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
|
||||
<option data-type="google" value="gemini-2.5-pro-exp-03-25">gemini-2.5-pro-exp-03-25</option>
|
||||
<option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05</option>
|
||||
<option data-type="google" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-lite-preview">gemini-2.0-flash-lite-preview</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-lite-preview-02-05">gemini-2.0-flash-lite-preview-02-05</option>
|
||||
<option data-type="google" value="gemini-2.0-flash">gemini-2.0-flash</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-exp-image-generation">gemini-2.0-flash-exp-image-generation</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp">gemini-2.0-flash-thinking-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp-01-21">gemini-2.0-flash-thinking-exp-01-21</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>
|
||||
@@ -146,8 +113,6 @@
|
||||
<option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
|
||||
<option data-type="vllm" value="vllm_current" data-i18n="currently_selected">[Currently selected]</option>
|
||||
<option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option>
|
||||
<option data-type="xai" value="grok-2-vision-1212">grok-2-vision-1212</option>
|
||||
<option data-type="xai" value="grok-vision-beta">grok-vision-beta</option>
|
||||
</select>
|
||||
</div>
|
||||
<div data-type="ollama">
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { DOMPurify, Fuse } from '../../../lib.js';
|
||||
import { Fuse } from '../../../lib.js';
|
||||
|
||||
import { event_types, eventSource, main_api, saveSettingsDebounced } from '../../../script.js';
|
||||
import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js';
|
||||
@@ -16,19 +16,12 @@ import { t } from '../../i18n.js';
|
||||
|
||||
const MODULE_NAME = 'connection-manager';
|
||||
const NONE = '<None>';
|
||||
const EMPTY = '<Empty>';
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
profiles: [],
|
||||
selectedProfile: null,
|
||||
};
|
||||
|
||||
// Commands that can record an empty value into the profile
|
||||
const ALLOW_EMPTY = [
|
||||
'stop-strings',
|
||||
'start-reply-with',
|
||||
];
|
||||
|
||||
const CC_COMMANDS = [
|
||||
'api',
|
||||
'preset',
|
||||
@@ -38,8 +31,6 @@ const CC_COMMANDS = [
|
||||
'model',
|
||||
'proxy',
|
||||
'stop-strings',
|
||||
'start-reply-with',
|
||||
'reasoning-template',
|
||||
];
|
||||
|
||||
const TC_COMMANDS = [
|
||||
@@ -54,8 +45,6 @@ const TC_COMMANDS = [
|
||||
'instruct-state',
|
||||
'tokenizer',
|
||||
'stop-strings',
|
||||
'start-reply-with',
|
||||
'reasoning-template',
|
||||
];
|
||||
|
||||
const FANCY_NAMES = {
|
||||
@@ -71,8 +60,6 @@ const FANCY_NAMES = {
|
||||
'context': 'Context Template',
|
||||
'tokenizer': 'Tokenizer',
|
||||
'stop-strings': 'Custom Stopping Strings',
|
||||
'start-reply-with': 'Start Reply With',
|
||||
'reasoning-template': 'Reasoning Template',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -120,7 +107,6 @@ class ConnectionManagerSpinner {
|
||||
/**
|
||||
* Get named arguments for the command callback.
|
||||
* @param {object} [args] Additional named arguments
|
||||
* @param {string} [args.force] Whether to force setting the value
|
||||
* @returns {object} Named arguments
|
||||
*/
|
||||
function getNamedArguments(args = {}) {
|
||||
@@ -156,8 +142,6 @@ const profilesProvider = () => [
|
||||
* @property {string} [instruct-state] Instruct Mode
|
||||
* @property {string} [tokenizer] Tokenizer
|
||||
* @property {string} [stop-strings] Custom Stopping Strings
|
||||
* @property {string} [start-reply-with] Start Reply With
|
||||
* @property {string} [reasoning-template] Reasoning Template
|
||||
* @property {string[]} [exclude] Commands to exclude
|
||||
*/
|
||||
|
||||
@@ -202,10 +186,9 @@ async function readProfileFromCommands(mode, profile, cleanUp = false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const allowEmpty = ALLOW_EMPTY.includes(command);
|
||||
const args = getNamedArguments();
|
||||
const result = await SlashCommandParser.commands[command].callback(args, '');
|
||||
if (result || (allowEmpty && result === '')) {
|
||||
if (result) {
|
||||
profile[command] = result;
|
||||
continue;
|
||||
}
|
||||
@@ -271,16 +254,11 @@ async function createConnectionProfile(forceName = null) {
|
||||
});
|
||||
const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n);
|
||||
const suggestedName = getUniqueName(collapseSpaces(`${profile.api ?? ''} ${profile.model ?? ''} - ${profile.preset ?? ''}`), isNameTaken);
|
||||
let name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName, { rows: 2 });
|
||||
// If it's cancelled, it will be false
|
||||
const name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName, { rows: 2 });
|
||||
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
name = DOMPurify.sanitize(String(name));
|
||||
if (!name) {
|
||||
toastr.error('Name cannot be empty.');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isNameTaken(name) || name === NONE) {
|
||||
toastr.error('A profile with the same name already exists.');
|
||||
@@ -312,8 +290,7 @@ async function deleteConnectionProfile() {
|
||||
return;
|
||||
}
|
||||
|
||||
const profile = extension_settings.connectionManager.profiles[index];
|
||||
const name = profile.name;
|
||||
const name = extension_settings.connectionManager.profiles[index].name;
|
||||
const confirm = await Popup.show.confirm(t`Are you sure you want to delete the selected profile?`, name);
|
||||
|
||||
if (!confirm) {
|
||||
@@ -323,8 +300,6 @@ async function deleteConnectionProfile() {
|
||||
extension_settings.connectionManager.profiles.splice(index, 1);
|
||||
extension_settings.connectionManager.selectedProfile = null;
|
||||
saveSettingsDebounced();
|
||||
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_DELETED, profile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,14 +309,7 @@ async function deleteConnectionProfile() {
|
||||
*/
|
||||
function makeFancyProfile(profile) {
|
||||
return Object.entries(FANCY_NAMES).reduce((acc, [key, value]) => {
|
||||
const allowEmpty = ALLOW_EMPTY.includes(key);
|
||||
if (!profile[key]) {
|
||||
if (profile[key] === '' && allowEmpty) {
|
||||
acc[value] = EMPTY;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (!profile[key]) return acc;
|
||||
acc[value] = profile[key];
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -371,12 +339,11 @@ async function applyConnectionProfile(profile) {
|
||||
}
|
||||
|
||||
const argument = profile[command];
|
||||
const allowEmpty = ALLOW_EMPTY.includes(command);
|
||||
if (!argument && !(allowEmpty && argument === '')) {
|
||||
if (!argument) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const args = getNamedArguments(allowEmpty ? { force: 'true' } : {});
|
||||
const args = getNamedArguments();
|
||||
await SlashCommandParser.commands[command].callback(args, argument);
|
||||
} catch (error) {
|
||||
console.error(`Failed to execute command: ${command} ${argument}`, error);
|
||||
@@ -524,7 +491,6 @@ async function renderDetailsContent(detailsContent) {
|
||||
saveSettingsDebounced();
|
||||
renderConnectionProfiles(profiles);
|
||||
await renderDetailsContent(detailsContent);
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_CREATED, profile);
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
|
||||
});
|
||||
|
||||
@@ -536,11 +502,9 @@ async function renderDetailsContent(detailsContent) {
|
||||
console.log('No profile selected');
|
||||
return;
|
||||
}
|
||||
const oldProfile = structuredClone(profile);
|
||||
await updateConnectionProfile(profile);
|
||||
await renderDetailsContent(detailsContent);
|
||||
saveSettingsDebounced();
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_UPDATED, oldProfile, profile);
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
|
||||
toastr.success('Connection profile updated', '', { timeOut: 1500 });
|
||||
});
|
||||
@@ -574,8 +538,7 @@ async function renderDetailsContent(detailsContent) {
|
||||
return acc;
|
||||
}, {});
|
||||
const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings }));
|
||||
let newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, {
|
||||
rows: 2,
|
||||
const newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, {
|
||||
customButtons: [{
|
||||
text: t`Save and Update`,
|
||||
classes: ['popup-button-ok'],
|
||||
@@ -586,15 +549,9 @@ async function renderDetailsContent(detailsContent) {
|
||||
}],
|
||||
});
|
||||
|
||||
// If it's cancelled, it will be false
|
||||
if (!newName) {
|
||||
return;
|
||||
}
|
||||
newName = DOMPurify.sanitize(String(newName));
|
||||
if (!newName) {
|
||||
toastr.error('Name cannot be empty.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.name !== newName && extension_settings.connectionManager.profiles.some(p => p.name === newName)) {
|
||||
toastr.error('A profile with the same name already exists.');
|
||||
@@ -605,7 +562,6 @@ async function renderDetailsContent(detailsContent) {
|
||||
return Object.entries(FANCY_NAMES).find(x => x[1] === String($(this).val()))?.[0];
|
||||
}).get();
|
||||
|
||||
const oldProfile = structuredClone(profile);
|
||||
if (newExcludeList.length !== profile.exclude.length || !newExcludeList.every(e => profile.exclude.includes(e))) {
|
||||
profile.exclude = newExcludeList;
|
||||
for (const command of newExcludeList) {
|
||||
@@ -620,11 +576,10 @@ async function renderDetailsContent(detailsContent) {
|
||||
|
||||
if (profile.name !== newName) {
|
||||
toastr.success('Connection profile renamed.');
|
||||
profile.name = newName;
|
||||
profile.name = String(newName);
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_UPDATED, oldProfile, profile);
|
||||
renderConnectionProfiles(profiles);
|
||||
await renderDetailsContent(detailsContent);
|
||||
});
|
||||
@@ -727,7 +682,6 @@ async function renderDetailsContent(detailsContent) {
|
||||
saveSettingsDebounced();
|
||||
renderConnectionProfiles(profiles);
|
||||
await renderDetailsContent(detailsContent);
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_CREATED, profile);
|
||||
return profile.name;
|
||||
},
|
||||
}));
|
||||
@@ -742,11 +696,9 @@ async function renderDetailsContent(detailsContent) {
|
||||
toastr.warning('No profile selected.');
|
||||
return '';
|
||||
}
|
||||
const oldProfile = structuredClone(profile);
|
||||
await updateConnectionProfile(profile);
|
||||
await renderDetailsContent(detailsContent);
|
||||
saveSettingsDebounced();
|
||||
await eventSource.emit(event_types.CONNECTION_PROFILE_UPDATED, oldProfile, profile);
|
||||
return profile.name;
|
||||
},
|
||||
}));
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
{{#each images}}
|
||||
<div class="expression_list_item interactable" data-expression="{{../expression}}" data-expression-type="{{this.type}}" data-filename="{{this.fileName}}">
|
||||
<div id="{{item}}" class="expression_list_item">
|
||||
<div class="expression_list_buttons">
|
||||
<div class="menu_button expression_list_upload" title="Upload image">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
@@ -8,14 +7,11 @@
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="expression_list_title">
|
||||
<span>{{../expression}}</span>
|
||||
{{#if ../isCustom}}
|
||||
<div class="expression_list_title {{textClass}}">
|
||||
<span>{{item}}</span>
|
||||
{{#if isCustom}}
|
||||
<small class="expression_list_custom">(custom)</small>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="expression_list_image_container" title="{{this.title}}">
|
||||
<img class="expression_list_image" src="{{this.imageSrc}}" alt="{{this.title}}" data-epression="{{../expression}}" />
|
||||
</div>
|
||||
<img class="expression_list_image" src="{{imageSrc}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
|
@@ -6,35 +6,30 @@
|
||||
</div>
|
||||
|
||||
<div class="inline-drawer-content">
|
||||
<label class="checkbox_label" for="expression_translate" title="Use the selected API from Chat Translation extension settings." data-i18n="[title]Use the selected API from Chat Translation extension settings.">
|
||||
<label class="checkbox_label" for="expression_translate" title="Use the selected API from Chat Translation extension settings.">
|
||||
<input id="expression_translate" type="checkbox">
|
||||
<span data-i18n="Translate text to English before classification">Translate text to English before classification</span>
|
||||
</label>
|
||||
<label class="checkbox_label" for="expressions_allow_multiple" title="A single expression can have multiple sprites. Whenever the expression is chosen, a random sprite for this expression will be selected." data-i18n="[title]A single expression can have multiple sprites. Whenever the expression is chosen, a random sprite for this expression will be selected.">
|
||||
<input id="expressions_allow_multiple" type="checkbox">
|
||||
<span data-i18n="Allow multiple sprites per expression">Allow multiple sprites per expression</span>
|
||||
<label class="checkbox_label" for="expressions_show_default">
|
||||
<input id="expressions_show_default" type="checkbox">
|
||||
<span data-i18n="Show default images (emojis) if sprite missing">Show default images (emojis) if sprite missing</span>
|
||||
</label>
|
||||
<label class="checkbox_label" for="expressions_reroll_if_same" title="If the same expression is used again, re-roll the sprite. This only applies to expressions that have multiple available sprites assigned." data-i18n="[title]If the same expression is used again, re-roll the sprite. This only applies to expressions that have multiple available sprites assigned.">
|
||||
<input id="expressions_reroll_if_same" type="checkbox">
|
||||
<span data-i18n="Re-roll if same expression is used again">Re-roll if same sprite is used again</span>
|
||||
<label id="image_type_block" class="checkbox_label" for="image_type_toggle">
|
||||
<input id="image_type_toggle" type="checkbox">
|
||||
<span data-i18n="Image Type - talkinghead (extras)">Image Type - talkinghead (extras)</span>
|
||||
</label>
|
||||
<div class="expression_api_block m-b-1 m-t-1">
|
||||
<label for="expression_api" data-i18n="Classifier API">Classifier API</label>
|
||||
<small data-i18n="Select the API for classifying expressions.">Select the API for classifying expressions.</small>
|
||||
<select id="expression_api" class="flex1 margin0">
|
||||
<option value="99" data-i18n="[ None ]">[ None ]</option>
|
||||
<option value="0" data-i18n="Local">Local</option>
|
||||
<option value="1" data-i18n="Extras">Extras (deprecated)</option>
|
||||
<option value="1" data-i18n="Extras">Extras</option>
|
||||
<option value="2" data-i18n="Main API">Main API</option>
|
||||
<option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="expression_llm_prompt_block m-b-1 m-t-1">
|
||||
<label class="checkbox_label" for="expressions_filter_available" title="When using LLM or WebLLM classifier, only show and use expressions that have sprites assigned to them." data-i18n="[title]When using LLM or WebLLM classifier, only show and use expressions that have sprites assigned to them.">
|
||||
<input id="expressions_filter_available" type="checkbox">
|
||||
<span data-i18n="Filter expressions for available sprites">Filter expressions for available sprites</span>
|
||||
</label>
|
||||
<label for="expression_llm_prompt" class="title_restorable m-t-1">
|
||||
<label for="expression_llm_prompt" class="title_restorable">
|
||||
<span data-i18n="LLM Prompt">LLM Prompt</span>
|
||||
<div id="expression_llm_prompt_restore" title="Restore default value" class="right_menu_button">
|
||||
<i class="fa-solid fa-clock-rotate-left fa-sm"></i>
|
||||
@@ -80,20 +75,8 @@
|
||||
<span data-i18n="Remove all image overrides">Remove all image overrides</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">
|
||||
<b data-i18n="Hint:">Hint:</b>
|
||||
<i>
|
||||
<span data-i18n="Create new folder in the _space">Create new folder in the </span><b>/characters/</b> <span data-i18n="folder of your user data directory and name it as the name of the character.">folder of your user data directory and name it as the name of the character.</span>
|
||||
<span data-i18n="Put images with expressions there. File names should follow the pattern:">Put images with expressions there. File names should follow the pattern: </span><tt data-i18n="expression_label_pattern">[expression_label].[image_format]</tt>
|
||||
</i>
|
||||
</p>
|
||||
<p>
|
||||
<i>
|
||||
<span>In case of multiple files per expression, file names can contain a suffix, either separated by a dot or a
|
||||
dash.
|
||||
Examples: </span><tt>joy.png</tt>, <tt>joy-1.png</tt>, <tt>joy.expressive.png</tt>
|
||||
</i>
|
||||
</p>
|
||||
<p class="hint"><b data-i18n="Hint:">Hint:</b> <i><span data-i18n="Create new folder in the _space">Create new folder in the </span><b>/characters/</b> <span data-i18n="folder of your user data directory and name it as the name of the character.">folder of your user data directory and name it as the name of the character.</span>
|
||||
<span data-i18n="Put images with expressions there. File names should follow the pattern:">Put images with expressions there. File names should follow the pattern: </span><tt data-i18n="expression_label_pattern">[expression_label].[image_format]</tt></i></p>
|
||||
<h3 id="image_list_header">
|
||||
<strong data-i18n="Sprite set:">Sprite set:</strong> <span id="image_list_header_name"></span>
|
||||
</h3>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user