Merge pull request #4516 from SillyTavern/staging Staging

6dabf12ed785087cfe50b8027c582b16faa2ac4d

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
74 files changed, +5303 -903Ignore whitespace
.gitignore+1 -1
@@ -54,4 +54,4 @@ public/scripts/extensions/third-party
54.aider*54.aider*
55.env55.env
56/StartDev.bat56/StartDev.bat
5757yarn.lock
default/config.yaml+18 -0
@@ -40,9 +40,15 @@ browserLaunch:
40port: 800040port: 8000
41# -- SSL options --41# -- SSL options --
42ssl:42ssl:
43 # Enable SSL/TLS encryption
43 enabled: false44 enabled: false
45 # Path to certificate (relative to server root)
44 certPath: "./certs/cert.pem"46 certPath: "./certs/cert.pem"
47 # Path to private key (relative to server root)
45 keyPath: "./certs/privkey.pem"48 keyPath: "./certs/privkey.pem"
49 # Private key passphrase (leave empty if not needed)
50 # For better security, use a CLI argument or an environment variable (SILLYTAVERN_SSL_KEYPASSPHRASE)
51 keyPassphrase: ""
46# -- SECURITY CONFIGURATION --52# -- SECURITY CONFIGURATION --
47# Toggle whitelist mode53# Toggle whitelist mode
48whitelistMode: true54whitelistMode: true
@@ -88,6 +94,18 @@ autheliaAuth: false
88# the username and passwords for basic auth are the same as those94# the username and passwords for basic auth are the same as those
89# for the individual accounts95# for the individual accounts
90perUserBasicAuth: false96perUserBasicAuth: false
97# Host whitelist configuration. Recommended if you're using a listen mode
98hostWhitelist:
99 # Enable or disable host whitelisting
100 enabled: false
101 # Scan incoming requests for potential host header spoofing
102 scan: true
103 # List of allowed hosts. Do not include localhost or IPs, these are safe.
104 # Use a dot to create subdomain patterns.
105 # Examples:
106 # - example.com
107 # - .trycloudflare.com
108 hosts: []
91109
92# User session timeout *in seconds* (defaults to 24 hours).110# User session timeout *in seconds* (defaults to 24 hours).
93## Set to a positive number to expire session after a certain time of inactivity111## Set to a positive number to expire session after a certain time of inactivity
default/public/error/host-not-allowed.html+21 -0
@@ -0,0 +1,21 @@
1<!DOCTYPE html>
2<html>
3
4<head>
5 <title>Forbidden</title>
6</head>
7
8<body>
9 <h1>Forbidden</h1>
10 <p>
11 If you are the system administrator, add the hostname you are accessing from to the
12 host whitelist, or disable host whitelisting in the
13 <code>config.yaml</code> file located in the root directory of your installation.
14 </p>
15 <hr />
16 <p>
17 <em>Access from this host is not allowed. This attempt has been logged.</em>
18 </p>
19</body>
20
21</html>
package-lock.json+40 -30
@@ -1,16 +1,16 @@
1{1{
2 "name": "sillytavern",2 "name": "sillytavern",
3 "version": "1.13.3",3 "version": "1.13.4",
4 "lockfileVersion": 3,4 "lockfileVersion": 3,
5 "requires": true,5 "requires": true,
6 "packages": {6 "packages": {
7 "": {7 "": {
8 "name": "sillytavern",8 "name": "sillytavern",
9 "version": "1.13.3",9 "version": "1.13.4",
10 "hasInstallScript": true,10 "hasInstallScript": true,
11 "license": "AGPL-3.0",11 "license": "AGPL-3.0",
12 "dependencies": {12 "dependencies": {
13 "@adobe/css-tools": "^4.4.3",13 "@adobe/css-tools": "^4.4.4",
14 "@agnai/sentencepiece-js": "^1.1.1",14 "@agnai/sentencepiece-js": "^1.1.1",
15 "@agnai/web-tokenizers": "^0.1.3",15 "@agnai/web-tokenizers": "^0.1.3",
16 "@iconfu/svg-inject": "^1.2.3",16 "@iconfu/svg-inject": "^1.2.3",
@@ -42,9 +42,9 @@
42 "archiver": "^7.0.1",42 "archiver": "^7.0.1",
43 "bing-translate-api": "^4.1.0",43 "bing-translate-api": "^4.1.0",
44 "body-parser": "^1.20.2",44 "body-parser": "^1.20.2",
45 "bowser": "^2.11.0",45 "bowser": "^2.12.1",
46 "bytes": "^3.1.2",46 "bytes": "^3.1.2",
47 "chalk": "^5.4.1",47 "chalk": "^5.6.0",
48 "command-exists": "^1.2.9",48 "command-exists": "^1.2.9",
49 "compression": "^1.8.1",49 "compression": "^1.8.1",
50 "cookie-parser": "^1.4.6",50 "cookie-parser": "^1.4.6",
@@ -64,6 +64,7 @@
64 "handlebars": "^4.7.8",64 "handlebars": "^4.7.8",
65 "helmet": "^8.1.0",65 "helmet": "^8.1.0",
66 "highlight.js": "^11.11.1",66 "highlight.js": "^11.11.1",
67 "host-validation-middleware": "^0.1.1",
67 "html-entities": "^2.6.0",68 "html-entities": "^2.6.0",
68 "iconv-lite": "^0.6.3",69 "iconv-lite": "^0.6.3",
69 "ip-matching": "^2.1.2",70 "ip-matching": "^2.1.2",
@@ -74,7 +75,7 @@
74 "lodash": "^4.17.21",75 "lodash": "^4.17.21",
75 "mime-types": "^3.0.1",76 "mime-types": "^3.0.1",
76 "moment": "^2.30.1",77 "moment": "^2.30.1",
77 "morphdom": "^2.7.5",78 "morphdom": "^2.7.7",
78 "multer": "^2.0.2",79 "multer": "^2.0.2",
79 "node-fetch": "^3.3.2",80 "node-fetch": "^3.3.2",
80 "node-persist": "^4.0.4",81 "node-persist": "^4.0.4",
@@ -90,14 +91,14 @@
90 "sillytavern-transformers": "2.14.6",91 "sillytavern-transformers": "2.14.6",
91 "simple-git": "^3.28.0",92 "simple-git": "^3.28.0",
92 "slidetoggle": "^4.0.0",93 "slidetoggle": "^4.0.0",
93 "tiktoken": "^1.0.21",94 "tiktoken": "^1.0.22",
94 "url-join": "^5.0.0",95 "url-join": "^5.0.0",
95 "vectra": "^0.2.2",96 "vectra": "^0.2.2",
96 "wavefile": "^11.0.0",97 "wavefile": "^11.0.0",
97 "webpack": "^5.98.0",98 "webpack": "^5.98.0",
98 "write-file-atomic": "^5.0.1",99 "write-file-atomic": "^5.0.1",
99 "ws": "^8.18.3",100 "ws": "^8.18.3",
100 "yaml": "^2.8.0",101 "yaml": "^2.8.1",
101 "yargs": "^17.7.1",102 "yargs": "^17.7.1",
102 "yauzl": "^3.2.0"103 "yauzl": "^3.2.0"
103 },104 },
@@ -114,7 +115,7 @@
114 "@types/cors": "^2.8.19",115 "@types/cors": "^2.8.19",
115 "@types/deno": "^2.3.0",116 "@types/deno": "^2.3.0",
116 "@types/express": "^4.17.23",117 "@types/express": "^4.17.23",
117 "@types/jquery": "^3.5.32",118 "@types/jquery": "^3.5.33",
118 "@types/jquery-cropper": "^1.0.4",119 "@types/jquery-cropper": "^1.0.4",
119 "@types/jquery.transit": "^0.9.33",120 "@types/jquery.transit": "^0.9.33",
120 "@types/jqueryui": "^1.12.24",121 "@types/jqueryui": "^1.12.24",
@@ -149,9 +150,9 @@
149 }150 }
150 },151 },
151 "node_modules/@adobe/css-tools": {152 "node_modules/@adobe/css-tools": {
152 "version": "4.4.3",153 "version": "4.4.4",
153 "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz",154 "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
154 "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==",155 "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==",
155 "license": "MIT"156 "license": "MIT"
156 },157 },
157 "node_modules/@agnai/sentencepiece-js": {158 "node_modules/@agnai/sentencepiece-js": {
@@ -1911,9 +1912,9 @@
1911 "license": "MIT"1912 "license": "MIT"
1912 },1913 },
1913 "node_modules/@types/jquery": {1914 "node_modules/@types/jquery": {
1914 "version": "3.5.32",1915 "version": "3.5.33",
1915 "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz",1916 "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz",
1916 "integrity": "sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==",1917 "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==",
1917 "dev": true,1918 "dev": true,
1918 "license": "MIT",1919 "license": "MIT",
1919 "dependencies": {1920 "dependencies": {
@@ -2896,9 +2897,9 @@
2896 "license": "ISC"2897 "license": "ISC"
2897 },2898 },
2898 "node_modules/bowser": {2899 "node_modules/bowser": {
2899 "version": "2.11.0",2900 "version": "2.12.1",
2900 "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",2901 "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz",
2901 "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==",2902 "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==",
2902 "license": "MIT"2903 "license": "MIT"
2903 },2904 },
2904 "node_modules/brace-expansion": {2905 "node_modules/brace-expansion": {
@@ -3126,9 +3127,9 @@
3126 }3127 }
3127 },3128 },
3128 "node_modules/chalk": {3129 "node_modules/chalk": {
3129 "version": "5.4.1",3130 "version": "5.6.0",
3130 "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",3131 "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
3131 "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",3132 "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
3132 "license": "MIT",3133 "license": "MIT",
3133 "engines": {3134 "engines": {
3134 "node": "^12.17.0 || ^14.13 || >=16.0.0"3135 "node": "^12.17.0 || ^14.13 || >=16.0.0"
@@ -5249,6 +5250,15 @@
5249 "node": ">=12.0.0"5250 "node": ">=12.0.0"
5250 }5251 }
5251 },5252 },
5253 "node_modules/host-validation-middleware": {
5254 "version": "0.1.1",
5255 "resolved": "https://registry.npmjs.org/host-validation-middleware/-/host-validation-middleware-0.1.1.tgz",
5256 "integrity": "sha512-fakcpp+x4nbP0fACY5gaHWpaOfstq3w8uB6wvhbPBLqH9GV/tdiM9Ht5mclZVbUuPLGBw1bkH5yyTD6HZq057g==",
5257 "license": "MIT",
5258 "engines": {
5259 "node": "^18.0.0 || >=20.0.0"
5260 }
5261 },
5252 "node_modules/html-entities": {5262 "node_modules/html-entities": {
5253 "version": "2.6.0",5263 "version": "2.6.0",
5254 "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",5264 "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
@@ -6178,9 +6188,9 @@
6178 }6188 }
6179 },6189 },
6180 "node_modules/morphdom": {6190 "node_modules/morphdom": {
6181 "version": "2.7.5",6191 "version": "2.7.7",
6182 "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.5.tgz",6192 "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.7.tgz",
6183 "integrity": "sha512-z6bfWFMra7kBqDjQGHud1LSXtq5JJC060viEkQFMBX6baIecpkNr2Ywrn2OQfWP3rXiNFQRPoFjD8/TvJcWcDg==",6193 "integrity": "sha512-04GmsiBcalrSCNmzfo+UjU8tt3PhZJKzcOy+r1FlGA7/zri8wre3I1WkYN9PT3sIeIKfW9bpyElA+VzOg2E24g==",
6184 "license": "MIT"6194 "license": "MIT"
6185 },6195 },
6186 "node_modules/ms": {6196 "node_modules/ms": {
@@ -8001,9 +8011,9 @@
8001 "license": "MIT"8011 "license": "MIT"
8002 },8012 },
8003 "node_modules/tiktoken": {8013 "node_modules/tiktoken": {
8004 "version": "1.0.21",8014 "version": "1.0.22",
8005 "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.21.tgz",8015 "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.22.tgz",
8006 "integrity": "sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==",8016 "integrity": "sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==",
8007 "license": "MIT"8017 "license": "MIT"
8008 },8018 },
8009 "node_modules/timm": {8019 "node_modules/timm": {
@@ -8610,9 +8620,9 @@
8610 }8620 }
8611 },8621 },
8612 "node_modules/yaml": {8622 "node_modules/yaml": {
8613 "version": "2.8.0",8623 "version": "2.8.1",
8614 "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",8624 "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
8615 "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",8625 "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
8616 "license": "ISC",8626 "license": "ISC",
8617 "bin": {8627 "bin": {
8618 "yaml": "bin.mjs"8628 "yaml": "bin.mjs"
package.json+9 -8
@@ -1,6 +1,6 @@
1{1{
2 "dependencies": {2 "dependencies": {
3 "@adobe/css-tools": "^4.4.3",3 "@adobe/css-tools": "^4.4.4",
4 "@agnai/sentencepiece-js": "^1.1.1",4 "@agnai/sentencepiece-js": "^1.1.1",
5 "@agnai/web-tokenizers": "^0.1.3",5 "@agnai/web-tokenizers": "^0.1.3",
6 "@iconfu/svg-inject": "^1.2.3",6 "@iconfu/svg-inject": "^1.2.3",
@@ -32,9 +32,9 @@
32 "archiver": "^7.0.1",32 "archiver": "^7.0.1",
33 "bing-translate-api": "^4.1.0",33 "bing-translate-api": "^4.1.0",
34 "body-parser": "^1.20.2",34 "body-parser": "^1.20.2",
35 "bowser": "^2.11.0",35 "bowser": "^2.12.1",
36 "bytes": "^3.1.2",36 "bytes": "^3.1.2",
37 "chalk": "^5.4.1",37 "chalk": "^5.6.0",
38 "command-exists": "^1.2.9",38 "command-exists": "^1.2.9",
39 "compression": "^1.8.1",39 "compression": "^1.8.1",
40 "cookie-parser": "^1.4.6",40 "cookie-parser": "^1.4.6",
@@ -54,6 +54,7 @@
54 "handlebars": "^4.7.8",54 "handlebars": "^4.7.8",
55 "helmet": "^8.1.0",55 "helmet": "^8.1.0",
56 "highlight.js": "^11.11.1",56 "highlight.js": "^11.11.1",
57 "host-validation-middleware": "^0.1.1",
57 "html-entities": "^2.6.0",58 "html-entities": "^2.6.0",
58 "iconv-lite": "^0.6.3",59 "iconv-lite": "^0.6.3",
59 "ip-matching": "^2.1.2",60 "ip-matching": "^2.1.2",
@@ -64,7 +65,7 @@
64 "lodash": "^4.17.21",65 "lodash": "^4.17.21",
65 "mime-types": "^3.0.1",66 "mime-types": "^3.0.1",
66 "moment": "^2.30.1",67 "moment": "^2.30.1",
67 "morphdom": "^2.7.5",68 "morphdom": "^2.7.7",
68 "multer": "^2.0.2",69 "multer": "^2.0.2",
69 "node-fetch": "^3.3.2",70 "node-fetch": "^3.3.2",
70 "node-persist": "^4.0.4",71 "node-persist": "^4.0.4",
@@ -80,14 +81,14 @@
80 "sillytavern-transformers": "2.14.6",81 "sillytavern-transformers": "2.14.6",
81 "simple-git": "^3.28.0",82 "simple-git": "^3.28.0",
82 "slidetoggle": "^4.0.0",83 "slidetoggle": "^4.0.0",
83 "tiktoken": "^1.0.21",84 "tiktoken": "^1.0.22",
84 "url-join": "^5.0.0",85 "url-join": "^5.0.0",
85 "vectra": "^0.2.2",86 "vectra": "^0.2.2",
86 "wavefile": "^11.0.0",87 "wavefile": "^11.0.0",
87 "webpack": "^5.98.0",88 "webpack": "^5.98.0",
88 "write-file-atomic": "^5.0.1",89 "write-file-atomic": "^5.0.1",
89 "ws": "^8.18.3",90 "ws": "^8.18.3",
90 "yaml": "^2.8.0",91 "yaml": "^2.8.1",
91 "yargs": "^17.7.1",92 "yargs": "^17.7.1",
92 "yauzl": "^3.2.0"93 "yauzl": "^3.2.0"
93 },94 },
@@ -112,7 +113,7 @@
112 "type": "git",113 "type": "git",
113 "url": "https://github.com/SillyTavern/SillyTavern.git"114 "url": "https://github.com/SillyTavern/SillyTavern.git"
114 },115 },
115 "version": "1.13.3",116 "version": "1.13.4",
116 "scripts": {117 "scripts": {
117 "start": "node server.js",118 "start": "node server.js",
118 "debug": "node --inspect server.js",119 "debug": "node --inspect server.js",
@@ -145,7 +146,7 @@
145 "@types/cors": "^2.8.19",146 "@types/cors": "^2.8.19",
146 "@types/deno": "^2.3.0",147 "@types/deno": "^2.3.0",
147 "@types/express": "^4.17.23",148 "@types/express": "^4.17.23",
148 "@types/jquery": "^3.5.32",149 "@types/jquery": "^3.5.33",
149 "@types/jquery-cropper": "^1.0.4",150 "@types/jquery-cropper": "^1.0.4",
150 "@types/jquery.transit": "^0.9.33",151 "@types/jquery.transit": "^0.9.33",
151 "@types/jqueryui": "^1.12.24",152 "@types/jqueryui": "^1.12.24",
public/css/backgrounds.css+229 -0
@@ -0,0 +1,229 @@
1/* Main Page Backgrounds */
2#bg1,
3#bg_custom {
4 background-repeat: no-repeat;
5 background-attachment: fixed;
6 background-size: cover;
7 position: absolute;
8 width: 100%;
9 height: 100%;
10 transition: background-image var(--animation-duration-3x) ease-in-out;
11}
12
13/* Fitting options */
14#background_fitting {
15 max-width: 6em;
16}
17
18/* Fill/Cover - scales to fill width while maintaining aspect ratio */
19#bg1.cover,
20#bg_custom.cover {
21 background-size: cover;
22 background-position: center;
23}
24
25/* Fit/Contain - shows entire image maintaining aspect ratio */
26#bg1.contain,
27#bg_custom.contain {
28 background-size: contain;
29 background-position: center;
30 background-repeat: no-repeat;
31}
32
33/* Stretch - stretches to fill entire space */
34#bg1.stretch,
35#bg_custom.stretch {
36 background-size: 100% 100%;
37}
38
39/* Center - centers without scaling */
40#bg1.center,
41#bg_custom.center {
42 background-size: auto;
43 background-position: center;
44 background-repeat: no-repeat;
45}
46
47body.reduced-motion #bg1,
48body.reduced-motion #bg_custom {
49 transition: none;
50}
51
52#bg1 {
53 background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
54 z-index: -3;
55}
56
57#bg_custom {
58 background-image: none;
59 z-index: -2;
60}
61
62.bg_example.flex-container.locked:not(:focus-visible) {
63 outline-color: var(--golden);
64}
65
66/* This is the main flex container for the entire drawer */
67#Backgrounds.drawer-content.openDrawer.bg-drawer-layout {
68 display: flex;
69 flex-direction: column;
70 height: calc(100vh - var(--topBarBlockSize));
71 max-height: calc(100vh - var(--topBarBlockSize));
72 height: calc(100dvh - var(--topBarBlockSize));
73 max-height: calc(100dvh - var(--topBarBlockSize));
74 overflow: hidden;
75 width: var(--sheldWidth);
76 max-width: var(--sheldWidth);
77 padding: 0;
78}
79
80#bg-header-fixed {
81 flex-shrink: 0;
82 padding: 5px;
83 background-color: var(--SmartThemeBlurTintColor);
84 border-bottom: 1px solid var(--SmartThemeBorderColor);
85}
86
87#bg-header-fixed>.flex-container {
88 display: flex;
89 align-items: center;
90 gap: 5px;
91}
92
93#bg-scrollable-content {
94 flex-grow: 1;
95 overflow-y: auto;
96 overflow-x: hidden;
97 padding: 0 5px 5px;
98}
99
100#bg-filter {
101 font-size: calc(var(--mainFontSize) * 0.95);
102}
103
104/* Thumbnail Menu & Buttons */
105.bg_example .mobile-only-menu-toggle {
106 display: none;
107}
108
109.bg_example.flex-container {
110 width: 30%;
111 max-width: 200px;
112 margin: 5px;
113 aspect-ratio: 16/9;
114 cursor: pointer;
115 box-shadow: 0 0 7px var(--black50a);
116
117 position: relative;
118 overflow: hidden;
119 border-radius: 8px;
120 border: 0px solid transparent;
121 outline: 2px solid var(--SmartThemeBorderColor);
122 outline-offset: -1px;
123}
124
125.bg_example.flex-container:focus-visible {
126 outline-offset: inherit;
127}
128
129.bg_example_img {
130 position: absolute;
131 top: -2px;
132 left: -2px;
133 right: -2px;
134 bottom: -2px;
135
136 background-image: inherit;
137
138 background-size: cover;
139 background-position: center;
140}
141
142.bg_example .jg-menu {
143 display: flex;
144 position: absolute;
145 top: 2px;
146 right: 2px;
147 background-color: rgba(0, 0, 0, 0.5);
148 border-radius: 8px;
149 gap: 3px;
150 padding: 3px 5px;
151 z-index: 3;
152 backdrop-filter: blur(4px);
153 border: 1px solid var(--SmartThemeBorderColor);
154 justify-items: center;
155 align-items: center;
156
157 opacity: 0;
158 visibility: hidden;
159 transform: scale(0.9);
160 transform-origin: center;
161 transition: opacity var(--animation-duration) ease-out, visibility var(--animation-duration) ease-out, transform var(--animation-duration) ease-out;
162}
163
164.bg_example:hover .jg-menu,
165.bg_example:focus-within .jg-menu {
166 opacity: 1;
167 visibility: visible;
168 transform: scale(1);
169}
170
171.bg_example .jg-button {
172 display: flex;
173 width: 30px;
174 height: 30px;
175 align-items: center;
176 justify-content: center;
177 color: white;
178 padding: 5px;
179 font-size: 1.1em;
180 border-radius: 6px;
181 transition: background-color var(--animation-duration) ease;
182}
183
184.bg_example .jg-button:hover {
185 background-color: rgba(255, 255, 255, 0.2);
186}
187
188.bg_example .jg-unlock {
189 display: none;
190}
191
192.bg_example.locked .jg-lock {
193 display: none;
194}
195
196.bg_example.locked .jg-unlock {
197 display: flex;
198}
199
200.bg_example:not([custom="true"]) .jg-copy,
201.bg_example[custom="true"] .jg-edit {
202 display: none;
203}
204
205/* Thumbnail Title */
206.bg_example .BGSampleTitle {
207 position: absolute;
208 bottom: 0;
209 left: 0;
210 right: 0;
211 background: linear-gradient(transparent, rgba(0, 0, 0, 0.9));
212 color: var(--SmartThemeBodyColor);
213 font-size: 0.9em;
214 font-weight: 600;
215 padding: 0px 6px 2px;
216 text-align: center;
217 white-space: nowrap;
218 overflow: hidden;
219 text-overflow: ellipsis;
220 opacity: 0;
221 transition: opacity var(--animation-duration) ease-in-out;
222 pointer-events: none;
223 border-radius: 0 0 8px 8px;
224}
225
226.bg_example:hover .BGSampleTitle,
227.bg_example:focus-within .BGSampleTitle {
228 opacity: 1;
229}
public/css/mobile-styles.css+81 -4
@@ -25,6 +25,87 @@
25 font-size: 15px;25 font-size: 15px;
26 }26 }
2727
28 #Backgrounds .bg_example .BGSampleTitle {
29 opacity: 1;
30 bottom: 0px;
31 }
32
33 .bg_example:hover .jg-menu,
34 .bg_example:focus-within .jg-menu {
35 display: none;
36 }
37
38 .bg_example.mobile-menu-open .jg-menu {
39 display: flex;
40 z-index: 4;
41 }
42
43 .bg_example .mobile-only-menu-toggle {
44 display: flex;
45 align-items: center;
46 justify-content: center;
47 position: absolute;
48 top: 5px;
49 right: 5px;
50 width: 30px;
51 height: 30px;
52 background-color: rgba(0, 0, 0, 0.4);
53 color: white;
54 border-radius: 6px;
55 z-index: 3;
56 cursor: pointer;
57 backdrop-filter: blur(2px);
58 }
59
60 #bg-header-controls {
61 flex-wrap: wrap;
62 row-gap: 10px;
63 }
64
65 #bg-header-fixed>.flex-container {
66 flex-wrap: wrap;
67 row-gap: 0px;
68 }
69
70 #Backgrounds:not(.selection-mode-active) #bg-header-fixed>.flex-container::after {
71 content: '';
72 order: 1;
73 flex-basis: 100%;
74 height: 0;
75 }
76
77 /* --- Row 1 Item --- */
78 #bg-header-fixed #bg-header-title {
79 order: 1;
80 flex-grow: 1;
81 }
82
83 #bg-header-fixed #background_fitting,
84 #bg-header-fixed #auto_background {
85 order: 1;
86 }
87
88 /* --- Row 2 Item --- */
89 #bg-header-fixed #bg-filter {
90 order: 2;
91 flex-grow: 1;
92 min-width: 0;
93 }
94
95 /* --- Row 3 Item --- */
96 #bg-header-fixed #add_background_button_top {
97 order: 3;
98 width: 100%;
99 text-align: center;
100 padding-top: 0.5em;
101 padding-bottom: 0.5em;
102 }
103
104 #Backgrounds.drawer-content.openDrawer.bg-drawer-layout {
105 width: 100dvw;
106 max-width: 100dvw;
107 }
108
28 #extensions_settings,109 #extensions_settings,
29 #extensions_settings2 {110 #extensions_settings2 {
30 width: 100% !important;111 width: 100% !important;
@@ -412,10 +493,6 @@
412 flex-basis: max(calc(100% / 2 - 10px), 180px);493 flex-basis: max(calc(100% / 2 - 10px), 180px);
413 }494 }
414495
415 .BGSampleTitle {
416 display: none;
417 }
418
419 .tag.excluded:after {496 .tag.excluded:after {
420 top: unset;497 top: unset;
421 bottom: unset;498 bottom: unset;
public/css/popup.css+25 -8
@@ -26,7 +26,6 @@ dialog {
2626
27 /* Fix weird animation issue with font-scaling during popup open */27 /* Fix weird animation issue with font-scaling during popup open */
28 backface-visibility: hidden;28 backface-visibility: hidden;
29 transform: translateZ(0);
30 -webkit-font-smoothing: subpixel-antialiased;29 -webkit-font-smoothing: subpixel-antialiased;
3130
32 /* Variables setup */31 /* Variables setup */
@@ -93,6 +92,11 @@ dialog {
93 animation: fade-in var(--popup-animation-speed) ease-in-out;92 animation: fade-in var(--popup-animation-speed) ease-in-out;
94}93}
9594
95/* Fix toast container snapping into the backdrop while the animation is running */
96.popup[opening] #toast-container {
97 visibility: hidden;
98}
99
96/* Open state of the dialog */100/* Open state of the dialog */
97.popup[open] {101.popup[open] {
98 color: var(--SmartThemeBodyColor);102 color: var(--SmartThemeBodyColor);
@@ -118,17 +122,30 @@ body.no-blur .popup[open]::backdrop {
118 animation: fade-out var(--popup-animation-speed) ease-in-out;122 animation: fade-out var(--popup-animation-speed) ease-in-out;
119}123}
120124
121.popup #toast-container {125/* Edge inset to match Toastr default spacing */
122 /* Fix toastr in dialogs by actually placing it at the top of the screen via transform */126:root {
123 height: 100dvh;127 --toast-edge: 12px;
124 top: calc(50% + var(--topBarBlockSize));128}
125 left: 50%;
126 transform: translate(-50%, -50%);
127129
128 /* Fix text align, popups are centered by default. toasts should not. */130.popup #toast-container {
131 /* Popups are centered by default; toasts should not be */
129 text-align: left;132 text-align: left;
130}133}
131134
135/* Per-position position adjustments caused by the top bar, inside the popup */
136.popup #toast-container.toast-top-left {
137 top: calc(var(--toast-edge) + var(--topBarBlockSize));
138}
139
140.popup #toast-container.toast-top-center {
141 /* toastr in core does not have a top offset on center, so we don't do that either in popups */
142 top: var(--topBarBlockSize);
143}
144
145.popup #toast-container.toast-top-right {
146 top: calc(var(--toast-edge) + var(--topBarBlockSize));
147}
148
132.popup-crop-wrap {149.popup-crop-wrap {
133 margin: 10px auto;150 margin: 10px auto;
134 max-height: 75vh;151 max-height: 75vh;
public/global.d.ts+11 -11
@@ -5,20 +5,20 @@ import { QuickReplyApi } from './scripts/extensions/quick-reply/api/QuickReplyAp
55
6declare global {6declare global {
7 // Custom types7 // Custom types
8 declare type InstructSettings = typeof power_user.instruct;8 type InstructSettings = typeof power_user.instruct;
9 declare type ContextSettings = typeof power_user.context;9 type ContextSettings = typeof power_user.context;
10 declare type ReasoningSettings = typeof power_user.reasoning;10 type ReasoningSettings = typeof power_user.reasoning;
1111
12 // Global namespace modules12 // Global namespace modules
13 interface Window {13 interface Window {
14 ai: any;14 ai: any;
15 }15 }
1616
17 declare var pdfjsLib;17 var pdfjsLib;
18 declare var ePub;18 var ePub;
19 declare var quickReplyApi: QuickReplyApi;19 var quickReplyApi: QuickReplyApi;
2020
21 declare var SillyTavern: {21 var SillyTavern: {
22 getContext(): typeof getContext;22 getContext(): typeof getContext;
23 llm: any;23 llm: any;
24 libs: typeof libs;24 libs: typeof libs;
@@ -63,7 +63,7 @@ declare global {
63 * @param lang Target language63 * @param lang Target language
64 * @param provider Translation provider64 * @param provider Translation provider
65 */65 */
66 async function translate(text: string, lang: string, provider: string = null): Promise<string>;66 function translate(text: string, lang: string, provider?: string | null): Promise<string>;
6767
68 interface ConvertVideoArgs {68 interface ConvertVideoArgs {
69 buffer: Uint8Array;69 buffer: Uint8Array;
@@ -76,9 +76,9 @@ declare global {
76 */76 */
77 function convertVideoToAnimatedWebp(args: ConvertVideoArgs): Promise<Uint8Array>;77 function convertVideoToAnimatedWebp(args: ConvertVideoArgs): Promise<Uint8Array>;
7878
79 interface ColorPickerEvent extends JQuery.ChangeEvent<HTMLElement> {79 type ColorPickerEvent = Omit<JQuery.ChangeEvent<HTMLElement>, "detail"> & {
80 detail: {80 detail: {
81 rgba: string;81 rgba: string;
82 };82 }
83 }83 };
84}84}
public/img/azure_openai.svg+1 -0
@@ -0,0 +1 @@
1<svg id="uuid-adbdae8e-5a41-46d1-8c18-aa73cdbfee32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" height="100px" width="100px" transform="rotate(0) scale(1, 1)"><path d="m0,2.7v12.6c0,1.491,1.209,2.7,2.7,2.7h12.6c1.491,0,2.7-1.209,2.7-2.7V2.7c0-1.491-1.209-2.7-2.7-2.7H2.7C1.209,0,0,1.209,0,2.7ZM10.8,0v3.6c0,3.976,3.224,7.2,7.2,7.2h-3.6c-3.976,0-7.199,3.222-7.2,7.198v-3.598c0-3.976-3.224-7.2-7.2-7.2h3.6c3.976,0,7.2-3.224,7.2-7.2Z" stroke-width="0"/></svg>
public/img/electronhub.svg+1 -0
@@ -0,0 +1 @@
1<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="118.66667" height="177.33333" viewBox="0 0 89 133"><path d="M14.5 1.8c-6.3 3-11.3 8.8-13.2 15.1C-.1 21.8-.2 27.6.6 70.6l.9 48.2 3.1 4.4c1.9 2.6 5.3 5.4 8.4 7l5.2 2.8h26.7c14.8 0 28.2-.5 30.2-1 5.4-1.5 11.6-8.6 12.4-14.3 1-6.5-.2-10.4-4.7-15.3-5.1-5.5-8.3-6.4-23.3-6.4C46.2 96 43 94.9 43 90.5c0-4.3 3.3-5.5 15.4-5.5 13 0 18-1.8 22.7-8.3 5.4-7.5 5-14.4-1.3-21.3-5.1-5.5-11-7.4-22.8-7.4-9.2 0-13-1.5-13-5 0-3.9 3.6-5 16.9-5 14.5 0 19.4-1.6 23.7-7.9 5.4-7.9 5.2-15.9-.6-22.5-5.4-6.1-6.7-6.4-37.5-7.1-26.3-.6-28.2-.5-32 1.3z"/></svg>
\ No newline at end of file1 \ No newline at end of file
public/index.html+104 -36
@@ -652,7 +652,7 @@
652 <input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536">652 <input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536">
653 </div>653 </div>
654 </div>654 </div>
655 <div class="range-block" data-source="openai,custom,xai,aimlapi,moonshot">655 <div class="range-block" data-source="openai,custom,xai,aimlapi,moonshot,azure_openai">
656 <div class="range-block-title" data-i18n="Multiple swipes per generation">656 <div class="range-block-title" data-i18n="Multiple swipes per generation">
657 Multiple swipes per generation657 Multiple swipes per generation
658 </div>658 </div>
@@ -691,7 +691,7 @@
691 </span>691 </span>
692 </div>692 </div>
693 </div>693 </div>
694 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi">694 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai">
695 <div class="range-block-title" data-i18n="Temperature">695 <div class="range-block-title" data-i18n="Temperature">
696 Temperature696 Temperature
697 </div>697 </div>
@@ -704,7 +704,7 @@
704 </div>704 </div>
705 </div>705 </div>
706 </div>706 </div>
707 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi">707 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai">
708 <div class="range-block-title" data-i18n="Frequency Penalty">708 <div class="range-block-title" data-i18n="Frequency Penalty">
709 Frequency Penalty709 Frequency Penalty
710 </div>710 </div>
@@ -717,7 +717,7 @@
717 </div>717 </div>
718 </div>718 </div>
719 </div>719 </div>
720 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi">720 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai">
721 <div class="range-block-title" data-i18n="Presence Penalty">721 <div class="range-block-title" data-i18n="Presence Penalty">
722 Presence Penalty722 Presence Penalty
723 </div>723 </div>
@@ -730,7 +730,7 @@
730 </div>730 </div>
731 </div>731 </div>
732 </div>732 </div>
733 <div class="range-block" data-source="claude,aimlapi,openrouter,makersuite,vertexai,cohere,perplexity">733 <div class="range-block" data-source="claude,aimlapi,openrouter,makersuite,vertexai,cohere,perplexity,electronhub">
734 <div class="range-block-title" data-i18n="Top K">734 <div class="range-block-title" data-i18n="Top K">
735 Top K735 Top K
736 </div>736 </div>
@@ -743,7 +743,7 @@
743 </div>743 </div>
744 </div>744 </div>
745 </div>745 </div>
746 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi">746 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai">
747 <div class="range-block-title" data-i18n="Top P">747 <div class="range-block-title" data-i18n="Top P">
748 Top P748 Top P
749 </div>749 </div>
@@ -980,7 +980,7 @@
980 </div>980 </div>
981 </div>981 </div>
982 </div>982 </div>
983 <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt,xai,pollinations,aimlapi,makersuite,vertexai">983 <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,electronhub,nanogpt,xai,pollinations,aimlapi,makersuite,vertexai,azure_openai">
984 <div class="range-block-title justifyLeft" data-i18n="Seed">984 <div class="range-block-title justifyLeft" data-i18n="Seed">
985 Seed985 Seed
986 </div>986 </div>
@@ -1970,7 +1970,7 @@
1970 </span>1970 </span>
1971 </div>1971 </div>
1972 </div>1972 </div>
1973 <div class="range-block" data-source="makersuite,vertexai,aimlapi,openrouter,claude,xai,nanogpt">1973 <div class="range-block" data-source="makersuite,vertexai,aimlapi,openrouter,claude,xai,electronhub,nanogpt">
1974 <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand">1974 <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand">
1975 <input id="openai_enable_web_search" type="checkbox" />1975 <input id="openai_enable_web_search" type="checkbox" />
1976 <span data-i18n="Enable web search">Enable web search</span>1976 <span data-i18n="Enable web search">Enable web search</span>
@@ -1984,7 +1984,7 @@
1984 </b>1984 </b>
1985 </div>1985 </div>
1986 </div>1986 </div>
1987 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks,cometapi">1987 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks,cometapi,electronhub,azure_openai">
1988 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">1988 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
1989 <input id="openai_function_calling" type="checkbox" />1989 <input id="openai_function_calling" type="checkbox" />
1990 <span data-i18n="Enable function calling">Enable function calling</span>1990 <span data-i18n="Enable function calling">Enable function calling</span>
@@ -1999,7 +1999,7 @@
1999 <strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing with "no tools" is used!</strong>1999 <strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing with "no tools" is used!</strong>
2000 </div>2000 </div>
2001 </div>2001 </div>
2002 <div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations,moonshot,cohere,cometapi">2002 <div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations,moonshot,cohere,cometapi,nanogpt,electronhub,azure_openai">
2003 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">2003 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
2004 <input id="openai_image_inlining" type="checkbox" />2004 <input id="openai_image_inlining" type="checkbox" />
2005 <span data-i18n="Send inline images">Send inline images</span>2005 <span data-i18n="Send inline images">Send inline images</span>
@@ -2015,7 +2015,7 @@
2015 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>2015 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>
2016 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>2016 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>
2017 </div>2017 </div>
2018 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations,cohere">2018 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations,cohere,cometapi,nanogpt,moonshot,aimlapi,openrouter,mistralai,electronhub,azure_openai">
2019 <div class="flex-container oneline-dropdown">2019 <div class="flex-container oneline-dropdown">
2020 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">2020 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
2021 Inline Image Quality2021 Inline Image Quality
@@ -2077,7 +2077,7 @@
2077 </span>2077 </span>
2078 </div>2078 </div>
2079 </div>2079 </div>
2080 <div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot,mistralai,fireworks,cometapi">2080 <div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot,mistralai,fireworks,cometapi,electronhub,azure_openai">
2081 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">2081 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
2082 <input id="openai_show_thoughts" type="checkbox" />2082 <input id="openai_show_thoughts" type="checkbox" />
2083 <span data-i18n="Request model reasoning">Request model reasoning</span>2083 <span data-i18n="Request model reasoning">Request model reasoning</span>
@@ -2091,7 +2091,7 @@
2091 </span>2091 </span>
2092 </div>2092 </div>
2093 </div>2093 </div>
2094 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai,makersuite,vertexai,aimlapi,openrouter,pollinations,perplexity,cometapi">2094 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai,makersuite,vertexai,aimlapi,openrouter,pollinations,perplexity,cometapi,electronhub,azure_openai">
2095 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">2095 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">
2096 <label for="openai_reasoning_effort">2096 <label for="openai_reasoning_effort">
2097 <span data-i18n="Reasoning Effort">Reasoning Effort</span>2097 <span data-i18n="Reasoning Effort">Reasoning Effort</span>
@@ -2105,7 +2105,7 @@
2105 <option data-i18n="openai_reasoning_effort_high" value="high">High</option>2105 <option data-i18n="openai_reasoning_effort_high" value="high">High</option>
2106 <option data-i18n="openai_reasoning_effort_maximum" value="max">Maximum</option>2106 <option data-i18n="openai_reasoning_effort_maximum" value="max">Maximum</option>
2107 </select>2107 </select>
2108 <div class="toggle-description justifyLeft marginBot5" data-source="openai,custom,xai,aimlapi,openrouter,perplexity" data-i18n="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.">2108 <div class="toggle-description justifyLeft marginBot5" data-source="openai,custom,xai,aimlapi,openrouter,perplexity,electronhub,azure_openai" data-i18n="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.">
2109 OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.2109 OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.
2110 </div>2110 </div>
2111 <div class="toggle-description justifyLeft marginBot5" data-source="claude" data-i18n="Allocates a portion of the response length for thinking (min: 1024 tokens, low: 10%, medium: 25%, high: 50%, max: 95%), but minimum 1024 tokens. Auto does not request thinking.">2111 <div class="toggle-description justifyLeft marginBot5" data-source="claude" data-i18n="Allocates a portion of the response length for thinking (min: 1024 tokens, low: 10%, medium: 25%, high: 50%, max: 95%), but minimum 1024 tokens. Auto does not request thinking.">
@@ -2144,7 +2144,7 @@
2144 </div>2144 </div>
2145 </div>2145 </div>
2146 </div>2146 </div>
2147 <div class="range-block m-t-1" data-source="openai,aimlapi,openrouter,custom">2147 <div class="range-block m-t-1" data-source="openai,aimlapi,openrouter,custom,azure_openai">
2148 <div id="logit_bias_openai" class="range-block-title openai_restorable" data-i18n="Logit Bias">2148 <div id="logit_bias_openai" class="range-block-title openai_restorable" data-i18n="Logit Bias">
2149 Logit Bias2149 Logit Bias
2150 </div>2150 </div>
@@ -2802,11 +2802,13 @@
2802 <optgroup>2802 <optgroup>
2803 <option value="ai21">AI21</option>2803 <option value="ai21">AI21</option>
2804 <option value="aimlapi">AI/ML API</option>2804 <option value="aimlapi">AI/ML API</option>
2805 <option value="azure_openai">Azure OpenAI</option>
2805 <option value="claude">Claude</option>2806 <option value="claude">Claude</option>
2806 <option value="cohere">Cohere</option>2807 <option value="cohere">Cohere</option>
2807 <!-- Temporarily disabled. -->2808 <!-- Temporarily disabled. -->
2808 <!-- <option value="cometapi">CometAPI</option> -->2809 <!-- <option value="cometapi">CometAPI</option> -->
2809 <option value="deepseek">DeepSeek</option>2810 <option value="deepseek">DeepSeek</option>
2811 <option value="electronhub">Electron Hub</option>
2810 <option value="fireworks">Fireworks AI</option>2812 <option value="fireworks">Fireworks AI</option>
2811 <option value="groq">Groq</option>2813 <option value="groq">Groq</option>
2812 <option value="makersuite">Google AI Studio</option>2814 <option value="makersuite">Google AI Studio</option>
@@ -3178,6 +3180,7 @@
3178 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>3180 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
3179 <option value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>3181 <option value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>
3180 <option value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>3182 <option value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>
3183 <option value="gemini-2.5-flash-image-preview">gemini-2.5-flash-image-preview</option>
3181 </optgroup>3184 </optgroup>
3182 <optgroup label="Gemini 2.0">3185 <optgroup label="Gemini 2.0">
3183 <option value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>3186 <option value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>
@@ -3356,6 +3359,7 @@
3356 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>3359 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
3357 <option value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>3360 <option value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>
3358 <option value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>3361 <option value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>
3362 <option value="gemini-2.5-flash-image-preview">gemini-2.5-flash-image-preview</option>
3359 </optgroup>3363 </optgroup>
3360 <optgroup label="Gemini 2.0">3364 <optgroup label="Gemini 2.0">
3361 <option value="gemini-2.0-flash-exp" data-mode="full">gemini-2.0-flash-exp</option>3365 <option value="gemini-2.0-flash-exp" data-mode="full">gemini-2.0-flash-exp</option>
@@ -3467,6 +3471,20 @@
3467 <option value="mistral-saba-24b">mistral-saba-24b</option>3471 <option value="mistral-saba-24b">mistral-saba-24b</option>
3468 </select>3472 </select>
3469 </div>3473 </div>
3474 <div id="electronhub_form" data-source="electronhub">
3475 <h4 data-i18n="Electron Hub API Key">Electron Hub API Key</h4>
3476 <div class="flex-container">
3477 <input id="api_key_electronhub" name="api_key_electronhub" class="text_pole flex1" value="" type="text" autocomplete="off">
3478 <div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_electronhub"></div>
3479 </div>
3480 <div data-for="api_key_electronhub" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
3481 For privacy reasons, your API key will be hidden after you click 'Connect'.
3482 </div>
3483 <h4 data-i18n="Electron Hub Model">Electron Hub Model</h4>
3484 <select id="model_electronhub_select">
3485 <option value="" data-i18n="-- Connect to the API --">-- Connect to the API --</option>
3486 </select>
3487 </div>
3470 <div id="nanogpt_form" data-source="nanogpt">3488 <div id="nanogpt_form" data-source="nanogpt">
3471 <h4 data-i18n="NanoGPT API Key">NanoGPT API Key</h4>3489 <h4 data-i18n="NanoGPT API Key">NanoGPT API Key</h4>
3472 <div class="flex-container">3490 <div class="flex-container">
@@ -3718,6 +3736,49 @@
3718 <option value="kimi-thinking-preview">kimi-thinking-preview</option>3736 <option value="kimi-thinking-preview">kimi-thinking-preview</option>
3719 </select>3737 </select>
3720 </div>3738 </div>
3739 <div id="azure_openai_settings" data-source="azure_openai">
3740 <!-- Azure Base URL -->
3741 <h4><span data-i18n="Azure Base URL">Azure Base URL</span></h4>
3742 <div class="flex-container">
3743 <input id="azure_base_url" data-setting="azure_base_url" class="text_pole wide100p" type="text" placeholder="https://your-resource.openai.azure.com/">
3744 </div>
3745
3746 <!-- Azure Deployment Name -->
3747 <h4><span data-i18n="Deployment Name">Deployment Name</span></h4>
3748 <div class="flex-container">
3749 <input id="azure_deployment_name" data-setting="azure_deployment_name" class="text_pole wide100p" type="text" placeholder="your-deployment-name" title="The name of your model deployment in Azure." data-i18n="[title]The name of your model deployment in Azure.">
3750 </div>
3751
3752 <!-- Azure API Version Dropdown -->
3753 <h4><span data-i18n="API Version">API Version</span></h4>
3754 <div class="flex-container">
3755 <select id="azure_api_version" data-setting="azure_api_version" class="text_pole wide100p">
3756 <option value="2025-04-01-preview">2025-04-01-preview</option>
3757 <option value="2024-10-21">2024-10-21</option>
3758 </select>
3759 </div>
3760
3761 <!-- Azure API Key -->
3762 <h4><span data-i18n="Azure API Key">Azure API Key</span></h4>
3763 <div class="flex-container">
3764 <input id="api_key_azure_openai" data-setting="api_key_azure_openai" class="text_pole flex1" type="password" autocomplete="off">
3765 <div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_azure_openai"></div>
3766 </div>
3767 <div class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'." data-for="api_key_azure_openai">
3768 For privacy reasons, your API key will be hidden after you click 'Connect'.
3769 </div>
3770
3771 <!-- Model Name (Select) -->
3772 <h4><span data-i18n="Model Name">Model Name</span></h4>
3773 <div class="flex-container">
3774 <select id="azure_openai_model" data-setting="azure_openai_model" class="text_pole wide100p">
3775 <option value="" disabled selected data-i18n="Click 'Connect' to fetch model name">Click 'Connect' to fetch model name</option>
3776 </select>
3777 </div>
3778 <div>
3779 <small data-i18n="The underlying model of your deployment. This is detected automatically when you connect.">The underlying model of your deployment. This is detected automatically when you connect.</small>
3780 </div>
3781 </div>
3721 <div id="prompt_post_processing_form">3782 <div id="prompt_post_processing_form">
3722 <h4>3783 <h4>
3723 <span data-i18n="Prompt Post-Processing">3784 <span data-i18n="Prompt Post-Processing">
@@ -5282,13 +5343,11 @@
5282 <div id="site_logo" class="drawer-toggle drawer-header" title="Change Background Image" data-i18n="[title]Change Background Image">5343 <div id="site_logo" class="drawer-toggle drawer-header" title="Change Background Image" data-i18n="[title]Change Background Image">
5283 <div class="drawer-icon fa-solid fa-panorama fa-fw closedIcon"></div>5344 <div class="drawer-icon fa-solid fa-panorama fa-fw closedIcon"></div>
5284 </div>5345 </div>
5285 <div id="Backgrounds" class="drawer-content closedDrawer">5346 <div id="Backgrounds" class="drawer-content closedDrawer bg-drawer-layout">
5286 <div class="flex-container">5347 <div id="bg-header-fixed">
5287 <div class="flex-container alignItemsBaseline wide100p">5348 <div class="flex-container alignItemsBaseline wide100p">
5288 <h3 class="margin0 flex2" data-i18n="Background Image">5349 <h3 id="bg-header-title" class="margin0" data-i18n="Backgrounds">Backgrounds</h3>
5289 Background Image5350 <input id="bg-filter" class="text_pole flex1" type="search" data-i18n="[placeholder]Search" placeholder="Search" />
5290 </h3>
5291 <input id="bg-filter" data-i18n="[placeholder]Filter" placeholder="Filter" class="text_pole flex1" type="search" />
5292 <select id="background_fitting" class="text_pole" data-i18n="[title]Background Fitting" title="Background Fitting">5351 <select id="background_fitting" class="text_pole" data-i18n="[title]Background Fitting" title="Background Fitting">
5293 <option value="classic" data-i18n="Classic">Classic</option>5352 <option value="classic" data-i18n="Classic">Classic</option>
5294 <option value="cover" data-i18n="Cover">Cover</option>5353 <option value="cover" data-i18n="Cover">Cover</option>
@@ -5300,17 +5359,17 @@
5300 <i class="fa-solid fa-wand-magic"></i>5359 <i class="fa-solid fa-wand-magic"></i>
5301 <span data-i18n="Auto-select">Auto-select</span>5360 <span data-i18n="Auto-select">Auto-select</span>
5302 </div>5361 </div>
5362 <label for="add_bg_button" id="add_background_button_top" class="menu_button menu_button_icon interactable" title="Add a new background">
5363 <i class="fa-solid fa-plus"></i>
5364 <span data-i18n="Add Background">Add Background</span>
5365 </label>
5303 </div>5366 </div>
5367 </div>
5368 <div id="bg-scrollable-content">
5304 <h3 data-i18n="System Backgrounds" class="wide100p textAlignCenter">5369 <h3 data-i18n="System Backgrounds" class="wide100p textAlignCenter">
5305 System Backgrounds5370 System Backgrounds
5306 </h3>5371 </h3>
5307 <div id="bg_menu_content" class="bg_list">5372 <div id="bg_menu_content" class="bg_list">
5308 <form id="form_bg_download" class="bg_example no-border no-shadow" action="javascript:void(null);" method="post" enctype="multipart/form-data">
5309 <label class="input-file">
5310 <input type="file" id="add_bg_button" name="avatar" accept="image/*, video/*">
5311 <div class="bg_example no-border no-shadow add_bg_but" style="background-image: url('/img/addbg3.png');"></div>
5312 </label>
5313 </form>
5314 </div>5373 </div>
5315 <h3 data-i18n="Chat Backgrounds" class="wide100p textAlignCenter">5374 <h3 data-i18n="Chat Backgrounds" class="wide100p textAlignCenter">
5316 Chat Backgrounds5375 Chat Backgrounds
@@ -5321,6 +5380,9 @@
5321 <div id="bg_custom_content" class="bg_list">5380 <div id="bg_custom_content" class="bg_list">
5322 </div>5381 </div>
5323 </div>5382 </div>
5383 <form id="form_bg_upload" style="display: none;">
5384 <input type="file" id="add_bg_button" name="avatar" accept="image/jpeg,image/png,image/gif,image/bmp,image/svg+xml,video/*">
5385 </form>
5324 </div>5386 </div>
5325 </div>5387 </div>
5326 <div id="extensions-settings-button" class="drawer">5388 <div id="extensions-settings-button" class="drawer">
@@ -5410,7 +5472,7 @@
5410 <div class="drawer-toggle">5472 <div class="drawer-toggle">
5411 <div class="drawer-icon fa-solid fa-face-smile fa-fw closedIcon" title="Persona Management" data-i18n="[title]Persona Management"></div>5473 <div class="drawer-icon fa-solid fa-face-smile fa-fw closedIcon" title="Persona Management" data-i18n="[title]Persona Management"></div>
5412 </div>5474 </div>
5413 <div class="drawer-content closedDrawer">5475 <div id="PersonaManagement" class="drawer-content closedDrawer">
5414 <div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">5476 <div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
5415 <div class="flex-container alignItemsBaseline wide100p">5477 <div class="flex-container alignItemsBaseline wide100p">
5416 <div class="flex1 flex-container alignItemsBaseline">5478 <div class="flex1 flex-container alignItemsBaseline">
@@ -6254,14 +6316,20 @@
6254 </div>6316 </div>
6255 </div>6317 </div>
6256 <div id="background_template" class="template_element">6318 <div id="background_template" class="template_element">
6257 <div class="bg_example flex-container" bgfile="" class="bg_example_img" title="">6319 <div class="bg_example flex-container" bgfile="" title="">
6258 <div title="Copy to system backgrounds" data-i18n="[title]Copy to system backgrounds" class="bg_button bg_example_copy fa-solid fa-file-arrow-up"></div>6320 <div class="bg_example_img"></div>
6259 <div title="Rename background" data-i18n="[title]Rename background" class="bg_button bg_example_edit fa-solid fa-pencil"></div>6321 <div class="mobile-only-menu-toggle">
6260 <div title="Lock" data-i18n="[title]Lock" class="bg_button bg_example_lock fa-solid fa-lock"></div>6322 <i class="fa-solid fa-ellipsis-vertical"></i>
6261 <div title="Unlock" data-i18n="[title]Unlock" class="bg_button bg_example_unlock fa-solid fa-lock-open"></div>6323 </div>
6262 <div title="Delete background" data-i18n="[title]Delete background" class="bg_button bg_example_cross fa-solid fa-circle-xmark"></div>6324 <div class="jg-menu">
6263 <div class="BGSampleTitle">6325 <div data-action="copy" class="jg-button jg-copy fa-solid fa-file-arrow-up" data-i18n="[title]Copy to system backgrounds" title="Copy to system backgrounds"></div>
6326 <!-- temporarily moved lock icon here (will be moved to header) -->
6327 <div data-action="lock" class="jg-button jg-lock fa-solid fa-lock fa-fw pointer" data-i18n="[title]Lock" title="Lock"></div>
6328 <div data-action="unlock" class="jg-button jg-unlock fa-solid fa-lock-open fa-fw pointer" data-i18n="[title]Unlock" title="Unlock"></div>
6329 <div data-action="edit" class="jg-button jg-edit fa-solid fa-pen-to-square fa-fw pointer" data-i18n="[title]Rename Background" title="Rename Background"></div>
6330 <div data-action="delete" class="jg-button jg-delete fa-solid fa-trash-can fa-fw pointer" data-i18n="[title]Delete Background" title="Delete Background"></div>
6264 </div>6331 </div>
6332 <div class="BGSampleTitle"></div>
6265 </div>6333 </div>
6266 </div>6334 </div>
6267 <!-- templates for JS to reuse when needed -->6335 <!-- templates for JS to reuse when needed -->
@@ -6437,7 +6505,7 @@
6437 </span>6505 </span>
6438 <div class="flex-container">6506 <div class="flex-container">
6439 <div class="flex-container flexFlowColumn">6507 <div class="flex-container flexFlowColumn">
6440 <label class="checkbox flex-container alignitemscenter flexNoGap" title="This entry will not be recursively activated by other entries.">6508 <label class="checkbox flex-container alignitemscenter flexNoGap" data-i18n="[title]This entry will not be recursively activated by other entries." title="This entry will not be recursively activated by other entries.">
6441 <input type="checkbox" name="excludeRecursion" />6509 <input type="checkbox" name="excludeRecursion" />
6442 <span data-i18n="Non-recursable">6510 <span data-i18n="Non-recursable">
6443 Non-recursable6511 Non-recursable
public/locales/ar-sa.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "وضع علامة",317 "flag": "وضع علامة",
318 "API key (optional)": "مفتاح API (اختياري)",318 "API key (optional)": "مفتاح API (اختياري)",
319 "Server url": "رابط الخادم",319 "Server url": "رابط الخادم",
320 "Electron Hub API Key": "مفتاح API لـ Electron Hub",
321 "Electron Hub Model": "نموذج Electron Hub",
320 "Example: http://127.0.0.1:5000": "مثال: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "مثال: http://127.0.0.1:5000",
321 "Custom model (optional)": "نموذج مخصص (اختياري)",323 "Custom model (optional)": "نموذج مخصص (اختياري)",
322 "vllm-project/vllm": "vllm-project/vllm (وضع غلاف OpenAI API)",324 "vllm-project/vllm": "vllm-project/vllm (وضع غلاف OpenAI API)",
public/locales/de-de.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "Flagge",317 "flag": "Flagge",
318 "API key (optional)": "API-Schlüssel (optional)",318 "API key (optional)": "API-Schlüssel (optional)",
319 "Server url": "Server-URL",319 "Server url": "Server-URL",
320 "Electron Hub API Key": "Electron Hub API-Schlüssel",
321 "Electron Hub Model": "Electron Hub-Modell",
320 "Example: http://127.0.0.1:5000": "Beispiel: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Beispiel: http://127.0.0.1:5000",
321 "Custom model (optional)": "Benutzerdefiniertes Modell (optional)",323 "Custom model (optional)": "Benutzerdefiniertes Modell (optional)",
322 "vllm-project/vllm": "vllm-project/vllm (OpenAI API-Wrappermodus)",324 "vllm-project/vllm": "vllm-project/vllm (OpenAI API-Wrappermodus)",
public/locales/es-es.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "bandera",317 "flag": "bandera",
318 "API key (optional)": "Clave API (opcional)",318 "API key (optional)": "Clave API (opcional)",
319 "Server url": "URL del servidor",319 "Server url": "URL del servidor",
320 "Electron Hub API Key": "Clave API de Electron Hub",
321 "Electron Hub Model": "Modelo de Electron Hub",
320 "Example: http://127.0.0.1:5000": "Ejemplo: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Ejemplo: http://127.0.0.1:5000",
321 "Custom model (optional)": "Modelo personalizado (opcional)",323 "Custom model (optional)": "Modelo personalizado (opcional)",
322 "vllm-project/vllm": "vllm-project/vllm (modo contenedor de API OpenAI)",324 "vllm-project/vllm": "vllm-project/vllm (modo contenedor de API OpenAI)",
public/locales/fr-fr.json+2 -0
@@ -1411,6 +1411,8 @@
1411 "Do not proceed if you do not agree to this!": "Ne continuez pas si vous n'êtes pas d'accord avec cela !",1411 "Do not proceed if you do not agree to this!": "Ne continuez pas si vous n'êtes pas d'accord avec cela !",
1412 "Claude API Key": "Clé API Claude",1412 "Claude API Key": "Clé API Claude",
1413 "Allow fallback models": "Autoriser les modèles de secours",1413 "Allow fallback models": "Autoriser les modèles de secours",
1414 "Electron Hub API Key": "Clé API Electron Hub",
1415 "Electron Hub Model": "Modèle Electron Hub",
1414 "NanoGPT API Key": "Clé API NanoGPT",1416 "NanoGPT API Key": "Clé API NanoGPT",
1415 "NanoGPT Model": "Modèle NanoGPT",1417 "NanoGPT Model": "Modèle NanoGPT",
1416 "DeepSeek API Key": "Clé API DeepSeek",1418 "DeepSeek API Key": "Clé API DeepSeek",
public/locales/it-it.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "bandiera",317 "flag": "bandiera",
318 "API key (optional)": "Chiave API (opzionale)",318 "API key (optional)": "Chiave API (opzionale)",
319 "Server url": "URL del server",319 "Server url": "URL del server",
320 "Electron Hub API Key": "Chiave API di Electron Hub",
321 "Electron Hub Model": "Modello di Electron Hub",
320 "Example: http://127.0.0.1:5000": "Esempio: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Esempio: http://127.0.0.1:5000",
321 "Custom model (optional)": "Modello personalizzato (opzionale)",323 "Custom model (optional)": "Modello personalizzato (opzionale)",
322 "vllm-project/vllm": "vllm-project/vllm (modalità wrapper API OpenAI)",324 "vllm-project/vllm": "vllm-project/vllm (modalità wrapper API OpenAI)",
public/locales/ja-jp.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "フラグ",317 "flag": "フラグ",
318 "API key (optional)": "APIキー(オプション)",318 "API key (optional)": "APIキー(オプション)",
319 "Server url": "サーバーURL",319 "Server url": "サーバーURL",
320 "Electron Hub API Key": "Electron Hub API キー",
321 "Electron Hub Model": "Electron Hub モデル",
320 "Example: http://127.0.0.1:5000": "例: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "例: http://127.0.0.1:5000",
321 "Custom model (optional)": "カスタムモデル(オプション)",323 "Custom model (optional)": "カスタムモデル(オプション)",
322 "vllm-project/vllm": "vllm-project/vllm (OpenAI API ラッパーモード)",324 "vllm-project/vllm": "vllm-project/vllm (OpenAI API ラッパーモード)",
public/locales/ko-kr.json+2 -0
@@ -319,6 +319,8 @@
319 "flag": "깃발",319 "flag": "깃발",
320 "API key (optional)": "API 키 (선택 사항)",320 "API key (optional)": "API 키 (선택 사항)",
321 "Server url": "서버 URL",321 "Server url": "서버 URL",
322 "Electron Hub API Key": "Electron Hub API 키",
323 "Electron Hub Model": "Electron Hub 모델",
322 "Example: http://127.0.0.1:5000": "예시: http://127.0.0.1:5000",324 "Example: http://127.0.0.1:5000": "예시: http://127.0.0.1:5000",
323 "Custom model (optional)": "사용자 정의 모델 (선택 사항)",325 "Custom model (optional)": "사용자 정의 모델 (선택 사항)",
324 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 래퍼 모드)",326 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 래퍼 모드)",
public/locales/lang.json+66 -17
@@ -1,17 +1,66 @@
1[
2 { "lang": "ar-sa", "display": "عربي (Arabic)" },
3 { "lang": "zh-cn", "display": "简体中文 (Chinese) (Simplified)" },
4 { "lang": "zh-tw", "display": "繁體中文 (Chinese) (Taiwan)" },
5 { "lang": "nl-nl", "display": "Nederlands (Dutch)" },
6 { "lang": "de-de", "display": "Deutsch (German)" },
7 { "lang": "fr-fr", "display": "Français (French)" },
8 { "lang": "is-is", "display": "íslenska (Icelandic)" },
9 { "lang": "it-it", "display": "Italiano (Italian)" },
10 { "lang": "ja-jp", "display": "日本語 (Japanese)" },
11 { "lang": "ko-kr", "display": "한국어 (Korean)" },
12 { "lang": "pt-pt", "display": "Português (Portuguese brazil)" },
13 { "lang": "ru-ru", "display": "Русский (Russian)" },
14 { "lang": "es-es", "display": "Español (Spanish)" },
15 { "lang": "uk-ua", "display": "Yкраїнська (Ukrainian)" },
16 { "lang": "vi-vn", "display": "Tiếng Việt (Vietnamese)" }
17]
17 \ No newline at end of file \ No newline at end of file
1[
2 {
3 "lang": "ar-sa",
4 "display": "عربي (Arabic)"
5 },
6 {
7 "lang": "zh-cn",
8 "display": "简体中文 (Chinese) (Simplified)"
9 },
10 {
11 "lang": "zh-tw",
12 "display": "繁體中文 (Chinese) (Taiwan)"
13 },
14 {
15 "lang": "nl-nl",
16 "display": "Nederlands (Dutch)"
17 },
18 {
19 "lang": "de-de",
20 "display": "Deutsch (German)"
21 },
22 {
23 "lang": "fr-fr",
24 "display": "Français (French)"
25 },
26 {
27 "lang": "is-is",
28 "display": "íslenska (Icelandic)"
29 },
30 {
31 "lang": "it-it",
32 "display": "Italiano (Italian)"
33 },
34 {
35 "lang": "ja-jp",
36 "display": "日本語 (Japanese)"
37 },
38 {
39 "lang": "ko-kr",
40 "display": "한국어 (Korean)"
41 },
42 {
43 "lang": "pt-pt",
44 "display": "Português (Portuguese brazil)"
45 },
46 {
47 "lang": "ru-ru",
48 "display": "Русский (Russian)"
49 },
50 {
51 "lang": "es-es",
52 "display": "Español (Spanish)"
53 },
54 {
55 "lang": "uk-ua",
56 "display": "Українська (Ukrainian)"
57 },
58 {
59 "lang": "vi-vn",
60 "display": "Tiếng Việt (Vietnamese)"
61 },
62 {
63 "lang": "th-th",
64 "display": "ไทย (Thai)"
65 }
66]
public/locales/pt-pt.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "bandeira",317 "flag": "bandeira",
318 "API key (optional)": "Chave da API (opcional)",318 "API key (optional)": "Chave da API (opcional)",
319 "Server url": "URL do servidor",319 "Server url": "URL do servidor",
320 "Electron Hub API Key": "Chave API Electron Hub",
321 "Electron Hub Model": "Modelo Electron Hub",
320 "Example: http://127.0.0.1:5000": "Exemplo: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Exemplo: http://127.0.0.1:5000",
321 "Custom model (optional)": "Modelo personalizado (opcional)",323 "Custom model (optional)": "Modelo personalizado (opcional)",
322 "vllm-project/vllm": "vllm-project/vllm (modo wrapper da API OpenAI)",324 "vllm-project/vllm": "vllm-project/vllm (modo wrapper da API OpenAI)",
public/locales/ru-ru.json+153 -84
@@ -40,7 +40,7 @@
40 "Smoothing Factor": "Коэффициент сглаживания",40 "Smoothing Factor": "Коэффициент сглаживания",
41 "No Repeat Ngram Size": "Размер no_repeat_ngram",41 "No Repeat Ngram Size": "Размер no_repeat_ngram",
42 "Min Length": "Мин. длина",42 "Min Length": "Мин. длина",
43 "Alternative server URL (leave empty to use the default value).": "URL альтернативного сервера (оставьте пустым для стандартного значения)",43 "Alternative server URL (leave empty to use the default value).": "URL реверс-прокси (оставьте пустым для стандартного значения)",
44 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",44 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
45 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",45 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
46 "Context Size (tokens)": "Размер контекста (в токенах)",46 "Context Size (tokens)": "Размер контекста (в токенах)",
@@ -145,7 +145,7 @@
145 "View API Usage Metrics": "Посмотреть статистику использования API",145 "View API Usage Metrics": "Посмотреть статистику использования API",
146 "Show External models (provided by API)": "Показать \"сторонние\" модели (предоставленные API)",146 "Show External models (provided by API)": "Показать \"сторонние\" модели (предоставленные API)",
147 "Allow fallback routes": "Разрешить резервные маршруты",147 "Allow fallback routes": "Разрешить резервные маршруты",
148 "Allow fallback routes Description": "Автоматически выбирает альтернативную модель, если выбранная модель не может удовлетворить ваш запрос.",148 "Allow fallback routes Description": "Автоматически выбирает альтернативную модель, если выбранная модель не может обслужить ваш запрос.",
149 "OpenRouter API Key": "Ключ от OpenRouter API",149 "OpenRouter API Key": "Ключ от OpenRouter API",
150 "OpenRouter Model": "Модель OpenRouter",150 "OpenRouter Model": "Модель OpenRouter",
151 "View Remaining Credits": "Посмотреть оставшиеся кредиты",151 "View Remaining Credits": "Посмотреть оставшиеся кредиты",
@@ -154,7 +154,7 @@
154 "View hidden API keys": "Посмотреть скрытые API-ключи",154 "View hidden API keys": "Посмотреть скрытые API-ключи",
155 "Advanced Formatting": "Расширенное форматирование",155 "Advanced Formatting": "Расширенное форматирование",
156 "Context Template": "Шаблон контекста",156 "Context Template": "Шаблон контекста",
157 "Replace Macro in Stop Strings": "Заменять макросы в пользовательских стоп-строках",157 "Replace Macro in Stop Strings": "Заменять макросы в стоп-строках",
158 "Story String": "Общий шаблон",158 "Story String": "Общий шаблон",
159 "Example Separator": "Разделитель примеров сообщений",159 "Example Separator": "Разделитель примеров сообщений",
160 "Chat Start": "Начало чата",160 "Chat Start": "Начало чата",
@@ -184,7 +184,7 @@
184 "Scan Depth": "Глубина сканирования",184 "Scan Depth": "Глубина сканирования",
185 "Case-Sensitive": "С учетом регистра",185 "Case-Sensitive": "С учетом регистра",
186 "Match Whole Words": "Только полное совпадение",186 "Match Whole Words": "Только полное совпадение",
187 "Use global setting": "Использовать глобальную настройку",187 "Use global setting": "Глоб. настройка",
188 "Yes": "Да",188 "Yes": "Да",
189 "No": "Нет",189 "No": "Нет",
190 "Context %": "Процент контекста",190 "Context %": "Процент контекста",
@@ -201,7 +201,7 @@
201 "Bubbles": "Пузыри",201 "Bubbles": "Пузыри",
202 "No Blur Effect": "Отключить размытие",202 "No Blur Effect": "Отключить размытие",
203 "No Text Shadows": "Отключить тень текста",203 "No Text Shadows": "Отключить тень текста",
204 "Waifu Mode": "Рeжим Вайфу",204 "Waifu Mode": "Режим вайфу",
205 "Message Timer": "Таймер сообщений",205 "Message Timer": "Таймер сообщений",
206 "Model Icon": "Значки моделей",206 "Model Icon": "Значки моделей",
207 "Advanced Character Search": "Расширенный поиск по персонажам",207 "Advanced Character Search": "Расширенный поиск по персонажам",
@@ -279,7 +279,7 @@
279 "Impersonate": "Перевоплощение",279 "Impersonate": "Перевоплощение",
280 "Regenerate": "Повторная генерация",280 "Regenerate": "Повторная генерация",
281 "Message Sound": "Звук сообщения",281 "Message Sound": "Звук сообщения",
282 "Author's Note": "Заметки автора",282 "Author's Note": "Авторские заметки",
283 "Replace empty message": "Заменять пустые сообщения",283 "Replace empty message": "Заменять пустые сообщения",
284 "Send this text instead of nothing when the text box is empty.": "Этот текст будет отправлен в случае отсутствия текста на отправку.",284 "Send this text instead of nothing when the text box is empty.": "Этот текст будет отправлен в случае отсутствия текста на отправку.",
285 "Unrestricted maximum value for the context slider": "Убрать потолок для ползунка контекста. Включайте только если точно понимаете, что делаете",285 "Unrestricted maximum value for the context slider": "Убрать потолок для ползунка контекста. Включайте только если точно понимаете, что делаете",
@@ -322,8 +322,8 @@
322 "Order ↘": "Порядок ↘",322 "Order ↘": "Порядок ↘",
323 "UID ↗": "UID ↗",323 "UID ↗": "UID ↗",
324 "UID ↘": "UID ↘",324 "UID ↘": "UID ↘",
325 "Trigger% ↗": "Триггер% ↗",325 "Trigger% ↗": "% срабатываний ↗",
326 "Trigger% ↘": "Триггер% ↘",326 "Trigger% ↘": "% срабатываний ↘",
327 "Depth:": "Глубина:",327 "Depth:": "Глубина:",
328 "Character Lore First": "Сначала лор персонажа",328 "Character Lore First": "Сначала лор персонажа",
329 "Global Lore First": "Сначала глобальный лор",329 "Global Lore First": "Сначала глобальный лор",
@@ -334,12 +334,17 @@
334 "Exclude from recursion": "Исключить из рекурсии",334 "Exclude from recursion": "Исключить из рекурсии",
335 "Entry Title/Memo": "Название или заметка о записи",335 "Entry Title/Memo": "Название или заметка о записи",
336 "Position:": "Положение:",336 "Position:": "Положение:",
337 "T_Position": "↑Char: Перед определениями Персонажа\n↓Char: После определений Персонажа\n↑AN: Перед Пометок автора\n↓AN: После Пометок автора\n@D: На глубине",337 "T_Position": "↑Перс: Перед описанием персонажа\n↓Перс: После описания персонажа\n↑ПС: Перед примерами сообщений\n↓ПС: После примеров сообщений\n↑АЗ: Перед авторскими заметками\n↓АЗ: После авторских заметок\nНа глуб. ⚙️: на глубине (система)\nНа глуб. 👤: на глубине (пользователь)\nНа глуб. 🤖: на глубине (ассистент)",
338 "Before Char Defs": "↑Перс.",338 "Before Char Defs": "↑Перс.",
339 "After Char Defs": "↓Перс.",339 "After Char Defs": "↓Перс.",
340 "Before AN": "↑АЗ",340 "Before AN": "↑АЗ",
341 "After AN": "↓АЗ",341 "After AN": "↓АЗ",
342 "Order": "Очерёдность:",342 "Before EM": "↑ПС",
343 "After EM": "↓ПС",
344 "at Depth System": "На глуб. ⚙️",
345 "at Depth User": "На глуб. 👤",
346 "at Depth AI": "На глуб. 🤖",
347 "Order": "Приоритет:",
343 "Update a theme file": "Обновить файл темы",348 "Update a theme file": "Обновить файл темы",
344 "Save as a new theme": "Сохранить как новую тему",349 "Save as a new theme": "Сохранить как новую тему",
345 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",350 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",
@@ -385,7 +390,7 @@
385 "Remove text shadow effect": "Удаление эффекта тени от текста.",390 "Remove text shadow effect": "Удаление эффекта тени от текста.",
386 "Reduce chat height, and put a static sprite behind the chat window": "Уменьшить высоту чата и поместить статичный спрайт за окном чата.",391 "Reduce chat height, and put a static sprite behind the chat window": "Уменьшить высоту чата и поместить статичный спрайт за окном чата.",
387 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список действий с сообщением, а не прятать их за '...'.",392 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список действий с сообщением, а не прятать их за '...'.",
388 "Alternative UI for numeric sampling parameters with fewer steps": "Альтернативный пользовательский интерфейс для числовых параметров выборки с меньшим количеством шагов.",393 "Alternative UI for numeric sampling parameters with fewer steps": "Уменьшить кол-во шагов для параметров, регулируемых слайдерами.",
389 "Entirely unrestrict all numeric sampling parameters": "Снять ограничения со всех числовых сэмплеров.",394 "Entirely unrestrict all numeric sampling parameters": "Снять ограничения со всех числовых сэмплеров.",
390 "Time the AI's message generation, and show the duration in the chat log": "Время генерации сообщений ИИ и его показ в журнале чата.",395 "Time the AI's message generation, and show the duration in the chat log": "Время генерации сообщений ИИ и его показ в журнале чата.",
391 "Show a timestamp for each message in the chat log": "Показывать временную метку для каждого сообщения в журнале чата.",396 "Show a timestamp for each message in the chat log": "Показывать временную метку для каждого сообщения в журнале чата.",
@@ -409,7 +414,7 @@
409 "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "При включении этой опции, пользовательский джейлбрейк будет заменяться кастомным джейлбрейком из карточки (при его наличии).",414 "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "При включении этой опции, пользовательский джейлбрейк будет заменяться кастомным джейлбрейком из карточки (при его наличии).",
410 "Show actual file names on the disk, in the characters list display only": "Отображение названий файлов персонажей на диске, только в списке персонажей.",415 "Show actual file names on the disk, in the characters list display only": "Отображение названий файлов персонажей на диске, только в списке персонажей.",
411 "Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored": "Запрашивать разрешения на импорт встроенных тегов карт при импорте персонажей. В противном случае встроенные теги игнорируются.",416 "Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored": "Запрашивать разрешения на импорт встроенных тегов карт при импорте персонажей. В противном случае встроенные теги игнорируются.",
412 "Hide character definitions from the editor panel behind a spoiler button": "Спрятать определения персонажей из панели редактора за кнопку спойлера.",417 "Hide character definitions from the editor panel behind a spoiler button": "Спрятать описания персонажей из панели редактора за кнопку спойлера.",
413 "Show a button in the input area to ask the AI to continue (extend) its last message": "Показывать на форме ответа кнопку, по нажатии на которую ИИ продолжит своё предыдущее сообщение.",418 "Show a button in the input area to ask the AI to continue (extend) its last message": "Показывать на форме ответа кнопку, по нажатии на которую ИИ продолжит своё предыдущее сообщение.",
414 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "Показывать кнопки со стрелками на последнем сообщении в чате, чтобы генерировать альтернативные ответы ИИ. Как для ПК, так и для мобильных устройств.",419 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "Показывать кнопки со стрелками на последнем сообщении в чате, чтобы генерировать альтернативные ответы ИИ. Как для ПК, так и для мобильных устройств.",
415 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "Позволяет использовать жесты смахивания на последнем сообщении в чате, чтобы вызвать альтернативную генерацию. Только для мобильных устройств, на ПК не работает.",420 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "Позволяет использовать жесты смахивания на последнем сообщении в чате, чтобы вызвать альтернативную генерацию. Только для мобильных устройств, на ПК не работает.",
@@ -427,8 +432,8 @@
427 "Your Persona": "Ваша персона",432 "Your Persona": "Ваша персона",
428 "Show notifications on switching personas": "Показывать уведомления при смене персоны",433 "Show notifications on switching personas": "Показывать уведомления при смене персоны",
429 "In Story String / Prompt Manager": "В общем шаблоне / Менеджере промптов",434 "In Story String / Prompt Manager": "В общем шаблоне / Менеджере промптов",
430 "Top of Author's Note": "Сверху от заметок автора",435 "Top of Author's Note": "Сверху от авторских заметок",
431 "Bottom of Author's Note": "Снизу от заметок автора",436 "Bottom of Author's Note": "Снизу от авторских заметок",
432 "How do I use this?": "Как пользоваться?",437 "How do I use this?": "Как пользоваться?",
433 "More...": "Ещё...",438 "More...": "Ещё...",
434 "Link to World Info": "Ссылка на информацию о мире",439 "Link to World Info": "Ссылка на информацию о мире",
@@ -436,7 +441,7 @@
436 "Scenario Override": "Перезапись сценария",441 "Scenario Override": "Перезапись сценария",
437 "Rename": "Переименовать",442 "Rename": "Переименовать",
438 "Character Description": "Описание персонажа",443 "Character Description": "Описание персонажа",
439 "Creator's Notes": "Заметки создателя",444 "Creator's Notes": "Примечание от создателя",
440 "A-Z": "A-Z",445 "A-Z": "A-Z",
441 "Z-A": "Z-A",446 "Z-A": "Z-A",
442 "Newest": "Сначала новые",447 "Newest": "Сначала новые",
@@ -471,7 +476,7 @@
471 "Enter your name": "Введите свое имя",476 "Enter your name": "Введите свое имя",
472 "Name this character": "Назовите этого персонажа",477 "Name this character": "Назовите этого персонажа",
473 "Search / Create Tags": "Искать / Создать тэги",478 "Search / Create Tags": "Искать / Создать тэги",
474 "Describe your character's physical and mental traits here.": "Опишите ментальные и физические черты персонажа",479 "Describe your character's physical and mental traits here.": "Опишите характер персонажа и его внешность",
475 "This will be the first message from the character that starts every chat.": "Это будет первое сообщение от персонажа при начале нового чата",480 "This will be the first message from the character that starts every chat.": "Это будет первое сообщение от персонажа при начале нового чата",
476 "Chat Name (Optional)": "Название чата (необязательно)",481 "Chat Name (Optional)": "Название чата (необязательно)",
477 "Search...": "Поиск...",482 "Search...": "Поиск...",
@@ -483,10 +488,10 @@
483 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",488 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
484 "(A brief description of the personality)": "(Краткое описание личности)",489 "(A brief description of the personality)": "(Краткое описание личности)",
485 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",490 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
486 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой строкой.)",491 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример со START и на новой строке.)",
487 "Type here...": "Пишите здесь...",492 "Type here...": "Пишите здесь...",
488 "Comma separated (required)": "Через запятую (обязательное поле)",493 "Comma separated (required)": "Через запятую (обязательное поле)",
489 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",494 "What this keyword should mean to the AI, sent verbatim": "Объясните ИИ, что он должен знать об этом ключевом слове",
490 "Filter to Character(s)": "Фильтр по персонажу(ам)",495 "Filter to Character(s)": "Фильтр по персонажу(ам)",
491 "Character Exclusion": "Исключить персонажей",496 "Character Exclusion": "Исключить персонажей",
492 "Inclusion Group": "Группа записей",497 "Inclusion Group": "Группа записей",
@@ -569,12 +574,12 @@
569 "Remove": "Убрать",574 "Remove": "Убрать",
570 "Select a World Info file for": "Выбрать файл с миром для",575 "Select a World Info file for": "Выбрать файл с миром для",
571 "Primary Lorebook": "Основной лорбук",576 "Primary Lorebook": "Основной лорбук",
572 "A selected World Info will be bound to this character as its own Lorebook.": "Информация о мире будет привязана к персонажу как его собственный лорбук.",577 "A selected World Info will be bound to this character as its own Lorebook.": "Данный мир будет привязан к персонажу как его собственный лорбук.",
573 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "Когда ИИ генерирует ответ, он будет совмещён с записями из глобально выбранного мира.",578 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "При генерации ответа, данный лорбук будет работать вместе с глобально выбранным лорбуком.",
574 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "При экспорте персонажа вместе с ним также выгрузится выбранный лорбук в виде JSON.",579 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "При экспорте персонажа вместе с ним также выгрузится выбранный лорбук в виде JSON.",
575 "Additional Lorebooks": "Вспомогательные лорбуки",580 "Additional Lorebooks": "Вспомогательные лорбуки",
576 "Associate one or more auxillary Lorebooks with this character.": "Привязать к этому персонажу один или больше вспомогательных лорбуков",581 "Associate one or more auxillary Lorebooks with this character.": "Привязать к этому персонажу один или больше вспомогательных лорбуков.",
577 "NOTE: These choices are optional and won't be preserved on character export!": "ВНИМАНИЕ: эти выборы необязательные и не будут сохранены при экспорте персонажа!",582 "NOTE: These choices are optional and won't be preserved on character export!": "ВНИМАНИЕ: вспомогательные лорбуки не будут выгружены при экспорте персонажа!",
578 "Rename chat file": "Переименовать чат",583 "Rename chat file": "Переименовать чат",
579 "Export JSONL chat file": "Экспортировать чат в формате JSONL",584 "Export JSONL chat file": "Экспортировать чат в формате JSONL",
580 "Download chat as plain text document": "Скачать чат в формате .txt",585 "Download chat as plain text document": "Скачать чат в формате .txt",
@@ -784,20 +789,20 @@
784 "Enable magnification for zoomed avatar display.": "Добавляет возможность приближать увеличенную версию аватарки.",789 "Enable magnification for zoomed avatar display.": "Добавляет возможность приближать увеличенную версию аватарки.",
785 "Unique to this chat": "Только для текущего чата",790 "Unique to this chat": "Только для текущего чата",
786 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Чекпоинты наследуют заметки от родительского чата, но впоследствие их всегда можно изменить.",791 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Чекпоинты наследуют заметки от родительского чата, но впоследствие их всегда можно изменить.",
787 "Include in World Info Scanning": "Учитывать при сканировании Информации о мире",792 "Include in World Info Scanning": "Учитывать при сканировании лорбуком",
788 "Before Main Prompt / Story String": "Перед основным промптом / строкой истории",793 "Before Main Prompt / Story String": "Перед основным промптом / общим шаблоном",
789 "After Main Prompt / Story String": "После основного промпта / строки истории",794 "After Main Prompt / Story String": "После основного промпта / общего шаблона",
790 "In-chat @ Depth": "Встав. на глуб.",795 "In-chat @ Depth": "Встав. на глуб.",
791 "as": "роль:",796 "as": "роль:",
792 "Insertion Frequency": "Частота вставки",797 "Insertion Frequency": "Частота вставки",
793 "(0 = Disable, 1 = Always)": "(0 = никогда, 1 = всегда)",798 "(0 = Disable, 1 = Always)": "(0 = никогда, 1 = всегда)",
794 "User inputs until next insertion:": "Ваших сообщений до след. вставки:",799 "User inputs until next insertion:": "Ваших сообщений до след. вставки:",
795 "Character Author's Note (Private)": "Заметки автора персонажа (личные)",800 "Character Author's Note (Private)": "Авторские заметки для персонажа (личные)",
796 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "Автоматически применятся к этому персонажу в качестве заметок автора. Будут использоваться в группах, но при активном групповом чате к редактированию недоступны.",801 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "Автоматически применятся к этому персонажу в качестве авторских заметок. Будут использоваться в группах, но при активном групповом чате к редактированию недоступны.",
797 "Use character author's note": "Использовать заметки автора персонажа",802 "Use character author's note": "Использовать авторские заметки для персонажа",
798 "Replace Author's Note": "Вместо заметок автора",803 "Replace Author's Note": "Вместо авторских заметок",
799 "Default Author's Note": "Стандартные заметки автора",804 "Default Author's Note": "Стандартные авторские заметки",
800 "Will be automatically added as the Author's Note for all new chats.": "Будут автоматически добавляться во все новые чаты в качестве Заметок автора",805 "Will be automatically added as the Author's Note for all new chats.": "Будут автоматически добавляться во все новые чаты в качестве авторских заметок",
801 "1 = disabled": "1 = откл.",806 "1 = disabled": "1 = откл.",
802 "write short replies, write replies using past tense": "пиши короткие ответы, пиши в настоящем времени",807 "write short replies, write replies using past tense": "пиши короткие ответы, пиши в настоящем времени",
803 "Positive Prompt": "Положительный промпт",808 "Positive Prompt": "Положительный промпт",
@@ -906,7 +911,7 @@
906 "ext_sum_force_text": "Пересказать сейчас",911 "ext_sum_force_text": "Пересказать сейчас",
907 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "Отключить авто-обновление пересказа. Пересказ всё время будет фиксированным. Однако останется возможность принудительно обновить пересказ через кнопку \"Пересказать сейчас\" (доступно только через Основное API)",912 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "Отключить авто-обновление пересказа. Пересказ всё время будет фиксированным. Однако останется возможность принудительно обновить пересказ через кнопку \"Пересказать сейчас\" (доступно только через Основное API)",
908 "ext_sum_pause": "Приостановить",913 "ext_sum_pause": "Приостановить",
909 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "Исключать из пересказа Информацию о мире и Заметки автора. Работает только для Основного API. Extras API всегда их исключает.",914 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "Исключать из пересказа Информацию о мире и Авторские заметки. Работает только для Основного API. Extras API всегда их исключает.",
910 "ext_sum_no_wi_an": "Без мира и заметок",915 "ext_sum_no_wi_an": "Без мира и заметок",
911 "ext_sum_settings_tip": "Изменить промпт пересказа, место для инжекта и т.д.",916 "ext_sum_settings_tip": "Изменить промпт пересказа, место для инжекта и т.д.",
912 "ext_sum_settings": "Настройки пересказа",917 "ext_sum_settings": "Настройки пересказа",
@@ -1333,13 +1338,7 @@
1333 "WI_Entry_Status_Normal": "Обычная",1338 "WI_Entry_Status_Normal": "Обычная",
1334 "WI_Entry_Status_Vectorized": "Векторизованная",1339 "WI_Entry_Status_Vectorized": "Векторизованная",
1335 "WI_Entry_Status_Disabled": "Отключена",1340 "WI_Entry_Status_Disabled": "Отключена",
1336 "Before EM": "↑EM",
1337 "After EM": "↓EM",
1338 "at Depth System": "@D ⚙️",
1339 "at Depth User": "@D 👤",
1340 "at Depth AI": "@D 🤖",
1341 "Depth": "Глубина",1341 "Depth": "Глубина",
1342 "Trigger %:": "Trigger %:",
1343 "Probability": "Вероятность",1342 "Probability": "Вероятность",
1344 "Duplicate world info entry": "Дублировать запись",1343 "Duplicate world info entry": "Дублировать запись",
1345 "Delete world info entry": "Удалить запись",1344 "Delete world info entry": "Удалить запись",
@@ -1411,26 +1410,7 @@
1411 "ext_regex_title": "Regex",1410 "ext_regex_title": "Regex",
1412 "ext_regex_import_target": "Импортировать в:",1411 "ext_regex_import_target": "Импортировать в:",
1413 "ext_regex_move_to_scoped": "Сделать локальным",1412 "ext_regex_move_to_scoped": "Сделать локальным",
1414 "Trigger Stable Diffusion": "Trigger Stable Diffusion",
1415 "sd_Yourself": "Yourself",
1416 "sd_Your_Face": "Your Face",
1417 "sd_Me": "Me",
1418 "sd_The_Whole_Story": "The Whole Story",
1419 "sd_The_Last_Message": "The Last Message",
1420 "sd_Raw_Last_Message": "Raw Last Message",
1421 "sd_Background": "Background",
1422 "Image Generation": "Image Generation",1413 "Image Generation": "Image Generation",
1423 "sd_refine_mode": "Allow to edit prompts manually before sending them to generation API",
1424 "sd_refine_mode_txt": "Edit prompts before generation",
1425 "sd_interactive_mode": "Automatically generate images when sending messages like 'send me a picture of cat'.",
1426 "sd_interactive_mode_txt": "Interactive mode",
1427 "sd_multimodal_captioning": "Use multimodal captioning to generate prompts for user and character portraits based on their avatars.",
1428 "sd_multimodal_captioning_txt": "Use multimodal captioning for portraits",
1429 "sd_expand": "Automatically extend prompts using text generation model",
1430 "sd_expand_txt": "Auto-enhance prompts",
1431 "sd_snap": "Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL).",
1432 "sd_snap_txt": "Snap auto-adjusted resolutions",
1433 "Source": "Source",
1434 "sd_auto_url": "Example: {{auto_url}}",1414 "sd_auto_url": "Example: {{auto_url}}",
1435 "Authentication (optional)": "Authentication (optional)",1415 "Authentication (optional)": "Authentication (optional)",
1436 "Example: username:password": "Example: username:password",1416 "Example: username:password": "Example: username:password",
@@ -1441,18 +1421,10 @@
1441 "sd_drawthings_auth_txt": "run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.",1421 "sd_drawthings_auth_txt": "run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.",
1442 "sd_vlad_url": "Example: {{vlad_url}}",1422 "sd_vlad_url": "Example: {{vlad_url}}",
1443 "The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",1423 "The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",
1444 "Hint: Save an API key in AI Horde API settings to use it here.": "Hint: Save an API key in AI Horde API settings to use it here.",
1445 "Allow NSFW images from Horde": "Разрешить NSFW-картинки в Horde",1424 "Allow NSFW images from Horde": "Разрешить NSFW-картинки в Horde",
1446 "Sanitize prompts (recommended)": "Sanitize prompts (recommended)",
1447 "Automatically adjust generation parameters to ensure free image generations.": "Automatically adjust generation parameters to ensure free image generations.",
1448 "Avoid spending Anlas": "Avoid spending Anlas",1425 "Avoid spending Anlas": "Avoid spending Anlas",
1449 "Opus tier": "(Opus tier)",1426 "Opus tier": "(Opus tier)",
1450 "View my Anlas": "View my Anlas",1427 "View my Anlas": "View my Anlas",
1451 "These settings only apply to DALL-E 3": "These settings only apply to DALL-E 3",
1452 "Image Style": "Image Style",
1453 "Image Quality": "Image Quality",
1454 "Standard": "Standard",
1455 "HD": "HD",
1456 "sd_comfy_url": "Example: {{comfy_url}}",1428 "sd_comfy_url": "Example: {{comfy_url}}",
1457 "Open workflow editor": "Open workflow editor",1429 "Open workflow editor": "Open workflow editor",
1458 "Create new workflow": "Create new workflow",1430 "Create new workflow": "Create new workflow",
@@ -1460,9 +1432,6 @@
1460 "Enhance": "Enhance",1432 "Enhance": "Enhance",
1461 "Refine": "Refine",1433 "Refine": "Refine",
1462 "Decrisper": "Decrisper",1434 "Decrisper": "Decrisper",
1463 "Sampling steps": "Sampling steps ()",
1464 "Width": "Width ()",
1465 "Height": "Height ()",
1466 "Resolution": "Resolution",1435 "Resolution": "Resolution",
1467 "Model": "Model",1436 "Model": "Model",
1468 "Sampling method": "Sampling method",1437 "Sampling method": "Sampling method",
@@ -1472,8 +1441,6 @@
1472 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.",1441 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.",
1473 "DYN": "DYN",1442 "DYN": "DYN",
1474 "Scheduler": "Scheduler",1443 "Scheduler": "Scheduler",
1475 "Restore Faces": "Restore Faces",
1476 "Hires. Fix": "Hires. Fix",
1477 "Upscaler": "Upscaler",1444 "Upscaler": "Upscaler",
1478 "Upscale by": "Upscale by",1445 "Upscale by": "Upscale by",
1479 "Denoising strength": "Denoising strength",1446 "Denoising strength": "Denoising strength",
@@ -1484,7 +1451,6 @@
1484 "Delete style": "Delete style",1451 "Delete style": "Delete style",
1485 "Common prompt prefix": "Common prompt prefix",1452 "Common prompt prefix": "Common prompt prefix",
1486 "sd_prompt_prefix_placeholder": "Use {prompt} to specify where the generated prompt will be inserted",1453 "sd_prompt_prefix_placeholder": "Use {prompt} to specify where the generated prompt will be inserted",
1487 "Negative common prompt prefix": "Negative common prompt prefix",
1488 "Character-specific prompt prefix": "Character-specific prompt prefix",1454 "Character-specific prompt prefix": "Character-specific prompt prefix",
1489 "Won't be used in groups.": "Won't be used in groups.",1455 "Won't be used in groups.": "Won't be used in groups.",
1490 "sd_character_prompt_placeholder": "Any characteristics that describe the currently selected character. Will be added after a common prompt prefix.\nExample: female, green eyes, brown hair, pink shirt",1456 "sd_character_prompt_placeholder": "Any characteristics that describe the currently selected character. Will be added after a common prompt prefix.\nExample: female, green eyes, brown hair, pink shirt",
@@ -1836,13 +1802,13 @@
1836 "Also delete the chat files": "Также удалить файлы чатов",1802 "Also delete the chat files": "Также удалить файлы чатов",
1837 "Delete the character?": "Удалить персонажа?",1803 "Delete the character?": "Удалить персонажа?",
1838 "Not a valid number": "Некорректное число",1804 "Not a valid number": "Некорректное число",
1839 "Author's Note depth updated": "Глубина заметок автора обновлена",1805 "Author's Note depth updated": "Глубина авторских заметок обновлена",
1840 "Author's Note frequency updated": "Частота заметок автора обновлена",1806 "Author's Note frequency updated": "Частота авторских заметок обновлена",
1841 "Not a valid position": "Некорректная позиция",1807 "Not a valid position": "Некорректная позиция",
1842 "Author's Note position updated": "Позиция заметок автора обновлена",1808 "Author's Note position updated": "Позиция авторских заметок обновлена",
1843 "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить заметки автора для этого персонажа.",1809 "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить авторские заметки для этого персонажа.",
1844 "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа",1810 "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа",
1845 "Author's Note text updated": "Текст заметок автора обновлён",1811 "Author's Note text updated": "Текст авторских заметок обновлён",
1846 "Group Validation": "Валидация группы",1812 "Group Validation": "Валидация группы",
1847 "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.",1813 "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.",
1848 "Group Chat could not be saved": "Не удалось сохранить групповой чат",1814 "Group Chat could not be saved": "Не удалось сохранить групповой чат",
@@ -1938,7 +1904,7 @@
1938 "Do you want to remove these fields before exporting?": "Желаете ли удалить эти поля перед экспортом?",1904 "Do you want to remove these fields before exporting?": "Желаете ли удалить эти поля перед экспортом?",
1939 "Save": "Сохранить",1905 "Save": "Сохранить",
1940 "Chat Lorebook": "Лорбук для чата",1906 "Chat Lorebook": "Лорбук для чата",
1941 "chat_world_template_txt": "Выбранный мир будет привязан к этому чату. Будет добавляться в промпт наряду с глобальным лорбуком и лором персонажа.",1907 "chat_world_template_txt": "Выбранный мир будет привязан к этому чату. Будет работать наряду с глобальным лорбуком и лором персонажа.",
1942 "world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + ЛКМ, чтобы открыть диалог привязки мира",1908 "world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + ЛКМ, чтобы открыть диалог привязки мира",
1943 "No auxillary Lorebooks set. Click here to select.": "Вспомогательный лорбук не выбран. Нажмите, чтобы выбрать.",1909 "No auxillary Lorebooks set. Click here to select.": "Вспомогательный лорбук не выбран. Нажмите, чтобы выбрать.",
1944 "ext_regex_user_input_desc": "Отправленные вами сообщения.",1910 "ext_regex_user_input_desc": "Отправленные вами сообщения.",
@@ -2010,7 +1976,7 @@
2010 "Imported tags:": "Импортируемые теги:",1976 "Imported tags:": "Импортируемые теги:",
2011 "Importing Tags": "Импорт тегов",1977 "Importing Tags": "Импорт тегов",
2012 "Couldn't import tags:": "Не удалось импортировать теги:",1978 "Couldn't import tags:": "Не удалось импортировать теги:",
2013 "Allow fallback models": "Разрешить fallback-модели",1979 "Allow fallback models": "Разрешить резервные модели",
2014 "Allow fallback providers": "Разрешить fallback-провайдеров",1980 "Allow fallback providers": "Разрешить fallback-провайдеров",
2015 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "Переключитесь на OpenRouter в Text Completion API, чтобы использовать форматирование Instruct-режима.",1981 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "Переключитесь на OpenRouter в Text Completion API, чтобы использовать форматирование Instruct-режима.",
2016 "Select providers. No selection = all providers.": "Выберите провайдера. Нет выбранного = выбраны все.",1982 "Select providers. No selection = all providers.": "Выберите провайдера. Нет выбранного = выбраны все.",
@@ -2028,6 +1994,8 @@
2028 "Click on the setting name to omit it from the profile.": "Нажмите на название настройки, чтобы исключить её из профиля",1994 "Click on the setting name to omit it from the profile.": "Нажмите на название настройки, чтобы исключить её из профиля",
2029 "Included settings:": "Сохранённые параметры:",1995 "Included settings:": "Сохранённые параметры:",
2030 "Server URL": "Адрес сервера",1996 "Server URL": "Адрес сервера",
1997 "Electron Hub API Key": "Ключ от API Electron Hub",
1998 "Electron Hub Model": "Модель Electron Hub",
2031 "NanoGPT API Key": "Ключ от API NanoGPT",1999 "NanoGPT API Key": "Ключ от API NanoGPT",
2032 "NanoGPT Model": "Модель NanoGPT",2000 "NanoGPT Model": "Модель NanoGPT",
2033 "Use extension settings": "Использовать настройки из расширения",2001 "Use extension settings": "Использовать настройки из расширения",
@@ -2044,14 +2012,14 @@
2044 "Title/Memo": "Название",2012 "Title/Memo": "Название",
2045 "Strategy": "Статус",2013 "Strategy": "Статус",
2046 "Position": "Позиция",2014 "Position": "Позиция",
2047 "Trigger %": "% срабатывания",2015 "Trigger %": "% срабатываний",
2048 "Use global": "Глоб. настройка",2016 "Use global": "Глоб. настройка",
2049 "Whole Words": "Целые слова",2017 "Whole Words": "Целые слова",
2050 "Non-recursable": "Не рекурсивная",2018 "Non-recursable": "Не рекурсивная",
2051 "Delay until recursion": "Рекурсивная",2019 "Delay until recursion": "Рекурсивная",
2052 "Toggle entry's active state.": "Вкл/выкл запись.",2020 "Toggle entry's active state.": "Вкл/выкл запись.",
2053 "Prioritize": "Важная",2021 "Prioritize": "Важная",
2054 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Важная запись получает приоритет среди всех выбранных. Если важных записей несколько, выбирается та, у которой выше \"Очерёдность\".",2022 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Важная запись получает приоритет среди всех выбранных. Если важных записей несколько, выбирается та, у которой выше \"Приоритет\".",
2055 "Group Weight": "Вес в группе",2023 "Group Weight": "Вес в группе",
2056 "A relative likelihood of entry activation within the group": "Относительная вероятность активации записи в рамках группы",2024 "A relative likelihood of entry activation within the group": "Относительная вероятность активации записи в рамках группы",
2057 "Sticky": "Липучка",2025 "Sticky": "Липучка",
@@ -2067,7 +2035,7 @@
2067 "Switch to plaintext mode": "Вкл/выкл режим чистого текста",2035 "Switch to plaintext mode": "Вкл/выкл режим чистого текста",
2068 "Exclude": "Режим исключения",2036 "Exclude": "Режим исключения",
2069 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Инвертировать логику: для выбранных в фильтре персонажей/тегов данная запись активна НЕ БУДЕТ",2037 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Инвертировать логику: для выбранных в фильтре персонажей/тегов данная запись активна НЕ БУДЕТ",
2070 "Apply current sorting as Order": "Настроить Очерёдность в соответствии с текущей сортировкой",2038 "Apply current sorting as Order": "Настроить Приоритет в соответствии с текущей сортировкой",
2071 "Create a new World Info": "Создать новый мир",2039 "Create a new World Info": "Создать новый мир",
2072 "Enter a name for the new file:": "Название нового файла:",2040 "Enter a name for the new file:": "Название нового файла:",
2073 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Если сразу несколько записей из одной группы окажутся активированными, по факту сработает только одна. Одна запись может входить в несколько групп, отделяются запятыми. Раздел в документации: World Info - Inclusion Group",2041 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Если сразу несколько записей из одной группы окажутся активированными, по факту сработает только одна. Одна запись может входить в несколько групп, отделяются запятыми. Раздел в документации: World Info - Inclusion Group",
@@ -2165,7 +2133,7 @@
2165 "openai_reasoning_effort_high": "Подробные",2133 "openai_reasoning_effort_high": "Подробные",
2166 "Persona Lore Alt+Click to open the lorebook": "Лорбук данной персоны\nAlt + ЛКМ чтобы открыть лорбук",2134 "Persona Lore Alt+Click to open the lorebook": "Лорбук данной персоны\nAlt + ЛКМ чтобы открыть лорбук",
2167 "Persona Lorebook for": "Лорбук для персоны",2135 "Persona Lorebook for": "Лорбук для персоны",
2168 "persona_world_template_txt": "Выбранная Информация о мире будет привязана к этой персоне. Информация будет добавляться в каждом промпте вместе с глобальным лорбуком и лорбуками персонажа и чата.",2136 "persona_world_template_txt": "Выбранный мир будет привязан к этой персоне. Будет работать вместе с глобальным лорбуком и лорбуками персонажа и чата.",
2169 "Global list": "Глобальный список",2137 "Global list": "Глобальный список",
2170 "Preset-specific list": "Список для данного пресета",2138 "Preset-specific list": "Список для данного пресета",
2171 "Banned tokens/strings are being sent in the request.": "Запрещённые токены и строки отсылаются в запросе.",2139 "Banned tokens/strings are being sent in the request.": "Запрещённые токены и строки отсылаются в запросе.",
@@ -2280,7 +2248,7 @@
2280 "Character Words": "Слов отправлено персонажем",2248 "Character Words": "Слов отправлено персонажем",
2281 "stats_header_User": "пользователю",2249 "stats_header_User": "пользователю",
2282 "stats_header_Character": "персонажу",2250 "stats_header_Character": "персонажу",
2283 "${0} Stats": "Статистика по ${0}", 2251 "${0} Stats": "Статистика по ${0}",
2284 "Context": "Контекст",2252 "Context": "Контекст",
2285 "Response": "Ответ",2253 "Response": "Ответ",
2286 "Connected": "Подключено",2254 "Connected": "Подключено",
@@ -2521,6 +2489,16 @@
2521 "Automatically generated chat backups.": "Автоматически создаваемые бэкапы чатов.",2489 "Automatically generated chat backups.": "Автоматически создаваемые бэкапы чатов.",
2522 "Settings Backups": "Копии настроек",2490 "Settings Backups": "Копии настроек",
2523 "Automatically generated settings backups.": "Автоматически создаваемые бэкапы настроек.",2491 "Automatically generated settings backups.": "Автоматически создаваемые бэкапы настроек.",
2492 "Files": "Файлы",
2493 "Files that are not associated with chat messages or Data Bank. WILL DELETE MANUAL UPLOADS!": "Файлы, не связанные ни с одним сообщением или банком данных. ЗАГРУЖЕННЫЕ ВРУЧНУЮ ФАЙЛЫ ТАКЖЕ БУДУТ УДАЛЕНЫ!",
2494 "Images": "Изображения",
2495 "Images that are not associated with chat messages. WILL DELETE MANUAL UPLOADS!": "Картинки, не связанные ни с одним сообщением. ЗАГРУЖЕННЫЕ ВРУЧНУЮ ФАЙЛЫ ТАКЖЕ БУДУТ УДАЛЕНЫ!",
2496 "Avatar Thumbnails": "Превью для аватарок",
2497 "Thumbnails for avatars of missing or deleted characters.": "Превью для аватарок удалённых персонажей",
2498 "Background Thumbnails": "Превью для фонов",
2499 "Thumbnails for missing or deleted backgrounds.": "Превью для удалённых фоновых изображений",
2500 "Persona Thumbnails": "Превью для персон",
2501 "Thumbnails for missing or deleted personas.": "Превью для удалённых персон",
2524 "Delete all items in this category": "Удалить всё в этой категории",2502 "Delete all items in this category": "Удалить всё в этой категории",
2525 "View item content": "Посмотреть содержимое",2503 "View item content": "Посмотреть содержимое",
2526 "Download item": "Скачать содержимое",2504 "Download item": "Скачать содержимое",
@@ -2583,5 +2561,96 @@
2583 "Moonshot AI API Key": "Ключ от API Moonshot AI",2561 "Moonshot AI API Key": "Ключ от API Moonshot AI",
2584 "Moonshot AI Model": "Модель Moonshot AI",2562 "Moonshot AI Model": "Модель Moonshot AI",
2585 "AI/ML API Key": "Ключ от API AI/ML",2563 "AI/ML API Key": "Ключ от API AI/ML",
2586 "AI/ML Model": "Модель AI/ML"2564 "AI/ML Model": "Модель AI/ML",
2565 "Replace Character": "Заменить персонажа",
2566 "Choose a new character card to replace this character with.": "Выберите, каким персонажем хотите заменить текущего.",
2567 "All chats, assets and group memberships will be preserved, but local changes to the character data will be lost.": "Чаты с персонажем, его ассеты и членство в группах сохранятся. Однако сделанные вами изменения в карточке будут утеряны.",
2568 "Proceed?": "Продолжить?",
2569 "No Creator's Notes provided.": "Создатель не оставил примечаний.",
2570 "No auxiliary Lorebooks set. Click here to select.": "Вспомогательных лорбуков нет. ЛКМ, чтобы выбрать.",
2571 "Persona Title (optional, display only)": "Сноска (не влияет на чат, только в интерфейсе)",
2572 "This entry will not be recursively activated by other entries.": "Не будет активироваться другими записями.",
2573 "This entry will not activate other entries recursively.": "Не будет активировать другие записи.",
2574 "This entry can only be activated on recursive checking.": "Активируется только другими записями.",
2575 "This entry will be included ignoring budget constraints, assuming all other checks pass.": "Будет включена в промпт даже при превышении бюджета токенов (при условии, что все остальные условия соблюдены)",
2576 "Ignore budget": "Игнорировать бюджет",
2577 "Filter to Generation Triggers": "Только для опред. типов генераций",
2578 "Apply current sorting as Order": "Выставить приоритеты по текущей сортировке",
2579 "Apply your current sorting to the \"Order\" field. The Order values will go down from the chosen number.": "Заполнить поле \"Приоритет\" в соответствии с текущей сортировкой для всех записей. Поля будут заполнены значениями, начиная с этого числа:",
2580 "More than 100 entries in this world. If you don't choose a number higher than that, the lower entries will default to 0.<br />(Usual default: 100)<br />Minimum: ${0}": "В этом лорбуке больше 100 записей! Если не выставить число больше 100, то последние записи будут иметь приоритет 0.<br/>(Стандартное значение: 100)<br/>(Минимальное число: ${0})",
2581 "Apply": "Применить",
2582 "Apply Current Sorting": "Выставить приоритеты по сортировке",
2583 "Delete the entry with UID: ${0}?": "Удалить запись с UID: ${0}?",
2584 "This action is irreversible!": "Отменить будет невозможно!",
2585 "Recursion Level": "Уровень рекурсии",
2586 "delay_until_recursion_level": "Насколько глубоко должна зайти рекурсия, чтобы иметь возможность активировать эту запись.\n\nИзначально рекурсия делает проход по 1 уровню. Если там не находится подходящих записей, то она идёт на 2 уровень, и там повторяет процесс.\nИ так, пока не дойдёт до самого глубокого уровня.\n\nАктуально только для рекурсивных записей (с отмеченной галочкой \"Рекурсивная\").",
2587 "A number lower than the entry count has been chosen. All entries below that will default to 0.": "Введённое число меньше, чем общее кол-во записей. Всем записям внизу списка присвоен приоритет 0.",
2588 "Invalid number: ${0}": "Некорректное число: ${0}",
2589 "Allow animations for WEBP backgrounds. This is only a change for the selection menu.": "Разрешить анимации в WEBP фонах. Актуально только для меню выбора.",
2590 "sd_refine_mode_txt": "Редактировать промпты перед генерацией",
2591 "sd_refine_mode": "Отправлять промпты на проверку вам, прежде чем пересылать в API",
2592 "sd_function_tool_txt": "Использовать вызов функций",
2593 "sd_function_tool": "Использовать функцию, чтобы понимать, когда пора генерировать изображение",
2594 "sd_interactive_mode_txt": "Интерактивный режим",
2595 "sd_interactive_mode": "Автоматически генерировать изображения, когда появляется сообщение вида \"send me a picture of a cat\"",
2596 "sd_multimodal_captioning_txt": "Мультимодальный промптинг",
2597 "sd_multimodal_captioning": "Использовать мультимодальный промптинг для создания портретов пользователя и персонажа, основываясь на их аватарках",
2598 "sd_free_extend_txt": "Дописывать промпты в свободном режиме",
2599 "sd_free_extend_small": "(для интерактивного режима или команд)",
2600 "sd_free_extend": "Автоматически дописывать промпты в свободном режиме (для всего, что не касается фонов и портретов), используя текущую выбранную LLM",
2601 "sd_snap_txt": "Корректировать автоматически выбранное разрешение",
2602 "sd_snap": "Подгонять картинки с фиксированным соотношением сторон (фоны, портреты) к ближайшему известному разрешению (рекомендуется для SDXL)",
2603 "Source": "API",
2604 "Hint: Save an API key in AI Horde API settings to use it here.": "Подсказка: сохраните ключ от API в настройках API на сайте AI Horde, чтобы использовать его автоматически.",
2605 "Sanitize prompts (recommended)": "Прогонять промпты через санитайзер (рекомендуется)",
2606 "Sampling method": "Метод сэмплинга",
2607 "Resolution": "Целевое разрешение",
2608 "Sampling steps": "Кол-во шагов",
2609 "Width": "Ширина",
2610 "Height": "Высота",
2611 "Not all samplers supported.": "Поддерживает не все виды сэмплинга.",
2612 "(-1 for random)": "(-1 для случайного)",
2613 "Common prompt prefix": "Фиксированный префикс для промптов",
2614 "Preset for prompt prefix and negative prompt": "Пресет для префиксов промпта и для негативных промптов",
2615 "Style": "Стиль",
2616 "Negative common prompt prefix": "Фиксированный префикс для негативных промптов",
2617 "Chat Message Visibility (by source)": "Видимость сообщений в чате (по источникам)",
2618 "Uncheck to hide the extension's messages in chat prompts.": "Снимите галочку, чтобы скрыть из промптов сообщения, приходящие из этого источника.",
2619 "Extensions Menu": "Меню расширений",
2620 "Slash Command": "Слэш-команды",
2621 "Interactive Mode": "Интерактивный режим",
2622 "Function Tool": "Функция",
2623 "Save style": "Сохранить стиль",
2624 "Delete style": "Удалить стиль",
2625 "Are you sure you want to delete the style \"${0}\"?": "Вы точно хотите удалить стиль \"${0}\"?",
2626 "API Key": "Ключ от API",
2627 "These settings only apply to DALL-E 3": "Данные настройки применяются только для DALL-E 3",
2628 "Image Style": "Стиль изображения",
2629 "Image Quality": "Качество изображения",
2630 "Standard": "Стандарт",
2631 "sd_res_512x512": "512x512 (1:1, иконки, аватарки)",
2632 "sd_res_600x600": "600x600(1:1, иконки, аватарки)",
2633 "sd_res_512x768": "512x768 (2:3, вертикальная аватарка для карточки)",
2634 "sd_res_768x512": "768x512 (3:2, 35мм плёнка для кинофильмов, альбомн. ориентация)",
2635 "sd_res_960x540": "960x540 (16:9, обои, альбомн. ориентация)",
2636 "sd_res_540x960": "540x960 (9:16, обои, книжн. ориентация)",
2637 "sd_res_1920x1088": "1920x1088 (16:9, обои, альбомн. ориентация)",
2638 "sd_res_1088x1920": "1088x1920 (9:16, обои, книжн. ориентация)",
2639 "sd_res_1280x720": "1280x720 (16:9, обои, альбомн. ориентация)",
2640 "sd_res_720x1280": "720x1280 (9:16, обои, книжн. ориентация)",
2641 "Click to set": "Нажмите чтобы задать",
2642 "Prompt Upsampling": "Применять Prompt Upsampling",
2643 "Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.": "При активации автоматически модифицирует промпт, чтобы сделать процесс генерации более креативным.",
2644 "Important:": "Важно:",
2645 "The server must be accessible from the SillyTavern host machine.": " Адрес должен быть доступен с сервера SillyTavern.",
2646 "Open workflow editor": "Открыть редактор воркфлоу",
2647 "Create new workflow": "Создать новый воркфлоу",
2648 "Delete workflow": "Удалить воркфлоу",
2649 "Delete the workflow? This action is irreversible.": "Удалить этот воркфлоу? Отменить будет невозможно.",
2650 "Swap width and height": "Поменять местами ширину и высоту",
2651 "Authentication (optional)": "Данные для аутентификации (необязательно)",
2652 "sd_auto_auth_warning_1": " запускайте Stable Diffusion с флагом",
2653 "sd_auto_auth_warning_2": "! Адрес SD должен быть доступен с сервера SillyTavern.",
2654 "Upscale by": "Множитель апскейлинга",
2655 "Hires steps (2nd pass)": "Кол-во шагов Hires (на втором проходе)"
2587}2656}
public/locales/th-th.json+1461 -0
@@ -0,0 +1,1461 @@
1{
2 "Favorite": "รายการโปรด",
3 "Tag": "แท็ก",
4 "Duplicate": "คัดลอก",
5 "Persona": "ตัวตน",
6 "Delete": "ลบ",
7 "AI Response Configuration": "ตั้งค่าการตอบกลับ AI",
8 "AI Configuration panel will stay open": "แผงการตั้งค่า AI จะคงเปิดอยู่",
9 "clickslidertips": "คลิกเพื่อดูคำแนะนำสไลเดอร์",
10 "MAD LAB MODE ON": "เปิดโหมด Mad Lab",
11 "Documentation on sampling parameters": "เอกสารอธิบายค่าพารามิเตอร์การสุ่ม",
12 "kobldpresets": "พรีเซ็ต KoboldAI",
13 "guikoboldaisettings": "การตั้งค่า GUI KoboldAI",
14 "Update current preset": "อัปเดตพรีเซ็ตปัจจุบัน",
15 "Save preset as": "บันทึกการปรับแต่ง",
16 "Import preset": "นำเข้าการปรับแต่ง",
17 "Export preset": "ส่งออกการปรับแต่ง",
18 "Restore current preset": "กู้คืนการปรับแต่งล่าสุด",
19 "Delete the preset": "ลบการปรับแต่ง",
20 "novelaipresets": "พรีเซ็ต NovelAI",
21 "Default": "ค่าเริ่มต้น",
22 "openaipresets": "พรีเซ็ต OpenAI",
23 "Text Completion presets": "รูปแบบของ Text Completion",
24 "AI Module": "โมดูลเอไอ",
25 "Changes the style of the generated text.": "ปรับเปลี่ยนสไตล์ในการสร้างข้อความ",
26 "No Module": "ไม่พบโมดูล",
27 "Instruct": "โหมดคำสั่ง",
28 "Prose Augmenter": "Prose Augmenter",
29 "Text Adventure": "เกมข้อความผจญภัย",
30 "response legth(tokens)": "ความยาวในการตอบ (ต่อโทเค็น)",
31 "Streaming": "สตรีมมิ่ง",
32 "Streaming_desc": "แสดงข้อความเป็นบิตต่อบิตขณะที่ถูกสร้างขึ้น",
33 "context size(tokens)": "ขนาดความจำ (ต่อโทเค็น)",
34 "unlocked": "ปลดล็อค",
35 "Only enable this if your model supports context sizes greater than 8192 tokens": "ให้เปิดสิ่งนี้เฉพาะถ้าหากความจำในโมเดลของคุณรองรับได้มากกว่า 8192 โทเค็น",
36 "Max prompt cost:": "ค่าใช้จ่ายสูงสุดของพรอมต์:",
37 "Display the response bit by bit as it is generated.": "แสดงข้อความทีละตัวอักษรขณะกำลังถูกสร้าง",
38 "When this is off, responses will be displayed all at once when they are complete.": "หากปิดสิ่งนี้ ระบบจะแสดงข้อความมาให้ทั้งหมดในครั้งเดียวเมื่อถูกสร้างจนจบ",
39 "Temperature": "Temperature",
40 "rep.pen": "rep.pen",
41 "Rep. Pen. Range.": "Rep. Pen. Range.",
42 "Rep. Pen. Slope": "Rep. Pen. Slope",
43 "Rep. Pen. Freq.": "Rep. Pen. Freq.",
44 "Rep. Pen. Presence": "Rep. Pen. Presence",
45 "TFS": "การสุ่มแบบ Tail Free",
46 "Phrase Repetition Penalty": "โทษการพูดซ้ำ",
47 "Off": "ปิด",
48 "Very light": "สว่างมาก",
49 "Light": "สว่าง",
50 "Medium": "ปานกลาง",
51 "Aggressive": "รุนแรง",
52 "Very aggressive": "รุนแรงมาก",
53 "Unlocked Context Size": "ปลดล๊อคขนาดความจำ",
54 "Unrestricted maximum value for the context slider": "ค่าสูงสุดไม่จำกัดสำหรับตัวปรับความจำ",
55 "Context Size (tokens)": "ขนาดความจำ (ต่อโทเค็น)",
56 "Max Response Length (tokens)": "ความยาวในการตอบสูงสุด (ต่อโทเค็น)",
57 "Multiple swipes per generation": "ปัดหลายครั้งต่อการสร้าง",
58 "Enable OpenAI completion streaming": "เปิดใช้งานการสตรีมผลลัพธ์จาก OpenAI",
59 "Frequency Penalty": "Frequency Penalty",
60 "Presence Penalty": "Presence Penalty",
61 "Count Penalty": "Count Penalty",
62 "Top K": "Top K",
63 "Top P": "Top P",
64 "Repetition Penalty": "Repetition Penalty",
65 "Min P": "Min P",
66 "Top A": "Top A",
67 "Quick Prompts Edit": "แก้ไขชุดคำสั่งแบบรวบรัด",
68 "Main": "หน้าหลัก",
69 "NSFW": "เนื้อหาไม่เหมาะสม",
70 "Jailbreak": "เจลเบรค",
71 "Utility Prompts": "ชุดคำสั่งเพิ่มเติม",
72 "Impersonation prompt": "ชุดคำสั่งสำหรับการสวมบทบาท",
73 "Restore default prompt": "คืนค่าชุดคำสั่งเริ่มต้น",
74 "Prompt that is used for Impersonation function": "ชุดคำสั่งที่จะถูกใช้ในฟังชั่นการสวมบทบาท",
75 "World Info Format Template": "เท็มเพลตรูปแบบของ World Info",
76 "Restore default format": "คืนค่ารูปแบบเริ่มต้น",
77 "Wraps activated World Info entries before inserting into the prompt.": "ห่อหุ้มรายการ World Info ที่เปิดใช้งานก่อนแทรกลงในพรอมต์",
78 "scenario_format_template_part_1": "ใช้",
79 "scenario_format_template_part_2": "เพื่อทำเครื่องหมายตำแหน่งที่จะแทรกเนื้อหา",
80 "Scenario Format Template": "เทมเพลตรูปแบบสถานการณ์",
81 "Personality Format Template": "เทมเพลตรูปแบบบุคลิกภาพ",
82 "Group Nudge Prompt Template": "เทมเพลตพรอมต์กระตุ้นกลุ่ม",
83 "Sent at the end of the group chat history to force reply from a specific character.": "ส่งท้ายประวัติแชทกลุ่มเพื่อบังคับให้ตัวละครหนึ่งตอบกลับ",
84 "New Chat": "เริ่มแชทใหม่",
85 "Restore new chat prompt": "คืนค่าชุดคำสั่งสำหรับแชทใหม่",
86 "Set at the beginning of the chat history to indicate that a new chat is about to start.": "ตั้งค่าไว้ต้นประวัติการแชทเพื่อระบุว่าแชทใหม่กำลังจะเริ่มต้น",
87 "New Group Chat": "เริ่มต้นแชทกลุ่มใหม่",
88 "Restore new group chat prompt": "คืนค่าชุดคำสั่งแชทกลุ่มใหม่",
89 "Set at the beginning of the chat history to indicate that a new group chat is about to start.": "ตั้งค่าไว้ต้นประวัติการแชทเพื่อระบุว่าแชทกลุ่มใหม่กำลังจะเริ่มต้น",
90 "New Example Chat": "ตัวอย่างการพูดคุยใหม่",
91 "Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "ตั้งค่าไว้ต้นตัวอย่างบทสนทนาเพื่อระบุว่าตัวอย่างแชทใหม่กำลังจะเริ่มต้น",
92 "Continue nudge": "กระตุ้นให้ดำเนินต่อ",
93 "Set at the end of the chat history when the continue button is pressed.": "ตั้งค่าไว้ท้ายประวัติการแชทเมื่อกดปุ่มดำเนินต่อ",
94 "Replace empty message": "เขียนทับข้อความที่ว่างเปล่า",
95 "Send this text instead of nothing when the text box is empty.": "ส่งข้อความที่กำหนดนี้ไปแทนข้อความที่ว่างเปล่า หากช่องแชทไม่ได้ส่งข้อความใดๆไป",
96 "Seed": "Seed",
97 "Set to get deterministic results. Use -1 for random seed.": "ตั้งค่าเพื่อให้ได้ผลลัพธ์ที่แน่นอน ใช้ -1 สำหรับ seed แบบสุ่ม",
98 "Temperature controls the randomness in token selection": "Temperature ควบคุมอัตราสุ่มในการเลือกโทเค็น",
99 "Top_K_desc": "จำกัดทางเลือกโทเค็นตามอันดับความน่าจะเป็นสูงสุด K ตัว",
100 "Top_P_desc": "จำกัดทางเลือกโทเค็นตามเปอร์เซ็นต์ความน่าจะเป็นสะสม",
101 "Typical P": "Typical P",
102 "Typical_P_desc": "การควบคุมการสุ่มโดยเลือกโทเค็นที่มีความน่าจะเป็นใกล้เคียงกับค่าเฉลี่ย",
103 "Min_P_desc": "กำหนดขีดจำกัดความน่าจะเป็นขั้นต่ำสำหรับการเลือกโทเค็น",
104 "Top_A_desc": "การสุ่มแบบ Top A ที่ปรับให้เหมาะสมกับบริบท",
105 "Tail_Free_Sampling_desc": "การสุ่มโดยตัดโทเค็นที่มีความน่าจะเป็นต่ำออก",
106 "rep.pen range": "ขอบเขตโทษการซ้ำ",
107 "Mirostat": "Mirostat",
108 "Mode": "โหมด",
109 "Mirostat_Mode_desc": "อธิบายโหมด Mirostat",
110 "Tau": "Tau",
111 "Mirostat_Tau_desc": "พารามิเตอร์ความแปรปรวนสำหรับผลลัพธ์ Mirostat",
112 "Eta": "Eta",
113 "Mirostat_Eta_desc": "อัตราการเรียนรู้ของ Mirostat",
114 "Ban EOS Token": "ห้ามใช้โทเค็น EOS",
115 "Ban_EOS_Token_desc": "บังคับให้โมเดลไม่จบการสร้างข้อความก่อนเวลาอันควร",
116 "GBNF Grammar": "ไวยากรณ์ GBNF",
117 "Type in the desired custom grammar": "พิมพ์ไวยากรณ์ที่ต้องการ",
118 "Samplers Order": "ลำดับของตัวสุ่ม",
119 "Samplers will be applied in a top-down order. Use with caution.": "ตัวสุ่มจะถูกใช้งานตามลำดับจากบนลงล่าง ใช้ด้วยความระมัดระวัง",
120 "Tail Free Sampling": "การสุ่มแบบ Tail Free",
121 "Load koboldcpp order": "โหลดลำดับของ koboldcpp",
122 "Preamble": "คำนำ",
123 "Use style tags to modify the writing style of the output.": "ใช้แท็กสไตล์เพื่อปรับเปลี่ยนรูปแบบการเขียนของผลลัพธ์",
124 "Banned Tokens": "โทเค็นที่ถูกห้าม",
125 "Sequences you don't want to appear in the output. One per line.": "ลำดับที่คุณไม่ต้องการให้ปรากฏในผลลัพธ์ หนึ่งรายการต่อบรรทัด",
126 "Logit Bias": "Logit Bias",
127 "Add": "เพิ่ม",
128 "Helps to ban or reenforce the usage of certain words": "ช่วยห้ามหรือเสริมการใช้คำบางคำ",
129 "CFG Scale": "CFG Scale",
130 "Negative Prompt": "Negative Prompt",
131 "Add text here that would make the AI generate things you don't want in your outputs.": "เพิ่มข้อความที่นี่ที่จะทำให้ AI สร้างสิ่งที่คุณไม่ต้องการในผลลัพธ์",
132 "Used if CFG Scale is unset globally, per chat or character": "ใช้เมื่อ CFG Scale ไม่ได้ถูกตั้งค่าทั่วไป ต่อแชท หรือตัวละคร",
133 "Mirostat Tau": "Mirostat Tau",
134 "Mirostat LR": "Mirostat LR",
135 "Min Length": "ความยาวต่ำสุด",
136 "Top K Sampling": "การสุ่มแบบ Top K",
137 "Nucleus Sampling": "การสุ่มแบบ Nucleus",
138 "Top A Sampling": "การสุ่มแบบ Top A",
139 "CFG": "CFG",
140 "Neutralize Samplers": "ทำให้ตัวสุ่มเป็นกลาง",
141 "Set all samplers to their neutral/disabled state.": "ตั้งค่าตัวสุ่มทั้งหมดให้เป็นสถานะเป็นกลาง/ปิดใช้งาน",
142 "Sampler Select": "เลือกตัวสุ่ม",
143 "Customize displayed samplers or add custom samplers.": "ปรับแต่งตัวสุ่มที่แสดงหรือเพิ่มตัวสุ่มแบบกำหนดเอง",
144 "Epsilon Cutoff": "Epsilon Cutoff",
145 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "Epsilon cutoff กำหนดขีดจำกัดความน่าจะเป็นขั้นต่ำที่โทเค็นจะถูกแยกออกจากการสุ่ม",
146 "Eta Cutoff": "Eta Cutoff",
147 "Eta_Cutoff_desc": "คำอธิบาย Eta Cutoff สำหรับการกรองโทเค็น",
148 "rep.pen decay": "การลดลงของ rep.pen",
149 "Encoder Rep. Pen.": "Encoder Rep. Pen.",
150 "No Repeat Ngram Size": "ขนาด Ngram ที่ไม่ซ้ำ",
151 "Skew": "ความเบี้ยว",
152 "Max Tokens Second": "โทเค็นสูงสุดต่อวินาที",
153 "Smooth Sampling": "Smooth Sampling",
154 "Smooth_Sampling_desc": "สามารถปรับการกระจายโดยใช้การแปลงดีกรี 2/3 ได้ ค่าที่ต่ำกว่าของ Smoothing Factor จะให้ความคิดสร้างสรรค์มากขึ้น โดยปกติ 0.2-0.3 จะเป็นจุดที่เหมาะสม (สมมติว่าเส้นโค้ง = 1) ค่าที่สูงขึ้นของ Smoothing Curve จะทำให้เส้นโค้งชันขึ้น และการเลือกที่มีความน่าจะเป็นต่ำจะได้รับการลงโทษอย่างรุนแรงมากขึ้น เส้นโค้ง 1.0 เหมือนกับการใช้ Smoothing Factor เพียงอย่างเดียว",
155 "Smoothing Factor": "Smoothing Factor",
156 "Smoothing Curve": "Smoothing Curve",
157 "DRY_Repetition_Penalty_desc": "DRY จะลงโทษโทเค็นที่ขยายจุดสิ้นสุดของข้อมูลนำเข้าเป็นลำดับที่เกิดขึ้นก่อนหน้านี้ในข้อมูลนำเข้า ตั้งค่า multiplier เป็น 0 เพื่อปิดใช้งาน",
158 "DRY Repetition Penalty": "DRY Repetition Penalty",
159 "DRY_Multiplier_desc": "ตั้งค่าให้มากกว่า 0 เพื่อเปิดใช้งาน DRY ควบคุมขนาดของโทษสำหรับลำดับสั้นที่สุดที่ได้รับโทษ",
160 "Multiplier": "Multiplier",
161 "DRY_Base_desc": "ควบคุมอัตราที่โทษเพิ่มขึ้นตามความยาวของลำดับที่เพิ่มขึ้น",
162 "Base": "ค่าพื้นฐาน",
163 "DRY_Allowed_Length_desc": "ลำดับที่ยาวที่สุดที่สามารถทำซ้ำได้โดยไม่มีโทษ",
164 "Allowed Length": "ความยาวที่อนุญาต",
165 "Penalty Range": "Penalty Range",
166 "DRY_Sequence_Breakers_desc": "โทเค็นที่ทำให้การจับคู่ลำดับไม่ต่อเนื่อง ระบุเป็นรายการที่คั่นด้วยเครื่องหมายจุลภาคของสตริงที่อยู่ในเครื่องหมายคำพูด",
167 "Sequence Breakers": "Sequence Breakers",
168 "JSON-serialized array of strings.": "อาร์เรย์ของสตริงในรูปแบบ JSON",
169 "Dynamic Temperature": "Tempurature แบบไดนามิก",
170 "Scale Temperature dynamically per token, based on the variation of probabilities": "ปรับขนาด Tempurature แบบไดนามิกต่อโทเค็น ตามความแปรปรวนของความน่าจะเป็น",
171 "Minimum Temp": "Temp ต่ำสุด",
172 "Maximum Temp": "Temp สูงสุด",
173 "Exponent": "เลขชี้กำลัง",
174 "Mirostat (mode=1 is only for llama.cpp)": "Mirostat (โหมด=1 สำหรับ llama.cpp เท่านั้น)",
175 "Mirostat_desc": "อธิบาย Mirostat",
176 "Mirostat Mode": "โหมด Mirostat",
177 "Variability parameter for Mirostat outputs": "พารามิเตอร์ความแปรปรวนสำหรับผลลัพธ์ Mirostat",
178 "Mirostat Eta": "Mirostat Eta",
179 "Learning rate of Mirostat": "อัตราการเรียนรู้ของ Mirostat",
180 "Beam search": "การค้นหาแบบ Beam",
181 "Helpful tip coming soon.": "เคล็ดลับมีประโยชน์จะมาเร็วๆ นี้",
182 "Number of Beams": "จำนวน Beam",
183 "Length Penalty": "โทษความยาว",
184 "Early Stopping": "การหยุดล่วงหน้า",
185 "Contrastive search": "การค้นหาแบบ Contrastive",
186 "Penalty Alpha": "Penalty Alpha",
187 "Strength of the Contrastive Search regularization term. Set to 0 to disable CS": "ความแรงของเทอม regularization ของ Contrastive Search ตั้งเป็น 0 เพื่อปิด CS",
188 "Do Sample": "Do Sample",
189 "Add BOS Token": "เพิ่ม BOS Token",
190 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "เพิ่ม bos_token ไว้ต้นพรอมต์ การปิดสิ่งนี้อาจทำให้การตอบกลับสร้างสรรค์มากขึ้น",
191 "Ban the eos_token. This forces the model to never end the generation prematurely": "ห้าม eos_token ทำให้โมเดลไม่สิ้นสุดการสร้างก่อนเวลาอันควร",
192 "Ignore EOS Token": "เพิกเฉยต่อ EOS Token",
193 "Ignore the EOS Token even if it generates.": "เพิกเฉย EOS Token แม้ว่าจะถูกสร้างขึ้น",
194 "Skip Special Tokens": "ข้าม Special Tokens",
195 "Temperature Last": "อุณหภูมิสุดท้าย",
196 "Temperature_Last_desc": "ใช้อุณหภูมิเป็นขั้นตอนสุดท้ายในการสุ่ม",
197 "Speculative Ngram": "Speculative Ngram",
198 "Use a different speculative decoding method without a draft model": "ใช้วิธีการ speculative decoding ที่แตกต่างกันโดยไม่ต้องใช้โมเดลร่าง",
199 "Spaces Between Special Tokens": "ช่องว่างระหว่าง Special Tokens",
200 "LLaMA / Mistral / Yi models only": "เฉพาะโมเดล LLaMA / Mistral / Yi เท่านั้น",
201 "Example: some text [42, 69, 1337]": "ตัวอย่าง: ข้อความบางส่วน [42, 69, 1337]",
202 "Classifier Free Guidance. More helpful tip coming soon": "คำแนะนำฟรีเกี่ยวกับเครื่องมือจำแนกประเภท จะมีเคล็ดลับที่เป็นประโยชน์เพิ่มเติมเร็วๆ นี้",
203 "Scale": "ขนาด",
204 "JSON Schema": "JSON Schema",
205 "Type in the desired JSON schema": "พิมพ์ JSON schema ที่ต้องการ",
206 "Grammar String": "สตริงไวยากรณ์",
207 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF หรือ EBNF ขึ้นอยู่กับแบ็กเอนด์ที่ใช้ หากคุณใช้สิ่งนี้ คุณควรรู้ว่าเป็นแบบไหน",
208 "Top P & Min P": "Top P & Min P",
209 "Load default order": "โหลดลำดับเริ่มต้น",
210 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "เฉพาะ llama.cpp กำหนดลำดับของตัวสุ่ม หากโหมด Mirostat ไม่ใช่ 0 ลำดับตัวสุ่มจะถูกเพิกเฉย",
211 "Sampler Priority": "ลำดับความสำคัญของตัวสุ่ม",
212 "Ooba only. Determines the order of samplers.": "เฉพาะ Ooba กำหนดลำดับของตัวสุ่ม",
213 "Character Names Behavior": "พฤติกรรมการแสดงชื่อตัวละคร",
214 "Helps the model to associate messages with characters.": "ช่วยให้โมเดลเชื่อมโยงข้อความกับตัวละคร",
215 "None": "ไม่มี",
216 "character_names_default": "ชื่อตัวละครเริ่มต้น",
217 "Don't add character names.": "ไม่เพิ่มชื่อตัวละคร",
218 "Completion": "การเสร็จสิ้น",
219 "character_names_completion": "การเสร็จสิ้นชื่อตัวละคร",
220 "Add character names to completion objects.": "เพิ่มชื่อตัวละครไปยังออบเจ็กต์การเสร็จสิ้น",
221 "Message Content": "เนื้อหาของข้อความ",
222 "Prepend character names to message contents.": "เพิ่มชื่อตัวละครไว้หน้าเนื้อหาข้อความ",
223 "Continue Postfix": "Continue Postfix",
224 "The next chunk of the continued message will be appended using this as a separator.": "ส่วนต่อไปของข้อความที่ต่อเนื่องจะถูกผนวกโดยใช้สิ่งนี้เป็นตัวคั่น",
225 "Space": "พื้นที่ว่าง",
226 "Newline": "ขึ้นบรรทัดใหม่",
227 "Double Newline": "ขึ้นสองบรรทัด",
228 "Wrap user messages in quotes before sending": "ครอบข้อความของผู้ใช้ด้วยเครื่องหมายคำพูดก่อนส่ง",
229 "Wrap in Quotes": "Wrap in Quotes",
230 "Wrap entire user message in quotes before sending.": "ครอบข้อความของผู้ใช้ทั้งหมดด้วยเครื่องหมายคำพูดก่อนส่ง",
231 "Leave off if you use quotes manually for speech.": "ปิดถ้าคุณใช้เครื่องหมายคำพูดด้วยตนเองสำหรับการพูด",
232 "Continue prefill": "Continue prefill",
233 "Continue sends the last message as assistant role instead of system message with instruction.": "Continue ส่งข้อความสุดท้ายเป็นบทบาทผู้ช่วยแทนที่จะเป็นข้อความระบบพร้อมคำแนะนำ",
234 "Squash system messages": "Squash system messages",
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "รวมข้อความระบบที่ติดต่อกันเป็นหนึ่งเดียว (ไม่รวมบทสนทนาตัวอย่าง) อาจปรับปรุงความสอดคล้องสำหรับโมเดลบางรุ่น",
236 "Enable function calling": "Enable function calling",
237 "enable_functions_desc_1":"อนุญาตให้มีการใช้งาน ",
238 "enable_functions_desc_2":"function tools" ,
239 "enable_functions_desc_3": "สามารถใช้งานโดยใช้ผ่าน Extension เพื่อเพิ่มฟังก์ชั่นเพิ่มเติม",
240 "enable_functions_desc_4": "ไม่ Support ถ้า Prompt Post-Processing with \"no tools\" ถูกเปิดใช้งาน",
241 "Allows the model to return its thinking process.": "อนุญาตให้โมเดลส่งคืนกระบวนการคิดของตัวเอง",
242 "This setting affects visibility only.": "การตั้งค่านี้มีผลต่อการมองเห็นเท่านั้น",
243 "Send inline images": "ส่งรูปภาพแบบ inline",
244 "image_inlining_hint_1": "ส่งรูปภาพในพรอมต์หากโมเดลรองรับ โดยใช้",
245 "image_inlining_hint_2": "เพื่อดำเนินการในข้อความนั้นๆ หรือใช้ตัวเลือก",
246 "image_inlining_hint_3": "เพื่อแนบไฟล์รูปภาพลงในแชท",
247 "Inline Image Quality": "คุณภาพรูปภาพ Inline",
248 "openai_inline_image_quality_auto": "อัตโนมัติ",
249 "openai_inline_image_quality_low": "ต่ำ",
250 "openai_inline_image_quality_high": "สูง",
251 "Use AI21 Tokenizer": "ใช้ AI21 Tokenizer",
252 "Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "ใช้ tokenizer ที่เหมาะสมสำหรับโมเดล Jurassic ซึ่งมีประสิทธิภาพมากกว่าของ GPT",
253 "Use Google Tokenizer": "ใช้ Google Tokenizer",
254 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "ใช้ tokenizer ที่เหมาะสมสำหรับโมเดล Google ผ่าน API ช้ากว่าในการประมวลผลพรอมต์ แต่นับโทเค็นได้แม่นยำมากกว่า",
255 "Use system prompt": "ใช้คำสั่งระบบ (system prompt)",
256 "(Gemini 1.5 Pro/Flash only)": "(เฉพาะ Gemini 1.5 Pro/Flash เท่านั้น)",
257 "Merges_all_system_messages_desc_1": "รวมข้อความระบบทั้งหมดจนถึงข้อความแรกที่ไม่ใช่บทบาทระบบ และส่งในฟิลด์",
258 "Merges_all_system_messages_desc_2": "เดี่ยว",
259 "Assistant Prefill": "Assistant Prefill",
260 "Start Claude's answer with...": "เริ่มคำตอบของ Claude ด้วย...",
261 "Assistant Impersonation Prefill": "Assistant Impersonation Prefill",
262 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "ส่งพรอมต์ระบบสำหรับโมเดลที่รองรับ หากปิดใช้งาน ข้อความผู้ใช้จะถูกเพิ่มไว้ต้นพรอมต์",
263 "User first message": "ข้อความเริ่มต้นของผู้ใช้",
264 "Restore User first message": "คืนค่าข้อความเริ่มต้นของผู้ใช้",
265 "Human message": "ข้อความของยูเซอร์",
266 "New preset": "เพิ่มการปรับแต่งใหม่",
267 "Delete preset": "ลบการปรับแต่ง",
268 "View / Edit bias preset": "ดู / แก้ไข bias preset",
269 "Add bias entry": "เพิ่มรายการ bias",
270 "Most tokens have a leading space.": "โทเค็นส่วนใหญ่มีช่องว่างนำหน้า",
271 "API Connections": "การเชื่อมต่อ API",
272 "Text Completion": "Text Completion",
273 "Chat Completion": "Chat Completion",
274 "NovelAI": "NovelAI",
275 "AI Horde": "AI Horde",
276 "KoboldAI": "KoboldAI",
277 "Avoid sending sensitive information to the Horde.": "โปรดหลีกเลี่ยงการส่งข้อมูลที่ละเอียดอ่อนไปยัง Horde",
278 "Review the Privacy statement": "ตรวจสอบนโยบายความเป็นส่วนตัว",
279 "Register a Horde account for faster queue times": "ลงทะเบียนบัญชี Horde เพื่อเวลาคิวที่เร็วขึ้น",
280 "Learn how to contribute your idle GPU cycles to the Horde": "เรียนรู้วิธีการใช้งาน GPU ของคุณกับ Horde",
281 "Adjust context size to worker capabilities": "ปรับขนาดบริบทให้เหมาะกับความสามารถของ worker",
282 "Adjust response length to worker capabilities": "ปรับความยาวการตอบกลับให้เหมาะกับความสามารถของ worker",
283 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "สามารถช่วยแก้ไขการตอบกลับที่ไม่ดีโดยการจัดคิวเฉพาะ worker ที่ได้รับการอนุมัติ อาจทำให้เวลาตอบกลับช้าลง",
284 "Trusted workers only": "เฉพาะ worker ที่เชื่อถือได้",
285 "API key": "คีย์ API",
286 "Get it here:": "รับได้ที่นี่:",
287 "Register": "ลงทะเบียน",
288 "View my Kudos": "ดู Kudos ของฉัน",
289 "Enter": "ส่ง",
290 "to use anonymous mode.": "เพื่อใช้โหมดไม่ระบุชื่อ",
291 "Clear your API key": "ล้างคีย์ API ของคุณ",
292 "For privacy reasons, your API key will be hidden after you reload the page.": "ด้วยเหตุผลด้านความเป็นส่วนตัว คีย์ API ของคุณจะถูกซ่อนไว้หลังจากรีเฟรชหน้าเว็บไซต์แล้ว",
293 "Models": "รุ่นโมเดล",
294 "Refresh models": "รีเฟรชโมเดล",
295 "-- Horde models not loaded --": "-- โมเดล Horde ไม่ได้โหลด --",
296 "Not connected...": "ไม่มีการเชื่อมต่อ...",
297 "API url": "URL ของ API",
298 "Example: http://127.0.0.1:5000/api ": "ตัวอย่าง: http://127.0.0.1:5000/api",
299 "Connect": "เชื่อมต่อ",
300 "Cancel": "ยกเลิก",
301 "Novel API key": "คีย์ API ของ Novel",
302 "Get your NovelAI API Key": "รับคีย์ API ของ NovelAI ได้ที่นี่",
303 "Enter it in the box below": "กรอกคีย์ที่ได้รับลงในช่องด้านล่าง",
304 "Novel AI Model": "โมเดลของ NovelAI",
305 "No connection...": "ไม่มีการเชื่อมต่อ...",
306 "API Type": "ประเภทของ API",
307 "Default (completions compatible)": "ค่าเริ่มต้น (รองรับการเติมข้อความอัตโนมัติ)",
308 "TogetherAI API Key": "คีย์ API ของ Together AI",
309 "TogetherAI Model": "โมเดลของ TogetherAI",
310 "-- Connect to the API --": "-- เชื่อมต่อ API --",
311 "OpenRouter API Key": "คีย์ API ของ OpenRouter",
312 "Click Authorize below or get the key from": "กดปุ่ม Authorize ข้างล่างเพื่อยืนยัน Persona หรือให้ทำการนำคีย์มาใส่เพื่อใช้งาน",
313 "View Remaining Credits": "เช็คเครดิตคงเหลือ",
314 "OpenRouter Model": "OpenRouter โมเดล",
315 "Model Providers": "ผู้ให้บริการโมเดล",
316 "InfermaticAI API Key": "คีย์ API ของ InfermaticAI",
317 "InfermaticAI Model": "โมเดลของ InfermaticAI",
318 "DreamGen API key": "คีย์ API ของ DreamGen",
319 "DreamGen Model": "โมเดลของ DreamGen",
320 "Mancer API key": "คีย์ API ของ Mancer",
321 "Mancer Model": "โมเดลของ Mancer",
322 "Make sure you run it with": "ให้แน่ใจว่าคุณเปิดมันโดยใช้...",
323 "flag": "flag",
324 "API key (optional)": "API key (ไม่บังคับ)",
325 "Server url": "ที่อยู่ของเซิร์ฟเวอร์ (URL)",
326 "Example: http://127.0.0.1:5000": "ตัวอย่าง: http://127.0.0.1:5000",
327 "Custom model (optional)": "โมเดลที่กำหนดเอง (ไม่บังคับ)",
328 "vllm-project/vllm": "vllm-project/vllm",
329 "vLLM API key": "คีย์ API ของ vLLM",
330 "Example: http://127.0.0.1:8000": "ตัวอย่าง: http://127.0.0.1:8000",
331 "vLLM Model": "โมเดลของ vLLM",
332 "PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine",
333 "Aphrodite API key": "คีย์ API ของ Aphrodite",
334 "Aphrodite Model": "โมเดลของ Aphrodite",
335 "ggerganov/llama.cpp": "ggerganov/llama.cpp",
336 "Example: http://127.0.0.1:8080": "ตัวอย่าง: http://127.0.0.1:8080",
337 "Example: http://127.0.0.1:11434": "ตัวอย่าง: http://127.0.0.1:11434",
338 "Ollama Model": "โมเดลของ Ollama",
339 "Download": "ดาวน์โหลด",
340 "Tabby API key": "คีย์ API ของ Tabby",
341 "koboldcpp API key (optional)": "คีย์ API ของ koboldcpp (เพิ่มเติม)",
342 "Example: http://127.0.0.1:5001": "ตัวอย่าง: http://127.0.0.1:5001",
343 "Authorize": "อนุญาต",
344 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "โปรดเข้าสู่ระบบเพื่อรับ OpenRouter API Token โดยใช้ OAuth Flow จากนั้นคุณจะถูกเปลี่ยนเส้นทางไปยังเว็บไซต์ของ openrouter.ai",
345 "Bypass status check": "ข้ามการตรวจสอบสถานะ",
346 "Chat Completion Source": "แหล่งที่มาของ Chat Completion",
347 "Reverse Proxy": "Reverse Proxy",
348 "Proxy Presets": "พรีเซ็ต Proxy",
349 "Saved addresses and passwords.": "ที่อยู่และรหัสผ่านที่บันทึกไว้",
350 "Save Proxy": "เซฟพร็อกซี",
351 "Delete Proxy": "ลบพร็อกซี",
352 "Proxy Name": "ชื่อพร็อกซี",
353 "This will show up as your saved preset.": "สิ่งนี้จะปรากฏเป็นพรีเซ็ตที่คุณเซฟไว้",
354 "Proxy Server URL": "Proxy Server URL",
355 "Alternative server URL (leave empty to use the default value).": "URL เซิร์ฟเวอร์สำรอง (เว้นว่างไว้เพื่อใช้ค่าเริ่มต้น)",
356 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "โปรดลบคีย์ OAI API จริงของคุณออกจากหน้า API Panel ก่อนที่จะเริ่มพิมพ์ข้อความใด ๆ ลงในช่องนี้",
357 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "เราไม่สามารถช่วยเหลือปัญหาจากการใช้พร็อกซีของ OpenAI ที่ไม่ได้รับการรับรองอย่างเป็นทางการ",
358 "Doesn't work? Try adding": "ไม่ทำงาน? ลองเพิ่ม",
359 "at the end!": "ไว้ท้ายสุด!",
360 "Proxy Password": "รหัสผ่าน Proxy",
361 "Will be used as a password for the proxy instead of API key.": "สิ่งนี้จะถูกใช้เป็นรหัสผ่านสำหรับเข้าถึงพร็อกซี แทนที่คีย์ API",
362 "Peek a password": "แสดงรหัสผ่าน",
363 "OpenAI API key": "คีย์ API ของ OpenAI",
364 "View API Usage Metrics": "ดูสถิติการใช้งาน API",
365 "Follow": "ติดตาม",
366 "these directions": "คำแนะนำเหล่านี้",
367 "to get your OpenAI API key.": "เพื่อรับคีย์ API ของ OpenAI",
368 "Use Proxy password field instead. This input will be ignored.": "ใช้ฟิลด์รหัสผ่าน Proxy แทน อินพุตนี้จะถูกเพิกเฉย",
369 "OpenAI Model": "โมเดล OpenAI",
370 "Bypass API status check": "ข้ามการตรวจสอบสถานะ API",
371 "Show External models (provided by API)": "แสดงโมเดลภายนอก (ที่ API จัดให้)",
372 "Get your key from": "รับคีย์ของคุณจาก",
373 "Anthropic's developer console": "คอนโซลนักพัฒนาของ Anthropic",
374 "Claude Model": "โมเดล Claude",
375 "Window AI Model": "โมเดล Window AI",
376 "Model Order": "ลำดับโมเดล",
377 "Alphabetically": "เรียงตามตัวอักษร",
378 "Price": "ราคา",
379 "Context Size": "ขนาดของบริบท",
380 "Group by vendors": "จัดกลุ่มตามผู้ขาย",
381 "Group by vendors Description": "คำอธิบายการจัดกลุ่มตามผู้ขาย",
382 "Allow fallback routes": "อนุญาตเส้นทางสำรอง",
383 "Allow fallback routes Description": "คำอธิบายการอนุญาตเส้นทางสำรอง",
384 "Scale API Key": "คีย์ API ของ Scale",
385 "Clear your cookie": "ล้างคุกกี้ของคุณ",
386 "Alt Method": "วิธีสำรอง",
387 "AI21 API Key": "คีย์ API ของ AI21",
388 "AI21 Model": "โมเดล AI21",
389 "Google AI Studio API Key": "คีย์ API ของ Google AI Studio",
390 "Google Model": "โมเดล Google",
391 "MistralAI API Key": "คีย์ API ของ MistralAI",
392 "MistralAI Model": "โมเดล MistralAI",
393 "Groq API Key": "คีย์ API ของ Groq",
394 "Groq Model": "โมเดล Groq",
395 "Perplexity API Key": "คีย์ API ของ Perplexity",
396 "Perplexity Model": "โมเดลของ Perplexity",
397 "Cohere API Key": "คีย์ API ของ Cohere",
398 "Cohere Model": "โมเดลของ Cohere",
399 "Custom Endpoint (Base URL)": "จุดปลายทางกำหนดเอง (Base URL)",
400 "Custom API Key": "คีย์ API แบบกำหนดเอง",
401 "Available Models": "โมเดลที่สามารถใช้ได้",
402 "Prompt Post-Processing": "การประมวลผลพรอมต์หลังการส่ง",
403 "Applies additional processing to the prompt before sending it to the API.": "ใช้การประมวลผลเพิ่มเติมกับพรอมต์ก่อนส่งไปยัง API",
404 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "ตรวจสอบการเชื่อมต่อ API โดยส่งข้อความทดสอบสั้นๆ โปรดทราบว่าคุณจะถูกเรียกเก็บเงินสำหรับการทดสอบนี้!",
405 "Test Message": "ทดสอบการส่งข้อความ",
406 "Auto-connect to Last Server": "เชื่อมต่อเซิฟเวอร์อัตโนมัติ",
407 "Missing key": "โปรดใส่ API Key",
408 "Key saved": "บันทึก API Key แล้ว",
409 "View hidden API keys": "แสดง API Key",
410 "AI Response Formatting": "แบบแผนการตอบกลับของ AI",
411 "Advanced Formatting": "แบบแผนขั้นสูง",
412 "Context Template": "เทมเพลต Context",
413 "Auto-select this preset for Instruct Mode": "เลือกพรีเซ็ตนี้โดยอัตโนมัติสำหรับโหมดคำสั่ง",
414 "Story String": "สตริงเรื่องราว",
415 "Example Separator": "ตัวแยกตัวอย่าง",
416 "Chat Start": "เริ่มต้นการแชท",
417 "Add Chat Start and Example Separator to a list of stopping strings.": "เพิ่ม Chat Start และ Example Separator ไปยังรายการสตริงหยุด",
418 "Use as Stop Strings": "ใช้เป็นสตริงหยุด",
419 "Allow Jailbreak": "อนุญาต Jailbreak",
420 "Context Order": "ลำดับ Context",
421 "Summary": "สรุป",
422 "Author's Note": "หมายเหตุจากผู้เขียน",
423 "Example Dialogues": "ตัวอย่างบทสนทนา",
424 "Hint": "คำแนะนำ",
425 "In-Chat Position not affected": "ตำแหน่งในแชทไม่ได้รับผลกระทบ",
426 "Instruct Mode": "โหมดคำสั่ง",
427 "Enabled": "เปิดใช้งาน",
428 "instruct_bind_to_context": "ผูกกับ context",
429 "Bind to Context": "ผูกกับ Context",
430 "Presets": "พรีเซ็ต",
431 "Auto-select this preset on API connection": "เลือกพรีเซ็ตนี้โดยอัตโนมัติเมื่อเชื่อมต่อ API",
432 "Activation Regex": "Regex การเปิดใช้งาน",
433 "Wrap Sequences with Newline": "ห่อลำดับด้วยบรรทัดใหม่",
434 "Replace Macro in Sequences": "แทนที่แมโครในลำดับ",
435 "Skip Example Dialogues Formatting": "ข้ามการจัดรูปแบบตัวอย่างบทสนทนา",
436 "Include Names": "รวมชื่อ",
437 "Force for Groups and Personas": "บังคับสำหรับกลุ่มและ Persona",
438 "System Prompt": "Prompt ของระบบ",
439 "Instruct Mode Sequences": "ลำดับโหมดคำสั่ง",
440 "System Prompt Wrapping": "การห่อหุ้ม System Prompt",
441 "Inserted before a System prompt.": "แทรกก่อน System prompt",
442 "System Prompt Prefix": "คำนำหน้า System Prompt",
443 "Inserted after a System prompt.": "แทรกหลัง System prompt",
444 "System Prompt Suffix": "คำต่อท้าย System Prompt",
445 "Chat Messages Wrapping": "การห่อหุ้มข้อความแชท",
446 "Inserted before a User message and as a last prompt line when impersonating.": "แทรกก่อนข้อความผู้ใช้และเป็นบรรทัดพรอมต์สุดท้ายเมื่อสวมบทบาท",
447 "User Message Prefix": "คำนำหน้าข้อความผู้ใช้",
448 "Inserted after a User message.": "แทรกหลังข้อความผู้ใช้",
449 "User Message Suffix": "คำต่อท้ายข้อความผู้ใช้",
450 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "แทรกก่อนข้อความผู้ช่วยและเป็นบรรทัดพรอมต์สุดท้ายเมื่อสร้างการตอบกลับ AI",
451 "Assistant Message Prefix": "คำนำหน้าข้อความผู้ช่วย",
452 "Inserted after an Assistant message.": "แทรกหลังข้อความผู้ช่วย",
453 "Assistant Message Suffix": "คำต่อท้ายข้อความผู้ช่วย",
454 "Inserted before a System (added by slash commands or extensions) message.": "แทรกก่อนข้อความระบบ (ที่เพิ่มโดยคำสั่ง slash หรือส่วนขยาย)",
455 "System Message Prefix": "คำนำหน้าข้อความระบบ",
456 "Inserted after a System message.": "แทรกหลังข้อความระบบ",
457 "System Message Suffix": "คำต่อท้ายข้อความระบบ",
458 "If enabled, System Sequences will be the same as User Sequences.": "หากเปิดใช้งาน System Sequences จะเหมือนกับ User Sequences",
459 "System same as User": "ระบบเหมือนกับผู้ใช้",
460 "Misc. Sequences": "ลำดับเบ็ดเตล็ด",
461 "Inserted before the first Assistant's message.": "แทรกก่อนข้อความแรกของผู้ช่วย",
462 "First Assistant Prefix": "คำนำหน้าผู้ช่วยแรก",
463 "instruct_last_output_sequence": "ลำดับผลลัพธ์สุดท้ายของคำสั่ง",
464 "Last Assistant Prefix": "คำนำหน้าผู้ช่วยสุดท้าย",
465 "Will be inserted as a last prompt line when using system/neutral generation.": "จะถูกแทรกเป็นบรรทัดพรอมต์สุดท้ายเมื่อใช้การสร้างแบบระบบ/เป็นกลาง",
466 "System Instruction Prefix": "คำนำหน้าคำสั่งระบบ",
467 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "หากมีลำดับหยุดถูกสร้างขึ้น ทุกอย่างหลังจากนั้นจะถูกลบออกจากผลลัพธ์ (รวมลำดับหยุดด้วย)",
468 "Stop Sequence": "ลำดับหยุด",
469 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "จะถูกแทรกที่ต้นประวัติการแชทหากไม่เริ่มต้นด้วยข้อความของผู้ใช้",
470 "User Filler Message": "ข้อความเติมของผู้ใช้",
471 "Context Formatting": "การจัดรูปแบบ Context",
472 "(Saved to Context Template)": "(บันทึกไปยัง Context Template)",
473 "Always add character's name to prompt": "เพิ่มชื่อตัวละครลงในพรอมต์เสมอ",
474 "Generate only one line per request": "สร้างเพียงหนึ่งบรรทัดต่อคำขอ",
475 "Trim Incomplete Sentences": "ตัดประโยคที่ไม่สมบูรณ์",
476 "Include Newline": "รวมบรรทัดใหม่",
477 "Misc. Settings": "การตั้งค่าเบ็ดเตล็ด",
478 "Collapse Consecutive Newlines": "ยุบบรรทัดใหม่ที่ติดต่อกัน",
479 "Trim spaces": "ตัดช่องว่าง",
480 "Tokenizer": "ตัวแบ่งโทเค็น",
481 "Token Padding": "การเพิ่มโทเค็น",
482 "Start Reply With": "เริ่มตอบด้วย",
483 "AI reply prefix": "คำนำหน้าการตอบของ AI",
484 "Show reply prefix in chat": "แสดงคำนำหน้าการตอบในแชท",
485 "Non-markdown strings": "Non-markdown strings",
486 "separate with commas w/o space between": "แยกด้วยเครื่องหมายจุลภาคโดยไม่มีช่องว่าง",
487 "Custom Stopping Strings": "สตริงหยุดกำหนดเอง",
488 "JSON serialized array of strings": "อาร์เรย์ของสตริงในรูปแบบ JSON",
489 "Replace Macro in Stop Strings": "แทนที่แมโครในสตริงหยุด",
490 "Auto-Continue": "ต่อบทอัตโนมัติ",
491 "Allow for Chat Completion APIs": "อนุญาตสำหรับ Chat Completion APIs",
492 "Target length (tokens)": "ความยาวเป้าหมาย (โทเค็น)",
493 "World Info": "ข้อมูลโลก (World info)",
494 "Locked = World Editor will stay open": "Locked = สามารถแก้ไขข้อมูลโลกได้เสมอ",
495 "Worlds/Lorebooks": "ข้อมูลโลก/Lorebooks",
496 "Active World(s) for all chats": "ข้อมูลโลกที่ถูกเปิดใช้งานทุกแชท",
497 "-- World Info not found --": "-- ไม่พบข้อมูลโลก --",
498 "Global World Info/Lorebook activation settings": "ตั้งค่าการใช้งานข้อมูลโลก/Lorebook",
499 "Click to expand": "กดเพื่อแสดงข้อมูล",
500 "Scan Depth": "Scan Depth",
501 "Context %": "Context %",
502 "Budget Cap": "Budget Cap",
503 "(0 = disabled)": "(0 = ปิดใช้งาน)",
504 "Scan chronologically until reached min entries or token budget.": "สแกนตามลำดับเวลาจนกว่าจะถึงรายการขั้นต่ำหรืองบโทเค็น",
505 "Min Activations": "การเปิดใช้งานขั้นต่ำ",
506 "Max Depth": "ความลึกสูงสุด",
507 "(0 = unlimited, use budget)": "(0 = ไม่จำกัด ใช้งบประมาณ)",
508 "Insertion Strategy": "กลยุทธ์การแทรก",
509 "Sorted Evenly": "เรียงลำดับเท่าๆ กัน",
510 "Create a new World Info": "สร้าง Lorebook ใหม่",
511 "Character Lore First": "เลือกใช้เนื้อหาตัวละครก่อน",
512 "Global Lore First": "เลือกใช้เนื้อหาหลักของโลกก่อน",
513 "Entries can activate other entries by mentioning their keywords": "สามารถเรียกใช้รายการเนื้อหาที่เกี่ยวข้องกันด้วย Keywords",
514 "Recursive Scan": "Recursive Scan",
515 "Lookup for the entry keys in the context will respect the case": "Lookup for the entry keys in the context will respect the case",
516 "Case Sensitive": "แยกตัวพิมพ์ใหญ่-เล็ก",
517 "If the entry key consists of only one word, it would not be matched as part of other words": "If the entry key consists of only one word, it would not be matched as part of other words",
518 "Match Whole Words": "ตรงกับคำทั้งหมด",
519 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "จะเลือกเนื้อหาที่มี keyword ตรงกันมากที่สุดเท่านั้น",
520 "Use Group Scoring": "Use Group Scoring",
521 "Alert if your world info is greater than the allocated budget.": "Alert if your world info is greater than the allocated budget.",
522 "Alert On Overflow": "Alert On Overflow",
523 "New": "ใหม่",
524 "or": "หรือ",
525 "--- Pick to Edit ---": "กดเพื่อแก้ไข",
526 "Rename World Info": "แก้ไขชื่อของข้อมูลโลก",
527 "Open all Entries": "แสดงเนื้อหาทั้งหมด",
528 "Close all Entries": "ปิดการแสดงเนื้อหาทั้งหมด",
529 "New Entry": "สร้างเนื้อหาใหม่",
530 "Fill empty Memo/Titles with Keywords": "สร้าง Keywords แทน Memo/ชื่อเรื่องที่ว่าง",
531 "Import World Info": "นำเข้าข้อมูลโลก/Lorebook",
532 "Export World Info": "ส่งออกข้อมูลโลก/Lorebook",
533 "Duplicate World Info": "ทำสำเนา ข้อมูลโลก/Lorebook",
534 "Delete World Info": "Delete World Info",
535 "Search...": "ค้นหา...",
536 "Search": "ค้นหา",
537 "Priority": "ลำดับความสำคัญ",
538 "Custom": "ปรับแต่ง",
539 "Title A-Z": "ชื่อเรียงจาก A-Z",
540 "Title Z-A": "ชื่อเรียงจาก Z-A",
541 "Tokens ↗": "Tokens ↗",
542 "Tokens ↘": "Tokens ↘",
543 "Depth ↗": "Depth ↗",
544 "Depth ↘": "Depth ↘",
545 "Order ↗": "Order ↗",
546 "Order ↘": "Order ↘",
547 "UID ↗": "UID ↗",
548 "UID ↘": "UID ↘",
549 "Trigger% ↗": "Trigger% ↗",
550 "Trigger% ↘": "Trigger% ↘",
551 "Refresh": "รีเฟรช",
552 "User Settings": "ตั้งค่าส่วนผู้ใช้งาน",
553 "Simple": "Simple",
554 "Advanced": "ขั้นสูง",
555 "UI Language": "ภาษา UI",
556 "Account": "บัญชีผู้ใช้งาน",
557 "Admin Panel": "แผงควบคุมผู้ดูแล",
558 "Logout": "ออกจากระบบ",
559 "Search Settings": "ค้นหาการตั้งค่า",
560 "UI Theme": "ธีม UI",
561 "Import a theme file": "นำเข้าไฟล์ธีม",
562 "Export a theme file": "ส่งออกไฟล์ธีม",
563 "Delete a theme": "ลบธีม",
564 "Update a theme file": "อัพเดทไฟล์ธีม",
565 "Save as a new theme": "บันทึกเป็นธีมใหม่",
566 "Avatar Style:": "รูปแบบภาพ Avatar:",
567 "Circle": "วงกลม",
568 "Square": "สี่เหลี่ยมผืนผ้า",
569 "Rectangle": "ทรงเหลี่ยม",
570 "Chat Style:": "รูปแบบหน้าต่างแชท:",
571 "Flat": "ขอบเรียบ",
572 "Bubbles": "บับเบิล",
573 "Document": "เอกสาร",
574 "Specify colors for your theme.": "เลือกเฉดสีสำหรับธีมของคุณ",
575 "Theme Colors": "สีธีม",
576 "Main Text": "ข้อความหลัก",
577 "Italics Text": "อักษรตัวเอียง",
578 "Underlined Text": "อักษรขีดเส้นใต้",
579 "Quote Text": "ข้อความคำพูด",
580 "Shadow Color": "เงาตัวอักษร",
581 "Chat Background": "สีพื้นหลังแชท",
582 "UI Background": "พื้นหลัง UI",
583 "UI Border": "เส้นขอบ UI",
584 "User Message Blur Tint": "สีข้อความ User",
585 "AI Message Blur Tint": "สีข้อความ AI",
586 "Chat Width": "ความกว้างแชท",
587 "Width of the main chat window in % of screen width": "ความกว้างของแชทเป็น % ของหน้าจอ",
588 "Font Scale": "ขนาดตัวอักษร",
589 "Font size": "ขนาดของตัวอักษร",
590 "Blur Strength": "ระดับความเบลอภาพพื้นหลัง",
591 "Blur strength on UI panels.": "ระดับความเบลอแผง UI",
592 "Text Shadow Width": "ขนาดของเงาอักษร",
593 "Strength of the text shadows": "ความหนาของเงาอักษร",
594 "Disables animations and transitions": "ปิดแอนิเมชั่นและการเปลี่ยนภาพ",
595 "Reduced Motion": "ลดการเคลื่อนไหว",
596 "removes blur from window backgrounds": "ลบความเบลอภาพพื้นหลัง",
597 "No Blur Effect": "ไม่มีเอฟเฟกต์เบลอ",
598 "Remove text shadow effect": "ลบเงาตัวอักษร",
599 "No Text Shadows": "ไม่มีเงาข้อความ",
600 "Reduce chat height, and put a static sprite behind the chat window": "ลดความสูงของหน้าต่างแชทและวาง sprite ตัวละครไว้ด้านหลังหน้าต่างแชท",
601 "Waifu Mode": "โหมด Waifu",
602 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "แสดงบริบทการบรรยายข้อความทั้งหมดเสมอโดยไม่ใส่ไว้หลัง '....'",
603 "Auto-Expand Message Actions": "ขยายตัวอักษรอัตโนมัติ",
604 "Alternative UI for numeric sampling parameters with fewer steps": "Alternative UI for numeric sampling parameters with fewer steps",
605 "Zen Sliders": "Zen Sliders",
606 "Entirely unrestrict all numeric sampling parameters": "Entirely unrestrict all numeric sampling parameters",
607 "Mad Lab Mode": "Mad Lab Mode",
608 "Time the AI's message generation, and show the duration in the chat log": "แสดงเวลาที่ใช้ในการสร้างข้อความของ AI, และให้แสดงจำนวนในประวัติแชท",
609 "Message Timer": "แสดงเวลาที่ AI ใช้ในการตอบข้อความ",
610 "Show a timestamp for each message in the chat log": "แสดงเวลาที่ส่งข้อความตอบกลับในประวัติการแชท",
611 "Chat Timestamps": "แสดงเวลาที่ส่งข้อความตอบกลับในแชท",
612 "Show an icon for the API that generated the message": "แสดงไอคอนสำหรับ API ที่สร้างข้อความ",
613 "Model Icon": "แสดงไอคอน Model AI ที่ตอบแชท",
614 "Show sequential message numbers in the chat log": "แสดงหมายเลขลำดับข้อความในประวัติแชท",
615 "Message IDs": "แสดงเลขจำนวนข้อความ",
616 "Hide avatars in chat messages.": "ซ่อนภาพ avatar ในแชทข้อความ",
617 "Hide Chat Avatars": "ซ่อนรูปโปรไฟล์แชท",
618 "Show the number of tokens in each message in the chat log": "แสดงจำนวนโทเค็นในแต่ละข้อความในบันทึกแชท",
619 "Show Message Token Count": "แสดงจำนวนโทเค็นที่ Ai ตอบแชท",
620 "Single-row message input area. Mobile only, no effect on PC": "พื้นที่ป้อนข้อความแบบแถวเดียวไม่ขึ้นบรรทัดใหม่ เฉพาะในมือถือเท่านั้น",
621 "Compact Input Area (Mobile)": "พื้นที่ป้อนข้อความขนาดกระทัดรัด (มือถือ)",
622 "In the Character Management panel, show quick selection buttons for favorited characters": "แสดงปุ่มลัดบอทตัวละครโปรดในแถบเลือกตัวละคร",
623 "Characters Hotswap": "การเปลี่ยนบอทตัวละครแบบรวดเร็ว",
624 "Enable magnification for zoomed avatar display.": "อนุญาตการซูมภาพโปรไฟล์อวาตาร์",
625 "Avatar Hover Magnification": "Avatar Hover Magnification",
626 "Enables a magnification effect on hover when you display the zoomed avatar after clicking an avatar's image in chat.": "เปิดเอฟเฟกต์ Magnification เมื่อคลิกรูปตัวละครค้างไว้ หลังจากคลิกดูรูปตัวละครแบบขยายในแชท",
627 "Show tagged character folders in the character list": "แสดงโฟลเดอร์ตัวละครตามแท็กในรายการ",
628 "Tags as Folders": "Tags as Folders",
629 "Tags_as_Folders_desc": "Tags_as_Folders_desc",
630 "Character Handling": "การจัดการตัวละคร",
631 "If set in the advanced character definitions, this field will be displayed in the characters list.": "หากตั้งค่าในคำจำกัดความตัวละครขั้นสูง ฟิลด์นี้จะแสดงในรายการตัวละคร",
632 "Char List Subheader": "หัวข้อย่อยรายการตัวละคร",
633 "Character Version": "เวอร์ชั่นของตัวละคร",
634 "Created by": "สร้างโดย",
635 "Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "ใช้การจับคู่แบบเลือน และค้นหาตัวละครในรายการด้วยฟิลด์ข้อมูลทั้งหมด ไม่ใช่แค่ชื่อ",
636 "Advanced Character Search": "การค้นหาตัวละครขั้นสูง",
637 "If checked and the character card contains a prompt override (System Prompt), use that instead": "ใช้ System Prompt ของการ์ดตัวละคร (ถ้ามี)",
638 "Prefer Character Card Prompt": "ใช้ Prompt ของการ์ดตัวละคร (ถ้ามี)",
639 "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "หากเลือกและการ์ดตัวละครมี jailbreak override (คำแนะนำหลังประวัติ) ให้ใช้แทน",
640 "Prefer Character Card Jailbreak": "ใช้ Jailbreak ของการ์ดตัวละคร (ถ้ามี)",
641 "never_resize_avatars_tooltip": "คำแนะนำไม่ปรับขนาดรูปตัวแทน",
642 "Never resize avatars": "ห้ามปรับขนาดรูปตัวแทน",
643 "Show actual file names on the disk, in the characters list display only": "แสดงชื่อไฟล์จริงในดิสก์ แสดงเฉพาะในรายการตัวละคร",
644 "Show avatar filenames": "แสดงชื่อไฟล์ของรูปตัวแทน",
645 "Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored": "แจ้งให้นำเข้าแท็กที่ฝังในการ์ดเมื่อนำเข้าตัวละคร มิฉะนั้นแท็กที่ฝังจะถูกเพิกเฉย",
646 "Import Card Tags": "นำเข้าการ์ดแท็ก",
647 "Hide character definitions from the editor panel behind a spoiler button": "ซ่อนคำจำกัดความตัวละครจากแผงตัวแก้ไขด้วยปุ่มสปอยเลอร์",
648 "Spoiler Free Mode": "โหมดปลอดสปอยเลอร์",
649 "Miscellaneous": "Miscellaneous",
650 "Reload and redraw the currently open chat": "โหลดและวาดแชทที่เปิดอยู่ใหม่",
651 "Reload Chat": "รีโหลดแชท",
652 "Debug Menu": "เมนูดีบัก",
653 "Smooth Streaming": "การสตรีมแบบนุ่มนวล",
654 "Experimental feature. May not work for all backends.": "ฟีเจอร์ทดลอง อาจไม่ทำงานกับแบ็กเอนด์ทั้งหมด",
655 "Slow": "ช้า",
656 "Fast": "เร็ว",
657 "Play a sound when a message generation finishes": "เล่นเสียงเมื่อการสร้างข้อความเสร็จสิ้น",
658 "Message Sound": "เสียงข้อความ",
659 "Only play a sound when ST's browser tab is unfocused": "เล่นเสียงเฉพาะเมื่อแท็บเบราว์เซอร์ของ ST ไม่ได้โฟกัส",
660 "Background Sound Only": "เฉพาะเสียงของพื้นหลังเท่านั้น",
661 "Reduce the formatting requirements on API URLs": "ลดข้อกำหนดการจัดรูปแบบสำหรับ URL ของ API",
662 "Relaxed API URLS": "URL API แบบผ่อนปรน",
663 "Ask to import the World Info/Lorebook for every new character with embedded lorebook. If unchecked, a brief message will be shown instead": "ถามเพื่อนำเข้า World Info/Lorebook สำหรับตัวละครใหม่ทุกตัวที่มี lorebook ฝังอยู่ หากไม่เลือก จะแสดงข้อความสั้นๆ แทน",
664 "Lorebook Import Dialog": "กล่องโต้ตอบการนำเข้า Lorebook",
665 "Restore unsaved user input on page refresh": "คืนค่าข้อมูลผู้ใช้ที่ไม่ได้บันทึกเมื่อรีเฟรชหน้า",
666 "Restore User Input": "คืนค่าข้อมูลผู้ใช้",
667 "Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "อนุญาตให้จัดตำแหน่งองค์ประกอบ UI บางส่วนด้วยการลาก เฉพาะ PC ไม่มีผลกับมือถือ",
668 "Movable UI Panels": "แผง UI ที่เคลื่อนย้ายได้",
669 "MovingUI preset. Predefined/saved draggable positions": "พรีเซ็ต MovingUI ตำแหน่งลากที่กำหนดไว้ล่วงหน้า/บันทึกไว้",
670 "MUI Preset": "พรีเซ็ต MUI",
671 "Save movingUI changes to a new file": "บันทึกการเปลี่ยนแปลง movingUI เป็นไฟล์ใหม่",
672 "Reset MovingUI panel sizes/locations.": "รีเซ็ตขนาด/ตำแหน่งแผง MovingUI",
673 "Apply a custom CSS style to all of the ST GUI": "ใช้สไตล์ CSS แบบกำหนดเองกับ GUI ของ ST ทั้งหมด",
674 "Custom CSS": "CSS แบบกำหนดเอง",
675 "Expand the editor": "ขยายตัวแก้ไข",
676 "Chat/Message Handling": "การจัดการแชท/ข้อความ",
677 "# Messages to Load": "จำนวนข้อความที่โหลด",
678 "The number of chat history messages to load before pagination.": "จำนวนข้อความในประวัติแชทที่จะโหลดก่อนแบ่งหน้า",
679 "(0 = All)": "(0 = ทั้งหมด)",
680 "Streaming FPS": "อัตราเฟรมการสตรีม",
681 "Update speed of streamed text.": "ความเร็วการอัปเดตข้อความที่สตรีม",
682 "Example Messages Behavior": "พฤติกรรมข้อความตัวอย่าง",
683 "Gradual push-out": "ผลักออกทีละน้อย",
684 "Always include examples": "รวมตัวอย่างเสมอ",
685 "Never include examples": "ไม่รวมตัวอย่าง",
686 "Send on Enter": "ส่งเมื่อกด Enter",
687 "Disabled": "ปิดใช้งาน",
688 "Automatic (PC)": "อัตโนมัติ (PC)",
689 "Press Send to continue": "กด Send เพื่อดำเนินต่อ",
690 "Show a button in the input area to ask the AI to continue (extend) its last message": "แสดงปุ่มในพื้นที่ป้อนข้อมูลเพื่อขอให้ AI ดำเนินต่อ (ขยาย) ข้อความสุดท้าย",
691 "Quick 'Continue' button": "ปุ่ม 'ดำเนินการต่อ' ด่วน",
692 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "แสดงปุ่มลูกศรในข้อความสุดท้ายในแชทเพื่อสร้างการตอบกลับ AI ทางเลือก ทั้ง PC และมือถือ",
693 "Swipes": "ปัด",
694 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "อนุญาตให้ใช้ท่าทางปัดในข้อความสุดท้ายในแชทเพื่อเรียกใช้การสร้างแบบปัด เฉพาะมือถือ ไม่มีผลกับ PC",
695 "Gestures": "ท่าทาง",
696 "Auto-load Last Chat": "โหลดแชทล่าสุดอัตโนมัติ",
697 "Auto-scroll Chat": "เลื่อนแชทอัตโนมัติ",
698 "Save edits to messages without confirmation as you type": "บันทึกการแก้ไขข้อความโดยอัตโนมัติขณะพิมพ์โดยไม่ต้องยืนยัน",
699 "Auto-save Message Edits": "บันทึกการแก้ไขข้อความอัตโนมัติ",
700 "Confirm message deletion": "ยืนยันการลบข้อความ",
701 "Auto-fix Markdown": "แก้ไข Markdown อัตโนมัติ",
702 "Disallow embedded media from other domains in chat messages": "ไม่อนุญาตสื่อที่ฝังจากโดเมนอื่นในข้อความแชท",
703 "Forbid External Media": "ห้ามสื่อภายนอก",
704 "Allow {{char}}: in bot messages": "อนุญาตให้ใช้ {{char}}: ในข้อความของบอท",
705 "Allow {{user}}: in bot messages": "อนุญาตให้ใช้ {{user}}: ในข้อความของบอท",
706 "Skip encoding and characters in message text, allowing a subset of HTML markup as well as Markdown": "ข้ามการเข้ารหัสตัวอักษร < และ > ในข้อความ อนุญาต HTML markup บางส่วนรวมถึง Markdown",
707 "Show tags in responses": "แสดง HTML tag ในข้อความ",
708 "Allow AI messages in groups to contain lines spoken by other group members": "อนุญาตให้ข้อความ AI ในกลุ่มมีบรรทัดที่พูดโดยสมาชิกกลุ่มอื่น",
709 "Relax message trim in Groups": "ผ่อนปรนการตัดข้อความในกลุ่ม",
710 "Log prompts to console": "บันทึกพรอมต์ไปยังคอนโซล",
711 "Requests logprobs from the API for the Token Probabilities feature": "ขอ logprobs จาก API สำหรับฟีเจอร์ Token Probabilities",
712 "Request token probabilities": "ขอความน่าจะเป็นของโทเค็น",
713 "Automatically reject and re-generate AI message based on configurable criteria": "ปฏิเสธและสร้างข้อความ AI ใหม่โดยอัตโนมัติตามเกณฑ์ที่กำหนดได้",
714 "Auto-swipe": "ปัดแบบอัตโนมัติ",
715 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "เปิดการใช้งานฟังก์ชั่นปัดแบบอัตโนมัติ การตั้งค่าในส่วนนี้มีผลก็ต่อเมื่อเปิดใช้งานฟังก์ชั่นปัดแบบอัตโนมัติแล้วเท่านั้น",
716 "Minimum generated message length": "ความยาวข้อความที่สร้างขั้นต่ำ",
717 "If the generated message is shorter than these many characters, trigger an auto-swipe": "หากข้อความที่สร้างสั้นกว่าจำนวนตัวอักษรนี้ จะเรียกใช้การปัดอัตโนมัติ",
718 "Blacklisted words": "คำต้องห้าม",
719 "words you dont want generated separated by comma ','": "คำที่คุณไม่ต้องการให้สร้าง คั่นด้วยเครื่องหมายจุลภาค ','",
720 "Blacklisted word count to swipe": "จำนวนคำต้องห้ามเพื่อปัด",
721 "Minimum number of blacklisted words detected to trigger an auto-swipe": "จำนวนขั้นต่ำของคำต้องห้ามที่ตรวจพบเพื่อเรียกใช้การปัดอัตโนมัติ",
722 "AutoComplete Settings": "การตั้งค่าการเติมข้อความอัตโนมัติ",
723 "Automatically hide details": "ซ่อนรายละเอียดอัตโนมัติ",
724 "Determines how entries are found for autocomplete.": "กำหนดวิธีการค้นหารายการสำหรับการเติมข้อความอัตโนมัติ",
725 "Autocomplete Matching": "การจับคู่การเติมข้อความอัตโนมัติ",
726 "Starts with": "เริ่มต้นด้วย",
727 "Includes": "รวมถึง",
728 "Fuzzy": "แบบเลือน",
729 "Sets the style of the autocomplete.": "ตั้งค่าสไตล์ของการเติมข้อความอัตโนมัติ",
730 "Autocomplete Style": "สไตล์การเติมข้อความอัตโนมัติ",
731 "Follow Theme": "ตามธีม",
732 "Dark": "มืด",
733 "Sets the font size of the autocomplete.": "ตั้งค่าขนาดตัวอักษรของการเติมข้อความอัตโนมัติ",
734 "Sets the width of the autocomplete.": "ตั้งค่าความกว้างของการเติมข้อความอัตโนมัติ",
735 "Autocomplete Width": "ความกว้างการเติมข้อความอัตโนมัติ",
736 "chat input box": "กล่องป้อนข้อความแชท",
737 "entire chat width": "ความกว้างแชททั้งหมด",
738 "full window width": "ความกว้างหน้าต่างเต็ม",
739 "STscript Settings": "การตั้งค่า STscript",
740 "Sets default flags for the STscript parser.": "ตั้งค่าแฟล็กเริ่มต้นสำหรับตัวแยกวิเคราะห์ STscript",
741 "Parser Flags": "แฟล็กตัวแยกวิเคราะห์",
742 "Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "เปลี่ยนเป็นการ escape ที่เข้มงวดกว่า อนุญาตให้ตัวอักษรคั่นทั้งหมดถูก escape ด้วยแบ็กสแลช และแบ็กสแลชก็สามารถ escape ได้เช่นกัน",
743 "STRICT_ESCAPING": "STRICT_ESCAPING",
744 "Replace all {{getvar::}} and {{getglobalvar::}} macros with scoped variables to avoid double macro substitution.": "แทนที่แมโคร {{getvar::}} และ {{getglobalvar::}} ทั้งหมดด้วยตัวแปรขอบเขตเพื่อหลีกเลี่ยงการแทนที่แมโครซ้ำ",
745 "REPLACE_GETVAR": "REPLACE_GETVAR",
746 "Change Background Image": "เปลี่ยนรูปภาพพื้นหลัง",
747 "Filter": "ตัวกรอง",
748 "Automatically select a background based on the chat context": "เลือกพื้นหลังโดยอัตโนมัติตามบริบทของแชท",
749 "Auto-select": "เลือกอัตโนมัติ",
750 "System Backgrounds": "พื้นหลังของระบบ",
751 "Chat Backgrounds": "พื้นหลังแชท",
752 "bg_chat_hint_1": "พื้นหลังของแชทที่ถูกสร้างด้วย",
753 "bg_chat_hint_2": "จะปรากฏที่นี่",
754 "Extensions": "โปรแกรมส่วนขยาย",
755 "Notify on extension updates": "แจ้งเตือนเมื่อมีการอัปเดตส่วนขยาย",
756 "Manage extensions": "จัดการส่วนขยาย",
757 "Import Extension From Git Repo": "นำเข้าส่วนขยายจาก Git Repo",
758 "Install extension": "ติดตั้งส่วนขยาย",
759 "Extras API:": "Extras API:",
760 "Auto-connect": "เชื่อมต่ออัตโนมัติ",
761 "Extras API URL": "Extras API URL",
762 "Extras API key (optional)": "คีย์ Extras API (ไม่บังคับ)",
763 "Persona Management": "การจัดการ Persona",
764 "How do I use this?": "ฉันจะใช้สิ่งนี้ยังไง?",
765 "Click for stats!": "คลิกเพื่อดูสถิติ!",
766 "Usage Stats": "ค่าสถิติการใช้งาน",
767 "Backup your personas to a file": "สำรองข้อมูล persona ของคุณเป็นไฟล์",
768 "Backup": "สำรองข้อมูล",
769 "Restore your personas from a file": "คืนค่า persona ของคุณจากไฟล์",
770 "Restore": "คืนค่า",
771 "Create a dummy persona": "สร้าง persona จำลอง",
772 "Create": "สร้าง",
773 "Toggle grid view": "เปลี่ยนการมองแบบตาราง",
774 "No persona description": "ไม่มีข้อมูล Persona",
775 "Name": "ชื่อ",
776 "Enter your name": "กรอกชื่อของคุณ",
777 "Click to set a new User Name": "คลิกเลือกเพื่อตั้งชื่อผู้ใช้ใหม่",
778 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "กดเพื่อล็อก Persona ที่คุณเลือกไว้กับแชทปัจจุบัน หรือให้กดอีกครั้งเพื่อปลดล็อก",
779 "Click to set user name for all messages": "คลิกเพื่อตั้งชื่อผู้ใช้สำหรับข้อความทั้งหมด",
780 "Persona Description": "ข้อมูล Persona",
781 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "ตัวอย่าง: [{{user}} เป็นสาวสวยน่ารักอายุ 25 ปี]",
782 "Tokens persona description": "คำอธิบาย persona ในโทเค็น",
783 "Position:": "ตำแหน่ง:",
784 "In Story String / Prompt Manager": "ในสตริงเรื่องราว / ตัวจัดการพรอมต์",
785 "Top of Author's Note": "ด้านบนของหมายเหตุผู้เขียน",
786 "Bottom of Author's Note": "ด้านล่างของหมายเหตุผู้เขียน",
787 "In-chat @ Depth": "ในแชท @ ความลึก",
788 "Depth:": "ความลึก:",
789 "Role:": "บทบาท:",
790 "System": "ระบบ",
791 "User": "ผู้ใช้",
792 "Assistant": "ผู้ช่วย",
793 "Show notifications on switching personas": "แสดงการแจ้งเตือนเมื่อเปลี่ยน persona",
794 "Allow multiple persona connections per character": "อนุญาตการเชื่อมต่อหลาย persona ต่อ 1 ตัวละคร",
795 "Auto-lock a chosen persona to the chat": "ล็อก persona ที่เลือกไว้กับแชทโดยอัตโนมัติ",
796 "Character Management": "จัดการตัวละคร",
797 "Locked = Character Management panel will stay open": "ล็อค = แผงการจัดการตัวละครจะเปิดค้างไว้",
798 "Select/Create Characters": "เลือก/สร้างตัวละคร",
799 "Favorite characters to add them to HotSwaps": "ใส่ตัวละครโปรดเพื่อเพิ่มลงใน HotSwaps",
800 "Token counts may be inaccurate and provided just for reference.": "จำนวนโทเค็นอาจไม่แม่นยำและให้ไว้เพื่ออ้างอิงเท่านั้น",
801 "Total tokens": "จำนวนโทเค็นทั้งหมด",
802 "Calculating...": "กำลังคำนวณผล...",
803 "Tokens": "จำนวนโทเค็น",
804 "Permanent tokens": "โทเค็นถาวร",
805 "Permanent": "ถาวร",
806 "About Token 'Limits'": "เกี่ยวกับ 'ขีดจำกัด' โทเค็น",
807 "Toggle character info panel": "เปิด/ปิดแผงข้อมูลตัวละคร",
808 "Name this character": "ชื่อของตัวละครนี้",
809 "extension_token_counter": "ตัวนับโทเค็นส่วนขยาย",
810 "Click to select a new avatar for this character": "กดเพื่อเลือกรูปตัวละครรูปใหม่",
811 "Add to Favorites": "เพิ่มไปยังรายการโปรด",
812 "Advanced Definition": "การตั้งค่าเพิ่มเติม",
813 "Character Lore": "Character Lore",
814 "Chat Lore": "Chat Lore",
815 "Export and Download": "นำออกและดาวน์โหลดการ์ดตัวละคร",
816 "Duplicate Character": "ทำสำเนาการ์ดตัวละครนั้นๆ",
817 "Create Character": "สร้างตัวละคร",
818 "Delete Character": "ลบตัวละคร",
819 "More...": "เพิ่มเติม",
820 "Link to World Info": "ลิงค์ตัวละครกับ Lorebook",
821 "Import Card Lore": "นำเข้า Lorebook ของการ์ดตัวละคร",
822 "Scenario Override": "เขียนทับ Scenario ของการ์ด",
823 "Convert to Persona": "แปลงการ์ดตัวละครไปเป็น Persona",
824 "Rename": "เปลี่ยนชื่อ",
825 "Link to Source": "ลิงค์ไปยัง Source",
826 "Replace / Update": "Replace / Update",
827 "Import Tags": "นำเข้าแท็ก",
828 "Search / Create Tags": "ค้นหา / สร้างแท็ก",
829 "View all tags": "ดูแท็กทั้งหมด",
830 "Creator's Notes": "ข้อความของผู้สร้าง",
831 "Show / Hide Description and First Message": "แสดง / ซ่อนคำอธิบายและข้อความแรก",
832 "Character Description": "คำอธิบายตัวละคร",
833 "Click to allow/forbid the use of external media for this character.": "คลิกเพื่ออนุญาตหรือห้ามการใช้สื่อภายนอกสำหรับตัวละครนี้",
834 "Ext. Media": "สื่อภายนอก",
835 "Describe your character's physical and mental traits here.": "กรอกรายละเอียดเกี่ยวกับรูปลักษณ์และบุคลิกของตัวละครของคุณที่นี่",
836 "First message": "ข้อความเริ่มต้น",
837 "Click to set additional greeting messages": "คลิกเพื่อกำหนดข้อความทักทายเพิ่มเติม",
838 "Alt. Greetings": "ข้อความทักทายแบบอื่น",
839 "This will be the first message from the character that starts every chat.": "ตัวละครจะเริ่มแชททุกครั้งด้วยข้อความนี้",
840 "Group Controls": "การควบคุมกลุ่ม",
841 "Chat Name (Optional)": "ชื่อแชท (ไม่บังคับ)",
842 "Click to select a new avatar for this group": "เลือกรูปภาพใหม่สำหรับกลุ่มนี้",
843 "Group reply strategy": "กลยุทธ์การตอบกลับของกลุ่ม",
844 "Natural order": "ลำดับธรรมชาติ",
845 "List order": "ลำดับรายการ",
846 "Group generation handling mode": "โหมดการจัดการการสร้างกลุ่ม",
847 "Swap character cards": "สลับการ์ดตัวละคร",
848 "Join character cards (exclude muted)": "รวมการ์ดตัวละคร (ยกเว้นที่ปิดเสียง)",
849 "Join character cards (include muted)": "รวมการ์ดตัวละคร (รวมที่ปิดเสียง)",
850 "Inserted before each part of the joined fields.": "แทรกก่อนแต่ละส่วนของฟิลด์ที่เชื่อมต่อ",
851 "Join Prefix": "คำนำหน้าการเชื่อมต่อ",
852 "When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> with the name of the part (e.g.: description, personality, scenario, etc.)": "เมื่อเลือก 'เชื่อมต่อการ์ดตัวละคร' ฟิลด์ทั้งหมดของตัวละครจะถูกรวมเข้าด้วยกัน หมายความว่าในสตริงเรื่องราว คำอธิบายตัวละครทั้งหมดจะถูกรวมเป็นข้อความใหญ่หนึ่งข้อความ หากคุณต้องการแยกฟิลด์เหล่านั้น คุณสามารถกำหนดคำนำหน้าหรือคำต่อท้ายได้ที่นี่ ค่านี้รองรับแมโครปกติและจะแทนที่ {{char}} ด้วยชื่อตัวละครที่เกี่ยวข้องและ <FIELDNAME> ด้วยชื่อของส่วน (เช่น: description, personality, scenario, ฯลฯ)",
853 "Inserted after each part of the joined fields.": "แทรกหลังแต่ละส่วนของฟิลด์ที่เชื่อมต่อ",
854 "Join Suffix": "คำต่อท้ายการเชื่อมต่อ",
855 "Set a group chat scenario": "ตั้งค่าสถานการณ์แชทกลุ่ม",
856 "Click to allow/forbid the use of external media for this group.": "คลิกเพื่ออนุญาต/ห้ามการใช้สื่อภายนอกสำหรับกลุ่มนี้",
857 "Restore collage avatar": "คืนค่าอวาตาร์แบบคอลลาจ",
858 "Allow self responses": "อนุญาตการตอบกลับตนเอง",
859 "Auto Mode": "โหมดอัตโนมัติ",
860 "Auto Mode delay": "ความล่าช้าของโหมดอัตโนมัติ",
861 "Hide Muted Member Sprites": "ซ่อน Sprite ของสมาชิกที่ปิดเสียง",
862 "Current Members": "สมาชิกปัจจุบัน",
863 "Add Members": "เพิ่มสมาชิก",
864 "Create New Character": "สร้างตัวละครใหม่",
865 "Import Character from File": "นำเข้าตัวละครจากไฟล์",
866 "Import content from external URL": "นำเข้าเนื้อหาจาก URL ภายนอก",
867 "Create New Chat Group": "สร้างกลุ่มการสนทนาใหม่",
868 "Characters sorting order": "การเรียงลำดับตัวละคร",
869 "A-Z": "A-Z",
870 "Z-A": "Z-A",
871 "Newest": "ใหม่ที่สุด",
872 "Oldest": "เก่าที่สุด",
873 "Favorites": "รายการโปรด",
874 "Recent": "ล่าสุด",
875 "Most chats": "แชทมากที่สุด",
876 "Least chats": "แชทน้อยที่สุด",
877 "Most tokens": "โทเค่นมากที่สุด",
878 "Least tokens": "โทเค่นน้อยที่สุด",
879 "Random": "สุ่ม",
880 "Toggle character grid view": "เปิด/ปิด การดูตัวละครแบบตาราง",
881 "Bulk_edit_characters": "แก้ไขตัวละครทั้งหมดพร้อมกัน",
882 "Bulk select all characters": "เลือกตัวละครทั้งหมดพร้อมกัน",
883 "Bulk delete characters": "ลบตัวละครทั้งหมด",
884 "popup-button-save": "บันทึก",
885 "popup-button-yes": "ตกลง",
886 "popup-button-no": "ไม่",
887 "popup-button-cancel": "ยกเลิก",
888 "popup-button-import": "นำเข้า",
889 "Advanced Definitions": "คำจำกัดความขั้นสูง",
890 "Prompt Overrides": "การกำหนด prompt ใหม่",
891 "(For Chat Completion and Instruct Mode)": "(สำหรับโหมดสนทนาและโหมดคำสั่ง)",
892 "Insert {{original}} into either box to include the respective default prompt from system settings.": "ใส่ {{original}} ลงในกล่องข้อความใดก็ได้ เพื่อดึงข้อความพร้อมท์เริ่มต้นตามการตั้งค่าระบบมาใช้งาน",
893 "Main Prompt": "Main Prompt",
894 "Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "เนื้อหาใด ๆ ที่ใส่ที่นี่จะมาแทนที่ข้อความพร้อมท์เจลเบรกเริ่มต้นที่ใช้กับตัวละครนี้ (ตามสเปกเวอร์ชัน 2: prompt ของระบบ)",
895 "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "เนื้อหาใด ๆ ที่ใส่ที่นี่จะมาแทนที่ข้อความพร้อมท์เจลเบรกเริ่มต้นที่ใช้กับตัวละครนี้ (ตามสเปกเวอร์ชัน 2: คำแนะนำหลังประวัติการสนทนา)",
896 "Creator's Metadata (Not sent with the AI prompt)": "ข้อมูล Meta ของผู้สร้าง (จะไม่ถูกส่งพร้อมกับคำสั่ง AI)",
897 "Creator's Metadata": "ข้อมูล Meta ของผู้สร้าง",
898 "(Not sent with the AI Prompt)": "จะไม่ถูกส่งพร้อมกับ AI Prompt",
899 "Everything here is optional": "ทุกอย่างที่นี่ไม่บังคับ",
900 "(Botmaker's name / Contact Info)": "(ชื่อผู้สร้างบอท / ข้อมูลติดต่อ)",
901 "(If you want to track character versions)": "(ถ้าคุณต้องการติดตามเวอร์ชันของตัวละคร)",
902 "(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "อธิบายบอท,ให้คำแนะนำการใช้งานหรือระบุรุ่นของโมเดลแชทที่ได้ทดสอบมา ข้อมูลนี้จะแสดงในรายการตัวละคร",
903 "Tags to Embed": "แท็กที่จะใส่",
904 "(Write a comma-separated list of tags)": "(เขียนรายการแท็กโดยคั่นด้วยเครื่องหมายจุลภาค)",
905 "Personality summary": "ลักษณะนิสัย , ลักษณะบุคลิกภาพ",
906 "(A brief description of the personality)": "คำบรรยายสั้น ๆ ของลักษณะนิสัย",
907 "Scenario": "ฉาก , สถานการณ์ , เหตุการณ์",
908 "(Circumstances and context of the interaction)": "สถานการณ์และบริบทของการโต้ตอบ",
909 "Character's Note": "บันทึกของตัวละคร , หมายเหตุของตัวละคร",
910 "(Text to be inserted in-chat @ designated depth and role)": "(Text to be inserted in-chat @ designated depth and role)",
911 "@ Depth": "@ Depth",
912 "Role": "บทบาท (Role)",
913 "Talkativeness": "ความช่างพูด",
914 "How often the character speaks in group chats!": "ความถี่ในการตอบแชทกลุ่มของตัวละคร!",
915 "How often the character speaks in": "ตัวละครพูดบ่อยแค่ไหนใน",
916 "group chats!": "แชทกลุ่ม!",
917 "Shy": "ขี้อาย",
918 "Normal": "ปกติ",
919 "Chatty": "ช่างจ้อ",
920 "Examples of dialogue": "บทสนทนาตัวอย่าง",
921 "Important to set the character's writing style.": "\"การตั้งค่าสไตล์การพูดของตัวละครเป็นสิ่งสำคัญ\"",
922 "(Examples of chat dialog. Begin each example with START on a new line.)": "\"(ตัวอย่างบทสนทนาให้เริ่มต้นด้วย START ตามด้วยประโยคใหม่)\"",
923 "Save": "บันทึก",
924 "Chat History": "ประวัติการแชท",
925 "Import Chat": "นำเข้าไฟล์แชท",
926 "Copy to system backgrounds": "คัดลอกไปยังพื้นหลัง",
927 "Rename background": "เปลี่ยนชื่อพื้นหลัง",
928 "Lock": "ล็อค",
929 "Unlock": "ปลดล็อค",
930 "Delete background": "ลบพื้นหลัง",
931 "Chat Scenario Override": "แทนที่สถานการณ์ในแชท",
932 "Remove": "ลบ",
933 "Type here...": "\"พิมพ์ที่นี่...\"",
934 "Chat Lorebook": "แชท Lorebook",
935 "Chat Lorebook for": "แชท Lorebook สำหรับ",
936 "chat_world_template_txt": "chat_world_template_txt",
937 "Select a World Info file for": "เลือกไฟล์ข้อมูลโลกสำหรับ",
938 "Primary Lorebook": "Lorebook หลัก",
939 "A selected World Info will be bound to this character as its own Lorebook.": "\"ข้อมูลโลก/Lorebook ที่เลือกจะถูกผูกติดกับตัวละครเหมือนเป็นเนื้อหาของมัน\"",
940 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "เมื่อสร้างการตอบกลับ AI จะรวมกับรายการจากตัวเลือก World Info ทั่วไป",
941 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "เมื่อส่งออกบอทจะมี Lorebook ที่ฝังไว้ติดไปกับบอทตัวละครด้วยทุกครั้ง",
942 "Additional Lorebooks": "Lorebook เพิ่มเติม",
943 "Associate one or more auxillary Lorebooks with this character.": "เชื่อมโยงหนึ่งหรือหลาย Lorebook เสริมกับตัวละครนี้",
944 "NOTE: These choices are optional and won't be preserved on character export!": "หมายเหตุ: ตัวเลือกเหล่านี้ไว้สำหรับเป็นทางเลือกเฉพาะทางเท่านั้น และจะไม่ถูกผูกไปด้วยเมื่อนำการ์ดตัวละครออก!",
945 "Rename chat file": "เปลี่ยนชื่อไฟล์แชท",
946 "Export JSONL chat file": "ส่งออกไฟล์แชทในรูปแบบJSONL",
947 "Download chat as plain text document": "ดาวน์โหลดแชทเป็นไฟล์ข้อความธรรมดา",
948 "Delete chat file": "ลบแชทไฟล์",
949 "Use tag as folder": "ใช้แท็กเป็นโฟลเดอร์",
950 "Hide on character card": "ซ่อนในการ์ดตัวละคร",
951 "Delete tag": "ลบแท็ก",
952 "Entry Title/Memo": "ชื่อเรื่อง/บันทึก",
953 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled",
954 "WI_Entry_Status_Constant": "Constant",
955 "WI_Entry_Status_Normal": "Normal",
956 "WI_Entry_Status_Vectorized": "Vectorized",
957 "WI_Entry_Status_Disabled": "Disabled",
958 "T_Position": "T_Position",
959 "Before Char Defs": "Before Char Defs",
960 "After Char Defs": "After Char Defs",
961 "Before EM": "Before EM",
962 "After EM": "After EM",
963 "Before AN": "Before AN",
964 "After AN": "After AN",
965 "at Depth System": "at Depth System",
966 "at Depth User": "at Depth User",
967 "at Depth AI": "at Depth AI",
968 "Depth": "Depth",
969 "Order:": "Order:",
970 "Order": "Order",
971 "Trigger %:": "Trigger %:",
972 "Probability": "Probability",
973 "Duplicate world info entry": "Duplicate world info entry",
974 "Delete world info entry": "Delete world info entry",
975 "Comma separated (required)": "ต้องคั่นด้วยเครื่องหมายจุลภาค , (จำเป็นต้องกรอก)",
976 "Primary Keywords": "คีย์เวิร์ดหลัก",
977 "Keywords or Regexes": "Keywords or Regexes",
978 "Comma separated list": "คั่นด้วยเครื่องหมาย comma",
979 "Switch to plaintext mode": "Switch to plaintext mode",
980 "Logic": "Logic",
981 "AND ANY": "AND ANY",
982 "AND ALL": "AND ALL",
983 "NOT ALL": "NOT ALL",
984 "NOT ANY": "NOT ANY",
985 "(ignored if empty)": "จะไม่ถูกนำมาพิจารณาถ้าว่างเปล่า",
986 "Optional Filter": "Optional Filter",
987 "Keywords or Regexes (ignored if empty)": "คำสำคัญหรือรูปแบบการค้นหา (จะไม่ถูกนำมาพิจารณาถ้าว่างเปล่า)",
988 "Comma separated list (ignored if empty)": "Comma separated list (ignored if empty)",
989 "Use global setting": "ใช้การตั้งค่าทั่วไป",
990 "Case-Sensitive": "รวจสอบความแตกต่างของตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก",
991 "Yes": "ใช่",
992 "No": "ไม่",
993 "Can be used to automatically activate Quick Replies": "สามารถใช้เพื่อเปิดใช้งานการตอบกลับด่วนโดยอัตโนมัติ",
994 "Automation ID": "รหัสอัตโนมัติ",
995 "( None )": "( ไม่ระบุ )",
996 "Content": "เนื้อหา",
997 "Exclude from recursion": "ยกเว้นจากการทำซ้ำ",
998 "Prevent further recursion (this entry will not activate others)": "ป้องกันการเรียกซ้ำเพิ่มเติม (รายการนี้จะไม่ทำให้รายการอื่นทำงาน)",
999 "Delay until recursion (this entry can only be activated on recursive checking)": "หน่วงเวลาจนกว่าจะเกิดการเรียกซ้ำ (รายการนี้จะถูกเปิดใช้งานได้เฉพาะเมื่อตรวจสอบแบบเรียกซ้ำเท่านั้น)",
1000 "What this keyword should mean to the AI, sent verbatim": "เนื้อหาของ Entry นั้นๆ",
1001 "Filter to Character(s)": "กรองเฉพาะตัวละคร",
1002 "Character Exclusion": "Character Exclusion",
1003 "-- Characters not found --": "ไม่พบตัวละคร",
1004 "Inclusion Group": "Inclusion Group",
1005 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group",
1006 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.",
1007 "Only one entry with the same label will be activated": "Only one entry with the same label will be activated",
1008 "A relative likelihood of entry activation within the group": "A relative likelihood of entry activation within the group",
1009 "Group Weight": "Group Weight",
1010 "Selective": "Selective",
1011 "Click to Edit": "คลิกเพื่อแก้ไข",
1012 "Use Probability": "Use Probability",
1013 "Add Memo": "เพิ่ม Memo",
1014 "Delete the background?": "ลบพื้นหลังหรือไม่?",
1015 "Text or token ids": "Text or token ids",
1016 "close": "ปิด",
1017 "prompt_manager_edit": "แก้ไข",
1018 "prompt_manager_name": "ชื่อ",
1019 "A name for this prompt.": "A name for this prompt.",
1020 "To whom this message will be attributed.": "To whom this message will be attributed.",
1021 "AI Assistant": "AI ผู้ช่วย",
1022 "prompt_manager_position": "position",
1023 "Next to other prompts (relative) or in-chat (absolute).": "Next to other prompts (relative) or in-chat (absolute).",
1024 "prompt_manager_relative": "relative",
1025 "prompt_manager_depth": "depth",
1026 "0 = after the last message, 1 = before the last message, etc.": "\"0 = หลังข้อความสุดท้าย, 1 = ก่อนข้อความสุดท้าย, เป็นต้น\"",
1027 "Prompt": "Prompt",
1028 "The prompt to be sent.": "The prompt to be sent.",
1029 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "This prompt cannot be overridden by character cards, even if overrides are preferred.",
1030 "prompt_manager_forbid_overrides": "forbid overrides",
1031 "reset": "รีเซ็ต",
1032 "save": "บันทึก",
1033 "This message is invisible for the AI": "ข้อความนี้เอไอจะมองไม่เห็น",
1034 "Message Actions": "การดำเนินการกับข้อความ , การจัดการข้อความ",
1035 "Translate message": "แปลข้อความ",
1036 "Generate Image": "สร้างภาพ",
1037 "Narrate": "บรรยาย",
1038 "Exclude message from prompts": "ไม่รวมข้อความนี้ในพรอมต์",
1039 "Include message in prompts": "รวมข้อความนี้ในพรอมต์",
1040 "Embed file or image": "ฝังไฟล์หรือภาพ",
1041 "Create checkpoint": "สร้างจุดเช็คพอยท์",
1042 "Create Branch": "Create Branch",
1043 "Copy": "คัดลอก",
1044 "Open checkpoint chat": "เปิดแชทจากจุดบันทึก",
1045 "Edit": "แก้ไข",
1046 "Confirm": "ยืนยัน",
1047 "Copy this message": "คัดลอกข้อความนี้",
1048 "Delete this message": "ลบข้อความนี้",
1049 "Move message up": "เลื่อนข้อความขึ้น",
1050 "Move message down": "เลื่อนข้อความลง",
1051 "Enlarge": "ขยาย",
1052 "Welcome to SillyTavern!": "ยินดีต้อนรับสู่ SillyTavern!",
1053 "welcome_message_part_1": "ยินดีต้อนรับสู่ SillyTavern! แอปพลิเคชันแชทบอทสำหรับมือโปร",
1054 "welcome_message_part_2": "เพื่อเริ่มใช้งาน คุณจะต้องเชื่อมต่อกับ API",
1055 "welcome_message_part_3": "โดยใช้เมนู API Connections ที่ไอคอนรูปปลั๊กด้านบน",
1056 "welcome_message_part_4": "หากคุณเป็นมือใหม่ ให้เปิดโหมด UI แบบเบสิคด้วยการพิมพ์คำสั่ง",
1057 "welcome_message_part_5": "เพื่อเรียนรู้เรื่องคำสั่งและสัญลักษณ์มาโคร",
1058 "welcome_message_part_6": "หากมีคำถามหรือข้อสงสัย สามารถเข้าร่วมดิสคอร์ดของเราได้ที่นี่",
1059 "Discord server": "เซิฟเวอร์ดิสคอร์ด",
1060 "welcome_message_part_7": "",
1061 "SillyTavern is aimed at advanced users.": "SillyTavern เหมาะสำหรับผู้ใช้งานระดับสูง",
1062 "If you're new to this, enable the simplified UI mode below.": "หากคุณเป็นผู้ใช้ใหม่ โปรดเปิดโหมดUI แบบง่ายด้านล่าง",
1063 "Change it later in the 'User Settings' panel.": "สามารถเปลี่ยนแปลงได้ภายหลังในรายการ ‘การตั้งค่าผู้ใช้'",
1064 "Enable simple UI mode": "เปิดใช้งานโหมด UI แบบง่าย",
1065 "Looking for AI characters?": "กำลังมองหาตัวละคร AI อยู่หรือไม่?",
1066 "onboarding_import": "Import",
1067 "from supported sources or view": "จากแหล่งที่รองรับหรือดู",
1068 "Sample characters": "ตัวอย่างตัวละคร",
1069 "Your Persona": "Persona ของคุณ",
1070 "Before you get started, you must select a persona name.": "ก่อนเริ่มต้นใช้งาน คุณต้องเลือกชื่อ Persona ก่อน",
1071 "welcome_message_part_8": "ข้อมูลนี้สามารถเปลี่ยนแปลงได้ตลอดเวลาที่ไอค่อน",
1072 "welcome_message_part_9": "",
1073 "Persona Name:": "ชื่อ Persona :",
1074 "Temporarily disable automatic replies from this character": "ปิดการตอบกลับอัตโนมัติจากตัวละครนี้ชั่วคราว",
1075 "Enable automatic replies from this character": "เปิดใช้งานการตอบกลับอัตโนมัติจากตัวละครนี้",
1076 "Trigger a message from this character": "เรียกให้ตัวละครนี้ส่งข้อความ",
1077 "Move up": "เลื่อนขึ้น",
1078 "Move down": "เลื่อนลง",
1079 "View character card": "ดูการ์ดตัวละคร",
1080 "Remove from group": "ลบออกจากกลุ่ม",
1081 "Add to group": "เพิ่มเข้ากลุ่ม",
1082 "Alternate Greetings": "รูปแบบการทักทายอื่น ๆ",
1083 "Alternate_Greetings_desc": " รายละเอียดของรูปแบบการทักทายอื่น ๆ",
1084 "Alternate Greetings Hint": "Alternate Greetings Hint",
1085 "(This will be the first message from the character that starts every chat)": "(This will be the first message from the character that starts every chat)",
1086 "Forbid Media Override explanation": "Forbid Media Override explanation",
1087 "Forbid Media Override subtitle": "Forbid Media Override subtitle",
1088 "Always forbidden": "ไม่อนุญาตโดยเด็ดขาด",
1089 "Always allowed": "อนุญาตเสมอ",
1090 "View contents": "ดูเนื้อหา",
1091 "Remove the file": "ลบไฟล์",
1092 "Unique to this chat": "Unique to this chat",
1093 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Checkpoints inherit the Note from their parent, and can be changed individually after that.",
1094 "Include in World Info Scanning": "Include in World Info Scanning",
1095 "Before Main Prompt / Story String": "Before Main Prompt / Story String",
1096 "After Main Prompt / Story String": "ข้อความหลังพรอมต์หลัก / ส่วนเริ่มต้นของเรื่อง",
1097 "as": "เช่น",
1098 "Insertion Frequency": "Insertion Frequency",
1099 "(0 = Disable, 1 = Always)": "(0 = ปิดใช้งาน, 1 = เปิดใช้งานเสมอ)",
1100 "User inputs until next insertion:": "User inputs until next insertion:",
1101 "Character Author's Note (Private)": "Character Author's Note (Private)",
1102 "Won't be shared with the character card on export.": "Won't be shared with the character card on export.",
1103 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "จะถูกเพิ่มเป็นบันทึกของผู้เขียนสำหรับตัวละครนี้โดยอัตโนมัติ สามารถใช้ในกลุ่มได้ แต่ไม่สามารถแก้ไขได้เมื่อมีการเปิดแชทกลุ่มอยู่",
1104 "Use character author's note": "Use character author's note",
1105 "Replace Author's Note": "Replace Author's Note",
1106 "Default Author's Note": "Default Author's Note",
1107 "Will be automatically added as the Author's Note for all new chats.": "Will be automatically added as the Author's Note for all new chats.",
1108 "Chat CFG": "Chat CFG",
1109 "1 = disabled": "1 = ปิดใช้งาน",
1110 "write short replies, write replies using past tense": "write short replies, write replies using past tense",
1111 "Positive Prompt": "Positive Prompt",
1112 "Use character CFG scales": "Use character CFG scales",
1113 "Character CFG": "Character CFG",
1114 "Will be automatically added as the CFG for this character.": "Will be automatically added as the CFG for this character.",
1115 "Global CFG": "Global CFG",
1116 "Will be used as the default CFG options for every chat unless overridden.": "Will be used as the default CFG options for every chat unless overridden.",
1117 "CFG Prompt Cascading": "CFG Prompt Cascading",
1118 "Combine positive/negative prompts from other boxes.": "Combine positive/negative prompts from other boxes.",
1119 "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.",
1120 "Always Include": "Always Include",
1121 "Chat Negatives": "ข้อเสียในการแชท",
1122 "Character Negatives": "ลักษณะนิสัยด้านลบของตัวละคร",
1123 "Global Negatives": "Global Negatives",
1124 "Custom Separator:": "Custom Separator:",
1125 "Insertion Depth:": "Insertion Depth:",
1126 "Token Probabilities": "Token Probabilities",
1127 "Select a token to see alternatives considered by the AI.": "Select a token to see alternatives considered by the AI.",
1128 "Not connected to API!": "ไม่ได้เชื่อมต่อ API!",
1129 "Type a message, or /? for help": "Type a message, or /? for help",
1130 "Continue script execution": "Continue script execution",
1131 "Pause script execution": "Pause script execution",
1132 "Abort script execution": "Abort script execution",
1133 "Abort request": "Abort request",
1134 "Continue the last message": "ต่อจากข้อความล่าสุด",
1135 "Send a message": "ส่งข้อความ",
1136 "Close chat": "ปิดแชทนี้",
1137 "Toggle Panels": "เปลี่ยนไปหน้าจัดการแชท",
1138 "Back to parent chat": "กลับไปที่แชทหลัก",
1139 "Save checkpoint": "เซฟเช็คพอยต์",
1140 "Convert to group": "เปลี่ยนเป็นแชทกลุ่ม",
1141 "Start new chat": "สร้างแชทใหม่",
1142 "Manage chat files": "จัดการแชทไฟล์",
1143 "Delete messages": "ลบข้อความ",
1144 "Regenerate": "รีข้อความใหม่",
1145 "Ask AI to write your message for you": "ให้ AI ช่วยเขียนข้อความสำหรับคุณ",
1146 "Impersonate": "สวมบทบาทในมุมมองของ User",
1147 "Continue": "ต่อจากเดิม",
1148 "Bind user name to that avatar": "เชื่อมชื่อผู้ใช้กับรูปตัวแทนนั้น",
1149 "Change persona image": "เปลี่ยนรูป Persona",
1150 "Select this as default persona for the new chats.": "ตั้งค่าตัวละครหลักสำหรับแชทใหม่ทุกครั้ง",
1151 "Delete persona": "ลบ persona",
1152 "These characters are the winners of character design contests and have outstandable quality.": "ตัวละครเหล่านี้เป็นผู้ชนะการประกวดออกแบบตัวละคร",
1153 "Contest Winners": "ผู้ชนะการประกวด",
1154 "These characters are the finalists of character design contests and have remarkable quality.": "ตัวละครเหล่านี้เป็นผู้เข้ารอบสุดท้ายของการประกวดออกแบบตัวละคร ",
1155 "Featured Characters": "ตัวละครยอดนิยม",
1156 "Attach a File": "แนบไฟล์ (รูป,เอกสาร)",
1157 "Open Data Bank": "เปิด Data Bank",
1158 "Enter a URL or the ID of a Fandom wiki page to scrape:": "Enter a URL or the ID of a Fandom wiki page to scrape:",
1159 "Examples:": "ตัวอย่าง:",
1160 "Example:": "ตัวอย่าง:",
1161 "Single file": "ไฟล์เดี่ยว",
1162 "All articles will be concatenated into a single file.": "All articles will be concatenated into a single file.",
1163 "File per article": "File per article",
1164 "Each article will be saved as a separate file.": "Each article will be saved as a separate file.",
1165 "Data Bank": "Data Bank",
1166 "These files will be available for extensions that support attachments (e.g. Vector Storage).": "These files will be available for extensions that support attachments (e.g. Vector Storage).",
1167 "Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.": "Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.",
1168 "Drag and drop files here to upload.": "Drag and drop files here to upload.",
1169 "Date (Newest First)": "เรียงจากวันที่ล่าสุดก่อน",
1170 "Date (Oldest First)": "เรียงจากวันที่เก่าสุดก่อน",
1171 "Name (A-Z)": "ชื่อ (A-Z)",
1172 "Name (Z-A)": "ชื่อ (Z-A)",
1173 "Size (Smallest First)": "ขนาด (เล็กที่สุดก่อน)",
1174 "Size (Largest First)": "ขนาด (ใหญ่ที่สุดก่อน)",
1175 "Bulk Edit": "แก้ไขกลุ่ม",
1176 "Select All": "เลือกทั้งหมด",
1177 "Select None": "ไม่เลือก",
1178 "Global Attachments": "ไฟล์แนบทั่วไป",
1179 "These files are available for all characters in all chats.": "ไฟล์เหล่านี้พร้อมใช้งานสำหรับตัวละครทั้งหมดในทุกแชท",
1180 "Character Attachments": "ไฟล์แนบตัวละคร",
1181 "These files are available the current character in all chats they are in.": "ไฟล์เหล่านี้พร้อมใช้งานสำหรับตัวละครปัจจุบันในทุกแชทที่มีส่วนร่วม",
1182 "Saved locally. Not exported.": "บันทึกในเครื่อง ไม่ส่งออก",
1183 "Chat Attachments": "ไฟล์แนบแชท",
1184 "These files are available to all characters in the current chat.": "ไฟล์เหล่านี้พร้อมใช้งานสำหรับตัวละครทั้งหมดในแชทปัจจุบัน",
1185 "Enter a base URL of the MediaWiki to scrape.": "ใส่ URL หลักของ MediaWiki ที่จะดึงข้อมูล",
1186 "Don't include the page name!": "อย่าใส่ชื่อหน้า!",
1187 "Enter web URLs to scrape (one per line):": "ใส่ URL เว็บที่จะดึงข้อมูล (หนึ่ง URL ต่อบรรทัด):",
1188 "Enter a video URL to download its transcript.": "ใส่ URL วิดีโอเพื่อดาวน์โหลดสคริปต์",
1189 "Expression API": "Expression API",
1190 "ext_sum_with": "สรุปด้วย:",
1191 "ext_sum_main_api": "API หลัก",
1192 "ext_sum_current_summary": "บทสรุปปัจจุบัน:",
1193 "ext_sum_restore_previous": "คืนค่าสถานะก่อนหน้า",
1194 "ext_sum_memory_placeholder": "บทสรุปจะถูกสร้างที่นี่...",
1195 "Trigger a summary update right now.": "สรุปทันที",
1196 "ext_sum_force_text": "สรุปทันที",
1197 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "ปิดการอัปเดตบทสรุปอัตโนมัติ ขณะหยุดชั่วคราว บทสรุปจะยังคงเดิม คุณยังสามารถบังคับให้อัปเดตได้โดยกดปุ่ม [สรุปทันที] (ใช้ได้เฉพาะกับ API หลักเท่านั้น)",
1198 "ext_sum_pause": "หยุดชั่วคราว",
1199 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "ละเว้น World Info และ Author's Note จากข้อความที่จะสรุป มีผลเฉพาะเมื่อใช้ API หลัก Extras API จะละเว้น WI/AN เสมอ",
1200 "ext_sum_no_wi_an": "ไม่มี WI/AN",
1201 "ext_sum_settings_tip": "แก้ไขพรอมต์สรุป ตำแหน่งแทรก และอื่นๆ",
1202 "ext_sum_settings": "การตั้งค่าบทสรุป",
1203 "ext_sum_prompt_builder": "ตัวสร้างพรอมต์",
1204 "ext_sum_prompt_builder_1_desc": "ส่วนขยายสร้างพรอมต์ของตัวเองโดยใช้ข้อความที่ยังไม่ได้สรุป บล็อกการแชทจนกว่าจะสร้างบทสรุปเสร็จ",
1205 "ext_sum_prompt_builder_1": "ดิบ, บล็อกกิ้ง",
1206 "ext_sum_prompt_builder_2_desc": "ส่วนขยายสร้างพรอมต์ของตัวเองโดยใช้ข้อความที่ยังไม่ได้สรุป ไม่บล็อกการแชทขณะสร้างบทสรุป แบ็กเอนด์ไม่ใช่ทั้งหมดที่รองรับโหมดนี้",
1207 "ext_sum_prompt_builder_2": "ดิบ, ไม่บล็อกกิ้ง",
1208 "ext_sum_prompt_builder_3_desc": "ส่วนขยายใช้ตัวสร้างพรอมต์หลักปกติและเพิ่มคำขอสรุปเป็นข้อความระบบสุดท้าย",
1209 "ext_sum_prompt_builder_3": "คลาสสิก, บล็อกกิ้ง",
1210 "Summary Prompt": "พรอมต์สรุป",
1211 "ext_sum_restore_default_prompt_tip": "คืนค่าพรอมต์เริ่มต้น",
1212 "ext_sum_prompt_placeholder": "พรอมต์นี้จะถูกส่งไปยัง AI เพื่อขอให้สร้างบทสรุป {{words}} จะถูกแทนที่ด้วยพารามิเตอร์ 'จำนวนคำ'",
1213 "ext_sum_target_length_1": "ความยาวบทสรุปเป้าหมาย",
1214 "ext_sum_target_length_2": "(",
1215 "ext_sum_target_length_3": "คำ)",
1216 "ext_sum_api_response_length_1": "ความยาวการตอบสนอง API",
1217 "ext_sum_api_response_length_2": "(",
1218 "ext_sum_api_response_length_3": "โทเค็น)",
1219 "ext_sum_0_default": "0 = เริ่มต้น",
1220 "ext_sum_raw_max_msg": "[ดิบ] ข้อความสูงสุดต่อคำขอ",
1221 "ext_sum_0_unlimited": "0 = ไม่จำกัด",
1222 "Update frequency": "ความถี่การอัปเดต",
1223 "ext_sum_update_every_messages_1": "อัปเดตทุก",
1224 "ext_sum_update_every_messages_2": "ข้อความ",
1225 "ext_sum_0_disable": "0 = ปิดใช้งาน",
1226 "ext_sum_auto_adjust_desc": "พยายามปรับช่วงเวลาโดยอัตโนมัติตามเมตริกของการแชท",
1227 "ext_sum_update_every_words_1": "อัปเดตทุก",
1228 "ext_sum_update_every_words_2": "คำ",
1229 "ext_sum_both_sliders": "หากทั้งสองสไลเดอร์เป็นค่าที่ไม่ใช่ศูนย์ ทั้งคู่จะทริกเกอร์การอัปเดตบทสรุปที่ช่วงเวลาของแต่ละอัน",
1230 "ext_sum_injection_template": "เทมเพลตการแทรก",
1231 "ext_sum_memory_template_placeholder": "{{summary}} จะถูกแทนที่ด้วยเนื้อหาบทสรุปปัจจุบัน",
1232 "ext_sum_injection_position": "ตำแหน่งการแทรก",
1233 "How many messages before the current end of the chat.": "จำนวนข้อความก่อนจุดสิ้นสุดปัจจุบันของการแชท",
1234 "ext_regex_title": "Regular Expression",
1235 "ext_regex_new_global_script": "+ โกลบอล",
1236 "ext_regex_new_scoped_script": "+ สโคป",
1237 "ext_regex_import_script": "นำเข้า",
1238 "ext_regex_global_scripts": "สคริปต์โกลบอล",
1239 "ext_regex_global_scripts_desc": "ใช้ได้กับตัวละครทั้งหมด บันทึกในการตั้งค่าท้องถิ่น",
1240 "ext_regex_scoped_scripts": "สคริปต์สโคป",
1241 "ext_regex_scoped_scripts_desc": "ใช้ได้เฉพาะตัวละครนี้ บันทึกในข้อมูลการ์ด",
1242 "Regex Editor": "ตัวแก้ไข Regex",
1243 "Test Mode": "โหมดทดสอบ",
1244 "ext_regex_desc": "Regex เป็นเครื่องมือสำหรับค้นหา/แทนที่สตริงโดยใช้นิพจน์ปกติ หากต้องการทราบรายละเอียด ให้คลิก [?] ข้างชื่อเรื่อง",
1245 "Input": "ป้อนข้อมูล",
1246 "ext_regex_test_input_placeholder": "พิมพ์ที่นี่...",
1247 "Output": "ผลลัพธ์",
1248 "ext_regex_output_placeholder": "ว่างเปล่า",
1249 "Script Name": "ชื่อสคริปต์",
1250 "Find Regex": "ค้นหา Regex",
1251 "Replace With": "แทนที่ด้วย",
1252 "ext_regex_replace_string_placeholder": "ใช้ {{match}} เพื่อรวมข้อความที่ตรงกันจากการค้นหา regex และใช้ $1, $2 เป็นต้น สำหรับกลุ่มที่จับได้",
1253 "Trim Out": "ตัดออก",
1254 "ext_regex_trim_placeholder": "ตัดส่วนที่ไม่ต้องการออกจากการจับคู่ regex โดยรวมก่อนการแทนที่ แยกแต่ละรายการด้วย Enter",
1255 "ext_regex_affects": "ส่งผลต่อ",
1256 "ext_regex_user_input": "ข้อมูลที่ผู้ใช้ป้อน",
1257 "ext_regex_ai_output": "ผลลัพธ์ AI",
1258 "Slash Commands": "คำสั่ง Slash",
1259 "ext_regex_min_depth_desc": "เมื่อใช้กับพรอมต์หรือการแสดงผล จะส่งผลต่อข้อความที่มีความลึกอย่างน้อย N ระดับเท่านั้น 0 = ข้อความสุดท้าย, 1 = ข้อความที่สองจากท้าย เป็นต้น นับเฉพาะรายการ WI @Depth และข้อความที่ใช้ได้ (ไม่ใช่ข้อความที่ซ่อนหรือระบบ)",
1260 "Min Depth": "ความลึกขั้นต่ำ",
1261 "ext_regex_min_depth_placeholder": "ไม่จำกัด",
1262 "ext_regex_max_depth_desc": "เมื่อใช้กับพรอมต์หรือการแสดงผล จะส่งผลต่อข้อความที่มีความลึกไม่เกิน N ระดับเท่านั้น 0 = ข้อความสุดท้าย, 1 = ข้อความที่สองจากท้าย เป็นต้น นับเฉพาะรายการ WI @Depth และข้อความที่ใช้ได้ (ไม่ใช่ข้อความที่ซ่อนหรือระบบ)",
1263 "ext_regex_other_options": "ตัวเลือกอื่นๆ",
1264 "Only Format Display": "จัดรูปแบบการแสดงผลเท่านั้น",
1265 "ext_regex_only_format_prompt_desc": "ประวัติการแชทจะไม่เปลี่ยนแปลง มีการเปลี่ยนแปลงเฉพาะพรอมต์เมื่อส่งคำขอ (ในระหว่างการสร้าง)",
1266 "Only Format Prompt (?)": "จัดรูปแบบพรอมต์เท่านั้น (?)",
1267 "Run On Edit": "ทำงานเมื่อแก้ไข",
1268 "ext_regex_substitute_regex_desc": "แทนที่ {{macros}} ใน Find Regex ก่อนการทำงาน",
1269 "Substitute Regex": "แทนที่ Regex",
1270 "ext_regex_import_target": "นำเข้าไปยัง:",
1271 "ext_regex_disable_script": "ปิดใช้งานสคริปต์",
1272 "ext_regex_enable_script": "เปิดใช้งานสคริปต์",
1273 "ext_regex_edit_script": "แก้ไขสคริปต์",
1274 "ext_regex_move_to_global": "ย้ายไปยังสคริปต์โกลบอล",
1275 "ext_regex_move_to_scoped": "ย้ายไปยังสคริปต์สโคป",
1276 "ext_regex_export_script": "ส่งออกสคริปต์",
1277 "ext_regex_delete_script": "ลบสคริปต์",
1278 "Trigger Stable Diffusion": "เรียกใช้ Stable Diffusion",
1279 "sd_Yourself": "ตัวคุณเอง",
1280 "sd_Your_Face": "ใบหน้าของคุณ",
1281 "sd_Me": "ฉัน",
1282 "sd_The_Whole_Story": "เรื่องราวทั้งหมด",
1283 "sd_The_Last_Message": "ข้อความสุดท้าย",
1284 "sd_Raw_Last_Message": "ข้อความสุดท้ายดิบ",
1285 "sd_Background": "พื้นหลัง",
1286 "Image Generation": "การสร้างภาพ",
1287 "Stop Image Generation": "หยุดการสร้างภาพ",
1288 "Generate Caption": "สร้างคำบรรยาย",
1289 "sd_refine_mode": "อนุญาตให้แก้ไขพรอมต์ด้วยตนเองก่อนส่งไปยัง API การสร้าง",
1290 "sd_refine_mode_txt": "แก้ไขพรอมต์ก่อนสร้าง",
1291 "sd_interactive_mode": "สร้างภาพโดยอัตโนมัติเมื่อส่งข้อความแบบ \"โปรดส่งรูปแมว\"",
1292 "sd_interactive_mode_txt": "โหมดโต้ตอบ",
1293 "sd_multimodal_captioning": "ใช้คำบรรยายมัลติโมดัลเพื่อสร้างพรอมต์สำหรับภาพพอร์ตเทรตผู้ใช้และตัวละครตามอวาตาร์",
1294 "sd_multimodal_captioning_txt": "ใช้คำบรรยายมัลติโมดัลสำหรับภาพพอร์ตเทรต",
1295 "sd_expand": "ขยายพรอมต์โดยอัตโนมัติโดยใช้โมเดลการสร้างข้อความ",
1296 "sd_expand_txt": "พรอมต์ปรับปรุงอัตโนมัติ",
1297 "sd_snap": "จัดคำขอการสร้างให้ตรงกับความละเอียดที่รู้จักที่ใกล้เคียงที่สุดด้วยอัตราส่วนที่บังคับ (พอร์ตเทรต, พื้นหลัง) โดยรักษาจำนวนพิกเซลสัมบูรณ์ (แนะนำสำหรับ SDXL)",
1298 "sd_snap_txt": "จัดความละเอียดอัตโนมัติ",
1299 "Source": "แหล่งที่มา",
1300 "sd_auto_url": "ตัวอย่าง: {{auto_url}}",
1301 "Authentication (optional)": "การยืนยันตัวตน (ไม่บังคับ)",
1302 "Example: username:password": "ตัวอย่าง: ชื่อผู้ใช้:รหัสผ่าน",
1303 "Important:": "สำคัญ:",
1304 "sd_auto_auth_warning_1": "ทำงาน SD Web UI ด้วย",
1305 "sd_auto_auth_warning_2": "แฟล็ก! เซิร์ฟเวอร์ต้องสามารถเข้าถึงได้จากเครื่อง SillyTavern โฮสต์",
1306 "sd_drawthings_url": "ตัวอย่าง: {{drawthings_url}}",
1307 "sd_drawthings_auth_txt": "ทำงานแอป DrawThings โดยเปิดใช้งานสวิตช์ HTTP API ใน UI เซิร์ฟเวอร์ต้องสามารถเข้าถึงได้จากเครื่อง SillyTavern โฮสต์",
1308 "sd_vlad_url": "ตัวอย่าง: {{vlad_url}}",
1309 "The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",
1310 "Hint: Save an API key in AI Horde API settings to use it here.": "Hint: Save an API key in AI Horde API settings to use it here.",
1311 "Allow NSFW images from Horde": "อนุญาตภาพที่ไม่เหมาะสมจาก Horde",
1312 "Sanitize prompts (recommended)": "Sanitize prompts (recommended)",
1313 "Automatically adjust generation parameters to ensure free image generations.": "Automatically adjust generation parameters to ensure free image generations.",
1314 "Avoid spending Anlas": "Avoid spending Anlas",
1315 "Opus tier": "Opus tier",
1316 "View my Anlas": "View my Anlas",
1317 "These settings only apply to DALL-E 3": "These settings only apply to DALL-E 3",
1318 "Image Style": "รูปแบบของรูปภาพ",
1319 "Image Quality": "คุณภาพของรูปภาพ",
1320 "Standard": "มาตรฐาน",
1321 "HD": "HD",
1322 "sd_comfy_url": "ตัวอย่าง: {{comfy_url}}",
1323 "Open workflow editor": "Open workflow editor",
1324 "Create new workflow": "Create new workflow",
1325 "Delete workflow": "Delete workflow",
1326 "Enhance": "Enhance",
1327 "Refine": "Refine",
1328 "Decrisper": "Decrisper",
1329 "Sampling steps": "Sampling steps",
1330 "Width": "ความกว้าง",
1331 "Height": "ความสูง",
1332 "Resolution": "ความละเอียดของรูปภาพ",
1333 "Model": "Model",
1334 "Sampling method": "Sampling method",
1335 "Karras (not all samplers supported)": "Karras (not all samplers supported)",
1336 "SMEA versions of samplers are modified to perform better at high resolution.": "SMEA versions of samplers are modified to perform better at high resolution.",
1337 "SMEA": "SMEA",
1338 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.",
1339 "DYN": "DYN",
1340 "Scheduler": "Scheduler",
1341 "Restore Faces": "Restore Faces",
1342 "Hires. Fix": "Hires. Fix",
1343 "Upscaler": "Upscaler",
1344 "Upscale by": "Upscale by",
1345 "Denoising strength": "Denoising strength",
1346 "Hires steps (2nd pass)": "Hires steps (2nd pass)",
1347 "Preset for prompt prefix and negative prompt": "Preset for prompt prefix and negative prompt",
1348 "Style": "รูปแบบ",
1349 "Save style": "เซฟรูปแบบ",
1350 "Delete style": "ลบรูปแบบ",
1351 "Common prompt prefix": "Common prompt prefix",
1352 "sd_prompt_prefix_placeholder": "ใช้ {prompt} เพื่อระบุตำแหน่งที่จะแทรกพรอมต์ที่สร้างขึ้น",
1353 "Negative common prompt prefix": "Negative common prompt prefix",
1354 "Character-specific prompt prefix": "Character-specific prompt prefix",
1355 "Won't be used in groups.": "Won't be used in groups.",
1356 "sd_character_prompt_placeholder": "ลักษณะเฉพาะที่อธิบายตัวละครที่เลือกอยู่ จะถูกเพิ่มหลังจาก common prompt prefix\nตัวอย่าง: ผู้หญิง, ตาสีเขียว, ผมสีน้ำตาล, เสื้อสีชมพู",
1357 "Character-specific negative prompt prefix": "Character-specific negative prompt prefix",
1358 "sd_character_negative_prompt_placeholder": "ลักษณะที่ไม่ควรปรากฏในตัวละครที่เลือก จะถูกเพิ่มหลังจาก common negative prompt prefix\nตัวอย่าง: เครื่องประดับ, รองเท้า, แว่นตา",
1359 "Shareable": "Shareable",
1360 "Image Prompt Templates": "Image Prompt Templates",
1361 "Vectors Model Warning": "Vectors Model Warning",
1362 "Translate files into English before processing": "แปลไฟล์เป็นภาษาอังกฤษก่อนดำเนินการ",
1363 "Manager Users": "จัดการผู้ใช้",
1364 "Enter a name for this persona:": "ใส่ชื่อสำหรับ Persona นี้",
1365 "New User": "ผู้ใช้ใหม่",
1366 "Status:": "สถานะ:",
1367 "Created:": "สร้าง:",
1368 "Display Name:": "ชื่อที่แสดง:",
1369 "User Handle:": "ตัวจัดการผู้ใช้:",
1370 "Password:": "รหัสผ่าน:",
1371 "Confirm Password:": "ยืนยันรหัสผ่าน:",
1372 "This will create a new subfolder...": "การกระทำนี้จะสร้างโฟลเดอร์ย่อยใหม่...",
1373 "Current Password:": "รหัสผ่านปัจจุบัน:",
1374 "New Password:": "รหัสผ่านใหม่:",
1375 "Confirm New Password:": "ยืนยันรหัสผ่านใหม่:",
1376 "Debug Warning": "Debug Warning",
1377 "Execute": "ดำเนินการ",
1378 "Are you sure you want to delete this user?": "คุณแน่ใจแล้วใช่ไหมที่ต้องการลบผู้ใช้นี้?",
1379 "Deleting:": "กำลังลบ...",
1380 "Also wipe user data.": "ลบข้อมูลผู้ใช้ด้วย",
1381 "Warning:": "คำเตือน",
1382 "This action is irreversible.": "การกระทำนี้ไม่สามารถย้อนกลับได้.",
1383 "Type the user's handle below to confirm:": "Type the user's handle below to confirm:",
1384 "Import Characters": "นำเข้าตัวละคร",
1385 "Enter the URL of the content to import": "ใส่ URL ของเนื้อหาที่จะนำเข้า",
1386 "Supported sources:": "แหล่งที่รองรับ:",
1387 "char_import_1": "การ์ดตัวละคร Chub (ลิงก์โดยตรงหรือ ID)",
1388 "char_import_example": "ตัวอย่าง:",
1389 "char_import_2": "Lorebook ของ Chub (ลิงก์โดยตรงหรือ ID)",
1390 "char_import_3": "การ์ดตัวละคร JanitorAI (ลิงก์โดยตรงหรือ UUID)",
1391 "char_import_4": "การ์ดตัวละคร Pygmalion.chat (ลิงก์โดยตรงหรือ UUID)",
1392 "char_import_5": "การ์ดตัวละคร AICharacterCards.com (ลิงก์โดยตรงหรือ ID)",
1393 "char_import_6": "ลิงก์ PNG โดยตรง (ดู",
1394 "char_import_7": "โฮสต์ที่อนุญาต)",
1395 "char_import_8": "การ์ดตัวละคร RisuRealm (ลิงก์โดยตรง)",
1396 "char_import_10": "การ์ดตัวละคร Perchance (ลิงก์โดยตรงหรือ UUID + .gz)",
1397 "Supports importing multiple characters.": "รองรับการนำเข้าตัวละครหลายตัว",
1398 "Write each URL or ID into a new line.": "เขียน URL หรือ ID แต่ละอันในบรรทัดใหม่",
1399 "Export for character": "ส่งออกการ์ดตัวละคร",
1400 "Export prompts for this character, including their order.": "ส่งออกข้อความสำหรับตัวละครนี้ พร้อมเรียงลำดับข้อความ",
1401 "Export all": "ส่งออกทั้งหมด",
1402 "Export all your prompts to a file": "ส่งออก Prompts ทั้งหมดในรูปแบบไฟล์",
1403 "Insert prompt": "แทรก prompt",
1404 "Delete prompt": "ลบ prompt",
1405 "Import a prompt list": "นำเข้ารายการ prompt",
1406 "Export this prompt list": "ส่งออกรายการ prompt",
1407 "Reset current character": "รีเซ็ตตัวละครปัจจุบัน",
1408 "New prompt": "New prompt",
1409 "Prompts": "Prompts",
1410 "Total Tokens:": "จำนวนโทเค็นทั้งหมด:",
1411 "prompt_manager_tokens": "โทเค็น",
1412 "Are you sure you want to reset your settings to factory defaults?": "คุณแน่ใจไหมว่าต้องการคืนค่าการตั้งค่าเป็นค่าเริ่มต้น?",
1413 "Don't forget to save a snapshot of your settings before proceeding.": "อย่าลืมบันทึกสแนปช็อตการตั้งค่าของคุณก่อนดำเนินการ",
1414 "Settings Snapshots": "สแนปช็อตการตั้งค่า",
1415 "Record a snapshot of your current settings.": "บันทึกสแนปช็อตของการตั้งค่าปัจจุบันของคุณ",
1416 "Make a Snapshot": "สร้างสแนปช็อต",
1417 "Restore this snapshot": "คืนค่าสแนปช็อตนี้",
1418 "Hi,": "สวัสดี,",
1419 "To enable multi-account features, restart the SillyTavern server with": "เพื่อเปิดระบบ Multiple Account ให้รีสตาร์ทเซิร์ฟเวอร์ SillyTavern อีกครั้ง",
1420 "Account Info": "รายละเอียดของบัญชี",
1421 "To change your user avatar, use the buttons below or select a default persona in the Persona Management menu.": "เพื่อเปลี่ยนรูปโปรไฟล์ของคุณ ให้ใช้ปุ่มด้านล่างหรือเลือก Persona ในเมนูการจัดการตัวละคร",
1422 "Set your custom avatar.": "ตั้งค่ารูปโปรไฟล์ของคุณ",
1423 "Remove your custom avatar.": "ลบรูปโปรไฟล์ของคุณ",
1424 "Handle:": "Handle:",
1425 "This account is password protected.": "บัญชีนี้มีการป้องกันด้วยรหัสผ่าน",
1426 "This account is not password protected.": "บัญชีนี้ไม่มีการป้องกันด้วยรหัสผ่าน",
1427 "Account Actions": "การดำเนินการบัญชี",
1428 "Change Password": "เปลี่ยนรหัสผ่าน",
1429 "Manage your settings snapshots.": "จัดการการตั้งค่า Snapshot ของคุณ",
1430 "Download a complete backup of your user data.": "ดาวน์โหลดการสำรองข้อมูลทั้งหมดของข้อมูลผู้ใช้",
1431 "Download Backup": "ดาวน์โหลดการสำรองข้อมูล",
1432 "Danger Zone": "Danger Zone",
1433 "Reset your settings to factory defaults.": "รีเซ็ตการตั้งค่าเป็นค่าเริ่มต้น",
1434 "Reset Settings": "รีเซ็ตการตั้งค่า",
1435 "Wipe all user data and reset your account to factory settings.": "ลบข้อมูลผู้ใช้ทั้งหมดและรีเซ็ตบัญชีของคุณเป็นการตั้งค่าเริ่มต้น",
1436 "Reset Everything": "คืนค่าทุกอย่างเป็นค่าเริ่มต้น",
1437 "Reset Code:": "Reset Code:",
1438 "Want to update?": "ต้องการอัพเดตไหม?",
1439 "How to start chatting?": "จะเริ่มการสนทนาอย่างไร?",
1440 "Click _space": "Click space",
1441 "and select a": "และเลือก",
1442 "Chat API": "Chat API",
1443 "and pick a character.": "และเลือกตัวละคร",
1444 "You can browse a list of bundled characters in the": "คุณสามารถเรียกดูรายการตัวละครที่มีมาให้ใน",
1445 "Download Extensions & Assets": "ดาวน์โหลดส่วนขยายและทรัพยากร",
1446 "Load a custom asset list or select": "โหลดรายการทรัพยากรที่กำหนดเองหรือเลือกจาก",
1447 "to install 3rd party extensions.": "เพื่อติดตั้งส่วนขยายจาก 3rd party",
1448 "menu within": "เมนูใน",
1449 "Confused or lost?": "สับสนหรือหลงทาง?",
1450 "click these icons!": "คลิกไอคอนเหล่านี้!",
1451 "in the chat bar": "ในแถบแชท",
1452 "SillyTavern Documentation Site": "เว็บไซต์เอกสาร SillyTavern",
1453 "Extras Installation Guide": "คู่มือการติดตั้ง Extras",
1454 "Still have questions?": "ยังคงมีคำถามอยู่ใช่ไหม?",
1455 "Join the SillyTavern Discord": "เข้าร่วมดิสคอร์ด SillyTavern ",
1456 "Post a GitHub issue": "Post a GitHub issue",
1457 "Contact the developers": "ติดต่อผู้พัฒนา",
1458 "Stop Inspecting": "Stop Inspecting",
1459 "Inspect Prompts": "ตรวจสอบ Prompt",
1460 "Toggle prompt inspection": "สลับการตรวจสอบ Prompt"
1461}
\ No newline at end of file1461 \ No newline at end of file
public/locales/uk-ua.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "прапорцем",317 "flag": "прапорцем",
318 "API key (optional)": "Ключ API (необов'язково)",318 "API key (optional)": "Ключ API (необов'язково)",
319 "Server url": "URL-адреса сервера",319 "Server url": "URL-адреса сервера",
320 "Electron Hub API Key": "Ключ API для Electron Hub",
321 "Electron Hub Model": "Модель Electron Hub",
320 "Example: http://127.0.0.1:5000": "Приклад: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Приклад: http://127.0.0.1:5000",
321 "Custom model (optional)": "Власна модель (необов'язково)",323 "Custom model (optional)": "Власна модель (необов'язково)",
322 "vllm-project/vllm": "vllm-project/vllm (режим оболонки OpenAI API)",324 "vllm-project/vllm": "vllm-project/vllm (режим оболонки OpenAI API)",
public/locales/vi-vn.json+2 -0
@@ -317,6 +317,8 @@
317 "flag": "cờ",317 "flag": "cờ",
318 "API key (optional)": "Key API (tùy chọn)",318 "API key (optional)": "Key API (tùy chọn)",
319 "Server url": "URL máy chủ",319 "Server url": "URL máy chủ",
320 "Electron Hub API Key": "Key API Electron Hub",
321 "Electron Hub Model": "Model Electron Hub",
320 "Example: http://127.0.0.1:5000": "Ví dụ: http://127.0.0.1:5000",322 "Example: http://127.0.0.1:5000": "Ví dụ: http://127.0.0.1:5000",
321 "Custom model (optional)": "Model tùy chỉnh (tùy chọn)",323 "Custom model (optional)": "Model tùy chỉnh (tùy chọn)",
322 "vllm-project/vllm": "vllm-project/vllm (Chế độ trình bao bọc API OpenAI)",324 "vllm-project/vllm": "vllm-project/vllm (Chế độ trình bao bọc API OpenAI)",
public/locales/zh-cn.json+378 -182
@@ -2,7 +2,7 @@
2 "Favorite": "星标",2 "Favorite": "星标",
3 "Tag": "标签",3 "Tag": "标签",
4 "Duplicate": "复制",4 "Duplicate": "复制",
5 "Persona": "用户角色",5 "Persona": "用户设定",
6 "Delete": "删除",6 "Delete": "删除",
7 "AI Response Configuration": "AI响应配置",7 "AI Response Configuration": "AI响应配置",
8 "AI Configuration panel will stay open": "AI配置面板将保持打开",8 "AI Configuration panel will stay open": "AI配置面板将保持打开",
@@ -21,10 +21,11 @@
21 "novelaipresets": "NovelAI 预设",21 "novelaipresets": "NovelAI 预设",
22 "Default": "默认",22 "Default": "默认",
23 "openaipresets": "对话补全预设",23 "openaipresets": "对话补全预设",
24 "Bind presets to API connections": "将预设与 API 配置绑定",
24 "Text Completion presets": "文本补全预设",25 "Text Completion presets": "文本补全预设",
25 "response legth(tokens)": "回复长度(以词符数计)",26 "response legth(tokens)": "回复长度(以词符数计)",
26 "Streaming": "流式传输",27 "Streaming": "流式传输",
27 "Streaming_desc": "逐位显示生成的回复",28 "Streaming_desc": "逐词显示生成的回复",
28 "context size(tokens)": "上下文长度(以词符数计)",29 "context size(tokens)": "上下文长度(以词符数计)",
29 "unlocked": "解锁",30 "unlocked": "解锁",
30 "Only enable this if your model supports context sizes greater than 8192 tokens": "仅在您的模型支持大于8192个词符的上下文长度时启用此选项",31 "Only enable this if your model supports context sizes greater than 8192 tokens": "仅在您的模型支持大于8192个词符的上下文长度时启用此选项",
@@ -73,10 +74,12 @@
73 "Context Size (tokens)": "上下文长度(以词符数计)",74 "Context Size (tokens)": "上下文长度(以词符数计)",
74 "Max Response Length (tokens)": "最大回复长度(以词符数计)",75 "Max Response Length (tokens)": "最大回复长度(以词符数计)",
75 "Multiple swipes per generation": "每次生成多个备选回复",76 "Multiple swipes per generation": "每次生成多个备选回复",
76 "Middle-out Transform": "Middle-out Transform",77 "Allow compressing requests by removing messages from the middle of the prompt.": "允许通过移除提示词中间的消息来压缩请求。",
77 "Auto": "Auto",78 "Middle-out Transform": "中心向外算法",
78 "Allow": "Allow",79 "Auto": "自动",
79 "Forbid": "Forbid",80 "Allow": "允许",
81 "Forbid": "禁止",
82 "Unknown": "未知",
80 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",83 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",
81 "Display the response bit by bit as it is generated.": "随着回复的生成,逐词逐句地显示结果。",84 "Display the response bit by bit as it is generated.": "随着回复的生成,逐词逐句地显示结果。",
82 "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",85 "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",
@@ -147,8 +150,9 @@
147 "Epsilon Cutoff": "ε 截断",150 "Epsilon Cutoff": "ε 截断",
148 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "ε 截断设置了一个概率下限,低于该下限的词符将被排除在采样之外。\n以 1e-4 单位;合适的值为 3。将其设置为 0 以禁用。",151 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "ε 截断设置了一个概率下限,低于该下限的词符将被排除在采样之外。\n以 1e-4 单位;合适的值为 3。将其设置为 0 以禁用。",
149 "Top nsigma": "Top nsigma",152 "Top nsigma": "Top nsigma",
153 "Min Keep": "Min Keep",
150 "Eta Cutoff": "η 截断",154 "Eta Cutoff": "η 截断",
151 "Eta_Cutoff_desc": "η截断是特殊η采样技术的主要参数。&#13;以1e-4为单位;合理的值为3。&#13;设置为0以禁用。&#13;有关详细信息,请参阅Hewitt等人的论文《Truncation Sampling as Language Model Desmoothing》(2022年)。",155 "Eta_Cutoff_desc": "η截断是特殊η采样技术的主要参数。以1e-4为单位;合理的值为3。设置为0以禁用。详细请参阅《Truncation Sampling as Language Model Desmoothing》(Hewitt et al., 2022)。",
152 "rep.pen decay": "重复惩罚衰减",156 "rep.pen decay": "重复惩罚衰减",
153 "Encoder Rep. Pen.": "编码器重复惩罚",157 "Encoder Rep. Pen.": "编码器重复惩罚",
154 "No Repeat Ngram Size": "无重复n-gram大小",158 "No Repeat Ngram Size": "无重复n-gram大小",
@@ -187,9 +191,9 @@
187 "Beam search": "束搜索",191 "Beam search": "束搜索",
188 "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采样中使用的贪婪暴力算法,用于找到最可能的单词或标记序列。它一次扩展多个候选序列,在每一步保留固定数量(光束宽度)的最佳序列。",192 "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采样中使用的贪婪暴力算法,用于找到最可能的单词或标记序列。它一次扩展多个候选序列,在每一步保留固定数量(光束宽度)的最佳序列。",
189 "# of Beams": "光束数量",193 "# of Beams": "光束数量",
190 "The number of sequences generated at each step with Beam Search.": "The number of sequences generated at each step with Beam Search.",194 "The number of sequences generated at each step with Beam Search.": "束搜索每步生成的序列数量。",
191 "Length Penalty": "长度惩罚",195 "Length Penalty": "长度惩罚",
192 "Penalize sequences based on their length.": "Penalize sequences based on their length.",196 "Penalize sequences based on their length.": "根据序列长度对其进行惩罚。",
193 "Early Stopping": "提前停止",197 "Early Stopping": "提前停止",
194 "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.": "控制光束搜索的停止条件。勾选时,当生成到达‘光束数量’的序列时停止。如果未勾选,则采用启发式方法,当几乎不可能找到更好的候选项时停止生成。",198 "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.": "控制光束搜索的停止条件。勾选时,当生成到达‘光束数量’的序列时停止。如果未勾选,则采用启发式方法,当几乎不可能找到更好的候选项时停止生成。",
195 "Contrastive search": "对比搜索",199 "Contrastive search": "对比搜索",
@@ -203,7 +207,7 @@
203 "Ignore EOS Token": "忽略序列结束词符",207 "Ignore EOS Token": "忽略序列结束词符",
204 "Ignore the EOS Token even if it generates.": "即使生成了序列结束词符,也忽略它。",208 "Ignore the EOS Token even if it generates.": "即使生成了序列结束词符,也忽略它。",
205 "Skip Special Tokens": "跳过特殊词符",209 "Skip Special Tokens": "跳过特殊词符",
206 "Request Model Reasoning": "Request Model Reasoning",210 "Request Model Reasoning": "请求模型推理",
207 "Temperature Last": "温度放最后",211 "Temperature Last": "温度放最后",
208 "Temperature_Last_desc": "温度采样器放到最后使用。这通常是合理的。\n当启用时:首先进行潜在词符的选择,然后应用温度来修正它们的相对概率(技术上是对数似然)。\n当禁用时:首先应用温度来修正所有词符的相对概率,然后从中选择潜在词符。\n禁用此项可以增大分布在尾部的词符概率,这可能加大得到不相关回复的几率。",212 "Temperature_Last_desc": "温度采样器放到最后使用。这通常是合理的。\n当启用时:首先进行潜在词符的选择,然后应用温度来修正它们的相对概率(技术上是对数似然)。\n当禁用时:首先应用温度来修正所有词符的相对概率,然后从中选择潜在词符。\n禁用此项可以增大分布在尾部的词符概率,这可能加大得到不相关回复的几率。",
209 "Speculative Ngram": "推测性 Ngram",213 "Speculative Ngram": "推测性 Ngram",
@@ -254,10 +258,16 @@
254 "Continue sends the last message as assistant role instead of system message with instruction.": "继续发送的是作为助手角色的最后一条消息,而不是带有指示的系统消息。",258 "Continue sends the last message as assistant role instead of system message with instruction.": "继续发送的是作为助手角色的最后一条消息,而不是带有指示的系统消息。",
255 "Squash system messages": "压缩系统消息",259 "Squash system messages": "压缩系统消息",
256 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "将连续的系统消息合并为一条(不包括示例对话),可能会提高一些模型的连贯性。",260 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "将连续的系统消息合并为一条(不包括示例对话),可能会提高一些模型的连贯性。",
261 "Enable web search": "启用联网搜索",
262 "Use search capabilities provided by the backend.": "使用后端提供的联网搜索功能。",
263 "openrouter_web_search_fee": "收费,每个提示词会多收 $0.02。",
257 "Enable function calling": "启用函数调用",264 "Enable function calling": "启用函数调用",
265 "Supported by the current model": "当前模型支持",
266 "Unsupported by the current model": "当前模型不支持",
258 "enable_functions_desc_1": "允许使用",267 "enable_functions_desc_1": "允许使用",
259 "enable_functions_desc_2": "功能工具",268 "enable_functions_desc_2": "功能工具",
260 "enable_functions_desc_3": "可以被各种扩展利用来提供附加功能。",269 "enable_functions_desc_3": "可以被各种扩展利用来提供附加功能。",
270 "enable_functions_desc_4": "当提示词后处理没有选择工具时不支持。",
261 "Send inline images": "发送图片",271 "Send inline images": "发送图片",
262 "image_inlining_hint_1": "如果模型支持,就可以在提示词中发送图片。\n发送消息时,点击",272 "image_inlining_hint_1": "如果模型支持,就可以在提示词中发送图片。\n发送消息时,点击",
263 "image_inlining_hint_2": "在这里(",273 "image_inlining_hint_2": "在这里(",
@@ -266,23 +276,38 @@
266 "openai_inline_image_quality_auto": "自动",276 "openai_inline_image_quality_auto": "自动",
267 "openai_inline_image_quality_low": "低",277 "openai_inline_image_quality_low": "低",
268 "openai_inline_image_quality_high": "高",278 "openai_inline_image_quality_high": "高",
279 "Send inline videos": "发送视频",
280 "video_inlining_hint_1": "当模型支持时,将视频发送给模型。使用",
281 "video_inlining_hint_2": "在任意消息上添加视频,或",
282 "video_inlining_hint_3": "菜单来添加视频。",
283 "video_inlining_hint_4": "视频必须在 20MB 以下且时长不超过1分钟。",
284 "Request inline images": "请求图片返回",
285 "Allows the model to return image attachments.": "允许模型返回图片附件。",
286 "Request inline images_desc_2": "与以下几个功能不兼容:函数调用、联网搜搜、系统提示词。",
269 "Use system prompt": "使用系统提示词",287 "Use system prompt": "使用系统提示词",
270 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",288 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
271 "Merges_all_system_messages_desc_2": "字段发送。",289 "Merges_all_system_messages_desc_2": "字段发送。",
272 "Request model reasoning": "请求思维链",290 "Request model reasoning": "请求思维链",
273 "Allows the model to return its thinking process.": "允许模型返回其思维过程。",291 "Allows the model to return its thinking process.": "允许模型返回其思维过程。",
292 "This setting affects visibility only.": "此设置只影响思维链是否可见。",
274 "Constrains effort on reasoning for reasoning models.": "限定模型推理的强度。\n当前支持低、中、高三种强度。\n降低推理强度可以让模型更快回复,并节省推理所用的词符数。。",293 "Constrains effort on reasoning for reasoning models.": "限定模型推理的强度。\n当前支持低、中、高三种强度。\n降低推理强度可以让模型更快回复,并节省推理所用的词符数。。",
275 "Reasoning Effort": "推理强度",294 "Reasoning Effort": "推理强度",
295 "openai_reasoning_effort_auto": "自动",
296 "openai_reasoning_effort_minimum": "极低",
276 "openai_reasoning_effort_low": "低",297 "openai_reasoning_effort_low": "低",
277 "openai_reasoning_effort_medium": "中",298 "openai_reasoning_effort_medium": "中",
278 "openai_reasoning_effort_high": "高",299 "openai_reasoning_effort_high": "高",
300 "openai_reasoning_effort_maximum": "极高",
301 "OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.": "OpenAI式选项:低、中、高。极低等于低,极高等于高。选择自动,则不传入推理强度参数。",
302 "Allocates a portion of the response length for thinking (min: 1024 tokens, low: 10%, medium: 25%, high: 50%, max: 95%), but minimum 1024 tokens. Auto does not request thinking.": "将最大回复空间的一部分分配给思维链(极低:1024词符,低:10%,中:25%,高:50%,极高:95%),最低1024词符。选择“自动”不会请求模型思维链。",
303 "Allocates a portion of the response length for thinking (Flash 2.5/Pro 2.5) (min: 0/128 tokens, low: 10%, medium: 25%, high: 50%, max: 24576/32768 tokens). Auto lets the model decide.": "将最大回复空间的一部分分配给思维链(仅 2.5 Flash / 2.5 Pro 模型)(极低:0/128词符,低:10%,中:25%,高:50%,极高:24576/32768词符),最低1024词符。选择“自动”会让模型自己决定。",
279 "Assistant Prefill": "AI预填",304 "Assistant Prefill": "AI预填",
280 "Expand the editor": "展开编辑器",305 "Expand the editor": "展开编辑器",
281 "Start Claude's answer with...": "以如下内容开始Claude的回答...",306 "Start Claude's answer with...": "以如下内容开始Claude的回答...",
282 "Assistant Impersonation Prefill": "AI帮答预填",307 "Assistant Impersonation Prefill": "AI帮答预填",
283 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",308 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",
284 "Confirm token parsing with": "确认使用以下工具进行词符解析",309 "Confirm token parsing with": "确认使用以下工具进行词符解析",
285 "Tokenizer": "词符化器",310 "Tokenizer": "分词器",
286 "New preset": "新预设",311 "New preset": "新预设",
287 "Delete preset": "删除预设",312 "Delete preset": "删除预设",
288 "View / Edit bias preset": "查看/编辑偏置预设",313 "View / Edit bias preset": "查看/编辑偏置预设",
@@ -305,14 +330,17 @@
305 "Adjust response length to worker capabilities": "根据工作单元能力调整响应长度",330 "Adjust response length to worker capabilities": "根据工作单元能力调整响应长度",
306 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "可以通过仅排队认证的工作单元来帮助处理不良回复。这可能会减慢回复速度。",331 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "可以通过仅排队认证的工作单元来帮助处理不良回复。这可能会减慢回复速度。",
307 "Trusted workers only": "仅信任的工作单元",332 "Trusted workers only": "仅信任的工作单元",
333 "Context": "上下文",
334 "Response": "回复",
308 "API key": "API密钥",335 "API key": "API密钥",
309 "Get it here:": "在此获取:",336 "Get it here:": "在此获取:",
310 "Register": "注册",337 "Register": "注册",
311 "View my Kudos": "查看我的荣誉",338 "View my Kudos": "查看我的荣誉",
312 "Enter": "输入",339 "Enter": "输入",
313 "to use anonymous mode.": "以使用匿名模式。",340 "to use anonymous mode.": "以使用匿名模式。",
314 "Clear your API key": "清除您的API密钥",341 "Save and connect": "保存并连接",
315 "For privacy reasons, your API key will be hidden after you reload the page.": "出于隐私原因,重新加载页面后您的 API 密钥将被隐藏。",342 "Manage API keys": "管理 API 密钥",
343 "For privacy reasons, your API key will be hidden after you click 'Connect'.": "出于隐私考虑,您的 API 密钥将会在点击“连接”后隐藏。",
316 "Models": "模型",344 "Models": "模型",
317 "Refresh models": "刷新模型",345 "Refresh models": "刷新模型",
318 "-- Horde models not loaded --": "-- Horde 模型未加载 --",346 "-- Horde models not loaded --": "-- Horde 模型未加载 --",
@@ -351,14 +379,16 @@
351 "Make sure you run it with": "确保您在运行时加上",379 "Make sure you run it with": "确保您在运行时加上",
352 "flag": "标志",380 "flag": "标志",
353 "Custom model (optional)": "自定义模型(可选)",381 "Custom model (optional)": "自定义模型(可选)",
354 "Featherless Model Selection": "Featherless Model Selection",382 "Featherless Model Selection": "Featherless 模型选择",
355 "Search...": "搜索...",383 "Search...": "搜索...",
356 "Search": "搜索",384 "Search": "搜索",
385 "Date Asc": "日期顺序",
386 "Date Desc": "日期倒序",
357 "category": "分类",387 "category": "分类",
358 "Top": "Top",388 "Top": "热门",
359 "New": "新建",389 "New": "新建",
360 "All": "All",390 "All": "全部",
361 "class": "All Classes",391 "All Classes": "所有分类",
362 "Toggle grid view": "切换网格视图",392 "Toggle grid view": "切换网格视图",
363 "No model description": "[无描述]",393 "No model description": "[无描述]",
364 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",394 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",
@@ -378,6 +408,7 @@
378 "Download": "下载",408 "Download": "下载",
379 "Tabby API key": "Tabby API 密钥",409 "Tabby API key": "Tabby API 密钥",
380 "Tabby Model": "Tabby 模型",410 "Tabby Model": "Tabby 模型",
411 "Experimental feature. Use at your own risk.": "实验性功能,请自行承担可能的风险。",
381 "must be set in Tabby's config.yml to switch models.": "必须在Tabby的config.yml内设置以切换模型",412 "must be set in Tabby's config.yml to switch models.": "必须在Tabby的config.yml内设置以切换模型",
382 "Use an admin API key.": "使用管理员API密钥。",413 "Use an admin API key.": "使用管理员API密钥。",
383 "koboldcpp API key (optional)": "koboldcpp API 密钥(可选)",414 "koboldcpp API key (optional)": "koboldcpp API 密钥(可选)",
@@ -397,8 +428,9 @@
397 "This will show up as your saved preset.": "这将显示为您保存的预设。",428 "This will show up as your saved preset.": "这将显示为您保存的预设。",
398 "Proxy Server URL": "代理服务器 URL",429 "Proxy Server URL": "代理服务器 URL",
399 "Alternative server URL (leave empty to use the default value).": "备用服务器 URL(留空以使用默认值)。",430 "Alternative server URL (leave empty to use the default value).": "备用服务器 URL(留空以使用默认值)。",
400 "Doesn't work? Try adding": "不起作用?在末尾添加",431 "Doesn't work? Try adding": "不行?在 URL 末尾添加",
401 "at the end!": "试试!",432 "at the end!": "试试!",
433 "suffix will be added automatically.": "的后缀会被自动补全。",
402 "Proxy Password": "代理密码",434 "Proxy Password": "代理密码",
403 "Will be used as a password for the proxy instead of API key.": "将用作代理的密码,而不是 API 密钥。",435 "Will be used as a password for the proxy instead of API key.": "将用作代理的密码,而不是 API 密钥。",
404 "Peek a password": "查看密码",436 "Peek a password": "查看密码",
@@ -418,8 +450,6 @@
418 "Get your key from": "从以下位置获取您的密钥",450 "Get your key from": "从以下位置获取您的密钥",
419 "Anthropic's developer console": "Anthropic 开发者控制台",451 "Anthropic's developer console": "Anthropic 开发者控制台",
420 "Claude Model": "Claude 模型",452 "Claude Model": "Claude 模型",
421 "Window AI Model": "Window AI 模型",
422 "Use extension settings": "使用扩展程序中的设定",
423 "Allow fallback routes Description": "如果所选模型无法响应您的请求,则自动选择备用模型。",453 "Allow fallback routes Description": "如果所选模型无法响应您的请求,则自动选择备用模型。",
424 "Allow fallback models": "允许后备模型",454 "Allow fallback models": "允许后备模型",
425 "Model Order": "OpenRouter 模型顺序",455 "Model Order": "OpenRouter 模型顺序",
@@ -428,25 +458,43 @@
428 "Context Size": "上下文长度",458 "Context Size": "上下文长度",
429 "Group by vendors": "按厂商分组",459 "Group by vendors": "按厂商分组",
430 "Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。",460 "Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。",
431 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "To use instruct formatting, switch to OpenRouter under Text Completion API.",461 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "要使用指导格式,请在 文字补全API 下切换到 OpenRouter。",
432 "AI21 API Key": "AI21 API 密钥",462 "AI21 API Key": "AI21 API 密钥",
433 "AI21 Model": "AI21 模型",463 "AI21 Model": "AI21 模型",
434 "Google AI Studio API Key": "Google AI Studio API 密钥",464 "Google AI Studio API Key": "Google AI Studio API 密钥",
435 "Google Model": "Google 模型",465 "Google Model": "Google 模型",
466 "Google Vertex AI Configuration": "Google Vertex AI 配置",
467 "Authentication Mode": "验证模式:",
468 "Express Mode (API Key)": "快速模式(API 密钥)",
469 "Full Version (Service Account)": "完整版本(Service Account)",
470 "(Express mode)": "(快速模式)",
471 "API Key": "API 密钥",
472 "Project ID": "项目ID:",
473 "Project ID is required when selecting regions other than the default (us-central1). You can find this in a model 404 error message.": "仅当选择非默认区域(us-central1)时才需要`项目ID`。\n 您可以在模型 404 错误消息中找到它。",
474 "Service Account Configuration": "服务帐户配置",
475 "Service Account JSON Content": "服务帐户 JSON 内容:",
476 "For privacy reasons, your Service Account JSON content will be hidden after you click 'Validate JSON'.": "出于隐私考虑,你的服务账号 JSON 内容将在点击“验证JSON”后隐藏。",
477 "Validate JSON": "验证JSON",
478 "Region": "地区:",
479 "View available regions and models": "查看可用地区和模型",
436 "MistralAI API Key": "MistralAI API 密钥",480 "MistralAI API Key": "MistralAI API 密钥",
437 "MistralAI Model": "MistralAI 模型",481 "MistralAI Model": "MistralAI 模型",
438 "Groq API Key": "Groq API 密钥",482 "Groq API Key": "Groq API 密钥",
439 "Groq Model": "Groq 模型",483 "Groq Model": "Groq 模型",
440 "NanoGPT API Key": "NanoGPT API Key",484 "Electron Hub API Key": "Electron Hub API 密钥",
441 "NanoGPT Model": "NanoGPT Model",485 "Electron Hub Model": "Electron Hub 模型",
486 "NanoGPT API Key": "NanoGPT API 密钥",
487 "NanoGPT Model": "NanoGPT 模型",
442 "DeepSeek API Key": "DeepSeek API 密钥",488 "DeepSeek API Key": "DeepSeek API 密钥",
443 "DeepSeek Model": "DeepSeek 模型",489 "DeepSeek Model": "DeepSeek 模型",
490 "Fireworks AI API Key": "Fireworks AI API 密钥",
491 "Fireworks AI Model": "Fireworks AI 模型",
492 "CometAPI API Key": "CometAPI API 密钥",
493 "CometAPI Model": "CometAPI 模型",
444 "Perplexity API Key": "Perplexity API 密钥",494 "Perplexity API Key": "Perplexity API 密钥",
445 "Perplexity Model": "Perplexity 模型",495 "Perplexity Model": "Perplexity 模型",
446 "Cohere API Key": "Cohere API 密钥",496 "Cohere API Key": "Cohere API 密钥",
447 "Cohere Model": "Cohere 模型",497 "Cohere Model": "Cohere 模型",
448 "Block Entropy API Key": "Block Entropy API 密钥",
449 "Select a Model": "选择一个模型",
450 "Custom Endpoint (Base URL)": "自定义端点(基础 URL)",498 "Custom Endpoint (Base URL)": "自定义端点(基础 URL)",
451 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",499 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
452 "Custom API Key": "自定义 API 密钥",500 "Custom API Key": "自定义 API 密钥",
@@ -454,15 +502,28 @@
454 "Enter a Model ID": "输入模型名",502 "Enter a Model ID": "输入模型名",
455 "Example: gpt-4o": "例如:gpt-4o",503 "Example: gpt-4o": "例如:gpt-4o",
456 "Available Models": "可用模型",504 "Available Models": "可用模型",
505 "xAI API Key": "xAI API 密钥",
506 "xAI Model": "xAI 模型",
507 "AI/ML API Key": "AI/ML API 密钥",
508 "AI/ML Model": "AI/ML 模型",
509 "Pollinations Model": "Pollinations 模型",
510 "Provided free of charge by Pollinations.AI": "由 Pollinations.AI 免费提供",
511 "Avoid sending sensitive information. Provider's outputs may include ads.": "请避免发送敏感信息。输出可能有提供商的广告。",
512 "Moonshot AI API Key": "Moonshot AI API 密钥",
513 "Moonshot AI Model": "Moonshot AI 模型",
457 "Prompt Post-Processing": "提示词后处理",514 "Prompt Post-Processing": "提示词后处理",
458 "Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",515 "Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",
459 "prompt_post_processing_none": "未选择",516 "prompt_post_processing_none": "未选择",
517 "prompt_post_processing_merge_tools": "合并相同角色连续的发言(含工具)",
518 "prompt_post_processing_semi_tools": "半严格(强制对话角色交替)(含工具)",
519 "prompt_post_processing_strict_tools": "严格(强制对话角色交替、用户最先)(含工具)",
460 "prompt_post_processing_merge": "合并相同角色连续的发言",520 "prompt_post_processing_merge": "合并相同角色连续的发言",
461 "prompt_post_processing_semi": "半严格(强制对话角色交替)",521 "prompt_post_processing_semi": "半严格(强制对话角色交替)",
462 "prompt_post_processing_strict": "严格(强制对话角色交替、用户最先)",522 "prompt_post_processing_strict": "严格(强制对话角色交替、用户最先)",
523 "prompt_post_processing_single": "单一用户消息(无工具)",
463 "Additional Parameters": "附加参数",524 "Additional Parameters": "附加参数",
464 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此消耗额度!",
465 "Test Message": "发送测试消息",525 "Test Message": "发送测试消息",
526 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此消耗额度!",
466 "Auto-connect to Last Server": "自动连接到上次的服务器",527 "Auto-connect to Last Server": "自动连接到上次的服务器",
467 "Missing key": "❌ 缺少密钥",528 "Missing key": "❌ 缺少密钥",
468 "Key saved": "密钥已保存",529 "Key saved": "密钥已保存",
@@ -484,6 +545,14 @@
484 "Restore current template": "还原当前模板",545 "Restore current template": "还原当前模板",
485 "Delete the template": "删除模板",546 "Delete the template": "删除模板",
486 "Story String": "故事字符串",547 "Story String": "故事字符串",
548 "Position:": "位置:",
549 "Default (top of context)": "默认(上下文顶部)",
550 "In-chat @ Depth": "聊天的特定深度",
551 "Depth:": "深度:",
552 "Role:": "身份:",
553 "System": "系统",
554 "User": "用户",
555 "Assistant": "助手",
487 "Example Separator": "示例分隔符",556 "Example Separator": "示例分隔符",
488 "Chat Start": "聊天开始",557 "Chat Start": "聊天开始",
489 "Context Formatting": "上下文格式",558 "Context Formatting": "上下文格式",
@@ -497,7 +566,6 @@
497 "Separators as Stop Strings": "分隔符作为终止字符串",566 "Separators as Stop Strings": "分隔符作为终止字符串",
498 "Add Character and User names to a list of stopping strings.": "将角色和用户名添加到停止字符串列表中。",567 "Add Character and User names to a list of stopping strings.": "将角色和用户名添加到停止字符串列表中。",
499 "Names as Stop Strings": "名称作为终止字符串",568 "Names as Stop Strings": "名称作为终止字符串",
500 "context_allow_post_history_instructions": "如果在角色卡中定义并且启用了“首选角色卡说明”,则在提示末尾包含后历史说明。\n不建议在文本补全模型中使用此功能,否则会导致输出错误。",
501 "Instruct Template": "指导模板",569 "Instruct Template": "指导模板",
502 "instruct_derived": "如果可能,从模型元数据中获取",570 "instruct_derived": "如果可能,从模型元数据中获取",
503 "instruct_bind_to_context": "如果启用,上下文模板将根据所选的指导模板名称或偏好自动选择。",571 "instruct_bind_to_context": "如果启用,上下文模板将根据所选的指导模板名称或偏好自动选择。",
@@ -508,12 +576,19 @@
508 "instruct_template_activation_regex_desc": "当连接到API或选择模型时,若模型名称与给定的正则表达式匹配,自动启用此指导模板。",576 "instruct_template_activation_regex_desc": "当连接到API或选择模型时,若模型名称与给定的正则表达式匹配,自动启用此指导模板。",
509 "Wrap Sequences with Newline": "用换行符包裹序列",577 "Wrap Sequences with Newline": "用换行符包裹序列",
510 "Replace Macro in Sequences": "替换序列中的宏",578 "Replace Macro in Sequences": "替换序列中的宏",
579 "Sequences as Stop Strings": "将序列用作终止字符串",
511 "Skip Example Dialogues Formatting": "跳过示例对话格式化",580 "Skip Example Dialogues Formatting": "跳过示例对话格式化",
512 "Include Names": "包括名称",581 "Include Names": "包括名称",
513 "Never": "永不",582 "Never": "永不",
514 "Groups and Past Personas": "群聊和过去的用户角色",583 "Groups and Past Personas": "群聊和过去的用户设定",
515 "Always": "永远",584 "Always": "永远",
516 "Instruct Sequences": "指令序列",585 "Instruct Sequences": "指令序列",
586 "Story String Sequences": "故事字符串序列",
587 "Used in Default position only.": "仅在默认位置使用。",
588 "Inserted before a Story String.": "插入在故事字符串之前。",
589 "Story String Prefix": "故事字符串前缀",
590 "Inserted after a Story String.": "插入在故事字符串之后。",
591 "Story String Suffix": "故事字符串后缀",
517 "User Message Sequences": "用户消息序列",592 "User Message Sequences": "用户消息序列",
518 "Inserted before a User message and as a last prompt line when impersonating.": "插入到用户消息之前并作为模拟时的最后一行提示词。",593 "Inserted before a User message and as a last prompt line when impersonating.": "插入到用户消息之前并作为模拟时的最后一行提示词。",
519 "User Prefix": "用户消息前缀",594 "User Prefix": "用户消息前缀",
@@ -531,11 +606,6 @@
531 "System Suffix": "系统消息后缀",606 "System Suffix": "系统消息后缀",
532 "If enabled, System Sequences will be the same as User Sequences.": "如果启用,系统序列将与用户序列相同。",607 "If enabled, System Sequences will be the same as User Sequences.": "如果启用,系统序列将与用户序列相同。",
533 "System same as User": "系统与用户相同",608 "System same as User": "系统与用户相同",
534 "System Prompt Sequences": "系统提示词序列",
535 "Inserted before a System prompt.": "插入到系统提示词之前。",
536 "System Prompt Prefix": "系统提示词前缀",
537 "Inserted after a System prompt.": "在系统提示词后插入。",
538 "System Prompt Suffix": "系统提示词后缀",
539 "Misc. Sequences": "杂项序列",609 "Misc. Sequences": "杂项序列",
540 "Inserted before the first Assistant's message.": "插入到第一个助理的消息之前。",610 "Inserted before the first Assistant's message.": "插入到第一个助理的消息之前。",
541 "First Assistant Prefix": "第一个助理前缀",611 "First Assistant Prefix": "第一个助理前缀",
@@ -565,7 +635,7 @@
565 "Replace Macro in Stop Strings": "替换自定义停止字符串中的宏",635 "Replace Macro in Stop Strings": "替换自定义停止字符串中的宏",
566 "Token Padding": "词符填充",636 "Token Padding": "词符填充",
567 "Reasoning": "推理",637 "Reasoning": "推理",
568 "reasoning_auto_parse": "Automatically parse reasoning blocks from main content between the reasoning prefix/suffix. Both fields must be defined and non-empty.",638 "reasoning_auto_parse": "自动从消息内容中提取由推理块前后缀包裹的推理块。前后缀都必须设置且不为空。",
569 "Auto-Parse": "自动解析",639 "Auto-Parse": "自动解析",
570 "reasoning_auto_expand": "自动展开推理内容块。",640 "reasoning_auto_expand": "自动展开推理内容块。",
571 "Auto-Expand": "自动展开",641 "Auto-Expand": "自动展开",
@@ -573,9 +643,10 @@
573 "Show Hidden": "显示隐藏内容",643 "Show Hidden": "显示隐藏内容",
574 "reasoning_add_to_prompts": "将已有的推理块添加到提示词。若需新增一个推理块,请使用消息编辑菜单。",644 "reasoning_add_to_prompts": "将已有的推理块添加到提示词。若需新增一个推理块,请使用消息编辑菜单。",
575 "Add to Prompts": "添加到提示词",645 "Add to Prompts": "添加到提示词",
576 "reasoning_max_additions": "Maximum number of reasoning blocks to be added per prompt, counting from the last message.",646 "reasoning_max_additions": "从最后一条消息开始,最多有多少个思维链可以被加入提示词。",
577 "Max": "最大值",647 "Max": "最大值",
578 "Reasoning Formatting": "推理内容格式化",648 "Reasoning Formatting": "推理内容格式化",
649 "Select your current Reasoning Template": "选择你当前的推理模板",
579 "reasoning_prefix": "插入在推理内容之前。",650 "reasoning_prefix": "插入在推理内容之前。",
580 "Prefix": "前缀",651 "Prefix": "前缀",
581 "reasoning_suffix": "插入在推理内容之后。",652 "reasoning_suffix": "插入在推理内容之后。",
@@ -583,6 +654,8 @@
583 "reasoning_separator": "插入在推理内容和消息内容之间。",654 "reasoning_separator": "插入在推理内容和消息内容之间。",
584 "Separator": "分隔符",655 "Separator": "分隔符",
585 "Miscellaneous": "杂项",656 "Miscellaneous": "杂项",
657 "Bind Model to Templates": "将模型与模板绑定",
658 "bind_model_templates_desc": "当连接到一个API或选择一个模型,且它们的名字与当前的指导和上下文模板名字匹配时,自动激活当前的指导和上下文模板。",
586 "Non-markdown strings": "非 Markdown 字符串",659 "Non-markdown strings": "非 Markdown 字符串",
587 "comma delimited,no spaces between": "以逗号分隔,无需空格",660 "comma delimited,no spaces between": "以逗号分隔,无需空格",
588 "Start Reply With": "以...开始回复",661 "Start Reply With": "以...开始回复",
@@ -623,20 +696,20 @@
623 "Alert On Overflow": "溢出警报",696 "Alert On Overflow": "溢出警报",
624 "or": "或",697 "or": "或",
625 "--- Pick to Edit ---": "--- 选择以编辑 ---",698 "--- Pick to Edit ---": "--- 选择以编辑 ---",
699 "Import World Info": "导入世界书",
700 "Export World Info": "导出世界书",
626 "Rename World Info": "重命名世界书",701 "Rename World Info": "重命名世界书",
702 "Duplicate World Info": "复制世界书",
703 "Delete World Info": "删除世界书",
704 "New Entry": "新条目",
627 "Open all Entries": "打开所有条目",705 "Open all Entries": "打开所有条目",
628 "Close all Entries": "关闭所有条目",706 "Close all Entries": "关闭所有条目",
629 "New Entry": "新条目",
630 "Fill empty Memo/Titles with Keywords": "使用关键字填充空的备忘录/标题",707 "Fill empty Memo/Titles with Keywords": "使用关键字填充空的备忘录/标题",
631 "Apply current sorting as Order": "应用当前排序作为顺序",708 "Apply current sorting as Order": "应用当前排序作为顺序",
632 "Import World Info": "导入世界书",
633 "Export World Info": "导出世界书",
634 "Duplicate World Info": "复制世界书",
635 "Delete World Info": "删除世界书",
636 "Priority": "优先级",709 "Priority": "优先级",
637 "Custom": "自定义",710 "Custom": "自定义",
638 "Title A-Z": "标题 A-Z",711 "Title A-Z": "标题 A 到 Z",
639 "Title Z-A": "标题 Z-A",712 "Title Z-A": "标题 Z 到 A",
640 "Tokens ↗": "词符 ↗",713 "Tokens ↗": "词符 ↗",
641 "Tokens ↘": "词符 ↘",714 "Tokens ↘": "词符 ↘",
642 "Depth ↗": "深度 ↗",715 "Depth ↗": "深度 ↗",
@@ -663,11 +736,19 @@
663 "Avatar Style:": "头像样式:",736 "Avatar Style:": "头像样式:",
664 "Circle": "圆形",737 "Circle": "圆形",
665 "Square": "正方形",738 "Square": "正方形",
739 "Rounded": "圆角",
666 "Rectangle": "矩形",740 "Rectangle": "矩形",
667 "Chat Style:": "聊天风格:",741 "Chat Style:": "聊天风格:",
668 "Flat": "扁平",742 "Flat": "扁平",
669 "Bubbles": "气泡",743 "Bubbles": "气泡",
670 "Document": "文档",744 "Document": "文档",
745 "Notifications:": "通知:",
746 "Top Left": "左上",
747 "Top Center": "顶部居中",
748 "Top Right": "右上",
749 "Bottom Left": "左下",
750 "Bottom Center": "底部居中",
751 "Bottom Right": "右下",
671 "Specify colors for your theme.": "指定您的主题的颜色。",752 "Specify colors for your theme.": "指定您的主题的颜色。",
672 "Theme Colors": "主题颜色",753 "Theme Colors": "主题颜色",
673 "Main Text": "主要文本",754 "Main Text": "主要文本",
@@ -726,6 +807,8 @@
726 "Show tagged character folders in the character list": "在角色列表中显示已标记的角色文件夹",807 "Show tagged character folders in the character list": "在角色列表中显示已标记的角色文件夹",
727 "Tags as Folders": "标签作为文件夹",808 "Tags as Folders": "标签作为文件夹",
728 "Tags_as_Folders_desc": "最近更改:标签必须在标签管理菜单中标记为文件夹才能显示。单击此处将其调出。",809 "Tags_as_Folders_desc": "最近更改:标签必须在标签管理菜单中标记为文件夹才能显示。单击此处将其调出。",
810 "Click the message text in the chat log to edit it.": "单机聊天记录中的消息就可以直接编辑。",
811 "Click to Edit": "单击编辑消息",
729 "Character Handling": "角色处理",812 "Character Handling": "角色处理",
730 "If set in the advanced character definitions, this field will be displayed in the characters list.": "如果在高级角色定义中设置,此字段将显示在角色列表中。",813 "If set in the advanced character definitions, this field will be displayed in the characters list.": "如果在高级角色定义中设置,此字段将显示在角色列表中。",
731 "Char List Subheader": "角色列表子标题",814 "Char List Subheader": "角色列表子标题",
@@ -745,6 +828,8 @@
745 "Prefer Character Card Instructions": "首选角色卡说明",828 "Prefer Character Card Instructions": "首选角色卡说明",
746 "never_resize_avatars_tooltip": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",829 "never_resize_avatars_tooltip": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
747 "Never resize avatars": "永不调整头像大小",830 "Never resize avatars": "永不调整头像大小",
831 "Allow animations for WEBP backgrounds. This is only a change for the selection menu.": "允许WEBP格式的背景动画。此更改仅对选择菜单生效",
832 "Animated background thumbnails": "背景缩略图动画",
748 "Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",833 "Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",
749 "Show avatar filenames": "显示头像文件名",834 "Show avatar filenames": "显示头像文件名",
750 "Hide character definitions from the editor panel behind a spoiler button": "在编辑器面板中,将角色定义隐藏在一个剧透按钮后面。",835 "Hide character definitions from the editor panel behind a spoiler button": "在编辑器面板中,将角色定义隐藏在一个剧透按钮后面。",
@@ -752,6 +837,8 @@
752 "Reload and redraw the currently open chat": "重新加载并重新渲染当前打开的聊天",837 "Reload and redraw the currently open chat": "重新加载并重新渲染当前打开的聊天",
753 "Reload Chat": "重新加载聊天",838 "Reload Chat": "重新加载聊天",
754 "Debug Menu": "调试菜单",839 "Debug Menu": "调试菜单",
840 "Find and delete backups, unused chats, files, images, etc.": "寻找和删除备份、未使用的聊天、文件、图片等。",
841 "Clean-Up": "清理",
755 "Smooth Streaming": "平滑流式传输",842 "Smooth Streaming": "平滑流式传输",
756 "Experimental feature. May not work for all backends.": "实验性功能。可能不适用于所有后端。",843 "Experimental feature. May not work for all backends.": "实验性功能。可能不适用于所有后端。",
757 "Slow": "慢",844 "Slow": "慢",
@@ -821,6 +908,8 @@
821 "Request token probabilities": "请求词符概率",908 "Request token probabilities": "请求词符概率",
822 "In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "在群聊中,突出显示当前排队等待生成响应的角色以及他们响应的顺序。",909 "In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "在群聊中,突出显示当前排队等待生成响应的角色以及他们响应的顺序。",
823 "Show group chat queue": "显示群聊队列",910 "Show group chat queue": "显示群聊队列",
911 "Always render style tags from greetings, even if the message is unloaded due to lazy loading.": "始终渲染问候消息里的样式标签,即便消息因懒加载策略还未被加载。",
912 "Pin greeting message styles": "固定问候消息样式",
824 "Automatically reject and re-generate AI message based on configurable criteria": "根据可配置的条件自动拒绝并重新生成AI消息",913 "Automatically reject and re-generate AI message based on configurable criteria": "根据可配置的条件自动拒绝并重新生成AI消息",
825 "Auto-swipe": "自动滑动",914 "Auto-swipe": "自动滑动",
826 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "启用自动滑动功能。仅当启用自动滑动时,本节中的设置才会生效",915 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "启用自动滑动功能。仅当启用自动滑动时,本节中的设置才会生效",
@@ -835,6 +924,10 @@
835 "Allow for Chat Completion APIs": "允许使用聊天补全API",924 "Allow for Chat Completion APIs": "允许使用聊天补全API",
836 "Target length (tokens)": "目标长度(以词符数计)",925 "Target length (tokens)": "目标长度(以词符数计)",
837 "AutoComplete Settings": "自动补全设置",926 "AutoComplete Settings": "自动补全设置",
927 "Visibility": "可见性",
928 "Don't show": "不显示",
929 "Input length > 1": "输入长度 > 1",
930 "Always show": "始终显示",
838 "Automatically hide details": "自动隐藏详细信息",931 "Automatically hide details": "自动隐藏详细信息",
839 "Determines how entries are found for autocomplete.": "确定如何找到自动补全的条目。",932 "Determines how entries are found for autocomplete.": "确定如何找到自动补全的条目。",
840 "Autocomplete Matching": "匹配",933 "Autocomplete Matching": "匹配",
@@ -863,8 +956,7 @@
863 "stscript_parser_flag_replace_getvar_label": "防止 {{getvar::}} {{getglobalvar::}} 宏具有自动评估的文字宏类值。\n例如,“{{newline}}”保留为文字字符串“{{newline}}”\n\n(这是通过在内部用范围变量替换 {{getvar::}} {{getglobalvar::}} 宏来实现的。)",956 "stscript_parser_flag_replace_getvar_label": "防止 {{getvar::}} {{getglobalvar::}} 宏具有自动评估的文字宏类值。\n例如,“{{newline}}”保留为文字字符串“{{newline}}”\n\n(这是通过在内部用范围变量替换 {{getvar::}} {{getglobalvar::}} 宏来实现的。)",
864 "REPLACE_GETVAR": "替换GETVAR",957 "REPLACE_GETVAR": "替换GETVAR",
865 "Change Background Image": "更改背景图片",958 "Change Background Image": "更改背景图片",
866 "Background Image": "背景图片",959 "Backgrounds": "背景",
867 "Filter": "搜索",
868 "Background Fitting": "背景图片尺寸",960 "Background Fitting": "背景图片尺寸",
869 "Classic": "经典",961 "Classic": "经典",
870 "Cover": "填充",962 "Cover": "填充",
@@ -873,6 +965,7 @@
873 "Center": "居中",965 "Center": "居中",
874 "Automatically select a background based on the chat context": "根据聊天上下文自动选择背景",966 "Automatically select a background based on the chat context": "根据聊天上下文自动选择背景",
875 "Auto-select": "自动选择",967 "Auto-select": "自动选择",
968 "Add Background": "添加背景",
876 "System Backgrounds": "系统背景",969 "System Backgrounds": "系统背景",
877 "Chat Backgrounds": "聊天背景",970 "Chat Backgrounds": "聊天背景",
878 "bg_chat_hint_1": "使用生成的聊天背景",971 "bg_chat_hint_1": "使用生成的聊天背景",
@@ -880,43 +973,50 @@
880 "Extensions": "扩展",973 "Extensions": "扩展",
881 "Notify on extension updates": "在扩展更新时通知",974 "Notify on extension updates": "在扩展更新时通知",
882 "Manage extensions": "管理扩展",975 "Manage extensions": "管理扩展",
883 "Import Extension From Git Repo": "从Git存储库导入扩展",976 "Import Extension From Git Repo": "从 Git 仓库导入扩展",
884 "Install extension": "安装扩展",977 "Install extension": "安装扩展",
978 "(DEPRECATED)": "(已弃用)",
885 "Extras API:": "扩展API:",979 "Extras API:": "扩展API:",
886 "Auto-connect": "自动连接",980 "Auto-connect": "自动连接",
887 "Extras API URL": "附加 API URL",981 "Extras API URL": "附加 API URL",
888 "Extras API key (optional)": "扩展API密钥(可选)",982 "Extras API key (optional)": "扩展API密钥(可选)",
889 "Persona Management": "用户角色管理",983 "Persona Management": "用户设定管理",
890 "Click for stats!": "点击查看统计!",984 "Click for stats!": "点击即可查看统计~",
891 "Usage Stats": "使用统计",985 "Usage Stats": "使用统计",
892 "Backup your personas to a file": "将用户角色备份到文件中",986 "Backup your personas to a file": "将用户设定备份到文件中",
893 "Backup": "备份",987 "Backup": "备份",
894 "Restore your personas from a file": "从文件中恢复用户角色",988 "Restore your personas from a file": "从文件中恢复用户设定",
895 "Restore": "恢复",989 "Restore": "恢复",
896 "Create a dummy persona": "创建空白用户角色",990 "Create a dummy persona": "创建空白用户设定",
897 "Create": "创建",991 "Create": "创建",
898 "No persona description": "[没有描述]",992 "No persona description": "[没有人设描述]",
899 "Name": "名称",993 "Current Persona": "当前人设",
900 "Enter your name": "输入您的名字",994 "Rename Persona": "重命名人设",
901 "Click to set a new User Name": "点击设置新的用户名",
902 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "单击以将您选择的用户角色锁定到当前聊天。再次单击以移除锁定。",
903 "Click to set user name for all messages": "点击为所有消息设置用户名",995 "Click to set user name for all messages": "点击为所有消息设置用户名",
904 "Persona Lore Alt+Click to open the lorebook": "Persona Lore\nAlt+Click to open the lorebook",996 "Persona Lore Alt+Click to open the lorebook": "Persona Lore\nAlt+Click to open the lorebook",
905 "Persona Description": "用户角色描述",997 "Change Persona Image": "更改人设图",
998 "Duplicate Persona": "复制人设",
999 "Delete Persona": "删除人设",
1000 "Persona Description": "用户设定描述",
906 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "示例:[{{user}}是一个28岁的罗马尼亚猫娘。]",1001 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "示例:[{{user}}是一个28岁的罗马尼亚猫娘。]",
907 "Tokens persona description": "用户角色描述词符数",1002 "Position": "插入位置",
908 "Position:": "位置:",1003 "Tokens persona description": "人设词符数",
909 "None (disabled)": "无(已禁用)",1004 "None (disabled)": "无(已禁用)",
910 "In Story String / Prompt Manager": "在故事字符串/提示词管理器中",1005 "In Story String / Prompt Manager": "在故事字符串/提示词管理器中",
911 "Top of Author's Note": "作者注的顶部",1006 "Top of Author's Note": "作者注的顶部",
912 "Bottom of Author's Note": "作者注的底部",1007 "Bottom of Author's Note": "作者注的底部",
913 "In-chat @ Depth": "聊天的特定深度",1008 "Connections": "链接",
914 "Depth:": "深度:",1009 "Click to select this as default persona for the new chats. Click again to remove it.": "点击将此人设设置为新聊天的默认人设。再次点击以移除。",
915 "Role:": "身份:",1010 "Click to lock your selected persona to the current character. Click again to remove the lock.": "点击将选择的用户设定与当前角色绑定。再次点击以解绑。",
916 "System": "系统",1011 "Character": "角色",
917 "User": "用户",1012 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "点击将选择的人设与当前聊天绑定。再次点击以解绑。",
918 "Assistant": "助手",1013 "Chat": "聊天",
919 "Show notifications on switching personas": "切换用户角色时显示通知",1014 "Global Settings": "全局设置",
1015 "Show notifications on switching personas": "切换用户设定时显示通知",
1016 "When multiple personas are connected to a character, a popup will appear to select which one to use": "当多个用户设定与一个角色绑定时,会弹出一个弹窗让用户选择使用哪一个。",
1017 "Allow multiple persona connections per character": "允许每个角色与多个用户设定绑定",
1018 "Whenever a persona is selected, it will be locked to the current chat and automatically selected when the chat is opened.": "当一个用户设定被选择,它会被自动绑定到当前聊天,并在此聊天后续打开时被自动选中。",
1019 "Auto-lock a chosen persona to the chat": "自动将选择的用户设定绑定到聊天",
920 "Character Management": "角色管理",1020 "Character Management": "角色管理",
921 "Locked = Character Management panel will stay open": "锁定 = 角色管理面板将保持打开状态",1021 "Locked = Character Management panel will stay open": "锁定 = 角色管理面板将保持打开状态",
922 "Select/Create Characters": "选择/创建角色",1022 "Select/Create Characters": "选择/创建角色",
@@ -934,8 +1034,9 @@
934 "Click to select a new avatar for this character": "单击以为此角色选择新的头像",1034 "Click to select a new avatar for this character": "单击以为此角色选择新的头像",
935 "Add to Favorites": "添加到收藏夹",1035 "Add to Favorites": "添加到收藏夹",
936 "Advanced Definition": "高级定义",1036 "Advanced Definition": "高级定义",
937 "world_button_title": "Character Lore\n\nClick to load\nShift-click to open 'Link to World Info' popup",1037 "world_button_title": "角色世界书\n\n单击加载\nShift+单击打开“链接到世界信息”弹出窗口",
938 "Chat Lore Alt+Click to open the lorebook": "Chat Lore\nAlt+Click to open the lorebook",1038 "Chat Lore Alt+Click to open the lorebook": "聊天世界书\nAlt+单击打开世界书",
1039 "Connected Personas": "绑定的用户设定",
939 "Export and Download": "导出并下载",1040 "Export and Download": "导出并下载",
940 "Duplicate Character": "复制角色",1041 "Duplicate Character": "复制角色",
941 "Create Character": "创建角色",1042 "Create Character": "创建角色",
@@ -949,11 +1050,14 @@
949 "Link to Source": "来源链接",1050 "Link to Source": "来源链接",
950 "Replace / Update": "替换 / 更新",1051 "Replace / Update": "替换 / 更新",
951 "Import Tags": "导入标签",1052 "Import Tags": "导入标签",
1053 "Set / Unset as Welcome Page Assistant": "设置 / 取消 为欢迎页面助理",
952 "Search / Create Tags": "搜索/创建标签",1054 "Search / Create Tags": "搜索/创建标签",
953 "View all tags": "查看所有标签",1055 "View all tags": "查看所有标签",
954 "Creator's Notes": "创作者的注释",1056 "Creator's Notes": "创作者的注释",
955 "Character details are hidden.": "角色详情已隐藏。",1057 "Allow / Forbid the use of global styles for this character.": "允许 / 禁止 该角色使用全局样式。",
956 "Show / Hide Description and First Message": "显示/隐藏描述和第一条消息",1058 "Show / Hide Description and First Message": "显示/隐藏描述和第一条消息",
1059 "No Creator's Notes provided.": "无创作者注释",
1060 "Character details are hidden.": "角色详情已隐藏。",
957 "Character Description": "角色描述",1061 "Character Description": "角色描述",
958 "Click to allow/forbid the use of external media for this character.": "单击以允许/禁止此角色使用外部媒体。",1062 "Click to allow/forbid the use of external media for this character.": "单击以允许/禁止此角色使用外部媒体。",
959 "Ext. Media": "扩展媒体",1063 "Ext. Media": "扩展媒体",
@@ -970,6 +1074,7 @@
970 "Manual": "手动",1074 "Manual": "手动",
971 "Natural order": "自然顺序",1075 "Natural order": "自然顺序",
972 "List order": "从上到下",1076 "List order": "从上到下",
1077 "Pooled order": "随机轮流顺序",
973 "Group generation handling mode": "群组生成处理模式",1078 "Group generation handling mode": "群组生成处理模式",
974 "Swap character cards": "交换角色卡",1079 "Swap character cards": "交换角色卡",
975 "Join character cards (exclude muted)": "加入角色卡(不包括被禁言的)",1080 "Join character cards (exclude muted)": "加入角色卡(不包括被禁言的)",
@@ -987,7 +1092,9 @@
987 "Auto Mode delay": "自动模式延迟",1092 "Auto Mode delay": "自动模式延迟",
988 "Hide Muted Member Sprites": "隐藏拼贴头像中被禁言的成员",1093 "Hide Muted Member Sprites": "隐藏拼贴头像中被禁言的成员",
989 "Current Members": "当前成员",1094 "Current Members": "当前成员",
1095 "Group is empty.": "暂无成员",
990 "Add Members": "添加成员",1096 "Add Members": "添加成员",
1097 "No characters available": "无可用角色",
991 "Create New Character": "新建角色",1098 "Create New Character": "新建角色",
992 "Import Character from File": "从文件导入角色",1099 "Import Character from File": "从文件导入角色",
993 "Import content from external URL": "从外部URL导入内容",1100 "Import content from external URL": "从外部URL导入内容",
@@ -1004,15 +1111,13 @@
1004 "Most tokens": "最多词符",1111 "Most tokens": "最多词符",
1005 "Least tokens": "最少词符",1112 "Least tokens": "最少词符",
1006 "Random": "随机",1113 "Random": "随机",
1114 "Toggle search bar": "切换搜索栏",
1007 "Toggle character grid view": "切换角色网格视图",1115 "Toggle character grid view": "切换角色网格视图",
1008 "Bulk_edit_characters": "批量编辑角色",1116 "Bulk_edit_characters": "批量编辑角色",
1009 "Bulk select all characters": "批量选择所有角色",1117 "Bulk select all characters": "批量选择所有角色",
1010 "Bulk delete characters": "批量删除角色",1118 "Bulk delete characters": "批量删除角色",
1011 "Bind user name to that avatar": "将用户名称绑定到该头像",1119 "Persona is locked to the current chat": "用户设定已被绑定到当前聊天",
1012 "Change persona image": "更改用户角色头像",1120 "Persona is locked to the current character": "用户设定已被绑定到当前角色",
1013 "Select this as default persona for the new chats.": "选择此项作为新聊天的默认用户角色。",
1014 "Duplicate persona": "复制用户角色",
1015 "Delete persona": "删除用户角色",
1016 "popup-button-save": "保存",1121 "popup-button-save": "保存",
1017 "popup-button-yes": "是",1122 "popup-button-yes": "是",
1018 "popup-button-no": "否",1123 "popup-button-no": "否",
@@ -1058,10 +1163,10 @@
1058 "Chat History": "聊天记录",1163 "Chat History": "聊天记录",
1059 "Import Chat": "导入聊天",1164 "Import Chat": "导入聊天",
1060 "Copy to system backgrounds": "复制到系统背景",1165 "Copy to system backgrounds": "复制到系统背景",
1061 "Rename background": "重命名背景",
1062 "Lock": "锁定",1166 "Lock": "锁定",
1063 "Unlock": "解锁",1167 "Unlock": "解锁",
1064 "Delete background": "删除背景",1168 "Rename Background": "重命名背景",
1169 "Delete Background": "删除背景",
1065 "Select a World Info file for": "选择一个世界书文件给",1170 "Select a World Info file for": "选择一个世界书文件给",
1066 "Primary Lorebook": "主要知识书",1171 "Primary Lorebook": "主要知识书",
1067 "A selected World Info will be bound to this character as its own Lorebook.": "所选的世界信息将会于该角色绑定,作为该角色自己的知识书",1172 "A selected World Info will be bound to this character as its own Lorebook.": "所选的世界信息将会于该角色绑定,作为该角色自己的知识书",
@@ -1076,30 +1181,8 @@
1076 "Delete chat file": "删除聊天文件",1181 "Delete chat file": "删除聊天文件",
1077 "Drag to reorder tag": "拖动以排序",1182 "Drag to reorder tag": "拖动以排序",
1078 "Use tag as folder": "标记为文件夹",1183 "Use tag as folder": "标记为文件夹",
1079 "Hide on character card": "在角色卡上隐藏",1184 "tag_entries": "标签条目",
1080 "Delete tag": "删除标签",1185 "Delete tag": "删除标签",
1081 "Toggle entry's active state.": "切换条目激活状态。",
1082 "Entry Title/Memo": "条目标题/备忘录",
1083 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "世界书条目状态:\r🔵 永久\r🟢 关键词\r🔗 向量化",
1084 "WI_Entry_Status_Constant": "永久",
1085 "WI_Entry_Status_Normal": "关键词",
1086 "WI_Entry_Status_Vectorized": "向量化",
1087 "T_Position": "↑Char:在角色定义之前\n↓Char:在角色定义之后\n↑AN:在作者注释之前\n↓AN:在作者注释之后\n@D:在深度D处",
1088 "Before Char Defs": "角色定义之前",
1089 "After Char Defs": "角色定义之后",
1090 "Before EM": "↑EM",
1091 "After EM": "↓EM",
1092 "Before AN": "作者注释之前",
1093 "After AN": "作者注释之后",
1094 "at Depth System": "@D ⚙​​️",
1095 "at Depth User": "@D 👤",
1096 "at Depth AI": "@D 🤖",
1097 "Depth": "深度",
1098 "Order:": "顺序:",
1099 "Order": "顺序",
1100 "Trigger %:": "触发 %:",
1101 "Duplicate world info entry": "重复的世界信息条目",
1102 "Delete world info entry": "删除世界信息条目",
1103 "Comma separated (required)": "逗号分隔(必填)",1186 "Comma separated (required)": "逗号分隔(必填)",
1104 "Primary Keywords": "主要关键字",1187 "Primary Keywords": "主要关键字",
1105 "Keywords or Regexes": "关键字或正则表达式",1188 "Keywords or Regexes": "关键字或正则表达式",
@@ -1119,17 +1202,22 @@
1119 "Use global": "使用全局",1202 "Use global": "使用全局",
1120 "Yes": "是",1203 "Yes": "是",
1121 "No": "否",1204 "No": "否",
1122 "Whole Words": "Whole Words",1205 "Whole Words": "完整单词",
1123 "Group Scoring": "Group Scoring",1206 "Group Scoring": "组评分",
1124 "Can be used to automatically activate Quick Replies": "可用于自动激活快速回复",1207 "Can be used to automatically activate Quick Replies": "可用于自动激活快速回复",
1125 "Automation ID": "自动化ID",1208 "Automation ID": "自动化ID",
1126 "( None )": "(没有任何)",1209 "( None )": "(没有任何)",
1127 "delay_until_recursion_level": "Defines delay levels for recursive scans.\r\rInitially, only the first level (smallest number) will match.\rOnce no matches are found, the next level becomes eligible for matching.\rThis repeats until all levels are checked.\r\rTied to the \"Delay until recursion\" setting.",1210 "delay_until_recursion_level": "定义递归扫描的延迟级别。\n最初,只有第一级(最小数字)会匹配。\n一旦未找到匹配项,下一个级别将变为匹配的候选项。\n这将重复,直到检查所有级别。\n与“延迟到递归”设置相关联。",
1128 "Recursion Level": "递归等级",1211 "Recursion Level": "递归等级",
1129 "Content": "内容",1212 "Content": "内容",
1213 "This entry will not be recursively activated by other entries.": "此条目不会被其他条目递归激活。",
1130 "Non-recursable": "不可递归(不会被其他条目激活)",1214 "Non-recursable": "不可递归(不会被其他条目激活)",
1215 "This entry will not activate other entries recursively.": "此条目不会被其他条目激活。",
1131 "Prevent further recursion": "防止进一步递归",1216 "Prevent further recursion": "防止进一步递归",
1217 "This entry can only be activated on recursive checking.": "此条目只能在递归检查时被激活。",
1132 "Delay until recursion": "延迟到递归",1218 "Delay until recursion": "延迟到递归",
1219 "This entry will be included ignoring budget constraints, assuming all other checks pass.": "只要其他检查通过,此条目将无视回复限额,直接加入提示词中",
1220 "Ignore budget": "无视回复限额",
1133 "What this keyword should mean to the AI, sent verbatim": "这个关键词对AI的含义,逐字发送",1221 "What this keyword should mean to the AI, sent verbatim": "这个关键词对AI的含义,逐字发送",
1134 "Inclusion Group": "包含组",1222 "Inclusion Group": "包含组",
1135 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "包含组可确保每次仅激活组中的一项(如果触发了多项)。支持多个逗号分隔的组。文档:世界信息 - 包含组",1223 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "包含组可确保每次仅激活组中的一项(如果触发了多项)。支持多个逗号分隔的组。文档:世界信息 - 包含组",
@@ -1151,28 +1239,66 @@
1151 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切换角色/标签筛选方式,将列出的角色和标签排除在匹配范围之外",1239 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切换角色/标签筛选方式,将列出的角色和标签排除在匹配范围之外",
1152 "Exclude": "排除",1240 "Exclude": "排除",
1153 "-- Characters not found --": "-- 未找到角色 --",1241 "-- Characters not found --": "-- 未找到角色 --",
1242 "Filter to Generation Triggers": "筛选生成触发器",
1243 "Continue": "继续",
1244 "Impersonate": "AI 帮答",
1245 "Swipe": "滑动",
1246 "Regenerate": "重新生成",
1247 "Quiet": "静默",
1154 "Selective": "选择性",1248 "Selective": "选择性",
1155 "Use Probability": "使用概率",1249 "Use Probability": "使用概率",
1156 "Add Memo": "添加备忘录",1250 "Add Memo": "添加备忘录",
1251 "Additional Matching Sources": "额外匹配来源",
1252 "Character Personality": "角色性格",
1253 "Toggle entry's active state.": "切换条目激活状态。",
1254 "Entry Title/Memo": "条目标题/备忘录",
1255 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "世界书条目状态:\r🔵 永久\r🟢 关键词\r🔗 向量化",
1256 "WI_Entry_Status_Constant": "永久",
1257 "WI_Entry_Status_Normal": "关键词",
1258 "WI_Entry_Status_Vectorized": "向量化",
1259 "T_Position": "↑Char:在角色定义之前\n↓Char:在角色定义之后\n↑AN:在作者注释之前\n↓AN:在作者注释之后\n@D:在深度D处",
1260 "Before Char Defs": "角色定义之前",
1261 "After Char Defs": "角色定义之后",
1262 "Before EM": "示例消息前(↑EM)",
1263 "After EM": "示例消息后(↓EM)",
1264 "Before AN": "作者注释之前",
1265 "After AN": "作者注释之后",
1266 "at Depth System": "@D ⚙ [系统]在深度​​️",
1267 "at Depth User": "@D 👤 [用户]在深度",
1268 "at Depth AI": "@D 🤖 [AI]在深度",
1269 "Depth": "深度",
1270 "Order:": "顺序:",
1271 "Order": "顺序",
1272 "Trigger %:": "触发 %:",
1273 "Move/Copy Entry to Another Lorebook": "移动或复制条目到其他世界书",
1274 "Duplicate world info entry": "重复的世界信息条目",
1275 "Delete world info entry": "删除世界信息条目",
1276 "This character will be used as a welcome page assistant.": "此角色将作为欢迎页面助理。",
1157 "Text or token ids": "文本或 [token ID]",1277 "Text or token ids": "文本或 [token ID]",
1158 "Type here...": "在此处输入...",1278 "Type here...": "在此处输入...",
1159 "close": "关闭",1279 "close": "关闭",
1160 "prompt_manager_edit": "编辑",1280 "prompt_manager_edit": "编辑",
1161 "prompt_manager_name": "姓名",1281 "prompt_manager_name": "姓名",
1162 "A name for this prompt.": "此提示词的名称。",1282 "A name for this prompt.": "此提示词的名称。",
1163 "To whom this message will be attributed.": "此消息应归于谁。",
1164 "AI Assistant": "AI助手",1283 "AI Assistant": "AI助手",
1284 "To whom this message will be attributed.": "此消息应归于谁。",
1285 "Triggers": "触发器",
1286 "Filter to specific generation types.": "筛选到特定的生成类型。",
1165 "prompt_manager_position": "位置",1287 "prompt_manager_position": "位置",
1166 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "相对(相对于提示管理器中的其他提示)或在聊天中@深度。",
1167 "prompt_manager_relative": "相对",1288 "prompt_manager_relative": "相对",
1168 "prompt_manager_in_chat": "聊天中",1289 "prompt_manager_in_chat": "聊天中",
1290 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "相对(相对于提示管理器中的其他提示)或在聊天中@深度。",
1169 "prompt_manager_depth": "深度",1291 "prompt_manager_depth": "深度",
1170 "0 = after the last message, 1 = before the last message, etc.": "“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",1292 "0 = after the last message, 1 = before the last message, etc.": "“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",
1293 "prompt_manager_order": "排序",
1294 "prompt_manager_order_note": "来自其他来源(世界信息、作者注释等)的提示注入的默认顺序为 100。",
1295 "Ordered from low/top to high/bottom, and at same order: Assistant, User, System.": "从低/顶到高/底排序,并按相同顺序:助手、用户、系统。",
1171 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示词的内容是从其他地方提取的,无法在此处进行编辑。",1296 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示词的内容是从其他地方提取的,无法在此处进行编辑。",
1172 "Prompt": "提示词",1297 "Prompt": "提示词",
1173 "The prompt to be sent.": "要发送的提示词。",
1174 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使选择覆盖,此提示词也不能被角色卡覆盖。",1298 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使选择覆盖,此提示词也不能被角色卡覆盖。",
1175 "prompt_manager_forbid_overrides": "禁止覆盖",1299 "prompt_manager_forbid_overrides": "禁止覆盖",
1300 "Source:": "来源:",
1301 "The prompt to be sent.": "要发送的提示词。",
1176 "reset": "重置",1302 "reset": "重置",
1177 "save": "保存",1303 "save": "保存",
1178 "This message is invisible for the AI": "此消息对AI不可见",1304 "This message is invisible for the AI": "此消息对AI不可见",
@@ -1197,13 +1323,14 @@
1197 "Thought for some time": "思考了一会",1323 "Thought for some time": "思考了一会",
1198 "Confirm Edit": "确认",1324 "Confirm Edit": "确认",
1199 "Remove reasoning": "删除推理内容",1325 "Remove reasoning": "删除推理内容",
1200 "Cancel edit": "Cancel edit",1326 "Cancel edit": "取消编辑",
1327 "Collapse all reasoning blocks": "折叠所有推理块",
1201 "Copy reasoning": "复制推理内容",1328 "Copy reasoning": "复制推理内容",
1202 "Edit reasoning": "编辑推理内容",1329 "Edit reasoning": "编辑推理内容",
1203 "Enlarge": "放大",1330 "Expand and zoom": "展开并缩放",
1204 "Caption": "标题",1331 "Caption": "标题",
1205 "Swipe left": "Swipe left",1332 "Swipe left": "向左滑动",
1206 "Swipe right": "Swipe right",1333 "Swipe right": "向右滑动",
1207 "Welcome to SillyTavern!": "欢迎来到 SillyTavern!",1334 "Welcome to SillyTavern!": "欢迎来到 SillyTavern!",
1208 "SillyTavern is aimed at advanced users.": "SillyTavern 面向高级用户。",1335 "SillyTavern is aimed at advanced users.": "SillyTavern 面向高级用户。",
1209 "welcome_message_part_1": "阅读",1336 "welcome_message_part_1": "阅读",
@@ -1218,11 +1345,11 @@
1218 "onboarding_import": "导入",1345 "onboarding_import": "导入",
1219 "from supported sources or view": "来自受支持的来源或查看",1346 "from supported sources or view": "来自受支持的来源或查看",
1220 "Sample characters": "示例角色",1347 "Sample characters": "示例角色",
1221 "Your Persona": "您的用户角色",1348 "Your Persona": "您的用户设定",
1222 "Before you get started, you must select a persona name.": "在开始之前,您必须选择一个用户角色名称。",1349 "Before you get started, you must select a persona name.": "在开始之前,您必须起一个用户名。",
1223 "welcome_message_part_8": "您可随时通过",1350 "welcome_message_part_8": "您可随时通过",
1224 "welcome_message_part_9": "图标来更改此设置。",1351 "welcome_message_part_9": "图标来更改此设置。",
1225 "Persona Name:": "用户角色名称:",1352 "Persona Name:": "用户设定名称:",
1226 "Temporarily disable automatic replies from this character": "临时禁言此角色",1353 "Temporarily disable automatic replies from this character": "临时禁言此角色",
1227 "Enable automatic replies from this character": "解除禁言此角色",1354 "Enable automatic replies from this character": "解除禁言此角色",
1228 "Trigger a message from this character": "强制触发该角色发言",1355 "Trigger a message from this character": "强制触发该角色发言",
@@ -1231,6 +1358,8 @@
1231 "View character card": "查看角色卡片",1358 "View character card": "查看角色卡片",
1232 "Remove from group": "踢出群聊",1359 "Remove from group": "踢出群聊",
1233 "Add to group": "拉入群聊",1360 "Add to group": "拉入群聊",
1361 "in this group": "在此群组中",
1362 "Go back": "返回",
1234 "Alternate Greetings": "额外问候语",1363 "Alternate Greetings": "额外问候语",
1235 "Alternate_Greetings_desc": "开始新聊天时,这些按钮将显示为第一条消息的滑动选项。\n群成员可以选择其中之一来发起对话。",1364 "Alternate_Greetings_desc": "开始新聊天时,这些按钮将显示为第一条消息的滑动选项。\n群成员可以选择其中之一来发起对话。",
1236 "alternate_greetings_hint_1": "点击",1365 "alternate_greetings_hint_1": "点击",
@@ -1295,9 +1424,6 @@
1295 "Start new chat": "开始新聊天",1424 "Start new chat": "开始新聊天",
1296 "Manage chat files": "管理聊天文件",1425 "Manage chat files": "管理聊天文件",
1297 "Delete messages": "删除消息",1426 "Delete messages": "删除消息",
1298 "Regenerate": "重新生成",
1299 "Impersonate": "AI 帮答",
1300 "Continue": "继续",
1301 "extension_install_1": "若想从此页安装扩展程序,你需要提前安装",1427 "extension_install_1": "若想从此页安装扩展程序,你需要提前安装",
1302 "extension_install_2": "。",1428 "extension_install_2": "。",
1303 "extension_install_3": "点这个图标(",1429 "extension_install_3": "点这个图标(",
@@ -1314,6 +1440,7 @@
1314 "Load an asset list": "加载资产列表",1440 "Load an asset list": "加载资产列表",
1315 "Load Asset List": "加载资产列表",1441 "Load Asset List": "加载资产列表",
1316 "Characters": "人物",1442 "Characters": "人物",
1443 "Attach a file or image to a current chat.": "将文件或图像附加到当前聊天中。",
1317 "Attach a File": "附加文件",1444 "Attach a File": "附加文件",
1318 "Enter a URL or the ID of a Fandom wiki page to scrape:": "输入要抓取的 Fandom wiki 页面的 URL 或 ID:",1445 "Enter a URL or the ID of a Fandom wiki page to scrape:": "输入要抓取的 Fandom wiki 页面的 URL 或 ID:",
1319 "Examples:": "例:",1446 "Examples:": "例:",
@@ -1327,12 +1454,12 @@
1327 "These files will be available for extensions that support attachments (e.g. Vector Storage).": "这些文件将可用于支持附件的扩展(例如 Vector Storage)。",1454 "These files will be available for extensions that support attachments (e.g. Vector Storage).": "这些文件将可用于支持附件的扩展(例如 Vector Storage)。",
1328 "Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.": "支持的文件类型:纯文本、PDF、Markdown、HTML、EPUB。",1455 "Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.": "支持的文件类型:纯文本、PDF、Markdown、HTML、EPUB。",
1329 "Drag and drop files here to upload.": "将文件拖放到此处进行上传。",1456 "Drag and drop files here to upload.": "将文件拖放到此处进行上传。",
1330 "Date (Newest First)": "日期(最新日期)",1457 "Date (Newest First)": "日期(最新在先)",
1331 "Date (Oldest First)": "日期(最早日期)",1458 "Date (Oldest First)": "日期(最老在先)",
1332 "Name (A-Z)": "姓名(从 A 到 Z)",1459 "Name (A-Z)": "姓名(从 A 到 Z)",
1333 "Name (Z-A)": "姓名 (Z-A)",1460 "Name (Z-A)": "姓名(从 Z 到 A)",
1334 "Size (Smallest First)": "尺寸(最小)",1461 "Size (Smallest First)": "大小(最小在先)",
1335 "Size (Largest First)": "尺寸(最大尺寸优先)",1462 "Size (Largest First)": "大小(最大在先)",
1336 "Bulk Edit": "批量编辑",1463 "Bulk Edit": "批量编辑",
1337 "Select All": "全选",1464 "Select All": "全选",
1338 "Select None": "清空选择",1465 "Select None": "清空选择",
@@ -1360,16 +1487,21 @@
1360 "Model": "模型",1487 "Model": "模型",
1361 "currently_selected": "[当前选定]",1488 "currently_selected": "[当前选定]",
1362 "currently_loaded": "[当前正在加载]",1489 "currently_loaded": "[当前正在加载]",
1490 "Custom Model Tag": "自定义模型标签",
1491 "(for [Custom model] option)": "(自定义模型选项用)",
1363 "Allow reverse proxy": "允许反向代理",1492 "Allow reverse proxy": "允许反向代理",
1364 "Hint:": "提示:",1493 "Hint:": "提示:",
1365 "Set your API keys and endpoints in the 'API Connections' tab first.": "首先在“API 连接”选项卡中设置您的 API 密钥和端点。",1494 "Set your API keys and endpoints in the 'API Connections' tab first.": "首先在“API 连接”选项卡中设置您的 API 密钥和端点。",
1495 "Use secondary URL": "使用备用URL",
1496 "Secondary captioning endpoint URL": "备用的描述文字生成端点URL",
1366 "Caption Prompt": "图像描述提示词",1497 "Caption Prompt": "图像描述提示词",
1367 "Ask every time": "每次都询问",1498 "Ask every time": "每次都询问",
1368 "Message Template": "消息模板",1499 "Message Template": "消息模板",
1369 "(use _space": "(使用",1500 "(use _space": "(使用",
1370 "macro)": "宏指令)",1501 "macro)": "宏指令)",
1371 "Automatically caption images": "自动为图像添加标题",1502 "Automatically caption images": "自动为图像添加描述文字",
1372 "Edit captions before saving": "保存前编辑标题",1503 "Edit captions before saving": "保存前编辑描述文字",
1504 "Show captions in chat": "在聊天中显示描述文字",
1373 "Included settings:": "包含的设置:",1505 "Included settings:": "包含的设置:",
1374 "{{@key}}": "{{@key}}:",1506 "{{@key}}": "{{@key}}:",
1375 "Profile name:": "配置名称:",1507 "Profile name:": "配置名称:",
@@ -1394,9 +1526,14 @@
1394 "Classifier API": "分类器 API",1526 "Classifier API": "分类器 API",
1395 "Select the API for classifying expressions.": "选择用于对表达式进行分类的API。",1527 "Select the API for classifying expressions.": "选择用于对表达式进行分类的API。",
1396 "Main API": "当前连接的 API",1528 "Main API": "当前连接的 API",
1397 "WebLLM Extension": "WebLLM Extension",1529 "WebLLM Extension": "WebLLM 扩展程序",
1530 "When using LLM or WebLLM classifier, only show and use expressions that have sprites assigned to them.": "When using LLM or WebLLM classifier, only show and use expressions that have sprites assigned to them.",
1531 "Filter expressions for available sprites": "Filter expressions for available sprites",
1398 "LLM Prompt": "大语言模型提示词",1532 "LLM Prompt": "大语言模型提示词",
1399 "Will be used if the API doesn't support JSON schemas or function calling.": "如果 API 不支持 JSON 模式或函数调用,则会使用它。",1533 "Used in addition to JSON schemas and function calling.": "Used in addition to JSON schemas and function calling.",
1534 "LLM Prompt Strategy": "LLM Prompt Strategy",
1535 "Limited Context": "Limited Context",
1536 "Full Context": "Full Context",
1400 "Default / Fallback Expression": "默认/后备表达式",1537 "Default / Fallback Expression": "默认/后备表达式",
1401 "Set the default and fallback expression being used when no matching expression is found.": "设置在未找到匹配表达式时使用的默认表达式和后备表达式。",1538 "Set the default and fallback expression being used when no matching expression is found.": "设置在未找到匹配表达式时使用的默认表达式和后备表达式。",
1402 "Custom Expressions": "自定义表达式",1539 "Custom Expressions": "自定义表达式",
@@ -1422,29 +1559,29 @@
1422 "ext_sum_with": "总结如下:",1559 "ext_sum_with": "总结如下:",
1423 "ext_sum_main_api": "主要 API",1560 "ext_sum_main_api": "主要 API",
1424 "ext_sum_webllm": "WebLLM 扩展",1561 "ext_sum_webllm": "WebLLM 扩展",
1425 "ext_sum_current_summary": "当前摘要:",1562 "ext_sum_current_summary": "当前总结:",
1426 "ext_sum_restore_tip": "恢复先前的摘要;重复使用以清除此聊天的摘要状态",1563 "ext_sum_restore_tip": "恢复先前的总结;重复使用以清除此聊天的总结状态",
1427 "ext_sum_restore_previous": "恢复上一个",1564 "ext_sum_restore_previous": "恢复上一个",
1428 "ext_sum_memory_placeholder": "摘要将在这里生成...",1565 "ext_sum_memory_placeholder": "总结将在这里生成...",
1429 "ext_sum_force_tip": "立即触发摘要更新。",1566 "ext_sum_force_tip": "立即触发总结。",
1430 "ext_sum_force_text": "现在总结",1567 "ext_sum_force_text": "立即总结",
1431 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "禁用自动摘要更新。暂停时,摘要保持原样。您仍然可以通过按“立即汇总”按钮(仅适用于主 API)强制更新。",1568 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "禁用自动总结。暂停时,总结保持原样。您仍然可以通过按“立即总结”按钮(仅适用于主 API)强制更新。",
1432 "ext_sum_pause": "暂停",1569 "ext_sum_pause": "暂停",
1433 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "从要总结的文本中省略世界信息和作者注释。仅在使用主 API 时有效。附加 API 始终省略世界书/作者注。",1570 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "从要总结的文本中省略世界信息和作者注释。仅在使用主 API 时有效。附加 API 始终省略世界书/作者注。",
1434 "ext_sum_no_wi_an": "无世界书/作者注",1571 "ext_sum_no_wi_an": "无世界书/作者注",
1435 "ext_sum_settings_tip": "编辑摘要提示词、插入位置等。",1572 "ext_sum_settings_tip": "编辑总结提示词、插入位置等。",
1436 "ext_sum_settings": "摘要设置",1573 "ext_sum_settings": "总结设置",
1437 "ext_sum_prompt_builder": "提示词生成器",1574 "ext_sum_prompt_builder": "提示词生成器",
1438 "ext_sum_prompt_builder_1_desc": "扩展将使用尚未汇总的消息构建自己的提示词。阻止聊天,直到生成摘要为止。",1575 "ext_sum_prompt_builder_1_desc": "将使用尚未被总结的消息构建提示词,在总结生成前暂停聊天。",
1439 "ext_sum_prompt_builder_1": "原始,阻塞",1576 "ext_sum_prompt_builder_1": "原始,阻塞",
1440 "ext_sum_prompt_builder_2_desc": "扩展将使用尚未汇总的消息构建自己的提示词。在生成摘要时不会阻止聊天。并非所有后端都支持此模式。",1577 "ext_sum_prompt_builder_2_desc": "将使用尚未被总结的消息构建提示词。在生成总结时不会暂停聊天。并非所有后端都支持此模式。",
1441 "ext_sum_prompt_builder_2": "原始,非阻塞",1578 "ext_sum_prompt_builder_2": "原始,非阻塞",
1442 "ext_sum_prompt_builder_3_desc": "扩展将使用常规主提示词生成器并将摘要请求添加为其作为最后的系统消息。",1579 "ext_sum_prompt_builder_3_desc": "将使用常规主提示词生成器,并将总结请求设为系统消息添加到提示词最后。",
1443 "ext_sum_prompt_builder_3": "经典,阻塞",1580 "ext_sum_prompt_builder_3": "经典,阻塞",
1444 "Summary Prompt": "摘要提示词",1581 "Summary Prompt": "总结提示词",
1445 "ext_sum_restore_default_prompt_tip": "恢复默认提示词",1582 "ext_sum_restore_default_prompt_tip": "恢复默认提示词",
1446 "ext_sum_prompt_placeholder": "该提示词将被发送给 AI,以请求生成摘要。{{words}} 将解析为“字数”参数。",1583 "ext_sum_prompt_placeholder": "该提示词将被发送给 AI,以请求生成总结。{{words}} 将解析为“字数”参数。",
1447 "ext_sum_target_length_1": "目标摘要长度",1584 "ext_sum_target_length_1": "目标总结长度",
1448 "ext_sum_target_length_2": "(",1585 "ext_sum_target_length_2": "(",
1449 "ext_sum_target_length_3": "字)",1586 "ext_sum_target_length_3": "字)",
1450 "ext_sum_api_response_length_1": "API 响应长度",1587 "ext_sum_api_response_length_1": "API 响应长度",
@@ -1464,10 +1601,10 @@
1464 "ext_sum_injection_template": "插入模板",1601 "ext_sum_injection_template": "插入模板",
1465 "ext_sum_memory_template_placeholder": "{{summary}} 将解析当前摘要内容。",1602 "ext_sum_memory_template_placeholder": "{{summary}} 将解析当前摘要内容。",
1466 "ext_sum_injection_position": "插入位置",1603 "ext_sum_injection_position": "插入位置",
1467 "ext_sum_include_wi_scan_desc": "在 WI 扫描中包括最新摘要。",1604 "ext_sum_include_wi_scan_desc": "在世界信息扫描中加入最新总结。",
1468 "ext_sum_include_wi_scan": "纳入世界信息扫描",1605 "ext_sum_include_wi_scan": "纳入世界信息扫描",
1469 "None (not injected)": "无(未注入)",1606 "None (not injected)": "无(未注入)",
1470 "ext_sum_injection_position_none": "摘要不会被注入到提示中。您仍然可以通过 {{summary}} 宏访问它。",1607 "ext_sum_injection_position_none": "总结不会被注入到提示中。您仍然可以通过 {{summary}} 宏访问它。",
1471 "How many messages before the current end of the chat.": "当前聊天结束前还有多少条消息。",1608 "How many messages before the current end of the chat.": "当前聊天结束前还有多少条消息。",
1472 "Labels and Message": "标签和信息",1609 "Labels and Message": "标签和信息",
1473 "Label": "标签",1610 "Label": "标签",
@@ -1489,6 +1626,7 @@
1489 "Execute on chat change": "聊天内容改变时执行",1626 "Execute on chat change": "聊天内容改变时执行",
1490 "Execute on new chat": "在新聊天中执行",1627 "Execute on new chat": "在新聊天中执行",
1491 "Execute on group member draft": "起草群组成员时执行",1628 "Execute on group member draft": "起草群组成员时执行",
1629 "Execute before message generation": "在消息生成前执行",
1492 "Automation ID:": "自动化标识",1630 "Automation ID:": "自动化标识",
1493 "Testing": "测试",1631 "Testing": "测试",
1494 "Execute": "执行",1632 "Execute": "执行",
@@ -1498,6 +1636,8 @@
1498 "Show Popout Button": "(在电脑上)展示弹出式按钮",1636 "Show Popout Button": "(在电脑上)展示弹出式按钮",
1499 "Global Quick Reply Sets": "全局快速回复集",1637 "Global Quick Reply Sets": "全局快速回复集",
1500 "Chat Quick Reply Sets": "聊天快速回复集",1638 "Chat Quick Reply Sets": "聊天快速回复集",
1639 "Character Quick Reply Sets": "角色快速回复集",
1640 "(Private)": "(私密)",
1501 "Edit Quick Replies": "编辑快速回复",1641 "Edit Quick Replies": "编辑快速回复",
1502 "Disable Send (Insert Into Input Field)": "禁用发送(插入输入字段)",1642 "Disable Send (Insert Into Input Field)": "禁用发送(插入输入字段)",
1503 "Place Quick Reply Before Input": "在输入前放置快速回复",1643 "Place Quick Reply Before Input": "在输入前放置快速回复",
@@ -1506,14 +1646,36 @@
1506 "macro for manual injection)": "宏用于手动注入)",1646 "macro for manual injection)": "宏用于手动注入)",
1507 "Color": "颜色",1647 "Color": "颜色",
1508 "Only apply color as accent": "仅应用颜色作为强调",1648 "Only apply color as accent": "仅应用颜色作为强调",
1649 "ext_regex_new_global_script_desc": "新增「全局」正规表达式",
1650 "ext_regex_new_scoped_script_desc": "新增「局部」正规表达式",
1651 "ext_regex_debugger_active_rules": "激活的规则",
1652 "ext_regex_debugger_save_order": "保存此顺序",
1653 "ext_regex_debugger_testing_area": "测试区域",
1654 "ext_regex_debugger_raw_input": "原始输入",
1655 "ext_regex_debugger_run_test": "运行测试",
1656 "ext_regex_debugger_display_replace": "替换",
1657 "ext_regex_debugger_display_highlight": "高亮",
1658 "ext_regex_debugger_render_text": "渲染为文本",
1659 "ext_regex_debugger_render_message": "渲染为消息",
1660 "ext_regex_debugger_step_by_step": "逐步转换",
1661 "ext_regex_debugger_final_output": "最终输出",
1509 "ext_regex_title": "正则",1662 "ext_regex_title": "正则",
1510 "ext_regex_new_global_script_desc": "新的全局正则表达式脚本",1663 "ext_regex_presets": "正则预设",
1664 "ext_regex_presets_desc": "可以轻松保存并切换多组正则开关状态。",
1665 "ext_regex_preset_create": "创建新预设",
1666 "ext_regex_preset_update": "更新已有预设",
1667 "ext_regex_preset_apply": "重新应用当前预设",
1668 "ext_regex_preset_delete": "删除当前预设",
1511 "ext_regex_new_global_script": "新建全局正则",1669 "ext_regex_new_global_script": "新建全局正则",
1512 "ext_regex_new_scoped_script_desc": "新的作用域正则表达式脚本",
1513 "ext_regex_new_scoped_script": "新建局部正则",1670 "ext_regex_new_scoped_script": "新建局部正则",
1514 "ext_regex_import_script": "导入正则",1671 "ext_regex_import_script": "导入正则",
1672 "ext_regex_bulk_edit": "批量编辑",
1673 "ext_regex_debugger_desc": "高级正则调试工具",
1674 "ext_regex_debugger": "调试工具",
1675 "Export": "导出",
1515 "ext_regex_global_scripts": "全局正则脚本",1676 "ext_regex_global_scripts": "全局正则脚本",
1516 "ext_regex_global_scripts_desc": "影响所有角色,保存在本地设定中",1677 "ext_regex_global_scripts_desc": "影响所有角色,保存在本地设定中",
1678 "No scripts found": "没有找到脚本",
1517 "ext_regex_scoped_scripts": "局部正则脚本",1679 "ext_regex_scoped_scripts": "局部正则脚本",
1518 "ext_regex_disallow_scoped": "不允许使用局部正则",1680 "ext_regex_disallow_scoped": "不允许使用局部正则",
1519 "ext_regex_allow_scoped": "允许使用局部正则",1681 "ext_regex_allow_scoped": "允许使用局部正则",
@@ -1521,6 +1683,7 @@
1521 "Regex Editor": "正则表达式编辑器",1683 "Regex Editor": "正则表达式编辑器",
1522 "Test Mode": "测试模式",1684 "Test Mode": "测试模式",
1523 "ext_regex_desc": "“正则”是一个使用“正则表达式”来查找/替换字符串的工具。如果您想了解更多信息,请点击标题旁边的“?”。",1685 "ext_regex_desc": "“正则”是一个使用“正则表达式”来查找/替换字符串的工具。如果您想了解更多信息,请点击标题旁边的“?”。",
1686 "ext_regex_flags_help": "点击此处了解更多关于正则表达式修饰符的知识。",
1524 "Input": "输入",1687 "Input": "输入",
1525 "ext_regex_test_input_placeholder": "在此输入...",1688 "ext_regex_test_input_placeholder": "在此输入...",
1526 "Output": "输出",1689 "Output": "输出",
@@ -1553,10 +1716,14 @@
1553 "Substitute (raw)": "替换(原始)",1716 "Substitute (raw)": "替换(原始)",
1554 "Substitute (escaped)": "替换(转义)",1717 "Substitute (escaped)": "替换(转义)",
1555 "Ephemerality": "短暂",1718 "Ephemerality": "短暂",
1719 "ext_regex_other_options_desc": "默认情况下,正则脚本将直接地、不可逆转地修改聊天文件。\r启用下方任意一或多项可以避免对聊天文件的修改,但仍然修改特定项目。",
1556 "ext_regex_only_format_visual_desc": "正则仅在聊天页面生效,聊天文件内的内容不会被改变。",1720 "ext_regex_only_format_visual_desc": "正则仅在聊天页面生效,聊天文件内的内容不会被改变。",
1557 "Only Format Display": "仅格式显示",1721 "Only Format Display": "仅格式显示",
1558 "ext_regex_only_format_prompt_desc": "聊天记录不会改变,只有在请求发送时(生成时)才会出现提示词。",1722 "ext_regex_only_format_prompt_desc": "聊天记录不会改变,只有在请求发送时(生成时)才会出现提示词。",
1559 "Only Format Prompt (?)": "仅格式提示词",1723 "Only Format Prompt (?)": "仅格式提示词",
1724 "This character has embedded regex script(s).": "此角色含有内置正则脚本。",
1725 "Would you like to allow using them?": "你想要启用它们吗?",
1726 "If you want to do it later, select 'Regex' from the extensions menu.": "你可以稍后在扩展栏的 \"正则\" 区域管理它们。",
1560 "ext_regex_import_target": "导入至:",1727 "ext_regex_import_target": "导入至:",
1561 "ext_regex_disable_script": "禁用脚本",1728 "ext_regex_disable_script": "禁用脚本",
1562 "ext_regex_enable_script": "启用脚本",1729 "ext_regex_enable_script": "启用脚本",
@@ -1608,6 +1775,7 @@
1608 "Avoid spending Anlas": "避免花费 Anlas",1775 "Avoid spending Anlas": "避免花费 Anlas",
1609 "Opus tier": "(作品层)",1776 "Opus tier": "(作品层)",
1610 "View my Anlas": "查看我的目录",1777 "View my Anlas": "查看我的目录",
1778 "Click to set": "点击设置",
1611 "These settings only apply to DALL-E 3": "这些设置仅适用于 DALL-E 3",1779 "These settings only apply to DALL-E 3": "这些设置仅适用于 DALL-E 3",
1612 "Image Style": "图像风格",1780 "Image Style": "图像风格",
1613 "Image Quality": "画面质量",1781 "Image Quality": "画面质量",
@@ -1618,10 +1786,9 @@
1618 "Create new workflow": "创建新的工作流",1786 "Create new workflow": "创建新的工作流",
1619 "Delete workflow": "删除工作流",1787 "Delete workflow": "删除工作流",
1620 "Enhance": "提高",1788 "Enhance": "提高",
1621 "API Key": "API 密钥",
1622 "Click to set": "点击设置",
1623 "You can find your API key in the Stability AI dashboard.": "您可以在 Stability AI 仪表板中找到您的 API 密钥。",1789 "You can find your API key in the Stability AI dashboard.": "您可以在 Stability AI 仪表板中找到您的 API 密钥。",
1624 "Style Preset": "风格预设",1790 "Style Preset": "风格预设",
1791 "Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.": "是否对提示词使用提示词增强(Upsampling)。若开启,则会自动修改提示词,使回复更有创造力。",
1625 "Prompt Upsampling": "提示词增强(Upsampling)",1792 "Prompt Upsampling": "提示词增强(Upsampling)",
1626 "Sampling method": "采样方法",1793 "Sampling method": "采样方法",
1627 "Scheduler": "调度器",1794 "Scheduler": "调度器",
@@ -1646,6 +1813,8 @@
1646 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 采样器的 DYN 变体通常会产生更加多样化的输出,但在非常高的分辨率下可能会失败。",1813 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 采样器的 DYN 变体通常会产生更加多样化的输出,但在非常高的分辨率下可能会失败。",
1647 "DYN": "动态",1814 "DYN": "动态",
1648 "Decrisper": "去伪器",1815 "Decrisper": "去伪器",
1816 "Enable guidance only after body has been formed, to improve diversity and saturation of samples. May reduce relevance": "Enable guidance only after body has been formed, to improve diversity and saturation of samples. May reduce relevance",
1817 "Variety+": "Variety+",
1649 "(-1 for random)": "(“-1”为随机)",1818 "(-1 for random)": "(“-1”为随机)",
1650 "Preset for prompt prefix and negative prompt": "提示词前缀和负面提示词的预设",1819 "Preset for prompt prefix and negative prompt": "提示词前缀和负面提示词的预设",
1651 "Style": "风格",1820 "Style": "风格",
@@ -1665,7 +1834,7 @@
1665 "Extensions Menu": "扩展菜单",1834 "Extensions Menu": "扩展菜单",
1666 "Slash Command": "快捷命令",1835 "Slash Command": "快捷命令",
1667 "Interactive Mode": "交互模式",1836 "Interactive Mode": "交互模式",
1668 "Function Tool": "Function Tool",1837 "Function Tool": "函数工具",
1669 "Image Prompt Templates": "图像提示模板",1838 "Image Prompt Templates": "图像提示模板",
1670 "Token Counter": "词符计数器",1839 "Token Counter": "词符计数器",
1671 "Type / paste in the box below to see the number of tokens in the text.": "在下方框中输入或粘贴你想要统计词符数量的文本。",1840 "Type / paste in the box below to see the number of tokens in the text.": "在下方框中输入或粘贴你想要统计词符数量的文本。",
@@ -1700,10 +1869,13 @@
1700 "Skip codeblocks": "跳过代码块",1869 "Skip codeblocks": "跳过代码块",
1701 "Skip tagged blocks": "跳过标签块里的内容(<标签>跳过这里</标签>)",1870 "Skip tagged blocks": "跳过标签块里的内容(<标签>跳过这里</标签>)",
1702 "Pass Asterisks to TTS Engine": "将星号传递给文本转语音服务",1871 "Pass Asterisks to TTS Engine": "将星号传递给文本转语音服务",
1872 "Works best when: Pass Asterisks to TTS Engine is enabled, and both Only narrate quotes and Ignore *text, even 'quotes', inside asterisks* are disabled.": "Works best when: Pass Asterisks to TTS Engine is enabled, and both Only narrate quotes and Ignore *text, even 'quotes', inside asterisks* are disabled.",
1873 "Different voices for quotes and text inside asterisks": "Different voices for \"quotes\", *text inside asterisks* and other text",
1703 "Audio Playback Speed": "音频播放速度",1874 "Audio Playback Speed": "音频播放速度",
1704 "Vector Storage": "向量存储",1875 "Vector Storage": "向量存储",
1705 "Vectorization Source": "向量化源",1876 "Vectorization Source": "向量化源",
1706 "Local (Transformers)": "本地(Transformers)",1877 "Local (Transformers)": "本地(Transformers)",
1878 "Secondary Embedding endpoint URL": "Secondary Embedding endpoint URL",
1707 "Vectorization Model": "向量化模型",1879 "Vectorization Model": "向量化模型",
1708 "Keep model in memory": "将模型保存在内存中",1880 "Keep model in memory": "将模型保存在内存中",
1709 "Hint: Set the URL in the API connection settings.": "提示:在 API 连接设置中设置 URL。",1881 "Hint: Set the URL in the API connection settings.": "提示:在 API 连接设置中设置 URL。",
@@ -1756,6 +1928,9 @@
1756 "This will create a new subfolder...": "这将在 /data/ 目录中创建一个新的子文件夹,以用户的句柄作为文件夹名称。",1928 "This will create a new subfolder...": "这将在 /data/ 目录中创建一个新的子文件夹,以用户的句柄作为文件夹名称。",
1757 "Note:": "提示:",1929 "Note:": "提示:",
1758 "this chat is temporary and will be deleted as soon as you leave it.": "此聊天会话是临时的,会在你离开时被删除。",1930 "this chat is temporary and will be deleted as soon as you leave it.": "此聊天会话是临时的,会在你离开时被删除。",
1931 "Import from JSONL": "从 JSONL 文件导入",
1932 "Load": "加载",
1933 "Export as JSONL": "导出为 JSONL 文件",
1759 "Enter a new display name:": "输入一个新的昵称:",1934 "Enter a new display name:": "输入一个新的昵称:",
1760 "Current Password:": "当前密码:",1935 "Current Password:": "当前密码:",
1761 "New Password:": "新密码:",1936 "New Password:": "新密码:",
@@ -1787,9 +1962,21 @@
1787 "custom_exclude_body_desc": "要从聊天完成请求主体中排除的参数(YAML 数组)\n\n示例:\n- frequency_penalty\n- presence_penalty",1962 "custom_exclude_body_desc": "要从聊天完成请求主体中排除的参数(YAML 数组)\n\n示例:\n- frequency_penalty\n- presence_penalty",
1788 "Include Request Headers": "包含请求标头",1963 "Include Request Headers": "包含请求标头",
1789 "custom_include_headers_desc": "聊天完成请求的附加标头(YAML 对象)\n\n示例:\nCustomHeader:自定义值\nAnotherHeader:自定义值",1964 "custom_include_headers_desc": "聊天完成请求的附加标头(YAML 对象)\n\n示例:\nCustomHeader:自定义值\nAnotherHeader:自定义值",
1965 "{{name}}": "{{name}}",
1966 "Delete all items in this category": "删除此分类下的所有项目",
1967 "View item content": "查看项目内容",
1968 "Download item": "下载项目",
1969 "Delete this item": "删除此项目",
1970 "Once deleted, the files will be gone forever!": "一旦删除,文件将会永久消失!(真的很久!)",
1971 "Make sure to back up your data in advance.": "确保你已提前备份好数据。",
1972 "Scan": "扫描",
1973 "No results yet. Tap 'Scan' to start scanning.": "暂无结果。点击 '扫描' 以启动扫描。",
1790 "Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.": "此类别中的功能仅供高级用户使用。如果您不确定后果,请不要点击任何内容。",1974 "Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.": "此类别中的功能仅供高级用户使用。如果您不确定后果,请不要点击任何内容。",
1791 "THIS IS PERMANENT!": "此操作不可逆!",1975 "THIS IS PERMANENT!": "此操作不可逆!",
1792 "Also delete the chat files": "同时删除聊天文件",1976 "Also delete the chat files": "同时删除聊天文件",
1977 "Delete Tag": "删除标签",
1978 "Do you want to delete the tag": "确定删除这个标签吗?",
1979 "If you want to merge all references to this tag into another tag, select it below:": "如果你想要将所有此标签的引用合并到其他标签,请从下方选择:",
1793 "Are you sure you want to delete this user?": "您确定要删除该用户吗?",1980 "Are you sure you want to delete this user?": "您确定要删除该用户吗?",
1794 "Deleting:": "删除:",1981 "Deleting:": "删除:",
1795 "Also wipe user data.": "同时清空用户数据",1982 "Also wipe user data.": "同时清空用户数据",
@@ -1798,6 +1985,12 @@
1798 "Type the user's handle below to confirm:": "在下面输入此用户的用户句柄以确认删除操作:",1985 "Type the user's handle below to confirm:": "在下面输入此用户的用户句柄以确认删除操作:",
1799 "Are you sure you want to duplicate this character?": "你确定要复制这个角色吗?",1986 "Are you sure you want to duplicate this character?": "你确定要复制这个角色吗?",
1800 "If you just want to start a new chat with the same character...": "如果你只是想要与此角色开启一个新的聊天,只需点击聊天左下方菜单中的“开始新聊天”按钮。",1987 "If you just want to start a new chat with the same character...": "如果你只是想要与此角色开启一个新的聊天,只需点击聊天左下方菜单中的“开始新聊天”按钮。",
1988 "There are no items to display.": "没有项目可供展示。",
1989 "Do you want to export connection data with the preset?": "你想要把API连接配置和预设一起导出吗?",
1990 "This includes the selected source, models, and other preferences set in the API Connections panel.": "这包含你在API连接配置页面中设置的URL、模型和其他偏好设置。",
1991 "Your stored API keys are never exported.": "你存储的API密钥永远不会被导出。",
1992 "Export connection data": "导出API连接配置",
1993 "Do not export connection data": "不导出API连接配置",
1801 "Forbid Media Override explanation": "当前角色/群聊成员使用外部媒体的能力。",1994 "Forbid Media Override explanation": "当前角色/群聊成员使用外部媒体的能力。",
1802 "Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。",1995 "Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。",
1803 "forbid_media_global_state_forbidden": "(禁止)",1996 "forbid_media_global_state_forbidden": "(禁止)",
@@ -1810,19 +2003,19 @@
1810 "help_format_4": "斜体",2003 "help_format_4": "斜体",
1811 "help_format_5": "**文本**",2004 "help_format_5": "**文本**",
1812 "help_format_6": "显示为",2005 "help_format_6": "显示为",
1813 "help_format_7": "大胆的",2006 "help_format_7": "粗体",
1814 "help_format_8": "***文本***",2007 "help_format_8": "***文本***",
1815 "help_format_9": "显示为",2008 "help_format_9": "显示为",
1816 "help_format_10": "粗斜体",2009 "help_format_10": "粗斜体",
1817 "help_format_11": "__文本__",2010 "help_format_11": "__文本__",
1818 "help_format_12": "显示为",2011 "help_format_12": "显示为",
1819 "help_format_13": "强调",2012 "help_format_13": "下划线",
1820 "help_format_14": "~~文本~~",2013 "help_format_14": "~~文本~~",
1821 "help_format_15": "显示为",2014 "help_format_15": "显示为",
1822 "help_format_16": "删除线",2015 "help_format_16": "删除线",
1823 "help_format_17": "[文本](网址)",2016 "help_format_17": "[文本](网址)",
1824 "help_format_18": "显示为",2017 "help_format_18": "显示为",
1825 "help_format_19": "超级链接",2018 "help_format_19": "超链接",
1826 "help_format_20": "![文本](网址)",2019 "help_format_20": "![文本](网址)",
1827 "help_format_21": "显示为图像",2020 "help_format_21": "显示为图像",
1828 "help_format_22": "```文本```",2021 "help_format_22": "```文本```",
@@ -1830,15 +2023,23 @@
1830 "help_format_like_this": "像这样",2023 "help_format_like_this": "像这样",
1831 "help_format_24": "`文本`",2024 "help_format_24": "`文本`",
1832 "help_format_25": "显示为",2025 "help_format_25": "显示为",
1833 "help_format_26": "内联代码",2026 "help_format_26": "行内代码块",
1834 "help_format_27": "> 文本",2027 "help_format_27": "> 文本",
1835 "help_format_28": "显示为块引用(请注意 > 后面的空格)",2028 "help_format_28": "显示为引用(请注意 > 后面要加一个空格)",
1836 "help_format_29": "# 文本",2029 "help_format_29": "# 文本",
1837 "help_format_30": "显示为大标题(注意空格)",2030 "help_format_30": "显示为大标题(注意空格)",
1838 "help_format_32": "## 文本",2031 "help_format_32": "## 文本",
1839 "help_format_33": "显示为中等标题(注意空格)",2032 "help_format_33": "显示为中等标题(注意空格)",
1840 "help_format_35": "### 文本",2033 "help_format_35": "### 文本",
1841 "help_format_36": "显示为小标题(注意空格)",2034 "help_format_36": "显示为小标题(注意空格)",
2035 "Creator's Notes contain CSS style tags. Do you want to apply them just to Creator's Notes or to the entire application?": "创作者注释中包含CSS样式标签。你是想把这个样式只应用到创作者注释,还是应用到整个页面?",
2036 "CAUTION: Malformed styles may cause issues.": "警告:错误样式可能会出问题。",
2037 "To change the preference later, use the": "若想后续修改偏好设置,请点击创作者注释处的",
2038 "button in the Creator's Notes block.": "按钮。",
2039 "Class names will be automatically prefixed with 'custom-'.": "CSS类名将会自动添加 'custom-' 前缀。",
2040 "Choose how to apply CSS style tags if they are defined in Creator's Notes of this character:": "选择如何应用这个角色的创作者注释中定义的CSS样式:",
2041 "Just to Creator's Notes": "仅应用到创作者注释",
2042 "Apply to the entire app": "应用到整个页面",
1842 "help_1": "您好!请选择您想要详细了解的帮助主题:",2043 "help_1": "您好!请选择您想要详细了解的帮助主题:",
1843 "help_2": "斜线命令",2044 "help_2": "斜线命令",
1844 "help_or": "或者",2045 "help_or": "或者",
@@ -1848,6 +2049,7 @@
1848 "help_6": "还有其他问题吗?",2049 "help_6": "还有其他问题吗?",
1849 "help_7": "SillyTavern 官方文档网站",2050 "help_7": "SillyTavern 官方文档网站",
1850 "help_8": "有更多信息!",2051 "help_8": "有更多信息!",
2052 "Characters and groups hidden by filters or closed folders": "因筛选条件或关闭的文件夹隐藏的角色和群组",
1851 "help_hotkeys_0": "热键/按键绑定",2053 "help_hotkeys_0": "热键/按键绑定",
1852 "help_hotkeys_1": "上",2054 "help_hotkeys_1": "上",
1853 "help_hotkeys_2": "编辑聊天中的最后一条消息",2055 "help_hotkeys_2": "编辑聊天中的最后一条消息",
@@ -1926,11 +2128,11 @@
1926 "help_macros_12": "您当前的角色描述",2128 "help_macros_12": "您当前的角色描述",
1927 "help_macros_13": "角色对话示例",2129 "help_macros_13": "角色对话示例",
1928 "help_macros_14": "未格式化的对话示例",2130 "help_macros_14": "未格式化的对话示例",
1929 "(only for Story String)": "(仅适用于故事字符串)",2131 "help_macros_summary": "“总结”扩展程序生成的最新聊天总结(如果有)。",
1930 "help_macros_summary": "“Summarize”扩展生成的最新聊天摘要(如果有)。",2132 "help_macros_15": "您当前的用户设定名称",
1931 "help_macros_15": "您当前的用户角色名称",
1932 "help_macros_16": "角色的名字",2133 "help_macros_16": "角色的名字",
1933 "help_macros_17": "角色的版本号",2134 "help_macros_17": "角色的版本号",
2135 "help_macros_charDepthPrompt": "角色的 @ 深度注释",
1934 "help_macros_18": "以逗号分隔的群成员名称列表或单人聊天中的角色名称。别名:{{charIfNotGroup}}",2136 "help_macros_18": "以逗号分隔的群成员名称列表或单人聊天中的角色名称。别名:{{charIfNotGroup}}",
1935 "help_groupNotMuted": "与 {{group}} 相同,但排除被禁言的成员",2137 "help_groupNotMuted": "与 {{group}} 相同,但排除被禁言的成员",
1936 "help_macros_19": "当前选定的 API 的文本生成模型名称。",2138 "help_macros_19": "当前选定的 API 的文本生成模型名称。",
@@ -2002,16 +2204,11 @@
2002 "help_macros_69": "替换为范围变量“name”的值",2204 "help_macros_69": "替换为范围变量“name”的值",
2003 "help_macros_70": "用范围变量“name”的索引处的项目值(对于数组/列表或对象/字典)替换",2205 "help_macros_70": "用范围变量“name”的索引处的项目值(对于数组/列表或对象/字典)替换",
2004 "Choose what to export": "选择您想要导出什么:",2206 "Choose what to export": "选择您想要导出什么:",
2005 "{{name}}": "{{name}}",
2006 "Choose what to import": "选择您想要导入什么:",2207 "Choose what to import": "选择您想要导入什么:",
2007 "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可稍后在 /backups 文件夹中恢复此聊天文件。",2208 "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可稍后在 /backups 文件夹中恢复此聊天文件。",
2008 "Also delete the current chat file": "同时删除当前聊天文件",2209 "Also delete the current chat file": "同时删除当前聊天文件",
2009 "Persona Lorebook for": "Persona Lorebook for",2210 "Persona Lorebook for": "Persona Lorebook for",
2010 "persona_world_template_txt": "A selected World Info will be bound to this persona. When generating an AI reply,\n it will be combined with the entries from global, character and chat lorebooks.",2211 "persona_world_template_txt": "A selected World Info will be bound to this persona. When generating an AI reply,\n it will be combined with the entries from global, character and chat lorebooks.",
2011 "Export for character": "导出角色",
2012 "Export prompts for this character, including their order.": "导出此角色的提示词,包括其顺序。",
2013 "Export all": "全部导出",
2014 "Export all your prompts to a file": "将所有提示词导出到文件",
2015 "Insert prompt": "插入提示词",2212 "Insert prompt": "插入提示词",
2016 "Import a prompt list": "导入提示词列表",2213 "Import a prompt list": "导入提示词列表",
2017 "Export this prompt list": "导出此提示词列表",2214 "Export this prompt list": "导出此提示词列表",
@@ -2019,6 +2216,7 @@
2019 "New prompt": "新提示词",2216 "New prompt": "新提示词",
2020 "Prompts": "提示词",2217 "Prompts": "提示词",
2021 "Total Tokens:": "总词符数:",2218 "Total Tokens:": "总词符数:",
2219 "Name": "名称",
2022 "prompt_manager_tokens": "词符",2220 "prompt_manager_tokens": "词符",
2023 "Are you sure you want to connect to the following proxy URL?": "你确定要连接到下面的代理URL吗?",2221 "Are you sure you want to connect to the following proxy URL?": "你确定要连接到下面的代理URL吗?",
2024 "Encountered an error while processing your request.": "处理请求时遇到了问题。",2222 "Encountered an error while processing your request.": "处理请求时遇到了问题。",
@@ -2029,12 +2227,20 @@
2029 "Are you sure you want to reset your settings to factory defaults?": "您确定要将您的设置重置为出厂默认设置吗?",2227 "Are you sure you want to reset your settings to factory defaults?": "您确定要将您的设置重置为出厂默认设置吗?",
2030 "Don't forget to save a snapshot of your settings before proceeding.": "在继续之前,不要忘记保存您的设置快照。",2228 "Don't forget to save a snapshot of your settings before proceeding.": "在继续之前,不要忘记保存您的设置快照。",
2031 "Enter your password below to confirm:": "输入您的密码以确认:",2229 "Enter your password below to confirm:": "输入您的密码以确认:",
2230 "Reset custom sampler selection": "重置自定义采样器选择",
2231 "Here you can toggle the display of individual samplers. (WIP)": "在此可以切换单个采样器的显示。(开发中)",
2032 "Chat Scenario Override": "聊天场景覆盖",2232 "Chat Scenario Override": "聊天场景覆盖",
2033 "Remove": "移除",2233 "Remove": "移除",
2034 "Unique to this chat.": "仅对此聊天生效。",2234 "Unique to this chat.": "仅对此聊天生效。",
2035 "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.",2235 "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.",
2036 "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.",2236 "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.",
2037 "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.",2237 "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.",
2238 "API:": "API:",
2239 "Key:": "密钥:",
2240 "Add Secret": "添加密钥",
2241 "No secrets saved.": "无保存的密钥。",
2242 "Copy ID": "复制 ID",
2243 "Select": "选择",
2038 "Settings Snapshots": "设置快照",2244 "Settings Snapshots": "设置快照",
2039 "Record a snapshot of your current settings.": "记录当前设置的快照。",2245 "Record a snapshot of your current settings.": "记录当前设置的快照。",
2040 "Make a Snapshot": "制作快照",2246 "Make a Snapshot": "制作快照",
@@ -2050,6 +2256,8 @@
2050 "Exclude Patterns": "排除模式",2256 "Exclude Patterns": "排除模式",
2051 "Glob patterns of files to exclude in the download.": "下载中要排除的文件的 Glob 模式。每个模式用换行符分隔。",2257 "Glob patterns of files to exclude in the download.": "下载中要排除的文件的 Glob 模式。每个模式用换行符分隔。",
2052 "Tag Management": "标签管理",2258 "Tag Management": "标签管理",
2259 "Remove unused tags": "删除未被使用的标签",
2260 "Prune": "精简",
2053 "Save your tags to a file": "将标签保存为文件",2261 "Save your tags to a file": "将标签保存为文件",
2054 "Restore tags from a file": "从文件中恢复标签",2262 "Restore tags from a file": "从文件中恢复标签",
2055 "Create a new tag": "新建一个标签",2263 "Create a new tag": "新建一个标签",
@@ -2078,8 +2286,8 @@
2078 "Reset Settings": "重置设置",2286 "Reset Settings": "重置设置",
2079 "Wipe all user data and reset your account to factory settings.": "删除所有用户数据并将您的账号重置为默认设置。",2287 "Wipe all user data and reset your account to factory settings.": "删除所有用户数据并将您的账号重置为默认设置。",
2080 "Reset Everything": "重置一切",2288 "Reset Everything": "重置一切",
2081 "This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.": "This will delete all your settings and data. There will be no undo button.\n Make sure you have a backup before proceeding.",2289 "This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.": "这将删除您所有的设置和数据,不可撤销。请确保您已备份数据。",
2082 "Account reset code has been posted to the server console.": "Account reset code has been posted to the server console.",2290 "Account reset code has been posted to the server console.": "账户重置代码已发布到服务器控制台。",
2083 "Reset Code:": "重置代码:",2291 "Reset Code:": "重置代码:",
2084 "Want to update?": "获取最新版本",2292 "Want to update?": "获取最新版本",
2085 "How to start chatting?": "如何快速开始聊天?",2293 "How to start chatting?": "如何快速开始聊天?",
@@ -2100,29 +2308,17 @@
2100 "Join the SillyTavern Discord": "加入 SillyTavern 的 Discord群组",2308 "Join the SillyTavern Discord": "加入 SillyTavern 的 Discord群组",
2101 "Post a GitHub issue": "在 GitHub 发布问题",2309 "Post a GitHub issue": "在 GitHub 发布问题",
2102 "Contact the developers": "联系开发者",2310 "Contact the developers": "联系开发者",
2103 "If you're connected to an API, try asking me something!": "若您已经配置好API,尝试发送些什么吧!",2311 "Show recent chats": "显示最近的聊天",
2312 "Hide recent chats": "隐藏最近的聊天",
2313 "Recent Chats": "最近的聊天",
2314 "Docs": "文档",
2315 "GitHub": "GitHub",
2316 "Discord": "Discord",
2317 "Temporary Chat": "临时聊天",
2318 "No recent chats": "无最近聊天",
2319 "Rename chat": "重命名聊天",
2320 "Delete chat": "删除聊天",
2104 "Title/Memo": "标题(备忘)",2321 "Title/Memo": "标题(备忘)",
2105 "Strategy": "触发策略",2322 "Strategy": "触发策略",
2106 "Position": "插入位置",2323 "Trigger %": "触发概率%"
2107 "Trigger %": "触发概率%",
2108 "Generate Caption": "生成图片描述",
2109 "(DEPRECATED)": "(已弃用)",
2110 "[Currently loaded]": "[当前加载]",
2111 "Change Persona Image": "更改角色图片",
2112 "Delete Persona": "删除角色",
2113 "Duplicate Persona": "复制角色",
2114 "Enter a name for this persona:": "输入角色名",
2115 "Enable web search": "启用联网搜索",
2116 "Current Persona": "当前角色",
2117 "Global Settings": "全局设置",
2118 "Select a model": "选择模型",
2119 "Thinking...": "思考中",
2120 "Valid": "有效",
2121 "Rename Persona": "重命名角色",
2122 "Sort By: Name (Z-A)": "排序: 名称(Z-A)",
2123 "Sort By: Name (A-Z)": "排序: 名称(A-Z)",
2124 "Sort By: Date (Oldest First)": "排序: 日期(从最远到最新)",
2125 "Sort By: Date (Newest First)": "排序: 日期(从最新到最远)",
2126 "Set the reasoning block of a message. Returns the reasoning block content.": "设置消息的推理块。返回推理块内容。",
2127 "Select providers. No selection = all providers.": "选择服务商。未选择 = 所有服务商。"
2128}2324}
public/locales/zh-tw.json+2 -0
@@ -1791,6 +1791,8 @@
1791 "Derive context size from backend": "從後端推導上下文大小",1791 "Derive context size from backend": "從後端推導上下文大小",
1792 "Using a proxy that you're not running yourself is a risk to your data privacy.": "使用非自行管理的代理服務可能導致您的資料隱私外洩。",1792 "Using a proxy that you're not running yourself is a risk to your data privacy.": "使用非自行管理的代理服務可能導致您的資料隱私外洩。",
1793 "Claude API Key": "Claude API 金鑰",1793 "Claude API Key": "Claude API 金鑰",
1794 "Electron Hub API Key": "Electron Hub API 金鑰",
1795 "Electron Hub Model": "Electron Hub 模型",
1794 "NanoGPT API Key": "NanoGPT API 金鑰",1796 "NanoGPT API Key": "NanoGPT API 金鑰",
1795 "NanoGPT Model": "NanoGPT 模型",1797 "NanoGPT Model": "NanoGPT 模型",
1796 "context_derived": "若可能,根據模型後設資料推導。",1798 "context_derived": "若可能,根據模型後設資料推導。",
public/login.html+1 -1
@@ -84,7 +84,7 @@
84 </div>84 </div>
8585
86 <script src="lib/jquery-3.5.1.min.js"></script>86 <script src="lib/jquery-3.5.1.min.js"></script>
87 <script src="scripts/login.js"></script>87 <script src="scripts/login.js" type="module"></script>
88</body>88</body>
8989
90</html>90</html>
public/script.js+12 -2
@@ -266,6 +266,7 @@ import { initDataMaid } from './scripts/data-maid.js';
266import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js';266import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js';
267import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js';267import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js';
268import { event_types, eventSource } from './scripts/events.js';268import { event_types, eventSource } from './scripts/events.js';
269import { initAccessibility } from './scripts/a11y.js';
269270
270// API OBJECT FOR EXTERNAL WIRING271// API OBJECT FOR EXTERNAL WIRING
271globalThis.SillyTavern = {272globalThis.SillyTavern = {
@@ -687,6 +688,7 @@ async function firstLoadInit() {
687 initCustomSelectedSamplers();688 initCustomSelectedSamplers();
688 initDataMaid();689 initDataMaid();
689 initItemizedPrompts();690 initItemizedPrompts();
691 initAccessibility();
690 addDebugFunctions();692 addDebugFunctions();
691 doDailyExtensionUpdatesCheck();693 doDailyExtensionUpdatesCheck();
692 await hideLoader();694 await hideLoader();
@@ -5237,7 +5239,13 @@ function extractImageFromData(data, { mainApi = null, chatCompletionSource = nul
5237 return `data:${inlineData.mimeType};base64,${inlineData.data}`;5239 return `data:${inlineData.mimeType};base64,${inlineData.data}`;
5238 }5240 }
5239 } break;5241 } break;
52405242 case chat_completion_sources.OPENROUTER: {
5243 const imageUrl = data?.choices[0]?.message?.images?.find(x => x.type === 'image_url')?.image_url?.url;
5244 if (isDataURL(imageUrl)) {
5245 return imageUrl;
5246 }
5247 // TODO: Handle remote URLs
5248 }
5241 }5249 }
5242 } break;5250 } break;
5243 }5251 }
@@ -5361,6 +5369,8 @@ export function extractJsonFromData(data, { mainApi = null, chatCompletionSource
5361 case chat_completion_sources.CUSTOM:5369 case chat_completion_sources.CUSTOM:
5362 case chat_completion_sources.COHERE:5370 case chat_completion_sources.COHERE:
5363 case chat_completion_sources.XAI:5371 case chat_completion_sources.XAI:
5372 case chat_completion_sources.ELECTRONHUB:
5373 case chat_completion_sources.AZURE_OPENAI:
5364 default:5374 default:
5365 result = tryParse(text);5375 result = tryParse(text);
5366 break;5376 break;
@@ -10808,7 +10818,7 @@ jQuery(async function () {
10808 }10818 }
10809 } break;10819 } break;
10810 case 'replace_update': {10820 case 'replace_update': {
10811 const confirm = await Popup.show.confirm('Replace Character', '<p>Choose a new character card to replace this character with.</p>All chats, assets and group memberships will be preserved, but local changes to the character data will be lost.<br />Proceed?');10821 const confirm = await Popup.show.confirm(t`Replace Character`, '<p>' + t`Choose a new character card to replace this character with.` + '</p>' + t`All chats, assets and group memberships will be preserved, but local changes to the character data will be lost.` + '<br />' + t`Proceed?`);
10812 if (confirm) {10822 if (confirm) {
10813 async function uploadReplacementCard(e) {10823 async function uploadReplacementCard(e) {
10814 const file = e.target.files[0];10824 const file = e.target.files[0];
public/scripts/RossAscends-mods.js+16 -7
@@ -402,6 +402,7 @@ function RA_autoconnect(PrevApi) {
402 || (secret_state[SECRET_KEYS.COHERE] && oai_settings.chat_completion_source == chat_completion_sources.COHERE)402 || (secret_state[SECRET_KEYS.COHERE] && oai_settings.chat_completion_source == chat_completion_sources.COHERE)
403 || (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)403 || (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)
404 || (secret_state[SECRET_KEYS.GROQ] && oai_settings.chat_completion_source == chat_completion_sources.GROQ)404 || (secret_state[SECRET_KEYS.GROQ] && oai_settings.chat_completion_source == chat_completion_sources.GROQ)
405 || (secret_state[SECRET_KEYS.ELECTRONHUB] && oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB)
405 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)406 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
406 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)407 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
407 || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)408 || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
@@ -411,6 +412,7 @@ function RA_autoconnect(PrevApi) {
411 || (secret_state[SECRET_KEYS.COMETAPI] && oai_settings.chat_completion_source == chat_completion_sources.COMETAPI)412 || (secret_state[SECRET_KEYS.COMETAPI] && oai_settings.chat_completion_source == chat_completion_sources.COMETAPI)
412 || (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)413 || (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
413 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)414 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
415 || (secret_state[SECRET_KEYS.AZURE_OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI)
414 ) {416 ) {
415 $('#api_button_openai').trigger('click');417 $('#api_button_openai').trigger('click');
416 }418 }
@@ -480,8 +482,7 @@ export function dragElement($elmnt) {
480482
481 let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;483 let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
482 let height, width, top, left, right, bottom,484 let height, width, top, left, right, bottom,
483 maxX, maxY, winHeight, winWidth,485 maxX, maxY, winHeight, winWidth;
484 topbar;
485486
486 const elmntName = $elmnt.attr('id');487 const elmntName = $elmnt.attr('id');
487 const elmntNameEscaped = $.escapeSelector(elmntName);488 const elmntNameEscaped = $.escapeSelector(elmntName);
@@ -540,11 +541,6 @@ export function dragElement($elmnt) {
540 winWidth = window.innerWidth;541 winWidth = window.innerWidth;
541 winHeight = window.innerHeight;542 winHeight = window.innerHeight;
542543
543 topbar = document.getElementById('top-bar');
544 const topbarstyle = getComputedStyle(topbar);
545 topBarFirstX = parseInt(topbarstyle.marginInline);
546 topBarLastY = parseInt(topbarstyle.height);
547
548 // Prepare state object if missing544 // Prepare state object if missing
549 if (!power_user.movingUIState[elmntName]) power_user.movingUIState[elmntName] = {};545 if (!power_user.movingUIState[elmntName]) power_user.movingUIState[elmntName] = {};
550546
@@ -1066,6 +1062,19 @@ export function initRossMods() {
1066 $('#option_regenerate').trigger('click');1062 $('#option_regenerate').trigger('click');
1067 $('#options').hide();1063 $('#options').hide();
1068 }1064 }
1065
1066 // If there is input text, we do not trigger a regenerate - we just send it
1067 if ($('#send_textarea').val() !== '') {
1068 if (shouldSendOnEnter()) {
1069 console.debug('Sending with Ctrl+Enter');
1070 event.preventDefault();
1071 sendTextareaMessage();
1072 } else {
1073 console.debug('Text area is not empty, but send on enter is disabled');
1074 }
1075 return;
1076 }
1077
1069 if (skipConfirm) {1078 if (skipConfirm) {
1070 doRegenerate();1079 doRegenerate();
1071 } else {1080 } else {
public/scripts/a11y.js+116 -0
@@ -0,0 +1,116 @@
1/**
2 * Shared module between login and main app.
3 * Be careful what you import!
4 */
5
6const buttonSelectors = [
7 '.menu_button',
8 '.right_menu_button',
9 '.mes_button',
10 '.drawer-icon',
11 '.inline-drawer-icon',
12 '.swipe_left',
13 '.swipe_right',
14 '.character_select',
15 '.tags .tag',
16 '.jg-menu .jg-button',
17 '.bg_example .mobile-only-menu-toggle',
18 '.paginationjs-pages li a',
19].join(', ');
20
21const listSelectors = [
22 '.options-content',
23 '.list-group',
24 '#rm_print_characters_block',
25 '#rm_group_members',
26 '#rm_group_add_members',
27 '.tag_view_list_tags',
28 '.secretKeyManagerList',
29 '.recentChatList',
30 '.dataMaidCategoryContent',
31 '#userList',
32 '.bg_list',
33].join(', ');
34
35const listItemSelectors = [
36 '.options-content .list-group-item',
37 '.list-group .list-group-item',
38 '#rm_print_characters_block .entity_block',
39 '#rm_group_members .group_member',
40 '#rm_group_add_members .group_member',
41 '.tag_view_list_tags .tag_view_item',
42 '.secretKeyManagerList .secretKeyManagerItem',
43 '.recentChatList .recentChat',
44 '.dataMaidCategoryContent .dataMaidItem',
45 '#userList .userSelect',
46 '.bg_list .bg_example',
47].join(', ');
48
49const toolbarSelectors = [
50 '.jg-menu',
51].join(', ');
52
53/** @type {Record<string, (element: Element) => void>} */
54const a11yRules = {
55 [buttonSelectors]: (element) => {
56 element.setAttribute('role', 'button');
57 },
58 [listSelectors]: (element) => {
59 element.setAttribute('role', 'list');
60 },
61 [listItemSelectors]: (element) => {
62 element.setAttribute('role', 'listitem');
63 },
64 [toolbarSelectors]: (element) => {
65 element.setAttribute('role', 'toolbar');
66 },
67 '#toast-container .toast': (element) => {
68 element.setAttribute('role', 'status');
69 },
70};
71
72/**
73 * Apply accessibility rules to an element.
74 * @param {Element} element Element to process.
75 */
76function applyA11yRules(element) {
77 try {
78 for (const [selector, rule] of Object.entries(a11yRules)) {
79 // Apply if the element directly matches the selector
80 if (element.matches(selector)) {
81 rule(element);
82 }
83 // Apply the rule to descendants
84 element.querySelectorAll(selector).forEach(rule);
85 }
86 } catch (error) {
87 console.error('Error applying accessibility rules to element:', element, error);
88 }
89}
90
91function setAccessibilityObserver() {
92 // Apply for existing elements
93 applyA11yRules(document.body);
94
95 // Setup observer for dynamic content
96 const observer = new MutationObserver((mutationsList) => {
97 for (const mutation of mutationsList) {
98 if (mutation.type === 'childList') {
99 for (const addedNode of mutation.addedNodes) {
100 if (addedNode instanceof Element && addedNode.nodeType === Node.ELEMENT_NODE) {
101 applyA11yRules(addedNode);
102 }
103 }
104 }
105 }
106 });
107
108 observer.observe(document.body, {
109 childList: true,
110 subtree: true,
111 });
112}
113
114export function initAccessibility() {
115 setAccessibilityObserver();
116}
public/scripts/backgrounds.js+51 -12
@@ -265,9 +265,10 @@ async function onCopyToSystemBackgroundClick(e) {
265 * It caches the thumbnail in local storage and returns a blob URL for the thumbnail.265 * It caches the thumbnail in local storage and returns a blob URL for the thumbnail.
266 * If the thumbnail cannot be fetched, it returns a transparent PNG pixel as a fallback.266 * If the thumbnail cannot be fetched, it returns a transparent PNG pixel as a fallback.
267 * @param {string} bg Background URL267 * @param {string} bg Background URL
268 * @param {boolean} isCustom Is the background custom?
268 * @returns {Promise<string>} Blob URL of the thumbnail269 * @returns {Promise<string>} Blob URL of the thumbnail
269 */270 */
270async function getThumbnailFromStorage(bg) {271async function getThumbnailFromStorage(bg, isCustom) {
271 const cachedBlobUrl = THUMBNAIL_BLOBS.get(bg);272 const cachedBlobUrl = THUMBNAIL_BLOBS.get(bg);
272 if (cachedBlobUrl) {273 if (cachedBlobUrl) {
273 return cachedBlobUrl;274 return cachedBlobUrl;
@@ -281,7 +282,8 @@ async function getThumbnailFromStorage(bg) {
281 }282 }
282283
283 try {284 try {
284 const response = await fetch(getBackgroundPath(bg), { cache: 'force-cache' });285 const url = isCustom ? bg : getBackgroundPath(bg);
286 const response = await fetch(url, { cache: 'force-cache' });
285 if (!response.ok) {287 if (!response.ok) {
286 throw new Error('Fetch failed with status: ' + response.status);288 throw new Error('Fetch failed with status: ' + response.status);
287 }289 }
@@ -519,7 +521,7 @@ async function resolveImageUrl(bg, isCustom) {
519 const fileExtension = bg.split('.').pop().toLowerCase();521 const fileExtension = bg.split('.').pop().toLowerCase();
520 const isAnimated = ['mp4', 'webp'].includes(fileExtension);522 const isAnimated = ['mp4', 'webp'].includes(fileExtension);
521 const thumbnailUrl = isAnimated && !background_settings.animation523 const thumbnailUrl = isAnimated && !background_settings.animation
522 ? await getThumbnailFromStorage(bg)524 ? await getThumbnailFromStorage(bg, isCustom)
523 : isCustom525 : isCustom
524 ? bg526 ? bg
525 : getThumbnailUrl('bg', bg);527 : getThumbnailUrl('bg', bg);
@@ -573,14 +575,21 @@ async function delBackground(bg) {
573}575}
574576
575async function onBackgroundUploadSelected() {577async function onBackgroundUploadSelected() {
576 const form = $('#form_bg_download').get(0);578 const form = $('#form_bg_upload').get(0);
577579
578 if (!(form instanceof HTMLFormElement)) {580 if (!(form instanceof HTMLFormElement)) {
579 console.error('form_bg_download is not a form');581 console.error('form_bg_upload is not a form');
580 return;582 return;
581 }583 }
582584
583 const formData = new FormData(form);585 const formData = new FormData(form);
586
587 const file = formData.get('avatar');
588 if (!(file instanceof File) || file.size === 0) {
589 form.reset();
590 return;
591 }
592
584 await convertFileIfVideo(formData);593 await convertFileIfVideo(formData);
585 await uploadBackground(formData);594 await uploadBackground(formData);
586 form.reset();595 form.reset();
@@ -614,7 +623,7 @@ async function convertFileIfVideo(formData) {
614 const sourceBuffer = await file.arrayBuffer();623 const sourceBuffer = await file.arrayBuffer();
615 const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name });624 const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name });
616 const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp');625 const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp');
617 const convertedFile = new File([convertedBuffer], convertedFileName, { type: 'image/webp' });626 const convertedFile = new File([new Uint8Array(convertedBuffer)], convertedFileName, { type: 'image/webp' });
618 formData.set('avatar', convertedFile);627 formData.set('avatar', convertedFile);
619 toastMessage.remove();628 toastMessage.remove();
620 } catch (error) {629 } catch (error) {
@@ -693,12 +702,42 @@ function onBackgroundFilterInput() {
693export function initBackgrounds() {702export function initBackgrounds() {
694 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);703 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
695 eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);704 eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
696 $(document).on('click', '.bg_example', onSelectBackgroundClick);705
697 $(document).on('click', '.bg_example_lock', onLockBackgroundClick);706 $(document)
698 $(document).on('click', '.bg_example_unlock', onUnlockBackgroundClick);707 .off('click', '.bg_example').on('click', '.bg_example', onSelectBackgroundClick)
699 $(document).on('click', '.bg_example_edit', onRenameBackgroundClick);708 .off('click', '.bg_example .mobile-only-menu-toggle').on('click', '.bg_example .mobile-only-menu-toggle', function (e) {
700 $(document).on('click', '.bg_example_cross', onDeleteBackgroundClick);709 e.stopPropagation();
701 $(document).on('click', '.bg_example_copy', onCopyToSystemBackgroundClick);710 const $context = $(this).closest('.bg_example');
711 const wasOpen = $context.hasClass('mobile-menu-open');
712 // Close all other open menus before opening a new one.
713 $('.bg_example.mobile-menu-open').removeClass('mobile-menu-open');
714 if (!wasOpen) {
715 $context.addClass('mobile-menu-open');
716 }
717 })
718 .off('click', '.jg-button').on('click', '.jg-button', function (e) {
719 e.stopPropagation();
720 const action = $(this).data('action');
721
722 switch (action) {
723 case 'lock':
724 onLockBackgroundClick.call(this, e.originalEvent);
725 break;
726 case 'unlock':
727 onUnlockBackgroundClick.call(this, e.originalEvent);
728 break;
729 case 'edit':
730 onRenameBackgroundClick.call(this, e.originalEvent);
731 break;
732 case 'delete':
733 onDeleteBackgroundClick.call(this, e.originalEvent);
734 break;
735 case 'copy':
736 onCopyToSystemBackgroundClick.call(this, e.originalEvent);
737 break;
738 }
739 });
740
702 $('#auto_background').on('click', autoBackgroundCommand);741 $('#auto_background').on('click', autoBackgroundCommand);
703 $('#add_bg_button').on('change', onBackgroundUploadSelected);742 $('#add_bg_button').on('change', onBackgroundUploadSelected);
704 $('#bg-filter').on('input', onBackgroundFilterInput);743 $('#bg-filter').on('input', onBackgroundFilterInput);
public/scripts/chats.js+28 -1
@@ -1758,7 +1758,34 @@ export function addDOMPurifyHooks() {
17581758
1759 // Replace line breaks with <br> in unknown elements1759 // Replace line breaks with <br> in unknown elements
1760 if (node instanceof HTMLUnknownElement) {1760 if (node instanceof HTMLUnknownElement) {
1761 node.innerHTML = node.innerHTML.trim().replaceAll('\n', '<br>');1761 node.innerHTML = node.innerHTML.trim();
1762
1763 /** @type {Text[]} */
1764 const candidates = [];
1765 const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
1766 while (walker.nextNode()) {
1767 const textNode = /** @type {Text} */ (walker.currentNode);
1768 if (!textNode.data.includes('\n')) continue;
1769
1770 // Skip if this text node is within a <pre> (any ancestor)
1771 if (textNode.parentElement && textNode.parentElement.closest('pre')) continue;
1772
1773 candidates.push(textNode);
1774 }
1775
1776 for (const textNode of candidates) {
1777 const parts = textNode.data.split('\n');
1778 const frag = document.createDocumentFragment();
1779 parts.forEach((part, idx) => {
1780 if (part.length) {
1781 frag.appendChild(document.createTextNode(part));
1782 }
1783 if (idx < parts.length - 1) {
1784 frag.appendChild(document.createElement('br'));
1785 }
1786 });
1787 textNode.replaceWith(frag);
1788 }
1762 }1789 }
17631790
1764 const isMediaAllowed = isExternalMediaAllowed();1791 const isMediaAllowed = isExternalMediaAllowed();
public/scripts/dynamic-styles.js+52 -16
@@ -33,7 +33,8 @@ const observer = new MutationObserver(mutations => {
33 * @param {boolean} [options.fromExtension=false] - Indicates if the styles are from an extension33 * @param {boolean} [options.fromExtension=false] - Indicates if the styles are from an extension
34 */34 */
35function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {35function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {
36 /** @type {{baseSelector: string, rule: CSSStyleRule}[]} */36 /** @typedef {{ type: 'media'|'supports'|'container', conditionText: string }} WrapperCond */
37 /** @type {{baseSelector: string, rule: CSSStyleRule, wrappers: WrapperCond[]}[]} */
37 const hoverRules = [];38 const hoverRules = [];
38 /** @type {Set<string>} */39 /** @type {Set<string>} */
39 const focusRules = new Set();40 const focusRules = new Set();
@@ -41,14 +42,28 @@ function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {
41 const PLACEHOLDER = ':__PLACEHOLDER__';42 const PLACEHOLDER = ':__PLACEHOLDER__';
4243
43 /**44 /**
45 * Builds a stable signature string for a chain of wrapper conditions so we can distinguish
46 * identical selectors under different contexts (e.g., different @media queries)
47 * @param {WrapperCond[]} wrappers
48 * @returns {string}
49 */
50 function wrapperSignature(wrappers) {
51 return wrappers.map(w => `${w.type}:${w.conditionText}`).join(';');
52 }
53
54 /**
44 * Processes the CSS rules and separates selectors for hover and focus55 * Processes the CSS rules and separates selectors for hover and focus
45 * @param {CSSRuleList} rules - The CSS rules to process56 * @param {CSSRuleList} rules - The CSS rules to process
57 * @param {WrapperCond[]} wrappers - Current chain of wrapper conditions (@media/@supports/etc.)
46 */58 */
47 function processRules(rules) {59 function processRules(rules, wrappers = []) {
48 Array.from(rules).forEach(rule => {60 Array.from(rules).forEach(rule => {
49 if (rule instanceof CSSImportRule) {61 if (rule instanceof CSSImportRule) {
50 // Make sure that @import rules are processed recursively62 // Make sure that @import rules are processed recursively
51 processImportedStylesheet(rule.styleSheet);63 // If the @import has media conditions, treat them as wrappers as well
64 /** @type {WrapperCond[]} */
65 const extra = (rule.media && rule.media.mediaText) ? [{ type: 'media', conditionText: rule.media.mediaText }] : [];
66 processImportedStylesheet(rule.styleSheet, [...wrappers, ...extra]);
52 } else if (rule instanceof CSSStyleRule) {67 } else if (rule instanceof CSSStyleRule) {
53 // Separate multiple selectors on a rule68 // Separate multiple selectors on a rule
54 const selectors = rule.selectorText.split(',').map(s => s.trim());69 const selectors = rule.selectorText.split(',').map(s => s.trim());
@@ -60,17 +75,25 @@ function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {
60 // We currently do nothing here. Rules containing both hover and focus are very specific and should never be automatically touched75 // We currently do nothing here. Rules containing both hover and focus are very specific and should never be automatically touched
61 }76 }
62 else if (isHover) {77 else if (isHover) {
63 const baseSelector = selector.replace(':hover', PLACEHOLDER).trim();78 const baseSelector = selector.replace(/:hover/g, PLACEHOLDER).trim();
64 hoverRules.push({ baseSelector, rule });79 hoverRules.push({ baseSelector, rule, wrappers: [...wrappers] });
65 } else if (isFocus) {80 } else if (isFocus) {
66 // We need to make sure that we remember all existing :focus, :focus-within and :focus-visible rules81 // We need to make sure that we remember all existing :focus, :focus-within and :focus-visible rules
67 const baseSelector = selector.replace(':focus-within', PLACEHOLDER).replace(':focus-visible', PLACEHOLDER).replace(':focus', PLACEHOLDER).trim();82 const baseSelector = selector.replace(/:focus(-within|-visible)?/g, PLACEHOLDER).trim();
68 focusRules.add(baseSelector);83 focusRules.add(`${baseSelector}|${wrapperSignature(wrappers)}`);
69 }84 }
70 });85 });
71 } else if (rule instanceof CSSMediaRule || rule instanceof CSSSupportsRule) {86 } else if (rule instanceof CSSMediaRule) {
72 // Recursively process nested rules87 // Recursively process nested @media rules
73 processRules(rule.cssRules);88 processRules(rule.cssRules, [...wrappers, { type: 'media', conditionText: rule.conditionText }]);
89 } else if (rule instanceof CSSSupportsRule) {
90 // Recursively process nested @supports rules
91 processRules(rule.cssRules, [...wrappers, { type: 'supports', conditionText: rule.conditionText }]);
92 } else if (rule instanceof window.CSSContainerRule) {
93 // Recursively process nested @container rules (if supported by the browser)
94 // Note: conditionText contains the query like "(min-width: 300px)" or "style(color)"
95 // Using 'container' as the type ensures uniqueness separate from @media/@supports
96 processRules(rule.cssRules, [...wrappers, { type: 'container', conditionText: rule.conditionText }]);
74 }97 }
75 });98 });
76 }99 }
@@ -78,21 +101,22 @@ function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {
78 /**101 /**
79 * Processes the CSS rules of an imported stylesheet recursively102 * Processes the CSS rules of an imported stylesheet recursively
80 * @param {CSSStyleSheet} sheet - The imported stylesheet to process103 * @param {CSSStyleSheet} sheet - The imported stylesheet to process
104 * @param {WrapperCond[]} wrappers - Wrapper conditions inherited from (at)import media
81 */105 */
82 function processImportedStylesheet(sheet) {106 function processImportedStylesheet(sheet, wrappers = []) {
83 if (sheet && sheet.cssRules) {107 if (sheet && sheet.cssRules) {
84 processRules(sheet.cssRules);108 processRules(sheet.cssRules, wrappers);
85 }109 }
86 }110 }
87111
88 processRules(styleSheet.cssRules);112 processRules(styleSheet.cssRules, []);
89113
90 /** @type {CSSStyleSheet} */114 /** @type {CSSStyleSheet} */
91 let targetStyleSheet = null;115 let targetStyleSheet = null;
92116
93 // Now finally create the dynamic focus rules117 // Now finally create the dynamic focus rules
94 hoverRules.forEach(({ baseSelector, rule }) => {118 hoverRules.forEach(({ baseSelector, rule, wrappers }) => {
95 if (!focusRules.has(baseSelector)) {119 if (!focusRules.has(`${baseSelector}|${wrapperSignature(wrappers)}`)) {
96 // Only initialize the dynamic stylesheet if needed120 // Only initialize the dynamic stylesheet if needed
97 targetStyleSheet ??= getDynamicStyleSheet({ fromExtension });121 targetStyleSheet ??= getDynamicStyleSheet({ fromExtension });
98122
@@ -103,7 +127,19 @@ function applyDynamicFocusStyles(styleSheet, { fromExtension = false } = {}) {
103 // If something like :focus-within or a more specific selector like `.blah:has(:focus-visible)` for elements inside,127 // If something like :focus-within or a more specific selector like `.blah:has(:focus-visible)` for elements inside,
104 // it should be manually defined in CSS.128 // it should be manually defined in CSS.
105 const focusSelector = rule.selectorText.replace(/:hover/g, ':focus-visible');129 const focusSelector = rule.selectorText.replace(/:hover/g, ':focus-visible');
106 const focusRule = `${focusSelector} { ${rule.style.cssText} }`;130 let focusRule = `${focusSelector} { ${rule.style.cssText} }`;
131
132 // Wrap the generated rule into the same @media/@supports/@container chain (if any)
133 if (wrappers.length > 0) {
134 // Build nested blocks from outermost to innermost
135 // Example: @media (x) { @supports (y) { <rule> } }
136 focusRule = wrappers.reduceRight((inner, w) => {
137 if (w.type === 'media') return `@media ${w.conditionText} { ${inner} }`;
138 if (w.type === 'supports') return `@supports ${w.conditionText} { ${inner} }`;
139 if (w.type === 'container') return `@container ${w.conditionText} { ${inner} }`;
140 return inner;
141 }, focusRule);
142 }
107143
108 try {144 try {
109 targetStyleSheet.insertRule(focusRule, targetStyleSheet.cssRules.length);145 targetStyleSheet.insertRule(focusRule, targetStyleSheet.cssRules.length);
public/scripts/extensions.js+2 -0
@@ -188,6 +188,8 @@ export const extension_settings = {
188 dice: {},188 dice: {},
189 /** @type {import('./char-data.js').RegexScriptData[]} */189 /** @type {import('./char-data.js').RegexScriptData[]} */
190 regex: [],190 regex: [],
191 /** @type {import('./extensions/regex/index.js').RegexPreset[]} */
192 regex_presets: [],
191 character_allowed_regex: [],193 character_allowed_regex: [],
192 tts: {},194 tts: {},
193 sd: {195 sd: {
public/scripts/extensions/caption/index.js+14 -4
@@ -439,6 +439,8 @@ jQuery(async function () {
439 'cohere': SECRET_KEYS.COHERE,439 'cohere': SECRET_KEYS.COHERE,
440 'aimlapi': SECRET_KEYS.AIMLAPI,440 'aimlapi': SECRET_KEYS.AIMLAPI,
441 'moonshot': SECRET_KEYS.MOONSHOT,441 'moonshot': SECRET_KEYS.MOONSHOT,
442 'nanogpt': SECRET_KEYS.NANOGPT,
443 'electronhub': SECRET_KEYS.ELECTRONHUB,
442 };444 };
443445
444 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {446 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
@@ -543,8 +545,10 @@ jQuery(async function () {
543 }545 }
544546
545 await processEndpoint('openrouter', '/api/openrouter/models/multimodal');547 await processEndpoint('openrouter', '/api/openrouter/models/multimodal');
546 await processEndpoint('aimlapi', '/api/backends/chat-completions/aimlapi/models/multimodal');548 await processEndpoint('aimlapi', '/api/backends/chat-completions/multimodal-models/aimlapi');
547 await processEndpoint('pollinations', '/api/backends/chat-completions/pollinations/models/multimodal');549 await processEndpoint('pollinations', '/api/backends/chat-completions/multimodal-models/pollinations');
550 await processEndpoint('nanogpt', '/api/backends/chat-completions/multimodal-models/nanogpt');
551 await processEndpoint('electronhub', '/api/backends/chat-completions/multimodal-models/electronhub');
548 }552 }
549553
550 await addSettings();554 await addSettings();
@@ -588,10 +592,12 @@ jQuery(async function () {
588 saveSettingsDebounced();592 saveSettingsDebounced();
589 });593 });
590 $('#caption_ollama_pull').on('click', (e) => {594 $('#caption_ollama_pull').on('click', (e) => {
591 const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : '';595 const selectedModel = extension_settings.caption.multimodal_model;
596 const staticModels = { 'ollama_current': textgenerationwebui_settings.ollama_model, 'ollama_custom': extension_settings.caption.ollama_custom_model };
597 const presetModel = staticModels[selectedModel] || selectedModel;
592 e.preventDefault();598 e.preventDefault();
593 $('#ollama_download_model').trigger('click');599 $('#ollama_download_model').trigger('click');
594 $('#dialogue_popup_input').val(presetModel);600 $('.popup .popup-input').val(presetModel);
595 });601 });
596 $('#caption_multimodal_api').on('change', async () => {602 $('#caption_multimodal_api').on('change', async () => {
597 const api = String($('#caption_multimodal_api').val());603 const api = String($('#caption_multimodal_api').val());
@@ -616,6 +622,10 @@ jQuery(async function () {
616 extension_settings.caption.show_in_chat = !!$('#caption_show_in_chat').prop('checked');622 extension_settings.caption.show_in_chat = !!$('#caption_show_in_chat').prop('checked');
617 saveSettingsDebounced();623 saveSettingsDebounced();
618 });624 });
625 $('#caption_ollama_custom_model').val(extension_settings.caption.ollama_custom_model || '').on('input', () => {
626 extension_settings.caption.ollama_custom_model = String($('#caption_ollama_custom_model').val()).trim();
627 saveSettingsDebounced();
628 });
619629
620 const onMessageEvent = async (index) => {630 const onMessageEvent = async (index) => {
621 if (!extension_settings.caption.auto_mode) {631 if (!extension_settings.caption.auto_mode) {
public/scripts/extensions/caption/settings.html+28 -7
@@ -21,6 +21,7 @@
21 <option value="anthropic">Anthropic</option>21 <option value="anthropic">Anthropic</option>
22 <option value="cohere">Cohere</option>22 <option value="cohere">Cohere</option>
23 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>23 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
24 <option value="electronhub">Electron Hub</option>
24 <option value="google">Google AI Studio</option>25 <option value="google">Google AI Studio</option>
25 <option value="vertexai">Google Vertex AI</option>26 <option value="vertexai">Google Vertex AI</option>
26 <option value="groq">Groq</option>27 <option value="groq">Groq</option>
@@ -28,6 +29,7 @@
28 <option value="llamacpp">llama.cpp</option>29 <option value="llamacpp">llama.cpp</option>
29 <option value="mistral">MistralAI</option>30 <option value="mistral">MistralAI</option>
30 <option value="moonshot">Moonshot AI</option>31 <option value="moonshot">Moonshot AI</option>
32 <option value="nanogpt">NanoGPT</option>
31 <option value="ollama">Ollama</option>33 <option value="ollama">Ollama</option>
32 <option value="openai">OpenAI</option>34 <option value="openai">OpenAI</option>
33 <option value="openrouter">OpenRouter</option>35 <option value="openrouter">OpenRouter</option>
@@ -40,7 +42,7 @@
40 <div class="flex1 flex-container flexFlowColumn flexNoGap">42 <div class="flex1 flex-container flexFlowColumn flexNoGap">
41 <label for="caption_multimodal_model" data-i18n="Model">Model</label>43 <label for="caption_multimodal_model" data-i18n="Model">Model</label>
42 <select id="caption_multimodal_model" class="flex1 text_pole">44 <select id="caption_multimodal_model" class="flex1 text_pole">
43 <!-- AI/ML API, OpenRouter, Pollinations are added externally by JavaScript -->45 <!-- AI/ML API, OpenRouter, Pollinations, NanoGPT are added externally by JavaScript -->
44 <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option>46 <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option>
45 <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option>47 <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option>
46 <option data-type="cohere" value="command-a-vision-07-2025">command-a-vision-07-2025</option>48 <option data-type="cohere" value="command-a-vision-07-2025">command-a-vision-07-2025</option>
@@ -110,6 +112,7 @@
110 <option data-type="google" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>112 <option data-type="google" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
111 <option data-type="google" value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>113 <option data-type="google" value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>
112 <option data-type="google" value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>114 <option data-type="google" value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>
115 <option data-type="google" value="gemini-2.5-flash-image-preview">gemini-2.5-flash-image-preview</option>
113 <option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>116 <option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>
114 <option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp → 2.5-pro-exp-03-25</option>117 <option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp → 2.5-pro-exp-03-25</option>
115 <option data-type="google" value="gemini-exp-1206">gemini-exp-1206 → 2.5-pro-exp-03-25</option>118 <option data-type="google" value="gemini-exp-1206">gemini-exp-1206 → 2.5-pro-exp-03-25</option>
@@ -146,17 +149,26 @@
146 <option data-type="vertexai" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>149 <option data-type="vertexai" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
147 <option data-type="vertexai" value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>150 <option data-type="vertexai" value="gemini-2.5-flash-lite">gemini-2.5-flash-lite</option>
148 <option data-type="vertexai" value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>151 <option data-type="vertexai" value="gemini-2.5-flash-lite-preview-06-17">gemini-2.5-flash-lite-preview-06-17</option>
152 <option data-type="vertexai" value="gemini-2.5-flash-image-preview">gemini-2.5-flash-image-preview</option>
149 <option data-type="vertexai" value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>153 <option data-type="vertexai" value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>
150 <option data-type="vertexai" value="gemini-2.0-flash-lite-001">gemini-2.0-flash-lite-001</option>154 <option data-type="vertexai" value="gemini-2.0-flash-lite-001">gemini-2.0-flash-lite-001</option>
151 <option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>155 <option data-type="groq" value="meta-llama/llama-4-scout-17b-16e-instruct">meta-llama/llama-4-scout-17b-16e-instruct</option>
152 <option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>156 <option data-type="groq" value="meta-llama/llama-4-maverick-17b-128e-instruct">meta-llama/llama-4-maverick-17b-128e-instruct</option>
153 <option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>
154 <option data-type="ollama" value="ollama_current" data-i18n="currently_selected">[Currently selected]</option>157 <option data-type="ollama" value="ollama_current" data-i18n="currently_selected">[Currently selected]</option>
158 <option data-type="ollama" value="ollama_custom" data-i18n="[Custom model]">[Custom model]</option>
155 <option data-type="ollama" value="bakllava">bakllava</option>159 <option data-type="ollama" value="bakllava">bakllava</option>
156 <option data-type="ollama" value="llava">llava</option>160 <option data-type="ollama" value="llava">llava</option>
157 <option data-type="ollama" value="llava-llama3">llava-llama3</option>161 <option data-type="ollama" value="llava-llama3">llava-llama3</option>
158 <option data-type="ollama" value="llava-phi3">llava-phi3</option>162 <option data-type="ollama" value="llava-phi3">llava-phi3</option>
159 <option data-type="ollama" value="moondream">moondream</option>163 <option data-type="ollama" value="moondream">moondream</option>
164 <option data-type="ollama" value="gemma3">gemma3</option>
165 <option data-type="ollama" value="minicpm-v">minicpm-v</option>
166 <option data-type="ollama" value="qwen2.5vl">qwen2.5vl</option>
167 <option data-type="ollama" value="granite3.2-vision">granite3.2-vision</option>
168 <option data-type="ollama" value="mistral-small3.1">mistral-small3.1</option>
169 <option data-type="ollama" value="mistral-small3.2">mistral-small3.2</option>
170 <option data-type="ollama" value="llama3.2-vision">llama3.2-vision</option>
171 <option data-type="ollama" value="llama4">llama4</option>
160 <option data-type="llamacpp" value="llamacpp_current" data-i18n="currently_loaded">[Currently loaded]</option>172 <option data-type="llamacpp" value="llamacpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
161 <option data-type="ooba" value="ooba_current" data-i18n="currently_loaded">[Currently loaded]</option>173 <option data-type="ooba" value="ooba_current" data-i18n="currently_loaded">[Currently loaded]</option>
162 <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option>174 <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
@@ -168,16 +180,25 @@
168 </select>180 </select>
169 </div>181 </div>
170 <div data-type="ollama">182 <div data-type="ollama">
171 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.183 <div>
184 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.
185 </div>
186 <div class="marginTop5">
187 <label for="caption_ollama_custom_model">
188 <span data-i18n="Custom Model Tag">Custom Model Tag</span>
189 <small data-i18n="(for [Custom model] option)">(for [Custom model] option)</small>
190 </label>
191 <input id="caption_ollama_custom_model" class="text_pole" type="text" placeholder="e.g. gemma3:latest" />
192 </div>
172 </div>193 </div>
173 <label data-type="openai,anthropic,google,vertexai,mistral,xai" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">194 <label data-type="openai,anthropic,google,vertexai,mistral,xai" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
174 <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">195 <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
175 <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>196 <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>
176 </label>197 </label>
177 <div class="flexBasis100p m-b-1">198 <div class="flexBasis100p marginBot10">
178 <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small>199 <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small>
179 </div>200 </div>
180 <div data-type="koboldcpp,ollama,vllm,llamacpp,ooba" class="flex-container flexFlowColumn">201 <div data-type="koboldcpp,ollama,vllm,llamacpp,ooba" class="flex-container flexFlowColumn wide100p">
181 <label for="caption_altEndpoint_enabled" class="checkbox_label">202 <label for="caption_altEndpoint_enabled" class="checkbox_label">
182 <input id="caption_altEndpoint_enabled" type="checkbox">203 <input id="caption_altEndpoint_enabled" type="checkbox">
183 <span data-i18n="Use secondary URL">Use secondary URL</span>204 <span data-i18n="Use secondary URL">Use secondary URL</span>
public/scripts/extensions/connection-manager/index.js+11 -0
@@ -43,6 +43,7 @@ const CC_COMMANDS = [
43 'reasoning-template',43 'reasoning-template',
44 'prompt-post-processing',44 'prompt-post-processing',
45 'secret-id',45 'secret-id',
46 'regex-preset',
46];47];
4748
48const TC_COMMANDS = [49const TC_COMMANDS = [
@@ -60,6 +61,7 @@ const TC_COMMANDS = [
60 'start-reply-with',61 'start-reply-with',
61 'reasoning-template',62 'reasoning-template',
62 'secret-id',63 'secret-id',
64 'regex-preset',
63];65];
6466
65const FANCY_NAMES = {67const FANCY_NAMES = {
@@ -79,6 +81,7 @@ const FANCY_NAMES = {
79 'reasoning-template': 'Reasoning Template',81 'reasoning-template': 'Reasoning Template',
80 'prompt-post-processing': 'Prompt Post-Processing',82 'prompt-post-processing': 'Prompt Post-Processing',
81 'secret-id': 'Secret',83 'secret-id': 'Secret',
84 'regex-preset': 'Regex Preset',
82};85};
8386
84/**87/**
@@ -357,6 +360,14 @@ function makeFancyProfile(profile) {
357 }360 }
358 }361 }
359362
363 if (key === 'regex-preset') {
364 const label = extension_settings.regex_presets?.find(p => p.id === profile[key])?.name;
365 if (label) {
366 acc[value] = label;
367 return acc;
368 }
369 }
370
360 acc[value] = profile[key];371 acc[value] = profile[key];
361 return acc;372 return acc;
362 }, {});373 }, {});
public/scripts/extensions/gallery/index.js+45 -5
@@ -63,10 +63,10 @@ mutationObserver.observe(document.body, {
63});63});
6464
65const SORT = Object.freeze({65const SORT = Object.freeze({
66 NAME_ASC: { value: 'nameAsc', field: 'name', order: 'asc', label: t`Sort By: Name (A-Z)` },66 NAME_ASC: { value: 'nameAsc', field: 'name', order: 'asc', label: t`Name (A-Z)` },
67 NAME_DESC: { value: 'nameDesc', field: 'name', order: 'desc', label: t`Sort By: Name (Z-A)` },67 NAME_DESC: { value: 'nameDesc', field: 'name', order: 'desc', label: t`Name (Z-A)` },
68 DATE_ASC: { value: 'dateAsc', field: 'date', order: 'asc', label: t`Sort By: Date (Oldest First)` },68 DATE_DESC: { value: 'dateDesc', field: 'date', order: 'desc', label: t`Newest` },
69 DATE_DESC: { value: 'dateDesc', field: 'date', order: 'desc', label: t`Sort By: Date (Newest First)` },69 DATE_ASC: { value: 'dateAsc', field: 'date', order: 'asc', label: t`Oldest` },
70});70});
7171
72const defaultSettings = Object.freeze({72const defaultSettings = Object.freeze({
@@ -376,6 +376,11 @@ async function makeMovable(url) {
376 const titleText = document.createElement('span');376 const titleText = document.createElement('span');
377 titleText.textContent = t`Image Gallery`;377 titleText.textContent = t`Image Gallery`;
378 dragTitle.append(titleText);378 dragTitle.append(titleText);
379
380 // Create a container for the controls
381 const controlsContainer = document.createElement('div');
382 controlsContainer.classList.add('flex-container', 'alignItemsCenter');
383
379 const sortSelect = document.createElement('select');384 const sortSelect = document.createElement('select');
380 sortSelect.classList.add('gallery-sort-select');385 sortSelect.classList.add('gallery-sort-select');
381386
@@ -394,7 +399,42 @@ async function makeMovable(url) {
394 });399 });
395400
396 sortSelect.value = getSortOrder();401 sortSelect.value = getSortOrder();
397 dragTitle.append(sortSelect);402 controlsContainer.appendChild(sortSelect);
403
404 // Create the "Add Image" button
405 const addImageButton = document.createElement('div');
406 addImageButton.classList.add('menu_button', 'menu_button_icon', 'interactable');
407 addImageButton.title = 'Add Image';
408 addImageButton.innerHTML = '<i class="fa-solid fa-plus fa-fw"></i><div>Add Image</div>';
409
410 // Create a hidden file input
411 const fileInput = document.createElement('input');
412 fileInput.type = 'file';
413 fileInput.accept = 'image/*';
414 fileInput.multiple = true;
415 fileInput.style.display = 'none';
416
417 // Trigger file input when the button is clicked
418 addImageButton.addEventListener('click', () => {
419 fileInput.click();
420 });
421
422 // Handle file selection
423 fileInput.addEventListener('change', async () => {
424 const files = fileInput.files;
425 if (files.length > 0) {
426 for (const file of files) {
427 await uploadFile(file, url);
428 }
429 // Refresh the gallery
430 closeButton.trigger('click');
431 await showCharGallery();
432 }
433 });
434
435 controlsContainer.appendChild(addImageButton);
436 dragTitle.append(controlsContainer);
437 newElement.append(fileInput); // Append hidden file input to the main element
398438
399 // add no-scrollbar class to this element439 // add no-scrollbar class to this element
400 newElement.addClass('no-scrollbar');440 newElement.addClass('no-scrollbar');
public/scripts/extensions/gallery/style.css+3 -2
@@ -2,6 +2,7 @@
2 display: flex;2 display: flex;
3 align-items: center;3 align-items: center;
4 justify-content: center;4 justify-content: center;
5 gap: 4px;
5}6}
67
7.gallery-folder-input {8.gallery-folder-input {
@@ -26,13 +27,13 @@
26 text-overflow: ellipsis;27 text-overflow: ellipsis;
27 width: 100%;28 width: 100%;
28 opacity: 0.8;29 opacity: 0.8;
29 background: none;30 background-color: var(--black30a);
31 border: 1px solid var(--SmartThemeBorderColor);
30 background-image: url(/img/down-arrow.svg);32 background-image: url(/img/down-arrow.svg);
31 background-repeat: no-repeat;33 background-repeat: no-repeat;
32 background-position: right 6px center;34 background-position: right 6px center;
33 background-size: 8px 5px;35 background-size: 8px 5px;
34 padding-right: 20px;36 padding-right: 20px;
35 font-size: calc(var(--mainFontSize)* 0.9);
36 margin-bottom: 0;37 margin-bottom: 0;
37}38}
3839
public/scripts/extensions/regex/debugger.css+263 -0
@@ -0,0 +1,263 @@
1/* Styles for the debugger UI */
2#regex_debugger_rules {
3 margin: 10px 0;
4}
5
6#regex_debugger_rules,
7#regex_debugger_rules .sortable-list {
8 padding-left: 0;
9}
10
11.regex-debugger-rules-list {
12 position: relative;
13}
14
15.regex-debugger-rule {
16 display: flex;
17 align-items: center;
18 padding: 8px 10px;
19 border: 1px solid var(--SmartThemeBorderColor);
20 border-radius: 5px;
21 cursor: pointer;
22 background-color: var(--black30a);
23 gap: 5px;
24 margin-bottom: 5px;
25}
26
27#regex_debugger_run_test_header {
28 justify-content: space-between;
29 align-items: center;
30}
31
32#regex_debugger_expand_steps,
33#regex_debugger_expand_final,
34#regex_debugger_save_order {
35 position: absolute;
36 top: 0;
37 right: 0;
38 margin: 0;
39}
40
41.regex-debugger-rule:hover {
42 filter: brightness(1.1);
43}
44
45.regex-debugger-rule .handle {
46 cursor: grab;
47 margin-right: 5px;
48}
49
50.regex-debugger-rule .rule-details {
51 flex-grow: 1;
52 text-align: left;
53 display: flex;
54 align-items: baseline;
55 gap: 5px;
56}
57
58.regex-debugger-rule .rule-name {
59 font-weight: bold;
60}
61
62.regex-debugger-rule .rule-regex {
63 font-size: 0.8em;
64 opacity: 0.8;
65 font-family: var(--monoFontFamily);
66}
67
68.regex-debugger-rule .rule-scope {
69 font-size: 0.8em;
70 padding: 2px 6px;
71 border-radius: 5px;
72 background-color: var(--black30a);
73 margin-left: auto;
74 margin-right: 10px;
75}
76
77.regex-debugger-rule .menu_button {
78 margin: 0;
79}
80
81#regex_debugger_raw_input {
82 min-height: 1.8em;
83}
84
85#regex_debugger_steps_output {
86 min-height: 2em;
87 max-height: 300px;
88 overflow-y: auto;
89 border: 1px solid var(--SmartThemeBorderColor);
90 border-radius: 5px;
91 padding: 5px;
92 background-color: var(--black30a);
93 font-family: var(--monoFontFamily);
94 font-size: 0.9em;
95}
96
97#regex_debugger_final_output {
98 min-height: 2em;
99 max-height: 300px;
100 overflow-y: auto;
101 border: 1px solid var(--SmartThemeBorderColor);
102 border-radius: 5px;
103 padding: 5px;
104 background-color: var(--black30a);
105 white-space: pre-wrap;
106 word-break: break-word;
107 text-align: left;
108}
109
110.step-header {
111 margin-top: 10px;
112 margin-bottom: 5px;
113}
114
115.step-output {
116 white-space: pre-wrap;
117 word-break: break-all;
118 padding: 5px;
119 background-color: var(--black30a);
120 border-radius: 5px;
121 text-align: left;
122}
123
124/* Classes to replace inline styles */
125.regex-debugger-no-rules {
126 padding: 10px;
127 text-align: center;
128 opacity: 0.8;
129}
130
131.regex-debugger-list-header {
132 font-weight: bold;
133 padding: 10px;
134}
135
136/* Styles for statistics */
137.step-header {
138 display: flex;
139 justify-content: space-between;
140 align-items: center;
141}
142
143.step-metrics {
144 font-size: 0.8em;
145 opacity: 0.8;
146 font-weight: normal;
147}
148
149.regex-debugger-summary {
150 padding: 8px;
151 margin-bottom: 10px;
152 border: 1px solid var(--SmartThemeBorderColor);
153 background-color: var(--black30a);
154 border-radius: 5px;
155 text-align: center;
156 font-size: 0.9em;
157}
158
159.regex-debugger-tester .results-header {
160 position: relative;
161 margin: 10px 0;
162}
163
164.regex-debugger-tester .radio_group {
165 text-align: left;
166}
167
168/* Styles for statistics and highlighting additions */
169.step-header {
170 display: flex;
171 justify-content: space-between;
172 align-items: center;
173 flex-wrap: wrap;
174 /* Allow wrapping on small screens */
175}
176
177.step-metrics {
178 font-size: 0.8em;
179 opacity: 0.8;
180 font-weight: normal;
181 white-space: nowrap;
182 /* Prevent metrics from breaking line */
183 margin-left: 10px;
184}
185
186.regex-debugger-summary {
187 padding: 8px;
188 margin-bottom: 10px;
189 border: 1px solid var(--SmartThemeBorderColor);
190 background-color: var(--black30a);
191 border-radius: 5px;
192 text-align: center;
193 font-size: 0.9em;
194}
195
196/* New highlight color for added text */
197mark.green_hl {
198 background-color: #28a745;
199 /* A standard green color */
200 color: white;
201}
202
203/* New highlight color for deleted text */
204mark.red_hl {
205 background-color: #dc3545;
206 /* A standard red color */
207 color: white;
208 text-decoration: line-through;
209}
210
211/* Styles for the expanded view with navigation */
212.expanded-regex-container {
213 display: flex;
214 height: 75vh;
215 /* Give the container a good height */
216 overflow: hidden;
217}
218
219.expanded-regex-nav {
220 flex: 0 0 200px;
221 /* Fixed width for the nav bar */
222 border-right: 1px solid var(--SmartThemeBorderColor);
223 padding: 5px;
224 overflow-y: auto;
225 background-color: var(--black30a);
226}
227
228.expanded-regex-nav a {
229 display: block;
230 padding: 6px 8px;
231 text-decoration: none;
232 color: var(--SmartThemeMainColor);
233 border-radius: 5px;
234 white-space: nowrap;
235 overflow: hidden;
236 text-overflow: ellipsis;
237}
238
239.expanded-regex-nav a:hover {
240 background-color: var(--background_hover_color);
241}
242
243.expanded-regex-nav a.active {
244 background-color: var(--highlight_color);
245 color: var(--text_color_black);
246}
247
248.expanded-regex-content {
249 flex-grow: 1;
250 overflow-y: auto;
251 padding-left: 10px;
252}
253
254#regex_debugger_render_mode {
255 padding-right: 20px;
256 margin-top: 5px;
257}
258
259.regex-popup-content {
260 white-space: pre-wrap;
261 word-break: break-all;
262 text-align: left;
263}
public/scripts/extensions/regex/debugger.html+175 -0
@@ -0,0 +1,175 @@
1<div class="regex-debugger-container">
2 <!-- Rules List Column -->
3 <div class="regex-debugger-rules-list">
4 <h3>
5 <i class="fa-solid fa-list-ol"></i>
6 <span data-i18n="ext_regex_debugger_active_rules"
7 >Active Rules</span
8 >
9 </h3>
10 <div class="flex-container">
11 <button
12 id="regex_debugger_save_order"
13 class="menu_button menu_button_icon interactable"
14 title="Save current rule order"
15 tabindex="0"
16 >
17 <i class="fa-solid fa-floppy-disk"></i>
18 <span data-i18n="ext_regex_debugger_save_order"
19 >Save Order</span
20 >
21 </button>
22 </div>
23 <ul id="regex_debugger_rules" class="sortable-list">
24 <!-- Rules will be populated here by JavaScript -->
25 </ul>
26 </div>
27
28 <!-- Testing Area Column -->
29 <div class="regex-debugger-tester">
30 <h3>
31 <i class="fa-solid fa-vial"></i>
32 <span data-i18n="ext_regex_debugger_testing_area"
33 >Testing Area</span
34 >
35 </h3>
36 <div class="regex-debugger-io">
37 <div class="regex-debugger-input">
38 <label
39 for="regex_debugger_raw_input"
40 data-i18n="ext_regex_debugger_raw_input"
41 >Raw Input</label
42 >
43 <textarea
44 id="regex_debugger_raw_input"
45 class="text_pole autoSetHeight"
46 rows="4"
47 ></textarea>
48 </div>
49 <div
50 id="regex_debugger_run_test_header"
51 class="flex-container"
52 >
53 <button
54 id="regex_debugger_run_test"
55 class="menu_button menu_button_icon interactable"
56 title="Run the test pipeline"
57 tabindex="0"
58 >
59 <i class="fa-solid fa-play"></i>
60 <span data-i18n="ext_regex_debugger_run_test"
61 >Run Test</span
62 >
63 </button>
64 <div class="flex-container gap10px">
65 <div class="radio_group">
66 <label
67 ><input
68 type="radio"
69 name="display_mode"
70 value="replace"
71 checked
72 />
73 <span data-i18n="ext_regex_debugger_display_replace"
74 >Replace</span
75 ></label
76 >
77 <label
78 ><input
79 type="radio"
80 name="display_mode"
81 value="highlight"
82 />
83 <span
84 data-i18n="ext_regex_debugger_display_highlight"
85 >Highlight</span
86 ></label
87 >
88 </div>
89 <select
90 id="regex_debugger_render_mode"
91 >
92 <option
93 value="text"
94 data-i18n="ext_regex_debugger_render_text"
95 >
96 Render as Text
97 </option>
98 <option
99 value="message"
100 data-i18n="ext_regex_debugger_render_message"
101 >
102 Render as Message
103 </option>
104 </select>
105 </div>
106 </div>
107 <div class="regex-debugger-results">
108 <div class="results-header">
109 <h4>
110 <i class="fa-solid fa-shoe-prints"></i>
111 <span data-i18n="ext_regex_debugger_step_by_step"
112 >Step-by-step Transformation</span
113 >
114 </h4>
115 <div
116 id="regex_debugger_expand_steps"
117 class="menu_button menu_button_icon"
118 title="Expand view"
119 >
120 <i class="fa-solid fa-expand"></i>
121 </div>
122 </div>
123 <div id="regex_debugger_steps_output" class="results-box"></div>
124
125 <div class="results-header">
126 <h4>
127 <i class="fa-solid fa-flag-checkered"></i>
128 <span data-i18n="ext_regex_debugger_final_output"
129 >Final Output</span
130 >
131 </h4>
132 <div
133 id="regex_debugger_expand_final"
134 class="menu_button menu_button_icon"
135 title="Expand view"
136 >
137 <i class="fa-solid fa-expand"></i>
138 </div>
139 </div>
140 <div
141 id="regex_debugger_final_output"
142 class="results-box final-output"
143 ></div>
144 </div>
145 </div>
146 </div>
147</div>
148
149<!-- Template for a single rule item -->
150<template id="regex_debugger_rule_template">
151 <li class="regex-debugger-rule" draggable="true">
152 <i class="fa-solid fa-grip-vertical handle"></i>
153 <label class="checkbox">
154 <input type="checkbox" class="rule-enabled" checked />
155 </label>
156 <div class="rule-details">
157 <span class="rule-name"></span>
158 <code class="rule-regex"></code>
159 <small class="rule-scope"></small>
160 </div>
161 <div class="menu_button menu_button_icon edit_rule" title="Edit Rule">
162 <i class="fa-solid fa-pencil"></i>
163 </div>
164 </li>
165</template>
166
167<!-- Template for a single transformation step -->
168<template id="regex_debugger_step_template">
169 <div class="step-result">
170 <div class="step-header">
171 <strong></strong>
172 </div>
173 <pre class="step-output"></pre>
174 </div>
175</template>
public/scripts/extensions/regex/dropdown.html+20 -0
@@ -26,6 +26,10 @@
26 <i class="fa-solid fa-edit"></i>26 <i class="fa-solid fa-edit"></i>
27 <small data-i18n="ext_regex_bulk_edit">Bulk Edit</small>27 <small data-i18n="ext_regex_bulk_edit">Bulk Edit</small>
28 </label>28 </label>
29 <div id="open_regex_debugger" class="menu_button menu_button_icon" data-i18n="[title]ext_regex_debugger_desc" title="Advanced Regex Debugger">
30 <i class="fa-solid fa-bug-slash"></i>
31 <small data-i18n="ext_regex_debugger">Debugger</small>
32 </div>
29 </div>33 </div>
30 <div class="regex_bulk_operations flex-container justifyCenter">34 <div class="regex_bulk_operations flex-container justifyCenter">
31 <div id="bulk_select_all_toggle" class="menu_button menu_button_icon" title="Toggle Select All">35 <div id="bulk_select_all_toggle" class="menu_button menu_button_icon" title="Toggle Select All">
@@ -49,6 +53,22 @@
49 </div>53 </div>
50 </div>54 </div>
51 <hr />55 <hr />
56 <div id="regex_presets_block">
57 <div class="flex-container alignItemsBaseline">
58 <strong class="flex1" data-i18n="ext_regex_presets">Regex Presets</strong>
59 </div>
60 <small data-i18n="ext_regex_presets_desc">
61 Save and switch between groups of enabled regex scripts.
62 </small>
63 <div class="flex-container marginTop5">
64 <select id="regex_presets" class="text_pole flex1"></select>
65 <div id="regex_preset_create" class="menu_button fa-solid fa-file-circle-plus" data-i18n="[title]ext_regex_preset_create" title="Create a new regex preset"></div>
66 <div id="regex_preset_update" class="menu_button fa-solid fa-save" data-i18n="[title]ext_regex_preset_update" title="Update existing regex preset"></div>
67 <div id="regex_preset_apply" class="menu_button fa-solid fa-recycle" data-i18n="[title]ext_regex_preset_apply" title="Re-apply current preset"></div>
68 <div id="regex_preset_delete" class="menu_button fa-solid fa-trash" data-i18n="[title]ext_regex_preset_delete" title="Delete current preset"></div>
69 </div>
70 </div>
71 <hr />
52 <div id="global_scripts_block" class="padding5">72 <div id="global_scripts_block" class="padding5">
53 <div>73 <div>
54 <strong data-i18n="ext_regex_global_scripts">Global Scripts</strong>74 <strong data-i18n="ext_regex_global_scripts">Global Scripts</strong>
public/scripts/extensions/regex/index.js+905 -6
@@ -1,13 +1,13 @@
1import { characters, eventSource, event_types, getCurrentChatId, reloadCurrentChat, saveSettingsDebounced, this_chid } from '../../../script.js';1import { characters, eventSource, event_types, getCurrentChatId, messageFormatting, reloadCurrentChat, saveSettingsDebounced, this_chid } from '../../../script.js';
2import { extension_settings, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';2import { extension_settings, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';
3import { selected_group } from '../../group-chats.js';3import { selected_group } from '../../group-chats.js';
4import { callGenericPopup, POPUP_TYPE } from '../../popup.js';4import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js';
5import { SlashCommand } from '../../slash-commands/SlashCommand.js';5import { SlashCommand } from '../../slash-commands/SlashCommand.js';
6import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';6import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
7import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';7import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
8import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';8import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
9import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';9import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
10import { download, equalsIgnoreCaseAndAccents, getFileText, getSortableDelay, isFalseBoolean, isTrueBoolean, regexFromString, setInfoBlock, uuidv4 } from '../../utils.js';10import { download, equalsIgnoreCaseAndAccents, getFileText, getSortableDelay, isFalseBoolean, isTrueBoolean, regexFromString, setInfoBlock, uuidv4, escapeHtml } from '../../utils.js';
11import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';11import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';
12import { t } from '../../i18n.js';12import { t } from '../../i18n.js';
13import { accountStorage } from '../../util/AccountStorage.js';13import { accountStorage } from '../../util/AccountStorage.js';
@@ -19,6 +19,452 @@ const sanitizeFileName = name => name.replace(/[\s.<>:"/\\|?*\x00-\x1F\x7F]/g, '
19 */19 */
2020
21/**21/**
22 * @typedef {object} RegexPresetItem
23 * @property {string} id - UUID of the regex script
24 */
25
26/**
27 * @typedef {object} RegexPreset
28 * @property {string} id - UUID of the preset
29 * @property {string} name - Name of the preset
30 * @property {boolean} isSelected - Whether the preset is currently selected
31 * @property {RegexPresetItem[]} global - The list of global preset items
32 * @property {RegexPresetItem[]} scoped - The list of scoped preset items
33 */
34
35/**
36 * @typedef {object} RegexPresetState
37 * @property {string[]} global - List of enabled global regex script IDs
38 * @property {string[]} scoped - List of enabled scoped regex script IDs
39 */
40
41class RegexPresetManager {
42 /** @type {HTMLSelectElement} */
43 presetSelect = null;
44
45 /** @type {HTMLElement} */
46 presetCreateButton = null;
47
48 /** @type {HTMLElement} */
49 presetUpdateButton = null;
50
51 /** @type {HTMLElement} */
52 presetApplyButton = null;
53
54 /** @type {HTMLElement} */
55 presetDeleteButton = null;
56
57 /** @type {string|null} */
58 currentPresetId = null;
59
60 /** @type {RegexPresetState|null} */
61 lastKnownState = null;
62
63 /**
64 * Captures the current state of enabled regex scripts for change detection.
65 * @returns {RegexPresetState} The current state object
66 */
67 captureCurrentState() {
68 const globalScripts = this.regexListToPresetItems(extension_settings.regex) || [];
69 const scopedScripts = this.regexListToPresetItems(characters[this_chid]?.data?.extensions?.regex_scripts) || [];
70
71 return {
72 global: globalScripts.map(item => item.id).sort(),
73 scoped: scopedScripts.map(item => item.id).sort(),
74 };
75 }
76
77 /**
78 * Compares two state objects to detect changes.
79 * @param {RegexPresetState} state1 First state object
80 * @param {RegexPresetState} state2 Second state object
81 * @returns {boolean} True if states are different
82 */
83 hasStateChanged(state1, state2) {
84 if (!state1 || !state2) return false;
85
86 const global1 = state1.global || [];
87 const global2 = state2.global || [];
88 const scoped1 = state1.scoped || [];
89 const scoped2 = state2.scoped || [];
90
91 if (global1.length !== global2.length || scoped1.length !== scoped2.length) {
92 return true;
93 }
94
95 return !global1.every(id => global2.includes(id)) ||
96 !scoped1.every(id => scoped2.includes(id));
97 }
98
99 /**
100 * Updates the stored state after a preset is applied or saved.
101 * @param {string} presetId - The current preset ID
102 */
103 updateStoredState(presetId) {
104 this.currentPresetId = presetId;
105 this.lastKnownState = this.captureCurrentState();
106 }
107
108 /**
109 * Checks if there are unsaved changes and shows a confirmation dialog.
110 * @returns {Promise<boolean>} True if user wants to proceed without saving
111 */
112 async checkUnsavedChanges() {
113 if (!this.currentPresetId || !this.lastKnownState) {
114 return true; // No current preset or state to compare
115 }
116
117 const currentState = this.captureCurrentState();
118 if (!this.hasStateChanged(this.lastKnownState, currentState)) {
119 return true; // No changes detected
120 }
121
122 const currentPreset = extension_settings.regex_presets.find(p => p.id === this.currentPresetId);
123 const presetName = currentPreset ? currentPreset.name : t`Unknown Preset`;
124
125 const choice = await Popup.show.confirm(
126 t`You have unsaved changes to the "${presetName}" preset.`,
127 t`Do you want to save them before switching?`,
128 {
129 okButton: t`Save Changes`,
130 cancelButton: t`Discard Changes`,
131 },
132 );
133
134 if (choice) {
135 // User chose to save changes
136 await this.savePreset(this.currentPresetId, true);
137 this.renderPresetList();
138 return true;
139 }
140
141 // User chose to discard changes
142 return true;
143 }
144
145 /**
146 * Sets up event listeners for the preset management UI.
147 * @returns {void}
148 */
149 setupEventListeners() {
150 this.presetSelect = /** @type {HTMLSelectElement} */ (document.getElementById('regex_presets'));
151 if (!this.presetSelect) {
152 console.error('RegexPresetManager: Could not find preset select element in the DOM.');
153 return;
154 }
155
156 this.presetSelect.addEventListener('change', async (event) => {
157 const selectedPresetId = this.presetSelect.value;
158 const fromSlashCommand = event instanceof CustomEvent && event?.detail?.fromSlashCommand === true;
159
160 // Check for unsaved changes before switching
161 if (!fromSlashCommand) {
162 const canProceed = await this.checkUnsavedChanges();
163 if (!canProceed) {
164 // Revert the selection
165 event.preventDefault();
166 const currentPreset = extension_settings.regex_presets.find(p => p.id === this.currentPresetId);
167 if (currentPreset) {
168 this.presetSelect.value = currentPreset.id;
169 }
170 return;
171 }
172 }
173
174 await this.applyPreset(selectedPresetId);
175 extension_settings.regex_presets.forEach(p => { p.isSelected = p.id === selectedPresetId; });
176 saveSettingsDebounced();
177 this.updateStoredState(selectedPresetId);
178 });
179
180 this.presetCreateButton = document.getElementById('regex_preset_create');
181 if (!this.presetCreateButton) {
182 console.error('RegexPresetManager: Could not find preset create button in the DOM.');
183 return;
184 }
185
186 this.presetCreateButton.addEventListener('click', async () => {
187 const newId = uuidv4();
188 await this.savePreset(newId, false);
189 this.renderPresetList();
190 this.updateStoredState(newId);
191 });
192
193 this.presetUpdateButton = document.getElementById('regex_preset_update');
194 if (!this.presetUpdateButton) {
195 console.error('RegexPresetManager: Could not find preset update button in the DOM.');
196 return;
197 }
198
199 this.presetUpdateButton.addEventListener('click', async () => {
200 const selectedPresetId = this.presetSelect.value;
201 await this.savePreset(selectedPresetId, true);
202 this.renderPresetList();
203 this.updateStoredState(selectedPresetId);
204 });
205
206 this.presetApplyButton = document.getElementById('regex_preset_apply');
207 if (!this.presetApplyButton) {
208 console.error('RegexPresetManager: Could not find preset apply button in the DOM.');
209 return;
210 }
211
212 this.presetApplyButton.addEventListener('click', async () => {
213 const selectedPresetId = this.presetSelect.value;
214 await this.applyPreset(selectedPresetId);
215 this.updateStoredState(selectedPresetId);
216 });
217
218 this.presetDeleteButton = document.getElementById('regex_preset_delete');
219 if (!this.presetDeleteButton) {
220 console.error('RegexPresetManager: Could not find preset delete button in the DOM.');
221 return;
222 }
223
224 this.presetDeleteButton.addEventListener('click', async () => {
225 const selectedPresetId = this.presetSelect.value;
226 await this.deletePreset(selectedPresetId);
227 this.renderPresetList();
228
229 const newSelectedPresetId = extension_settings.regex_presets.find(p => p.isSelected)?.id;
230 if (newSelectedPresetId) {
231 await this.applyPreset(newSelectedPresetId);
232 this.presetSelect.value = newSelectedPresetId;
233 this.updateStoredState(newSelectedPresetId);
234 } else {
235 this.currentPresetId = null;
236 this.lastKnownState = null;
237 }
238 });
239
240 this.renderPresetList();
241
242 // Initialize the stored state with the currently selected preset
243 const selectedPreset = extension_settings.regex_presets?.find(p => p.isSelected);
244 if (selectedPreset) {
245 this.updateStoredState(selectedPreset.id);
246 }
247 }
248
249 /**
250 * Registers slash commands related to regex presets.
251 * @returns {void}
252 */
253 registerSlashCommands() {
254 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
255 name: 'regex-preset',
256 helpString: t`Selects a regex preset by name or ID. Gets the current regex preset ID if no argument is provided.`,
257 callback: (args, name) => {
258 if (!this.presetSelect) {
259 return '';
260 }
261
262 name = String(name ?? '').trim();
263
264 if (name) {
265 const quiet = isTrueBoolean(args?.quiet?.toString());
266 const foundId = extension_settings.regex_presets.find(p => equalsIgnoreCaseAndAccents(p.id, name) || equalsIgnoreCaseAndAccents(p.name, name))?.id;
267
268 if (foundId) {
269 this.presetSelect.value = foundId;
270 this.presetSelect.dispatchEvent(new CustomEvent('change', { detail: { fromSlashCommand: true } }));
271 return foundId;
272 }
273
274 !quiet && toastr.warning(`Regex preset "${name}" not found`);
275 return '';
276 }
277
278 return this.presetSelect.value;
279 },
280 returns: 'current preset ID',
281 namedArgumentList: [
282 SlashCommandNamedArgument.fromProps({
283 name: 'quiet',
284 description: 'Suppress the toast message on preset change',
285 typeList: [ARGUMENT_TYPE.BOOLEAN],
286 defaultValue: 'false',
287 enumList: commonEnumProviders.boolean('trueFalse')(),
288 }),
289 ],
290 unnamedArgumentList: [
291 SlashCommandArgument.fromProps({
292 description: 'regex preset name or ID',
293 typeList: [ARGUMENT_TYPE.STRING],
294 enumProvider: () => extension_settings.regex_presets.map(x => new SlashCommandEnumValue(x.id, x.name, enumTypes.enum, enumIcons.preset)),
295 }),
296 ],
297 }));
298 }
299
300 /**
301 * Renders the list of regex presets in the UI.
302 * @returns {void}
303 */
304 renderPresetList() {
305 if (!this.presetSelect) {
306 return;
307 }
308
309 this.presetSelect.innerHTML = '';
310
311 if (!Array.isArray(extension_settings.regex_presets) || extension_settings.regex_presets.length === 0) {
312 const fallbackOption = new Option(t`[No presets saved]`, '', true, true);
313 this.presetSelect.appendChild(fallbackOption);
314 this.presetSelect.disabled = true;
315 return;
316 }
317
318 extension_settings.regex_presets.forEach(preset => {
319 const option = new Option(preset.name, preset.id, preset.isSelected, preset.isSelected);
320 this.presetSelect.appendChild(option);
321 });
322
323 this.presetSelect.disabled = false;
324 }
325
326 /**
327 * Applies a preset list to a target list of scripts.
328 * @param {Object} params The parameters object
329 * @param {RegexPresetItem[]} params.presetList The list of preset items
330 * @param {RegexScript[]} params.targetList The list of target scripts to modify
331 * @param {(targetList: RegexScript[]) => Promise<any>} params.saveFunction Function to save the modified list
332 */
333 async applyPresetList({ presetList, targetList, saveFunction }) {
334 if (!Array.isArray(targetList) || !Array.isArray(presetList)) {
335 return;
336 }
337
338 // Only enable scripts that are in the preset
339 targetList.forEach((script => {
340 script.disabled = !presetList.some(p => p.id === script.id);
341 }));
342
343 // First sort by the order in the preset, then the original order
344 targetList.sort((a, b) => {
345 const aIndex = presetList.findIndex(p => p.id === a.id);
346 const bIndex = presetList.findIndex(p => p.id === b.id);
347 return aIndex - bIndex || targetList.indexOf(a) - targetList.indexOf(b);
348 });
349
350 await saveFunction(targetList);
351 }
352
353 /**
354 * Applies a regex preset to the current context.
355 * @param {string} presetId - The ID of the preset to apply
356 * @returns {Promise<void>}
357 */
358 async applyPreset(presetId) {
359 const preset = extension_settings.regex_presets.find(p => p.id === presetId);
360 if (!preset) {
361 toastr.error(t`Could not find the selected preset.`);
362 return;
363 }
364
365 // Apply to both global and scoped lists
366 await this.applyPresetList({
367 presetList: preset.global,
368 targetList: extension_settings.regex,
369 saveFunction: () => saveSettingsDebounced(),
370 });
371 await this.applyPresetList({
372 presetList: preset.scoped,
373 targetList: characters[this_chid]?.data?.extensions?.regex_scripts,
374 saveFunction: (scripts) => writeExtensionField(this_chid, 'regex_scripts', scripts),
375 });
376
377 // Render the changes to the UI
378 await loadRegexScripts();
379 // Apply the changes to the current chat
380 await reloadCurrentChat();
381 }
382
383 /**
384 * Converts a list of regex scripts to preset items.
385 * @param {RegexScript[]} list The list of regex scripts
386 * @returns {RegexPresetItem[] | null} The list of preset items, or null if the input is invalid
387 */
388 regexListToPresetItems(list) {
389 if (!Array.isArray(list)) {
390 return null;
391 }
392
393 return list.filter(x => !x.disabled).map(s => ({ id: s.id }));
394 }
395
396 /**
397 * Saves a regex preset.
398 * @param {string} presetId - The ID of the preset
399 * @param {boolean} isUpdate - Whether this is an update operation
400 * @returns {Promise<void>}
401 */
402 async savePreset(presetId, isUpdate) {
403 const existingPreset = isUpdate ? extension_settings.regex_presets.find(p => p.id === presetId) : null;
404
405 if (isUpdate && !existingPreset) {
406 toastr.error(t`Could not find the preset to update.`);
407 return;
408 }
409
410 const name = isUpdate ? existingPreset.name : await Popup.show.input(t`Enter a name for the new regex preset:`, '');
411 const id = isUpdate ? existingPreset.id : presetId;
412
413 if (!name || !name.trim().length) {
414 return;
415 }
416
417 const preset = {
418 id: id,
419 name: name,
420 isSelected: false,
421 global: this.regexListToPresetItems(extension_settings.regex),
422 scoped: this.regexListToPresetItems(characters[this_chid]?.data?.extensions?.regex_scripts),
423 };
424
425 if (isUpdate) {
426 Object.assign(existingPreset, preset);
427 } else {
428 extension_settings.regex_presets.push(preset);
429 }
430
431 extension_settings.regex_presets.forEach(p => { p.isSelected = p.id === id; });
432 saveSettingsDebounced();
433
434 toastr.success(isUpdate ? t`Regex preset updated` : t`Regex preset saved`);
435 }
436
437 /**
438 * Deletes a regex preset.
439 * @param {string} presetId - The ID of the preset to delete
440 * @returns {Promise<void>}
441 */
442 async deletePreset(presetId) {
443 const presetIndex = extension_settings.regex_presets.findIndex(p => p.id === presetId);
444 if (presetIndex === -1) {
445 toastr.error(t`Could not find the preset to delete.`);
446 return;
447 }
448
449 const presetName = extension_settings.regex_presets[presetIndex].name;
450 const confirm = await Popup.show.confirm(t`Are you sure you want to delete this regex preset?`, presetName);
451 if (!confirm) {
452 return;
453 }
454
455 extension_settings.regex_presets.splice(presetIndex, 1);
456
457 // Select the first preset if any exist
458 extension_settings.regex_presets.forEach((p, i) => { p.isSelected = i === 0; });
459 saveSettingsDebounced();
460
461 toastr.success(t`Regex preset deleted`);
462 }
463}
464
465const presetManager = new RegexPresetManager();
466
467/**
22 * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data468 * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data
23 *469 *
24 * @return {RegexScript[]} An array of regex scripts, where each script is an object containing the necessary information.470 * @return {RegexScript[]} An array of regex scripts, where each script is an object containing the necessary information.
@@ -94,13 +540,18 @@ async function saveRegexScript(regexScript, existingScriptIndex, isScoped) {
94 if (currentChatId !== undefined && currentChatId !== null) {540 if (currentChatId !== undefined && currentChatId !== null) {
95 await reloadCurrentChat();541 await reloadCurrentChat();
96 }542 }
543
544 const debuggerPopup = $('#regex_debugger_popup');
545 if (debuggerPopup.length) {
546 populateDebuggerRuleList(debuggerPopup.parent());
547 }
97}548}
98549
99async function deleteRegexScript({ id, isScoped }) {550async function deleteRegexScript({ id, isScoped }) {
100 const array = (isScoped ? characters[this_chid]?.data?.extensions?.regex_scripts : extension_settings.regex) ?? [];551 const array = (isScoped ? characters[this_chid]?.data?.extensions?.regex_scripts : extension_settings.regex) ?? [];
101552
102 const existingScriptIndex = array.findIndex((script) => script.id === id);553 const existingScriptIndex = array.findIndex((script) => script.id === id);
103 if (!existingScriptIndex || existingScriptIndex !== -1) {554 if (existingScriptIndex !== -1) {
104 array.splice(existingScriptIndex, 1);555 array.splice(existingScriptIndex, 1);
105556
106 if (isScoped) {557 if (isScoped) {
@@ -330,6 +781,442 @@ async function onRegexEditorOpenClick(existingId, isScoped) {
330}781}
331782
332/**783/**
784 * Builds an HTML string for a replacement, highlighting literal parts in green
785 * and keeping back-referenced parts plain.
786 * @param {RegExpMatchArray} match The match object from `matchAll`.
787 * @param {string} pattern The replacement pattern string (e.g., "new text $1").
788 * @returns {string} The constructed HTML string.
789 */
790function buildReplacementHtml(match, pattern) {
791 const container = document.createDocumentFragment();
792 let lastIndex = 0;
793 const backrefRegex = /\$\$|\$&|\$`|\$'|\$(\d{1,2})/g;
794
795 let reMatch;
796 while ((reMatch = backrefRegex.exec(pattern)) !== null) {
797 // Part of the pattern before the back-reference is a literal.
798 const literalPart = pattern.substring(lastIndex, reMatch.index);
799 if (literalPart) {
800 const mark = document.createElement('mark');
801 mark.className = 'green_hl';
802 mark.innerText = literalPart;
803 container.appendChild(mark);
804 }
805
806 const backref = reMatch[0];
807 if (backref === '$$') {
808 container.appendChild(document.createTextNode('$'));
809 } else if (backref === '$&') {
810 const mark = document.createElement('mark');
811 mark.className = 'yellow_hl';
812 mark.innerText = match[0];
813 container.appendChild(mark);
814 } else if (backref === '$`') {
815 container.appendChild(document.createTextNode(match.input.substring(0, match.index)));
816 } else if (backref === '$\'') {
817 container.appendChild(document.createTextNode(match.input.substring(match.index + match[0].length)));
818 } else { // It's a numbered capture group, $n.
819 const groupIndex = parseInt(reMatch[1], 10);
820 if (groupIndex > 0 && groupIndex < match.length && match[groupIndex] !== undefined) {
821 const mark = document.createElement('mark');
822 mark.className = 'yellow_hl';
823 mark.innerText = match[groupIndex];
824 container.appendChild(mark);
825 } else {
826 // Not a valid group index, treat it as a literal.
827 const mark = document.createElement('mark');
828 mark.className = 'green_hl';
829 mark.innerText = backref;
830 container.appendChild(mark);
831 }
832 }
833 lastIndex = backrefRegex.lastIndex;
834 }
835
836 // The final part of the pattern after the last back-reference.
837 const finalLiteralPart = pattern.substring(lastIndex);
838 if (finalLiteralPart) {
839 const mark = document.createElement('mark');
840 mark.className = 'green_hl';
841 mark.innerText = finalLiteralPart;
842 container.appendChild(mark);
843 }
844
845 // To get the HTML content, we need a temporary parent element.
846 const tempDiv = document.createElement('div');
847 tempDiv.appendChild(container);
848 return tempDiv.innerHTML;
849}
850
851function executeRegexScriptForDebugging(script, text) {
852 let err;
853 let originalRegex;
854
855 try {
856 originalRegex = regexFromString(script.findRegex);
857 if (!originalRegex) throw new Error('Invalid regex string');
858 } catch (e) {
859 err = `Compile error: ${e.message}`;
860 return { output: text, highlightedOutput: text, error: err, charsCaptured: 0, charsAdded: 0, charsRemoved: 0 };
861 }
862
863 const globalRegex = new RegExp(originalRegex.source, originalRegex.flags.includes('g') ? originalRegex.flags : originalRegex.flags + 'g');
864 const matches = [...text.matchAll(globalRegex)];
865
866 if (matches.length === 0) {
867 return { output: text, highlightedOutput: escapeHtml(text), error: null, charsCaptured: 0, charsAdded: 0, charsRemoved: 0 };
868 }
869
870 let outputText = '';
871 let highlightedOutput = ''; // This will now be our "diff view"
872 let lastIndex = 0;
873 let totalCharsCaptured = 0;
874 let totalCharsAdded = 0;
875 let totalCharsRemoved = 0;
876
877 try {
878 for (const match of matches) {
879 const originalMatchText = match[0];
880 totalCharsCaptured += originalMatchText.length;
881
882 // Append text between matches (this part is unchanged)
883 const precedingText = text.substring(lastIndex, match.index);
884 outputText += precedingText;
885 highlightedOutput += escapeHtml(precedingText);
886
887 // --- Start of new diff and statistics logic ---
888 let charsAddedInMatch = 0;
889 let charsKeptFromMatch = 0;
890 const backrefRegex = /\$\$|\$&|\$`|\$'|\$(\d{1,2})/g;
891 let lastPatternIndex = 0;
892 let reMatch;
893 let replacementForPlainText = '';
894
895 // This loop calculates the stats accurately
896 while ((reMatch = backrefRegex.exec(script.replaceString)) !== null) {
897 const literalPart = script.replaceString.substring(lastPatternIndex, reMatch.index);
898 charsAddedInMatch += literalPart.length;
899 replacementForPlainText += literalPart;
900 const backref = reMatch[0];
901 if (backref === '$$') {
902 replacementForPlainText += '$';
903 } else if (backref === '$&') {
904 charsKeptFromMatch += (match[0] || '').length; replacementForPlainText += (match[0] || '');
905 } else if (backref === '$`') {
906 const part = match.input.substring(0, match.index); charsKeptFromMatch += part.length; replacementForPlainText += part;
907 } else if (backref === '$\'') {
908 const part = match.input.substring(match.index + match[0].length); charsKeptFromMatch += part.length; replacementForPlainText += part;
909 } else {
910 const groupIndex = parseInt(reMatch[1], 10);
911 if (groupIndex > 0 && groupIndex < match.length && match[groupIndex] !== undefined) {
912 charsKeptFromMatch += match[groupIndex].length;
913 replacementForPlainText += match[groupIndex];
914 }
915 }
916 lastPatternIndex = backrefRegex.lastIndex;
917 }
918 const finalLiteralPart = script.replaceString.substring(lastPatternIndex);
919 charsAddedInMatch += finalLiteralPart.length;
920 replacementForPlainText += finalLiteralPart;
921
922 totalCharsAdded += charsAddedInMatch;
923 totalCharsRemoved += (originalMatchText.length - charsKeptFromMatch);
924
925 outputText += replacementForPlainText;
926 // --- End of statistics logic ---
927
928 // --- Build the new Diff View HTML ---
929 // 1. Show the entire original match as "removed" (red strikethrough)
930 highlightedOutput += `<mark class='red_hl'>${escapeHtml(originalMatchText)}</mark>`;
931 // 2. Add an arrow to signify transformation
932 highlightedOutput += ' → ';
933 // 3. Build the replacement string with green (added) and yellow (kept) parts
934 highlightedOutput += buildReplacementHtml(match, script.replaceString);
935
936 lastIndex = match.index + originalMatchText.length;
937 }
938
939 // Append text after the last match
940 const trailingText = text.substring(lastIndex);
941 outputText += trailingText;
942 highlightedOutput += escapeHtml(trailingText);
943
944 } catch (e) {
945 err = (err ? err + '; ' : '') + `Replace error: ${e.message}`;
946 outputText = text; // Fallback
947 highlightedOutput = escapeHtml(text);
948 }
949
950 return {
951 output: outputText,
952 highlightedOutput: highlightedOutput,
953 error: err,
954 charsCaptured: totalCharsCaptured,
955 charsAdded: totalCharsAdded,
956 charsRemoved: totalCharsRemoved,
957 };
958}
959
960function populateDebuggerRuleList(container) {
961 const rulesContainer = container.find('#regex_debugger_rules');
962 const ruleTemplate = container.find('#regex_debugger_rule_template');
963 if (!rulesContainer.length || !ruleTemplate.length) {
964 console.error('Regex Debugger: Could not find rule list or template in the DOM.');
965 return;
966 }
967
968 rulesContainer.empty();
969
970 const allScripts = getRegexScripts();
971 if (!allScripts || allScripts.length === 0) {
972 rulesContainer.append('<div class="regex-debugger-no-rules">No regex rules found.</div>');
973 return;
974 }
975
976 const globalScriptIds = new Set((extension_settings.regex ?? []).map(s => s.id));
977 const globalScripts = [];
978 const scopedScripts = [];
979
980 allScripts.forEach(script => {
981 const scriptCopy = structuredClone(script); // Use structuredClone for deep copy
982 if (globalScriptIds.has(script.id)) {
983 // @ts-ignore
984 scriptCopy.isScoped = false;
985 globalScripts.push(scriptCopy);
986 } else {
987 // @ts-ignore
988 scriptCopy.isScoped = true;
989 scopedScripts.push(scriptCopy);
990 }
991 });
992
993 container.data('allScripts', [...globalScripts, ...scopedScripts]);
994
995 const renderRule = (script) => {
996 if (!script.id) script.id = uuidv4();
997 const ruleElementContent = $(ruleTemplate.prop('content')).clone();
998 const ruleElement = ruleElementContent.find('.regex-debugger-rule');
999
1000 ruleElement.attr('data-id', script.id);
1001 // @ts-ignore
1002 ruleElement.find('.rule-name').text(script.scriptName);
1003 ruleElement.find('.rule-regex').text(script.findRegex);
1004 // @ts-ignore
1005 ruleElement.find('.rule-scope').text(script.isScoped ? 'Scoped' : 'Global');
1006 ruleElement.find('.rule-enabled').prop('checked', !script.disabled);
1007 // @ts-ignore
1008 ruleElement.find('.edit_rule').on('click', () => onRegexEditorOpenClick(script.id, script.isScoped));
1009
1010 ruleElement.on('click', function (event) {
1011 if ($(event.target).is('input, .menu_button, .menu_button i')) {
1012 return;
1013 }
1014 const scriptId = $(this).data('id');
1015 const stepElement = $(`#step-result-${scriptId}`);
1016 const container = $('#regex_debugger_steps_output');
1017
1018 if (stepElement.length && container.length) {
1019 // Replace scrollIntoView with scrollTop animation
1020 const targetTop = stepElement.position().top;
1021 const containerScrollTop = container.scrollTop();
1022 const containerHeight = container.height();
1023
1024 // Center the element if possible
1025 let scrollTo = containerScrollTop + targetTop - (containerHeight / 2) + (stepElement.height() / 2);
1026
1027 container.animate({ scrollTop: scrollTo }, 300); // 300ms smooth scroll
1028
1029 stepElement.css('transition', 'background-color 0.5s').css('background-color', 'var(--highlight_color)');
1030 setTimeout(() => stepElement.css('background-color', ''), 1000);
1031 }
1032 });
1033
1034 return ruleElementContent;
1035 };
1036
1037 if (globalScripts.length > 0) {
1038 rulesContainer.append('<div class="list-header regex-debugger-list-header">Global Rules</div>');
1039 const globalList = $('<ul id="regex_debugger_rules_global" class="sortable-list"></ul>');
1040 globalScripts.forEach(script => globalList.append(renderRule(script)));
1041 rulesContainer.append(globalList);
1042 }
1043
1044 if (scopedScripts.length > 0) {
1045 rulesContainer.append('<div class="list-header regex-debugger-list-header">Scoped Rules</div>');
1046 const scopedList = $('<ul id="regex_debugger_rules_scoped" class="sortable-list"></ul>');
1047 scopedScripts.forEach(script => scopedList.append(renderRule(script)));
1048 rulesContainer.append(scopedList);
1049 }
1050}
1051
1052/**
1053 * Opens the regex debugger.
1054 * @returns {Promise<void>}
1055 */
1056async function onRegexDebuggerOpenClick() {
1057 const templateContent = await renderExtensionTemplateAsync('regex', 'debugger');
1058 const debuggerHtml = $('<div>').html(templateContent);
1059
1060 const stepTemplate = debuggerHtml.find('#regex_debugger_step_template');
1061
1062 populateDebuggerRuleList(debuggerHtml);
1063
1064 // @ts-ignore
1065 debuggerHtml.find('#regex_debugger_rules_global').sortable({ delay: getSortableDelay() }).disableSelection();
1066 // @ts-ignore
1067 debuggerHtml.find('#regex_debugger_rules_scoped').sortable({ delay: getSortableDelay() }).disableSelection();
1068
1069 debuggerHtml.find('#regex_debugger_run_test').on('click', function () {
1070 const allScripts = debuggerHtml.data('allScripts');
1071 const orderedRuleIds = [
1072 ...$('#regex_debugger_rules_global').find('li.regex-debugger-rule').map((i, el) => $(el).data('id')).get(),
1073 ...$('#regex_debugger_rules_scoped').find('li.regex-debugger-rule').map((i, el) => $(el).data('id')).get(),
1074 ];
1075
1076 const rawInput = String($('#regex_debugger_raw_input').val());
1077 const stepsOutput = $('#regex_debugger_steps_output');
1078 const finalOutput = $('#regex_debugger_final_output');
1079
1080 if (!stepsOutput.length || !finalOutput.length) return;
1081
1082 const displayMode = $('input[name="display_mode"]:checked').val();
1083 stepsOutput.empty();
1084 finalOutput.empty();
1085 $('#regex_debugger_final_summary').remove();
1086
1087 if (!allScripts) return;
1088 let textForNextStep = rawInput;
1089 let totalCharsCaptured = 0;
1090 let totalCharsAdded = 0;
1091 let totalCharsRemoved = 0;
1092
1093 orderedRuleIds.forEach(scriptId => {
1094 const ruleElement = $(`#regex_debugger_rules [data-id="${scriptId}"]`);
1095 if (!ruleElement.find('.rule-enabled').is(':checked')) return;
1096
1097 const script = allScripts.find(s => s.id === scriptId);
1098
1099 if (script) {
1100 const result = executeRegexScriptForDebugging(script, textForNextStep);
1101 totalCharsCaptured += result.charsCaptured;
1102 totalCharsAdded += result.charsAdded;
1103 totalCharsRemoved += result.charsRemoved;
1104
1105 const stepElement = $(stepTemplate.prop('content')).clone();
1106 // Set the ID on the TOP-LEVEL element that is being appended.
1107 stepElement.find('>:first-child').attr('id', `step-result-${script.id}`);
1108 const stepHeader = stepElement.find('.step-header');
1109 stepHeader.find('strong').text(`After: ${script.scriptName}`);
1110
1111 const metricsHtml = `<span class="step-metrics">Captured: ${result.charsCaptured}, Added: +${result.charsAdded}, Removed: -${result.charsRemoved}</span>`;
1112 stepHeader.append(metricsHtml);
1113
1114 if (displayMode === 'highlight') {
1115 stepElement.find('.step-output').html(result.highlightedOutput);
1116 } else {
1117 stepElement.find('.step-output').text(result.output);
1118 }
1119
1120 if (result.error) {
1121 stepHeader.append($(`<div class='warning_text text_rose-500'>${result.error}</div>`));
1122 }
1123
1124 stepsOutput.append(stepElement);
1125 textForNextStep = result.output;
1126 }
1127 });
1128
1129 const summaryHtml = `
1130 <div id="regex_debugger_final_summary" class="regex-debugger-summary">
1131 <strong>Total Captured:</strong> ${totalCharsCaptured} | <strong>Total Added:</strong> +${totalCharsAdded} | <strong>Total Removed:</strong> -${totalCharsRemoved}
1132 </div>
1133 `;
1134 finalOutput.before(summaryHtml);
1135
1136 const renderMode = $('#regex_debugger_render_mode').val();
1137 if (renderMode === 'message') {
1138 const formattedHtml = messageFormatting(textForNextStep, 'Debugger', true, false, null);
1139 const messageBlock = $('<div class="mes"><div class="mes_text"></div></div>');
1140 messageBlock.find('.mes_text').html(formattedHtml);
1141 finalOutput.append(messageBlock);
1142 } else {
1143 finalOutput.text(textForNextStep);
1144 }
1145 });
1146
1147 debuggerHtml.find('#regex_debugger_save_order').on('click', async function () {
1148 const allKnownScripts = getRegexScripts();
1149 const newGlobalScripts = $('#regex_debugger_rules_global').children('li').map((_, el) => allKnownScripts.find(s => s.id === $(el).data('id'))).get().filter(Boolean);
1150 const newScopedScripts = $('#regex_debugger_rules_scoped').children('li').map((_, el) => allKnownScripts.find(s => s.id === $(el).data('id'))).get().filter(Boolean);
1151
1152 extension_settings.regex = newGlobalScripts;
1153 if (this_chid !== undefined) {
1154 await writeExtensionField(this_chid, 'regex_scripts', newScopedScripts);
1155 }
1156
1157 saveSettingsDebounced();
1158 await loadRegexScripts();
1159 toastr.success(t`Regex script order saved!`);
1160
1161 const currentPopupContent = $('div:has(> #regex_debugger_rules)');
1162 populateDebuggerRuleList(currentPopupContent);
1163 // @ts-ignore
1164 currentPopupContent.find('#regex_debugger_rules_global').sortable({ delay: getSortableDelay() }).disableSelection();
1165 // @ts-ignore
1166 currentPopupContent.find('#regex_debugger_rules_scoped').sortable({ delay: getSortableDelay() }).disableSelection();
1167 });
1168
1169 debuggerHtml.find('#regex_debugger_expand_steps').on('click', function () {
1170 const popupContainer = $('<div class="expanded-regex-container"></div>');
1171 const navPanel = $('<div class="expanded-regex-nav"><h4>Steps</h4></div>');
1172 const contentPanel = $('<div class="expanded-regex-content"></div>');
1173
1174 const content = $('#regex_debugger_steps_output').clone().html();
1175 contentPanel.html(content);
1176
1177 $('#regex_debugger_rules .regex-debugger-rule').each(function () {
1178 const ruleElement = $(this);
1179 const scriptId = ruleElement.data('id');
1180 const scriptName = ruleElement.find('.rule-name').text();
1181
1182 const link = $(`<a href="#">${escapeHtml(scriptName)}</a>`);
1183 link.data('target-id', `step-result-${scriptId}`);
1184
1185 link.on('click', function (e) {
1186 e.preventDefault();
1187 navPanel.find('a').removeClass('active');
1188 $(this).addClass('active');
1189
1190 const targetId = $(this).data('target-id');
1191 // The selector is now correct for the structure.
1192 const targetElement = contentPanel.find(`#${targetId}`);
1193
1194 if (targetElement.length) {
1195 const scrollTo = contentPanel.scrollTop() + targetElement.position().top;
1196 contentPanel.animate({ scrollTop: scrollTo }, 300);
1197
1198 targetElement.css('transition', 'background-color 0.5s').css('background-color', 'var(--highlight_color)');
1199 setTimeout(() => targetElement.css('background-color', ''), 1000);
1200 }
1201 });
1202
1203 navPanel.append(link);
1204 });
1205
1206 popupContainer.append(navPanel).append(contentPanel);
1207 callGenericPopup(popupContainer, POPUP_TYPE.TEXT, 'Step-by-step Transformation', { wide: true, allowVerticalScrolling: false });
1208 });
1209
1210 debuggerHtml.find('#regex_debugger_expand_final').on('click', function () {
1211 const content = $('#regex_debugger_final_output').html();
1212 const popupContent = $('<div class="regex-popup-content"></div>').html(content);
1213 callGenericPopup(popupContent, POPUP_TYPE.TEXT, 'Final Output', { wide: true, large: true, allowVerticalScrolling: true });
1214 });
1215
1216 await callGenericPopup(debuggerHtml.children(), POPUP_TYPE.TEXT, '', { wide: true, allowVerticalScrolling: true });
1217}
1218
1219/**
333 * Updates the info block in the regex editor with hints regarding the find regex.1220 * Updates the info block in the regex editor with hints regarding the find regex.
334 * @param {JQuery<HTMLElement>} editorHtml The editor HTML1221 * @param {JQuery<HTMLElement>} editorHtml The editor HTML
335 */1222 */
@@ -592,8 +1479,12 @@ async function checkEmbeddedRegexScripts() {
592// Workaround for loading in sequence with other extensions1479// Workaround for loading in sequence with other extensions
593// NOTE: Always puts extension at the top of the list, but this is fine since it's static1480// NOTE: Always puts extension at the top of the list, but this is fine since it's static
594jQuery(async () => {1481jQuery(async () => {
595 if (extension_settings.regex) {1482 if (!Array.isArray(extension_settings.regex)) {
596 migrateSettings();1483 extension_settings.regex = [];
1484 }
1485
1486 if (!Array.isArray(extension_settings.regex_presets)) {
1487 extension_settings.regex_presets = [];
597 }1488 }
5981489
599 // Manually disable the extension since static imports auto-import the JS file1490 // Manually disable the extension since static imports auto-import the JS file
@@ -601,11 +1492,14 @@ jQuery(async () => {
601 return;1492 return;
602 }1493 }
6031494
1495 migrateSettings();
1496
604 const settingsHtml = $(await renderExtensionTemplateAsync('regex', 'dropdown'));1497 const settingsHtml = $(await renderExtensionTemplateAsync('regex', 'dropdown'));
605 $('#regex_container').append(settingsHtml);1498 $('#regex_container').append(settingsHtml);
606 $('#open_regex_editor').on('click', function () {1499 $('#open_regex_editor').on('click', function () {
607 onRegexEditorOpenClick(false, false);1500 onRegexEditorOpenClick(false, false);
608 });1501 });
1502 $('#open_regex_debugger').on('click', onRegexDebuggerOpenClick);
609 $('#open_scoped_editor').on('click', function () {1503 $('#open_scoped_editor').on('click', function () {
610 if (this_chid === undefined) {1504 if (this_chid === undefined) {
611 toastr.error(t`No character selected.`);1505 toastr.error(t`No character selected.`);
@@ -726,6 +1620,7 @@ jQuery(async () => {
726 },1620 },
727 ];1621 ];
728 for (const { selector, setter, getter } of sortableDatas) {1622 for (const { selector, setter, getter } of sortableDatas) {
1623 // @ts-ignore
729 $(selector).sortable({1624 $(selector).sortable({
730 delay: getSortableDelay(),1625 delay: getSortableDelay(),
731 stop: async function () {1626 stop: async function () {
@@ -778,6 +1673,7 @@ jQuery(async () => {
778 });1673 });
7791674
780 await loadRegexScripts();1675 await loadRegexScripts();
1676 // @ts-ignore
781 $('#saved_regex_scripts').sortable('enable');1677 $('#saved_regex_scripts').sortable('enable');
7821678
783 const localEnumProviders = {1679 const localEnumProviders = {
@@ -856,4 +1752,7 @@ jQuery(async () => {
8561752
857 eventSource.on(event_types.CHAT_CHANGED, checkEmbeddedRegexScripts);1753 eventSource.on(event_types.CHAT_CHANGED, checkEmbeddedRegexScripts);
858 eventSource.on(event_types.CHARACTER_DELETED, purgeEmbeddedRegexScripts);1754 eventSource.on(event_types.CHARACTER_DELETED, purgeEmbeddedRegexScripts);
1755
1756 presetManager.setupEventListeners();
1757 presetManager.registerSlashCommands();
859});1758});
public/scripts/extensions/regex/style.css+10 -8
@@ -1,3 +1,5 @@
1@import "debugger.css";
2
1.regex_settings .menu_button {3.regex_settings .menu_button {
2 width: fit-content;4 width: fit-content;
3 display: flex;5 display: flex;
@@ -39,19 +41,19 @@
39 opacity: 0.5;41 opacity: 0.5;
40}42}
4143
42.enable_scoped:checked ~ .regex-toggle-on {44.enable_scoped:checked~.regex-toggle-on {
43 display: block;45 display: block;
44}46}
4547
46.enable_scoped:checked ~ .regex-toggle-off {48.enable_scoped:checked~.regex-toggle-off {
47 display: none;49 display: none;
48}50}
4951
50.enable_scoped:not(:checked) ~ .regex-toggle-on {52.enable_scoped:not(:checked)~.regex-toggle-on {
51 display: none;53 display: none;
52}54}
5355
54.enable_scoped:not(:checked) ~ .regex-toggle-off {56.enable_scoped:not(:checked)~.regex-toggle-off {
55 display: block;57 display: block;
56}58}
5759
@@ -90,19 +92,19 @@ input.enable_scoped {
90 cursor: pointer;92 cursor: pointer;
91}93}
9294
93.disable_regex:checked ~ .regex-toggle-off {95.disable_regex:checked~.regex-toggle-off {
94 display: block;96 display: block;
95}97}
9698
97.disable_regex:checked ~ .regex-toggle-on {99.disable_regex:checked~.regex-toggle-on {
98 display: none;100 display: none;
99}101}
100102
101.disable_regex:not(:checked) ~ .regex-toggle-off {103.disable_regex:not(:checked)~.regex-toggle-off {
102 display: none;104 display: none;
103}105}
104106
105.disable_regex:not(:checked) ~ .regex-toggle-on {107.disable_regex:not(:checked)~.regex-toggle-on {
106 display: block;108 display: block;
107}109}
108110
public/scripts/extensions/shared.js+19 -6
@@ -22,12 +22,6 @@ export async function getMultimodalCaption(base64Img, prompt) {
2222
23 throwIfInvalidModel(useReverseProxy);23 throwIfInvalidModel(useReverseProxy);
2424
25 const noPrefix = ['ollama'].includes(extension_settings.caption.multimodal_api);
26
27 if (noPrefix && base64Img.startsWith('data:image/')) {
28 base64Img = base64Img.split(',')[1];
29 }
30
31 // OpenRouter has a payload limit of ~2MB. Google is 4MB, but we love democracy.25 // OpenRouter has a payload limit of ~2MB. Google is 4MB, but we love democracy.
32 // Ooba requires all images to be JPEGs. Koboldcpp just asked nicely.26 // Ooba requires all images to be JPEGs. Koboldcpp just asked nicely.
33 const isOllama = extension_settings.caption.multimodal_api === 'ollama';27 const isOllama = extension_settings.caption.multimodal_api === 'ollama';
@@ -47,6 +41,9 @@ export async function getMultimodalCaption(base64Img, prompt) {
47 } else if (!safeMimeTypes.includes(mimeType)) {41 } else if (!safeMimeTypes.includes(mimeType)) {
48 base64Img = await createThumbnail(base64Img, null, null);42 base64Img = await createThumbnail(base64Img, null, null);
49 }43 }
44 if (isOllama && base64Img.startsWith('data:image/')) {
45 base64Img = base64Img.split(',')[1];
46 }
5047
51 const proxyUrl = useReverseProxy ? oai_settings.reverse_proxy : '';48 const proxyUrl = useReverseProxy ? oai_settings.reverse_proxy : '';
52 const proxyPassword = useReverseProxy ? oai_settings.proxy_password : '';49 const proxyPassword = useReverseProxy ? oai_settings.proxy_password : '';
@@ -72,6 +69,10 @@ export async function getMultimodalCaption(base64Img, prompt) {
72 requestBody.model = textgenerationwebui_settings.ollama_model;69 requestBody.model = textgenerationwebui_settings.ollama_model;
73 }70 }
7471
72 if (extension_settings.caption.multimodal_model === 'ollama_custom') {
73 requestBody.model = extension_settings.caption.ollama_custom_model;
74 }
75
75 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled76 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
76 ? extension_settings.caption.alt_endpoint_url77 ? extension_settings.caption.alt_endpoint_url
77 : textgenerationwebui_settings.server_urls[textgen_types.OLLAMA];78 : textgenerationwebui_settings.server_urls[textgen_types.OLLAMA];
@@ -211,6 +212,10 @@ function throwIfInvalidModel(useReverseProxy) {
211 throw new Error('Ollama model is not set.');212 throw new Error('Ollama model is not set.');
212 }213 }
213214
215 if (multimodalApi === 'ollama' && multimodalModel === 'ollama_custom' && !extension_settings.caption.ollama_custom_model) {
216 throw new Error('Ollama custom model tag is not set.');
217 }
218
214 if (multimodalApi === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP] && !altEndpointEnabled) {219 if (multimodalApi === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP] && !altEndpointEnabled) {
215 throw new Error('LlamaCPP server URL is not set.');220 throw new Error('LlamaCPP server URL is not set.');
216 }221 }
@@ -242,6 +247,14 @@ function throwIfInvalidModel(useReverseProxy) {
242 if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) {247 if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) {
243 throw new Error('Moonshot AI API key is not set.');248 throw new Error('Moonshot AI API key is not set.');
244 }249 }
250
251 if (multimodalApi === 'nanogpt' && !secret_state[SECRET_KEYS.NANOGPT]) {
252 throw new Error('NanoGPT API key is not set.');
253 }
254
255 if (multimodalApi === 'electronhub' && !secret_state[SECRET_KEYS.ELECTRONHUB]) {
256 throw new Error('Electron Hub API key is not set.');
257 }
245}258}
246259
247/**260/**
public/scripts/extensions/stable-diffusion/index.js+129 -13
@@ -57,7 +57,7 @@ import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js';
57import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';57import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
58import { ToolManager } from '../../tool-calling.js';58import { ToolManager } from '../../tool-calling.js';
59import { MacrosParser } from '../../macros.js';59import { MacrosParser } from '../../macros.js';
60import { t } from '../../i18n.js';60import { t, translate } from '../../i18n.js';
61import { oai_settings } from '../../openai.js';61import { oai_settings } from '../../openai.js';
6262
63export { MODULE_NAME };63export { MODULE_NAME };
@@ -82,6 +82,7 @@ const sources = {
82 pollinations: 'pollinations',82 pollinations: 'pollinations',
83 stability: 'stability',83 stability: 'stability',
84 huggingface: 'huggingface',84 huggingface: 'huggingface',
85 electronhub: 'electronhub',
85 nanogpt: 'nanogpt',86 nanogpt: 'nanogpt',
86 bfl: 'bfl',87 bfl: 'bfl',
87 falai: 'falai',88 falai: 'falai',
@@ -650,7 +651,7 @@ async function onDeleteStyleClick() {
650 return;651 return;
651 }652 }
652653
653 const confirmed = await callGenericPopup(`Are you sure you want to delete the style "${selectedStyle}"?`, POPUP_TYPE.CONFIRM, '', { okButton: 'Delete', cancelButton: 'Cancel' });654 const confirmed = await callGenericPopup(t`Are you sure you want to delete the style "${selectedStyle}"?`, POPUP_TYPE.CONFIRM, '', { okButton: 'Delete', cancelButton: 'Cancel' });
654655
655 if (!confirmed) {656 if (!confirmed) {
656 return;657 return;
@@ -925,16 +926,16 @@ function onADetailerFaceChange() {
925}926}
926927
927const resolutionOptions = {928const resolutionOptions = {
928 sd_res_512x512: { width: 512, height: 512, name: '512x512 (1:1, icons, profile pictures)' },929 sd_res_512x512: { width: 512, height: 512, name: translate('512x512 (1:1, icons, profile pictures)', 'sd_res_512x512') },
929 sd_res_600x600: { width: 600, height: 600, name: '600x600 (1:1, icons, profile pictures)' },930 sd_res_600x600: { width: 600, height: 600, name: translate('600x600 (1:1, icons, profile pictures)', 'sd_res_600x600') },
930 sd_res_512x768: { width: 512, height: 768, name: '512x768 (2:3, vertical character card)' },931 sd_res_512x768: { width: 512, height: 768, name: translate('512x768 (2:3, vertical character card)', 'sd_res_512x768') },
931 sd_res_768x512: { width: 768, height: 512, name: '768x512 (3:2, horizontal 35-mm movie film)' },932 sd_res_768x512: { width: 768, height: 512, name: translate('768x512 (3:2, horizontal 35-mm movie film)', 'sd_res_768x512') },
932 sd_res_960x540: { width: 960, height: 540, name: '960x540 (16:9, horizontal wallpaper)' },933 sd_res_960x540: { width: 960, height: 540, name: translate('960x540 (16:9, horizontal wallpaper)', 'sd_res_960x540') },
933 sd_res_540x960: { width: 540, height: 960, name: '540x960 (9:16, vertical wallpaper)' },934 sd_res_540x960: { width: 540, height: 960, name: translate('540x960 (9:16, vertical wallpaper)', 'sd_res_540x960') },
934 sd_res_1920x1088: { width: 1920, height: 1088, name: '1920x1088 (16:9, 1080p, horizontal wallpaper)' },935 sd_res_1920x1088: { width: 1920, height: 1088, name: translate('1920x1088 (16:9, 1080p, horizontal wallpaper)', 'sd_res_1920x1088') },
935 sd_res_1088x1920: { width: 1088, height: 1920, name: '1088x1920 (9:16, 1080p, vertical wallpaper)' },936 sd_res_1088x1920: { width: 1088, height: 1920, name: translate('1088x1920 (9:16, 1080p, vertical wallpaper)', 'sd_res_1088x1920') },
936 sd_res_1280x720: { width: 1280, height: 720, name: '1280x720 (16:9, 720p, horizontal wallpaper)' },937 sd_res_1280x720: { width: 1280, height: 720, name: translate('1280x720 (16:9, 720p, horizontal wallpaper)', 'sd_res_1280x720') },
937 sd_res_720x1280: { width: 720, height: 1280, name: '720x1280 (9:16, 720p, vertical wallpaper)' },938 sd_res_720x1280: { width: 720, height: 1280, name: translate('720x1280 (9:16, 720p, vertical wallpaper)', 'sd_res_720x1280') },
938 sd_res_1024x1024: { width: 1024, height: 1024, name: '1024x1024 (1:1, SDXL)' },939 sd_res_1024x1024: { width: 1024, height: 1024, name: '1024x1024 (1:1, SDXL)' },
939 sd_res_1152x896: { width: 1152, height: 896, name: '1152x896 (9:7, SDXL)' },940 sd_res_1152x896: { width: 1152, height: 896, name: '1152x896 (9:7, SDXL)' },
940 sd_res_896x1152: { width: 896, height: 1152, name: '896x1152 (7:9, SDXL)' },941 sd_res_896x1152: { width: 896, height: 1152, name: '896x1152 (7:9, SDXL)' },
@@ -1289,6 +1290,7 @@ async function onModelChange() {
1289 sources.pollinations,1290 sources.pollinations,
1290 sources.stability,1291 sources.stability,
1291 sources.huggingface,1292 sources.huggingface,
1293 sources.electronhub,
1292 sources.nanogpt,1294 sources.nanogpt,
1293 sources.bfl,1295 sources.bfl,
1294 sources.falai,1296 sources.falai,
@@ -1506,6 +1508,9 @@ async function loadSamplers() {
1506 case sources.huggingface:1508 case sources.huggingface:
1507 samplers = ['N/A'];1509 samplers = ['N/A'];
1508 break;1510 break;
1511 case sources.electronhub:
1512 samplers = ['N/A'];
1513 break;
1509 case sources.nanogpt:1514 case sources.nanogpt:
1510 samplers = ['N/A'];1515 samplers = ['N/A'];
1511 break;1516 break;
@@ -1702,6 +1707,9 @@ async function loadModels() {
1702 case sources.huggingface:1707 case sources.huggingface:
1703 models = [{ value: '', text: '<Enter Model ID above>' }];1708 models = [{ value: '', text: '<Enter Model ID above>' }];
1704 break;1709 break;
1710 case sources.electronhub:
1711 models = await loadElectronHubModels();
1712 break;
1705 case sources.nanogpt:1713 case sources.nanogpt:
1706 models = await loadNanoGPTModels();1714 models = await loadNanoGPTModels();
1707 break;1715 break;
@@ -1806,6 +1814,24 @@ async function loadTogetherAIModels() {
1806 return [];1814 return [];
1807}1815}
18081816
1817async function loadElectronHubModels() {
1818 if (!secret_state[SECRET_KEYS.ELECTRONHUB]) {
1819 console.debug('Electron Hub API key is not set.');
1820 return [];
1821 }
1822
1823 const result = await fetch('/api/sd/electronhub/models', {
1824 method: 'POST',
1825 headers: getRequestHeaders(),
1826 });
1827
1828 if (result.ok) {
1829 return await result.json();
1830 }
1831
1832 return [];
1833}
1834
1809async function loadNanoGPTModels() {1835async function loadNanoGPTModels() {
1810 if (!secret_state[SECRET_KEYS.NANOGPT]) {1836 if (!secret_state[SECRET_KEYS.NANOGPT]) {
1811 console.debug('NanoGPT API key is not set.');1837 console.debug('NanoGPT API key is not set.');
@@ -2131,6 +2157,9 @@ async function loadSchedulers() {
2131 case sources.huggingface:2157 case sources.huggingface:
2132 schedulers = ['N/A'];2158 schedulers = ['N/A'];
2133 break;2159 break;
2160 case sources.electronhub:
2161 schedulers = ['N/A'];
2162 break;
2134 case sources.nanogpt:2163 case sources.nanogpt:
2135 schedulers = ['N/A'];2164 schedulers = ['N/A'];
2136 break;2165 break;
@@ -2228,6 +2257,9 @@ async function loadVaes() {
2228 case sources.huggingface:2257 case sources.huggingface:
2229 vaes = ['N/A'];2258 vaes = ['N/A'];
2230 break;2259 break;
2260 case sources.electronhub:
2261 vaes = ['N/A'];
2262 break;
2231 case sources.nanogpt:2263 case sources.nanogpt:
2232 vaes = ['N/A'];2264 vaes = ['N/A'];
2233 break;2265 break;
@@ -2811,6 +2843,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
2811 case sources.huggingface:2843 case sources.huggingface:
2812 result = await generateHuggingFaceImage(prefixedPrompt, signal);2844 result = await generateHuggingFaceImage(prefixedPrompt, signal);
2813 break;2845 break;
2846 case sources.electronhub:
2847 result = await generateElectronHubImage(prefixedPrompt, signal);
2848 break;
2814 case sources.nanogpt:2849 case sources.nanogpt:
2815 result = await generateNanoGPTImage(prefixedPrompt, negativePrompt, signal);2850 result = await generateNanoGPTImage(prefixedPrompt, negativePrompt, signal);
2816 break;2851 break;
@@ -3014,6 +3049,56 @@ function getClosestAspectRatio(width, height, source) {
3014}3049}
30153050
3016/**3051/**
3052 * Get closest size for Electron Hub
3053 * @param {number} width - The width of the image
3054 * @param {number} height - The height of the image
3055 * @returns {Promise<string>} - The closest size
3056 */
3057async function getClosestSize(width, height) {
3058 const response = await fetch('/api/sd/electronhub/sizes', {
3059 method: 'POST',
3060 headers: getRequestHeaders(),
3061 body: JSON.stringify({
3062 model: extension_settings.sd.model,
3063 }),
3064 });
3065 if (!response.ok) {
3066 const text = await response.text();
3067 throw new Error(text);
3068 }
3069 const result = await response.json();
3070 const sizesData = result.sizes;
3071
3072 const closestSize = sizesData.reduce((closest, size) => {
3073 if (!size || typeof size !== 'string') {
3074 return closest;
3075 }
3076 const sizeParts = size.split('x');
3077 if (sizeParts.length !== 2) {
3078 return closest;
3079 }
3080
3081 const sizeWidth = Number(sizeParts[0]);
3082 const sizeHeight = Number(sizeParts[1]);
3083 const targetWidth = Number(width);
3084 const targetHeight = Number(height);
3085
3086 if (isNaN(sizeWidth) || isNaN(sizeHeight) || isNaN(targetWidth) || isNaN(targetHeight)) {
3087 return closest;
3088 }
3089
3090 const sizeArea = sizeWidth * sizeHeight;
3091 const targetArea = targetWidth * targetHeight;
3092 const diff = Math.abs(sizeArea - targetArea);
3093
3094 return diff < closest.diff ? { size, diff } : closest;
3095 }, { size: null, diff: Infinity });
3096
3097 const size = closestSize.size;
3098 return size;
3099}
3100
3101/**
3017 * Generates an image using Stability AI.3102 * Generates an image using Stability AI.
3018 * @param {string} prompt - The main instruction used to guide the image generation.3103 * @param {string} prompt - The main instruction used to guide the image generation.
3019 * @param {string} negativePrompt - The instruction used to restrict the image generation.3104 * @param {string} negativePrompt - The instruction used to restrict the image generation.
@@ -3565,6 +3650,35 @@ async function generateHuggingFaceImage(prompt, signal) {
3565}3650}
35663651
3567/**3652/**
3653 * Generates an image using the Electron Hub API.
3654 * @param {string} prompt - The main instruction used to guide the image generation.
3655 * @param {AbortSignal} signal - An AbortSignal object that can be used to cancel the request.
3656 * @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
3657 */
3658async function generateElectronHubImage(prompt, signal) {
3659 const size = await getClosestSize(extension_settings.sd.width, extension_settings.sd.height);
3660
3661 const result = await fetch('/api/sd/electronhub/generate', {
3662 method: 'POST',
3663 headers: getRequestHeaders(),
3664 signal: signal,
3665 body: JSON.stringify({
3666 model: extension_settings.sd.model,
3667 prompt: prompt,
3668 size: size,
3669 }),
3670 });
3671
3672 if (result.ok) {
3673 const data = await result.json();
3674 return { format: 'jpg', data: data.image };
3675 } else {
3676 const text = await result.text();
3677 throw new Error(text);
3678 }
3679}
3680
3681/**
3568 * Generates an image using the NanoGPT API.3682 * Generates an image using the NanoGPT API.
3569 * @param {string} prompt - The main instruction used to guide the image generation.3683 * @param {string} prompt - The main instruction used to guide the image generation.
3570 * @param {string} negativePrompt - The instruction used to restrict the image generation.3684 * @param {string} negativePrompt - The instruction used to restrict the image generation.
@@ -3847,7 +3961,7 @@ async function onComfyNewWorkflowClick() {
3847}3961}
38483962
3849async function onComfyDeleteWorkflowClick() {3963async function onComfyDeleteWorkflowClick() {
3850 const confirm = await callGenericPopup('Delete the workflow? This action is irreversible.', POPUP_TYPE.CONFIRM, '', { okButton: 'Delete', cancelButton: 'Cancel' });3964 const confirm = await callGenericPopup(t`Delete the workflow? This action is irreversible.`, POPUP_TYPE.CONFIRM, '', { okButton: t`Delete`, cancelButton: t`Cancel` });
3851 if (!confirm) {3965 if (!confirm) {
3852 return;3966 return;
3853 }3967 }
@@ -4014,6 +4128,8 @@ function isValidState() {
4014 return secret_state[SECRET_KEYS.STABILITY];4128 return secret_state[SECRET_KEYS.STABILITY];
4015 case sources.huggingface:4129 case sources.huggingface:
4016 return secret_state[SECRET_KEYS.HUGGINGFACE];4130 return secret_state[SECRET_KEYS.HUGGINGFACE];
4131 case sources.electronhub:
4132 return secret_state[SECRET_KEYS.ELECTRONHUB];
4017 case sources.nanogpt:4133 case sources.nanogpt:
4018 return secret_state[SECRET_KEYS.NANOGPT];4134 return secret_state[SECRET_KEYS.NANOGPT];
4019 case sources.bfl:4135 case sources.bfl:
public/scripts/extensions/stable-diffusion/settings.html+5 -1
@@ -41,6 +41,7 @@
41 <option value="bfl">BFL (Black Forest Labs)</option>41 <option value="bfl">BFL (Black Forest Labs)</option>
42 <option value="comfy">ComfyUI</option>42 <option value="comfy">ComfyUI</option>
43 <option value="drawthings">DrawThings HTTP API</option>43 <option value="drawthings">DrawThings HTTP API</option>
44 <option value="electronhub">Electron Hub</option>
44 <option value="extras">Extras API (deprecated)</option>45 <option value="extras">Extras API (deprecated)</option>
45 <option value="falai">FAL.AI</option>46 <option value="falai">FAL.AI</option>
46 <option value="google">Google AI</option>47 <option value="google">Google AI</option>
@@ -93,6 +94,9 @@
93 <label for="sd_huggingface_model_id" data-i18n="Model ID">Model ID</label>94 <label for="sd_huggingface_model_id" data-i18n="Model ID">Model ID</label>
94 <input id="sd_huggingface_model_id" type="text" class="text_pole" data-i18n="[placeholder]e.g. black-forest-labs/FLUX.1-dev" placeholder="e.g. black-forest-labs/FLUX.1-dev" value="" />95 <input id="sd_huggingface_model_id" type="text" class="text_pole" data-i18n="[placeholder]e.g. black-forest-labs/FLUX.1-dev" placeholder="e.g. black-forest-labs/FLUX.1-dev" value="" />
95 </div>96 </div>
97 <div data-sd-source="electronhub">
98 <i>Hint: Save an API key in the Electron Hub (Chat Completion) API settings to use it here.</i>
99 </div>
96 <div data-sd-source="nanogpt">100 <div data-sd-source="nanogpt">
97 <i>Hint: Save an API key in the NanoGPT (Chat Completion) API settings to use it here.</i>101 <i>Hint: Save an API key in the NanoGPT (Chat Completion) API settings to use it here.</i>
98 </div>102 </div>
@@ -260,7 +264,7 @@
260 <span data-i18n="Click to set">Click to set</span>264 <span data-i18n="Click to set">Click to set</span>
261 </div>265 </div>
262 </div>266 </div>
263 <label class="checkbox_label marginBot5" for="sd_bfl_upsampling" title="Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.">267 <label class="checkbox_label marginBot5" for="sd_bfl_upsampling" data-i18n="[title]Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation." title="Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.">
264 <input id="sd_bfl_upsampling" type="checkbox" />268 <input id="sd_bfl_upsampling" type="checkbox" />
265 <span data-i18n="Prompt Upsampling">269 <span data-i18n="Prompt Upsampling">
266 Prompt Upsampling270 Prompt Upsampling
public/scripts/extensions/translate/index.js+4 -2
@@ -182,9 +182,11 @@ function isGeneratingSwipe(messageId) {
182 return $(`#chat .mes[mesid="${messageId}"] .mes_text`).text() === '...';182 return $(`#chat .mes[mesid="${messageId}"] .mes_text`).text() === '...';
183}183}
184184
185async function translateImpersonate(text) {185async function translateImpersonate() {
186 const sendTextArea = $('#send_textarea');
187 const text = sendTextArea.val().toString();
186 const translatedText = await translate(text, extension_settings.translate.target_language);188 const translatedText = await translate(text, extension_settings.translate.target_language);
187 $('#send_textarea').val(translatedText);189 sendTextArea.val(translatedText);
188}190}
189191
190/**192/**
public/scripts/extensions/tts/index.js+77 -4
@@ -637,11 +637,8 @@ async function processTtsQueue() {
637 }637 }
638638
639 if (extension_settings.tts.narrate_quoted_only) {639 if (extension_settings.tts.narrate_quoted_only) {
640 const special_quotes = /[“”«»「」『』""]/g; // Extend this regex to include other special quotes
641 text = text.replace(special_quotes, '"');
642 const matches = text.match(/".*?"/g); // Matches text inside double quotes, non-greedily
643 const partJoiner = (ttsProvider?.separator || ' ... ');640 const partJoiner = (ttsProvider?.separator || ' ... ');
644 text = matches ? matches.join(partJoiner) : text;641 text = joinQuotedBlocks(text, { separator: partJoiner, includeQuotes: true });
645 }642 }
646643
647 // Remove embedded images644 // Remove embedded images
@@ -702,6 +699,82 @@ async function processTtsQueue() {
702 }699 }
703}700}
704701
702/**
703 * Extract and join quoted blocks with proper matching pairs and nesting.
704 * - Captures outermost quotes and everything inside (including different inner quote styles).
705 * - Requires matching opener/closer style (e.g., “ ... ”, 「 ... 」, « ... », etc.).
706 * - Ignores incomplete/unclosed quotes (doesn't include them in the result).
707 * - Symmetric quotes like "..." and "..." are supported (not nesting the same symmetric style).
708 *
709 * @param {string} text - The text to process
710 * @param {object} [opts={}] - Optional options object
711 * @param {string} [opts.separator=' ... '] - String to join multiple quoted blocks
712 * @param {boolean} [opts.includeQuotes=true] - Keep the quote chars around the captured text
713 * @param {boolean} [opts.returnEmptyOnNoQuotes=false] - Return an empty string if no quotes are found
714 * @param {Array<[string,string]>} [opts.pairs] - Custom quote pairs; defaults cover EN/DE/FR/JP
715 * @returns {string} The joined quoted blocks, or the original text if no quotes found
716 */
717function joinQuotedBlocks(text, opts = {}) {
718 const {
719 separator = ' ... ',
720 includeQuotes = true,
721 returnEmptyOnNoQuotes = false,
722 pairs = [
723 // typographic doubles
724 ['„', '“'], // DE low-high
725 ['“', '”'], // EN
726 ['«', '»'], // FR open « close »
727 ['»', '«'], // Some locales open »
728 // typographic singles
729 ['‘', '’'],
730 ['‚', '‘'],
731 // Japanese corner quotes
732 ['「', '」'],
733 ['『', '』'],
734 // symmetric doubles
735 ['"', '"'],
736 ['"', '"'],
737 ],
738 } = opts;
739
740 if (!text || typeof text !== 'string') return text;
741
742 const openToClose = Object.fromEntries(pairs);
743
744 const segments = [];
745 const stack = []; // [{ opener, expectedClose, start }]
746 for (let i = 0; i < text.length; i++) {
747 const ch = text[i];
748 const top = stack[stack.length - 1];
749
750 // Prefer closing the current open pair if the char matches its expected closer
751 if (top && ch === top.expectedClose) {
752 const finished = stack.pop();
753 if (stack.length === 0) {
754 // Only collect outermost quotes (contains all nested content)
755 segments.push(text.slice(finished.start, i + 1));
756 }
757 continue;
758 }
759
760 // Otherwise, see if this is a new opener
761 if (openToClose[ch]) {
762 stack.push({ opener: ch, expectedClose: openToClose[ch], start: i });
763 continue;
764 }
765
766 // If it's a stray closer that doesn't match current top, ignore
767 }
768
769 if (!segments.length) return returnEmptyOnNoQuotes ? '' : text;
770
771 const cleaned = includeQuotes
772 ? segments
773 : segments.map(s => s.slice(1, -1)); // all defined pairs are single-char quotes
774
775 return cleaned.join(separator);
776}
777
705async function playFullConversation() {778async function playFullConversation() {
706 resetTtsPlayback();779 resetTtsPlayback();
707780
public/scripts/keyboard.js+1 -1
@@ -11,7 +11,7 @@ const interactableSelectors = [
11 '.avatar-container', // Persona list blocks11 '.avatar-container', // Persona list blocks
12 '.tag .tag_remove', // Remove button in removable tags12 '.tag .tag_remove', // Remove button in removable tags
13 '.bg_example', // Background elements in the background menu13 '.bg_example', // Background elements in the background menu
14 '.bg_example .bg_button', // The inline buttons on the backgrounds14 '.bg_example .jg-button, .bg_example .mobile-only-menu-toggle', // The inline buttons on the backgrounds
15 '#options a', // Option entries in the popup options menu15 '#options a', // Option entries in the popup options menu
16 '.mes_buttons .mes_button', // Small inline buttons on the chat messages16 '.mes_buttons .mes_button', // Small inline buttons on the chat messages
17 '.extraMesButtons>div:not(.mes_button)', // The extra/extension buttons inline on the chat messages17 '.extraMesButtons>div:not(.mes_button)', // The extra/extension buttons inline on the chat messages
public/scripts/login.js+4 -0
@@ -1,3 +1,5 @@
1import { initAccessibility } from './a11y.js';
2
1/**3/**
2 * CRSF token for requests.4 * CRSF token for requests.
3 */5 */
@@ -265,6 +267,8 @@ function configureDiscreetLogin() {
265}267}
266268
267(async function () {269(async function () {
270 initAccessibility();
271
268 csrfToken = await getCsrfToken();272 csrfToken = await getCsrfToken();
269 const userList = await getUserList();273 const userList = await getUserList();
270274
public/scripts/openai.js+301 -57
@@ -179,6 +179,7 @@ export const chat_completion_sources = {
179 COHERE: 'cohere',179 COHERE: 'cohere',
180 PERPLEXITY: 'perplexity',180 PERPLEXITY: 'perplexity',
181 GROQ: 'groq',181 GROQ: 'groq',
182 ELECTRONHUB: 'electronhub',
182 NANOGPT: 'nanogpt',183 NANOGPT: 'nanogpt',
183 DEEPSEEK: 'deepseek',184 DEEPSEEK: 'deepseek',
184 AIMLAPI: 'aimlapi',185 AIMLAPI: 'aimlapi',
@@ -187,6 +188,7 @@ export const chat_completion_sources = {
187 MOONSHOT: 'moonshot',188 MOONSHOT: 'moonshot',
188 FIREWORKS: 'fireworks',189 FIREWORKS: 'fireworks',
189 COMETAPI: 'cometapi',190 COMETAPI: 'cometapi',
191 AZURE_OPENAI: 'azure_openai',
190};192};
191193
192const character_names_behavior = {194const character_names_behavior = {
@@ -240,6 +242,8 @@ const sensitiveFields = [
240 'custom_include_headers',242 'custom_include_headers',
241 'vertexai_region',243 'vertexai_region',
242 'vertexai_express_project_id',244 'vertexai_express_project_id',
245 'azure_base_url',
246 'azure_deployment_name',
243];247];
244248
245/**249/**
@@ -271,6 +275,7 @@ export const settingsToUpdate = {
271 cohere_model: ['#model_cohere_select', 'cohere_model', false, true],275 cohere_model: ['#model_cohere_select', 'cohere_model', false, true],
272 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true],276 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true],
273 groq_model: ['#model_groq_select', 'groq_model', false, true],277 groq_model: ['#model_groq_select', 'groq_model', false, true],
278 electronhub_model: ['#model_electronhub_select', 'electronhub_model', false, true],
274 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true],279 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true],
275 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true],280 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true],
276 aimlapi_model: ['#model_aimlapi_select', 'aimlapi_model', false, true],281 aimlapi_model: ['#model_aimlapi_select', 'aimlapi_model', false, true],
@@ -329,6 +334,10 @@ export const settingsToUpdate = {
329 n: ['#n_openai', 'n', false, false],334 n: ['#n_openai', 'n', false, false],
330 bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true],335 bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true],
331 request_images: ['#openai_request_images', 'request_images', true, false],336 request_images: ['#openai_request_images', 'request_images', true, false],
337 azure_base_url: ['#azure_base_url', 'azure_base_url', false, true],
338 azure_deployment_name: ['#azure_deployment_name', 'azure_deployment_name', false, true],
339 azure_api_version: ['#azure_api_version', 'azure_api_version', false, true],
340 azure_openai_model: ['#azure_openai_model', 'azure_openai_model', false, true],
332 extensions: ['#NULL_SELECTOR', 'extensions', false, false],341 extensions: ['#NULL_SELECTOR', 'extensions', false, false],
333};342};
334343
@@ -369,6 +378,7 @@ const default_settings = {
369 cohere_model: 'command-r-plus',378 cohere_model: 'command-r-plus',
370 perplexity_model: 'sonar-pro',379 perplexity_model: 'sonar-pro',
371 groq_model: 'llama-3.3-70b-versatile',380 groq_model: 'llama-3.3-70b-versatile',
381 electronhub_model: 'gpt-4o-mini',
372 nanogpt_model: 'gpt-4o-mini',382 nanogpt_model: 'gpt-4o-mini',
373 deepseek_model: 'deepseek-chat',383 deepseek_model: 'deepseek-chat',
374 aimlapi_model: 'gpt-4o-mini-2024-07-18',384 aimlapi_model: 'gpt-4o-mini-2024-07-18',
@@ -377,6 +387,10 @@ const default_settings = {
377 cometapi_model: 'gpt-4o',387 cometapi_model: 'gpt-4o',
378 moonshot_model: 'kimi-latest',388 moonshot_model: 'kimi-latest',
379 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',389 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
390 azure_base_url: '',
391 azure_deployment_name: '',
392 azure_api_version: '2024-02-15-preview',
393 azure_openai_model: '',
380 custom_model: '',394 custom_model: '',
381 custom_url: '',395 custom_url: '',
382 custom_include_body: '',396 custom_include_body: '',
@@ -458,6 +472,7 @@ const oai_settings = {
458 cohere_model: 'command-r-plus',472 cohere_model: 'command-r-plus',
459 perplexity_model: 'sonar-pro',473 perplexity_model: 'sonar-pro',
460 groq_model: 'llama-3.1-70b-versatile',474 groq_model: 'llama-3.1-70b-versatile',
475 electronhub_model: 'gpt-4o-mini',
461 nanogpt_model: 'gpt-4o-mini',476 nanogpt_model: 'gpt-4o-mini',
462 deepseek_model: 'deepseek-chat',477 deepseek_model: 'deepseek-chat',
463 aimlapi_model: 'gpt-4-turbo',478 aimlapi_model: 'gpt-4-turbo',
@@ -466,6 +481,10 @@ const oai_settings = {
466 cometapi_model: 'gpt-4o',481 cometapi_model: 'gpt-4o',
467 moonshot_model: 'kimi-latest',482 moonshot_model: 'kimi-latest',
468 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',483 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
484 azure_base_url: '',
485 azure_deployment_name: '',
486 azure_api_version: '2024-02-15-preview',
487 azure_openai_model: '',
469 custom_model: '',488 custom_model: '',
470 custom_url: '',489 custom_url: '',
471 custom_include_body: '',490 custom_include_body: '',
@@ -905,7 +924,6 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
905924
906 // Insert chat messages as long as there is budget available925 // Insert chat messages as long as there is budget available
907 const chatPool = [...messages].reverse();926 const chatPool = [...messages].reverse();
908 const firstNonInjected = chatPool.find(x => !x.injected);
909 for (let index = 0; index < chatPool.length; index++) {927 for (let index = 0; index < chatPool.length; index++) {
910 const chatPrompt = chatPool[index];928 const chatPrompt = chatPool[index];
911929
@@ -946,22 +964,6 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
946 }964 }
947965
948 if (chatCompletion.canAfford(chatMessage)) {966 if (chatCompletion.canAfford(chatMessage)) {
949 if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) {
950 // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message
951 if (chatPrompt.role === 'assistant') {
952 const supportsAssistantPrefill = oai_settings.chat_completion_source === chat_completion_sources.CLAUDE;
953 const assistantPrefill = supportsAssistantPrefill ? substituteParams(oai_settings.assistant_prefill) : '';
954 const messageContent = [assistantPrefill, chatMessage.content].filter(x => x).join('\n\n');
955 const continueMessage = await Message.createAsync(chatMessage.role, messageContent, chatMessage.identifier);
956 const collection = new MessageCollection('continuePrefill', continueMessage);
957 chatCompletion.add(collection, -1);
958 continue;
959 }
960 const collection = new MessageCollection('continuePrefill', chatMessage);
961 chatCompletion.add(collection, -1);
962 continue;
963 }
964
965 chatCompletion.insertAtStart(chatMessage, 'chatHistory');967 chatCompletion.insertAtStart(chatMessage, 'chatHistory');
966 } else {968 } else {
967 break;969 break;
@@ -1221,6 +1223,21 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
1221 chatCompletion.reserveBudget(toolTokens);1223 chatCompletion.reserveBudget(toolTokens);
1222 }1224 }
12231225
1226 // Displace the message to be continued from its original position before performing in-chat injections
1227 // In case if it is an assistant message, we want to prepend the users assistant prefill on the message
1228 if (type === 'continue' && oai_settings.continue_prefill && messages.length) {
1229 const chatMessage = messages.shift();
1230 const isAssistantRole = chatMessage.role === 'assistant';
1231 const supportsAssistantPrefill = oai_settings.chat_completion_source === chat_completion_sources.CLAUDE;
1232 const namesInCompletion = oai_settings.names_behavior === character_names_behavior.COMPLETION;
1233 const assistantPrefill = isAssistantRole && supportsAssistantPrefill ? substituteParams(oai_settings.assistant_prefill) : '';
1234 const messageContent = [assistantPrefill, chatMessage.content].filter(x => x).join('\n\n');
1235 const continueMessage = await Message.createAsync(chatMessage.role, messageContent, 'continuePrefill');
1236 chatMessage.name && namesInCompletion && await continueMessage.setName(promptManager.sanitizeName(chatMessage.name));
1237 controlPrompts.add(continueMessage);
1238 chatCompletion.reserveBudget(continueMessage);
1239 }
1240
1224 // Add in-chat injections1241 // Add in-chat injections
1225 messages = await populationInjectionPrompts(absolutePrompts, messages);1242 messages = await populationInjectionPrompts(absolutePrompts, messages);
12261243
@@ -1544,6 +1561,11 @@ export function tryParseStreamingError(response, decoded, { quiet = false } = {}
1544 !quiet && toastr.error(data.message, 'Chat Completion API');1561 !quiet && toastr.error(data.message, 'Chat Completion API');
1545 throw new Error(data);1562 throw new Error(data);
1546 }1563 }
1564
1565 if (data.detail) {
1566 !quiet && toastr.error(data.detail?.error?.message || response.statusText, 'Chat Completion API');
1567 throw new Error(data);
1568 }
1547 }1569 }
1548 catch {1570 catch {
1549 // No JSON. Do nothing.1571 // No JSON. Do nothing.
@@ -1616,6 +1638,8 @@ export function getChatCompletionModel(source = null) {
1616 return oai_settings.perplexity_model;1638 return oai_settings.perplexity_model;
1617 case chat_completion_sources.GROQ:1639 case chat_completion_sources.GROQ:
1618 return oai_settings.groq_model;1640 return oai_settings.groq_model;
1641 case chat_completion_sources.ELECTRONHUB:
1642 return oai_settings.electronhub_model;
1619 case chat_completion_sources.NANOGPT:1643 case chat_completion_sources.NANOGPT:
1620 return oai_settings.nanogpt_model;1644 return oai_settings.nanogpt_model;
1621 case chat_completion_sources.DEEPSEEK:1645 case chat_completion_sources.DEEPSEEK:
@@ -1632,6 +1656,8 @@ export function getChatCompletionModel(source = null) {
1632 return oai_settings.moonshot_model;1656 return oai_settings.moonshot_model;
1633 case chat_completion_sources.FIREWORKS:1657 case chat_completion_sources.FIREWORKS:
1634 return oai_settings.fireworks_model;1658 return oai_settings.fireworks_model;
1659 case chat_completion_sources.AZURE_OPENAI:
1660 return oai_settings.azure_openai_model;
1635 default:1661 default:
1636 console.error(`Unknown chat completion source: ${activeSource}`);1662 console.error(`Unknown chat completion source: ${activeSource}`);
1637 return '';1663 return '';
@@ -1776,6 +1802,26 @@ function saveModelList(data) {
1776 }1802 }
1777 }1803 }
17781804
1805 if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
1806 $('#model_electronhub_select').empty();
1807 model_list.forEach((model) => {
1808 if (model?.endpoints?.includes('/v1/chat/completions')) {
1809 $('#model_electronhub_select').append(
1810 $('<option>', {
1811 value: model.id,
1812 text: model.name,
1813 }));
1814 }
1815 });
1816
1817 const selectedModel = model_list.find(model => model.id === oai_settings.electronhub_model);
1818 if (model_list.length > 0 && (!selectedModel || !oai_settings.electronhub_model)) {
1819 oai_settings.electronhub_model = model_list[0].id;
1820 }
1821
1822 $('#model_electronhub_select').val(oai_settings.electronhub_model).trigger('change');
1823 }
1824
1779 if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {1825 if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {
1780 $('#model_nanogpt_select').empty();1826 $('#model_nanogpt_select').empty();
1781 model_list.forEach((model) => {1827 model_list.forEach((model) => {
@@ -1928,6 +1974,16 @@ function saveModelList(data) {
19281974
1929 $('#model_cometapi_select').val(oai_settings.cometapi_model).trigger('change');1975 $('#model_cometapi_select').val(oai_settings.cometapi_model).trigger('change');
1930 }1976 }
1977
1978 if (oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) {
1979 const modelId = model_list?.[0]?.id || '';
1980 oai_settings.azure_openai_model = modelId;
1981
1982 $('#azure_openai_model')
1983 .empty()
1984 .append(new Option(modelId || 'None', modelId || '', true, true))
1985 .trigger('change');
1986 }
1931}1987}
19321988
1933function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {1989function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -2039,6 +2095,7 @@ function getReasoningEffort() {
2039 // These sources expect the effort as string.2095 // These sources expect the effort as string.
2040 const reasoningEffortSources = [2096 const reasoningEffortSources = [
2041 chat_completion_sources.OPENAI,2097 chat_completion_sources.OPENAI,
2098 chat_completion_sources.AZURE_OPENAI,
2042 chat_completion_sources.CUSTOM,2099 chat_completion_sources.CUSTOM,
2043 chat_completion_sources.XAI,2100 chat_completion_sources.XAI,
2044 chat_completion_sources.AIMLAPI,2101 chat_completion_sources.AIMLAPI,
@@ -2046,24 +2103,43 @@ function getReasoningEffort() {
2046 chat_completion_sources.POLLINATIONS,2103 chat_completion_sources.POLLINATIONS,
2047 chat_completion_sources.PERPLEXITY,2104 chat_completion_sources.PERPLEXITY,
2048 chat_completion_sources.COMETAPI,2105 chat_completion_sources.COMETAPI,
2106 chat_completion_sources.ELECTRONHUB,
2049 ];2107 ];
20502108
2051 if (!reasoningEffortSources.includes(oai_settings.chat_completion_source)) {2109 if (!reasoningEffortSources.includes(oai_settings.chat_completion_source)) {
2052 return oai_settings.reasoning_effort;2110 return oai_settings.reasoning_effort;
2053 }2111 }
20542112
2055 switch (oai_settings.reasoning_effort) {2113 function resolveReasoningEffort() {
2056 case reasoning_effort_types.auto:2114 switch (oai_settings.reasoning_effort) {
2115 case reasoning_effort_types.auto:
2116 return undefined;
2117 case reasoning_effort_types.min:
2118 return [chat_completion_sources.OPENAI, chat_completion_sources.AZURE_OPENAI].includes(oai_settings.chat_completion_source) && /^gpt-5/.test(getChatCompletionModel())
2119 ? reasoning_effort_types.min
2120 : reasoning_effort_types.low;
2121 case reasoning_effort_types.max:
2122 return reasoning_effort_types.high;
2123 default:
2124 return oai_settings.reasoning_effort;
2125 }
2126 }
2127
2128 const reasoningEffort = resolveReasoningEffort();
2129
2130 // Check if the resolved effort supported by the model
2131 if (oai_settings.chat_completion_source === chat_completion_sources.ELECTRONHUB) {
2132 if (Array.isArray(model_list) && reasoningEffort) {
2133 const currentModel = model_list.find(m => m.id === oai_settings.electronhub_model);
2134 const supportedEfforts = currentModel?.metadata?.supported_reasoning_efforts;
2135 if (Array.isArray(supportedEfforts) && supportedEfforts.includes(reasoningEffort)) {
2136 return reasoningEffort;
2137 }
2057 return undefined;2138 return undefined;
2058 case reasoning_effort_types.min:2139 }
2059 return chat_completion_sources.OPENAI === oai_settings.chat_completion_source && /^gpt-5/.test(oai_settings.openai_model)
2060 ? reasoning_effort_types.min
2061 : reasoning_effort_types.low;
2062 case reasoning_effort_types.max:
2063 return reasoning_effort_types.high;
2064 default:
2065 return oai_settings.reasoning_effort;
2066 }2140 }
2141
2142 return reasoningEffort;
2067}2143}
20682144
2069/**2145/**
@@ -2102,18 +2178,20 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2102 const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;2178 const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
2103 const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;2179 const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
2104 const isAimlapi = oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI;2180 const isAimlapi = oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI;
2181 const isElectronHub = oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB;
2105 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;2182 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;
2106 const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;2183 const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;
2107 const isMoonshot = oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT;2184 const isMoonshot = oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT;
2185 const isAzureOpenAI = oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI; // Add this line
2108 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);2186 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
2109 const isQuiet = type === 'quiet';2187 const isQuiet = type === 'quiet';
2110 const isImpersonate = type === 'impersonate';2188 const isImpersonate = type === 'impersonate';
2111 const isContinue = type === 'continue';2189 const isContinue = type === 'continue';
2112 const stream = oai_settings.stream_openai && !isQuiet && !(isOAI && ['o1-2024-12-17', 'o1'].includes(oai_settings.openai_model));2190 const stream = oai_settings.stream_openai && !isQuiet && !((isOAI || isAzureOpenAI) && ['o1-2024-12-17', 'o1'].includes(getChatCompletionModel()));
2113 const useLogprobs = !!power_user.request_token_probabilities;2191 const useLogprobs = !!power_user.request_token_probabilities;
2114 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom || isXAI || isAimlapi || isMoonshot);2192 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isAzureOpenAI || isCustom || isXAI || isAimlapi || isMoonshot);
21152193
2116 const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.OPENROUTER, chat_completion_sources.CUSTOM];2194 const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.AZURE_OPENAI, chat_completion_sources.OPENROUTER, chat_completion_sources.CUSTOM];
2117 if (oai_settings.bias_preset_selected2195 if (oai_settings.bias_preset_selected
2118 && logitBiasSources.includes(oai_settings.chat_completion_source)2196 && logitBiasSources.includes(oai_settings.chat_completion_source)
2119 && Array.isArray(oai_settings.bias_presets[oai_settings.bias_preset_selected])2197 && Array.isArray(oai_settings.bias_presets[oai_settings.bias_preset_selected])
@@ -2151,6 +2229,16 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2151 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing,2229 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing,
2152 };2230 };
21532231
2232 if (isAzureOpenAI) {
2233 generate_data.azure_base_url = oai_settings.azure_base_url;
2234 generate_data.azure_deployment_name = oai_settings.azure_deployment_name;
2235 generate_data.azure_api_version = oai_settings.azure_api_version;
2236 // Reasoning effort is not supported on some Azure models (e.g. GPT-3.x, GPT-4.x)
2237 if (/^gpt-[34]/.test(oai_settings.azure_openai_model)) {
2238 delete generate_data.reasoning_effort;
2239 }
2240 }
2241
2154 if (!canMultiSwipe && ToolManager.canPerformToolCalls(type)) {2242 if (!canMultiSwipe && ToolManager.canPerformToolCalls(type)) {
2155 await ToolManager.registerFunctionToolsOpenAI(generate_data);2243 await ToolManager.registerFunctionToolsOpenAI(generate_data);
2156 }2244 }
@@ -2168,18 +2256,18 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2168 }2256 }
21692257
2170 // Add logprobs request (currently OpenAI only, max 5 on their side)2258 // Add logprobs request (currently OpenAI only, max 5 on their side)
2171 if (useLogprobs && (isOAI || isCustom || isDeepSeek || isXAI || isAimlapi)) {2259 if (useLogprobs && (isOAI || isAzureOpenAI || isCustom || isDeepSeek || isXAI || isAimlapi)) {
2172 generate_data['logprobs'] = 5;2260 generate_data['logprobs'] = 5;
2173 }2261 }
21742262
2175 // Remove logit bias/logprobs/stop-strings if not supported by the model2263 // Remove logit bias/logprobs/stop-strings if not supported by the model
2176 const isVision = (m) => ['gpt', 'vision'].every(x => m.includes(x));2264 const isVision = (m) => ['gpt', 'vision'].every(x => m.includes(x));
2177 if (isOAI && isVision(oai_settings.openai_model) || isOpenRouter && isVision(oai_settings.openrouter_model)) {2265 if ((isOAI && isVision(oai_settings.openai_model)) || (isAzureOpenAI && isVision(oai_settings.azure_openai_model)) || (isOpenRouter && isVision(oai_settings.openrouter_model))) {
2178 delete generate_data.logit_bias;2266 delete generate_data.logit_bias;
2179 delete generate_data.stop;2267 delete generate_data.stop;
2180 delete generate_data.logprobs;2268 delete generate_data.logprobs;
2181 }2269 }
2182 if (isOAI && oai_settings.openai_model.includes('gpt-4.5') || isOpenRouter && oai_settings.openrouter_model.includes('gpt-4.5')) {2270 if ((isOAI && oai_settings.openai_model.includes('gpt-4.5')) || (isAzureOpenAI && oai_settings.azure_openai_model.includes('gpt-4.5')) || (isOpenRouter && oai_settings.openrouter_model.includes('gpt-4.5'))) {
2183 delete generate_data.logprobs;2271 delete generate_data.logprobs;
2184 }2272 }
21852273
@@ -2262,16 +2350,6 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2262 // https://api-docs.deepseek.com/api/create-chat-completion2350 // https://api-docs.deepseek.com/api/create-chat-completion
2263 if (isDeepSeek) {2351 if (isDeepSeek) {
2264 generate_data.top_p = generate_data.top_p || Number.EPSILON;2352 generate_data.top_p = generate_data.top_p || Number.EPSILON;
2265
2266 if (generate_data.model.endsWith('-reasoner')) {
2267 delete generate_data.top_p;
2268 delete generate_data.temperature;
2269 delete generate_data.frequency_penalty;
2270 delete generate_data.presence_penalty;
2271 delete generate_data.top_logprobs;
2272 delete generate_data.logprobs;
2273 delete generate_data.logit_bias;
2274 }
2275 }2353 }
22762354
2277 if (isXAI) {2355 if (isXAI) {
@@ -2295,13 +2373,20 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2295 delete generate_data.max_tokens;2373 delete generate_data.max_tokens;
2296 }2374 }
22972375
2376 // https://docs.electronhub.ai/api-reference/chat/completions
2377 if (isElectronHub) {
2378 generate_data['top_k'] = Number(oai_settings.top_k_openai);
2379 }
2380
2298 const seedSupportedSources = [2381 const seedSupportedSources = [
2299 chat_completion_sources.OPENAI,2382 chat_completion_sources.OPENAI,
2383 chat_completion_sources.AZURE_OPENAI,
2300 chat_completion_sources.OPENROUTER,2384 chat_completion_sources.OPENROUTER,
2301 chat_completion_sources.MISTRALAI,2385 chat_completion_sources.MISTRALAI,
2302 chat_completion_sources.CUSTOM,2386 chat_completion_sources.CUSTOM,
2303 chat_completion_sources.COHERE,2387 chat_completion_sources.COHERE,
2304 chat_completion_sources.GROQ,2388 chat_completion_sources.GROQ,
2389 chat_completion_sources.ELECTRONHUB,
2305 chat_completion_sources.NANOGPT,2390 chat_completion_sources.NANOGPT,
2306 chat_completion_sources.XAI,2391 chat_completion_sources.XAI,
2307 chat_completion_sources.POLLINATIONS,2392 chat_completion_sources.POLLINATIONS,
@@ -2313,7 +2398,7 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2313 generate_data['seed'] = oai_settings.seed;2398 generate_data['seed'] = oai_settings.seed;
2314 }2399 }
23152400
2316 if (isOAI && /^(o1|o3|o4)/.test(oai_settings.openai_model)) {2401 if ((isOAI && /^(o1|o3|o4)/.test(oai_settings.openai_model)) || (isAzureOpenAI && /^(o1|o3|o4)/.test(oai_settings.azure_openai_model))) {
2317 generate_data.max_completion_tokens = generate_data.max_tokens;2402 generate_data.max_completion_tokens = generate_data.max_tokens;
2318 delete generate_data.max_tokens;2403 delete generate_data.max_tokens;
2319 delete generate_data.logprobs;2404 delete generate_data.logprobs;
@@ -2336,7 +2421,7 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2336 }2421 }
2337 }2422 }
23382423
2339 if (isOAI && /^gpt-5/.test(oai_settings.openai_model)) {2424 if ((isOAI && /^gpt-5/.test(oai_settings.openai_model)) || (isAzureOpenAI && /^gpt-5/.test(oai_settings.azure_openai_model))) {
2340 generate_data.max_completion_tokens = generate_data.max_tokens;2425 generate_data.max_completion_tokens = generate_data.max_tokens;
2341 delete generate_data.max_tokens;2426 delete generate_data.max_tokens;
2342 delete generate_data.logprobs;2427 delete generate_data.logprobs;
@@ -2466,11 +2551,15 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov
2466 }2551 }
2467 return data.choices?.[0]?.delta?.content || '';2552 return data.choices?.[0]?.delta?.content || '';
2468 } else if (chat_completion_source === chat_completion_sources.OPENROUTER) {2553 } else if (chat_completion_source === chat_completion_sources.OPENROUTER) {
2554 const imageUrl = data?.choices?.[0]?.delta?.images?.find(x => x.type === 'image_url')?.image_url?.url;
2555 if (imageUrl) {
2556 state.image = imageUrl;
2557 }
2469 if (show_thoughts) {2558 if (show_thoughts) {
2470 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');2559 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
2471 }2560 }
2472 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';2561 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
2473 } else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI, chat_completion_sources.MOONSHOT, chat_completion_sources.COMETAPI].includes(chat_completion_source)) {2562 } else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI, chat_completion_sources.MOONSHOT, chat_completion_sources.COMETAPI, chat_completion_sources.ELECTRONHUB].includes(chat_completion_source)) {
2474 if (show_thoughts) {2563 if (show_thoughts) {
2475 state.reasoning +=2564 state.reasoning +=
2476 data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content ??2565 data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content ??
@@ -2502,6 +2591,7 @@ function parseChatCompletionLogprobs(data) {
25022591
2503 switch (oai_settings.chat_completion_source) {2592 switch (oai_settings.chat_completion_source) {
2504 case chat_completion_sources.OPENAI:2593 case chat_completion_sources.OPENAI:
2594 case chat_completion_sources.AZURE_OPENAI:
2505 case chat_completion_sources.DEEPSEEK:2595 case chat_completion_sources.DEEPSEEK:
2506 case chat_completion_sources.XAI:2596 case chat_completion_sources.XAI:
2507 case chat_completion_sources.CUSTOM:2597 case chat_completion_sources.CUSTOM:
@@ -2510,7 +2600,7 @@ function parseChatCompletionLogprobs(data) {
2510 }2600 }
2511 // OpenAI Text Completion API is treated as a chat completion source2601 // OpenAI Text Completion API is treated as a chat completion source
2512 // by SillyTavern, hence its presence in this function.2602 // by SillyTavern, hence its presence in this function.
2513 return textCompletionModels.includes(oai_settings.openai_model)2603 return textCompletionModels.includes(getChatCompletionModel())
2514 ? parseOpenAITextLogprobs(data.choices[0]?.logprobs)2604 ? parseOpenAITextLogprobs(data.choices[0]?.logprobs)
2515 : parseOpenAIChatLogprobs(data.choices[0]?.logprobs);2605 : parseOpenAIChatLogprobs(data.choices[0]?.logprobs);
2516 default:2606 default:
@@ -3432,6 +3522,7 @@ function loadOpenAISettings(data, settings) {
3432 oai_settings.cohere_model = settings.cohere_model ?? default_settings.cohere_model;3522 oai_settings.cohere_model = settings.cohere_model ?? default_settings.cohere_model;
3433 oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;3523 oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;
3434 oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;3524 oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
3525 oai_settings.electronhub_model = settings.electronhub_model ?? default_settings.electronhub_model;
3435 oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;3526 oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
3436 oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;3527 oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
3437 oai_settings.aimlapi_model = settings.aimlapi_model ?? default_settings.aimlapi_model;3528 oai_settings.aimlapi_model = settings.aimlapi_model ?? default_settings.aimlapi_model;
@@ -3447,6 +3538,10 @@ function loadOpenAISettings(data, settings) {
3447 oai_settings.custom_include_headers = settings.custom_include_headers ?? default_settings.custom_include_headers;3538 oai_settings.custom_include_headers = settings.custom_include_headers ?? default_settings.custom_include_headers;
3448 oai_settings.custom_prompt_post_processing = settings.custom_prompt_post_processing ?? default_settings.custom_prompt_post_processing;3539 oai_settings.custom_prompt_post_processing = settings.custom_prompt_post_processing ?? default_settings.custom_prompt_post_processing;
3449 oai_settings.google_model = settings.google_model ?? default_settings.google_model;3540 oai_settings.google_model = settings.google_model ?? default_settings.google_model;
3541 oai_settings.azure_base_url = settings.azure_base_url ?? default_settings.azure_base_url;
3542 oai_settings.azure_deployment_name = settings.azure_deployment_name ?? default_settings.azure_deployment_name;
3543 oai_settings.azure_api_version = settings.azure_api_version ?? default_settings.azure_api_version;
3544 oai_settings.azure_openai_model = settings.azure_openai_model ?? default_settings.azure_openai_model;
3450 oai_settings.vertexai_model = settings.vertexai_model ?? default_settings.vertexai_model;3545 oai_settings.vertexai_model = settings.vertexai_model ?? default_settings.vertexai_model;
3451 oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;3546 oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;
3452 oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;3547 oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;
@@ -3527,6 +3622,8 @@ function loadOpenAISettings(data, settings) {
3527 $(`#model_perplexity_select option[value="${oai_settings.perplexity_model}"`).prop('selected', true);3622 $(`#model_perplexity_select option[value="${oai_settings.perplexity_model}"`).prop('selected', true);
3528 $('#model_groq_select').val(oai_settings.groq_model);3623 $('#model_groq_select').val(oai_settings.groq_model);
3529 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).prop('selected', true);3624 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).prop('selected', true);
3625 $('#model_electronhub_select').val(oai_settings.electronhub_model);
3626 $(`#model_electronhub_select option[value="${oai_settings.electronhub_model}"`).prop('selected', true);
3530 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);3627 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);
3531 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).prop('selected', true);3628 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).prop('selected', true);
3532 $('#model_deepseek_select').val(oai_settings.deepseek_model);3629 $('#model_deepseek_select').val(oai_settings.deepseek_model);
@@ -3541,6 +3638,11 @@ function loadOpenAISettings(data, settings) {
3541 $(`#model_moonshot_select option[value="${oai_settings.moonshot_model}"`).prop('selected', true);3638 $(`#model_moonshot_select option[value="${oai_settings.moonshot_model}"`).prop('selected', true);
3542 $('#custom_model_id').val(oai_settings.custom_model);3639 $('#custom_model_id').val(oai_settings.custom_model);
3543 $('#custom_api_url_text').val(oai_settings.custom_url);3640 $('#custom_api_url_text').val(oai_settings.custom_url);
3641 $('#azure_base_url').val(oai_settings.azure_base_url);
3642 $('#azure_deployment_name').val(oai_settings.azure_deployment_name);
3643 $('#azure_api_version').val(oai_settings.azure_api_version);
3644 $('#azure_openai_model').val(oai_settings.azure_openai_model);
3645
3544 $('#openai_max_context').val(oai_settings.openai_max_context);3646 $('#openai_max_context').val(oai_settings.openai_max_context);
3545 $('#openai_max_context_counter').val(`${oai_settings.openai_max_context}`);3647 $('#openai_max_context_counter').val(`${oai_settings.openai_max_context}`);
3546 $('#model_openrouter_select').val(oai_settings.openrouter_model);3648 $('#model_openrouter_select').val(oai_settings.openrouter_model);
@@ -3719,6 +3821,12 @@ async function getStatusOpen() {
3719 return resultCheckStatus();3821 return resultCheckStatus();
3720 }3822 }
37213823
3824 if (oai_settings.chat_completion_source === chat_completion_sources.AZURE_OPENAI && !isValidUrl(oai_settings.azure_base_url)) {
3825 console.debug('Invalid endpoint URL of Azure OpenAI API:', oai_settings.azure_base_url);
3826 setOnlineStatus(t`Invalid Azure endpoint URL. Requests may fail.`);
3827 return resultCheckStatus();
3828 }
3829
3722 let data = {3830 let data = {
3723 reverse_proxy: oai_settings.reverse_proxy,3831 reverse_proxy: oai_settings.reverse_proxy,
3724 proxy_password: oai_settings.proxy_password,3832 proxy_password: oai_settings.proxy_password,
@@ -3744,6 +3852,12 @@ async function getStatusOpen() {
3744 data.custom_include_headers = oai_settings.custom_include_headers;3852 data.custom_include_headers = oai_settings.custom_include_headers;
3745 }3853 }
37463854
3855 if (oai_settings.chat_completion_source === chat_completion_sources.AZURE_OPENAI) {
3856 data.azure_base_url = oai_settings.azure_base_url;
3857 data.azure_deployment_name = oai_settings.azure_deployment_name;
3858 data.azure_api_version = oai_settings.azure_api_version;
3859 }
3860
3747 const canBypass = (oai_settings.chat_completion_source === chat_completion_sources.OPENAI && oai_settings.bypass_status_check) || oai_settings.chat_completion_source === chat_completion_sources.CUSTOM;3861 const canBypass = (oai_settings.chat_completion_source === chat_completion_sources.OPENAI && oai_settings.bypass_status_check) || oai_settings.chat_completion_source === chat_completion_sources.CUSTOM;
3748 if (canBypass) {3862 if (canBypass) {
3749 setOnlineStatus(t`Status check bypassed`);3863 setOnlineStatus(t`Status check bypassed`);
@@ -3813,6 +3927,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3813 xai_model: settings.xai_model,3927 xai_model: settings.xai_model,
3814 pollinations_model: settings.pollinations_model,3928 pollinations_model: settings.pollinations_model,
3815 aimlapi_model: settings.aimlapi_model,3929 aimlapi_model: settings.aimlapi_model,
3930 electronhub_model: settings.electronhub_model,
3816 moonshot_model: settings.moonshot_model,3931 moonshot_model: settings.moonshot_model,
3817 fireworks_model: settings.fireworks_model,3932 fireworks_model: settings.fireworks_model,
3818 cometapi_model: settings.cometapi_model,3933 cometapi_model: settings.cometapi_model,
@@ -3824,6 +3939,10 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3824 custom_prompt_post_processing: settings.custom_prompt_post_processing,3939 custom_prompt_post_processing: settings.custom_prompt_post_processing,
3825 google_model: settings.google_model,3940 google_model: settings.google_model,
3826 vertexai_model: settings.vertexai_model,3941 vertexai_model: settings.vertexai_model,
3942 azure_base_url: settings.azure_base_url,
3943 azure_deployment_name: settings.azure_deployment_name,
3944 azure_api_version: settings.azure_api_version,
3945 azure_openai_model: settings.azure_openai_model,
3827 temperature: settings.temp_openai,3946 temperature: settings.temp_openai,
3828 frequency_penalty: settings.freq_pen_openai,3947 frequency_penalty: settings.freq_pen_openai,
3829 presence_penalty: settings.pres_pen_openai,3948 presence_penalty: settings.pres_pen_openai,
@@ -4578,6 +4697,47 @@ function getFireworksMaxContext(model, isUnlocked) {
4578 return max_32k;4697 return max_32k;
4579}4698}
45804699
4700/**
4701 * Get the maximum context size for the ElectronHub model
4702 * @param {string} model Model identifier
4703 * @param {boolean} isUnlocked Whether context limits are unlocked
4704 * @returns {number} Maximum context size in tokens
4705 */
4706function getElectronHubMaxContext(model, isUnlocked) {
4707 if (isUnlocked) {
4708 return unlocked_max;
4709 }
4710
4711 if (Array.isArray(model_list)) {
4712 const modelInfo = model_list.find(m => m.id === model);
4713 if (modelInfo?.tokens) {
4714 return modelInfo.tokens;
4715 }
4716 }
4717 return max_8k;
4718}
4719
4720/**
4721 * Get the maximum context size for the NanoGPT model
4722 * @param {string} model Model identifier
4723 * @param {boolean} isUnlocked Whether context limits are unlocked
4724 * @returns {number} Maximum context size in tokens
4725 */
4726function getNanoGptMaxContext(model, isUnlocked) {
4727 if (isUnlocked) {
4728 return unlocked_max;
4729 }
4730
4731 if (Array.isArray(model_list)) {
4732 const modelInfo = model_list.find(m => m.id === model);
4733 if (modelInfo?.context_length) {
4734 return modelInfo.context_length;
4735 }
4736 }
4737
4738 return max_128k;
4739}
4740
4581async function onModelChange() {4741async function onModelChange() {
4582 biasCache = undefined;4742 biasCache = undefined;
4583 let value = String($(this).val() || '');4743 let value = String($(this).val() || '');
@@ -4668,6 +4828,15 @@ async function onModelChange() {
4668 oai_settings.groq_model = value;4828 oai_settings.groq_model = value;
4669 }4829 }
46704830
4831 if ($(this).is('#model_electronhub_select')) {
4832 if (!value) {
4833 console.debug('Null ElectronHub model selected. Ignoring.');
4834 return;
4835 }
4836 console.log('ElectronHub model changed to', value);
4837 oai_settings.electronhub_model = value;
4838 }
4839
4671 if ($(this).is('#model_nanogpt_select')) {4840 if ($(this).is('#model_nanogpt_select')) {
4672 if (!value) {4841 if (!value) {
4673 console.debug('Null NanoGPT model selected. Ignoring.');4842 console.debug('Null NanoGPT model selected. Ignoring.');
@@ -4736,11 +4905,21 @@ async function onModelChange() {
4736 oai_settings.cometapi_model = value;4905 oai_settings.cometapi_model = value;
4737 }4906 }
47384907
4908 if ($(this).is('#azure_openai_model')) {
4909 if (!value) {
4910 console.debug('Null Azure OpenAI model selected. Ignoring.');
4911 return;
4912 }
4913 oai_settings.azure_openai_model = value;
4914 }
4915
4739 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {4916 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
4740 if (oai_settings.max_context_unlocked) {4917 if (oai_settings.max_context_unlocked) {
4741 $('#openai_max_context').attr('max', max_2mil);4918 $('#openai_max_context').attr('max', max_2mil);
4742 } else if (value.includes('gemini-1.5-pro')) {4919 } else if (value.includes('gemini-1.5-pro')) {
4743 $('#openai_max_context').attr('max', max_2mil);4920 $('#openai_max_context').attr('max', max_2mil);
4921 } else if (value.includes('gemini-2.5-flash-image-preview')) {
4922 $('#openai_max_context').attr('max', max_32k);
4744 } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash') || value.includes('gemini-2.0-pro') || value.includes('gemini-exp') || value.includes('gemini-2.5-flash') || value.includes('gemini-2.5-pro') || value.includes('learnlm-2.0-flash')) {4923 } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash') || value.includes('gemini-2.0-pro') || value.includes('gemini-exp') || value.includes('gemini-2.5-flash') || value.includes('gemini-2.5-pro') || value.includes('learnlm-2.0-flash')) {
4745 $('#openai_max_context').attr('max', max_1mil);4924 $('#openai_max_context').attr('max', max_1mil);
4746 } else if (value.includes('gemma-3-27b-it')) {4925 } else if (value.includes('gemma-3-27b-it')) {
@@ -4808,7 +4987,7 @@ async function onModelChange() {
4808 $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');4987 $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
4809 }4988 }
48104989
4811 if (oai_settings.chat_completion_source == chat_completion_sources.OPENAI) {4990 if ([chat_completion_sources.AZURE_OPENAI, chat_completion_sources.OPENAI].includes(oai_settings.chat_completion_source)) {
4812 $('#openai_max_context').attr('max', getMaxContextOpenAI(value));4991 $('#openai_max_context').attr('max', getMaxContextOpenAI(value));
4813 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));4992 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));
4814 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');4993 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
@@ -4907,15 +5086,21 @@ async function onModelChange() {
4907 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');5086 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4908 }5087 }
49095088
4910 if (oai_settings.chat_completion_source === chat_completion_sources.NANOGPT) {5089 if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
4911 if (oai_settings.max_context_unlocked) {5090 const maxContext = getElectronHubMaxContext(oai_settings.electronhub_model, oai_settings.max_context_unlocked);
4912 $('#openai_max_context').attr('max', unlocked_max);5091 $('#openai_max_context').attr('max', maxContext);
4913 } else {5092 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4914 $('#openai_max_context').attr('max', max_128k);5093 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4915 }5094 oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
5095 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
5096 }
49165097
5098 if (oai_settings.chat_completion_source === chat_completion_sources.NANOGPT) {
5099 const maxContext = getNanoGptMaxContext(oai_settings.nanogpt_model, oai_settings.max_context_unlocked);
5100 $('#openai_max_context').attr('max', maxContext);
4917 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);5101 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4918 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');5102 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
5103 oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
4919 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');5104 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4920 }5105 }
49215106
@@ -5204,6 +5389,19 @@ async function onConnectButtonClick(e) {
5204 }5389 }
5205 }5390 }
52065391
5392 if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
5393 const api_key_electronhub = String($('#api_key_electronhub').val()).trim();
5394
5395 if (api_key_electronhub.length) {
5396 await writeSecret(SECRET_KEYS.ELECTRONHUB, api_key_electronhub);
5397 }
5398
5399 if (!secret_state[SECRET_KEYS.ELECTRONHUB]) {
5400 console.log('No secret key saved for Electron Hub');
5401 return;
5402 }
5403 }
5404
5207 if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {5405 if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {
5208 const api_key_nanogpt = String($('#api_key_nanogpt').val()).trim();5406 const api_key_nanogpt = String($('#api_key_nanogpt').val()).trim();
52095407
@@ -5295,6 +5493,20 @@ async function onConnectButtonClick(e) {
5295 }5493 }
5296 }5494 }
52975495
5496 if (oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) {
5497 const api_key_azure_openai = String($('#api_key_azure_openai').val()).trim();
5498
5499 if (api_key_azure_openai.length) {
5500 await writeSecret(SECRET_KEYS.AZURE_OPENAI, api_key_azure_openai);
5501 }
5502
5503 if (!api_key_azure_openai && !secret_state[SECRET_KEYS.AZURE_OPENAI]) {
5504 console.log('No secret key saved for Azure OpenAI');
5505 return;
5506 }
5507 }
5508
5509
5298 startStatusLoading();5510 startStatusLoading();
5299 saveSettingsDebounced();5511 saveSettingsDebounced();
5300 await getStatusOpen();5512 await getStatusOpen();
@@ -5338,6 +5550,9 @@ function toggleChatCompletionForms() {
5338 else if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) {5550 else if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) {
5339 $('#model_groq_select').trigger('change');5551 $('#model_groq_select').trigger('change');
5340 }5552 }
5553 else if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
5554 $('#model_electronhub_select').trigger('change');
5555 }
5341 else if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {5556 else if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {
5342 $('#model_nanogpt_select').trigger('change');5557 $('#model_nanogpt_select').trigger('change');
5343 }5558 }
@@ -5365,6 +5580,9 @@ function toggleChatCompletionForms() {
5365 else if (oai_settings.chat_completion_source == chat_completion_sources.COMETAPI) {5580 else if (oai_settings.chat_completion_source == chat_completion_sources.COMETAPI) {
5366 $('#model_cometapi_select').trigger('change');5581 $('#model_cometapi_select').trigger('change');
5367 }5582 }
5583 else if (oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) {
5584 $('#azure_openai_model').trigger('change');
5585 }
53685586
5369 $('[data-source]').each(function () {5587 $('[data-source]').each(function () {
5370 const validSources = $(this).data('source').split(',');5588 const validSources = $(this).data('source').split(',');
@@ -5484,10 +5702,15 @@ export function isImageInliningSupported() {
54845702
5485 switch (oai_settings.chat_completion_source) {5703 switch (oai_settings.chat_completion_source) {
5486 case chat_completion_sources.OPENAI:5704 case chat_completion_sources.OPENAI:
5705 case chat_completion_sources.AZURE_OPENAI: {
5706 const modelToCheck = oai_settings.chat_completion_source === chat_completion_sources.AZURE_OPENAI
5707 ? oai_settings.azure_openai_model
5708 : oai_settings.openai_model;
5487 return visionSupportedModels.some(model =>5709 return visionSupportedModels.some(model =>
5488 oai_settings.openai_model.includes(model)5710 modelToCheck.includes(model)
5489 && ['gpt-4-turbo-preview', 'o1-mini', 'o3-mini'].some(x => !oai_settings.openai_model.includes(x)),5711 && ['gpt-4-turbo-preview', 'o1-mini', 'o3-mini'].some(x => !modelToCheck.includes(x)),
5490 );5712 );
5713 }
5491 case chat_completion_sources.MAKERSUITE:5714 case chat_completion_sources.MAKERSUITE:
5492 return visionSupportedModels.some(model => oai_settings.google_model.includes(model));5715 return visionSupportedModels.some(model => oai_settings.google_model.includes(model));
5493 case chat_completion_sources.VERTEXAI:5716 case chat_completion_sources.VERTEXAI:
@@ -5495,7 +5718,7 @@ export function isImageInliningSupported() {
5495 case chat_completion_sources.CLAUDE:5718 case chat_completion_sources.CLAUDE:
5496 return visionSupportedModels.some(model => oai_settings.claude_model.includes(model));5719 return visionSupportedModels.some(model => oai_settings.claude_model.includes(model));
5497 case chat_completion_sources.OPENROUTER:5720 case chat_completion_sources.OPENROUTER:
5498 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.openrouter_model)?.architecture?.modality === 'text+image->text');5721 return (Array.isArray(model_list) && ['text+image->text+image', 'text+image->text'].includes(model_list.find(m => m.id === oai_settings.openrouter_model)?.architecture?.modality));
5499 case chat_completion_sources.CUSTOM:5722 case chat_completion_sources.CUSTOM:
5500 return true;5723 return true;
5501 case chat_completion_sources.MISTRALAI:5724 case chat_completion_sources.MISTRALAI:
@@ -5506,12 +5729,16 @@ export function isImageInliningSupported() {
5506 return visionSupportedModels.some(model => oai_settings.xai_model.includes(model));5729 return visionSupportedModels.some(model => oai_settings.xai_model.includes(model));
5507 case chat_completion_sources.AIMLAPI:5730 case chat_completion_sources.AIMLAPI:
5508 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.aimlapi_model)?.features?.includes('openai/chat-completion.vision'));5731 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.aimlapi_model)?.features?.includes('openai/chat-completion.vision'));
5732 case chat_completion_sources.ELECTRONHUB:
5733 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.electronhub_model)?.metadata?.vision);
5509 case chat_completion_sources.POLLINATIONS:5734 case chat_completion_sources.POLLINATIONS:
5510 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.pollinations_model)?.vision);5735 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.pollinations_model)?.vision);
5511 case chat_completion_sources.COMETAPI:5736 case chat_completion_sources.COMETAPI:
5512 return true;5737 return true;
5513 case chat_completion_sources.MOONSHOT:5738 case chat_completion_sources.MOONSHOT:
5514 return visionSupportedModels.some(model => oai_settings.moonshot_model.includes(model));5739 return visionSupportedModels.some(model => oai_settings.moonshot_model.includes(model));
5740 case chat_completion_sources.NANOGPT:
5741 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.nanogpt_model)?.capabilities?.vision);
5515 default:5742 default:
5516 return false;5743 return false;
5517 }5744 }
@@ -6164,6 +6391,21 @@ export function initOpenAI() {
6164 saveSettingsDebounced();6391 saveSettingsDebounced();
6165 });6392 });
61666393
6394 $('#azure_base_url').on('input', function () {
6395 oai_settings.azure_base_url = String($(this).val());
6396 saveSettingsDebounced();
6397 });
6398
6399 $('#azure_deployment_name').on('input', function () {
6400 oai_settings.azure_deployment_name = String($(this).val());
6401 saveSettingsDebounced();
6402 });
6403
6404 $('#azure_api_version').on('input change', function () {
6405 oai_settings.azure_api_version = String($(this).val());
6406 saveSettingsDebounced();
6407 });
6408
6167 $('#character_names_none').on('input', function () {6409 $('#character_names_none').on('input', function () {
6168 oai_settings.names_behavior = character_names_behavior.NONE;6410 oai_settings.names_behavior = character_names_behavior.NONE;
6169 setNamesBehaviorControls();6411 setNamesBehaviorControls();
@@ -6312,6 +6554,7 @@ export function initOpenAI() {
6312 $('#model_cohere_select').on('change', onModelChange);6554 $('#model_cohere_select').on('change', onModelChange);
6313 $('#model_perplexity_select').on('change', onModelChange);6555 $('#model_perplexity_select').on('change', onModelChange);
6314 $('#model_groq_select').on('change', onModelChange);6556 $('#model_groq_select').on('change', onModelChange);
6557 $('#model_electronhub_select').on('change', onModelChange);
6315 $('#model_nanogpt_select').on('change', onModelChange);6558 $('#model_nanogpt_select').on('change', onModelChange);
6316 $('#model_deepseek_select').on('change', onModelChange);6559 $('#model_deepseek_select').on('change', onModelChange);
6317 $('#model_aimlapi_select').on('change', onModelChange);6560 $('#model_aimlapi_select').on('change', onModelChange);
@@ -6321,6 +6564,7 @@ export function initOpenAI() {
6321 $('#model_cometapi_select').on('change', onModelChange);6564 $('#model_cometapi_select').on('change', onModelChange);
6322 $('#model_moonshot_select').on('change', onModelChange);6565 $('#model_moonshot_select').on('change', onModelChange);
6323 $('#model_fireworks_select').on('change', onModelChange);6566 $('#model_fireworks_select').on('change', onModelChange);
6567 $('#azure_openai_model').on('change', onModelChange);
6324 $('#settings_preset_openai').on('change', onSettingsPresetChange);6568 $('#settings_preset_openai').on('change', onSettingsPresetChange);
6325 $('#new_oai_preset').on('click', onNewPresetClick);6569 $('#new_oai_preset').on('click', onNewPresetClick);
6326 $('#delete_oai_preset').on('click', onDeletePresetClick);6570 $('#delete_oai_preset').on('click', onDeletePresetClick);
public/scripts/reasoning.js+23 -0
@@ -1348,6 +1348,29 @@ function registerReasoningAppEvents() {
1348 for (const event of [event_types.GENERATION_STOPPED, event_types.GENERATION_ENDED, event_types.CHAT_CHANGED]) {1348 for (const event of [event_types.GENERATION_STOPPED, event_types.GENERATION_ENDED, event_types.CHAT_CHANGED]) {
1349 eventSource.on(event, () => PromptReasoning.clearLatest());1349 eventSource.on(event, () => PromptReasoning.clearLatest());
1350 }1350 }
1351
1352 eventSource.makeFirst(event_types.IMPERSONATE_READY, async () => {
1353 if (!power_user.reasoning.auto_parse) {
1354 return;
1355 }
1356
1357 const sendTextArea = /** @type {HTMLTextAreaElement} */ (document.getElementById('send_textarea'));
1358
1359 if (!sendTextArea) {
1360 console.warn('[Reasoning] Send textarea not found');
1361 return;
1362 }
1363
1364 console.debug('[Reasoning] Auto-parsing reasoning block for impersonation');
1365
1366 if (!sendTextArea.value) {
1367 console.debug('[Reasoning] Reasoning is empty, skipping');
1368 return;
1369 }
1370
1371 sendTextArea.value = removeReasoningFromString(sendTextArea.value);
1372 sendTextArea.dispatchEvent(new Event('input', { bubbles: true }));
1373 });
1351}1374}
13521375
1353/**1376/**
public/scripts/secrets.js+6 -0
@@ -47,10 +47,12 @@ export const SECRET_KEYS = {
47 PERPLEXITY: 'api_key_perplexity',47 PERPLEXITY: 'api_key_perplexity',
48 GROQ: 'api_key_groq',48 GROQ: 'api_key_groq',
49 AZURE_TTS: 'api_key_azure_tts',49 AZURE_TTS: 'api_key_azure_tts',
50 AZURE_OPENAI: 'api_key_azure_openai',
50 FEATHERLESS: 'api_key_featherless',51 FEATHERLESS: 'api_key_featherless',
51 HUGGINGFACE: 'api_key_huggingface',52 HUGGINGFACE: 'api_key_huggingface',
52 STABILITY: 'api_key_stability',53 STABILITY: 'api_key_stability',
53 CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts',54 CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts',
55 ELECTRONHUB: 'api_key_electronhub',
54 NANOGPT: 'api_key_nanogpt',56 NANOGPT: 'api_key_nanogpt',
55 TAVILY: 'api_key_tavily',57 TAVILY: 'api_key_tavily',
56 BFL: 'api_key_bfl',58 BFL: 'api_key_bfl',
@@ -95,6 +97,7 @@ const FRIENDLY_NAMES = {
95 [SECRET_KEYS.GROQ]: 'Groq',97 [SECRET_KEYS.GROQ]: 'Groq',
96 [SECRET_KEYS.FEATHERLESS]: 'Featherless',98 [SECRET_KEYS.FEATHERLESS]: 'Featherless',
97 [SECRET_KEYS.HUGGINGFACE]: 'HuggingFace',99 [SECRET_KEYS.HUGGINGFACE]: 'HuggingFace',
100 [SECRET_KEYS.ELECTRONHUB]: 'Electron Hub',
98 [SECRET_KEYS.NANOGPT]: 'NanoGPT',101 [SECRET_KEYS.NANOGPT]: 'NanoGPT',
99 [SECRET_KEYS.GENERIC]: 'Generic (OpenAI-compatible)',102 [SECRET_KEYS.GENERIC]: 'Generic (OpenAI-compatible)',
100 [SECRET_KEYS.DEEPSEEK]: 'DeepSeek',103 [SECRET_KEYS.DEEPSEEK]: 'DeepSeek',
@@ -120,6 +123,7 @@ const FRIENDLY_NAMES = {
120 [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',123 [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',
121 [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',124 [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',
122 [SECRET_KEYS.COMETAPI]: 'CometAPI',125 [SECRET_KEYS.COMETAPI]: 'CometAPI',
126 [SECRET_KEYS.AZURE_OPENAI]: 'Azure OpenAI',
123};127};
124128
125const INPUT_MAP = {129const INPUT_MAP = {
@@ -148,6 +152,7 @@ const INPUT_MAP = {
148 [SECRET_KEYS.GROQ]: '#api_key_groq',152 [SECRET_KEYS.GROQ]: '#api_key_groq',
149 [SECRET_KEYS.FEATHERLESS]: '#api_key_featherless',153 [SECRET_KEYS.FEATHERLESS]: '#api_key_featherless',
150 [SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',154 [SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',
155 [SECRET_KEYS.ELECTRONHUB]: '#api_key_electronhub',
151 [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',156 [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
152 [SECRET_KEYS.GENERIC]: '#api_key_generic',157 [SECRET_KEYS.GENERIC]: '#api_key_generic',
153 [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',158 [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
@@ -157,6 +162,7 @@ const INPUT_MAP = {
157 [SECRET_KEYS.MOONSHOT]: '#api_key_moonshot',162 [SECRET_KEYS.MOONSHOT]: '#api_key_moonshot',
158 [SECRET_KEYS.FIREWORKS]: '#api_key_fireworks',163 [SECRET_KEYS.FIREWORKS]: '#api_key_fireworks',
159 [SECRET_KEYS.COMETAPI]: '#api_key_cometapi',164 [SECRET_KEYS.COMETAPI]: '#api_key_cometapi',
165 [SECRET_KEYS.AZURE_OPENAI]: '#api_key_azure_openai',
160};166};
161167
162const getLabel = () => moment().format('L LT');168const getLabel = () => moment().format('L LT');
public/scripts/slash-commands.js+361 -367
@@ -229,7 +229,7 @@ export function initDefaultSlashCommands() {
229 SlashCommandParser.addCommandObject(SlashCommand.fromProps({229 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
230 name: 'dupe',230 name: 'dupe',
231 callback: duplicateCharacter,231 callback: duplicateCharacter,
232 helpString: 'Duplicates the currently selected character.',232 helpString: t`Duplicates the currently selected character.`,
233 }));233 }));
234 SlashCommandParser.addCommandObject(SlashCommand.fromProps({234 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
235 name: 'api',235 name: 'api',
@@ -306,11 +306,11 @@ export function initDefaultSlashCommands() {
306 toastr.clear(toast);306 toastr.clear(toast);
307 return text?.toString()?.trim() ?? '';307 return text?.toString()?.trim() ?? '';
308 },308 },
309 returns: 'the current API',309 returns: t`the current API`,
310 namedArgumentList: [310 namedArgumentList: [
311 SlashCommandNamedArgument.fromProps({311 SlashCommandNamedArgument.fromProps({
312 name: 'quiet',312 name: 'quiet',
313 description: 'Suppress the toast message on connection',313 description: t`Suppress the toast message on connection`,
314 typeList: [ARGUMENT_TYPE.BOOLEAN],314 typeList: [ARGUMENT_TYPE.BOOLEAN],
315 defaultValue: 'false',315 defaultValue: 'false',
316 enumList: commonEnumProviders.boolean('trueFalse')(),316 enumList: commonEnumProviders.boolean('trueFalse')(),
@@ -318,7 +318,7 @@ export function initDefaultSlashCommands() {
318 ],318 ],
319 unnamedArgumentList: [319 unnamedArgumentList: [
320 SlashCommandArgument.fromProps({320 SlashCommandArgument.fromProps({
321 description: 'API to connect to',321 description: t`API to connect to`,
322 typeList: [ARGUMENT_TYPE.STRING],322 typeList: [ARGUMENT_TYPE.STRING],
323 enumList: Object.entries(CONNECT_API_MAP).sort(([a], [b]) => a.localeCompare(b)).map(([api, { selected }]) =>323 enumList: Object.entries(CONNECT_API_MAP).sort(([a], [b]) => a.localeCompare(b)).map(([api, { selected }]) =>
324 new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === selected)),324 new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === selected)),
@@ -327,10 +327,10 @@ export function initDefaultSlashCommands() {
327 ],327 ],
328 helpString: `328 helpString: `
329 <div>329 <div>
330 Connect to an API. If no argument is provided, it will return the currently connected API.330 ${t`Connect to an API. If no argument is provided, it will return the currently connected API.`}
331 </div>331 </div>
332 <div>332 <div>
333 <strong>Available APIs:</strong>333 <strong>${t`Available APIs:`}</strong>
334 <pre><code>${Object.keys(CONNECT_API_MAP).sort((a, b) => a.localeCompare(b)).join(', ')}</code></pre>334 <pre><code>${Object.keys(CONNECT_API_MAP).sort((a, b) => a.localeCompare(b)).join(', ')}</code></pre>
335 </div>335 </div>
336 `,336 `,
@@ -367,7 +367,7 @@ export function initDefaultSlashCommands() {
367 namedArgumentList: [367 namedArgumentList: [
368 new SlashCommandNamedArgument(368 new SlashCommandNamedArgument(
369 'await',369 'await',
370 'Whether to await for the triggered generation before continuing',370 t`Whether to await for the triggered generation before continuing`,
371 [ARGUMENT_TYPE.BOOLEAN],371 [ARGUMENT_TYPE.BOOLEAN],
372 false,372 false,
373 false,373 false,
@@ -381,13 +381,13 @@ export function initDefaultSlashCommands() {
381 ],381 ],
382 helpString: `382 helpString: `
383 <div>383 <div>
384 Calls an impersonation response, with an optional additional prompt.384 ${t`Calls an impersonation response, with an optional additional prompt.`}
385 </div>385 </div>
386 <div>386 <div>
387 If <code>await=true</code> named argument is passed, the command will wait for the impersonation to end before continuing.387 ${t`If <code>await=true</code> named argument is passed, the command will wait for the impersonation to end before continuing.`}
388 </div>388 </div>
389 <div>389 <div>
390 <strong>Example:</strong>390 <strong>${t`Example:`}</strong>
391 <ul>391 <ul>
392 <li>392 <li>
393 <pre><code class="language-stscript">/impersonate What is the meaning of life?</code></pre>393 <pre><code class="language-stscript">/impersonate What is the meaning of life?</code></pre>
@@ -426,7 +426,7 @@ export function initDefaultSlashCommands() {
426 $(currentChatDeleteButton).trigger('click', { fromSlashCommand: true });426 $(currentChatDeleteButton).trigger('click', { fromSlashCommand: true });
427 }));427 }));
428 },428 },
429 helpString: 'Deletes the current chat.',429 helpString: t`Deletes the current chat.`,
430 }));430 }));
431 SlashCommandParser.addCommandObject(SlashCommand.fromProps({431 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
432 name: 'renamechat',432 name: 'renamechat',
@@ -449,18 +449,18 @@ export function initDefaultSlashCommands() {
449 },449 },
450 unnamedArgumentList: [450 unnamedArgumentList: [
451 new SlashCommandArgument(451 new SlashCommandArgument(
452 'new chat name', [ARGUMENT_TYPE.STRING], true,452 t`new chat name`, [ARGUMENT_TYPE.STRING], true,
453 ),453 ),
454 ],454 ],
455 helpString: 'Renames the current chat.',455 helpString: t`Renames the current chat.`,
456 }));456 }));
457 SlashCommandParser.addCommandObject(SlashCommand.fromProps({457 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
458 name: 'getchatname',458 name: 'getchatname',
459 callback: async function doGetChatName() {459 callback: async function doGetChatName() {
460 return getCurrentChatDetails().sessionName;460 return getCurrentChatDetails().sessionName;
461 },461 },
462 returns: 'chat file name',462 returns: t`chat file name`,
463 helpString: 'Returns the name of the current chat file into the pipe.',463 helpString: t`Returns the name of the current chat file into the pipe.`,
464 }));464 }));
465 SlashCommandParser.addCommandObject(SlashCommand.fromProps({465 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
466 name: 'closechat',466 name: 'closechat',
@@ -468,7 +468,7 @@ export function initDefaultSlashCommands() {
468 $('#option_close_chat').trigger('click');468 $('#option_close_chat').trigger('click');
469 return '';469 return '';
470 },470 },
471 helpString: 'Closes the current chat.',471 helpString: t`Closes the current chat.`,
472 }));472 }));
473 SlashCommandParser.addCommandObject(SlashCommand.fromProps({473 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
474 name: 'tempchat',474 name: 'tempchat',
@@ -476,7 +476,7 @@ export function initDefaultSlashCommands() {
476 return new Promise((resolve, reject) => {476 return new Promise((resolve, reject) => {
477 const eventCallback = async (chatId) => {477 const eventCallback = async (chatId) => {
478 if (chatId) {478 if (chatId) {
479 return reject('Not in a temporary chat');479 return reject(t`Not in a temporary chat`);
480 }480 }
481 await newAssistantChat({ temporary: true });481 await newAssistantChat({ temporary: true });
482 return resolve('');482 return resolve('');
@@ -484,12 +484,12 @@ export function initDefaultSlashCommands() {
484 eventSource.once(event_types.CHAT_CHANGED, eventCallback);484 eventSource.once(event_types.CHAT_CHANGED, eventCallback);
485 $('#option_close_chat').trigger('click');485 $('#option_close_chat').trigger('click');
486 setTimeout(() => {486 setTimeout(() => {
487 reject('Failed to open temporary chat');487 reject(t`Failed to open temporary chat`);
488 eventSource.removeListener(event_types.CHAT_CHANGED, eventCallback);488 eventSource.removeListener(event_types.CHAT_CHANGED, eventCallback);
489 }, debounce_timeout.relaxed);489 }, debounce_timeout.relaxed);
490 });490 });
491 },491 },
492 helpString: 'Opens a temporary chat with Assistant.',492 helpString: t`Opens a temporary chat with Assistant.`,
493 }));493 }));
494 SlashCommandParser.addCommandObject(SlashCommand.fromProps({494 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
495 name: 'panels',495 name: 'panels',
@@ -498,17 +498,17 @@ export function initDefaultSlashCommands() {
498 return '';498 return '';
499 },499 },
500 aliases: ['togglepanels'],500 aliases: ['togglepanels'],
501 helpString: 'Toggle UI panels on/off',501 helpString: t`Toggle UI panels on/off`,
502 }));502 }));
503 SlashCommandParser.addCommandObject(SlashCommand.fromProps({503 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
504 name: 'forcesave',504 name: 'forcesave',
505 callback: async function () {505 callback: async function () {
506 await saveSettings();506 await saveSettings();
507 await saveChatConditional();507 await saveChatConditional();
508 toastr.success('Chat and settings saved.');508 toastr.success(t`Chat and settings saved.`);
509 return '';509 return '';
510 },510 },
511 helpString: 'Forces a save of the current chat and settings',511 helpString: t`Forces a save of the current chat and settings`,
512 }));512 }));
513 SlashCommandParser.addCommandObject(SlashCommand.fromProps({513 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
514 name: 'instruct',514 name: 'instruct',
@@ -531,18 +531,18 @@ export function initDefaultSlashCommands() {
531 selectInstructPreset(foundName, { quiet: quiet });531 selectInstructPreset(foundName, { quiet: quiet });
532 return foundName;532 return foundName;
533 },533 },
534 returns: 'current template',534 returns: t`current template`,
535 namedArgumentList: [535 namedArgumentList: [
536 SlashCommandNamedArgument.fromProps({536 SlashCommandNamedArgument.fromProps({
537 name: 'quiet',537 name: 'quiet',
538 description: 'Suppress the toast message on template change',538 description: t`Suppress the toast message on template change`,
539 typeList: [ARGUMENT_TYPE.BOOLEAN],539 typeList: [ARGUMENT_TYPE.BOOLEAN],
540 defaultValue: 'false',540 defaultValue: 'false',
541 enumList: commonEnumProviders.boolean('trueFalse')(),541 enumList: commonEnumProviders.boolean('trueFalse')(),
542 }),542 }),
543 SlashCommandNamedArgument.fromProps({543 SlashCommandNamedArgument.fromProps({
544 name: 'forceGet',544 name: 'forceGet',
545 description: 'Force getting a name even if instruct mode is disabled',545 description: t`Force getting a name even if instruct mode is disabled`,
546 typeList: [ARGUMENT_TYPE.BOOLEAN],546 typeList: [ARGUMENT_TYPE.BOOLEAN],
547 defaultValue: 'false',547 defaultValue: 'false',
548 enumList: commonEnumProviders.boolean('trueFalse')(),548 enumList: commonEnumProviders.boolean('trueFalse')(),
@@ -550,18 +550,18 @@ export function initDefaultSlashCommands() {
550 ],550 ],
551 unnamedArgumentList: [551 unnamedArgumentList: [
552 SlashCommandArgument.fromProps({552 SlashCommandArgument.fromProps({
553 description: 'instruct template name',553 description: t`instruct template name`,
554 typeList: [ARGUMENT_TYPE.STRING],554 typeList: [ARGUMENT_TYPE.STRING],
555 enumProvider: () => instruct_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)),555 enumProvider: () => instruct_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)),
556 }),556 }),
557 ],557 ],
558 helpString: `558 helpString: `
559 <div>559 <div>
560 Selects instruct mode template by name. Enables instruct mode if not already enabled.560 ${t`Selects instruct mode template by name. Enables instruct mode if not already enabled.`}
561 Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed.561 ${t`Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed.`}
562 </div>562 </div>
563 <div>563 <div>
564 <strong>Example:</strong>564 <strong>${t`Example:`}</strong>
565 <ul>565 <ul>
566 <li>566 <li>
567 <pre><code class="language-stscript">/instruct creative</code></pre>567 <pre><code class="language-stscript">/instruct creative</code></pre>
@@ -573,20 +573,20 @@ export function initDefaultSlashCommands() {
573 SlashCommandParser.addCommandObject(SlashCommand.fromProps({573 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
574 name: 'instruct-on',574 name: 'instruct-on',
575 callback: enableInstructCallback,575 callback: enableInstructCallback,
576 helpString: 'Enables instruct mode.',576 helpString: t`Enables instruct mode.`,
577 }));577 }));
578 SlashCommandParser.addCommandObject(SlashCommand.fromProps({578 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
579 name: 'instruct-off',579 name: 'instruct-off',
580 callback: disableInstructCallback,580 callback: disableInstructCallback,
581 helpString: 'Disables instruct mode',581 helpString: t`Disables instruct mode`,
582 }));582 }));
583 SlashCommandParser.addCommandObject(SlashCommand.fromProps({583 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
584 name: 'instruct-state',584 name: 'instruct-state',
585 aliases: ['instruct-toggle'],585 aliases: ['instruct-toggle'],
586 helpString: 'Gets the current instruct mode state. If an argument is provided, it will set the instruct mode state.',586 helpString: t`Gets the current instruct mode state. If an argument is provided, it will set the instruct mode state.`,
587 unnamedArgumentList: [587 unnamedArgumentList: [
588 SlashCommandArgument.fromProps({588 SlashCommandArgument.fromProps({
589 description: 'instruct mode state',589 description: t`instruct mode state`,
590 typeList: [ARGUMENT_TYPE.BOOLEAN],590 typeList: [ARGUMENT_TYPE.BOOLEAN],
591 enumList: commonEnumProviders.boolean('trueFalse')(),591 enumList: commonEnumProviders.boolean('trueFalse')(),
592 }),592 }),
@@ -622,11 +622,11 @@ export function initDefaultSlashCommands() {
622 selectContextPreset(foundName, { quiet: quiet });622 selectContextPreset(foundName, { quiet: quiet });
623 return foundName;623 return foundName;
624 },624 },
625 returns: 'template name',625 returns: t`template name`,
626 namedArgumentList: [626 namedArgumentList: [
627 SlashCommandNamedArgument.fromProps({627 SlashCommandNamedArgument.fromProps({
628 name: 'quiet',628 name: 'quiet',
629 description: 'Suppress the toast message on template change',629 description: t`Suppress the toast message on template change`,
630 typeList: [ARGUMENT_TYPE.BOOLEAN],630 typeList: [ARGUMENT_TYPE.BOOLEAN],
631 defaultValue: 'false',631 defaultValue: 'false',
632 enumList: commonEnumProviders.boolean('trueFalse')(),632 enumList: commonEnumProviders.boolean('trueFalse')(),
@@ -634,12 +634,12 @@ export function initDefaultSlashCommands() {
634 ],634 ],
635 unnamedArgumentList: [635 unnamedArgumentList: [
636 SlashCommandArgument.fromProps({636 SlashCommandArgument.fromProps({
637 description: 'context template name',637 description: t`context template name`,
638 typeList: [ARGUMENT_TYPE.STRING],638 typeList: [ARGUMENT_TYPE.STRING],
639 enumProvider: () => context_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)),639 enumProvider: () => context_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)),
640 }),640 }),
641 ],641 ],
642 helpString: 'Selects context template by name. Gets the current template if no name is provided',642 helpString: t`Selects context template by name. Gets the current template if no name is provided`,
643 }));643 }));
644 SlashCommandParser.addCommandObject(SlashCommand.fromProps({644 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
645 name: 'chat-manager',645 name: 'chat-manager',
@@ -648,32 +648,32 @@ export function initDefaultSlashCommands() {
648 return '';648 return '';
649 },649 },
650 aliases: ['chat-history', 'manage-chats'],650 aliases: ['chat-history', 'manage-chats'],
651 helpString: 'Opens the chat manager for the current character/group.',651 helpString: t`Opens the chat manager for the current character/group.`,
652 }));652 }));
653 SlashCommandParser.addCommandObject(SlashCommand.fromProps({653 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
654 name: '?',654 name: '?',
655 callback: helpCommandCallback,655 callback: helpCommandCallback,
656 aliases: ['help'],656 aliases: ['help'],
657 unnamedArgumentList: [SlashCommandArgument.fromProps({657 unnamedArgumentList: [SlashCommandArgument.fromProps({
658 description: 'help topic',658 description: t`help topic`,
659 typeList: [ARGUMENT_TYPE.STRING],659 typeList: [ARGUMENT_TYPE.STRING],
660 enumList: [660 enumList: [
661 new SlashCommandEnumValue('slash', 'slash commands (STscript)', enumTypes.command, '/'),661 new SlashCommandEnumValue('slash', t`slash commands (STscript)`, enumTypes.command, '/'),
662 new SlashCommandEnumValue('macros', '{{macros}} (text replacement)', enumTypes.macro, enumIcons.macro),662 new SlashCommandEnumValue('macros', t`{{macros}} (text replacement)`, enumTypes.macro, enumIcons.macro),
663 new SlashCommandEnumValue('format', 'chat/text formatting', enumTypes.name, '★'),663 new SlashCommandEnumValue('format', t`chat/text formatting`, enumTypes.name, '★'),
664 new SlashCommandEnumValue('hotkeys', 'keyboard shortcuts', enumTypes.enum, '⏎'),664 new SlashCommandEnumValue('hotkeys', t`keyboard shortcuts`, enumTypes.enum, '⏎'),
665 ],665 ],
666 })],666 })],
667 helpString: 'Get help on macros, chat formatting and commands.',667 helpString: t`Get help on macros, chat formatting and commands.`,
668 }));668 }));
669 SlashCommandParser.addCommandObject(SlashCommand.fromProps({669 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
670 name: 'bg',670 name: 'bg',
671 callback: setBackgroundCallback,671 callback: setBackgroundCallback,
672 aliases: ['background'],672 aliases: ['background'],
673 returns: 'the current background',673 returns: t`the current background`,
674 unnamedArgumentList: [674 unnamedArgumentList: [
675 SlashCommandArgument.fromProps({675 SlashCommandArgument.fromProps({
676 description: 'background filename',676 description: t`background filename`,
677 typeList: [ARGUMENT_TYPE.STRING],677 typeList: [ARGUMENT_TYPE.STRING],
678 enumProvider: () => [...document.querySelectorAll('.bg_example')]678 enumProvider: () => [...document.querySelectorAll('.bg_example')]
679 .map(it => new SlashCommandEnumValue(it.getAttribute('bgfile')))679 .map(it => new SlashCommandEnumValue(it.getAttribute('bgfile')))
@@ -682,13 +682,13 @@ export function initDefaultSlashCommands() {
682 ],682 ],
683 helpString: `683 helpString: `
684 <div>684 <div>
685 Sets a background according to the provided filename. Partial names allowed.685 ${t`Sets a background according to the provided filename. Partial names allowed.`}
686 </div>686 </div>
687 <div>687 <div>
688 If no background is provided, this will return the currently selected background.688 ${t`If no background is provided, this will return the currently selected background.`}
689 </div>689 </div>
690 <div>690 <div>
691 <strong>Example:</strong>691 <strong>${t`Example:`}</strong>
692 <ul>692 <ul>
693 <li>693 <li>
694 <pre><code>/bg beach.jpg</code></pre>694 <pre><code>/bg beach.jpg</code></pre>
@@ -704,31 +704,31 @@ export function initDefaultSlashCommands() {
704 name: 'char-find',704 name: 'char-find',
705 aliases: ['findchar'],705 aliases: ['findchar'],
706 callback: (args, name) => {706 callback: (args, name) => {
707 if (typeof name !== 'string') throw new Error('name must be a string');707 if (typeof name !== 'string') throw new Error(t`name must be a string`);
708 if (args.preferCurrent instanceof SlashCommandClosure || Array.isArray(args.preferCurrent)) throw new Error('preferCurrent cannot be a closure or array');708 if (args.preferCurrent instanceof SlashCommandClosure || Array.isArray(args.preferCurrent)) throw new Error(t`preferCurrent cannot be a closure or array`);
709 if (args.quiet instanceof SlashCommandClosure || Array.isArray(args.quiet)) throw new Error('quiet cannot be a closure or array');709 if (args.quiet instanceof SlashCommandClosure || Array.isArray(args.quiet)) throw new Error(t`quiet cannot be a closure or array`);
710710
711 const char = findChar({ name: name, filteredByTags: validateArrayArgString(args.tag, 'tag'), preferCurrentChar: !isFalseBoolean(args.preferCurrent), quiet: isTrueBoolean(args.quiet) });711 const char = findChar({ name: name, filteredByTags: validateArrayArgString(args.tag, 'tag'), preferCurrentChar: !isFalseBoolean(args.preferCurrent), quiet: isTrueBoolean(args.quiet) });
712 return char?.avatar ?? '';712 return char?.avatar ?? '';
713 },713 },
714 returns: 'the avatar key (unique identifier) of the character',714 returns: t`the avatar key (unique identifier) of the character`,
715 namedArgumentList: [715 namedArgumentList: [
716 SlashCommandNamedArgument.fromProps({716 SlashCommandNamedArgument.fromProps({
717 name: 'tag',717 name: 'tag',
718 description: 'Supply one or more tags to filter down to the correct character for the provided name, if multiple characters have the same name.',718 description: t`Supply one or more tags to filter down to the correct character for the provided name, if multiple characters have the same name.`,
719 typeList: [ARGUMENT_TYPE.STRING],719 typeList: [ARGUMENT_TYPE.STRING],
720 enumProvider: commonEnumProviders.tags('assigned'),720 enumProvider: commonEnumProviders.tags('assigned'),
721 acceptsMultiple: true,721 acceptsMultiple: true,
722 }),722 }),
723 SlashCommandNamedArgument.fromProps({723 SlashCommandNamedArgument.fromProps({
724 name: 'preferCurrent',724 name: 'preferCurrent',
725 description: 'Prefer current character or characters in a group, if multiple characters match',725 description: t`Prefer current character or characters in a group, if multiple characters match`,
726 typeList: [ARGUMENT_TYPE.BOOLEAN],726 typeList: [ARGUMENT_TYPE.BOOLEAN],
727 defaultValue: 'true',727 defaultValue: 'true',
728 }),728 }),
729 SlashCommandNamedArgument.fromProps({729 SlashCommandNamedArgument.fromProps({
730 name: 'quiet',730 name: 'quiet',
731 description: 'Do not show warning if multiple charactrers are found',731 description: t`Do not show warning if multiple charactrers are found`,
732 typeList: [ARGUMENT_TYPE.BOOLEAN],732 typeList: [ARGUMENT_TYPE.BOOLEAN],
733 defaultValue: 'false',733 defaultValue: 'false',
734 enumProvider: commonEnumProviders.boolean('trueFalse'),734 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -736,31 +736,29 @@ export function initDefaultSlashCommands() {
736 ],736 ],
737 unnamedArgumentList: [737 unnamedArgumentList: [
738 SlashCommandArgument.fromProps({738 SlashCommandArgument.fromProps({
739 description: 'Character name - or unique character identifier (avatar key)',739 description: t`Character name - or unique character identifier (avatar key)`,
740 typeList: [ARGUMENT_TYPE.STRING],740 typeList: [ARGUMENT_TYPE.STRING],
741 enumProvider: commonEnumProviders.characters('character'),741 enumProvider: commonEnumProviders.characters('character'),
742 }),742 }),
743 ],743 ],
744 helpString: `744 helpString: `
745 <div>745 <div>
746 Searches for a character and returns its avatar key.746 ${t`Searches for a character and returns its avatar key.`}
747 </div>747 </div>
748 <div>748 <div>
749 This can be used to choose the correct character for something like <code>/sendas</code> or other commands in need of a character name749 ${t`This can be used to choose the correct character for something like <code>/sendas</code> or other commands in need of a character name if you have multiple characters with the same name.`}
750 if you have multiple characters with the same name.
751 </div>750 </div>
752 <div>751 <div>
753 <strong>Example:</strong>752 <strong>${t`Example:`}</strong>
754 <ul>753 <ul>
755 <li>754 <li>
756 <pre><code>/char-find name="Chloe"</code></pre>755 <pre><code>/char-find name="Chloe"</code></pre>
757 Returns the avatar key for "Chloe".756 ${t`Returns the avatar key for "Chloe".`}
758 </li>757 </li>
759 <li>758 <li>
760 <pre><code>/search name="Chloe" tag="friend"</code></pre>759 <pre><code>/search name="Chloe" tag="friend"</code></pre>
761 Returns the avatar key for the character "Chloe" that is tagged with "friend".760 ${t`Returns the avatar key for the character "Chloe" that is tagged with "friend".`}
762 This is useful if you for example have multiple characters named "Chloe", and the others are "foe", "goddess", or anything else,761 ${t`This is useful if you for example have multiple characters named "Chloe", and the others are "foe", "goddess", or anything else, so you can actually select the character you are looking for.`}
763 so you can actually select the character you are looking for.
764 </li>762 </li>
765 </ul>763 </ul>
766 </div>764 </div>
@@ -770,36 +768,36 @@ export function initDefaultSlashCommands() {
770 name: 'sendas',768 name: 'sendas',
771 rawQuotes: true,769 rawQuotes: true,
772 callback: sendMessageAs,770 callback: sendMessageAs,
773 returns: 'Optionally the text of the sent message, if specified in the "return" argument',771 returns: t`Optionally the text of the sent message, if specified in the "return" argument`,
774 namedArgumentList: [772 namedArgumentList: [
775 SlashCommandNamedArgument.fromProps({773 SlashCommandNamedArgument.fromProps({
776 name: 'name',774 name: 'name',
777 description: 'Character name - or unique character identifier (avatar key)',775 description: t`Character name - or unique character identifier (avatar key)`,
778 typeList: [ARGUMENT_TYPE.STRING],776 typeList: [ARGUMENT_TYPE.STRING],
779 isRequired: true,777 isRequired: true,
780 enumProvider: commonEnumProviders.characters('character'),778 enumProvider: commonEnumProviders.characters('character'),
781 }),779 }),
782 SlashCommandNamedArgument.fromProps({780 SlashCommandNamedArgument.fromProps({
783 name: 'avatar',781 name: 'avatar',
784 description: 'Character avatar override (Can be either avatar key or just the character name to pull the avatar from)',782 description: t`Character avatar override (Can be either avatar key or just the character name to pull the avatar from)`,
785 typeList: [ARGUMENT_TYPE.STRING],783 typeList: [ARGUMENT_TYPE.STRING],
786 enumProvider: commonEnumProviders.characters('character'),784 enumProvider: commonEnumProviders.characters('character'),
787 }),785 }),
788 SlashCommandNamedArgument.fromProps({786 SlashCommandNamedArgument.fromProps({
789 name: 'compact',787 name: 'compact',
790 description: 'Use compact layout',788 description: t`Use compact layout`,
791 typeList: [ARGUMENT_TYPE.BOOLEAN],789 typeList: [ARGUMENT_TYPE.BOOLEAN],
792 defaultValue: 'false',790 defaultValue: 'false',
793 }),791 }),
794 SlashCommandNamedArgument.fromProps({792 SlashCommandNamedArgument.fromProps({
795 name: 'at',793 name: 'at',
796 description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',794 description: t`position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how 'depth' usually works. For example, -1 will insert the message right before the last message in chat.`,
797 typeList: [ARGUMENT_TYPE.NUMBER],795 typeList: [ARGUMENT_TYPE.NUMBER],
798 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),796 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
799 }),797 }),
800 SlashCommandNamedArgument.fromProps({798 SlashCommandNamedArgument.fromProps({
801 name: 'return',799 name: 'return',
802 description: 'The way how you want the return value to be provided',800 description: t`The way how you want the return value to be provided`,
803 typeList: [ARGUMENT_TYPE.STRING],801 typeList: [ARGUMENT_TYPE.STRING],
804 defaultValue: 'none',802 defaultValue: 'none',
805 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),803 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -807,7 +805,7 @@ export function initDefaultSlashCommands() {
807 }),805 }),
808 SlashCommandNamedArgument.fromProps({806 SlashCommandNamedArgument.fromProps({
809 name: 'raw',807 name: 'raw',
810 description: 'If true, does not alter quoted literal unnamed arguments',808 description: t`If true, does not alter quoted literal unnamed arguments`,
811 typeList: [ARGUMENT_TYPE.BOOLEAN],809 typeList: [ARGUMENT_TYPE.BOOLEAN],
812 defaultValue: 'true',810 defaultValue: 'true',
813 enumProvider: commonEnumProviders.boolean('trueFalse'),811 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -821,23 +819,23 @@ export function initDefaultSlashCommands() {
821 ],819 ],
822 helpString: `820 helpString: `
823 <div>821 <div>
824 Sends a message as a specific character. Uses the character avatar if it exists in the characters list.822 ${t`Sends a message as a specific character. Uses the character avatar if it exists in the characters list.`}
825 </div>823 </div>
826 <div>824 <div>
827 <strong>Example:</strong>825 <strong>${t`Example:`}</strong>
828 <ul>826 <ul>
829 <li>827 <li>
830 <pre><code>/sendas name="Chloe" Hello, guys!</code></pre>828 <pre><code>/sendas name="Chloe" Hello, guys!</code></pre>
831 will send "Hello, guys!" from "Chloe".829 ${t`will send "Hello, guys!" from "Chloe".`}
832 </li>830 </li>
833 <li>831 <li>
834 <pre><code>/sendas name="Chloe" avatar="BigBadBoss" Hehehe, I am the big bad evil, fear me.</code></pre>832 <pre><code>/sendas name="Chloe" avatar="BigBadBoss" Hehehe, I am the big bad evil, fear me.</code></pre>
835 will send a message as the character "Chloe", but utilizing the avatar from a character named "BigBadBoss".833 ${t`will send a message as the character "Chloe", but utilizing the avatar from a character named "BigBadBoss".`}
836 </li>834 </li>
837 </ul>835 </ul>
838 </div>836 </div>
839 <div>837 <div>
840 If "compact" is set to true, the message is sent using a compact layout.838 ${t`If "compact" is set to true, the message is sent using a compact layout.`}
841 </div>839 </div>
842 `,840 `,
843 }));841 }));
@@ -846,11 +844,11 @@ export function initDefaultSlashCommands() {
846 rawQuotes: true,844 rawQuotes: true,
847 callback: sendNarratorMessage,845 callback: sendNarratorMessage,
848 aliases: ['nar'],846 aliases: ['nar'],
849 returns: 'Optionally the text of the sent message, if specified in the "return" argument',847 returns: t`Optionally the text of the sent message, if specified in the "return" argument`,
850 namedArgumentList: [848 namedArgumentList: [
851 new SlashCommandNamedArgument(849 new SlashCommandNamedArgument(
852 'compact',850 'compact',
853 'compact layout',851 t`compact layout`,
854 [ARGUMENT_TYPE.BOOLEAN],852 [ARGUMENT_TYPE.BOOLEAN],
855 false,853 false,
856 false,854 false,
@@ -858,18 +856,18 @@ export function initDefaultSlashCommands() {
858 ),856 ),
859 SlashCommandNamedArgument.fromProps({857 SlashCommandNamedArgument.fromProps({
860 name: 'at',858 name: 'at',
861 description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',859 description: t`position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how 'depth' usually works. For example, -1 will insert the message right before the last message in chat.`,
862 typeList: [ARGUMENT_TYPE.NUMBER],860 typeList: [ARGUMENT_TYPE.NUMBER],
863 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),861 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
864 }),862 }),
865 SlashCommandNamedArgument.fromProps({863 SlashCommandNamedArgument.fromProps({
866 name: 'name',864 name: 'name',
867 description: 'Optional custom display name to use for this system narrator message.',865 description: t`Optional custom display name to use for this system narrator message.`,
868 typeList: [ARGUMENT_TYPE.STRING],866 typeList: [ARGUMENT_TYPE.STRING],
869 }),867 }),
870 SlashCommandNamedArgument.fromProps({868 SlashCommandNamedArgument.fromProps({
871 name: 'return',869 name: 'return',
872 description: 'The way how you want the return value to be provided',870 description: t`The way how you want the return value to be provided`,
873 typeList: [ARGUMENT_TYPE.STRING],871 typeList: [ARGUMENT_TYPE.STRING],
874 defaultValue: 'none',872 defaultValue: 'none',
875 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),873 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -877,7 +875,7 @@ export function initDefaultSlashCommands() {
877 }),875 }),
878 SlashCommandNamedArgument.fromProps({876 SlashCommandNamedArgument.fromProps({
879 name: 'raw',877 name: 'raw',
880 description: 'If true, does not alter quoted literal unnamed arguments',878 description: t`If true, does not alter quoted literal unnamed arguments`,
881 typeList: [ARGUMENT_TYPE.BOOLEAN],879 typeList: [ARGUMENT_TYPE.BOOLEAN],
882 defaultValue: 'true',880 defaultValue: 'true',
883 enumProvider: commonEnumProviders.boolean('trueFalse'),881 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -891,13 +889,13 @@ export function initDefaultSlashCommands() {
891 ],889 ],
892 helpString: `890 helpString: `
893 <div>891 <div>
894 Sends a message as a system narrator.892 ${t`Sends a message as a system narrator.`}
895 </div>893 </div>
896 <div>894 <div>
897 If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.895 ${t`If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.`}
898 </div>896 </div>
899 <div>897 <div>
900 <strong>Example:</strong>898 <strong>${t`Example:`}</strong>
901 <ul>899 <ul>
902 <li>900 <li>
903 <pre><code>/sys The sun sets in the west.</code></pre>901 <pre><code>/sys The sun sets in the west.</code></pre>
@@ -914,20 +912,20 @@ export function initDefaultSlashCommands() {
914 callback: setNarratorName,912 callback: setNarratorName,
915 unnamedArgumentList: [913 unnamedArgumentList: [
916 new SlashCommandArgument(914 new SlashCommandArgument(
917 'name', [ARGUMENT_TYPE.STRING], false,915 t`name`, [ARGUMENT_TYPE.STRING], false,
918 ),916 ),
919 ],917 ],
920 helpString: 'Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.',918 helpString: t`Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.`,
921 }));919 }));
922 SlashCommandParser.addCommandObject(SlashCommand.fromProps({920 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
923 name: 'comment',921 name: 'comment',
924 rawQuotes: true,922 rawQuotes: true,
925 callback: sendCommentMessage,923 callback: sendCommentMessage,
926 returns: 'Optionally the text of the sent message, if specified in the "return" argument',924 returns: t`Optionally the text of the sent message, if specified in the "return" argument`,
927 namedArgumentList: [925 namedArgumentList: [
928 new SlashCommandNamedArgument(926 new SlashCommandNamedArgument(
929 'compact',927 'compact',
930 'Whether to use a compact layout',928 t`Whether to use a compact layout`,
931 [ARGUMENT_TYPE.BOOLEAN],929 [ARGUMENT_TYPE.BOOLEAN],
932 false,930 false,
933 false,931 false,
@@ -935,13 +933,13 @@ export function initDefaultSlashCommands() {
935 ),933 ),
936 SlashCommandNamedArgument.fromProps({934 SlashCommandNamedArgument.fromProps({
937 name: 'at',935 name: 'at',
938 description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',936 description: t`position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how 'depth' usually works. For example, -1 will insert the message right before the last message in chat.`,
939 typeList: [ARGUMENT_TYPE.NUMBER],937 typeList: [ARGUMENT_TYPE.NUMBER],
940 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),938 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
941 }),939 }),
942 SlashCommandNamedArgument.fromProps({940 SlashCommandNamedArgument.fromProps({
943 name: 'return',941 name: 'return',
944 description: 'The way how you want the return value to be provided',942 description: t`The way how you want the return value to be provided`,
945 typeList: [ARGUMENT_TYPE.STRING],943 typeList: [ARGUMENT_TYPE.STRING],
946 defaultValue: 'none',944 defaultValue: 'none',
947 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),945 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -949,7 +947,7 @@ export function initDefaultSlashCommands() {
949 }),947 }),
950 SlashCommandNamedArgument.fromProps({948 SlashCommandNamedArgument.fromProps({
951 name: 'raw',949 name: 'raw',
952 description: 'If true, does not alter quoted literal unnamed arguments',950 description: t`If true, does not alter quoted literal unnamed arguments`,
953 typeList: [ARGUMENT_TYPE.BOOLEAN],951 typeList: [ARGUMENT_TYPE.BOOLEAN],
954 defaultValue: 'true',952 defaultValue: 'true',
955 enumProvider: commonEnumProviders.boolean('trueFalse'),953 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -965,13 +963,13 @@ export function initDefaultSlashCommands() {
965 ],963 ],
966 helpString: `964 helpString: `
967 <div>965 <div>
968 Adds a note/comment message not part of the chat.966 ${t`Adds a note/comment message not part of the chat.`}
969 </div>967 </div>
970 <div>968 <div>
971 If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.969 ${t`If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.`}
972 </div>970 </div>
973 <div>971 <div>
974 <strong>Example:</strong>972 <strong>${t`Example:`}</strong>
975 <ul>973 <ul>
976 <li>974 <li>
977 <pre><code>/comment This is a comment</code></pre>975 <pre><code>/comment This is a comment</code></pre>
@@ -987,19 +985,19 @@ export function initDefaultSlashCommands() {
987 name: 'single',985 name: 'single',
988 callback: setStoryModeCallback,986 callback: setStoryModeCallback,
989 aliases: ['story'],987 aliases: ['story'],
990 helpString: 'Sets the message style to single document mode without names or avatars visible.',988 helpString: t`Sets the message style to single document mode without names or avatars visible.`,
991 }));989 }));
992 SlashCommandParser.addCommandObject(SlashCommand.fromProps({990 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
993 name: 'bubble',991 name: 'bubble',
994 callback: setBubbleModeCallback,992 callback: setBubbleModeCallback,
995 aliases: ['bubbles'],993 aliases: ['bubbles'],
996 helpString: 'Sets the message style to bubble chat mode.',994 helpString: t`Sets the message style to bubble chat mode.`,
997 }));995 }));
998 SlashCommandParser.addCommandObject(SlashCommand.fromProps({996 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
999 name: 'flat',997 name: 'flat',
1000 callback: setFlatModeCallback,998 callback: setFlatModeCallback,
1001 aliases: ['default'],999 aliases: ['default'],
1002 helpString: 'Sets the message style to flat chat mode.',1000 helpString: t`Sets the message style to flat chat mode.`,
1003 }));1001 }));
1004 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1002 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1005 name: 'continue',1003 name: 'continue',
@@ -1008,7 +1006,7 @@ export function initDefaultSlashCommands() {
1008 namedArgumentList: [1006 namedArgumentList: [
1009 new SlashCommandNamedArgument(1007 new SlashCommandNamedArgument(
1010 'await',1008 'await',
1011 'Whether to await for the continued generation before proceeding',1009 t`Whether to await for the continued generation before proceeding`,
1012 [ARGUMENT_TYPE.BOOLEAN],1010 [ARGUMENT_TYPE.BOOLEAN],
1013 false,1011 false,
1014 false,1012 false,
@@ -1022,21 +1020,21 @@ export function initDefaultSlashCommands() {
1022 ],1020 ],
1023 helpString: `1021 helpString: `
1024 <div>1022 <div>
1025 Continues the last message in the chat, with an optional additional prompt.1023 ${t`Continues the last message in the chat, with an optional additional prompt.`}
1026 </div>1024 </div>
1027 <div>1025 <div>
1028 If <code>await=true</code> named argument is passed, the command will await for the continued generation before proceeding.1026 ${t`If <code>await=true</code> named argument is passed, the command will await for the continued generation before proceeding.`}
1029 </div>1027 </div>
1030 <div>1028 <div>
1031 <strong>Example:</strong>1029 <strong>${t`Example:`}</strong>
1032 <ul>1030 <ul>
1033 <li>1031 <li>
1034 <pre><code>/continue</code></pre>1032 <pre><code>/continue</code></pre>
1035 Continues the chat with no additional prompt and immediately proceeds to the next command.1033 ${t`Continues the chat with no additional prompt and immediately proceeds to the next command.`}
1036 </li>1034 </li>
1037 <li>1035 <li>
1038 <pre><code>/continue await=true Let's explore this further...</code></pre>1036 <pre><code>/continue await=true Let's explore this further...</code></pre>
1039 Continues the chat with the provided prompt and waits for the generation to finish.1037 ${t`Continues the chat with the provided prompt and waits for the generation to finish.`}
1040 </li>1038 </li>
1041 </ul>1039 </ul>
1042 </div>1040 </div>
@@ -1045,16 +1043,16 @@ export function initDefaultSlashCommands() {
1045 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1043 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1046 name: 'go',1044 name: 'go',
1047 callback: goToCharacterCallback,1045 callback: goToCharacterCallback,
1048 returns: 'The character/group name',1046 returns: t`The character/group name`,
1049 unnamedArgumentList: [1047 unnamedArgumentList: [
1050 SlashCommandArgument.fromProps({1048 SlashCommandArgument.fromProps({
1051 description: 'Character name - or unique character identifier (avatar key)',1049 description: t`Character name - or unique character identifier (avatar key)`,
1052 typeList: [ARGUMENT_TYPE.STRING],1050 typeList: [ARGUMENT_TYPE.STRING],
1053 isRequired: true,1051 isRequired: true,
1054 enumProvider: commonEnumProviders.characters('all'),1052 enumProvider: commonEnumProviders.characters('all'),
1055 }),1053 }),
1056 ],1054 ],
1057 helpString: 'Opens up a chat with the character or group by its name',1055 helpString: t`Opens up a chat with the character or group by its name`,
1058 aliases: ['char'],1056 aliases: ['char'],
1059 }));1057 }));
1060 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1058 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
@@ -1064,21 +1062,21 @@ export function initDefaultSlashCommands() {
1064 const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: chats !== null ? isTrueBoolean(chats) : null });1062 const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: chats !== null ? isTrueBoolean(chats) : null });
1065 return String(renamed);1063 return String(renamed);
1066 },1064 },
1067 returns: 'true/false - Whether the rename was successful',1065 returns: t`true/false - Whether the rename was successful`,
1068 namedArgumentList: [1066 namedArgumentList: [
1069 new SlashCommandNamedArgument(1067 new SlashCommandNamedArgument(
1070 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true',1068 'silent', t`Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'true',
1071 ),1069 ),
1072 new SlashCommandNamedArgument(1070 new SlashCommandNamedArgument(
1073 'chats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '<null>',1071 'chats', t`Rename char in all previous chats`, [ARGUMENT_TYPE.BOOLEAN], false, false, '<null>',
1074 ),1072 ),
1075 ],1073 ],
1076 unnamedArgumentList: [1074 unnamedArgumentList: [
1077 new SlashCommandArgument(1075 new SlashCommandArgument(
1078 'new char name', [ARGUMENT_TYPE.STRING], true,1076 t`new char name`, [ARGUMENT_TYPE.STRING], true,
1079 ),1077 ),
1080 ],1078 ],
1081 helpString: 'Renames the current character.',1079 helpString: t`Renames the current character.`,
1082 }));1080 }));
1083 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1081 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1084 name: 'sysgen',1082 name: 'sysgen',
@@ -1086,7 +1084,7 @@ export function initDefaultSlashCommands() {
1086 namedArgumentList: [1084 namedArgumentList: [
1087 SlashCommandNamedArgument.fromProps({1085 SlashCommandNamedArgument.fromProps({
1088 name: 'trim',1086 name: 'trim',
1089 description: 'Trim the output by the last sentence boundary',1087 description: t`Trim the output by the last sentence boundary`,
1090 typeList: [ARGUMENT_TYPE.BOOLEAN],1088 typeList: [ARGUMENT_TYPE.BOOLEAN],
1091 defaultValue: 'false',1089 defaultValue: 'false',
1092 isRequired: false,1090 isRequired: false,
@@ -1094,7 +1092,7 @@ export function initDefaultSlashCommands() {
1094 }),1092 }),
1095 SlashCommandNamedArgument.fromProps({1093 SlashCommandNamedArgument.fromProps({
1096 name: 'compact',1094 name: 'compact',
1097 description: 'Use a compact layout for the message',1095 description: t`Use a compact layout for the message`,
1098 typeList: [ARGUMENT_TYPE.BOOLEAN],1096 typeList: [ARGUMENT_TYPE.BOOLEAN],
1099 defaultValue: 'false',1097 defaultValue: 'false',
1100 isRequired: false,1098 isRequired: false,
@@ -1103,18 +1101,18 @@ export function initDefaultSlashCommands() {
1103 }),1101 }),
1104 SlashCommandNamedArgument.fromProps({1102 SlashCommandNamedArgument.fromProps({
1105 name: 'at',1103 name: 'at',
1106 description: 'Position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',1104 description: t`Position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how 'depth' usually works. For example, -1 will insert the message right before the last message in chat.`,
1107 typeList: [ARGUMENT_TYPE.NUMBER],1105 typeList: [ARGUMENT_TYPE.NUMBER],
1108 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),1106 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
1109 }),1107 }),
1110 SlashCommandNamedArgument.fromProps({1108 SlashCommandNamedArgument.fromProps({
1111 name: 'name',1109 name: 'name',
1112 description: 'Optional custom display name to use for this system narrator message.',1110 description: t`Optional custom display name to use for this system narrator message.`,
1113 typeList: [ARGUMENT_TYPE.STRING],1111 typeList: [ARGUMENT_TYPE.STRING],
1114 }),1112 }),
1115 SlashCommandNamedArgument.fromProps({1113 SlashCommandNamedArgument.fromProps({
1116 name: 'return',1114 name: 'return',
1117 description: 'The way how you want the return value to be provided',1115 description: t`The way how you want the return value to be provided`,
1118 typeList: [ARGUMENT_TYPE.STRING],1116 typeList: [ARGUMENT_TYPE.STRING],
1119 defaultValue: 'none',1117 defaultValue: 'none',
1120 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),1118 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -1126,23 +1124,23 @@ export function initDefaultSlashCommands() {
1126 'prompt', [ARGUMENT_TYPE.STRING], true,1124 'prompt', [ARGUMENT_TYPE.STRING], true,
1127 ),1125 ),
1128 ],1126 ],
1129 helpString: 'Generates a system message using a specified prompt.',1127 helpString: t`Generates a system message using a specified prompt.`,
1130 }));1128 }));
1131 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1129 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1132 name: 'ask',1130 name: 'ask',
1133 callback: askCharacter,1131 callback: askCharacter,
1134 returns: 'Optionally the text of the sent message, if specified in the "return" argument',1132 returns: t`Optionally the text of the sent message, if specified in the "return" argument`,
1135 namedArgumentList: [1133 namedArgumentList: [
1136 SlashCommandNamedArgument.fromProps({1134 SlashCommandNamedArgument.fromProps({
1137 name: 'name',1135 name: 'name',
1138 description: 'Character name - or unique character identifier (avatar key)',1136 description: t`Character name - or unique character identifier (avatar key)`,
1139 typeList: [ARGUMENT_TYPE.STRING],1137 typeList: [ARGUMENT_TYPE.STRING],
1140 isRequired: true,1138 isRequired: true,
1141 enumProvider: commonEnumProviders.characters('character'),1139 enumProvider: commonEnumProviders.characters('character'),
1142 }),1140 }),
1143 SlashCommandNamedArgument.fromProps({1141 SlashCommandNamedArgument.fromProps({
1144 name: 'return',1142 name: 'return',
1145 description: 'The way how you want the return value to be provided',1143 description: t`The way how you want the return value to be provided`,
1146 typeList: [ARGUMENT_TYPE.STRING],1144 typeList: [ARGUMENT_TYPE.STRING],
1147 defaultValue: 'pipe',1145 defaultValue: 'pipe',
1148 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),1146 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -1154,7 +1152,7 @@ export function initDefaultSlashCommands() {
1154 'prompt', [ARGUMENT_TYPE.STRING], false, false,1152 'prompt', [ARGUMENT_TYPE.STRING], false, false,
1155 ),1153 ),
1156 ],1154 ],
1157 helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.',1155 helpString: t`Asks a specified character card a prompt. Character name must be provided in a named argument.`,
1158 }));1156 }));
1159 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1157 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1160 name: 'delname',1158 name: 'delname',
@@ -1162,7 +1160,7 @@ export function initDefaultSlashCommands() {
1162 namedArgumentList: [],1160 namedArgumentList: [],
1163 unnamedArgumentList: [1161 unnamedArgumentList: [
1164 SlashCommandArgument.fromProps({1162 SlashCommandArgument.fromProps({
1165 description: 'Character name - or unique character identifier (avatar key)',1163 description: t`Character name - or unique character identifier (avatar key)`,
1166 typeList: [ARGUMENT_TYPE.STRING],1164 typeList: [ARGUMENT_TYPE.STRING],
1167 isRequired: true,1165 isRequired: true,
1168 enumProvider: commonEnumProviders.characters('character'),1166 enumProvider: commonEnumProviders.characters('character'),
@@ -1171,10 +1169,10 @@ export function initDefaultSlashCommands() {
1171 aliases: ['cancel'],1169 aliases: ['cancel'],
1172 helpString: `1170 helpString: `
1173 <div>1171 <div>
1174 Deletes all messages attributed to a specified name.1172 ${t`Deletes all messages attributed to a specified name.`}
1175 </div>1173 </div>
1176 <div>1174 <div>
1177 <strong>Example:</strong>1175 <strong>${t`Example:`}</strong>
1178 <ul>1176 <ul>
1179 <li>1177 <li>
1180 <pre><code>/delname John</code></pre>1178 <pre><code>/delname John</code></pre>
@@ -1187,11 +1185,11 @@ export function initDefaultSlashCommands() {
1187 name: 'send',1185 name: 'send',
1188 rawQuotes: true,1186 rawQuotes: true,
1189 callback: sendUserMessageCallback,1187 callback: sendUserMessageCallback,
1190 returns: 'Optionally the text of the sent message, if specified in the "return" argument',1188 returns: t`Optionally the text of the sent message, if specified in the "return" argument`,
1191 namedArgumentList: [1189 namedArgumentList: [
1192 new SlashCommandNamedArgument(1190 new SlashCommandNamedArgument(
1193 'compact',1191 'compact',
1194 'whether to use a compact layout',1192 t`whether to use a compact layout`,
1195 [ARGUMENT_TYPE.BOOLEAN],1193 [ARGUMENT_TYPE.BOOLEAN],
1196 false,1194 false,
1197 false,1195 false,
@@ -1199,20 +1197,20 @@ export function initDefaultSlashCommands() {
1199 ),1197 ),
1200 SlashCommandNamedArgument.fromProps({1198 SlashCommandNamedArgument.fromProps({
1201 name: 'at',1199 name: 'at',
1202 description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',1200 description: t`position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how 'depth' usually works. For example, -1 will insert the message right before the last message in chat.`,
1203 typeList: [ARGUMENT_TYPE.NUMBER],1201 typeList: [ARGUMENT_TYPE.NUMBER],
1204 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),1202 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
1205 }),1203 }),
1206 SlashCommandNamedArgument.fromProps({1204 SlashCommandNamedArgument.fromProps({
1207 name: 'name',1205 name: 'name',
1208 description: 'display name',1206 description: t`display name`,
1209 typeList: [ARGUMENT_TYPE.STRING],1207 typeList: [ARGUMENT_TYPE.STRING],
1210 defaultValue: '{{user}}',1208 defaultValue: '{{user}}',
1211 enumProvider: commonEnumProviders.personas,1209 enumProvider: commonEnumProviders.personas,
1212 }),1210 }),
1213 SlashCommandNamedArgument.fromProps({1211 SlashCommandNamedArgument.fromProps({
1214 name: 'return',1212 name: 'return',
1215 description: 'The way how you want the return value to be provided',1213 description: t`The way how you want the return value to be provided`,
1216 typeList: [ARGUMENT_TYPE.STRING],1214 typeList: [ARGUMENT_TYPE.STRING],
1217 defaultValue: 'none',1215 defaultValue: 'none',
1218 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),1216 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
@@ -1220,7 +1218,7 @@ export function initDefaultSlashCommands() {
1220 }),1218 }),
1221 SlashCommandNamedArgument.fromProps({1219 SlashCommandNamedArgument.fromProps({
1222 name: 'raw',1220 name: 'raw',
1223 description: 'If true, does not alter quoted literal unnamed arguments',1221 description: t`If true, does not alter quoted literal unnamed arguments`,
1224 typeList: [ARGUMENT_TYPE.BOOLEAN],1222 typeList: [ARGUMENT_TYPE.BOOLEAN],
1225 defaultValue: 'true',1223 defaultValue: 'true',
1226 enumProvider: commonEnumProviders.boolean('trueFalse'),1224 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -1236,16 +1234,16 @@ export function initDefaultSlashCommands() {
1236 ],1234 ],
1237 helpString: `1235 helpString: `
1238 <div>1236 <div>
1239 Adds a user message to the chat log without triggering a generation.1237 ${t`Adds a user message to the chat log without triggering a generation.`}
1240 </div>1238 </div>
1241 <div>1239 <div>
1242 If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.1240 ${t`If <code>compact</code> is set to <code>true</code>, the message is sent using a compact layout.`}
1243 </div>1241 </div>
1244 <div>1242 <div>
1245 If <code>name</code> is set, it will be displayed as the message sender. Can be an empty for no name.1243 ${t`If <code>name</code> is set, it will be displayed as the message sender. Can be an empty for no name.`}
1246 </div>1244 </div>
1247 <div>1245 <div>
1248 <strong>Example:</strong>1246 <strong>${t`Example:`}</strong>
1249 <ul>1247 <ul>
1250 <li>1248 <li>
1251 <pre><code>/send Hello there!</code></pre>1249 <pre><code>/send Hello there!</code></pre>
@@ -1263,7 +1261,7 @@ export function initDefaultSlashCommands() {
1263 namedArgumentList: [1261 namedArgumentList: [
1264 new SlashCommandNamedArgument(1262 new SlashCommandNamedArgument(
1265 'await',1263 'await',
1266 'Whether to await for the triggered generation before continuing',1264 t`Whether to await for the triggered generation before continuing`,
1267 [ARGUMENT_TYPE.BOOLEAN],1265 [ARGUMENT_TYPE.BOOLEAN],
1268 false,1266 false,
1269 false,1267 false,
@@ -1272,7 +1270,7 @@ export function initDefaultSlashCommands() {
1272 ],1270 ],
1273 unnamedArgumentList: [1271 unnamedArgumentList: [
1274 SlashCommandArgument.fromProps({1272 SlashCommandArgument.fromProps({
1275 description: 'group member index (starts with 0) or name',1273 description: t`group member index (starts with 0) or name`,
1276 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1274 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1277 isRequired: false,1275 isRequired: false,
1278 enumProvider: commonEnumProviders.groupMembers(),1276 enumProvider: commonEnumProviders.groupMembers(),
@@ -1280,10 +1278,10 @@ export function initDefaultSlashCommands() {
1280 ],1278 ],
1281 helpString: `1279 helpString: `
1282 <div>1280 <div>
1283 Triggers a message generation. If in group, can trigger a message for the specified group member index or name.1281 ${t`Triggers a message generation. If in group, can trigger a message for the specified group member index or name.`}
1284 </div>1282 </div>
1285 <div>1283 <div>
1286 If <code>await=true</code> named argument is passed, the command will await for the triggered generation before continuing.1284 ${t`If <code>await=true</code> named argument is passed, the command will await for the triggered generation before continuing.`}
1287 </div>1285 </div>
1288 `,1286 `,
1289 }));1287 }));
@@ -1293,7 +1291,7 @@ export function initDefaultSlashCommands() {
1293 namedArgumentList: [1291 namedArgumentList: [
1294 SlashCommandNamedArgument.fromProps({1292 SlashCommandNamedArgument.fromProps({
1295 name: 'name',1293 name: 'name',
1296 description: 'only hide messages from a certain character or persona',1294 description: t`only hide messages from a certain character or persona`,
1297 typeList: [ARGUMENT_TYPE.STRING],1295 typeList: [ARGUMENT_TYPE.STRING],
1298 enumProvider: commonEnumProviders.messageNames,1296 enumProvider: commonEnumProviders.messageNames,
1299 isRequired: false,1297 isRequired: false,
@@ -1302,13 +1300,13 @@ export function initDefaultSlashCommands() {
1302 ],1300 ],
1303 unnamedArgumentList: [1301 unnamedArgumentList: [
1304 SlashCommandArgument.fromProps({1302 SlashCommandArgument.fromProps({
1305 description: 'message index (starts with 0) or range, defaults to the last message index if not provided',1303 description: t`message index (starts with 0) or range, defaults to the last message index if not provided`,
1306 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],1304 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],
1307 isRequired: false,1305 isRequired: false,
1308 enumProvider: commonEnumProviders.messages(),1306 enumProvider: commonEnumProviders.messages(),
1309 }),1307 }),
1310 ],1308 ],
1311 helpString: 'Hides a chat message from the prompt.',1309 helpString: t`Hides a chat message from the prompt.`,
1312 }));1310 }));
1313 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1311 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1314 name: 'unhide',1312 name: 'unhide',
@@ -1316,7 +1314,7 @@ export function initDefaultSlashCommands() {
1316 namedArgumentList: [1314 namedArgumentList: [
1317 SlashCommandNamedArgument.fromProps({1315 SlashCommandNamedArgument.fromProps({
1318 name: 'name',1316 name: 'name',
1319 description: 'only unhide messages from a certain character or persona',1317 description: t`only unhide messages from a certain character or persona`,
1320 typeList: [ARGUMENT_TYPE.STRING],1318 typeList: [ARGUMENT_TYPE.STRING],
1321 enumProvider: commonEnumProviders.messageNames,1319 enumProvider: commonEnumProviders.messageNames,
1322 isRequired: false,1320 isRequired: false,
@@ -1325,36 +1323,36 @@ export function initDefaultSlashCommands() {
1325 ],1323 ],
1326 unnamedArgumentList: [1324 unnamedArgumentList: [
1327 SlashCommandArgument.fromProps({1325 SlashCommandArgument.fromProps({
1328 description: 'message index (starts with 0) or range, defaults to the last message index if not provided',1326 description: t`message index (starts with 0) or range, defaults to the last message index if not provided`,
1329 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],1327 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],
1330 isRequired: false,1328 isRequired: false,
1331 enumProvider: commonEnumProviders.messages(),1329 enumProvider: commonEnumProviders.messages(),
1332 }),1330 }),
1333 ],1331 ],
1334 helpString: 'Unhides a message from the prompt.',1332 helpString: t`Unhides a message from the prompt.`,
1335 }));1333 }));
1336 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1334 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1337 name: 'member-get',1335 name: 'member-get',
1338 aliases: ['getmember', 'memberget'],1336 aliases: ['getmember', 'memberget'],
1339 callback: (async ({ field = 'name' }, arg) => {1337 callback: (async ({ field = 'name' }, arg) => {
1340 if (!selected_group) {1338 if (!selected_group) {
1341 toastr.warning('Cannot run /member-get command outside of a group chat.');1339 toastr.warning(t`Cannot run /member-get command outside of a group chat.`);
1342 return '';1340 return '';
1343 }1341 }
1344 if (field === '') {1342 if (field === '') {
1345 toastr.warning('\'/member-get field=\' argument required!');1343 toastr.warning(t`'/member-get field=' argument required!`);
1346 return '';1344 return '';
1347 }1345 }
1348 field = field.toString();1346 field = field.toString();
1349 arg = arg.toString();1347 arg = arg.toString();
1350 if (!['name', 'index', 'id', 'avatar'].includes(field)) {1348 if (!['name', 'index', 'id', 'avatar'].includes(field)) {
1351 toastr.warning('\'/member-get field=\' argument required!');1349 toastr.warning(t`'/member-get field=' argument required!`);
1352 return '';1350 return '';
1353 }1351 }
1354 const isId = !isNaN(parseInt(arg));1352 const isId = !isNaN(parseInt(arg));
1355 const groupMember = findGroupMemberId(arg, true);1353 const groupMember = findGroupMemberId(arg, true);
1356 if (!groupMember) {1354 if (!groupMember) {
1357 toastr.warning(`No group member found using ${isId ? 'id' : 'string'} ${arg}`);1355 toastr.warning(t`No group member found using ${isId ? 'id' : 'string'} ${arg}`);
1358 return '';1356 return '';
1359 }1357 }
1360 return groupMember[field];1358 return groupMember[field];
@@ -1362,27 +1360,27 @@ export function initDefaultSlashCommands() {
1362 namedArgumentList: [1360 namedArgumentList: [
1363 SlashCommandNamedArgument.fromProps({1361 SlashCommandNamedArgument.fromProps({
1364 name: 'field',1362 name: 'field',
1365 description: 'Whether to retrieve the name, index, id, or avatar.',1363 description: t`Whether to retrieve the name, index, id, or avatar.`,
1366 typeList: [ARGUMENT_TYPE.STRING],1364 typeList: [ARGUMENT_TYPE.STRING],
1367 isRequired: true,1365 isRequired: true,
1368 defaultValue: 'name',1366 defaultValue: 'name',
1369 enumList: [1367 enumList: [
1370 new SlashCommandEnumValue('name', 'Character name'),1368 new SlashCommandEnumValue('name', t`Character name`),
1371 new SlashCommandEnumValue('index', 'Group member index'),1369 new SlashCommandEnumValue('index', t`Group member index`),
1372 new SlashCommandEnumValue('avatar', 'Character avatar'),1370 new SlashCommandEnumValue('avatar', t`Character avatar`),
1373 new SlashCommandEnumValue('id', 'Character index'),1371 new SlashCommandEnumValue('id', t`Character index`),
1374 ],1372 ],
1375 }),1373 }),
1376 ],1374 ],
1377 unnamedArgumentList: [1375 unnamedArgumentList: [
1378 SlashCommandArgument.fromProps({1376 SlashCommandArgument.fromProps({
1379 description: 'member index (starts with 0), name, or avatar',1377 description: t`member index (starts with 0), name, or avatar`,
1380 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1378 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1381 isRequired: true,1379 isRequired: true,
1382 enumProvider: commonEnumProviders.groupMembers(),1380 enumProvider: commonEnumProviders.groupMembers(),
1383 }),1381 }),
1384 ],1382 ],
1385 helpString: 'Retrieves a group member\'s name, index, id, or avatar.',1383 helpString: t`Retrieves a group member's name, index, id, or avatar.`,
1386 }));1384 }));
1387 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1385 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1388 name: 'member-disable',1386 name: 'member-disable',
@@ -1390,13 +1388,13 @@ export function initDefaultSlashCommands() {
1390 aliases: ['disable', 'disablemember', 'memberdisable'],1388 aliases: ['disable', 'disablemember', 'memberdisable'],
1391 unnamedArgumentList: [1389 unnamedArgumentList: [
1392 SlashCommandArgument.fromProps({1390 SlashCommandArgument.fromProps({
1393 description: 'member index (starts with 0) or name',1391 description: t`member index (starts with 0) or name`,
1394 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1392 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1395 isRequired: true,1393 isRequired: true,
1396 enumProvider: commonEnumProviders.groupMembers(),1394 enumProvider: commonEnumProviders.groupMembers(),
1397 }),1395 }),
1398 ],1396 ],
1399 helpString: 'Disables a group member from being drafted for replies.',1397 helpString: t`Disables a group member from being drafted for replies.`,
1400 }));1398 }));
1401 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1399 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1402 name: 'member-enable',1400 name: 'member-enable',
@@ -1404,13 +1402,13 @@ export function initDefaultSlashCommands() {
1404 callback: enableGroupMemberCallback,1402 callback: enableGroupMemberCallback,
1405 unnamedArgumentList: [1403 unnamedArgumentList: [
1406 SlashCommandArgument.fromProps({1404 SlashCommandArgument.fromProps({
1407 description: 'member index (starts with 0) or name',1405 description: t`member index (starts with 0) or name`,
1408 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1406 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1409 isRequired: true,1407 isRequired: true,
1410 enumProvider: commonEnumProviders.groupMembers(),1408 enumProvider: commonEnumProviders.groupMembers(),
1411 }),1409 }),
1412 ],1410 ],
1413 helpString: 'Enables a group member to be drafted for replies.',1411 helpString: t`Enables a group member to be drafted for replies.`,
1414 }));1412 }));
1415 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1413 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1416 name: 'member-add',1414 name: 'member-add',
@@ -1418,7 +1416,7 @@ export function initDefaultSlashCommands() {
1418 aliases: ['addmember', 'memberadd'],1416 aliases: ['addmember', 'memberadd'],
1419 unnamedArgumentList: [1417 unnamedArgumentList: [
1420 SlashCommandArgument.fromProps({1418 SlashCommandArgument.fromProps({
1421 description: 'Character name - or unique character identifier (avatar key)',1419 description: t`Character name - or unique character identifier (avatar key)`,
1422 typeList: [ARGUMENT_TYPE.STRING],1420 typeList: [ARGUMENT_TYPE.STRING],
1423 isRequired: true,1421 isRequired: true,
1424 enumProvider: () => selected_group ? commonEnumProviders.characters('character')() : [],1422 enumProvider: () => selected_group ? commonEnumProviders.characters('character')() : [],
@@ -1426,10 +1424,10 @@ export function initDefaultSlashCommands() {
1426 ],1424 ],
1427 helpString: `1425 helpString: `
1428 <div>1426 <div>
1429 Adds a new group member to the group chat.1427 ${t`Adds a new group member to the group chat.`}
1430 </div>1428 </div>
1431 <div>1429 <div>
1432 <strong>Example:</strong>1430 <strong>${t`Example:`}</strong>
1433 <ul>1431 <ul>
1434 <li>1432 <li>
1435 <pre><code>/member-add John Doe</code></pre>1433 <pre><code>/member-add John Doe</code></pre>
@@ -1444,7 +1442,7 @@ export function initDefaultSlashCommands() {
1444 aliases: ['removemember', 'memberremove'],1442 aliases: ['removemember', 'memberremove'],
1445 unnamedArgumentList: [1443 unnamedArgumentList: [
1446 SlashCommandArgument.fromProps({1444 SlashCommandArgument.fromProps({
1447 description: 'member index (starts with 0) or name',1445 description: t`member index (starts with 0) or name`,
1448 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1446 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1449 isRequired: true,1447 isRequired: true,
1450 enumProvider: commonEnumProviders.groupMembers(),1448 enumProvider: commonEnumProviders.groupMembers(),
@@ -1452,10 +1450,10 @@ export function initDefaultSlashCommands() {
1452 ],1450 ],
1453 helpString: `1451 helpString: `
1454 <div>1452 <div>
1455 Removes a group member from the group chat.1453 ${t`Removes a group member from the group chat.`}
1456 </div>1454 </div>
1457 <div>1455 <div>
1458 <strong>Example:</strong>1456 <strong>${t`Example:`}</strong>
1459 <ul>1457 <ul>
1460 <li>1458 <li>
1461 <pre><code>/member-remove 2</code></pre>1459 <pre><code>/member-remove 2</code></pre>
@@ -1471,13 +1469,13 @@ export function initDefaultSlashCommands() {
1471 aliases: ['upmember', 'memberup'],1469 aliases: ['upmember', 'memberup'],
1472 unnamedArgumentList: [1470 unnamedArgumentList: [
1473 SlashCommandArgument.fromProps({1471 SlashCommandArgument.fromProps({
1474 description: 'member index (starts with 0) or name',1472 description: t`member index (starts with 0) or name`,
1475 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1473 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1476 isRequired: true,1474 isRequired: true,
1477 enumProvider: commonEnumProviders.groupMembers(),1475 enumProvider: commonEnumProviders.groupMembers(),
1478 }),1476 }),
1479 ],1477 ],
1480 helpString: 'Moves a group member up in the group chat list.',1478 helpString: t`Moves a group member up in the group chat list.`,
1481 }));1479 }));
1482 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1480 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1483 name: 'member-down',1481 name: 'member-down',
@@ -1485,13 +1483,13 @@ export function initDefaultSlashCommands() {
1485 aliases: ['downmember', 'memberdown'],1483 aliases: ['downmember', 'memberdown'],
1486 unnamedArgumentList: [1484 unnamedArgumentList: [
1487 SlashCommandArgument.fromProps({1485 SlashCommandArgument.fromProps({
1488 description: 'member index (starts with 0) or name',1486 description: t`member index (starts with 0) or name`,
1489 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1487 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1490 isRequired: true,1488 isRequired: true,
1491 enumProvider: commonEnumProviders.groupMembers(),1489 enumProvider: commonEnumProviders.groupMembers(),
1492 }),1490 }),
1493 ],1491 ],
1494 helpString: 'Moves a group member down in the group chat list.',1492 helpString: t`Moves a group member down in the group chat list.`,
1495 }));1493 }));
1496 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1494 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1497 name: 'member-peek',1495 name: 'member-peek',
@@ -1499,7 +1497,7 @@ export function initDefaultSlashCommands() {
1499 callback: peekCallback,1497 callback: peekCallback,
1500 unnamedArgumentList: [1498 unnamedArgumentList: [
1501 SlashCommandArgument.fromProps({1499 SlashCommandArgument.fromProps({
1502 description: 'member index (starts with 0) or name',1500 description: t`member index (starts with 0) or name`,
1503 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],1501 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
1504 isRequired: true,1502 isRequired: true,
1505 enumProvider: commonEnumProviders.groupMembers(),1503 enumProvider: commonEnumProviders.groupMembers(),
@@ -1507,14 +1505,14 @@ export function initDefaultSlashCommands() {
1507 ],1505 ],
1508 helpString: `1506 helpString: `
1509 <div>1507 <div>
1510 Shows a group member character card without switching chats.1508 ${t`Shows a group member character card without switching chats.`}
1511 </div>1509 </div>
1512 <div>1510 <div>
1513 <strong>Examples:</strong>1511 <strong>${t`Examples:`}</strong>
1514 <ul>1512 <ul>
1515 <li>1513 <li>
1516 <pre><code>/peek Gloria</code></pre>1514 <pre><code>/peek Gloria</code></pre>
1517 Shows the character card for the character named "Gloria".1515 ${t`Shows the character card for the character named "Gloria".`}
1518 </li>1516 </li>
1519 </ul>1517 </ul>
1520 </div>1518 </div>
@@ -1524,16 +1522,16 @@ export function initDefaultSlashCommands() {
1524 name: 'member-count',1522 name: 'member-count',
1525 callback: countGroupMemberCallback,1523 callback: countGroupMemberCallback,
1526 aliases: ['countmember', 'membercount'],1524 aliases: ['countmember', 'membercount'],
1527 helpString: 'Returns the total number of group members in the group chat list.',1525 helpString: t`Returns the total number of group members in the group chat list.`,
1528 }));1526 }));
1529 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1527 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1530 name: 'delswipe',1528 name: 'delswipe',
1531 callback: deleteSwipeCallback,1529 callback: deleteSwipeCallback,
1532 returns: 'the new, currently selected swipe id',1530 returns: t`the new, currently selected swipe id`,
1533 aliases: ['swipedel'],1531 aliases: ['swipedel'],
1534 unnamedArgumentList: [1532 unnamedArgumentList: [
1535 SlashCommandArgument.fromProps({1533 SlashCommandArgument.fromProps({
1536 description: '1-based swipe id',1534 description: t`1-based swipe id`,
1537 typeList: [ARGUMENT_TYPE.NUMBER],1535 typeList: [ARGUMENT_TYPE.NUMBER],
1538 isRequired: true,1536 isRequired: true,
1539 enumProvider: () => Array.isArray(chat[chat.length - 1]?.swipes) ?1537 enumProvider: () => Array.isArray(chat[chat.length - 1]?.swipes) ?
@@ -1543,18 +1541,18 @@ export function initDefaultSlashCommands() {
1543 ],1541 ],
1544 helpString: `1542 helpString: `
1545 <div>1543 <div>
1546 Deletes a swipe from the last chat message. If swipe id is not provided, it deletes the current swipe.1544 ${t`Deletes a swipe from the last chat message. If swipe id is not provided, it deletes the current swipe.`}
1547 </div>1545 </div>
1548 <div>1546 <div>
1549 <strong>Example:</strong>1547 <strong>${t`Example:`}</strong>
1550 <ul>1548 <ul>
1551 <li>1549 <li>
1552 <pre><code>/delswipe</code></pre>1550 <pre><code>/delswipe</code></pre>
1553 Deletes the current swipe.1551 ${t`Deletes the current swipe.`}
1554 </li>1552 </li>
1555 <li>1553 <li>
1556 <pre><code>/delswipe 2</code></pre>1554 <pre><code>/delswipe 2</code></pre>
1557 Deletes the second swipe from the last chat message.1555 ${t`Deletes the second swipe from the last chat message.`}
1558 </li>1556 </li>
1559 </ul>1557 </ul>
1560 </div>1558 </div>
@@ -1564,14 +1562,14 @@ export function initDefaultSlashCommands() {
1564 name: 'echo',1562 name: 'echo',
1565 rawQuotes: true,1563 rawQuotes: true,
1566 callback: echoCallback,1564 callback: echoCallback,
1567 returns: 'the text',1565 returns: t`the text`,
1568 namedArgumentList: [1566 namedArgumentList: [
1569 new SlashCommandNamedArgument(1567 new SlashCommandNamedArgument(
1570 'title', 'title of the toast message', [ARGUMENT_TYPE.STRING], false,1568 'title', t`title of the toast message`, [ARGUMENT_TYPE.STRING], false,
1571 ),1569 ),
1572 SlashCommandNamedArgument.fromProps({1570 SlashCommandNamedArgument.fromProps({
1573 name: 'severity',1571 name: 'severity',
1574 description: 'severity level of the toast message',1572 description: t`severity level of the toast message`,
1575 typeList: [ARGUMENT_TYPE.STRING],1573 typeList: [ARGUMENT_TYPE.STRING],
1576 defaultValue: 'info',1574 defaultValue: 'info',
1577 enumProvider: () => [1575 enumProvider: () => [
@@ -1583,54 +1581,54 @@ export function initDefaultSlashCommands() {
1583 }),1581 }),
1584 SlashCommandNamedArgument.fromProps({1582 SlashCommandNamedArgument.fromProps({
1585 name: 'timeout',1583 name: 'timeout',
1586 description: 'time in milliseconds to display the toast message. Set this and \'extendedTimeout\' to 0 to show indefinitely until dismissed.',1584 description: t`time in milliseconds to display the toast message. Set this and 'extendedTimeout' to 0 to show indefinitely until dismissed.`,
1587 typeList: [ARGUMENT_TYPE.NUMBER],1585 typeList: [ARGUMENT_TYPE.NUMBER],
1588 defaultValue: `${toastr.options.timeOut}`,1586 defaultValue: `${toastr.options.timeOut}`,
1589 }),1587 }),
1590 SlashCommandNamedArgument.fromProps({1588 SlashCommandNamedArgument.fromProps({
1591 name: 'extendedTimeout',1589 name: 'extendedTimeout',
1592 description: 'time in milliseconds to display the toast message. Set this and \'timeout\' to 0 to show indefinitely until dismissed.',1590 description: t`time in milliseconds to display the toast message. Set this and 'timeout' to 0 to show indefinitely until dismissed.`,
1593 typeList: [ARGUMENT_TYPE.NUMBER],1591 typeList: [ARGUMENT_TYPE.NUMBER],
1594 defaultValue: `${toastr.options.extendedTimeOut}`,1592 defaultValue: `${toastr.options.extendedTimeOut}`,
1595 }),1593 }),
1596 SlashCommandNamedArgument.fromProps({1594 SlashCommandNamedArgument.fromProps({
1597 name: 'preventDuplicates',1595 name: 'preventDuplicates',
1598 description: 'prevent duplicate toasts with the same message from being displayed.',1596 description: t`prevent duplicate toasts with the same message from being displayed.`,
1599 typeList: [ARGUMENT_TYPE.BOOLEAN],1597 typeList: [ARGUMENT_TYPE.BOOLEAN],
1600 defaultValue: 'false',1598 defaultValue: 'false',
1601 enumList: commonEnumProviders.boolean('trueFalse')(),1599 enumList: commonEnumProviders.boolean('trueFalse')(),
1602 }),1600 }),
1603 SlashCommandNamedArgument.fromProps({1601 SlashCommandNamedArgument.fromProps({
1604 name: 'awaitDismissal',1602 name: 'awaitDismissal',
1605 description: 'wait for the toast to be dismissed before continuing.',1603 description: t`wait for the toast to be dismissed before continuing.`,
1606 typeList: [ARGUMENT_TYPE.BOOLEAN],1604 typeList: [ARGUMENT_TYPE.BOOLEAN],
1607 defaultValue: 'false',1605 defaultValue: 'false',
1608 enumList: commonEnumProviders.boolean('trueFalse')(),1606 enumList: commonEnumProviders.boolean('trueFalse')(),
1609 }),1607 }),
1610 SlashCommandNamedArgument.fromProps({1608 SlashCommandNamedArgument.fromProps({
1611 name: 'cssClass',1609 name: 'cssClass',
1612 description: 'additional CSS class to add to the toast message (e.g. for custom styling)',1610 description: t`additional CSS class to add to the toast message (e.g. for custom styling)`,
1613 typeList: [ARGUMENT_TYPE.STRING],1611 typeList: [ARGUMENT_TYPE.STRING],
1614 }),1612 }),
1615 SlashCommandNamedArgument.fromProps({1613 SlashCommandNamedArgument.fromProps({
1616 name: 'color',1614 name: 'color',
1617 description: 'custom CSS color of the toast message. Accepts all valid CSS color values (e.g. \'red\', \'#FF0000\', \'rgb(255, 0, 0)\').<br />>Can be more customizable with the \'cssClass\' argument and custom classes.',1615 description: t`custom CSS color of the toast message. Accepts all valid CSS color values (e.g. 'red', '#FF0000', 'rgb(255, 0, 0)').<br />>Can be more customizable with the 'cssClass' argument and custom classes.`,
1618 }),1616 }),
1619 SlashCommandNamedArgument.fromProps({1617 SlashCommandNamedArgument.fromProps({
1620 name: 'escapeHtml',1618 name: 'escapeHtml',
1621 description: 'whether to escape HTML in the toast message.',1619 description: t`whether to escape HTML in the toast message.`,
1622 typeList: [ARGUMENT_TYPE.BOOLEAN],1620 typeList: [ARGUMENT_TYPE.BOOLEAN],
1623 defaultValue: 'true',1621 defaultValue: 'true',
1624 enumList: commonEnumProviders.boolean('trueFalse')(),1622 enumList: commonEnumProviders.boolean('trueFalse')(),
1625 }),1623 }),
1626 SlashCommandNamedArgument.fromProps({1624 SlashCommandNamedArgument.fromProps({
1627 name: 'onClick',1625 name: 'onClick',
1628 description: 'a closure to call when the toast is clicked. This executed closure receives scope as provided in the script. Careful about possible side effects when manipulating variables and more.',1626 description: t`a closure to call when the toast is clicked. This executed closure receives scope as provided in the script. Careful about possible side effects when manipulating variables and more.`,
1629 typeList: [ARGUMENT_TYPE.CLOSURE],1627 typeList: [ARGUMENT_TYPE.CLOSURE],
1630 }),1628 }),
1631 SlashCommandNamedArgument.fromProps({1629 SlashCommandNamedArgument.fromProps({
1632 name: 'raw',1630 name: 'raw',
1633 description: 'If true, does not alter quoted literal unnamed arguments',1631 description: t`If true, does not alter quoted literal unnamed arguments`,
1634 typeList: [ARGUMENT_TYPE.BOOLEAN],1632 typeList: [ARGUMENT_TYPE.BOOLEAN],
1635 defaultValue: 'true',1633 defaultValue: 'true',
1636 enumProvider: commonEnumProviders.boolean('trueFalse'),1634 enumProvider: commonEnumProviders.boolean('trueFalse'),
@@ -1665,32 +1663,32 @@ export function initDefaultSlashCommands() {
1665 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1663 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1666 name: 'gen',1664 name: 'gen',
1667 callback: generateCallback,1665 callback: generateCallback,
1668 returns: 'generated text',1666 returns: t`generated text`,
1669 namedArgumentList: [1667 namedArgumentList: [
1670 SlashCommandNamedArgument.fromProps({1668 SlashCommandNamedArgument.fromProps({
1671 name: 'trim',1669 name: 'trim',
1672 description: 'Trim the output by the last sentence boundary',1670 description: t`Trim the output by the last sentence boundary`,
1673 typeList: [ARGUMENT_TYPE.BOOLEAN],1671 typeList: [ARGUMENT_TYPE.BOOLEAN],
1674 defaultValue: 'false',1672 defaultValue: 'false',
1675 isRequired: false,1673 isRequired: false,
1676 enumProvider: commonEnumProviders.boolean('trueFalse'),1674 enumProvider: commonEnumProviders.boolean('trueFalse'),
1677 }),1675 }),
1678 new SlashCommandNamedArgument(1676 new SlashCommandNamedArgument(
1679 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(),1677 'lock', t`lock user input during generation`, [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(),
1680 ),1678 ),
1681 SlashCommandNamedArgument.fromProps({1679 SlashCommandNamedArgument.fromProps({
1682 name: 'name',1680 name: 'name',
1683 description: 'in-prompt character name for instruct mode (or unique character identifier (avatar key), which will be used as name)',1681 description: t`in-prompt character name for instruct mode (or unique character identifier (avatar key), which will be used as name)`,
1684 typeList: [ARGUMENT_TYPE.STRING],1682 typeList: [ARGUMENT_TYPE.STRING],
1685 defaultValue: 'System',1683 defaultValue: 'System',
1686 enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)],1684 enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)],
1687 }),1685 }),
1688 new SlashCommandNamedArgument(1686 new SlashCommandNamedArgument(
1689 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false,1687 'length', t`API response length in tokens`, [ARGUMENT_TYPE.NUMBER], false,
1690 ),1688 ),
1691 SlashCommandNamedArgument.fromProps({1689 SlashCommandNamedArgument.fromProps({
1692 name: 'as',1690 name: 'as',
1693 description: 'role of the output prompt',1691 description: t`role of the output prompt`,
1694 typeList: [ARGUMENT_TYPE.STRING],1692 typeList: [ARGUMENT_TYPE.STRING],
1695 enumList: [1693 enumList: [
1696 new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.assistant),1694 new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.assistant),
@@ -1705,30 +1703,30 @@ export function initDefaultSlashCommands() {
1705 ],1703 ],
1706 helpString: `1704 helpString: `
1707 <div>1705 <div>
1708 Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating and allowing to configure the in-prompt name for instruct mode (default = "System").1706 ${t`Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating and allowing to configure the in-prompt name for instruct mode (default = "System").`}
1709 </div>1707 </div>
1710 <div>1708 <div>
1711 "as" argument controls the role of the output prompt: system (default) or char. If "length" argument is provided as a number in tokens, allows to temporarily override an API response length.1709 ${t`"as" argument controls the role of the output prompt: system (default) or char. If "length" argument is provided as a number in tokens, allows to temporarily override an API response length.`}
1712 </div>1710 </div>
1713 `,1711 `,
1714 }));1712 }));
1715 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1713 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1716 name: 'genraw',1714 name: 'genraw',
1717 callback: generateRawCallback,1715 callback: generateRawCallback,
1718 returns: 'generated text',1716 returns: t`generated text`,
1719 namedArgumentList: [1717 namedArgumentList: [
1720 new SlashCommandNamedArgument(1718 new SlashCommandNamedArgument(
1721 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(),1719 'lock', t`lock user input during generation`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(),
1722 ),1720 ),
1723 new SlashCommandNamedArgument(1721 new SlashCommandNamedArgument(
1724 'instruct', 'use instruct mode', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),1722 'instruct', t`use instruct mode`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),
1725 ),1723 ),
1726 new SlashCommandNamedArgument(1724 new SlashCommandNamedArgument(
1727 'stop', 'one-time custom stop strings', [ARGUMENT_TYPE.LIST], false, false, '[]',1725 'stop', t`one-time custom stop strings`, [ARGUMENT_TYPE.LIST], false, false, '[]',
1728 ),1726 ),
1729 SlashCommandNamedArgument.fromProps({1727 SlashCommandNamedArgument.fromProps({
1730 name: 'as',1728 name: 'as',
1731 description: 'role of the output prompt',1729 description: t`role of the output prompt`,
1732 defaultValue: 'system',1730 defaultValue: 'system',
1733 typeList: [ARGUMENT_TYPE.STRING],1731 typeList: [ARGUMENT_TYPE.STRING],
1734 enumList: [1732 enumList: [
@@ -1737,16 +1735,16 @@ export function initDefaultSlashCommands() {
1737 ],1735 ],
1738 }),1736 }),
1739 new SlashCommandNamedArgument(1737 new SlashCommandNamedArgument(
1740 'system', 'system prompt at the start', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], false,1738 'system', t`system prompt at the start`, [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], false,
1741 ),1739 ),
1742 new SlashCommandNamedArgument(1740 new SlashCommandNamedArgument(
1743 'prefill', 'prefill prompt at the end', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], false,1741 'prefill', t`prefill prompt at the end`, [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], false,
1744 ),1742 ),
1745 new SlashCommandNamedArgument(1743 new SlashCommandNamedArgument(
1746 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], false,1744 'length', t`API response length in tokens`, [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], false,
1747 ),1745 ),
1748 new SlashCommandNamedArgument(1746 new SlashCommandNamedArgument(
1749 'trim', 'trim {{user}} and {{char}} prefixes from the output', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),1747 'trim', t`trim {{user}} and {{char}} prefixes from the output`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),
1750 ),1748 ),
1751 ],1749 ],
1752 unnamedArgumentList: [1750 unnamedArgumentList: [
@@ -1756,31 +1754,31 @@ export function initDefaultSlashCommands() {
1756 ],1754 ],
1757 helpString: `1755 helpString: `
1758 <div>1756 <div>
1759 Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating. Does not include chat history or character card.1757 ${t`Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating. Does not include chat history or character card.`}
1760 </div>1758 </div>
1761 <div>1759 <div>
1762 Use instruct=off to skip instruct formatting, e.g. <pre><code>/genraw instruct=off Why is the sky blue?</code></pre>1760 ${t`Use instruct=off to skip instruct formatting, e.g. <pre><code>/genraw instruct=off Why is the sky blue?</code></pre>`}
1763 </div>1761 </div>
1764 <div>1762 <div>
1765 Use stop=... with a JSON-serialized array to add one-time custom stop strings, e.g. <pre><code>/genraw stop=["\\n"] Say hi</code></pre>1763 ${t`Use stop=... with a JSON-serialized array to add one-time custom stop strings, e.g. <pre><code>/genraw stop=["\\n"] Say hi</code></pre>`}
1766 </div>1764 </div>
1767 <div>1765 <div>
1768 "as" argument controls the role of the output prompt: system (default) or char. "system" argument adds an (optional) system prompt at the start.1766 ${t`"as" argument controls the role of the output prompt: system (default) or char. "system" argument adds an (optional) system prompt at the start.`}
1769 </div>1767 </div>
1770 <div>1768 <div>
1771 If "length" argument is provided as a number in tokens, allows to temporarily override an API response length.1769 ${t`If "length" argument is provided as a number in tokens, allows to temporarily override an API response length.`}
1772 </div>1770 </div>
1773 `,1771 `,
1774 }));1772 }));
1775 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1773 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1776 name: 'addswipe',1774 name: 'addswipe',
1777 callback: addSwipeCallback,1775 callback: addSwipeCallback,
1778 returns: 'the new swipe id',1776 returns: t`the new swipe id`,
1779 aliases: ['swipeadd'],1777 aliases: ['swipeadd'],
1780 namedArgumentList: [1778 namedArgumentList: [
1781 SlashCommandNamedArgument.fromProps({1779 SlashCommandNamedArgument.fromProps({
1782 name: 'switch',1780 name: 'switch',
1783 description: 'switch to the new swipe',1781 description: t`switch to the new swipe`,
1784 typeList: [ARGUMENT_TYPE.BOOLEAN],1782 typeList: [ARGUMENT_TYPE.BOOLEAN],
1785 enumList: commonEnumProviders.boolean()(),1783 enumList: commonEnumProviders.boolean()(),
1786 }),1784 }),
@@ -1792,10 +1790,10 @@ export function initDefaultSlashCommands() {
1792 ],1790 ],
1793 helpString: `1791 helpString: `
1794 <div>1792 <div>
1795 Adds a swipe to the last chat message.1793 ${t`Adds a swipe to the last chat message.`}
1796 </div>1794 </div>
1797 <div>1795 <div>
1798 Use switch=true to switch to directly switch to the new swipe.1796 ${t`Use switch=true to switch to directly switch to the new swipe.`}
1799 </div>`,1797 </div>`,
1800 }));1798 }));
1801 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1799 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
@@ -1804,14 +1802,13 @@ export function initDefaultSlashCommands() {
1804 const stopped = stopGeneration();1802 const stopped = stopGeneration();
1805 return String(stopped);1803 return String(stopped);
1806 },1804 },
1807 returns: 'true/false, whether the generation was running and got stopped',1805 returns: t`true/false, whether the generation was running and got stopped`,
1808 helpString: `1806 helpString: `
1809 <div>1807 <div>
1810 Stops the generation and any streaming if it is currently running.1808 ${t`Stops the generation and any streaming if it is currently running.`}
1811 </div>1809 </div>
1812 <div>1810 <div>
1813 Note: This command cannot be executed from the chat input, as sending any message or script from there is blocked during generation.1811 ${t`Note: This command cannot be executed from the chat input, as sending any message or script from there is blocked during generation. But it can be executed via automations or QR scripts/buttons.`}
1814 But it can be executed via automations or QR scripts/buttons.
1815 </div>1812 </div>
1816 `,1813 `,
1817 aliases: ['generate-stop'],1814 aliases: ['generate-stop'],
@@ -1822,27 +1819,27 @@ export function initDefaultSlashCommands() {
1822 namedArgumentList: [1819 namedArgumentList: [
1823 SlashCommandNamedArgument.fromProps({1820 SlashCommandNamedArgument.fromProps({
1824 name: 'quiet',1821 name: 'quiet',
1825 description: 'Whether to suppress the toast message notifying about the /abort call.',1822 description: t`Whether to suppress the toast message notifying about the /abort call.`,
1826 typeList: [ARGUMENT_TYPE.BOOLEAN],1823 typeList: [ARGUMENT_TYPE.BOOLEAN],
1827 defaultValue: 'true',1824 defaultValue: 'true',
1828 }),1825 }),
1829 ],1826 ],
1830 unnamedArgumentList: [1827 unnamedArgumentList: [
1831 SlashCommandArgument.fromProps({1828 SlashCommandArgument.fromProps({
1832 description: 'The reason for aborting command execution. Shown when quiet=false',1829 description: t`The reason for aborting command execution. Shown when quiet=false`,
1833 typeList: [ARGUMENT_TYPE.STRING],1830 typeList: [ARGUMENT_TYPE.STRING],
1834 }),1831 }),
1835 ],1832 ],
1836 helpString: 'Aborts the slash command batch execution.',1833 helpString: t`Aborts the slash command batch execution.`,
1837 }));1834 }));
1838 SlashCommandParser.addCommandObject(SlashCommand.fromProps({1835 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1839 name: 'fuzzy',1836 name: 'fuzzy',
1840 callback: fuzzyCallback,1837 callback: fuzzyCallback,
1841 returns: 'matching item',1838 returns: t`matching item`,
1842 namedArgumentList: [1839 namedArgumentList: [
1843 SlashCommandNamedArgument.fromProps({1840 SlashCommandNamedArgument.fromProps({
1844 name: 'list',1841 name: 'list',
1845 description: 'list of items to match against',1842 description: t`list of items to match against`,
1846 acceptsMultiple: false,1843 acceptsMultiple: false,
1847 isRequired: true,1844 isRequired: true,
1848 typeList: [ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.VARIABLE_NAME],1845 typeList: [ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.VARIABLE_NAME],
@@ -1850,7 +1847,7 @@ export function initDefaultSlashCommands() {
1850 }),1847 }),
1851 SlashCommandNamedArgument.fromProps({1848 SlashCommandNamedArgument.fromProps({
1852 name: 'threshold',1849 name: 'threshold',
1853 description: 'fuzzy match threshold (0.0 to 1.0)',1850 description: t`fuzzy match threshold (0.0 to 1.0)`,
1854 typeList: [ARGUMENT_TYPE.NUMBER],1851 typeList: [ARGUMENT_TYPE.NUMBER],
1855 isRequired: false,1852 isRequired: false,
1856 defaultValue: '0.4',1853 defaultValue: '0.4',
@@ -1858,44 +1855,43 @@ export function initDefaultSlashCommands() {
1858 }),1855 }),
1859 SlashCommandNamedArgument.fromProps({1856 SlashCommandNamedArgument.fromProps({
1860 name: 'mode',1857 name: 'mode',
1861 description: 'fuzzy match mode',1858 description: t`fuzzy match mode`,
1862 typeList: [ARGUMENT_TYPE.STRING],1859 typeList: [ARGUMENT_TYPE.STRING],
1863 isRequired: false,1860 isRequired: false,
1864 defaultValue: 'first',1861 defaultValue: 'first',
1865 acceptsMultiple: false,1862 acceptsMultiple: false,
1866 enumList: [1863 enumList: [
1867 new SlashCommandEnumValue('first', 'first match below the threshold', enumTypes.enum, enumIcons.default),1864 new SlashCommandEnumValue('first', t`first match below the threshold`, enumTypes.enum, enumIcons.default),
1868 new SlashCommandEnumValue('best', 'best match below the threshold', enumTypes.enum, enumIcons.default),1865 new SlashCommandEnumValue('best', t`best match below the threshold`, enumTypes.enum, enumIcons.default),
1869 ],1866 ],
1870 }),1867 }),
1871 ],1868 ],
1872 unnamedArgumentList: [1869 unnamedArgumentList: [
1873 new SlashCommandArgument(1870 new SlashCommandArgument(
1874 'text to search', [ARGUMENT_TYPE.STRING], true,1871 t`text to search`, [ARGUMENT_TYPE.STRING], true,
1875 ),1872 ),
1876 ],1873 ],
1877 helpString: `1874 helpString: `
1878 <div>1875 <div>
1879 Performs a fuzzy match of each item in the <code>list</code> against the <code>text to search</code>.1876 ${t`Performs a fuzzy match of each item in the <code>list</code> against the <code>text to search</code>. If any item matches, then its name is returned. If no item matches the text, no value is returned.`}
1880 If any item matches, then its name is returned. If no item matches the text, no value is returned.
1881 </div>1877 </div>
1882 <div>1878 <div>
1883 The optional <code>threshold</code> (default is 0.4) allows control over the match strictness.1879 ${t`The optional <code>threshold</code> (default is 0.4) allows control over the match strictness.`}
1884 A low value (min 0.0) means the match is very strict.1880 ${t`A low value (min 0.0) means the match is very strict.`}
1885 At 1.0 (max) the match is very loose and will match anything.1881 ${t`At 1.0 (max) the match is very loose and will match anything.`}
1886 </div>1882 </div>
1887 <div>1883 <div>
1888 The optional <code>mode</code> argument allows to control the behavior when multiple items match the text.1884 ${t`The optional <code>mode</code> argument allows to control the behavior when multiple items match the text.`}
1889 <ul>1885 <ul>
1890 <li><code>first</code> (default) returns the first match below the threshold.</li>1886 <li>${t`<code>first</code> (default) returns the first match below the threshold.`}</li>
1891 <li><code>best</code> returns the best match below the threshold.</li>1887 <li>${t`<code>best</code> returns the best match below the threshold.`}</li>
1892 </ul>1888 </ul>
1893 </div>1889 </div>
1894 <div>1890 <div>
1895 The returned value passes to the next command through the pipe.1891 ${t`The returned value passes to the next command through the pipe.`}
1896 </div>1892 </div>
1897 <div>1893 <div>
1898 <strong>Example:</strong>1894 <strong>${t`Example:`}</strong>
1899 <ul>1895 <ul>
1900 <li>1896 <li>
1901 <pre><code>/fuzzy list=["a","b","c"] threshold=0.4 abc</code></pre>1897 <pre><code>/fuzzy list=["a","b","c"] threshold=0.4 abc</code></pre>
@@ -1908,23 +1904,23 @@ export function initDefaultSlashCommands() {
1908 name: 'pass',1904 name: 'pass',
1909 callback: (_, arg) => {1905 callback: (_, arg) => {
1910 // We do not support arrays of closures. Arrays of strings will be send as JSON1906 // We do not support arrays of closures. Arrays of strings will be send as JSON
1911 if (Array.isArray(arg) && arg.some(x => x instanceof SlashCommandClosure)) throw new Error('Command /pass does not support multiple closures');1907 if (Array.isArray(arg) && arg.some(x => x instanceof SlashCommandClosure)) throw new Error(t`Command /pass does not support multiple closures`);
1912 if (Array.isArray(arg)) return JSON.stringify(arg);1908 if (Array.isArray(arg)) return JSON.stringify(arg);
1913 return arg;1909 return arg;
1914 },1910 },
1915 returns: 'the provided value',1911 returns: t`the provided value`,
1916 unnamedArgumentList: [1912 unnamedArgumentList: [
1917 new SlashCommandArgument(1913 new SlashCommandArgument(
1918 'text', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY, ARGUMENT_TYPE.CLOSURE], true,1914 t`text`, [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY, ARGUMENT_TYPE.CLOSURE], true,
1919 ),1915 ),
1920 ],1916 ],
1921 aliases: ['return'],1917 aliases: ['return'],
1922 helpString: `1918 helpString: `
1923 <div>1919 <div>
1924 <pre><span class="monospace">/pass (text)</span> – passes the text to the next command through the pipe.</pre>1920 <pre><span class="monospace">/pass (text)</span> – ${t`passes the text to the next command through the pipe.`}</pre>
1925 </div>1921 </div>
1926 <div>1922 <div>
1927 <strong>Example:</strong>1923 <strong>${t`Example:`}</strong>
1928 <ul>1924 <ul>
1929 <li><pre><code>/pass Hello world</code></pre></li>1925 <li><pre><code>/pass Hello world</code></pre></li>
1930 </ul>1926 </ul>
@@ -1937,15 +1933,15 @@ export function initDefaultSlashCommands() {
1937 aliases: ['wait', 'sleep'],1933 aliases: ['wait', 'sleep'],
1938 unnamedArgumentList: [1934 unnamedArgumentList: [
1939 new SlashCommandArgument(1935 new SlashCommandArgument(
1940 'milliseconds', [ARGUMENT_TYPE.NUMBER], true,1936 t`milliseconds`, [ARGUMENT_TYPE.NUMBER], true,
1941 ),1937 ),
1942 ],1938 ],
1943 helpString: `1939 helpString: `
1944 <div>1940 <div>
1945 Delays the next command in the pipe by the specified number of milliseconds.1941 ${t`Delays the next command in the pipe by the specified number of milliseconds.`}
1946 </div>1942 </div>
1947 <div>1943 <div>
1948 <strong>Example:</strong>1944 <strong>${t`Example:`}</strong>
1949 <ul>1945 <ul>
1950 <li>1946 <li>
1951 <pre><code>/delay 1000</code></pre>1947 <pre><code>/delay 1000</code></pre>
@@ -1958,59 +1954,59 @@ export function initDefaultSlashCommands() {
1958 name: 'input',1954 name: 'input',
1959 aliases: ['prompt'],1955 aliases: ['prompt'],
1960 callback: inputCallback,1956 callback: inputCallback,
1961 returns: 'user input',1957 returns: t`user input`,
1962 namedArgumentList: [1958 namedArgumentList: [
1963 SlashCommandNamedArgument.fromProps({1959 SlashCommandNamedArgument.fromProps({
1964 name: 'default',1960 name: 'default',
1965 description: 'default value of the input field',1961 description: t`default value of the input field`,
1966 typeList: [ARGUMENT_TYPE.STRING],1962 typeList: [ARGUMENT_TYPE.STRING],
1967 }),1963 }),
1968 SlashCommandNamedArgument.fromProps({1964 SlashCommandNamedArgument.fromProps({
1969 name: 'large',1965 name: 'large',
1970 description: 'popup window will be shown larger in height, with more space for content (input field needs to be sized via \'rows\' argument)',1966 description: t`popup window will be shown larger in height, with more space for content (input field needs to be sized via 'rows' argument)`,
1971 typeList: [ARGUMENT_TYPE.BOOLEAN],1967 typeList: [ARGUMENT_TYPE.BOOLEAN],
1972 defaultValue: 'off',1968 defaultValue: 'off',
1973 enumList: commonEnumProviders.boolean('onOff')(),1969 enumList: commonEnumProviders.boolean('onOff')(),
1974 }),1970 }),
1975 SlashCommandNamedArgument.fromProps({1971 SlashCommandNamedArgument.fromProps({
1976 name: 'wide',1972 name: 'wide',
1977 description: 'popup window will be shown wider, with a wider input field',1973 description: t`popup window will be shown wider, with a wider input field`,
1978 typeList: [ARGUMENT_TYPE.BOOLEAN],1974 typeList: [ARGUMENT_TYPE.BOOLEAN],
1979 defaultValue: 'off',1975 defaultValue: 'off',
1980 enumList: commonEnumProviders.boolean('onOff')(),1976 enumList: commonEnumProviders.boolean('onOff')(),
1981 }),1977 }),
1982 SlashCommandNamedArgument.fromProps({1978 SlashCommandNamedArgument.fromProps({
1983 name: 'okButton',1979 name: 'okButton',
1984 description: 'text for the ok button',1980 description: t`text for the ok button`,
1985 typeList: [ARGUMENT_TYPE.STRING],1981 typeList: [ARGUMENT_TYPE.STRING],
1986 defaultValue: 'Ok',1982 defaultValue: 'Ok',
1987 }),1983 }),
1988 SlashCommandNamedArgument.fromProps({1984 SlashCommandNamedArgument.fromProps({
1989 name: 'rows',1985 name: 'rows',
1990 description: 'number of rows for the input field (lines being displayed)',1986 description: t`number of rows for the input field (lines being displayed)`,
1991 typeList: [ARGUMENT_TYPE.NUMBER],1987 typeList: [ARGUMENT_TYPE.NUMBER],
1992 }),1988 }),
1993 SlashCommandNamedArgument.fromProps({1989 SlashCommandNamedArgument.fromProps({
1994 name: 'onSuccess',1990 name: 'onSuccess',
1995 description: 'closure to execute when the ok button is clicked or the input is closed as successful (via Enter, etc)',1991 description: t`closure to execute when the ok button is clicked or the input is closed as successful (via Enter, etc)`,
1996 typeList: [ARGUMENT_TYPE.CLOSURE],1992 typeList: [ARGUMENT_TYPE.CLOSURE],
1997 }),1993 }),
1998 SlashCommandNamedArgument.fromProps({1994 SlashCommandNamedArgument.fromProps({
1999 name: 'onCancel',1995 name: 'onCancel',
2000 description: 'closure to execute when the cancel button is clicked or the input is closed as cancelled (via Escape, etc)',1996 description: t`closure to execute when the cancel button is clicked or the input is closed as cancelled (via Escape, etc)`,
2001 typeList: [ARGUMENT_TYPE.CLOSURE],1997 typeList: [ARGUMENT_TYPE.CLOSURE],
2002 }),1998 }),
2003 ],1999 ],
2004 unnamedArgumentList: [2000 unnamedArgumentList: [
2005 SlashCommandArgument.fromProps({2001 SlashCommandArgument.fromProps({
2006 description: 'text to display',2002 description: t`text to display`,
2007 typeList: [ARGUMENT_TYPE.STRING],2003 typeList: [ARGUMENT_TYPE.STRING],
2008 }),2004 }),
2009 ],2005 ],
2010 helpString: `2006 helpString: `
2011 <div>2007 <div>
2012 Shows a popup with the provided text and an input field.2008 ${t`Shows a popup with the provided text and an input field.`}
2013 The <code>default</code> argument is the default value of the input field, and the text argument is the text to display.2009 ${t`The <code>default</code> argument is the default value of the input field, and the text argument is the text to display.`}
2014 </div>2010 </div>
2015 `,2011 `,
2016 }));2012 }));
@@ -2018,15 +2014,15 @@ export function initDefaultSlashCommands() {
2018 name: 'run',2014 name: 'run',
2019 aliases: ['call', 'exec'],2015 aliases: ['call', 'exec'],
2020 callback: runCallback,2016 callback: runCallback,
2021 returns: 'result of the executed closure of QR',2017 returns: t`result of the executed closure of QR`,
2022 namedArgumentList: [2018 namedArgumentList: [
2023 new SlashCommandNamedArgument(2019 new SlashCommandNamedArgument(
2024 'args', 'named arguments', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY], false, true,2020 'args', t`named arguments`, [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY], false, true,
2025 ),2021 ),
2026 ],2022 ],
2027 unnamedArgumentList: [2023 unnamedArgumentList: [
2028 SlashCommandArgument.fromProps({2024 SlashCommandArgument.fromProps({
2029 description: 'scoped variable or qr label',2025 description: t`scoped variable or qr label`,
2030 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.CLOSURE],2026 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.CLOSURE],
2031 isRequired: true,2027 isRequired: true,
2032 enumProvider: (executor, scope) => [2028 enumProvider: (executor, scope) => [
@@ -2037,8 +2033,8 @@ export function initDefaultSlashCommands() {
2037 ],2033 ],
2038 helpString: `2034 helpString: `
2039 <div>2035 <div>
2040 Runs a closure from a scoped variable, or a Quick Reply with the specified name from a currently active preset or from another preset.2036 ${t`Runs a closure from a scoped variable, or a Quick Reply with the specified name from a currently active preset or from another preset.`}
2041 Named arguments can be referenced in a QR with <code>{{arg::key}}</code>.2037 ${t`Named arguments can be referenced in a QR with <code>{{arg::key}}</code>.`}
2042 </div>2038 </div>
2043 `,2039 `,
2044 }));2040 }));
@@ -2048,14 +2044,14 @@ export function initDefaultSlashCommands() {
2048 aliases: ['message'],2044 aliases: ['message'],
2049 namedArgumentList: [2045 namedArgumentList: [
2050 new SlashCommandNamedArgument(2046 new SlashCommandNamedArgument(
2051 'names', 'show message author names', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(),2047 'names', t`show message author names`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(),
2052 ),2048 ),
2053 new SlashCommandNamedArgument(2049 new SlashCommandNamedArgument(
2054 'hidden', 'include hidden messages', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),2050 'hidden', t`include hidden messages`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(),
2055 ),2051 ),
2056 SlashCommandNamedArgument.fromProps({2052 SlashCommandNamedArgument.fromProps({
2057 name: 'role',2053 name: 'role',
2058 description: 'filter messages by role',2054 description: t`filter messages by role`,
2059 typeList: [ARGUMENT_TYPE.STRING],2055 typeList: [ARGUMENT_TYPE.STRING],
2060 enumList: [2056 enumList: [
2061 new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.system),2057 new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.system),
@@ -2066,33 +2062,33 @@ export function initDefaultSlashCommands() {
2066 ],2062 ],
2067 unnamedArgumentList: [2063 unnamedArgumentList: [
2068 SlashCommandArgument.fromProps({2064 SlashCommandArgument.fromProps({
2069 description: 'message index (starts with 0) or range',2065 description: t`message index (starts with 0) or range`,
2070 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],2066 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],
2071 isRequired: true,2067 isRequired: true,
2072 enumProvider: commonEnumProviders.messages(),2068 enumProvider: commonEnumProviders.messages(),
2073 }),2069 }),
2074 ],2070 ],
2075 returns: 'the specified message or range of messages as a string',2071 returns: t`the specified message or range of messages as a string`,
2076 helpString: `2072 helpString: `
2077 <div>2073 <div>
2078 Returns the specified message or range of messages as a string.2074 ${t`Returns the specified message or range of messages as a string.`}
2079 </div>2075 </div>
2080 <div>2076 <div>
2081 Use the <code>hidden=off</code> argument to exclude hidden messages.2077 ${t`Use the <code>hidden=off</code> argument to exclude hidden messages.`}
2082 </div>2078 </div>
2083 <div>2079 <div>
2084 Use the <code>role</code> argument to filter messages by role. Possible values are: system, assistant, user.2080 ${t`Use the <code>role</code> argument to filter messages by role. Possible values are: system, assistant, user.`}
2085 </div>2081 </div>
2086 <div>2082 <div>
2087 <strong>Examples:</strong>2083 <strong>${t`Examples:`}</strong>
2088 <ul>2084 <ul>
2089 <li>2085 <li>
2090 <pre><code>/messages 10</code></pre>2086 <pre><code>/messages 10</code></pre>
2091 Returns the 10th message.2087 ${t`Returns the 10th message.`}
2092 </li>2088 </li>
2093 <li>2089 <li>
2094 <pre><code>/messages names=on 5-10</code></pre>2090 <pre><code>/messages names=on 5-10</code></pre>
2095 Returns messages 5 through 10 with author names.2091 ${t`Returns messages 5 through 10 with author names.`}
2096 </li>2092 </li>
2097 </ul>2093 </ul>
2098 </div>2094 </div>
@@ -2103,15 +2099,15 @@ export function initDefaultSlashCommands() {
2103 callback: setInputCallback,2099 callback: setInputCallback,
2104 unnamedArgumentList: [2100 unnamedArgumentList: [
2105 new SlashCommandArgument(2101 new SlashCommandArgument(
2106 'text', [ARGUMENT_TYPE.STRING], true,2102 t`text`, [ARGUMENT_TYPE.STRING], true,
2107 ),2103 ),
2108 ],2104 ],
2109 helpString: `2105 helpString: `
2110 <div>2106 <div>
2111 Sets the user input to the specified text and passes it to the next command through the pipe.2107 ${t`Sets the user input to the specified text and passes it to the next command through the pipe.`}
2112 </div>2108 </div>
2113 <div>2109 <div>
2114 <strong>Example:</strong>2110 <strong>${t`Example:`}</strong>
2115 <ul>2111 <ul>
2116 <li>2112 <li>
2117 <pre><code>/setinput Hello world</code></pre>2113 <pre><code>/setinput Hello world</code></pre>
@@ -2123,57 +2119,57 @@ export function initDefaultSlashCommands() {
2123 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2119 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2124 name: 'popup',2120 name: 'popup',
2125 callback: popupCallback,2121 callback: popupCallback,
2126 returns: 'popup text',2122 returns: t`popup text`,
2127 namedArgumentList: [2123 namedArgumentList: [
2128 SlashCommandNamedArgument.fromProps({2124 SlashCommandNamedArgument.fromProps({
2129 name: 'scroll',2125 name: 'scroll',
2130 description: 'allows vertical scrolling of the content',2126 description: t`allows vertical scrolling of the content`,
2131 typeList: [ARGUMENT_TYPE.BOOLEAN],2127 typeList: [ARGUMENT_TYPE.BOOLEAN],
2132 enumList: commonEnumProviders.boolean('trueFalse')(),2128 enumList: commonEnumProviders.boolean('trueFalse')(),
2133 defaultValue: 'true',2129 defaultValue: 'true',
2134 }),2130 }),
2135 SlashCommandNamedArgument.fromProps({2131 SlashCommandNamedArgument.fromProps({
2136 name: 'large',2132 name: 'large',
2137 description: 'show large popup',2133 description: t`show large popup`,
2138 typeList: [ARGUMENT_TYPE.BOOLEAN],2134 typeList: [ARGUMENT_TYPE.BOOLEAN],
2139 enumList: commonEnumProviders.boolean('trueFalse')(),2135 enumList: commonEnumProviders.boolean('trueFalse')(),
2140 defaultValue: 'false',2136 defaultValue: 'false',
2141 }),2137 }),
2142 SlashCommandNamedArgument.fromProps({2138 SlashCommandNamedArgument.fromProps({
2143 name: 'wide',2139 name: 'wide',
2144 description: 'show wide popup',2140 description: t`show wide popup`,
2145 typeList: [ARGUMENT_TYPE.BOOLEAN],2141 typeList: [ARGUMENT_TYPE.BOOLEAN],
2146 enumList: commonEnumProviders.boolean('trueFalse')(),2142 enumList: commonEnumProviders.boolean('trueFalse')(),
2147 defaultValue: 'false',2143 defaultValue: 'false',
2148 }),2144 }),
2149 SlashCommandNamedArgument.fromProps({2145 SlashCommandNamedArgument.fromProps({
2150 name: 'wider',2146 name: 'wider',
2151 description: 'show wider popup',2147 description: t`show wider popup`,
2152 typeList: [ARGUMENT_TYPE.BOOLEAN],2148 typeList: [ARGUMENT_TYPE.BOOLEAN],
2153 enumList: commonEnumProviders.boolean('trueFalse')(),2149 enumList: commonEnumProviders.boolean('trueFalse')(),
2154 defaultValue: 'false',2150 defaultValue: 'false',
2155 }),2151 }),
2156 SlashCommandNamedArgument.fromProps({2152 SlashCommandNamedArgument.fromProps({
2157 name: 'transparent',2153 name: 'transparent',
2158 description: 'show transparent popup',2154 description: t`show transparent popup`,
2159 typeList: [ARGUMENT_TYPE.BOOLEAN],2155 typeList: [ARGUMENT_TYPE.BOOLEAN],
2160 enumList: commonEnumProviders.boolean('trueFalse')(),2156 enumList: commonEnumProviders.boolean('trueFalse')(),
2161 defaultValue: 'false',2157 defaultValue: 'false',
2162 }),2158 }),
2163 SlashCommandNamedArgument.fromProps({2159 SlashCommandNamedArgument.fromProps({
2164 name: 'okButton',2160 name: 'okButton',
2165 description: 'text for the OK button',2161 description: t`text for the OK button`,
2166 typeList: [ARGUMENT_TYPE.STRING],2162 typeList: [ARGUMENT_TYPE.STRING],
2167 defaultValue: 'OK',2163 defaultValue: 'OK',
2168 }),2164 }),
2169 SlashCommandNamedArgument.fromProps({2165 SlashCommandNamedArgument.fromProps({
2170 name: 'cancelButton',2166 name: 'cancelButton',
2171 description: 'text for the Cancel button',2167 description: t`text for the Cancel button`,
2172 typeList: [ARGUMENT_TYPE.STRING],2168 typeList: [ARGUMENT_TYPE.STRING],
2173 }),2169 }),
2174 SlashCommandNamedArgument.fromProps({2170 SlashCommandNamedArgument.fromProps({
2175 name: 'result',2171 name: 'result',
2176 description: 'if enabled, returns the popup result (as an integer) instead of the popup text. Resolves to 1 for OK and 0 cancel button, empty string for exiting out.',2172 description: t`if enabled, returns the popup result (as an integer) instead of the popup text. Resolves to 1 for OK and 0 cancel button, empty string for exiting out.`,
2177 typeList: [ARGUMENT_TYPE.BOOLEAN],2173 typeList: [ARGUMENT_TYPE.BOOLEAN],
2178 enumList: commonEnumProviders.boolean('trueFalse')(),2174 enumList: commonEnumProviders.boolean('trueFalse')(),
2179 defaultValue: 'false',2175 defaultValue: 'false',
@@ -2181,18 +2177,18 @@ export function initDefaultSlashCommands() {
2181 ],2177 ],
2182 unnamedArgumentList: [2178 unnamedArgumentList: [
2183 SlashCommandArgument.fromProps({2179 SlashCommandArgument.fromProps({
2184 description: 'popup text',2180 description: t`popup text`,
2185 typeList: [ARGUMENT_TYPE.STRING],2181 typeList: [ARGUMENT_TYPE.STRING],
2186 isRequired: true,2182 isRequired: true,
2187 }),2183 }),
2188 ],2184 ],
2189 helpString: `2185 helpString: `
2190 <div>2186 <div>
2191 Shows a blocking popup with the specified text and buttons.2187 ${t`Shows a blocking popup with the specified text and buttons.`}
2192 Returns the popup text.2188 ${t`Returns the popup text.`}
2193 </div>2189 </div>
2194 <div>2190 <div>
2195 <strong>Example:</strong>2191 <strong>${t`Example:`}</strong>
2196 <ul>2192 <ul>
2197 <li>2193 <li>
2198 <pre><code>/popup large=on wide=on okButton="Confirm" Please confirm this action.</code></pre>2194 <pre><code>/popup large=on wide=on okButton="Confirm" Please confirm this action.</code></pre>
@@ -2207,17 +2203,17 @@ export function initDefaultSlashCommands() {
2207 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2203 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2208 name: 'buttons',2204 name: 'buttons',
2209 callback: buttonsCallback,2205 callback: buttonsCallback,
2210 returns: 'clicked button label (or array of labels if multiple is enabled)',2206 returns: t`clicked button label (or array of labels if multiple is enabled)`,
2211 namedArgumentList: [2207 namedArgumentList: [
2212 SlashCommandNamedArgument.fromProps({2208 SlashCommandNamedArgument.fromProps({
2213 name: 'labels',2209 name: 'labels',
2214 description: 'button labels',2210 description: t`button labels`,
2215 typeList: [ARGUMENT_TYPE.LIST],2211 typeList: [ARGUMENT_TYPE.LIST],
2216 isRequired: true,2212 isRequired: true,
2217 }),2213 }),
2218 SlashCommandNamedArgument.fromProps({2214 SlashCommandNamedArgument.fromProps({
2219 name: 'multiple',2215 name: 'multiple',
2220 description: 'if enabled multiple buttons can be clicked/toggled, and all clicked buttons are returned as an array',2216 description: t`if enabled multiple buttons can be clicked/toggled, and all clicked buttons are returned as an array`,
2221 typeList: [ARGUMENT_TYPE.BOOLEAN],2217 typeList: [ARGUMENT_TYPE.BOOLEAN],
2222 enumList: commonEnumProviders.boolean('trueFalse')(),2218 enumList: commonEnumProviders.boolean('trueFalse')(),
2223 defaultValue: 'false',2219 defaultValue: 'false',
@@ -2225,18 +2221,18 @@ export function initDefaultSlashCommands() {
2225 ],2221 ],
2226 unnamedArgumentList: [2222 unnamedArgumentList: [
2227 SlashCommandArgument.fromProps({2223 SlashCommandArgument.fromProps({
2228 description: 'text',2224 description: t`text`,
2229 typeList: [ARGUMENT_TYPE.STRING],2225 typeList: [ARGUMENT_TYPE.STRING],
2230 isRequired: true,2226 isRequired: true,
2231 }),2227 }),
2232 ],2228 ],
2233 helpString: `2229 helpString: `
2234 <div>2230 <div>
2235 Shows a blocking popup with the specified text and buttons.2231 ${t`Shows a blocking popup with the specified text and buttons.`}
2236 Returns the clicked button label into the pipe or empty string if canceled.2232 ${t`Returns the clicked button label into the pipe or empty string if canceled.`}
2237 </div>2233 </div>
2238 <div>2234 <div>
2239 <strong>Example:</strong>2235 <strong>${t`Example:`}</strong>
2240 <ul>2236 <ul>
2241 <li>2237 <li>
2242 <pre><code>/buttons labels=["Yes","No"] Do you want to continue?</code></pre>2238 <pre><code>/buttons labels=["Yes","No"] Do you want to continue?</code></pre>
@@ -2248,14 +2244,14 @@ export function initDefaultSlashCommands() {
2248 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2244 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2249 name: 'trimtokens',2245 name: 'trimtokens',
2250 callback: trimTokensCallback,2246 callback: trimTokensCallback,
2251 returns: 'trimmed text',2247 returns: t`trimmed text`,
2252 namedArgumentList: [2248 namedArgumentList: [
2253 new SlashCommandNamedArgument(2249 new SlashCommandNamedArgument(
2254 'limit', 'number of tokens to keep', [ARGUMENT_TYPE.NUMBER], true,2250 'limit', t`number of tokens to keep`, [ARGUMENT_TYPE.NUMBER], true,
2255 ),2251 ),
2256 SlashCommandNamedArgument.fromProps({2252 SlashCommandNamedArgument.fromProps({
2257 name: 'direction',2253 name: 'direction',
2258 description: 'trim direction',2254 description: t`trim direction`,
2259 typeList: [ARGUMENT_TYPE.STRING],2255 typeList: [ARGUMENT_TYPE.STRING],
2260 isRequired: true,2256 isRequired: true,
2261 enumList: [2257 enumList: [
@@ -2266,15 +2262,15 @@ export function initDefaultSlashCommands() {
2266 ],2262 ],
2267 unnamedArgumentList: [2263 unnamedArgumentList: [
2268 new SlashCommandArgument(2264 new SlashCommandArgument(
2269 'text', [ARGUMENT_TYPE.STRING], false,2265 t`text`, [ARGUMENT_TYPE.STRING], false,
2270 ),2266 ),
2271 ],2267 ],
2272 helpString: `2268 helpString: `
2273 <div>2269 <div>
2274 Trims the start or end of text to the specified number of tokens.2270 ${t`Trims the start or end of text to the specified number of tokens.`}
2275 </div>2271 </div>
2276 <div>2272 <div>
2277 <strong>Example:</strong>2273 <strong>${t`Example:`}</strong>
2278 <ul>2274 <ul>
2279 <li>2275 <li>
2280 <pre><code>/trimtokens limit=5 direction=start This is a long sentence with many words</code></pre>2276 <pre><code>/trimtokens limit=5 direction=start This is a long sentence with many words</code></pre>
@@ -2286,18 +2282,18 @@ export function initDefaultSlashCommands() {
2286 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2282 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2287 name: 'trimstart',2283 name: 'trimstart',
2288 callback: trimStartCallback,2284 callback: trimStartCallback,
2289 returns: 'trimmed text',2285 returns: t`trimmed text`,
2290 unnamedArgumentList: [2286 unnamedArgumentList: [
2291 new SlashCommandArgument(2287 new SlashCommandArgument(
2292 'text', [ARGUMENT_TYPE.STRING], true,2288 t`text`, [ARGUMENT_TYPE.STRING], true,
2293 ),2289 ),
2294 ],2290 ],
2295 helpString: `2291 helpString: `
2296 <div>2292 <div>
2297 Trims the text to the start of the first full sentence.2293 ${t`Trims the text to the start of the first full sentence.`}
2298 </div>2294 </div>
2299 <div>2295 <div>
2300 <strong>Example:</strong>2296 <strong>${t`Example:`}</strong>
2301 <ul>2297 <ul>
2302 <li>2298 <li>
2303 <pre><code>/trimstart This is a sentence. And here is another sentence.</code></pre>2299 <pre><code>/trimstart This is a sentence. And here is another sentence.</code></pre>
@@ -2309,38 +2305,38 @@ export function initDefaultSlashCommands() {
2309 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2305 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2310 name: 'trimend',2306 name: 'trimend',
2311 callback: trimEndCallback,2307 callback: trimEndCallback,
2312 returns: 'trimmed text',2308 returns: t`trimmed text`,
2313 unnamedArgumentList: [2309 unnamedArgumentList: [
2314 new SlashCommandArgument(2310 new SlashCommandArgument(
2315 'text', [ARGUMENT_TYPE.STRING], true,2311 t`text`, [ARGUMENT_TYPE.STRING], true,
2316 ),2312 ),
2317 ],2313 ],
2318 helpString: 'Trims the text to the end of the last full sentence.',2314 helpString: t`Trims the text to the end of the last full sentence.`,
2319 }));2315 }));
2320 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2316 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2321 name: 'inject',2317 name: 'inject',
2322 returns: 'injection ID',2318 returns: t`injection ID`,
2323 callback: injectCallback,2319 callback: injectCallback,
2324 namedArgumentList: [2320 namedArgumentList: [
2325 SlashCommandNamedArgument.fromProps({2321 SlashCommandNamedArgument.fromProps({
2326 name: 'id',2322 name: 'id',
2327 description: 'injection ID',2323 description: t`injection ID`,
2328 typeList: [ARGUMENT_TYPE.STRING],2324 typeList: [ARGUMENT_TYPE.STRING],
2329 isRequired: false,2325 isRequired: false,
2330 enumProvider: commonEnumProviders.injects,2326 enumProvider: commonEnumProviders.injects,
2331 }),2327 }),
2332 new SlashCommandNamedArgument(2328 new SlashCommandNamedArgument(
2333 'position', 'injection position', [ARGUMENT_TYPE.STRING], false, false, 'after', ['before', 'after', 'chat', 'none'],2329 'position', t`injection position`, [ARGUMENT_TYPE.STRING], false, false, 'after', ['before', 'after', 'chat', 'none'],
2334 ),2330 ),
2335 new SlashCommandNamedArgument(2331 new SlashCommandNamedArgument(
2336 'depth', 'injection depth', [ARGUMENT_TYPE.NUMBER], false, false, '4',2332 'depth', t`injection depth`, [ARGUMENT_TYPE.NUMBER], false, false, '4',
2337 ),2333 ),
2338 new SlashCommandNamedArgument(2334 new SlashCommandNamedArgument(
2339 'scan', 'include injection content into World Info scans', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',2335 'scan', t`include injection content into World Info scans`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
2340 ),2336 ),
2341 SlashCommandNamedArgument.fromProps({2337 SlashCommandNamedArgument.fromProps({
2342 name: 'role',2338 name: 'role',
2343 description: 'role for in-chat injections',2339 description: t`role for in-chat injections`,
2344 typeList: [ARGUMENT_TYPE.STRING],2340 typeList: [ARGUMENT_TYPE.STRING],
2345 isRequired: false,2341 isRequired: false,
2346 enumList: [2342 enumList: [
@@ -2350,11 +2346,11 @@ export function initDefaultSlashCommands() {
2350 ],2346 ],
2351 }),2347 }),
2352 new SlashCommandNamedArgument(2348 new SlashCommandNamedArgument(
2353 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',2349 'ephemeral', t`remove injection after generation`, [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
2354 ),2350 ),
2355 SlashCommandNamedArgument.fromProps({2351 SlashCommandNamedArgument.fromProps({
2356 name: 'filter',2352 name: 'filter',
2357 description: 'if a filter is defined, an injection will only be performed if the closure returns true',2353 description: t`if a filter is defined, an injection will only be performed if the closure returns true`,
2358 typeList: [ARGUMENT_TYPE.CLOSURE],2354 typeList: [ARGUMENT_TYPE.CLOSURE],
2359 isRequired: false,2355 isRequired: false,
2360 acceptsMultiple: false,2356 acceptsMultiple: false,
@@ -2362,20 +2358,20 @@ export function initDefaultSlashCommands() {
2362 ],2358 ],
2363 unnamedArgumentList: [2359 unnamedArgumentList: [
2364 new SlashCommandArgument(2360 new SlashCommandArgument(
2365 'text', [ARGUMENT_TYPE.STRING], false,2361 t`text`, [ARGUMENT_TYPE.STRING], false,
2366 ),2362 ),
2367 ],2363 ],
2368 helpString: 'Injects a text into the LLM prompt for the current chat. Requires a unique injection ID (will be auto-generated if not provided). Positions: "before" main prompt, "after" main prompt, in-"chat", hidden with "none" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false). Hidden injects in "none" position are not inserted into the prompt but can be used for triggering WI entries. Returns the injection ID.',2364 helpString: t`Injects a text into the LLM prompt for the current chat. Requires a unique injection ID (will be auto-generated if not provided). Positions: "before" main prompt, "after" main prompt, in-"chat", hidden with "none" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false). Hidden injects in "none" position are not inserted into the prompt but can be used for triggering WI entries. Returns the injection ID.`,
2369 }));2365 }));
2370 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2366 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2371 name: 'listinjects',2367 name: 'listinjects',
2372 callback: listInjectsCallback,2368 callback: listInjectsCallback,
2373 helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>return</code> argument to change the return type.',2369 helpString: t`Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>return</code> argument to change the return type.`,
2374 returns: 'Optionalls the JSON object of script injections',2370 returns: t`Optionally the JSON object of script injections`,
2375 namedArgumentList: [2371 namedArgumentList: [
2376 SlashCommandNamedArgument.fromProps({2372 SlashCommandNamedArgument.fromProps({
2377 name: 'return',2373 name: 'return',
2378 description: 'The way how you want the return value to be provided',2374 description: t`The way how you want the return value to be provided`,
2379 typeList: [ARGUMENT_TYPE.STRING],2375 typeList: [ARGUMENT_TYPE.STRING],
2380 defaultValue: 'popup-html',2376 defaultValue: 'popup-html',
2381 enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }),2377 enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }),
@@ -2384,14 +2380,14 @@ export function initDefaultSlashCommands() {
2384 // TODO remove some day2380 // TODO remove some day
2385 SlashCommandNamedArgument.fromProps({2381 SlashCommandNamedArgument.fromProps({
2386 name: 'format',2382 name: 'format',
2387 description: '!!! DEPRECATED - use "return" instead !!! output format',2383 description: t`!!! DEPRECATED - use "return" instead !!! output format`,
2388 typeList: [ARGUMENT_TYPE.STRING],2384 typeList: [ARGUMENT_TYPE.STRING],
2389 isRequired: true,2385 isRequired: true,
2390 forceEnum: true,2386 forceEnum: true,
2391 enumList: [2387 enumList: [
2392 new SlashCommandEnumValue('popup', 'Show injects in a popup.', enumTypes.enum, enumIcons.default),2388 new SlashCommandEnumValue('popup', t`Show injects in a popup.`, enumTypes.enum, enumIcons.default),
2393 new SlashCommandEnumValue('chat', 'Post a system message to the chat.', enumTypes.enum, enumIcons.default),2389 new SlashCommandEnumValue('chat', t`Post a system message to the chat.`, enumTypes.enum, enumIcons.default),
2394 new SlashCommandEnumValue('none', 'Just return the injects as a JSON object.', enumTypes.enum, enumIcons.default),2390 new SlashCommandEnumValue('none', t`Just return the injects as a JSON object.`, enumTypes.enum, enumIcons.default),
2395 ],2391 ],
2396 }),2392 }),
2397 ],2393 ],
@@ -2401,37 +2397,37 @@ export function initDefaultSlashCommands() {
2401 aliases: ['flushinjects'],2397 aliases: ['flushinjects'],
2402 unnamedArgumentList: [2398 unnamedArgumentList: [
2403 SlashCommandArgument.fromProps({2399 SlashCommandArgument.fromProps({
2404 description: 'injection ID or a variable name pointing to ID',2400 description: t`injection ID or a variable name pointing to ID`,
2405 typeList: [ARGUMENT_TYPE.STRING],2401 typeList: [ARGUMENT_TYPE.STRING],
2406 defaultValue: '',2402 defaultValue: '',
2407 enumProvider: commonEnumProviders.injects,2403 enumProvider: commonEnumProviders.injects,
2408 }),2404 }),
2409 ],2405 ],
2410 callback: flushInjectsCallback,2406 callback: flushInjectsCallback,
2411 helpString: 'Removes a script injection for the current chat. If no ID is provided, removes all script injections.',2407 helpString: t`Removes a script injection for the current chat. If no ID is provided, removes all script injections.`,
2412 }));2408 }));
2413 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2409 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2414 name: 'tokens',2410 name: 'tokens',
2415 callback: (_, text) => {2411 callback: (_, text) => {
2416 if (text instanceof SlashCommandClosure || Array.isArray(text)) throw new Error('Unnamed argument cannot be a closure for command /tokens');2412 if (text instanceof SlashCommandClosure || Array.isArray(text)) throw new Error(t`Unnamed argument cannot be a closure for command /tokens`);
2417 return getTokenCountAsync(text).then(count => String(count));2413 return getTokenCountAsync(text).then(count => String(count));
2418 },2414 },
2419 returns: 'number of tokens',2415 returns: t`number of tokens`,
2420 unnamedArgumentList: [2416 unnamedArgumentList: [
2421 new SlashCommandArgument(2417 new SlashCommandArgument(
2422 'text', [ARGUMENT_TYPE.STRING], true,2418 t`text`, [ARGUMENT_TYPE.STRING], true,
2423 ),2419 ),
2424 ],2420 ],
2425 helpString: 'Counts the number of tokens in the provided text.',2421 helpString: t`Counts the number of tokens in the provided text.`,
2426 }));2422 }));
2427 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2423 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2428 name: 'model',2424 name: 'model',
2429 callback: modelCallback,2425 callback: modelCallback,
2430 returns: 'current model',2426 returns: t`current model`,
2431 namedArgumentList: [2427 namedArgumentList: [
2432 SlashCommandNamedArgument.fromProps({2428 SlashCommandNamedArgument.fromProps({
2433 name: 'quiet',2429 name: 'quiet',
2434 description: 'suppress the toast message on model change',2430 description: t`suppress the toast message on model change`,
2435 typeList: [ARGUMENT_TYPE.BOOLEAN],2431 typeList: [ARGUMENT_TYPE.BOOLEAN],
2436 defaultValue: 'false',2432 defaultValue: 'false',
2437 enumList: commonEnumProviders.boolean('trueFalse')(),2433 enumList: commonEnumProviders.boolean('trueFalse')(),
@@ -2439,22 +2435,22 @@ export function initDefaultSlashCommands() {
2439 ],2435 ],
2440 unnamedArgumentList: [2436 unnamedArgumentList: [
2441 SlashCommandArgument.fromProps({2437 SlashCommandArgument.fromProps({
2442 description: 'model name',2438 description: t`model name`,
2443 typeList: [ARGUMENT_TYPE.STRING],2439 typeList: [ARGUMENT_TYPE.STRING],
2444 enumProvider: () => getModelOptions(true)?.options?.map(option => new SlashCommandEnumValue(option.value, option.value !== option.text ? option.text : null)) ?? [],2440 enumProvider: () => getModelOptions(true)?.options?.map(option => new SlashCommandEnumValue(option.value, option.value !== option.text ? option.text : null)) ?? [],
2445 }),2441 }),
2446 ],2442 ],
2447 helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',2443 helpString: t`Sets the model for the current API. Gets the current model name if no argument is provided.`,
2448 }));2444 }));
2449 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2445 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2450 name: 'getpromptentry',2446 name: 'getpromptentry',
2451 aliases: ['getpromptentries'],2447 aliases: ['getpromptentries'],
2452 callback: getPromptEntryCallback,2448 callback: getPromptEntryCallback,
2453 returns: 'true/false state of prompt(s)',2449 returns: t`true/false state of prompt(s)`,
2454 namedArgumentList: [2450 namedArgumentList: [
2455 SlashCommandNamedArgument.fromProps({2451 SlashCommandNamedArgument.fromProps({
2456 name: 'identifier',2452 name: 'identifier',
2457 description: 'Prompt entry identifier(s) to retrieve',2453 description: t`Prompt entry identifier(s) to retrieve`,
2458 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],2454 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
2459 acceptsMultiple: true,2455 acceptsMultiple: true,
2460 enumProvider: () =>2456 enumProvider: () =>
@@ -2464,7 +2460,7 @@ export function initDefaultSlashCommands() {
2464 }),2460 }),
2465 SlashCommandNamedArgument.fromProps({2461 SlashCommandNamedArgument.fromProps({
2466 name: 'name',2462 name: 'name',
2467 description: 'Prompt entry name(s) to retrieve',2463 description: t`Prompt entry name(s) to retrieve`,
2468 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],2464 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
2469 acceptsMultiple: true,2465 acceptsMultiple: true,
2470 enumProvider: () =>2466 enumProvider: () =>
@@ -2474,7 +2470,7 @@ export function initDefaultSlashCommands() {
2474 }),2470 }),
2475 SlashCommandNamedArgument.fromProps({2471 SlashCommandNamedArgument.fromProps({
2476 name: 'return',2472 name: 'return',
2477 description: 'Whether the return will be simple, a list, or a dict.',2473 description: t`Whether the return will be simple, a list, or a dict.`,
2478 typeList: [ARGUMENT_TYPE.STRING],2474 typeList: [ARGUMENT_TYPE.STRING],
2479 defaultValue: 'simple',2475 defaultValue: 'simple',
2480 enumList: ['simple', 'list', 'dict'],2476 enumList: ['simple', 'list', 'dict'],
@@ -2482,10 +2478,10 @@ export function initDefaultSlashCommands() {
2482 ],2478 ],
2483 helpString: `2479 helpString: `
2484 <div>2480 <div>
2485 Gets the state of the specified prompt entries.2481 ${t`Gets the state of the specified prompt entries.`}
2486 </div>2482 </div>
2487 <div>2483 <div>
2488 If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.2484 ${t`If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.`}
2489 </div>2485 </div>
2490 `,2486 `,
2491 }));2487 }));
@@ -2496,7 +2492,7 @@ export function initDefaultSlashCommands() {
2496 namedArgumentList: [2492 namedArgumentList: [
2497 SlashCommandNamedArgument.fromProps({2493 SlashCommandNamedArgument.fromProps({
2498 name: 'identifier',2494 name: 'identifier',
2499 description: 'Prompt entry identifier(s) to target',2495 description: t`Prompt entry identifier(s) to target`,
2500 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],2496 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
2501 acceptsMultiple: true,2497 acceptsMultiple: true,
2502 enumProvider: () => {2498 enumProvider: () => {
@@ -2506,7 +2502,7 @@ export function initDefaultSlashCommands() {
2506 }),2502 }),
2507 SlashCommandNamedArgument.fromProps({2503 SlashCommandNamedArgument.fromProps({
2508 name: 'name',2504 name: 'name',
2509 description: 'Prompt entry name(s) to target',2505 description: t`Prompt entry name(s) to target`,
2510 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],2506 typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
2511 acceptsMultiple: true,2507 acceptsMultiple: true,
2512 enumProvider: () => {2508 enumProvider: () => {
@@ -2517,7 +2513,7 @@ export function initDefaultSlashCommands() {
2517 ],2513 ],
2518 unnamedArgumentList: [2514 unnamedArgumentList: [
2519 SlashCommandArgument.fromProps({2515 SlashCommandArgument.fromProps({
2520 description: 'Set entry/entries on or off',2516 description: t`Set entry/entries on or off`,
2521 typeList: [ARGUMENT_TYPE.STRING],2517 typeList: [ARGUMENT_TYPE.STRING],
2522 isRequired: true,2518 isRequired: true,
2523 acceptsMultiple: false,2519 acceptsMultiple: false,
@@ -2525,16 +2521,16 @@ export function initDefaultSlashCommands() {
2525 enumList: commonEnumProviders.boolean('onOffToggle')(),2521 enumList: commonEnumProviders.boolean('onOffToggle')(),
2526 }),2522 }),
2527 ],2523 ],
2528 helpString: 'Sets the specified prompt manager entry/entries on or off.',2524 helpString: t`Sets the specified prompt manager entry/entries on or off.`,
2529 }));2525 }));
2530 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2526 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2531 name: 'pick-icon',2527 name: 'pick-icon',
2532 callback: async () => ((await showFontAwesomePicker()) ?? false).toString(),2528 callback: async () => ((await showFontAwesomePicker()) ?? false).toString(),
2533 returns: 'The chosen icon name or false if cancelled.',2529 returns: t`The chosen icon name or false if cancelled.`,
2534 helpString: `2530 helpString: `
2535 <div>Opens a popup with all the available Font Awesome icons and returns the selected icon's name.</div>2531 <div>${t`Opens a popup with all the available Font Awesome icons and returns the selected icon's name.`}</div>
2536 <div>2532 <div>
2537 <strong>Example:</strong>2533 <strong>${t`Example:`}</strong>
2538 <ul>2534 <ul>
2539 <li>2535 <li>
2540 <pre><code>/pick-icon |\n/if left={{pipe}} rule=eq right=false\n\telse={: /echo chosen icon: "{{pipe}}" :}\n\t{: /echo cancelled icon selection :}\n|</code></pre>2536 <pre><code>/pick-icon |\n/if left={{pipe}} rule=eq right=false\n\telse={: /echo chosen icon: "{{pipe}}" :}\n\t{: /echo cancelled icon selection :}\n|</code></pre>
@@ -2546,12 +2542,12 @@ export function initDefaultSlashCommands() {
2546 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2542 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2547 name: 'api-url',2543 name: 'api-url',
2548 callback: setApiUrlCallback,2544 callback: setApiUrlCallback,
2549 returns: 'the current API url',2545 returns: t`the current API url`,
2550 aliases: ['server'],2546 aliases: ['server'],
2551 namedArgumentList: [2547 namedArgumentList: [
2552 SlashCommandNamedArgument.fromProps({2548 SlashCommandNamedArgument.fromProps({
2553 name: 'api',2549 name: 'api',
2554 description: 'API to set/get the URL for - if not provided, current API is used',2550 description: t`API to set/get the URL for - if not provided, current API is used`,
2555 typeList: [ARGUMENT_TYPE.STRING],2551 typeList: [ARGUMENT_TYPE.STRING],
2556 enumList: [2552 enumList: [
2557 new SlashCommandEnumValue('custom', 'custom OpenAI-compatible', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'openai')), 'O'),2553 new SlashCommandEnumValue('custom', 'custom OpenAI-compatible', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'openai')), 'O'),
@@ -2561,14 +2557,14 @@ export function initDefaultSlashCommands() {
2561 }),2557 }),
2562 SlashCommandNamedArgument.fromProps({2558 SlashCommandNamedArgument.fromProps({
2563 name: 'connect',2559 name: 'connect',
2564 description: 'Whether to auto-connect to the API after setting the URL',2560 description: t`Whether to auto-connect to the API after setting the URL`,
2565 typeList: [ARGUMENT_TYPE.BOOLEAN],2561 typeList: [ARGUMENT_TYPE.BOOLEAN],
2566 defaultValue: 'true',2562 defaultValue: 'true',
2567 enumList: commonEnumProviders.boolean('trueFalse')(),2563 enumList: commonEnumProviders.boolean('trueFalse')(),
2568 }),2564 }),
2569 SlashCommandNamedArgument.fromProps({2565 SlashCommandNamedArgument.fromProps({
2570 name: 'quiet',2566 name: 'quiet',
2571 description: 'suppress the toast message on API change',2567 description: t`suppress the toast message on API change`,
2572 typeList: [ARGUMENT_TYPE.BOOLEAN],2568 typeList: [ARGUMENT_TYPE.BOOLEAN],
2573 defaultValue: 'false',2569 defaultValue: 'false',
2574 enumList: commonEnumProviders.boolean('trueFalse')(),2570 enumList: commonEnumProviders.boolean('trueFalse')(),
@@ -2576,31 +2572,29 @@ export function initDefaultSlashCommands() {
2576 ],2572 ],
2577 unnamedArgumentList: [2573 unnamedArgumentList: [
2578 SlashCommandArgument.fromProps({2574 SlashCommandArgument.fromProps({
2579 description: 'API url to connect to',2575 description: t`API url to connect to`,
2580 typeList: [ARGUMENT_TYPE.STRING],2576 typeList: [ARGUMENT_TYPE.STRING],
2581 }),2577 }),
2582 ],2578 ],
2583 helpString: `2579 helpString: `
2584 <div>2580 <div>
2585 Set the API url / server url for the currently selected API, including the port. If no argument is provided, it will return the current API url.2581 ${t`Set the API url / server url for the currently selected API, including the port. If no argument is provided, it will return the current API url.`}
2586 </div>2582 </div>
2587 <div>2583 <div>
2588 If a manual API is provided to <b>set</b> the URL, make sure to set <code>connect=false</code>, as auto-connect only works for the currently selected API,2584 ${t`If a manual API is provided to <b>set</b> the URL, make sure to set <code>connect=false</code>, as auto-connect only works for the currently selected API, or consider switching to it with <code>/api</code> first.`}
2589 or consider switching to it with <code>/api</code> first.
2590 </div>2585 </div>
2591 <div>2586 <div>
2592 This slash command works for most of the Text Completion sources, KoboldAI Classic, and also Custom OpenAI compatible for the Chat Completion sources. If unsure which APIs are supported,2587 ${t`This slash command works for most of the Text Completion sources, KoboldAI Classic, and also Custom OpenAI compatible for the Chat Completion sources. If unsure which APIs are supported, check the auto-completion of the optional <code>api</code> argument of this command.`}
2593 check the auto-completion of the optional <code>api</code> argument of this command.
2594 </div>2588 </div>
2595 `,2589 `,
2596 }));2590 }));
2597 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2591 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2598 name: 'tokenizer',2592 name: 'tokenizer',
2599 callback: selectTokenizerCallback,2593 callback: selectTokenizerCallback,
2600 returns: 'current tokenizer',2594 returns: t`current tokenizer`,
2601 unnamedArgumentList: [2595 unnamedArgumentList: [
2602 SlashCommandArgument.fromProps({2596 SlashCommandArgument.fromProps({
2603 description: 'tokenizer name',2597 description: t`tokenizer name`,
2604 typeList: [ARGUMENT_TYPE.STRING],2598 typeList: [ARGUMENT_TYPE.STRING],
2605 enumList: getAvailableTokenizers().map(tokenizer =>2599 enumList: getAvailableTokenizers().map(tokenizer =>
2606 new SlashCommandEnumValue(tokenizer.tokenizerKey, tokenizer.tokenizerName, enumTypes.enum, enumIcons.default)),2600 new SlashCommandEnumValue(tokenizer.tokenizerKey, tokenizer.tokenizerName, enumTypes.enum, enumIcons.default)),
@@ -2608,10 +2602,10 @@ export function initDefaultSlashCommands() {
2608 ],2602 ],
2609 helpString: `2603 helpString: `
public/scripts/slash-commands/SlashCommand.js+0 -0
public/scripts/textgen-models.js+0 -0
public/scripts/tokenizers.js+0 -0
public/scripts/tool-calling.js+0 -0
public/scripts/world-info.js+0 -0
public/style.css+0 -0
src/command-line.js+0 -0
src/constants.js+0 -0
src/endpoints/backends/chat-completions.js+0 -0
src/endpoints/backends/text-completions.js+0 -0
src/endpoints/content-manager.js+0 -0
src/endpoints/openai.js+0 -0
src/endpoints/openrouter.js+0 -0
src/endpoints/secrets.js+0 -0
src/endpoints/stable-diffusion.js+0 -0
src/middleware/hostWhitelist.js+0 -0
src/server-main.js+0 -0
src/server-startup.js+0 -0
Diff truncated