Compare commits

..

476 Commits

Author SHA1 Message Date
Cohee
db2c9a9926 Attempt docker release fix 6 2024-05-20 23:50:34 +03:00
Cohee
1fc88e97f4 Attempt docker release fix 5 2024-05-20 23:44:01 +03:00
Cohee
3d3914645d Attempt docker release fix 4 2024-05-20 23:35:36 +03:00
Cohee
68ade7b384 Attempt docker release fix 3 2024-05-20 23:20:28 +03:00
Cohee
071f232611 Attempt docker release fix 2 2024-05-20 23:08:51 +03:00
Cohee
dff5ca7e92 Attempt docker release fix 2024-05-20 23:03:22 +03:00
Cohee
ab75680ed3 Merge pull request #2277 from SillyTavern/staging
Staging
2024-05-19 23:48:13 +03:00
Cohee
8c11d7e8e8 Merge pull request #2276 from LenAnderson/stop-parser-from-bitching-about-space-after-quote
stop parser from complaining about space after closing quote of quoted argument
2024-05-19 23:09:29 +03:00
LenAnderson
8b776491e8 fix endOfText
current char must be whitespace as well
2024-05-19 15:52:30 -04:00
Cohee
884f26924c Merge pull request #2275 from SillyTavern/staging
Staging
2024-05-19 22:37:33 +03:00
Cohee
5f79579a4d Fix selector? 2024-05-19 22:33:16 +03:00
Cohee
82f56da16b Hide script buttons on old Safari 2024-05-19 22:25:41 +03:00
Cohee
bc2035d362 Don't use autocomplete on old Safari 2024-05-19 22:22:32 +03:00
Cohee
41f25edb15 Workaround for old Safari 2024-05-19 22:16:14 +03:00
Cohee
cf28d6653c Import characters from RisuRealm by URL 2024-05-19 20:19:20 +03:00
Cohee
53496d70f1 Merge pull request #2272 from SillyTavern/staging
Staging
2024-05-19 16:45:57 +03:00
Cohee
a17588fb1b Fix smooth streaming not working with multiswipe 2024-05-19 16:41:55 +03:00
Cohee
dd06fddd17 #2267 Fix multiswipe mutating the array returning by the streaming provider 2024-05-19 16:38:18 +03:00
Cohee
e9c5618b10 Force generate unlock on wrapped API error 2024-05-19 16:25:22 +03:00
Cohee
b32330df0f Merge pull request #2270 from LenAnderson/more-subcommand-shenanigans
More subcommand shenanigans
2024-05-19 15:47:55 +03:00
LenAnderson
3bd2edf4d2 move silencing of loud /aborts into execute function when handled 2024-05-19 07:34:09 -04:00
Cohee
697949c784 Bump package version 2024-05-19 14:31:52 +03:00
Cohee
ee913be46b Merge pull request #2266 from sasha0552/vllm-fixes
vLLM fixes
2024-05-19 14:23:07 +03:00
LenAnderson
4f5813a6ce fix /if subcommand strings 2024-05-19 06:40:08 -04:00
LenAnderson
4a25821aba remove todo 2024-05-19 06:38:59 -04:00
LenAnderson
c049ff82cd fix typehint 2024-05-19 06:31:58 -04:00
LenAnderson
64ee2d77b8 fix switch to play button when script paused 2024-05-19 06:27:27 -04:00
LenAnderson
ce4106eb3d fix typehint 2024-05-19 06:27:09 -04:00
LenAnderson
974d27ce26 use root AbortController in /if subcommands 2024-05-19 06:26:16 -04:00
LenAnderson
dfe482b37b fix /abort toast and loop break in /times 2024-05-19 06:25:19 -04:00
LenAnderson
1f18694aa6 fix /abort toasts in /while 2024-05-19 06:25:00 -04:00
LenAnderson
89de668241 fix typehints for while unnamed arg / command 2024-05-19 06:24:32 -04:00
LenAnderson
963e525e07 fix typehints 2024-05-19 06:23:34 -04:00
LenAnderson
94ee7167e0 unescape macros inside unnamed arg arrays 2024-05-19 06:23:19 -04:00
Cohee
48c075fb42 #2268 Fix while command 2024-05-19 12:19:08 +03:00
Cohee
581e5f1f04 Fix app init 2024-05-19 11:52:49 +03:00
RossAscends
9637b3fbe1 Merge branch 'staging' of https://github.com/Cohee1207/SillyTavern into staging 2024-05-19 15:06:32 +09:00
RossAscends
c7232ae23c WIP textgen API custom sampler display 2024-05-19 15:06:29 +09:00
sasha0552
01912f5b3e Fix typo 2024-05-19 04:36:36 +00:00
sasha0552
db5e2d95c2 vLLM fixes
* Enable seed field for vLLM

* Enable beam search for vLLM

* Set the default length penalty to 1
(There is validation error from vLLM when beam search is disabled and the value is not equal to 1)
2024-05-19 04:34:11 +00:00
Cohee
d9b55df883 Fix loading characters from assets on first load 2024-05-19 03:31:09 +03:00
Cohee
4e9a113a35 Merge pull request #2265 from LenAnderson/parser-post-stuff
Follow ups and fixes for the new STscript parser
2024-05-18 22:39:23 +03:00
LenAnderson
70a35e9b49 Merge branch 'staging' into parser-post-stuff 2024-05-18 14:51:06 -04:00
LenAnderson
bc4a8fbe1d fix qr editor height on narrow 2024-05-18 14:49:33 -04:00
LenAnderson
d77a70b25a fix type hints 2024-05-18 14:49:02 -04:00
LenAnderson
e49317a73c fix empty unnamed arg assignment start 2024-05-18 14:48:50 -04:00
LenAnderson
87cc28ae28 subcommand and /abort fixes
- use AbortController in /abort instead of execption
- allow quiet abort
- allow loud abort
- allow abort reason
- abort when aborted in subcommand
- break out of loops when aborted inside
- fix parsing of subcommands with multiple commands
2024-05-18 14:48:31 -04:00
Cohee
b93131ec7a Lint fix 2024-05-18 18:58:25 +03:00
Cohee
4219468e20 Merge pull request #2264 from racinmat/staging
fix: correct usage of fuzzy search in emotion detection by LLM
2024-05-18 18:57:34 +03:00
Cohee
4227968dfa Allow using JSON schema with llamacpp server 2024-05-18 18:50:48 +03:00
Cohee
09790bb994 Merge pull request #2258 from Wolfsblvt/tag-slash-commands
Add slash commands for tag management
2024-05-18 18:34:24 +03:00
Matěj Račinský
bd1bfee941 fix: correct usage of fuzzy search 2024-05-18 17:05:55 +02:00
Cohee
0b3c49da90 Allow bulk edit menu to wrap 2024-05-18 17:59:59 +03:00
Cohee
c3d6e10795 Fix error on creating new tags 2024-05-18 17:55:22 +03:00
Cohee
1eae9bd18b Draw updated tags immediately 2024-05-18 17:51:42 +03:00
Cohee
99e24f5588 Fix group tags opacity 2024-05-18 17:51:10 +03:00
Cohee
38e2bf955c Merge branch 'staging' into tag-slash-commands 2024-05-18 17:27:04 +03:00
Cohee
0653dad5c5 Show tags when auto-loading a character 2024-05-18 17:05:46 +03:00
Cohee
51af830db8 Only auto-select preset on full name match 2024-05-18 16:46:41 +03:00
Cohee
2bde9d2b15 Merge branch 'staging' into tag-slash-commands 2024-05-18 16:42:25 +03:00
Cohee
d9f9da99e6 Silent /abort 2024-05-18 15:41:02 +03:00
Cohee
64b0123acf Merge pull request #2263 from PasserDreamer/staging
Fix some typo and add zh-tw locale.
2024-05-18 14:48:47 +03:00
Cohee
35d853b851 Normalize "Default" and "Match Whole Words" data-i18n 2024-05-18 14:43:06 +03:00
Cohee
1966baad84 Fix invalid JSON format 2024-05-18 14:39:04 +03:00
Cohee
a398566b33 [BUG]In the lorebooks management panel, when in the Custom sorting mode, entries may be incorrectly placed above the table header. #2262 2024-05-18 13:27:22 +03:00
PasserDreamer
9f2c473040 Add files via upload
Add zh-TW locale.
2024-05-18 17:23:54 +08:00
PasserDreamer
a73db4984a Merge pull request #3 from PasserDreamer/patch-3
Update mediawiki-scrape.html
2024-05-18 17:21:45 +08:00
PasserDreamer
3498eb92bb Merge pull request #2 from PasserDreamer/patch-1
Update fandom-scrape.html
2024-05-18 17:17:25 +08:00
PasserDreamer
455db18d71 Merge pull request #1 from PasserDreamer/patch-2
Update index.html
2024-05-18 17:16:37 +08:00
PasserDreamer
2aeffe4095 Update mediawiki-scrape.html
fix typo
2024-05-18 15:58:32 +08:00
PasserDreamer
c4fe9749d5 Update index.html
Correct typos and standardize the capitalization of repeated terms.
2024-05-18 15:52:42 +08:00
PasserDreamer
a0512585b1 Update fandom-scrape.html
fix missing qoutes.
2024-05-18 15:34:05 +08:00
Cohee
c52bdb9a4a Use new command names in examples 2024-05-17 20:59:00 +03:00
Cohee
bbd9c89357 Add aliases for group member commands 2024-05-17 20:57:03 +03:00
Cohee
fb2190ace1 #2254 Don't suppress abort in subcommands 2024-05-17 18:21:13 +03:00
Cohee
deb09bf5bf Fix console errors on not found command autocomplete 2024-05-17 17:47:40 +03:00
Cohee
d951beb626 #2260 Handle window resize in script editor 2024-05-17 17:47:18 +03:00
Cohee
748dd5f2e6 Remove duplicate command registration 2024-05-17 15:04:53 +03:00
Cohee
75de4c8fcb Resolve boolean fields to constant values when searching WI entries with command 2024-05-17 14:32:57 +03:00
Cohee
432be09583 Merge pull request #2259 from Succubyss/staging
[Claude] Implements Assistant Impersonation Prefill
2024-05-17 11:15:37 +03:00
Succubyss
c822b9e2da Implements Assistant Impersonation Prefill 2024-05-16 21:59:58 -05:00
Cohee
c661fea07d #2227 Implement content scaffolding 2024-05-17 02:43:14 +03:00
Cohee
782f85e05d Merge pull request #2233 from Bronya-Rand/staging
feat: add default tip to welcome screen
2024-05-17 02:07:01 +03:00
Wolfsblvt
96a9f7108c Add slash commands for tag management 2024-05-17 01:01:51 +02:00
Bronya-Rand
9475147435 chore: adjustments to the bundle notice and update spanish locale 2024-05-16 17:45:42 -05:00
Cohee
909ec4191d Allow JS syntax in instruct activation regex 2024-05-17 01:14:07 +03:00
Cohee
59d00cca74 Allow multiple import of regex 2024-05-17 01:06:00 +03:00
Cohee
71a3e2c91b Don't modify response length when changing mancer model 2024-05-17 00:19:43 +03:00
Cohee
719202ba12 Merge pull request #2200 from Wolfsblvt/wi-regex-keys
WI regex keys
2024-05-17 00:06:07 +03:00
Cohee
8ae4332110 Fix secondary key expansion 2024-05-17 00:03:41 +03:00
Cohee
964f53273c Emit event after edited message is updated 2024-05-16 23:13:11 +03:00
Cohee
9e10022014 I want my 5px back... 2024-05-16 23:11:59 +03:00
Cohee
2eaabe13e3 Merge branch 'staging' into wi-regex-keys 2024-05-16 22:18:32 +03:00
steve green
3389b5dd16 allow custom source url (#2255)
* Update script.js to allow custom source url

* type hint fix

* fixes
2024-05-16 22:16:57 +03:00
Cohee
3832afaeba Merge pull request #2257 from hexa4ce/staging
Add support for new characterhub.org url (chub.ai legacy site)
2024-05-16 22:11:10 +03:00
Cohee
e026ddf6be Merge pull request #2246 from Wolfsblvt/tag-folders-folder-filter
Tag folders folder filter
2024-05-16 22:08:28 +03:00
hexa4ce
116fa673c6 Added support for new characterhub.org url (former chub.ai) for character imports 2024-05-16 20:07:52 +02:00
Cohee
517da9f972 TTS Rate: fix settings load. Hide when System source. 2024-05-16 09:35:48 +03:00
Cohee
74256dc411 Merge pull request #2249 from Surye/staging
Added TTS Audio Playback Speed Config
2024-05-16 09:35:04 +03:00
Vincent Castellano
1b23a62c13 Added TTS Audio Playback Speed Config 2024-05-15 23:16:25 -07:00
Wolfsblvt
97de520f9a Fix switchy button to top-right 2024-05-16 00:25:11 +02:00
Wolfsblvt
a6333f3285 Sort tag folder inline avatars too 2024-05-16 00:05:03 +02:00
Cohee
012f70336f Prevent header from jumping a few pixels when switching from list to character view 2024-05-16 01:02:22 +03:00
Cohee
7fbed26c26 #2245 Fix custom group avatar display 2024-05-16 00:49:26 +03:00
Wolfsblvt
a94af2678b Re-enable autofit height on plaintext key input 2024-05-15 23:47:48 +02:00
Cohee
eb57289b2a Non-Chromium browsers require padding to wrap around 2024-05-16 00:38:32 +03:00
Wolfsblvt
068b542c50 Tag folders "onboarding" icon (: 2024-05-15 23:37:18 +02:00
Cohee
912fd36e29 Set proper height of fancypants button instead of 100% 2024-05-16 00:32:58 +03:00
Cohee
b7a91770dc Set line height for select2 search textarea
Prevent height from jumping around when input is focused
2024-05-16 00:28:35 +03:00
Cohee
f0af503b4a Transition to full opacity on hover 2024-05-16 00:22:09 +03:00
Cohee
3d023a5cf6 Add opacity to fancypants switch button
Want it to be more subtle yet still visible
2024-05-16 00:18:27 +03:00
Cohee
1e2d1aa118 Merge branch 'staging' into wi-regex-keys 2024-05-16 00:12:51 +03:00
Cohee
ccfd3606dc Msg. to Load step 25 => 5 2024-05-16 00:05:28 +03:00
Cohee
fe95e09c8b Merge pull request #2244 from Wolfsblvt/rename-chat-command
Add /renamechat slash command
2024-05-15 23:47:12 +03:00
Cohee
bac90edfad Merge branch 'staging' into wi-regex-keys 2024-05-15 23:40:19 +03:00
Wolfsblvt
b7043a428f Add /renamechat slash command 2024-05-15 22:39:32 +02:00
Cohee
5b47b83fe2 Merge pull request #2238 from Succubyss/staging
Typo fix for /while example
2024-05-15 23:35:19 +03:00
Cohee
7289ed72f8 #2240 Yes, I can add new maker suite model 2024-05-15 23:31:09 +03:00
Cohee
c4936ed535 Properly colorize webkit search cancel button 2024-05-15 23:06:10 +03:00
Cohee
8a5f05fb74 Merge pull request #2242 from LenAnderson/fix-qr-editor-narrow
fix QR editor on narrow screen
2024-05-15 23:05:24 +03:00
LenAnderson
36f7bc4aae reduce qr editor small screen height 2024-05-15 15:41:26 -04:00
Cohee
d5869e3f90 Fix isolated modules not being loaded 2024-05-15 17:21:51 +03:00
Cohee
3b83d081db Merge pull request #2243 from steve02081504/patch-3
add import tags in list of more opt
2024-05-15 17:15:14 +03:00
steve green
6861135925 Discard changes to public/scripts/power-user.js 2024-05-15 22:02:28 +08:00
LenAnderson
b6f47c9927 prevent overflow of command results 2024-05-15 09:51:23 -04:00
steve02081504
796cc3c60c add import_tags in list of more opt 2024-05-15 21:34:09 +08:00
steve green
f1a57d76a2 enable import_card_tags by default 2024-05-15 21:27:50 +08:00
LenAnderson
87b61f7cff fix QR editor on narrow screen 2024-05-15 08:27:22 -04:00
Cohee
1999f607d6 Set tainted flag metadata if a message was edited 2024-05-15 13:19:23 +03:00
Wolfsblvt
bb2f553c46 Tag Folders folder filter showing only folders 2024-05-15 02:06:11 +02:00
Wolfsblvt
6c2dc6756b Merge branch 'wi-regex-keys' of https://github.com/Wolfsblvt/SillyTavern into wi-regex-keys 2024-05-15 00:39:02 +02:00
Wolfsblvt
f7c12264e8 Fix select2 local ajax breaking 2024-05-15 00:38:48 +02:00
Succubyss
9ef3dea884 rule typo fix for /while example 2024-05-14 17:36:10 -05:00
Cohee
cd90e252bf Add 1px height 2024-05-15 01:23:44 +03:00
Cohee
ac2475fb26 Merge branch 'staging' into wi-regex-keys 2024-05-15 01:21:45 +03:00
Wolfsblvt
8f1a959da1 WI key input default to plaintext 2024-05-15 00:19:09 +02:00
Cohee
8c55e1b05b Merge pull request #2231 from Wolfsblvt/wi-multiple-inlcusion-groups
WI support multiple inclusion groups
2024-05-15 01:07:57 +03:00
Cohee
a0bbee8b79 Merge branch 'staging' into wi-multiple-inlcusion-groups 2024-05-15 00:54:27 +03:00
Cohee
caf85ad040 Merge pull request #2229 from Wolfsblvt/wi-delay-until-recursion
WI entry setting "Delay until recursion"
2024-05-15 00:43:52 +03:00
Cohee
3999bee482 Replace UI hint text
I just don't like "checking" twice in one sentence
2024-05-15 00:39:26 +03:00
Cohee
f0016b5368 Fallback for old safari 2024-05-14 23:43:17 +03:00
Cohee
1dec93de8a Fallback for old safari 2024-05-14 23:28:52 +03:00
Cohee
caf236d60a Add event for chat completion settings ready 2024-05-14 22:34:40 +03:00
Cohee
c8ed8e06f1 Unset doc-height for body 2024-05-14 22:13:00 +03:00
Cohee
aa845b4727 Cancel message deletion with Escape 2024-05-14 22:06:38 +03:00
Cohee
1ebe5547d4 Revert themed coloring for message deletion highlight 2024-05-14 22:05:04 +03:00
Cohee
e2e7d5870a Export a function for renaming an active chat 2024-05-14 21:15:01 +03:00
Cohee
ea45d372f3 Reformat es-es for cleaner diff 2024-05-14 19:57:31 +03:00
Cohee
3113109f0a Use a proper tokenizer for GPT-4o 2024-05-14 15:30:11 +03:00
Bronya-Rand
6ec51ff086 chore: slight english updates and spanish translation cuz I know it 2024-05-14 10:02:04 +01:00
Azariel Del Carmen
9eae4d9739 Merge branch 'SillyTavern:staging' into staging 2024-05-14 10:47:08 +01:00
Bronya-Rand
84aa746241 feat: add default asset tip to welcome screen 2024-05-14 09:46:37 +01:00
Wolfsblvt
00ce078630 WI key input mode switch fancy/plaintext
- Implemented switch between fancy and plaintext input controls
- Fixed splitting keys into regexes index issue
- Fixed focus falsely adding text as key
2024-05-14 04:51:22 +02:00
Wolfsblvt
5426431adf Merge branch 'staging' into wi-regex-keys 2024-05-14 01:56:36 +02:00
Wolfsblvt
726ec0fbfc WI automation/inclusionGroup not autocompleting to itself 2024-05-14 00:54:43 +02:00
Wolfsblvt
094fc1f24b WI allow multiple inclusion groups on a single entry 2024-05-14 00:16:41 +02:00
Cohee
49cb8daf7d Add inline image control 2024-05-14 01:08:31 +03:00
Cohee
16660e995e Merge pull request #2230 from deffcolony/staging
Update ko-kr.json
2024-05-14 00:21:31 +03:00
deffcolony
8469f43285 Update ko-kr.json
checked by WolfCat (pk2381)
2024-05-13 22:48:21 +02:00
Wolfsblvt
6865f84eb1 Someone forgot preventRecursion default value 2024-05-13 22:39:21 +02:00
Wolfsblvt
036603c9e9 WI entry setting "Delay until recursion" 2024-05-13 22:33:25 +02:00
Cohee
ab0f57aba3 Merge pull request #2228 from LenAnderson/parser-fix-sort
fix autocomplete sort
2024-05-13 23:18:33 +03:00
Cohee
13c755c197 Compact summary log 2024-05-13 23:17:28 +03:00
LenAnderson
5250d1fcaf fix sort 2024-05-13 16:16:52 -04:00
Cohee
492f857012 Remove console spam 2024-05-13 23:16:33 +03:00
Cohee
f1a0462ca3 Fix toast msg in SD command 2024-05-13 23:11:07 +03:00
Cohee
6254ac6fbf Merge pull request #2226 from LenAnderson/parser-improvements
Parser improvements
2024-05-13 22:57:29 +03:00
Cohee
5207b3a7f0 Role filter conditions 2024-05-13 22:45:06 +03:00
Cohee
9e968de4e4 Merge pull request #2225 from kingbased/staging
gpt-4o
2024-05-13 22:30:31 +03:00
LenAnderson
e3edb96568 add force hide and auto hide of autocomplete details 2024-05-13 14:56:36 -04:00
LenAnderson
dded42374c show command details as argument and enum details 2024-05-13 14:56:17 -04:00
Cohee
c561fb4fab Don't check for system flag in role filter 2024-05-13 21:40:01 +03:00
based
67610b9f7f gpt-4o 2024-05-14 04:37:36 +10:00
Cohee
14aa70eea8 Add role and hidden arguments to /messages commands 2024-05-13 21:36:55 +03:00
Cohee
28da838bd1 Add install to plugin manager script 2024-05-13 21:22:01 +03:00
Cohee
fd18e0cc78 #2192 Fix order of events in TTS/translate interaction 2024-05-13 18:53:54 +03:00
Cohee
2a30a74886 Merge pull request #2221 from steve02081504/patch-1
More worldinfo page sizes
2024-05-13 16:56:53 +03:00
Cohee
6130ebb6d9 Merge pull request #2223 from LenAnderson/parser-flags-doclinks
populate parser flag doclinks in user settings
2024-05-13 16:52:50 +03:00
Cohee
64500bfb37 Add rounding to textarea autofit 2024-05-13 16:48:35 +03:00
Cohee
cc077732c4 Sort assets alphabetically, highlight in the list 2024-05-13 16:29:34 +03:00
Cohee
cd47f3b238 Fix loading locale data 2024-05-13 15:56:37 +03:00
Cohee
297519c401 Default to 100% probability in external imported lorebooks 2024-05-13 15:24:16 +03:00
LenAnderson
09d410ec48 populate parser flag doclinks 2024-05-13 08:04:58 -04:00
steve green
1369025092 More worldinfo page sizes
I have a WIbook with more than 100 entries and it's a hassle to edit
So I want to raise the cap to 1000
2024-05-13 17:51:42 +08:00
Cohee
5147233391 Fix file attachment chunk size inconsistency 2024-05-13 00:27:32 +03:00
Cohee
38585cb6af Merge pull request #2218 from SillyTavern/default-content
Default content
2024-05-13 00:12:39 +03:00
Cohee
f53775d3f5 Fix img scaling 2024-05-13 00:11:14 +03:00
Cohee
14ba7fc646 Split downloadables list into sections 2024-05-13 00:05:32 +03:00
Cohee
5b7bfbaa98 Merge branch 'staging' into default-content 2024-05-12 23:48:49 +03:00
Len
1d75b98393 STscript Parser Rewrite (#1965)
* set isForced to true on input

* make floating auto-complete follow horizontal scrolling

* add callable closure vars

* changes to /let and /var for callable closures

* fix error message

* fix scope for closure arguments

* if should return the pipe result from closures

* use /run to call closures and no arguments on immediate closures

* throw exception from QRs window-function if no match

* when to show autocomplete vs info only

* autocomplete positioning

* autocomplete styling

* add theming to autocomplete (theme, dark, light)

* improve autocomplete show/hide logic and editor selection

* use blur tint color instead of chat tint color and use blur setting

* cleanup and docs

* use scope macros for QR args

* add enter to select autocomplete

* fix no executor found

* cleanup and comment

* fix alias list in help string

* fallback to empty string piped value if null or undefined

* fix typo

* blur textarea on ctrl+enter execute (and refocus after)

* stop executeSlashCommand if parser throws

* move /let and /var callbacks into functions

* switch textarea to monospace when value starts with slash

* add double pipe a pipe breaker

* fix /? slash

* remove some logging

* add "/:name" as shorthand for "/run name" after all

* move shit around

* fix error message

* use testRunShorthandEnd

* use parseQuotedValue and parseValue to determine name for "/:"

QR labels and set names can include spaces

* add some adjustments to make autocomplete work properly

some hint in there about "/:" would still be nice

* add autocomplete style  selector

* only strip quotes from subcommand if they are at both ends

* fix JSDoc

* escaping

* allow open quotes on dry run

* throwing shit at the wall for /: autocomplete

* escapes only for symbols

* clean up autocomplete

* improve performance

* fix scope macros

* remove unescaping of pipes

* fix macros in scope copy

* fix "/? slash"

* don't run parser for getNameAt if text has not changed

* fix options filter

* re-enable blur listener

* restore selection on non-replace select

* fix for escaping first character of value

* add support for {{pipe}} and {{var::}} closures

* add index support to var macro

* add scoped var macro to macro help

* more escape fixes

* reduce autocomplete render debounce

* cleanup

* restore old escape handling and parser flag for strict escaping

* fix "no match" autocomplete message

* add dummy commands for comments and parser flag

* fix type annotations

* somewhat safer macro replacements

* fix autocomplete select on blank / "no match"

* fix cutting off handled part in substitution

* add parser flag REPLACE_GETVAR

Replaces all {{getvar::}} and {{getglobalvar::}} macros with {{var::}}.
Inserts a series of command executors before the command with the macros that:
- save {{pipe}} to a var
- call /getvar or /getglobalvar to get the variable used in the macro
- call /let to save the retrieved variable
- return the saved {{pipe}} value

This helps to avoid double-substitutions when the var values contain text that could be interpreted as macros.

* remove old parser

* fix send on enter when no match

* deal with pipes in quoted values (loose escaping)

* add default parser flags to user settings

* allow quoted values in unnamed argument

* set parser flag without explicit state to "on"

* add click hint on parser error toast

* dirty more detailed cmd defs

* remove name from unnamed arg

* move autocomplete into class and floating with details

* replace jQuery's trigger('input') on #send_textarea with native events because jQuery does not dispatch the native event

* fix ctrl+space

* fix arrow navigation

* add comments

* fix pointer block

* add static fromProps

* fix up dummy commands

* migrate all commands to addCommandObject

* remove commented comment command

* fix alias in details

* add range as argument type

* switch to addCommandObject

* switch to addCommandObject

* fix height

* fix floating details position on left

* re-enable blur event

* use auto width for full details on floating autocomplete

* auto-size floating full details

* fix typo

* re-enable blur listener

* don't prevent enter when selected item is fully typed out

* add autocomplete details tooltips

* add language to slash command examples

* move makeItem into option and command and fix click select

* use autocomplete parts in /? slash

* fix alias formatting

* add language to slash command examples

* fix details position on initial input history

* small screen styles

* replace registerSlashCommand with detailed declarations

* put name on first line

* add missing returns

* fix missing comma

* fix alias display in autocomplete list

* remove args from help string

* move parser settings to its own section

* jsdoc

* hljs stscript lang

* add hljs to autocomplete help examples

* add missing import

* apply autocomplete colors to stscript codeblocks (hljs)

* add fromProps

* cache autocomplete elements

* towards generic autocomplete

* remove unused imports

* fix blanks

* add return types

* re-enable blur

* fix blank check

* Caption messages by id

* add aborting command execution

* fix return type

* fix chat input font reset

* add slash command progress indicator

* add missing return

* mark registerSlashCommand deprecated

* why??

* separate abort logic for commands

* remove parsing of quoted values from unnamed arg

* add adjustable autocomplete width

* revert stop button pulse

* add progress and pause/abort to QR editor

* add resize event on autocomplete width change

* add key= argument to all get vars

* refactoring

* introduce NamedArgumentAsignment

* add TODOs

* refactoring

* record start and end of named arg assignment

* refactoring

* prevent duplicate calls to show

* refactoring

* remove macro ac

* add secondary autocomplete and enum descriptions

* add syntax highlighting to QR editor

* add enum descriptions to /while

* add /let key=... to scope variable names

* add unnamed argument assignment class and unnamed argument splitting

* fix QR editor style

* remove dash before autocomplete help text

* add autocomplete for unnamed enums

* fix remaining dom after holding backslash

* fix for unnamed enums

* fix autocomplete for /parser-flag

* add parser-flag enum help

* fix type annotations

* fix autocomplete result for /:

* add colored autocomplete type icons

* collapse second line autocomplete help if empty

* mark optional named args in autocomplete

* fix when what

* remove duplicate debug buttons

* dispatch input on autocomplete select

* prevent grow from editor syntax layer

* add auto-adjust qr editor caret color

* remove text-shadow from autocomplete

* join value strings in /let and /var

* add /abort syntax highlight

* fix attempting secondary result when there is none

* rename settings headers and split autocomplete / stscript

* add parser flag tooltips

* add tooltips to chat width stops

* fix typo

* return clone of help item

* fix enum string

* don't make optional notice for autocomplete arguments smaller

* avoid scrollbar in chat input

* add rudimentary macro autocomplete

* strip macro from helptext

* finally remove closure delimiters around root

* cleanup

* fix index stuff for removed closure delimiters

* fix type hint

* add child commands to progress indicator

* include sub-separator in macro autocomplete

* remove all mentions of interruptsGeneration and purge

* remove unused imports

* fix syntax highlight with newline at end of input

* cleanup select pointer events

* coalesce onProgress call

* add regex to STscript syntax highlighting

* fix closure end

* fix autocomplete type icon alignment

* adjustments for small screens

* fix removing wrong element

* add missing "at=" arg to /sys, /comment, /sendas

* add font scale setting for autocomplete

* add target=_blank for parser flag links

* fix for searching enums

* remove REGEXP_MODE from hljs
just causes trouble

* fix autocomplete in closures

* fix typo

* fix type hint

* Get rid of scroll bar on load

* Add type hint for /send name argument. Fix 'at' types

* Add 'negative' arg hint to /sd command

* reenable blur event

* Allow /summarize to process any text

* Compact layout of script toggles

* Expand CSS by default

* fix double ranger indicator and adjust to narrow container

* make custom css input fill available vertical space

* reduce scroll lag

* use default cursor on scrollbar

* Clean-up module loading in index.html

* fix tab indent with hljs

---------

Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
2024-05-12 22:15:05 +03:00
Cohee
c7d75b7789 llamacpp broke 2024-05-12 21:41:07 +03:00
Cohee
4ccedb939c Merge pull request #2204 from steve02081504/patch-2
impl `{{char_version}}`
2024-05-12 17:57:37 +03:00
Cohee
4bb463dd56 Remove unused macros 2024-05-12 17:55:00 +03:00
Cohee
0ba600bb2b Code clean-up 2024-05-12 17:53:38 +03:00
Cohee
9ed6ee2161 Sample Character browser to onboarding 2024-05-12 16:43:09 +03:00
Cohee
c4ade296ae Rotate Flux the Cat to downloadable content index 2024-05-12 15:09:00 +03:00
Cohee
0ed81e3b1a Rotate Coding Sensei to downloadable content index 2024-05-12 14:49:13 +03:00
steve02081504
d1933be86a remove {{char_version_url_encoded}} 2024-05-12 09:04:50 +08:00
steve02081504
15ff8de45c add template 2024-05-12 09:04:03 +08:00
steve02081504
45a080016e move to env 2024-05-12 09:01:30 +08:00
steve02081504
78cf6e9086 Merge branch 'staging' into pr/2204 2024-05-12 08:51:47 +08:00
Cohee
7d65a6e264 Add content manager config notice 2024-05-12 00:44:46 +03:00
Cohee
91945ec77e Fix misleading JSDoc 2024-05-12 00:22:36 +03:00
Cohee
7b472f13af Require a ping call before generation 2024-05-12 00:18:56 +03:00
Cohee
a93777e3b7 (chore) JSDoc comment 2024-05-11 23:38:26 +03:00
Cohee
2f310c72fa Merge pull request #2215 from Hydroerotic/staging
Added {{timeDiff}} macro.
2024-05-11 23:35:55 +03:00
Cohee
6a4ee68113 Message delete highlight color follows theme settings 2024-05-11 17:17:42 +03:00
Cohee
e73b5713fd Add types for moment 2024-05-11 14:49:11 +03:00
Hydroerotic
1f81086a21 Update macros.js 2024-05-11 14:13:03 +03:00
Hydroerotic
3e48f4b805 Update macros.html 2024-05-11 13:55:15 +03:00
Hydroerotic
432be2ee57 Update macros.js 2024-05-11 13:52:31 +03:00
steve green
f421139402 Create char-data.js for type hint (#2209)
* Create char-data.js for type hint

code from 7df0d1e06d/src/charData.mjs

* add hint

* fixes

* `class` -> JSdoc typedef by AI

* use `import`

* `v2DataWorldInfo`

* Rename book typedef

* Fix type errors

---------

Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
2024-05-11 12:05:13 +03:00
RossAscends
081223cc8f Merge pull request #2212 from bdashore3/token-ban-upgrades
min_length and strict string ban for Tabby
2024-05-11 17:39:05 +09:00
Cohee
27ccc6b090 Minor stylistic changes 2024-05-11 11:38:22 +03:00
kingbri
62faddac8d Textgen: Add banned_strings
TabbyAPI supports the ability to ban the presence of strings during
a generation. Add this support in SillyTavern by handling lines
enclosed in quotes as a special case.

Signed-off-by: kingbri <bdashore3@proton.me>
2024-05-11 00:58:29 -04:00
kingbri
6804e4c679 Index: Expose min_tokens for TabbyAPI
Now supports the minimum amount of tokens to generate.

Signed-off-by: kingbri <bdashore3@proton.me>
2024-05-10 23:19:57 -04:00
Cohee
10ee002091 Merge pull request #2208 from Wolfsblvt/global-d-ts-expanded
Expand global.d.ts with code docs
2024-05-10 23:11:08 +03:00
Cohee
1430eb26ea Delete toastr types.
@Wolfsblvt add it back if needed.
2024-05-10 23:05:14 +03:00
Cohee
e17a18ad5d #2210 Replace line breaks with <br> in unknown elements 2024-05-10 15:59:01 +03:00
Wolfsblvt
eeaa52bf5d Expand global.d.ts with code docs
- docs and syntax for toastr
- docs and syntax for Fuse
- docs and syntax for select2
- docs and syntax for sortable
2024-05-10 04:48:30 +02:00
Cohee
07a6cb1252 Merge pull request #2205 from deffcolony/staging
move expression buttons above Sprite set
2024-05-10 02:24:11 +03:00
steve02081504
7a2f6fb63f {{char_version_url_encoded}} and macro replace in creator_notes_spoiler 2024-05-10 07:24:05 +08:00
Wolfsblvt
bb3ac095c4 WI key input allow click to edit
- Allow click on WI keys to edit
- Removes them from the key list, allowing direct text editing
- Refactor select2 click subscribe some more, fixing a few issues
2024-05-10 00:42:35 +02:00
Wolfsblvt
e18d554489 Refactor select2 choice click event to utils 2024-05-09 23:30:18 +02:00
Wolfsblvt
a2625ecec6 Merge branch 'staging' into wi-regex-keys 2024-05-09 22:54:27 +02:00
deffcolony
0252b21901 move expression buttons above Sprite set
+moves the buttons above the Sprite set so user does not have to scroll all the way down to find the buttons now it will be directly visiable
2024-05-09 16:33:04 +02:00
steve green
f6343436b4 impl {{char_version}}
https://github.com/SillyTavern/SillyTavern/issues/2111
2024-05-09 22:29:48 +08:00
Cohee
9db2f1cb91 #2203 Fix copy not working in hidden messages 2024-05-09 16:07:13 +03:00
Cohee
2fd59f5aef Merge pull request #2201 from Wolfsblvt/wi-no-doube-substitution
Do not double-parse WI content on "always on"
2024-05-09 15:31:14 +03:00
Cohee
18d96bc346 Merge branch 'staging' into wi-no-doube-substitution 2024-05-09 15:11:16 +03:00
Cohee
535da63e52 Merge pull request #2199 from steve02081504/patch-4
remove empty entrys from WIs
2024-05-09 15:05:25 +03:00
Cohee
e14c9506b6 Revert entry post-process 2024-05-09 14:55:09 +03:00
Cohee
88aae5978f Simplify condition. Add debug log 2024-05-09 14:53:17 +03:00
Wolfsblvt
b9d72bfdf4 Do not double-parse WI content on "always on" 2024-05-09 06:37:14 +02:00
Wolfsblvt
f4bb4fe51e Merge branch 'staging' into wi-regex-keys 2024-05-09 04:23:14 +02:00
Wolfsblvt
cf77b9e7ee WI regex key syntax highlighting 2024-05-09 03:35:36 +02:00
steve green
ff1399d1ba remove empty lines from WIs 2024-05-09 08:30:40 +08:00
Wolfsblvt
eb273a1873 WI key dropdown templating shows all keys
- Cache all keys for the loaded lorebook
- Key selection dropdown shows all keys and how often they are used already
- More templating changes
2024-05-08 20:34:53 +02:00
Cohee
0587931cae Merge pull request #2193 from steve02081504/patch-2
also run regex on worldinfo
2024-05-08 21:15:39 +03:00
Cohee
7408673e41 Back to const 2024-05-08 20:59:59 +03:00
Cohee
3c0664dfb6 Fix naming and improve regex application 2024-05-08 20:10:52 +03:00
steve green
d2477bba0c also run regex on worldinfo 2024-05-08 23:41:18 +08:00
Cohee
f37e444791 Merge pull request #2194 from LenAnderson/remove-hljs-innerText
don't replace codeblock textContent with innerText in hljs
2024-05-08 18:13:02 +03:00
LenAnderson
1ae08f49c5 don't replace codeblock textContent with innerText in hljs 2024-05-08 11:03:05 -04:00
Cohee
10fda0b220 Merge pull request #2191 from bdashore3/example-wi
mesExamples anchor for World Info
2024-05-08 15:33:45 +03:00
Cohee
61d5dde497 Fix line break 2024-05-08 15:13:12 +03:00
Cohee
edc33584da Fix extra & in title 2024-05-08 15:12:31 +03:00
Cohee
883da48762 Prefer const variables 2024-05-08 15:10:53 +03:00
Cohee
49d0d9f557 Fix return type on empty WI 2024-05-08 15:04:17 +03:00
Cohee
852bc15a94 Update title and i18n 2024-05-08 14:10:34 +03:00
Cohee
c43ddd9d62 Merge pull request #2178 from Yokayo/staging
Localization enhancements
2024-05-08 13:59:52 +03:00
Cohee
c851961234 Update movingUI i18n attr 2024-05-08 13:58:54 +03:00
kingbri
01aacb9280 World Info: Add example messages insertion point
Allow insertion above and below mesExamples (also known as the
"examples of dialogue") box.

Signed-off-by: kingbri <bdashore3@proton.me>
2024-05-07 21:55:26 -04:00
Yokayo
dfa8c6c3d4 Remove cases 2024-05-07 13:11:59 +07:00
Wolfsblvt
fda0e886e4 WI custom styling for regex keys
- WI custom style for regex keys
- moved select2 styling to its own file
2024-05-07 05:44:18 +02:00
Wolfsblvt
5a45e64999 Regex matching for keys and secondary keys 2024-05-07 02:52:22 +02:00
Wolfsblvt
70a2f71e33 WI switch key controls to multi input
- Switch key/secondarykey controls to select2 input
- Custom tokenizer for regex parsing, allowing comma in regex
- Keep mobile-compatibility by switching to textarea
- select2 utility method to pre-fill options
- New inline display mode of select2 multi to save space
2024-05-07 02:01:54 +02:00
Cohee
4a70e68c22 Add ping endpoint 2024-05-07 01:27:17 +03:00
Cohee
542018cecb Fix group weight clamping 2024-05-06 22:55:31 +03:00
Cohee
5e7c214c89 Substitute macros in text to classify 2024-05-06 22:02:13 +03:00
Cohee
12eabd167d #2187 Add negative prefix arg to /sd command 2024-05-06 21:39:07 +03:00
Cohee
c73bfbd7b0 Safari bruh moment 2024-05-06 21:21:03 +03:00
Cohee
7063fce2af Selectable openrouter providers 2024-05-06 19:26:20 +03:00
Cohee
694cf6f762 Fix field name 2024-05-06 17:27:43 +03:00
Cohee
83c77c1f18 Split group weight and trigger% 2024-05-06 17:00:42 +03:00
Cohee
d54ccece5c Merge pull request #2184 from bdashore3/fixed-slash-command
Fix the /send command
2024-05-06 16:25:09 +03:00
kingbri
96506947cb Messages + Instruct: Fix /send with name= if name is empty
If name is empty, don't try appending it to the message. This applies
for both instruct and non-instruct modes.

Signed-off-by: kingbri <bdashore3@proton.me>
2024-05-05 20:35:03 -04:00
Cohee
afc3071576 Add name argument for /send.
Tech debt: move to new STscript branch
2024-05-06 01:18:59 +03:00
Cohee
10727d9a02 Add plugins update script 2024-05-06 00:49:00 +03:00
Cohee
55d31a976f Add generic mediawiki downloader 2024-05-05 22:26:13 +03:00
Cohee
181b5aff97 Add Groq as chat completion source 2024-05-05 18:53:12 +03:00
Cohee
31f1b34911 Add Perplexity L3-sonar models 2024-05-05 18:21:58 +03:00
Cohee
ef5499c8dc Remove disable attribute from send_textarea. It never worked. 2024-05-05 14:27:20 +03:00
Cohee
292ecf580e #2182 Add quiet prompts to WI buffer 2024-05-05 14:23:54 +03:00
Cohee
de1ca9af74 Add type casts 2024-05-05 13:51:16 +03:00
Cohee
75832c1ad6 Fix group removal 2024-05-05 01:08:49 +03:00
Cohee
39a54d158d Per-entry group scoring 2024-05-05 00:42:33 +03:00
Cohee
2bf9869e5f Add WI group scoring mode 2024-05-04 23:51:28 +03:00
Cohee
b13434c505 Merge branch 'release' into staging 2024-05-04 20:45:48 +03:00
Cohee
5197809d6b Add global variables to TypeScript definitions. Fix some errors 2024-05-04 20:44:41 +03:00
RossAscends
204a934553 update coding sensei with proper codeblock format 2024-05-05 00:06:46 +09:00
Cohee
de1bb90c23 #2180 Add critical style tag 2024-05-04 15:36:25 +03:00
Cohee
478be72659 #2181 Unstuck moving images 2024-05-04 14:20:59 +03:00
Cohee
d972ed5a2b #2180 Add preload for style 2024-05-04 14:02:53 +03:00
RossAscends
932d3dc10c fix tabby sampler viz, clarify grammar title 2024-05-04 15:10:47 +09:00
Cohee
f610d5930c Update readme.md 2024-05-04 02:50:41 +03:00
Cohee
05db2552b3 Fix Top K disabled state for Infermatic.
Also an icon.
2024-05-04 02:37:05 +03:00
Cohee
7bfd666321 Add llama 3 tokenizer 2024-05-03 23:59:39 +03:00
Yokayo
a02f5ead7e Some localization fixes 2024-05-04 03:37:55 +07:00
Yokayo
d9fae7a02c Fix tabs 2024-05-04 02:52:19 +07:00
Yokayo
29e2e8f607 Add localizable strings to two extensions & translate them 2024-05-04 02:44:15 +07:00
Cohee
7bc87b6e28 8x22b is supposed to have 64k tokens 2024-05-03 21:36:41 +03:00
Cohee
ed65ddf981 Add Open Mixtral 8x22b model 2024-05-03 20:22:17 +03:00
Cohee
3c2017c7b8 Fix Mistral's Chat Completion moment 2024-05-03 20:22:03 +03:00
Cohee
6b2b849a26 Add server history connect missing attributes 2024-05-03 20:05:42 +03:00
Cohee
b69c5bcd17 Merge pull request #2177 from sasha0552/vllm-support
vLLM support
2024-05-03 20:03:05 +03:00
Cohee
7b87f44518 Clean-up API-specific settings 2024-05-03 20:02:13 +03:00
RossAscends
5ab1e74c5f WIP Debug Menu button to copy API setup to clipboard. 2024-05-03 16:35:32 +09:00
sasha0552
2bd239fe81 Initial vLLM support 2024-05-02 22:40:40 +00:00
Cohee
1a219e32fe Switch sheld to use flex layout 2024-05-02 23:15:17 +03:00
Cohee
203146f7e2 Remove borders from scrollbar gutters.
Leave for moving UI vertical
2024-05-02 23:06:16 +03:00
Cohee
73bea1f454 Merge pull request #2175 from Wolfsblvt/wi-search-quickselect
WI world search allows quick-select
2024-05-02 22:51:24 +03:00
Cohee
408151c9cb Use menu_button style colors 2024-05-02 22:50:07 +03:00
Wolfsblvt
f0adbc3c28 WI world search allows quick-select 2024-05-02 20:04:24 +02:00
Cohee
9acf057aae Fix reset on generic draggables 2024-05-02 01:34:47 +03:00
Cohee
f796387e7e Limit background title height 2024-05-01 23:43:11 +03:00
Cohee
9f1c306920 I really have to spell it out 2024-05-01 23:42:50 +03:00
Cohee
2f85e50c6f Merge pull request #2171 from 24adamcho/generic-card-download
Character card import from generic sources (specifically Discord, Catbox.moe)
2024-05-01 19:58:17 +03:00
Cohee
eb4cae4e6d Add WL to config. Code clean-up. 2024-05-01 19:52:34 +03:00
Cohee
e4e6882f12 Fix scroll to bottom on chat open 2024-05-01 14:56:55 +03:00
Cohee
15a288b63d Remove shadow from top bar drawers 2024-05-01 14:52:17 +03:00
Cohee
620cd6dfc2 Move persona functions from script. Clean-up exports 2024-05-01 14:03:24 +03:00
Cohee
a5475e7752 Merge pull request #2165 from Wolfsblvt/scored-search-sorting
Scored search sorting
2024-05-01 13:44:08 +03:00
Cohee
bddfd5763b Fix persona filter rule 2024-05-01 12:49:53 +03:00
Cohee
21edb655d3 Merge branch 'staging' into scored-search-sorting 2024-05-01 12:48:26 +03:00
Cohee
51f0d1f33e Merge pull request #2169 from Wolfsblvt/fix-wi-whole-world
Fixes WI word matching not working for non-words
2024-05-01 11:07:31 +03:00
Cohee
da31b6fda8 Merge pull request #2170 from Wolfsblvt/wi-panel-performance
Improve performance of drawing WI panel
2024-05-01 10:31:41 +03:00
Cohee
2b071bed90 Format style 2024-05-01 10:25:42 +03:00
Cohee
1cf935eaf3 Merge pull request #2168 from Wolfsblvt/world-info-search-resizing
WI search bar now flexibly scales width
2024-05-01 10:24:08 +03:00
Wolfsblvt
b33b5264e5 Improve performance of drawing WI panel
- Fix performance issue by unsubscribing events before redrawing the panel
2024-05-01 02:08:52 +02:00
Wolfsblvt
8ca50098d5 Fixes WI word matching not working for non-words
- Fixes the regex that matched WI keys as "whole words" not working correctly if the key itself was not a word
2024-04-30 23:51:47 +02:00
Wolfsblvt
d82ed50fa4 Enable unix-like extended search for fuzzy search 2024-04-30 23:12:52 +02:00
Wolfsblvt
f894237a12 Tweaked weighting scores more 2024-04-30 22:12:49 +02:00
Wolfsblvt
9d8ebd7bd2 WI search bar now flexibly scales width 2024-04-30 21:35:27 +02:00
Adam
5c552a3d53 added examples for catbox/discord import links 2024-04-29 23:44:15 -05:00
Adam
300b68177b #1958 added support for generic url downloading (current whitelist: discordapp.com, catbox.moe) 2024-04-29 23:42:50 -05:00
Wolfsblvt
83f79c1466 Fix non-fuzzy char search
- Utilize new utility function that checks insensitive and without accents
2024-04-30 06:03:41 +02:00
Wolfsblvt
bc94e3992f Modify weightings for fuzzy group search 2024-04-30 05:40:31 +02:00
Wolfsblvt
1c44df8079 Modify weightings for fuzzy WI search 2024-04-30 05:30:24 +02:00
Wolfsblvt
b6b1df6a7c Fuzzy char search searches tags too 2024-04-30 05:14:01 +02:00
Wolfsblvt
b4aa7831e7 Scored search sorting for char list 2024-04-30 04:30:39 +02:00
Wolfsblvt
d1cdd60883 Scored search sorting for personas 2024-04-30 02:27:44 +02:00
Wolfsblvt
a850352eab Scored search sorting for world info 2024-04-30 01:39:47 +02:00
Cohee
d9d76ba16d #2164 Add error toasts to VecStore 2024-04-30 00:17:39 +03:00
Cohee
993284f9c1 #2164 Disable-able data bank attachments 2024-04-30 00:06:14 +03:00
Cohee
a7d3130f9a Remove non-existent foreign lorebook extensions 2024-04-29 15:33:56 +03:00
Cohee
e0df5783f8 Allow macros in positive and negative prompts 2024-04-29 13:50:55 +03:00
Cohee
e4de6da5b8 Add server plugin support for MS Edge TTS 2024-04-29 01:07:19 +03:00
Cohee
87219f897e Check that char.list has any filters before applying hidden block. 2024-04-28 21:33:37 +03:00
Cohee
73cf58826f Pause autoplay on external media removal 2024-04-28 20:11:58 +03:00
Cohee
be4637a3a0 Handle <br> in message texts with Showdown instead of manually 2024-04-28 20:00:22 +03:00
Cohee
6ac6c7cfda #2159 Move debounce constants to a separate module 2024-04-28 19:47:53 +03:00
Cohee
94e9b8f4b1 Merge branch 'staging' of https://github.com/SillyTavern/SillyTavern into staging 2024-04-28 19:29:12 +03:00
Cohee
bc6149deeb Merge pull request #2158 from racinmat/racinsky/itemization
refactor: prompt itemization split to multiple functions
2024-04-28 18:55:10 +03:00
Cohee
a0d975c3c0 Add bottom margin to in-chat tables 2024-04-28 18:39:57 +03:00
Cohee
d51b155e52 Add ability for extensions to intercept edited message text 2024-04-28 18:39:32 +03:00
Cohee
fb1b327f9a [skip ci] ESLint 2024-04-28 16:58:28 +03:00
Matěj Račinský
754cdc4d58 refactor: prompt itemization split to multiple functions 2024-04-28 14:09:10 +02:00
Cohee
a73cb9ad3d Merge pull request #2154 from Bronya-Rand/staging
chore: disable merge conflict workflow on forks
2024-04-28 14:50:28 +03:00
Cohee
58ecc0dc0d Merge pull request #2155 from Wolfsblvt/fix-bogus-folder-select
Fix bogus folder not working if tag was cut off
2024-04-28 14:43:07 +03:00
Cohee
3821e91be0 Merge pull request #2156 from Wolfsblvt/debounce-some-searches
Debounce WI, Character and Persona search + common debounce timeouts
2024-04-28 14:38:50 +03:00
Cohee
de2bb7938a Utilize import for vector store 2024-04-28 14:35:35 +03:00
Wolfsblvt
61e2877c4b Debounce Character and Persona search 2024-04-28 06:27:55 +02:00
Wolfsblvt
d7ade487b8 Refactor common enum for debounce timeouts 2024-04-28 06:21:47 +02:00
Wolfsblvt
6d04e93f34 Debounce WI search 2024-04-28 05:42:15 +02:00
Bronya-Rand
d7a7af756a chore: disable docker publish on forks 2024-04-28 03:55:49 +01:00
Wolfsblvt
0c5fe3d637 Fix bogus folder not working if tag was cut off 2024-04-28 04:47:16 +02:00
Bronya-Rand
eb0a116cc7 chore: only allow merge conflicts to run in ST repo 2024-04-28 03:41:50 +01:00
Cohee
e08a21ebe7 Deprecate old /sendas syntax.
"name" arg is now required, but defaults to {{char}} for compatibility
2024-04-28 03:53:17 +03:00
Cohee
49074effce Merge pull request #2119 from Bronya-Rand/staging
feat: Third-Party Parser Support
2024-04-28 00:15:08 +03:00
Bronya-Rand
ffe8b3c909 chore: leftover cleanup 2024-04-27 22:09:11 +01:00
Bronya-Rand
7856afee92 chore: remove mihoyo scraper 2024-04-27 22:08:38 +01:00
Bronya-Rand
fe533b7c7f chore: revert back to typedef 2024-04-27 22:01:15 +01:00
Azariel Del Carmen
fc158ca176 Merge branch 'staging' into staging 2024-04-27 21:49:02 +01:00
Cohee
f632888b4c Move scripts init at the end of HTML page 2024-04-27 23:44:08 +03:00
Bronya-Rand
8324632e4e chore: add iconAvailable to ScraperInfo 2024-04-27 21:43:53 +01:00
Bronya-Rand
be4b20af97 chore: remove mihoyo icon 2024-04-27 21:42:03 +01:00
Cohee
5a4e0a06e6 Better icon for YT captioner 2024-04-27 23:27:53 +03:00
Bronya-Rand
fb71d3b562 chore: remove miHoYo parser from first-party scrapers 2024-04-27 21:27:14 +01:00
Bronya-Rand
b96d1e79a0 feat: create proper classes and export for extension use 2024-04-27 21:26:39 +01:00
Cohee
0d310c434d Update FontAwesome 2024-04-27 23:25:35 +03:00
Cohee
b111834122 Insert custom prompts to the start of the list 2024-04-27 23:16:44 +03:00
Cohee
2847b5ee45 [skip ci] Fix format 2024-04-27 23:02:51 +03:00
Cohee
943906d8a3 Fix UTF-8 file name uploads
https://github.com/expressjs/multer/issues/1104
2024-04-27 22:58:32 +03:00
Cohee
cbedfa4664 Use atomic write 2024-04-27 22:02:04 +03:00
Cohee
01ccc32274 Cache config.yaml reads 2024-04-27 21:59:57 +03:00
Cohee
3b153a6c9b Check that path exists before serving 2024-04-27 21:54:28 +03:00
Cohee
1bcdc2652c Split pre and post listen setup tasks. Only shutdown plugins once 2024-04-27 21:41:32 +03:00
Cohee
ea050b98ef Merge pull request #2150 from evpeople/release
add a button to translate input message
2024-04-27 21:23:26 +03:00
Cohee
b30d69b2a6 Clean-up styles and JQuery use 2024-04-27 21:22:50 +03:00
Cohee
60e099e852 Clean-up diff pt.2 2024-04-27 21:15:44 +03:00
Cohee
c49b37f968 Clean-up diff 2024-04-27 21:11:41 +03:00
Cohee
404d9db359 Merge pull request #2147 from Wolfsblvt/wi-entry-inclusion-prio
World Info inclusion group prio toggle
2024-04-27 21:09:43 +03:00
Cohee
5ac0390446 Fix naming convention for LB extension fields 2024-04-27 21:03:55 +03:00
Cohee
6e98fb1c5e Clean-up debug logs 2024-04-27 20:42:49 +03:00
Cohee
053d7f9eaa Remove the /inject when value is empty 2024-04-27 20:25:55 +03:00
Cohee
5dcfda0514 Cut UI labels. Add expand to custom CSS 2024-04-27 20:02:30 +03:00
Cohee
b42125a654 Fix content index 2024-04-27 18:03:14 +03:00
Cohee
413cec8a9f Merge branch 'staging' into wi-entry-inclusion-prio 2024-04-27 18:00:00 +03:00
Cohee
8e7ffab793 Merge pull request #2149 from Wolfsblvt/duplicate-wi-entries
Button to duplicate WI entries
2024-04-27 17:57:59 +03:00
Cohee
770aee4953 Adjust title widths 2024-04-27 17:52:47 +03:00
Cohee
f479901c87 Merge pull request #2152 from Wolfsblvt/auto-sort-tags-option
Option to auto-sort tags (+UI improvements)
2024-04-27 17:45:23 +03:00
Cohee
1dbe7897d4 Prevent ticking if confirm canceled 2024-04-27 17:41:27 +03:00
Cohee
c95956766e Don't need a hack since you're not awaiting the popup 2024-04-27 17:33:52 +03:00
Cohee
e92c0db6a2 Merge pull request #2148 from HiroseKoichi/staging
Use names in place of roles for ChatML and LLama-3-Instruct
2024-04-27 17:17:22 +03:00
Hirose
3a8b8ed639 Skill Issue 2024-04-27 08:20:44 -05:00
Hirose
3a78d69b5b Use {{name}} macro, create new templates 2024-04-27 07:39:52 -05:00
Wolfsblvt
2e562d187a Option to auto-sort tags (+UI improvements)
- Toggle to auto-sort tags alphabetically
- Init auto-sort based on current sorted state, if not chosen before
- Tag management redraw list if changes happen
- Tag management highlight renamed rows on auto-sort if they get automatically reordered
- Manual drag&drop of tags disables auto-sort option
- Small fixes to popup tag management pop drawing
- Utility function to flash highlight via CSS
2024-04-27 10:26:01 +02:00
evpeople
4521dde455 add a button to translate input message 2024-04-27 13:46:13 +08:00
Wolfsblvt
b64b0e3362 Button to duplicate WI entries
- Add an option to duplicate a WI entry, copying everything besides UID
- moved UI move action on new WI entry to the UI function, not inside utility
2024-04-27 06:18:26 +02:00
RossAscends
f8ca73265b userSettings expandables get borders 2024-04-27 13:13:54 +09:00
RossAscends
1f7614af33 re-order/style User Settings Panel 2024-04-27 12:50:33 +09:00
Wolfsblvt
a48a9318c1 Add groupOverride to server endpoint too 2024-04-27 04:49:08 +02:00
Wolfsblvt
dcb042681d Change group prio name, add default value set 2024-04-27 04:40:35 +02:00
Wolfsblvt
7df2f7e752 WI inclusion groups will never roll for trigger% 2024-04-27 03:44:00 +02:00
Hirose
c3578d2cda Use names in place of role for ChatML and LLama-3-Instruct 2024-04-26 20:14:51 -05:00
Wolfsblvt
8db39a58fb World Info inclusion group prio toggle 2024-04-27 02:23:37 +02:00
Cohee
bbdbb08301 Fix main prompt clearing on disabling 2024-04-27 00:08:30 +03:00
Cohee
b06e09c030 Merge pull request #2131 from Yokayo/staging
Localization enhancements
2024-04-26 23:05:55 +03:00
Cohee
bb2bcdbf61 The dot went MIA 2024-04-26 23:04:11 +03:00
Cohee
2e278e7323 Fix missing localization for unknown locale 2024-04-26 22:57:42 +03:00
Cohee
4c9d52422b [chore] ESLint and JSDoc 2024-04-26 22:46:13 +03:00
Cohee
f4ba1f68ef Merge pull request #2136 from BlueprintCoding/release
Added import function for AICharacterCards.com cards
2024-04-26 22:42:04 +03:00
Cohee
12497e8fb1 Merge pull request #2141 from valadaptive/generate-cleanups-4
Clean up Generate(), part 4
2024-04-26 22:40:04 +03:00
Cohee
8153e747ef Merge pull request #2135 from johnflux/staging
Fix SillyTavern being launched from a different working directory
2024-04-26 22:06:40 +03:00
Cohee
63b597beb8 Fix node serve startup 2024-04-26 22:02:46 +03:00
Cohee
cdbb0b21da Merge pull request #2145 from sirius422/fix-regex-filename-non-eng-characters
Change the naming rule of regex exporting
2024-04-26 21:59:05 +03:00
Cohee
b2f40e490b Fix mobile-styles.css for waifuMode
Mobile bros want a waifu too
2024-04-26 21:51:28 +03:00
sirius422
a96e1903a3 Change the naming rule of regex exporting 2024-04-27 00:05:10 +08:00
Cohee
be7eb8b2b5 Merge pull request #2143 from aisu-wata0/style_mes_block_overflow_y
style: `.mes_block { overflow-y: clip; }`
2024-04-26 18:36:17 +03:00
Cohee
3b6372431a Merge pull request #2144 from sirius422/fix-json-export-extension
Add json extension to exported oai and LogitBias presets
2024-04-26 18:30:55 +03:00
sirius422
389ee7917f Add json extension to exported oai and LogitBias presets 2024-04-26 23:07:25 +08:00
Cohee
212e61d2a1 Lazy initialization of Claude tokenizer. Add JSDoc for tokenizer handlers 2024-04-26 15:17:02 +03:00
Cohee
1b60e4a013 Init user storage module before server listening 2024-04-26 14:09:40 +03:00
Aisu Wata
93cd93ada3 style: .mes_block { overflow-y: clip; } 2024-04-25 21:49:12 -03:00
Cohee
babb4cb57b Fix tag key for 0-index 2024-04-25 18:15:38 +03:00
valadaptive
dbcc75471f Refactor CFG prompt gen in getCombinedPrompt
We don't need to create the cfgPrompt variable unless useCfgPrompt is
true, so move it inside the if-block.
2024-04-25 09:09:30 -04:00
valadaptive
2a0497ca9e Only generate negative prompt for textgen API
The original comment mentions that we need to get the negative prompt
first since it "has the unmodified mesSend array", but we've cloned the
mesSend array since forever, so I don't think mutation is an issue
anymore.
2024-04-25 09:09:30 -04:00
valadaptive
2d0767306e Remove unnecessary cfgPrompt null-chains
We already check if cfgPrompt exists.
2024-04-25 09:09:30 -04:00
valadaptive
8ca83bb255 Extract CFG check 2024-04-25 09:09:30 -04:00
valadaptive
80a6406062 Don't reassign thisPromptBits
Instead, just use additionalPromptStuff where thisPromptBits was used
after the assignment.
2024-04-25 09:09:30 -04:00
valadaptive
ff9345a843 Make generate_data preparation a switch-case
We switch based on main_api. In the future, I'd like to move the
openai-specific token count stuff outside the switch case and extract
the generate_data preparation into its own function that we can pass
main_api into.
2024-04-25 09:09:30 -04:00
valadaptive
fe663c4f04 Move auto_adjust_response_length logic
This if-block only applies to Kobold Horde, so move it inside the Kobold
and Horde-specific case in the else-if chain.
2024-04-25 09:09:30 -04:00
Cohee
9fbb012697 Merge branch 'release' into staging 2024-04-25 12:56:17 +03:00
Cohee
0070950911 Revert grid view spacing 2024-04-25 12:26:21 +03:00
Cohee
62cf611fdc Merge pull request #2138 from Wolfsblvt/fix-expression-list-resolve
Fix expression list resolve
2024-04-25 11:00:34 +03:00
RossAscends
75814433a6 dont default to hiding avatars on new installs 2024-04-25 14:42:48 +09:00
RossAscends
e59a5b4449 toggle to hide chat avatars 2024-04-25 12:51:56 +09:00
Wolfsblvt
161e512805 Fix expression list resolve
- New expression api "LLM" still queried local classify model for expressions, fixed by returning default list
- Fixed failed API calls crashing Expressions extension
2024-04-25 04:29:20 +02:00
Blueprint Coding
305afb3713 Added import function for AICharacterCards.com cards
Added ability to import cards directly from aicharactercards.com via it's api like Chub and Janny.
Video of it in action: https://streamable.com/gbfdtw

Just pass the last two slash vars from the url (the author and card title) from a page. EX: aicharcards/the-game-master to:
https://aicharactercards.com/wp-json/pngapi/v1/image/

In this example: https://aicharactercards.com/wp-json/pngapi/v1/image/aicharcards/the-game-master
2024-04-24 18:04:17 -06:00
John Tapsell
1acbef1890 Fix SillyTavern being launched from a different working directory
Fixes launching ST from ST launcher on mac
2024-04-24 16:15:32 -07:00
Cohee
f90f370fed Merge pull request #2134 from StefanDanielSchwarz/Phi-Instruct-presets
Phi Instruct context+instruct presets
2024-04-25 01:44:12 +03:00
Stefan Daniel Schwarz
d34a0ee20e Phi Instruct context+instruct presets 2024-04-24 23:47:04 +02:00
Cohee
01e3964232 Auto-backup settings every 10 minutes. Increase backups limit to 50. 2024-04-24 23:45:49 +03:00
Cohee
153638c2cd Add error handling to auto login 2024-04-24 23:14:26 +03:00
Yokayo
4bb719359c Fix tabs 2024-04-24 21:19:26 +07:00
Yokayo
847eb60806 Update ru-ru.json 2024-04-24 21:14:03 +07:00
Yokayo
e799bd3920 Fix getMissingTranslations() and change its behavior 2024-04-24 21:12:40 +07:00
Yokayo
2b1aee9e71 Localize two hard-coded strings 2024-04-24 21:07:42 +07:00
Yokayo
d65f068310 More localizable strings 2024-04-24 20:58:24 +07:00
Yokayo
b1c199e650 Add more localizable strings 2024-04-24 19:02:00 +07:00
Cohee
51014e7a8d Fix VRM assets console spam 2024-04-24 10:54:55 +03:00
Cohee
530bf81940 #2127 Encode export PNG name 2024-04-24 10:48:08 +03:00
Cohee
2bba186c9e Add slash command and d&d hint for data bank 2024-04-24 02:37:57 +03:00
Cohee
61241df0d4 Add download and move for DB attachments 2024-04-24 02:33:16 +03:00
Cohee
b6b9b542d7 Add drag&drop to data bank 2024-04-24 01:51:54 +03:00
Cohee
71f41d5233 Fix server crash in auto login 2024-04-23 21:11:47 +03:00
Cohee
a421af9ea9 Increase max attachment size 2024-04-23 21:06:59 +03:00
Cohee
75372ad0cc Use Map for caches instead of objects 2024-04-23 16:15:54 +03:00
Cohee
d1f292f462 Merge pull request #2122 from joenunezb/fix-informaticai-missing-choices-message
Fix: Handle InformaticAI Endpoint response without message in response payload
2024-04-23 14:07:27 +03:00
joenunezb
890cf81627 Fix: InformaticAI response without message in choices 2024-04-23 03:56:50 -07:00
Cohee
d97f0a4c4d Add new NAI Diffusion model 2024-04-23 03:18:45 +03:00
Cohee
4370db6bdc Implement World Info activation using Vector Storage 2024-04-23 03:09:52 +03:00
Bronya-Rand
770f3e5da3 chore: apply align-items center and img sample for img only scraper icons 2024-04-22 19:12:02 +01:00
Bronya-Rand
0f0895f345 feat: implement miHoYo scraper 2024-04-22 19:11:00 +01:00
Cohee
6d1933c8f3 Escape name regex in message formatting function 2024-04-22 17:35:42 +03:00
Cohee
776260c85a Add Data Bank to attachments extension display name 2024-04-22 16:25:46 +03:00
Cohee
5a5463bd5d #2095 Suppress auto-execution on streamed swiped generations. 2024-04-22 16:02:50 +03:00
Cohee
2f45f50d37 Add config value for forwarded IPs whitelisting 2024-04-22 15:52:59 +03:00
Cohee
41ad7c5d26 Verify data bank attachments 2024-04-22 02:34:50 +03:00
Cohee
df93d43c36 Remove obnoxious mobile padding on right panel 2024-04-22 00:02:48 +03:00
Cohee
bc9c70556e Clean-up mentions of /public/ 2024-04-21 23:53:46 +03:00
Cohee
f75daba6c0 Image inlining hint always visible 2024-04-21 23:38:18 +03:00
Cohee
47b6562605 Merge pull request #2112 from SillyTavern/staging
Staging
2024-04-21 21:24:12 +03:00
deffcolony
1c9b89fdcc Create issue-auto-comments.yml 2024-04-16 12:48:29 +02:00
deffcolony
035dbe4901 added issue/pr label workflows
3 months of inactivity Bot posts a comment to remind about it and assigns a stale label No further activity - 5 work days passes bot closes the issue
2024-04-15 16:41:43 +02:00
198 changed files with 19573 additions and 11556 deletions

View File

@@ -44,6 +44,7 @@ module.exports = {
toastr: 'readonly',
Readability: 'readonly',
isProbablyReaderable: 'readonly',
ePub: 'readonly',
},
},
],

2
.github/close-label.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
🐛 Bug: ✅ Fixed
🦄 Feature Request: ✅ Implemented

62
.github/issue-auto-comments.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
comment:
footer: |
---
> I am a bot, and this is an automated message 🤖
labels:
- name: ✖️ Invalid
labeled:
issue:
action: close
body: >
Hello @{{ issue.user.login }} your ticket has been marked as invalid.
Please ensure you follow the issue template, provide all requested info,
and be sure to check the docs + previous issues prior to raising tickets.
pr:
body: Thank you @{{ pull_request.user.login }} for suggesting this. Please follow the pull request templates.
action: close
- name: 👩‍💻 Good First Issue
labeled:
issue:
body: >
This issue has been marked as a good first issue for first-time contributors to implement!
This is a great way to support the project, while also improving your skills, you'll also be credited as a contributor once your PR is merged.
If you're new to SillyTavern [here are a collection of resources](https://docs.sillytavern.app/)
If you need any support at all, feel free to reach out via [Discord](https://discord.gg/sillytavern).
- name: ❌ wontfix
labeled:
issue:
action: close
body: >
This ticked has been marked as 'wontfix', which usually means it is out-of-scope, or not feasible at this time.
You can still fork the project and make the changes yourself.
- name: ✅ Fixed
labeled:
issue:
body: >
Hello @{{ issue.user.login }}! It looks like all or part of this issue has now been implemented.
- name: ‼️ High Priority
labeled:
issue:
body: >
This ticket has been marked as high priority, and has been bumped to the top of the priority list.
You should expect an implementation to be pushed out soon. Thank you for your patience.
- name: 💀 Spam
labeled:
issue:
action: close
locking: lock
lock_reason: spam
body: >
This issue has been identified as spam, and is now locked.
Users who repeatedly raise spam issues may be blocked or reported.
- name: ⛔ Don't Merge
labeled:
pr:
body: This PR has been temporarily blocked from merging.

2
.github/readme.md vendored
View File

@@ -326,7 +326,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.**
* TAI Base by Humi: Unknown license
* TAI Base by Humi: MIT
* Cohee's modifications and derived code: AGPL v3
* RossAscends' additions: AGPL v3
* Portions of CncAnon's TavernAITurbo mod: Unknown license

View File

@@ -0,0 +1,28 @@
# Based on a label applied to an issue, the bot will add a comment with some additional info
name: 🎯 Auto-Reply to Labeled Tickets
on:
issues:
types:
- labeled
- unlabeled
pull_request_target:
types:
- labeled
- unlabeled
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Label Commenter
uses: peaceiris/actions-label-commenter@v1
with:
config_file: .github/issue-auto-comments.yml
github_token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,17 @@
# Detect and label pull requests that have merge conflicts
name: 🏗️ Check Merge Conflicts
on:
push:
branches:
- staging
jobs:
check-conflicts:
if: github.repository == 'SillyTavern/SillyTavern'
runs-on: ubuntu-latest
steps:
- uses: mschilde/auto-label-merge-conflicts@master
with:
CONFLICT_LABEL_NAME: "🚫 Merge Conflicts"
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
MAX_RETRIES: 5
WAIT_MS: 5000

View File

@@ -0,0 +1,82 @@
# Closes any issues that no longer have user interaction
name: 🎯 Close Stale Issues
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # Runs every day at midnight UTC
jobs:
stale:
runs-on: ubuntu-latest
steps:
# Comment on, then close issues that haven't been updated for ages
- name: Close Stale Issues
uses: actions/stale@v4
with:
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
days-before-stale: 360
days-before-close: 5
operations-per-run: 30
remove-stale-when-updated: true
enable-statistics: true
stale-issue-message: >
This issue has gone 3 months without an update. To keep the ticket open, please indicate that it is still relevant in a comment below.
Otherwise it will be closed in 5 working days.
stale-pr-message: >
This PR is stale because it has been open 6 weeks with no activity. Either remove the stale label or comment below with a short update,
otherwise this PR will be closed in 5 days.
close-issue-message: >
This issue was automatically closed because it has been stalled for over 1 year with no activity.
close-pr-message: >
This pull request was automatically closed because it has been stalled for over 1 year with no activity.
stale-issue-label: '⚰️ Stale'
close-issue-label: '🕸️ Inactive'
stale-pr-label: '⚰️ Stale'
close-pr-label: '🕸️ Inactive'
exempt-issue-labels: '📌 Keep Open'
exempt-pr-labels: '📌 Keep Open'
labels-to-add-when-unstale: '📌 Keep Open'
# Comment on, then close issues that required a response from the user, but didn't get one
- name: Close Issues without Response
uses: actions/stale@v4
with:
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
days-before-stale: 5
days-before-close: 3
operations-per-run: 30
remove-stale-when-updated: true
stale-issue-message: >
Hi! Looks like additional info is required for this issue to be addressed.
Don't forget to provide this within the next few days to keep your ticket open.
close-issue-message: 'Issue closed due to no response from user.'
only-labels: '🚏 Awaiting User Response'
labels-to-remove-when-unstale: '🚏 Awaiting User Response, 🛑 No Response'
stale-issue-label: '🛑 No Response'
close-issue-label: '🕸️ Inactive'
exempt-issue-labels: '📌 Keep Open'
exempt-pr-labels: '📌 Keep Open'
# Comment on issues that we should have replied to
- name: Notify Repo Owner to Respond
uses: actions/stale@v4
with:
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
days-before-stale: 7
days-before-close: 365
operations-per-run: 30
remove-stale-when-updated: true
stale-issue-message: Hey SillyTavern, - Don't forget to respond!
stale-pr-message: Hey SillyTavern, - Don't forget to respond!
only-labels: '👤 Awaiting Maintainer Response'
labels-to-remove-when-unstale: '👤 Awaiting Maintainer Response'
close-issue-message: 'Closed due to no response from repo author for over a year'
close-pr-message: 'Closed due to no response from repo author for over a year'
stale-issue-label: '👤 Awaiting Maintainer Response'
stale-pr-label: '👤 Awaiting Maintainer Response'
close-issue-label: '🕸️ Inactive'
close-pr-label: '🕸️ Inactive'
exempt-issue-labels: '📌 Keep Open'
exempt-pr-labels: '📌 Keep Open'

View File

@@ -21,6 +21,7 @@ env:
jobs:
build:
if: github.repository == 'SillyTavern/SillyTavern'
runs-on: ubuntu-latest
steps:
@@ -29,7 +30,7 @@ jobs:
run: |
echo "IMAGE_NAME=${REPO,,}" >> ${GITHUB_ENV}
# Using the following workaround because currently GitHub Actions
# Using the following workaround because currently GitHub Actions
# does not support logical AND/OR operations on triggers
# It's currently not possible to have `branches` under the `schedule` trigger
- name: Checkout the release branch (on release)
@@ -64,7 +65,10 @@ jobs:
id: metadata
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: ${{ env.BRANCH_NAME }}
# Release version tag if the workflow is triggered by a release
# Branch name tag if the workflow is triggered by a push
tags: |
${{ github.event_name == 'release' && github.ref_name || env.BRANCH_NAME }}
# Login into package repository as the person who created the release
- name: Log in to the Container registry
@@ -91,5 +95,6 @@ jobs:
- name: Docker tag latest and push
if: ${{ github.event_name == 'release' }}
run: |
docker tag $IMAGE_NAME:${{ github.ref_name }} $IMAGE_NAME:latest
docker push $IMAGE_NAME:latest
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

39
.github/workflows/get-pr-size.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
# Adds a comment to new PRs, showing the compressed size and size difference of new code
# And also labels the PR based on the number of lines changes
name: 🌈 Check PR Size
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Find and comment with compressed size
- name: Get Compressed Size
uses: preactjs/compressed-size-action@v2
with:
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
pattern: './dist/**/*.{js,css,html}'
strip-hash: '\\b\\w{8}\\.'
exclude: '**/node_modules/**'
minimum-change-threshold: 100
# Check number of lines of code added
- name: Label based on Lines of Code
uses: codelytv/pr-size-labeler@v1
with:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
xs_max_size: '10'
s_max_size: '100'
m_max_size: '500'
l_max_size: '1000'
s_label: '🟩 PR - Small'
m_label: '🟨 PR - Medium'
l_label: '🟧 PR - Large'
xl_label: '🟥 PR - XL'
fail_if_xl: 'false'
message_if_xl: >
It looks like this PR is very large (over 1000 lines).
Try to avoid addressing multiple issues in a single PR, and
in the future consider breaking large tasks down into smaller steps.
This it to make reviewing, testing, reverting and general quality management easier.

View File

@@ -0,0 +1,17 @@
# When a new comment is added to an issue, if it had the Stale or Awaiting User Response labels, then those labels will be removed
name: 🎯 Remove Pending Labels on Close
on:
issues:
types: [closed]
jobs:
remove-labels:
runs-on: ubuntu-latest
steps:
- name: Remove Labels when Closed
uses: actions-cool/issues-helper@v2
with:
actions: remove-labels
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: '🚏 Awaiting User Response,⚰️ Stale,👤 Awaiting Maintainer Response'

View File

@@ -0,0 +1,42 @@
# When a new comment is added to an issue, if it had the Stale or Awaiting User Response labels, then those labels will be removed
name: 🎯 Add/ Remove Awaiting Response Labels
on:
issue_comment:
types: [created]
jobs:
remove-stale:
runs-on: ubuntu-latest
if: ${{ github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' }}
steps:
- name: Remove Stale labels when Updated
uses: actions-cool/issues-helper@v2
with:
actions: remove-labels
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: '🚏 Awaiting User Response,⚰️ Stale'
add-awaiting-author:
runs-on: ubuntu-latest
if: ${{!github.event.issue.pull_request && github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' && github.event.issue.state == 'open' }}
steps:
- name: Add Awaiting Author labels when Updated
uses: actions-cool/issues-helper@v2
with:
actions: add-labels
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: '👤 Awaiting Maintainer Response'
remove-awaiting-author:
runs-on: ubuntu-latest
if: ${{ github.event.comment.author_association == 'OWNER' }}
steps:
- name: Remove Awaiting Author labels when Updated
uses: actions-cool/issues-helper@v2
with:
actions: remove-labels
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: '👤 Awaiting Maintainer Response'

View File

@@ -1,43 +0,0 @@
name: Update SillyTavern-Docs
on:
push:
branches:
- main
jobs:
update_docs:
runs-on: ubuntu-latest
steps:
- name: Checkout current repository
uses: actions/checkout@v2
- name: Checkout SillyTavern-Docs repository
uses: actions/checkout@v2
with:
repository: SillyTavern/SillyTavern-Docs
path: SillyTavern-Docs
- name: Clone SillyTavern wiki into SillyTavern-Docs/extensions
run: rm -rf SillyTavern-Docs/extensions && git clone https://github.com/SillyTavern/SillyTavern.wiki.git SillyTavern-Docs/extensions && rm -rf SillyTavern-Docs/extensions/.git
- name: Copy files
run: |
cp public/notes/content.md SillyTavern-Docs/guidebook.md
cp faq.md SillyTavern-Docs/faq.md
cp readme.md SillyTavern-Docs/readme.md
cp public/notes/update.md SillyTavern-Docs/update.md
- name: Deploy to external repository
uses: cpina/github-action-push-to-another-repository@main
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
with:
# GitHub Action output files
source-directory: SillyTavern-Docs/
destination-github-username: SillyTavern
destination-repository-name: SillyTavern-Docs
user-email: github-actions[bot]@users.noreply.github.com
user-name: "GitHub Actions"
target-branch: "main"

1
.gitignore vendored
View File

@@ -47,3 +47,4 @@ access.log
public/css/user.css
/plugins/
/data
/default/scaffold

View File

@@ -33,7 +33,14 @@ If you insist on installing via a zip, here is the tedious process for doing the
2. Unzip it into a folder OUTSIDE of your current ST installation.
3. Do the usual setup procedure for your OS to install the NodeJS requirements.
4. Copy the following files/folders as necessary(*) from your old ST installation:
4a. Updating 1.12.0 and above
Copy the user data directory from your data root into the data root of the new install.
By default: /data/default-user
4a. Migrating from <1.12.0 to >=1.20.0
Copy the following files/folders as necessary(*) from your old ST installation:
- Assets
- Backgrounds
@@ -54,16 +61,15 @@ If you insist on installing via a zip, here is the tedious process for doing the
- Worlds
- User
- settings.json
- secrets.json <---- this one is in the base folder, not /public/
- secrets.json <---- This one is in the base folder, not /public/
(*) 'As necessary' = "If you made any custom content related to those folders".
None of the folders are mandatory, so only copy what you need.
**NB: DO NOT COPY THE ENTIRE /PUBLIC/ FOLDER.**
Doing so could break the new install and prevent new features from being present.
Paste those items into the /data/default-user folder of the new install.
5. Paste those items into the /Public/ folder of the new install.
5. Start SillyTavern once again with the method appropriate to your OS, and pray you got it right.
6. Start SillyTavern once again with the method appropriate to your OS, and pray you got it right.
7. If everything shows up, you can safely delete the old ST folder.
6. If everything shows up, you can safely delete the old ST folder.

View File

@@ -9,6 +9,8 @@ port: 8000
# -- SECURITY CONFIGURATION --
# Toggle whitelist mode
whitelistMode: true
# Whitelist will also verify IP in X-Forwarded-For / X-Real-IP headers
enableForwardedWhitelist: true
# Whitelist of allowed IP addresses
whitelist:
- 127.0.0.1
@@ -46,6 +48,12 @@ allowKeysExposure: false
skipContentCheck: false
# Disable automatic chats backup
disableChatBackup: false
# Allowed hosts for card downloads
whitelistImportDomains:
- localhost
- cdn.discordapp.com
- files.catbox.moe
- raw.githubusercontent.com
# API request overrides (for KoboldAI and Text Completion APIs)
## Note: host includes the port number if it's not the default (80 or 443)
## Format is an array of objects:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

View File

@@ -107,14 +107,6 @@
"filename": "default_Seraphina.png",
"type": "character"
},
{
"filename": "default_CodingSensei.png",
"type": "character"
},
{
"filename": "default_FluxTheCat.png",
"type": "character"
},
{
"filename": "Seraphina",
"type": "sprites"
@@ -539,6 +531,10 @@
"filename": "presets/context/Llama 3 Instruct.json",
"type": "context"
},
{
"filename": "presets/context/Phi.json",
"type": "context"
},
{
"filename": "presets/instruct/Adventure.json",
"type": "instruct"
@@ -631,6 +627,10 @@
"filename": "presets/instruct/Llama 3 Instruct.json",
"type": "instruct"
},
{
"filename": "presets/instruct/Phi.json",
"type": "instruct"
},
{
"filename": "presets/moving-ui/Default.json",
"type": "moving_ui"
@@ -642,5 +642,21 @@
{
"filename": "presets/quick-replies/Default.json",
"type": "quick_replies"
},
{
"filename": "presets/instruct/Llama-3-Instruct-Names.json",
"type": "instruct"
},
{
"filename": "presets/instruct/ChatML-Names.json",
"type": "instruct"
},
{
"filename": "presets/context/Llama-3-Instruct-Names.json",
"type": "context"
},
{
"filename": "presets/context/ChatML-Names.json",
"type": "context"
}
]

View File

@@ -0,0 +1,12 @@
{
"story_string": "<|im_start|>system\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}<|im_end|>",
"example_separator": "",
"chat_start": "",
"use_stop_strings": false,
"allow_jailbreak": false,
"always_force_name2": true,
"trim_sentences": false,
"include_newline": false,
"single_line": false,
"name": "ChatML-Names"
}

View File

@@ -0,0 +1,12 @@
{
"story_string": "<|start_header_id|>system<|end_header_id|>\n\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}<|eot_id|>",
"example_separator": "",
"chat_start": "",
"use_stop_strings": false,
"allow_jailbreak": false,
"always_force_name2": true,
"trim_sentences": false,
"include_newline": false,
"single_line": false,
"name": "Llama-3-Instruct-Names"
}

View File

@@ -0,0 +1,12 @@
{
"story_string": "<|system|>\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}<|end|>\n",
"example_separator": "",
"chat_start": "",
"use_stop_strings": false,
"allow_jailbreak": false,
"always_force_name2": true,
"trim_sentences": false,
"include_newline": false,
"single_line": false,
"name": "Phi"
}

View File

@@ -0,0 +1,24 @@
{
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
"input_sequence": "<|im_start|>[{{name}}]",
"output_sequence": "<|im_start|>[{{name}}]",
"last_output_sequence": "",
"system_sequence": "<|im_start|>system",
"stop_sequence": "<|im_end|>",
"wrap": true,
"macro": true,
"names": false,
"names_force_groups": false,
"activation_regex": "",
"system_sequence_prefix": "",
"system_sequence_suffix": "",
"first_output_sequence": "",
"skip_examples": false,
"output_suffix": "<|im_end|>\n",
"input_suffix": "<|im_end|>\n",
"system_suffix": "<|im_end|>\n",
"user_alignment_message": "",
"system_same_as_user": false,
"last_system_sequence": "",
"name": "ChatML-Names"
}

View File

@@ -0,0 +1,24 @@
{
"system_prompt": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.",
"input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
"output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
"last_output_sequence": "",
"system_sequence": "<|start_header_id|>system<|end_header_id|>\n\n",
"stop_sequence": "<|eot_id|>",
"wrap": false,
"macro": true,
"names": false,
"names_force_groups": false,
"activation_regex": "",
"system_sequence_prefix": "",
"system_sequence_suffix": "",
"first_output_sequence": "",
"skip_examples": false,
"output_suffix": "<|eot_id|>",
"input_suffix": "<|eot_id|>",
"system_suffix": "<|eot_id|>",
"user_alignment_message": "",
"system_same_as_user": true,
"last_system_sequence": "",
"name": "Llama-3-Instruct-Names"
}

View File

@@ -0,0 +1,24 @@
{
"system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
"input_sequence": "<|user|>\n",
"output_sequence": "<|assistant|>\n",
"first_output_sequence": "",
"last_output_sequence": "",
"system_sequence_prefix": "",
"system_sequence_suffix": "",
"stop_sequence": "<|end|>",
"wrap": false,
"macro": true,
"names": true,
"names_force_groups": true,
"activation_regex": "",
"skip_examples": false,
"output_suffix": "<|end|>\n",
"input_suffix": "<|end|>\n",
"system_sequence": "<|system|>\n",
"system_suffix": "<|end|>\n",
"user_alignment_message": "",
"last_system_sequence": "",
"system_same_as_user": false,
"name": "Phi"
}

View File

@@ -231,6 +231,7 @@
"api_url_scale": "",
"show_external_models": false,
"assistant_prefill": "",
"assistant_impersonation": "",
"human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
"use_ai21_tokenizer": false,
"use_google_tokenizer": false,

View File

@@ -33,8 +33,8 @@
"negative_prompt": "",
"grammar_string": "",
"banned_tokens": "",
"ignore_eos_token_aphrodite": false,
"spaces_between_special_tokens_aphrodite": true,
"ignore_eos_token": false,
"spaces_between_special_tokens": true,
"type": "ooba",
"legacy_api": false,
"sampler_order": [

View File

@@ -33,8 +33,8 @@
"negative_prompt": "",
"grammar_string": "",
"banned_tokens": "",
"ignore_eos_token_aphrodite": false,
"spaces_between_special_tokens_aphrodite": true,
"ignore_eos_token": false,
"spaces_between_special_tokens": true,
"type": "ooba",
"legacy_api": false,
"sampler_order": [

View File

@@ -33,8 +33,8 @@
"negative_prompt": "",
"grammar_string": "",
"banned_tokens": "",
"ignore_eos_token_aphrodite": false,
"spaces_between_special_tokens_aphrodite": true,
"ignore_eos_token": false,
"spaces_between_special_tokens": true,
"type": "ooba",
"legacy_api": false,
"sampler_order": [

View File

@@ -387,14 +387,8 @@
}
],
"tag_map": {
"default_FluxTheCat.png": [
"1345561466591"
],
"default_Seraphina.png": [
"1345561466591"
],
"default_CodingSensei.png": [
"1345561466591"
]
},
"nai_settings": {
@@ -630,6 +624,7 @@
"show_external_models": false,
"proxy_password": "",
"assistant_prefill": "",
"assistant_impersonation": "",
"use_ai21_tokenizer": false
}
}

View File

@@ -0,0 +1,26 @@
# Content Scaffolding
Content files in this folder will be copied for all users (old and new) on the server startup.
1. You **must** create an `index.json` file in `/default/scaffold` for it to work. The syntax is the same as for default content.
2. All file paths should be relative to `/default/scaffold`, the use of subdirectories is allowed.
3. Scaffolded files are copied first, so they override any of the default files (presets/settings/etc.) that have the same file name.
## Example
```json
[
{
"filename": "themes/Midnight.json",
"type": "theme"
},
{
"filename": "backgrounds/city.png",
"type": "background"
},
{
"filename": "characters/Charlie.png",
"type": "character"
}
]
```

15
package-lock.json generated
View File

@@ -1,18 +1,17 @@
{
"name": "sillytavern",
"version": "1.12.0-preview",
"version": "1.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sillytavern",
"version": "1.12.0-preview",
"version": "1.12.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
"@agnai/sentencepiece-js": "^1.1.1",
"@agnai/web-tokenizers": "^0.1.3",
"@dqbd/tiktoken": "^1.0.13",
"@zeldafan0225/ai_horde": "^4.0.1",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
@@ -46,6 +45,7 @@
"sanitize-filename": "^1.6.3",
"sillytavern-transformers": "^2.14.6",
"simple-git": "^3.19.1",
"tiktoken": "^1.0.15",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"write-file-atomic": "^5.0.1",
@@ -82,10 +82,6 @@
"version": "0.1.3",
"license": "Apache-2.0"
},
"node_modules/@dqbd/tiktoken": {
"version": "1.0.13",
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"dev": true,
@@ -4403,6 +4399,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/tiktoken": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.15.tgz",
"integrity": "sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw=="
},
"node_modules/timm": {
"version": "1.7.1",
"license": "MIT"

View File

@@ -2,7 +2,6 @@
"dependencies": {
"@agnai/sentencepiece-js": "^1.1.1",
"@agnai/web-tokenizers": "^0.1.3",
"@dqbd/tiktoken": "^1.0.13",
"@zeldafan0225/ai_horde": "^4.0.1",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
@@ -36,6 +35,7 @@
"sanitize-filename": "^1.6.3",
"sillytavern-transformers": "^2.14.6",
"simple-git": "^3.19.1",
"tiktoken": "^1.0.15",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"write-file-atomic": "^5.0.1",
@@ -68,13 +68,15 @@
"type": "git",
"url": "https://github.com/SillyTavern/SillyTavern.git"
},
"version": "1.12.0-preview",
"version": "1.12.0",
"scripts": {
"start": "node server.js",
"start-multi": "node server.js --disableCsrf",
"start:no-csrf": "node server.js --disableCsrf",
"postinstall": "node post-install.js",
"lint": "eslint \"src/**/*.js\" \"public/**/*.js\" ./*.js",
"lint-fix": "eslint \"src/**/*.js\" \"public/**/*.js\" ./*.js --fix"
"lint:fix": "eslint \"src/**/*.js\" \"public/**/*.js\" ./*.js --fix",
"plugins:update": "node plugins update",
"plugins:install": "node plugins install"
},
"bin": {
"sillytavern": "./server.js"

75
plugins.js Normal file
View File

@@ -0,0 +1,75 @@
// Plugin manager script.
// Usage: node plugins.js update
// More operations coming soon.
const { default: git } = require('simple-git');
const fs = require('fs');
const path = require('path');
const { color } = require('./src/util');
const pluginsPath = './plugins';
const command = process.argv[2];
if (command === 'update') {
console.log(color.magenta('Updating all plugins'));
updatePlugins();
}
if (command === 'install') {
const pluginName = process.argv[3];
console.log('Installing a new plugin', color.green(pluginName));
installPlugin(pluginName);
}
async function updatePlugins() {
const directories = fs.readdirSync(pluginsPath)
.filter(file => !file.startsWith('.'))
.filter(file => fs.statSync(path.join(pluginsPath, file)).isDirectory());
console.log(`Found ${color.cyan(directories.length)} directories in ./plugins`);
for (const directory of directories) {
try {
console.log(`Updating plugin ${color.green(directory)}...`);
const pluginPath = path.join(pluginsPath, directory);
const pluginRepo = git(pluginPath);
await pluginRepo.fetch();
const commitHash = await pluginRepo.revparse(['HEAD']);
const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
const log = await pluginRepo.log({
from: commitHash,
to: trackingBranch,
});
if (log.total === 0) {
console.log(`Plugin ${color.blue(directory)} is already up to date`);
continue;
}
await pluginRepo.pull();
const latestCommit = await pluginRepo.revparse(['HEAD']);
console.log(`Plugin ${color.green(directory)} updated to commit ${color.cyan(latestCommit)}`);
} catch (error) {
console.error(color.red(`Failed to update plugin ${directory}: ${error.message}`));
}
}
console.log(color.magenta('All plugins updated!'));
}
async function installPlugin(pluginName) {
try {
const pluginPath = path.join(pluginsPath, path.basename(pluginName, '.git'));
if (fs.existsSync(pluginPath)) {
return console.log(color.yellow(`Directory already exists at ${pluginPath}`));
}
await git().clone(pluginName, pluginPath, { '--depth': 1 });
console.log(`Plugin ${color.green(pluginName)} installed to ${color.cyan(pluginPath)}`);
}
catch (error) {
console.error(color.red(`Failed to install plugin ${pluginName}`), error);
}
}

6
public/css/brands.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -204,3 +204,7 @@ input.extension_missing[type="checkbox"] {
#extensionsMenu>#translate_chat {
order: 7;
}
#extensionsMenu>#translate_input_message {
order: 8;
}

File diff suppressed because it is too large Load Diff

9
public/css/fontawesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -231,9 +231,11 @@
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
}
/*
#right-nav-panel {
padding-right: 15px;
}
*/
#floatingPrompt,
#cfgConfig,
@@ -307,6 +309,10 @@
object-fit: cover;
}
body.waifuMode .zoomed_avatar_container {
height: 100%;
}
body.waifuMode .zoomed_avatar {
width: fit-content;
max-height: calc(60vh - 60px);

View File

@@ -171,3 +171,78 @@
.select2-results__option.select2-results__message::before {
display: none;
}
.select2-selection__choice__display {
/* Fix weird alignment on the left side */
margin-left: 1px;
}
/* Styling for choice remove icon */
span.select2.select2-container .select2-selection__choice__remove {
cursor: pointer;
transition: background-color 0.3s;
color: var(--SmartThemeBodyColor);
background-color: var(--black50a);
}
span.select2.select2-container .select2-selection__choice__remove:hover {
color: var(--SmartThemeBodyColor);
background-color: var(--white30a);
}
/* Custom class to support styling to show clickable choice options */
.select2_choice_clickable+span.select2-container .select2-selection__choice__display {
cursor: pointer;
}
.select2_choice_clickable_buttonstyle+span.select2-container .select2-selection__choice__display {
cursor: pointer;
transition: background-color 0.3s;
color: var(--SmartThemeBodyColor);
background-color: var(--black50a);
}
.select2_choice_clickable_buttonstyle+span.select2-container .select2-selection__choice__display:hover {
background-color: var(--white30a);
}
/* Custom class to support same line multi inputs of select2 controls */
.select2_multi_sameline+span.select2-container .select2-selection--multiple {
display: flex;
flex-wrap: wrap;
}.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
/* Allow search placeholder to take up all space if needed */
flex-grow: 1;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
/* Fix weird styling choice or huge margin around selected options */
margin-block-start: 2px;
margin-block-end: 2px;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search__field {
/* Min height to reserve spacing */
min-height: calc(var(--mainFontSize) + 13px);
/* Min width to be clickable */
min-width: 4em;
align-content: center;
/* Fix search textarea alignment issue with UL elements */
margin-top: 0px;
height: unset;
/* Prevent height from jumping around when input is focused */
line-height: 1;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
/* Min height to reserve spacing */
min-height: calc(var(--mainFontSize) + 13px);
}
/* Make search bar invisible unless the select2 is active, to save space */
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
height: 1px;
}
.select2_multi_sameline+span.select2-container.select2-container--focus .select2-selection--multiple .select2-search--inline {
height: unset;
}

View File

@@ -1,24 +0,0 @@
:root,
:host {
--fa-style-family-classic: 'Font Awesome 6 Free';
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free';
}
@font-face {
font-family: 'Font Awesome 6 Free';
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype");
}
.fas,
.fa-solid {
font-weight: 900;
}
/*!
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/

6
public/css/solid.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2024 Fonticons, Inc.
*/
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}

View File

@@ -360,6 +360,14 @@
flex: 2 !important;
}
.flex3 {
flex: 3;
}
.flex4 {
flex: 4;
}
.flexFlowColumn {
flex-flow: column;
}
@@ -563,4 +571,4 @@ textarea:disabled {
height: 30px;
text-align: center;
padding: 5px;
}
}

View File

@@ -103,7 +103,8 @@
}
#bulkTagsList,
#tagList .tag {
#tagList .tag,
#groupTagList .tag {
opacity: 0.6;
}
@@ -193,7 +194,8 @@
filter: brightness(75%) saturate(0.6);
}
.tag_as_folder:hover {
.tag_as_folder:hover,
.tag_as_folder.flash {
filter: brightness(150%) saturate(0.6) !important;
}

View File

@@ -19,7 +19,8 @@ body.no-timer .mes_timer,
body.no-timestamps .timestamp,
body.no-tokenCount .tokenCounterDisplay,
body.no-mesIDDisplay .mesIDDisplay,
body.no-modelIcons .icon-svg {
body.no-modelIcons .icon-svg,
body.hideChatAvatars .mesAvatarWrapper .avatar {
display: none !important;
}
@@ -123,10 +124,16 @@ body.charListGrid #rm_print_characters_block .bogus_folder_select_back .avatar {
}
/* Hack for keeping the spacing */
/*
body.charListGrid #rm_print_characters_block .ch_add_placeholder {
display: flex !important;
opacity: 0;
}
*/
body.charListGrid #rm_print_characters_block .ch_additional_info {
display: none;
}
/*big avatars mode page-wide changes*/
@@ -433,14 +440,6 @@ body.expandMessageActions .mes .mes_buttons .extraMesButtonsHint {
display: none !important;
}
#openai_image_inlining:not(:checked)~#image_inlining_hint {
display: none;
}
#openai_image_inlining:checked~#image_inlining_hint {
display: block;
}
#smooth_streaming:not(:checked)~#smooth_streaming_speed_control {
display: none;
}

View File

@@ -76,6 +76,12 @@
.world_entry_form_control {
display: flex;
flex-direction: column;
position: relative;
}
.world_entry_form_control .keyprimarytextpole,
.world_entry_form_control .keysecondarytextpole {
padding-right: 25px;
}
.world_entry_thin_controls {
@@ -101,7 +107,7 @@
height: auto;
margin-top: 0;
margin-bottom: 0;
min-height: calc(var(--mainFontSize) + 13px);
min-height: calc(var(--mainFontSize) + 14px);
}
.delete_entry_button {
@@ -157,7 +163,12 @@
width: 10em;
}
#world_info_search,
#world_info_search {
width: 10em;
min-width: 10em;
flex-grow: 1;
}
#world_info_sort_order {
width: 7em;
}
@@ -191,3 +202,58 @@
.WIEntryHeaderTitleMobile {
display: none;
}
span.select2-container .select2-selection__choice__display:has(> .regex_item),
span.select2-container .select2-results__option:has(> .result_block .regex_item) {
background-color: #D27D2D30;
}
.regex_item .regex_icon {
background-color: var(--black30a);
color: var(--SmartThemeBodyColor);
border: 1px solid var(--SmartThemeBorderColor);
border-radius: 7px;
font-weight: bold;
font-size: calc(var(--mainFontSize) * 0.75);
padding: 0px 3px;
position: relative;
top: -1px;
margin-right: 3px;
}
.select2-results__option .regex_item .regex_icon {
margin-right: 6px;
}
.select2-results__option .item_count {
margin-left: 10px;
float: right;
}
select.keyselect+span.select2-container .select2-selection--multiple {
padding-right: 30px;
}
.switch_input_type_icon {
cursor: pointer;
font-weight: bold;
height: 20px;
width: fit-content;
margin-right: 5px;
margin-top: calc(5px + var(--mainFontSize));
position: absolute;
right: 0;
padding: 1px;
background-color: transparent;
border: none;
font-size: 1em;
opacity: 0.5;
color: var(--SmartThemeBodyColor);
transition: opacity 0.3s;
}
.switch_input_type_icon:hover {
opacity: 1;
}

1360
public/global.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

48
public/img/groq.svg Normal file
View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="107.644"
height="156.436"
viewBox="0 0 107.644 156.436"
fill="none"
version="1.1"
id="svg9"
sodipodi:docname="groqcloud_dark_v2.svg"
inkscape:version="1.3 (0e150ed, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview9"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.667"
inkscape:cx="499.25037"
inkscape:cy="56.971514"
inkscape:window-width="1312"
inkscape:window-height="449"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="0"
inkscape:current-layer="svg9" />
<defs
id="defs9">
<clipPath
id="clip0_872_2594">
<rect
width="1000"
height="200.345"
id="rect9"
x="0"
y="0" />
</clipPath>
</defs>
<path
d="M 54.0487,0.00281139 C 24.4736,-0.29748861 0.303066,23.497811 0.00281057,53.072911 -0.297445,82.648011 23.4978,106.89401 53.0729,107.11901 c 0.3003,0 0.6756,0 0.9758,0 H 71.6888 V 87.077011 H 54.0487 c -18.4656,0.225 -33.6285,-14.563 -33.8537,-33.1033 -0.2252,-18.4657 14.5624,-33.6286 33.1031,-33.8538 0.2252,0 0.5255,0 0.7506,0 18.4657,0 33.5536,15.0128 33.5536,33.4784 v 49.316699 c 0,18.316 -14.9377,33.254 -33.2533,33.479 -8.7825,0 -17.1145,-3.603 -23.2698,-9.834 l -14.187,14.187 c 9.8333,9.909 23.1947,15.539 37.1565,15.689 h 0.7507 c 29.1998,-0.451 52.6946,-24.096 52.8446,-53.296 V 52.247211 C 106.894,23.197511 83.1735,0.00281139 54.1238,0.00281139 Z"
id="path7" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="70pt"
height="70pt"
viewBox="0 0 70 70"
preserveAspectRatio="xMidYMid"
id="svg15"
sodipodi:docname="infermatic.svg"
inkscape:version="1.3 (0e150ed, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs15" />
<sodipodi:namedview
id="namedview15"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
inkscape:zoom="0.75112613"
inkscape:cx="306.2069"
inkscape:cy="50.590705"
inkscape:window-width="1312"
inkscape:window-height="449"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="0"
inkscape:current-layer="svg15" />
<path
id="path15"
d="m 1030,375 v -75 h 75 74 l 6,33 c 4,18 5,51 3,72 l -3,40 -77,3 -78,3 z m 547,619 c -4,-4 -7,-41 -7,-81 v -74 l 78,3 77,3 v 75 75 l -70,3 c -39,1 -74,0 -78,-4 z m -547,-74 v -79 l 133,-3 132,-3 3,-267 2,-268 h 215 215 v 75 75 h -135 -135 l -2,273 -3,272 -212,3 -213,2 z"
transform="matrix(0.1,0,0,-0.1,-103,99.999998)" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -42,6 +42,46 @@ EventEmitter.prototype.on = function (event, listener) {
this.events[event].push(listener);
};
/**
* Makes the listener the last to be called when the event is emitted
* @param {string} event Event name
* @param {function} listener Event listener
*/
EventEmitter.prototype.makeLast = function (event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
const events = this.events[event];
const idx = events.indexOf(listener);
if (idx > -1) {
events.splice(idx, 1);
}
events.push(listener);
}
/**
* Makes the listener the first to be called when the event is emitted
* @param {string} event Event name
* @param {function} listener Event listener
*/
EventEmitter.prototype.makeFirst = function (event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
const events = this.events[event];
const idx = events.indexOf(listener);
if (idx > -1) {
events.splice(idx, 1);
}
events.unshift(listener);
}
EventEmitter.prototype.removeListener = function (event, listener) {
var idx;

View File

@@ -3,7 +3,6 @@
"kobldpresets": "الإعدادات المسبقة لـ Kobold",
"guikoboldaisettings": "إعدادات واجهة KoboldAI",
"novelaipreserts": "الإعدادات المسبقة لـ NovelAI",
"default": "افتراضي",
"openaipresets": "الإعدادات المسبقة لـ OpenAI",
"text gen webio(ooba) presets": "الإعدادات المسبقة لـ WebUI(ooba)",
"response legth(tokens)": "طول الاستجابة (بعدد الاحرف او الرموز)",
@@ -62,7 +61,7 @@
"Temperature": "درجة الحرارة",
"Frequency Penalty": "عقوبة التكرار",
"Presence Penalty": "عقوبة الوجود",
"Top-p": "أعلى p",
"Top-p": "أعلى p",
"Display bot response text chunks as they are generated": "عرض النصوص لجظة بلحظة",
"Top A": "أعلى A",
"Typical Sampling": "عينة نموذجية",
@@ -101,7 +100,7 @@
"Inserts jailbreak as a last system message.": "يدرج كسر الحظر كرسالة نظام أخيرة.",
"This tells the AI to ignore its usual content restrictions.": "هذا يخبر الذكاء الاصطناعي بتجاهل القيود المعتادة على المحتوى.",
"NSFW Encouraged": "NSFW مشجع",
"Tell the AI that NSFW is allowed.": "قل للذكاء الاصطناعي أنه يُسمح بـ NSFW",
"Tell the AI that NSFW is allowed.": "قل للذكاء الاصطناعي أنه يُسمح بـ NSFW",
"NSFW Prioritized": "الأولوية للمحتوى غير مناسب للعمل",
"NSFW prompt text goes first in the prompt to emphasize its effect.": "النص الغير مناسب للعمل يأتي أولاً في التعليمات لتأكيد تأثيره.",
"Streaming": "البث المباشر ل",
@@ -141,7 +140,7 @@
"Influences bot behavior in its responses": "يؤثر على سلوك الروبوت في ردوده.",
"Connect": "الاتصال",
"Test Message": "رسالة اختبار",
"API": "واجهة برمجة التطبيقات (API)",
"API": "واجهة برمجة التطبيقات (API)",
"KoboldAI": "KoboldAI",
"Use Horde": "استخدام Horde",
"API url": "رابط API",
@@ -206,7 +205,7 @@
"Scale API Key": "مفتاح API لـ Scale",
"Alt Method": "طريقة بديلة",
"AI21 API Key": "مفتاح API لـ AI21",
"AI21 Model": "نموذج AI21",
"AI21 Model": "نموذج AI21",
"View API Usage Metrics": "عرض مقاييس استخدام واجهة برمجة التطبيقات",
"Show External models (provided by API)": "عرض النماذج الخارجية (المقدمة من قبل واجهة برمجة التطبيقات)",
"Bot": "روبوت:",
@@ -495,7 +494,6 @@
"Global Lore First": "سرد العالم أولاً",
"Recursive Scan": "فحص متكرر",
"Case Sensitive": "حساس لحالة الأحرف",
"Match whole words": "تطابق الكلمات الكاملة",
"Alert On Overflow": "تنبيه عند التجاوز",
"World/Lore Editor": "محرر العالم/السرد",
"--- None ---": "--- لا شيء ---",
@@ -915,7 +913,7 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "استخدم المحلل النحوي المناسب لنماذج Google عبر واجهة برمجة التطبيقات الخاصة بهم. معالجة الإشارات الأولية بطيئة، ولكنها تقدم عداد رمز دقيق جدًا.",
"Load koboldcpp order": "تحميل أمر koboldcpp",
"Use Google Tokenizer": "استخدم محلل النحوي من Google"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Kobold-Einstellungen von vorher",
"guikoboldaisettings": "KoboldAI-Einstellungen für das Menü",
"novelaipreserts": "NovelAI-Einstellungen von früher",
"default": "Normal",
"openaipresets": "OpenAI-Einstellungen von vorher",
"text gen webio(ooba) presets": "WebUI(ooba)-Einstellungen für Texterstellung",
"response legth(tokens)": "Länge der Antwort (Tokens)",
@@ -494,7 +493,6 @@
"Global Lore First": "Globale Lore zuerst",
"Recursive Scan": "Rekursive Suche",
"Case Sensitive": "Groß-/Kleinschreibung beachten",
"Match whole words": "Ganze Wörter abgleichen",
"Alert On Overflow": "Warnung bei Überlauf",
"World/Lore Editor": "Welt-/Lore-Editor",
"--- None ---": "--- Keine ---",
@@ -917,5 +915,5 @@
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Preajustes de Kobold",
"guikoboldaisettings": "Ajustes de interfaz de KoboldAI",
"novelaipreserts": "Preajustes de NovelAI",
"default": "Predeterminado",
"openaipresets": "Preajustes de OpenAI",
"text gen webio(ooba) presets": "Preajustes de Text Gen WebUI(ooba)",
"response legth(tokens)": "Longitud de respuesta (tokens)",
@@ -494,7 +493,6 @@
"Global Lore First": "Historia Global Primero",
"Recursive Scan": "Escaneo Recursiva",
"Case Sensitive": "Sensible a mayúsculas y minúsculas",
"Match whole words": "Coincidir palabras completas",
"Alert On Overflow": "Alerta en Desbordamiento",
"World/Lore Editor": "Editor de Mundo/Historia",
"--- None ---": "--- Ninguno ---",
@@ -891,6 +889,7 @@
"Chat API": " API de chat",
"and pick a character": "y elige un personaje",
"in the chat bar": "en la barra de chat",
"You can browse a list of bundled characters in the Download Extensions & Assets menu within": "Puedes explorar una lista de personajes incluidos en el menú de Download Extensions & Assets dentro de ",
"Confused or lost?": "¿Confundido o perdido?",
"click these icons!": "¡Haz clic en estos iconos!",
"SillyTavern Documentation Site": "Sitio de documentación de SillyTavern",
@@ -914,7 +913,4 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Usa el tokenizador apropiado para los modelos de Google a través de su API. Procesamiento de indicaciones más lento, pero ofrece un recuento de tokens mucho más preciso.",
"Load koboldcpp order": "Cargar orden de koboldcpp",
"Use Google Tokenizer": "Usar Tokenizador de Google"
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Préréglages de Kobold",
"guikoboldaisettings": "Paramètres de l'interface utilisateur de KoboldAI",
"novelaipreserts": "Préréglages de NovelAI",
"default": "Par défaut",
"openaipresets": "Préréglages d'OpenAI",
"text gen webio(ooba) presets": "Préréglages de WebUI(ooba)",
"response legth(tokens)": "Longueur de la réponse (en tokens)",
@@ -205,7 +204,7 @@
"Scale API Key": "Clé API Scale",
"Alt Method": "Méthode alternative",
"AI21 API Key": "Clé API AI21",
"AI21 Model": "Modèle AI21",
"AI21 Model": "Modèle AI21",
"View API Usage Metrics": "Afficher les mesures d'utilisation de l'API",
"Show External models (provided by API)": "Afficher les modèles externes (fournis par l'API)",
"Bot": "Bot",
@@ -494,7 +493,6 @@
"Global Lore First": "Lore global d'abord",
"Recursive Scan": "Analyse récursive",
"Case Sensitive": "Sensible à la casse",
"Match whole words": "Correspondre aux mots entiers",
"Alert On Overflow": "Alerte en cas de dépassement",
"World/Lore Editor": "Éditeur de monde/lore",
"--- None ---": "--- Aucun ---",
@@ -914,5 +912,5 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Utilisez le tokenizer approprié pour les modèles Google via leur API. Traitement des invitations plus lent, mais offre un décompte de jetons beaucoup plus précis.",
"Load koboldcpp order": "Charger l'ordre koboldcpp",
"Use Google Tokenizer": "Utiliser le tokenizer Google"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Fyrir stillingar Kobold",
"guikoboldaisettings": "Stillingar fyrir KoboldAI viðmót",
"novelaipreserts": "Fyrir stillingar NovelAI",
"default": "Sjálfgefið",
"openaipresets": "Fyrir stillingar OpenAI",
"text gen webio(ooba) presets": "Fyrir stillingar WebUI(ooba) textagerðar",
"response legth(tokens)": "Lengd svars (í táknum eða stöfum)",
@@ -62,7 +61,7 @@
"Temperature": "Hitastig",
"Frequency Penalty": "Tíðnarefning",
"Presence Penalty": "Tilkoma refning",
"Top-p": "Topp-p",
"Top-p": "Topp-p",
"Display bot response text chunks as they are generated": "Birta bætir svarborðstextabrot þegar þau eru búnar til",
"Top A": "Topp A",
"Typical Sampling": "Venjuleg úrtaka",
@@ -495,7 +494,6 @@
"Global Lore First": "Fyrst heimsfræði",
"Recursive Scan": "Endurkvæm skoðun",
"Case Sensitive": "Skilgreiningarfræðilegt",
"Match whole words": "Nákvæm samræmi",
"Alert On Overflow": "Viðvörun um flæði",
"World/Lore Editor": "Heims-/fræðiritari",
"--- None ---": "--- Engin ---",
@@ -915,5 +913,5 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Notaðu rétta tokenizer fyrir Google módel með þeirra API. Hægri umhvörf fyrir hvöttavinnslu, en býður upp á miklu nákvæmari talningu á táknunum.",
"Load koboldcpp order": "Hlaðið inn færslu af koboldcpp",
"Use Google Tokenizer": "Notaðu Google Tokenizer"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Preimpostazioni Kobold",
"guikoboldaisettings": "Impostazioni dell'interfaccia KoboldAI",
"novelaipreserts": "Preimpostazioni NovelAI",
"default": "Predefinito",
"openaipresets": "Preimpostazioni OpenAI",
"text gen webio(ooba) presets": "Preimpostazioni WebUI(ooba) per la generazione di testo",
"response legth(tokens)": "Lunghezza della risposta (token)",
@@ -495,7 +494,6 @@
"Global Lore First": "Lore Globale Prima",
"Recursive Scan": "Scansione Ricorsiva",
"Case Sensitive": "Sensibile alle Maiuscole",
"Match whole words": "Corrispondi a parole intere",
"Alert On Overflow": "Avviso Su Overflow",
"World/Lore Editor": "Editor di Mondo/Lore",
"--- None ---": "--- Nessuno ---",
@@ -917,5 +915,5 @@
"Use Google Tokenizer": "Usa il Tokenizer di Google"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Koboldのプリセット",
"guikoboldaisettings": "KoboldAIのGUI設定",
"novelaipreserts": "NovelAIのプリセット",
"default": "デフォルト",
"openaipresets": "OpenAIのプリセット",
"text gen webio(ooba) presets": "WebUI(ooba)のプリセット",
"response legth(tokens)": "応答の長さ(トークン数)",
@@ -140,7 +139,7 @@
"Influences bot behavior in its responses": "返信でボットの動作に影響を与えます",
"Connect": "接続",
"Test Message": "テストメッセージ",
"API": "API",
"API": "API",
"KoboldAI": "KoboldAI",
"Use Horde": "ホードを使用",
"API url": "API URL",
@@ -494,7 +493,6 @@
"Global Lore First": "グローバルロアを最初に表示",
"Recursive Scan": "再帰的スキャン",
"Case Sensitive": "大文字と小文字を区別する",
"Match whole words": "完全な単語の一致",
"Alert On Overflow": "オーバーフロー時に警告",
"World/Lore Editor": "ワールド/ロアの編集",
"--- None ---": "--- なし ---",
@@ -914,5 +912,5 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Googleモデル用の適切なトークナイザーを使用します。 API経由で。 処理が遅くなりますが、トークンの数え上げがはるかに正確になります。",
"Load koboldcpp order": "koboldcppオーダーを読み込む",
"Use Google Tokenizer": "Googleトークナイザーを使用"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "코볼드 사전 설정",
"guikoboldaisettings": "KoboldAI 인터페이스 설정",
"novelaipreserts": "NovelAI 사전 설정",
"default": "기본값",
"openaipresets": "OpenAI 사전 설정",
"text gen webio(ooba) presets": "텍스트 생성 WebUI(ooba) 사전 설정",
"response legth(tokens)": "응답 길이 (토큰)",
@@ -425,7 +424,7 @@
"Start new chat": "새로운 채팅 시작",
"View past chats": "과거 채팅 보기",
"Delete messages": "메시지 삭제",
"Impersonate": "사칭",
"Impersonate": "대신 말하기",
"Regenerate": "재생성",
"PNG": "PNG",
"JSON": "JSON",
@@ -495,7 +494,6 @@
"Global Lore First": "글로벌 로어 우선",
"Recursive Scan": "재귀 스캔",
"Case Sensitive": "대소문자 구분",
"Match whole words": "전체 단어 일치",
"Alert On Overflow": "오버플로우 알림",
"World/Lore Editor": "월드/로어 편집기",
"--- None ---": "--- 없음 ---",
@@ -914,7 +912,30 @@
"Learn how to contribute your idle GPU cycles to the Horde": "여유로운 GPU 주기를 호드에 기여하는 방법 배우기",
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Google 모델용 적절한 토크나이저를 사용하여 API를 통해 제공됩니다. 더 느린 프롬프트 처리지만 훨씬 정확한 토큰 계산을 제공합니다.",
"Load koboldcpp order": "코볼드 CPP 순서로 로드",
"Use Google Tokenizer": "Google 토크나이저 사용"
"Use Google Tokenizer": "구글 토크나이저 사용",
"Hide Chat Avatars": "채팅 아바타 숨기기",
"Hide avatars in chat messages.": "채팅 메시지에서 아바타 숨김.",
"Avatar Hover Magnification": "아바타 마우스오버 시 확대",
"Enable magnification for zoomed avatar display.": "마우스 오버 시 아바타가 커지도록 설정하세요.",
"AutoComplete Settings": "자동 완성 설정",
"Autocomplete Matching": "자동 완성 매칭",
"Starts with": "시작하는 단어로",
"Autocomplete Style": "자동 완성 스타일",
"Includes": "포함하는",
"Fuzzy": "퍼지 매칭",
"Follow Theme": "테마 적용",
"Dark": "다크 모드",
"Sets the font size of the autocomplete.": "자동 완성 글꼴 크기 설정",
"Autocomplete Width": "자동 완성 너비 조절",
"Parser Flags": "파서 플래그 설정",
"Sets default flags for the STscript parser.": "STscript 파서 기본 플래그 설정",
"Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "모든 구분자를 백슬래시로 이스케이핑하고, 백슬래시 자체도 이스케이프할 수 있도록 엄격한 방식으로 전환합니다.",
"STscript Settings": "STscript 설정",
"Smooth Streaming": "부드러운 스트리밍",
"Experimental feature. May not work for all backends.": "실험적인 기능으로, 모든 백엔드에서 작동이 보장되지는 않을 수 있습니다.",
"Char List Subheader": "문자 목록 하위 제목",
"Account": "계정",
"Theme Colors": "테마 색상",
"# Messages to Load": "로딩할 메시지 수"
}
}

View File

@@ -1,6 +1,7 @@
[
{ "lang": "ar-sa", "display": "عربي (Arabic)" },
{ "lang": "zh-cn", "display": "简体中文 (Chinese) (Simplified)" },
{ "lang": "zh-tw", "display": "繁體中文 (Chinese) (Taiwan)" },
{ "lang": "nl-nl", "display": "Nederlands (Dutch)" },
{ "lang": "de-de", "display": "Deutsch (German)" },
{ "lang": "fr-fr", "display": "Français (French)" },

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Kobold voorinstellingen",
"guikoboldaisettings": "KoboldAI-interface-instellingen",
"novelaipreserts": "NovelAI-voorinstellingen",
"default": "Standaard",
"openaipresets": "OpenAI-voorinstellingen",
"text gen webio(ooba) presets": "WebUI(ooba)-voorinstellingen voor tekstgeneratie",
"response legth(tokens)": "Reactielengte (tokens)",
@@ -495,7 +494,6 @@
"Global Lore First": "Globale Lore Eerst",
"Recursive Scan": "Recursieve Scan",
"Case Sensitive": "Hoofdlettergevoelig",
"Match whole words": "Hele woorden matchen",
"Alert On Overflow": "Waarschuwing bij overloop",
"World/Lore Editor": "Wereld/Lore Editor",
"--- None ---": "--- Geen ---",
@@ -917,5 +915,5 @@
"Use Google Tokenizer": "Google Tokenizer gebruiken"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Configurações predefinidas do Kobold",
"guikoboldaisettings": "Configurações da interface do KoboldAI",
"novelaipreserts": "Configurações predefinidas do NovelAI",
"default": "Padrão",
"openaipresets": "Configurações predefinidas do OpenAI",
"text gen webio(ooba) presets": "Configurações predefinidas do WebUI(ooba) para geração de texto",
"response legth(tokens)": "Comprimento da resposta (tokens)",
@@ -493,7 +492,6 @@
"Global Lore First": "Lore Global Primeiro",
"Recursive Scan": "Verificação Recursiva",
"Case Sensitive": "Sensível a Maiúsculas",
"Match whole words": "Corresponder palavras inteiras",
"Alert On Overflow": "Alerta em Overflow",
"World/Lore Editor": "Editor de Mundo/Lore",
"--- None ---": "--- Nenhum ---",
@@ -915,5 +913,5 @@
"Use Google Tokenizer": "Usar Tokenizer do Google"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Пресеты для Kobold",
"guikoboldaisettings": "Настройки из интерфейса KoboldAI",
"novelaipreserts": "Пресеты для NovelAI",
"default": "По умолчанию",
"openaipresets": "Пресеты для OpenAI",
"text gen webio(ooba) presets": "Пресеты для WebUI(ooba)",
"response legth(tokens)": "Ответ (в токенах)",
@@ -38,7 +37,7 @@
"LLaMA / Mistral / Yi models only": "Только для моделей LLaMA / Mistral / Yi. Перед этим обязательно выберите подходящий токенизатор.\nПоследовательности, которых не должно быть на выходе.\nОдна на строку. Текст или [идентификаторы токенов].\nМногие токены имеют пробел впереди. Используйте счетчик токенов, если не уверены.",
"Example: some text [42, 69, 1337]": "Пример:\nкакой-то текст\n[42, 69, 1337]",
"Classifier Free Guidance. More helpful tip coming soon": "Classifier Free Guidance. Чуть позже опишем более подробно",
"Scale": "Масштаб",
"Scale": "Scale",
"GBNF Grammar": "Грамматика GBNF",
"Usage Stats": "Статистика исп.",
"Click for stats!": "Нажмите для получения статистики!",
@@ -97,7 +96,7 @@
"Sequences you don't want to appear in the output. One per line.": "Строки, которых не должно быть в выходном тексте. По одной на строчку.",
"AI Module": "Модуль ИИ",
"Changes the style of the generated text.": "Изменяет стиль создаваемого текста.",
"Used if CFG Scale is unset globally, per chat or character": "Используется, если масштаб CFG не установлен глобально, для каждого чата или персонажа.",
"Used if CFG Scale is unset globally, per chat or character": "Используется, если CFG Scale не установлен глобально, для каждого чата или персонажа.",
"Inserts jailbreak as a last system message.": "Вставлять JailBreak последним системным сообщением.",
"This tells the AI to ignore its usual content restrictions.": "Сообщает AI о необходимости игнорировать стандартные ограничения контента.",
"NSFW Encouraged": "Поощрять NSFW",
@@ -262,7 +261,7 @@
"Auto-Continue": "Авто-продолжение",
"Collapse Consecutive Newlines": "Сворачивать последовательные новые строки",
"Allow for Chat Completion APIs": "Разрешить для API Chat Completion",
"Target length (tokens)": "Целевая длина (токены)",
"Target length (tokens)": "Целевая длина (в токенах)",
"Keep Example Messages in Prompt": "Сохранять примеры сообщений в промпте",
"Remove Empty New Lines from Output": "Удалять пустые строчки из вывода",
"Disabled for all models": "Выключено для всех моделей",
@@ -276,7 +275,7 @@
"World Info": "Информация о мире",
"Scan Depth": "Глубина сканирования",
"Case-Sensitive": "С учетом регистра",
"Match Whole Words": "Только целые слова",
"Match Whole Words": "Только полное совпадение",
"Use global setting": "Использовать глобальную настройку",
"Yes": "Да",
"No": "Нет",
@@ -300,11 +299,11 @@
"Chat Style": "Стиль чата",
"Default": "По умолчанию",
"Bubbles": "Пузыри",
"No Blur Effect": "Отключить эффект размытия",
"No Text Shadows": "Отключить тень от текста",
"No Blur Effect": "Отключить размытие",
"No Text Shadows": "Отключить тень текста",
"Waifu Mode": "Рeжим Вайфу",
"Message Timer": "Таймер сообщений",
"Model Icon": "Показать значки модели",
"Model Icon": "Значки моделей",
"# of messages (0 = disabled)": "# сообщений (0 = отключено)",
"Advanced Character Search": "Расширенный поиск по персонажам",
"Allow {{char}}: in bot messages": "Показывать {{char}}: в ответах",
@@ -314,7 +313,7 @@
"Lorebook Import Dialog": "Показывать окно импорта лорбука",
"MUI Preset": "Пресет MUI:",
"If set in the advanced character definitions, this field will be displayed in the characters list.": "Если это поле задано в расширенных параметрах персонажа, оно будет отображаться в списке персонажей.",
"Relaxed API URLS": "Смягченные URL-адреса API",
"Relaxed API URLS": "Смягчённые адреса API",
"Custom CSS": "Пользовательский CSS",
"Default (oobabooga)": "По умолчанию (oobabooga)",
"Mancer Model": "Модель Mancer",
@@ -381,7 +380,7 @@
"text": "текст",
"Delete": "Удалить",
"Cancel": "Отменить",
"Advanced Defininitions": "Продвинутое описание",
"Advanced Defininitions": "Расширенное описание",
"Personality summary": "Сводка по личности",
"A brief description of the personality": "Краткое описание личности",
"Scenario": "Сценарий",
@@ -431,7 +430,7 @@
"JSON": "JSON",
"presets": "Пресеты",
"Message Sound": "Звук сообщения",
"Author's Note": "Пометки автора",
"Author's Note": "Заметки автора",
"Send Jailbreak": "Отправлять джейлбрейк",
"Replace empty message": "Заменять пустые сообщения",
"Send this text instead of nothing when the text box is empty.": "Этот текст будет отправлен в случае отсутствия текста на отправку.",
@@ -475,7 +474,7 @@
"--- Pick to Edit ---": "--- Выберите для редактирования ---",
"or": "или",
"New": "Новый",
"Priority": "Приритет",
"Priority": "Приоритет",
"Custom": "Пользовательский",
"Title A-Z": "Название от A до Z",
"Title Z-A": "Название от Z до A",
@@ -495,7 +494,6 @@
"Global Lore First": "Сначала глобальный лор",
"Recursive Scan": "Рекурсивное сканирование",
"Case Sensitive": "Учитывать регистр",
"Match whole words": "Только полное совпадение",
"Alert On Overflow": "Оповещение о переполнении",
"World/Lore Editor": "Редактировать мир или лор",
"--- None ---": "--- Отсутствует ---",
@@ -528,7 +526,7 @@
"UI Border": "Границы UI",
"Chat Style:": "Стиль чата",
"Chat Width (PC)": "Ширина чата (для ПК)",
"Chat Timestamps": "Временные метки в чате",
"Chat Timestamps": "Метки времени в чате",
"Tags as Folders": "Теги как папки",
"Chat Truncation": "Усечение чата",
"(0 = unlimited)": "(0 = неограниченное)",
@@ -559,8 +557,8 @@
"Disables animations and transitions": "Отключение анимаций и переходов.",
"removes blur from window backgrounds": "Убрать размытие с фона окон, чтобы ускорить рендеринг.",
"Remove text shadow effect": "Удаление эффекта тени от текста.",
"Reduce chat height, and put a static sprite behind the chat window": "Уменьшитm высоту чата и поместить статичный спрайт за окном чата.",
"Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список контекстных элементов 'Действия с сообщением' для сообщений чата, а не прятать их за '...'.",
"Reduce chat height, and put a static sprite behind the chat window": "Уменьшить высоту чата и поместить статичный спрайт за окном чата.",
"Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список действий с сообщением, а не прятать их за '...'.",
"Alternative UI for numeric sampling parameters with fewer steps": "Альтернативный пользовательский интерфейс для числовых параметров выборки с меньшим количеством шагов.",
"Entirely unrestrict all numeric sampling parameters": "Полностью разграничить все числовые параметры выборки.",
"Time the AI's message generation, and show the duration in the chat log": "Время генерации сообщений ИИ и его показ в журнале чата.",
@@ -600,7 +598,7 @@
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Включить авто-свайп. Настройки в этом разделе действуют только при включенном авто-свайпе.",
"If the generated message is shorter than this, trigger an auto-swipe": "Если сгенерированное сообщение короче этого значения, срабатывает авто-свайп.",
"Reload and redraw the currently open chat": "Перезагрузить и перерисовать открытый в данный момент чат.",
"Auto-Expand Message Actions": "Развернуть контекстные элементы",
"Auto-Expand Message Actions": "Развернуть действия",
"Not Connected": "Не подключено",
"Persona Management": "Управление персоной",
"Persona Description": "Описание персоны",
@@ -629,16 +627,15 @@
"Most chats": "Больше всего чатов",
"Least chats": "Меньше всего чатов",
"Back": "Назад",
"Prompt Overrides (For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct mode)": "Перезапись промпта (Для OpenAI/Claude/Scale API, Window/OpenRouter, и режима Instruct)",
"Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы использовать соответствующий промпт из системных настроек",
"Prompt Overrides": "Индивидуальный промпт",
"(For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct Mode)": "(для API OpenAI/Claude/Scale, Window/OpenRouter, а также режима Instruct)",
"Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы вставить соответствующий промпт из системных настроек",
"Main Prompt": "Основной промпт",
"Jailbreak": "Джейлбрейк",
"Creator's Metadata (Not sent with the AI prompt)": "Метаданные (не отправляются ИИ)",
"Everything here is optional": "Все поля необязательные",
"Created by": "Автор",
"Character Version": "Версия персонажа",
"Tags to Embed": "Встраиваемые теги",
"How often the character speaks in group chats!": "Как часто персонаж говорит в групповых чатах",
"Important to set the character's writing style.": "Серьёзно влияет на стиль письма персонажа.",
"ATTENTION!": "ВНИМАНИЕ!",
"Samplers Order": "Порядок сэмплеров",
@@ -655,7 +652,7 @@
"Use 'Unlocked Context' to enable chunked generation.": "Использовать 'Неограниченный контекст' для активации кусочной генерации",
"It extends the context window in exchange for reply generation speed.": "Увеличивает размер контекста в обмен на скорость генерации.",
"Continue": "Продолжить",
"CFG Scale": "Масштаб CFG",
"CFG Scale": "CFG Scale",
"Editing:": "Изменения",
"AI reply prefix": "Префикс для ответа ИИ",
"Custom Stopping Strings": "Стоп-строки",
@@ -671,9 +668,9 @@
"Chat Name (Optional)": "Название чата (необязательно)",
"Filter...": "Фильтры...",
"Search...": "Поиск...",
"Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "Все содержание этой ячейки будет заменять стандартный Промт",
"Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Все содержание этой ячейки будет заменять стандартный Джейлбрейк",
"(Botmaker's name / Contact Info)": "(Имя автора / Контакты)",
"Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "Все содержимое этого поля будет заменять стандартный промпт",
"Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Все содержимое этого поля будет заменять стандартный джейлбрейк",
"(Botmaker's name / Contact Info)": "(Имя автора, контакты)",
"(If you want to track character versions)": "Если вы хотите отслеживать версии персонажа",
"(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(Описание персонажа, советы по использованию, список моделей, на которых он тестировался. Информация будет отображаться в списке персонажей)",
"(Write a comma-separated list of tags)": "(Список тегов через запятую)",
@@ -713,12 +710,12 @@
"Restore defaul note": "Восстановить стандартную заметку",
"API Connections": "Соединения с API",
"Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь с плохими ответами ставя в очередь только подтвержденных работников. Может замедлить время ответа.",
"Clear your API key": "Очистите свой ключ от API",
"Clear your API key": "Стереть ключ от API",
"Refresh models": "Обновить модели",
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "Проверка работоспособности вашего соединения с API. Знайте, что оно будет отправлено от вашего лица.",
"Create New": "Создать новое",
"Edit": "Изменить",
"Edit": "Редактировать",
"Locked = World Editor will stay open": "Закреплено = Редактор мира останется открытым",
"Entries can activate other entries by mentioning their keywords": "Записи могут активировать другие записи, если в них содержатся ключевые слова",
"Lookup for the entry keys in the context will respect the case": "Большая буква имеет значение при активации ключевого слова",
@@ -847,7 +844,7 @@
"Underlined Text": "Подчёркнутый",
"Token Probabilities": "Вероятности токенов",
"Close chat": "Закрыть чат",
"Manage chat files": "Управление файлами чата",
"Manage chat files": "Управление чатами",
"Import Extension From Git Repo": "Импортировать расширение из Git Repository",
"Install extension": "Установить расширение",
"Manage extensions": "Управление расширениями",
@@ -863,12 +860,12 @@
"When this is off, responses will be displayed all at once when they are complete.": "Если параметр выключен, ответы будут отображаться сразу целиком, и только после полного завершения генерации.",
"Quick Prompts Edit": "Быстрое редактирование промптов",
"Enable OpenAI completion streaming": "Включить стриминг OpenAI",
"Main": "Главное",
"Main": "Основной",
"Utility Prompts": "Служебные промпты",
"Add character names": "Добавить имена персонажей",
"Send names in the message objects. Helps the model to associate messages with characters.": "Отправить имена в объектах сообщений. Помогает модели ассоциировать сообщения с персонажами.",
"Continue prefill": "Префилл для продолжения",
"Continue sends the last message as assistant role instead of system message with instruction.": "Продолжение отправляет последнее сообщение в роли ассистента, а не системное сообщение с инструкцией.",
"Continue sends the last message as assistant role instead of system message with instruction.": "Продолжение отправляет последнее сообщение в роли ассистента, вместо системного сообщения с инструкцией.",
"Squash system messages": "Склеивать сообщения системыы",
"Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Объединяет последовательные системные сообщения в одно (за исключением примеров диалогов). Может улучшить согласованность для некоторых моделей.",
"Send inline images": "Отправлять встроенные изображения",
@@ -888,11 +885,11 @@
"Want to update?": "Хотите обновиться?",
"How to start chatting?": "Как начать общение?",
"Click": "Нажмите",
"and select a": " и выберите",
"and select a": " и выберите ",
"Chat API": "API чата",
"and pick a character": "и выберите персонажа",
"in the chat bar": " в поле чата",
"Confused or lost?": "Запутались или потерялись?",
"Confused or lost?": "Не можете в чём-то разобраться?",
"click these icons!": "нажмите на эти значки!",
"SillyTavern Documentation Site": "Сайт документации SillyTavern",
"Extras Installation Guide": "Руководство по установке Extras",
@@ -973,12 +970,209 @@
"Most tokens have a leading space.": "У большинства токенов в начале пробел.",
"Prompts": "Промпты",
"Text or token ids": "Текст или [идентификаторы токенов]",
"World Info Format Template": "Шаблон форматирования информации о мире",
"World Info Format Template": "Шаблон оформления информации о мире",
"Wraps activated World Info entries before inserting into the prompt.": "Дополняет информацию об активном на данный момент мире перед её отправкой в промпт.",
"Doesn't work? Try adding": "Не работает? Попробуйте добавить в конце",
"at the end!": "!",
"Authorize": "Авторизоваться",
"No persona description": "[Нет описания]",
"Not connected to API!": "Нет соединения с API!",
"Type a message, or /? for help": "Введите сообщение, или /? для получения справки по командам"
"Type a message, or /? for help": "Введите сообщение, или /? для получения справки по командам",
"Welcome to SillyTavern!": "Добро пожаловать в SillyTavern!",
"Won't be shared with the character card on export.": "Не попадут в карточку персонажа при экспорте.",
"Web-search": "Веб-поиск",
"Persona Name:": "Имя персоны:",
"User first message": "Первое сообщение пользователя",
"extension_token_counter": "Токенов:",
"Character's Note": "Заметка о персонаже",
"(Text to be inserted in-chat @ designated depth and role)": "Этот текст будет вставлен в чат на заданную глубину и с определённой ролью",
"@ Depth": "Глубина",
"Role": "Роль",
"System": "Система",
"User": "Пользователь",
"Assistant": "Ассистент",
"How often the character speaks in": "Как часто персонаж говорит в",
"group chats!": "групповых чатах!",
"Creator's Metadata": "Метаданные",
"(Not sent with the AI Prompt)": "(не отправляются ИИ)",
"New Chat": "Новый чат",
"Import Chat": "Импорт чата",
"Chat Lore": "Лор чата",
"Chat Lorebook for": "Лорбук для чата",
"A selected World Info will be bound to this chat.": "Выбранный мир будет привязан к этому чату. При генерации ответа ИИ он будет совмещён с записями из глобального лорбука и лорбука персонажа.",
"Missing key": "❌ Ключа нет",
"Key saved": "✔️ Ключ сохранён",
"Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "Использовать токенайзер для моделей Jurassic, эффективнее GPT-токенайзера",
"Use system prompt (Gemini 1.5 pro+ only)": "Использовать системный промпт (только для Gemini 1.5 pro и выше)",
"Experimental feature. May not work for all backends.": "Экспериментальная возможность, на некоторых бэкендах может не работать.",
"Avatar Hover Magnification": "Зум аватарки по наведению",
"Enable magnification for zoomed avatar display.": "Добавляет возможность приближать увеличенную версию аватарки.",
"Unique to this chat": "Только для текущего чата",
"Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Чекпоинты наследуют заметки от родительского чата, но впоследствие их всегда можно изменить.",
"Include in World Info Scanning": "Учитывать при сканировании Информации о мире",
"Before Main Prompt / Story String": "Перед основным промптом / строкой истории",
"After Main Prompt / Story String": "После основного промпта / строки истории",
"In-chat @ Depth": "Встав. на глуб.",
"as": "роль:",
"Insertion Frequency": "Частота вставки",
"(0 = Disable, 1 = Always)": "(0 = никогда, 1 = всегда)",
"User inputs until next insertion:": "Ваших сообщений до след. вставки:",
"Character Author's Note (Private)": "Заметки автора персонажа (личные)",
"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.": "Автоматически применятся к этому персонажу в качестве заметок автора. Будут использоваться в группах, но при активном групповом чате к редактированию недоступны.",
"Use character author's note": "Использовать заметки автора персонажа",
"Replace Author's Note": "Вместо заметок автора",
"Top of Author's Note": "Сверху от заметок автора",
"Bottom of Author's Note": "Снизу от заметок автора",
"Default Author's Note": "Стандартные заметки автора",
"Will be automatically added as the Author's Note for all new chats.": "Будут автоматически добавляться во все новые чаты в качестве Заметок автора",
"1 = disabled": "1 = откл.",
"write short replies, write replies using past tense": "пиши короткие ответы, пиши в настоящем времени",
"Positive Prompt": "Положительный промпт",
"Character CFG": "CFG для персонажа",
"Will be automatically added as the CFG for this character.": "Автоматически применится к персонажу как его CFG.",
"Global CFG": "Глобальный CFG",
"Will be used as the default CFG options for every chat unless overridden.": "Будет применяться как стандартный CFG для всех чатов, если не указаны индивидуальные настройки.",
"CFG Prompt Cascading": "Совмещение CFG-промптов",
"Combine positive/negative prompts from other boxes.": "Комбинировать различные положительные и негативные промпты.",
"For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "К примеру, если отметить галочки с чатом, персонажем и глобальной настройкой, то все эти негативы соберутся в одну строку, разделённую запятыми.",
"Always Include": "Всегда применять",
"Chat Negatives": "Негативы от чата",
"Character Negatives": "Негативы от персонажа",
"Global Negatives": "Глобальные негативы",
"Custom Separator:": "Кастомный разделитель:",
"Insertion Depth:": "Глубина вставки:",
"Chat CFG": "CFG для чата",
"Chat backgrounds generated with the": "Здесь будут появляться фоны, сгенерированные расширением",
"extension will appear here.": ".",
"Prevent further recursion (this entry will not activate others)": "Пресечь дальнейшую рекурсию (эта запись не будет активировать другие)",
"Alert if your world info is greater than the allocated budget.": "Оповещать, если ваш мир выходит за выделенный бюджет.",
"Convert to Persona": "Преобразовать в персону",
"Link to Source": "Ссылка на источник",
"Replace / Update": "Заменить / Обновить",
"Smoothing Curve": "Кривая сглаживания",
"Message Actions": "Действия с сообщением",
"SillyTavern is aimed at advanced users.": "SillyTavern рассчитана на продвинутых пользователей.",
"If you're new to this, enable the simplified UI mode below.": "Если вы новичок, советуем включить упрощённый UI.",
"Enable simple UI mode": "Включить упрощённый UI",
"welcome_message_part_1": "Ознакомьтесь с",
"welcome_message_part_2": "официальной документацией",
"welcome_message_part_3": ".",
"welcome_message_part_4": "Введите",
"welcome_message_part_5": "в чате, чтобы получить справку по командам и макросам.",
"welcome_message_part_6": "Заходите на наш",
"Discord server": "Discord-сервер,",
"welcome_message_part_7": "там публикуется много разной полезной информации, в том числе анонсы.",
"Before you get started, you must select a persona name.": "Для начала вам следует выбрать имя своей персоны.",
"welcome_message_part_8": "Его можно будет изменить в любое время через иконку",
"welcome_message_part_9": ".",
"UI Language:": "Язык интерфейса:",
"Ignore EOS Token": "Игнорировать EOS-токен",
"Ignore the EOS Token even if it generates.": "Игнорировать EOS-токен, даже если он сгенерировался.",
"Hide Muted Member Sprites": "Скрыть спрайты заглушенных участников",
"Group generation handling mode": "Генерировать ответы путём...",
"Swap character cards": "Подмены карточки персонажа",
"Join character cards (exclude muted)": "Совмещения карточек (кроме заглушенных)",
"Join character cards (include muted)": "Совмещения карточек (включая заглушенных)",
"Click to allow/forbid the use of external media for this group.": "Нажмите, чтобы разрешить/запретить использование внешних медиа в этой группе.",
"Scenario Format Template": "Шаблон оформления сценария",
"scenario_format_template_part_1": "Используйте",
"scenario_format_template_part_2": "чтобы указать, куда именно вставляется основное содержимое.",
"Personality Format Template": "Шаблон оформления характера",
"Group Nudge Prompt Template": "Шаблон промпта-подсказки для групп",
"Sent at the end of the group chat history to force reply from a specific character.": "Добавляется в конец истории сообщений в групповом чате, чтобы запросить ответ от конкретного персонажа.",
"Set at the beginning of the chat history to indicate that a new chat is about to start.": "Добавляется в начале истории сообщений в качестве указания на то, что дальше начнётся новый чат.",
"New Group Chat": "Новый групповой чат",
"Set at the beginning of the chat history to indicate that a new group chat is about to start.": "Добавляется в начале истории сообщений в качестве указания на то, что дальше начнётся новый групповой чат.",
"New Example Chat": "Новый образец чата",
"Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "Добавляется в начале примеров диалогов в качестве указания на то, что дальше начнётся новый чат-пример.",
"Continue nudge": "Подсказка для продолжения",
"Set at the end of the chat history when the continue button is pressed.": "Добавляется в конец истории чата, когда отправлен запрос на продолжение текущего сообщения.",
"Prompts": "Промпты",
"Your Persona": "Ваша персона",
"Continue Postfix": "Постфикс для продолжения",
"Space": "Пробел",
"Newline": "Новая строка",
"Double Newline": "Две новые строки",
"The next chunk of the continued message will be appended using this as a separator.": "Используется в качестве разделителя между уже имеющимся сообщением и его новым отрывком, при генерации продолжения",
"Regex Editor": "Редактор рег. выражений",
"ext_regex_open_editor": "Открыть редактор",
"ext_regex_import_script": "Импорт скрипта",
"ext_regex_saved_scripts": "Сохранённые скрипты",
"ext_regex_desc": "Regex - это инструмент, позволяющий находить и изменять строки, используя регулярные выражения. Для более подробной информации нажмите ? рядом с заголовком.",
"Input": "Поле ввода",
"ext_regex_test_input_placeholder": "Введите текст...",
"Output": "Результат",
"ext_regex_output_placeholder": "Пусто",
"Script Name": "Название скрипта",
"Find Regex": "Рег. выражение для поиска",
"Replace With": "Замена",
"ext_regex_replace_string_placeholder": "Чтобы вставить всё вхождение рег. выражения, используйте {{match}}. Чтобы вставить группу символов, используйте $1, $2 и т.д.",
"Trim Out": "Усечение",
"ext_regex_trim_placeholder": "Удалить перед обработкой ненужные части текста. Каждый элемент с новой строки.",
"Slash Commands": "Слэш-команды",
"Min Depth": "Мин. глубина",
"ext_regex_min_depth_desc": "При форматировании затрагивать только те сообщения, которые находятся как минимум на глубине N. 0 = последнее сообщение, 1 = предпоследнее и т.д. Учитываются только видимые сообщения, т.е. не скрытые и не системные.",
"ext_regex_max_depth_desc": "При форматировании затрагивать только те сообщения, которые находятся на глубине не более N. 0 = последнее сообщение, 1 = предпоследнее и т.д. Учитываются только видимые сообщения, т.е. не скрытые и не системные.",
"ext_regex_min_depth_placeholder": "Неогранич.",
"ext_regex_other_options": "Другие опции",
"Only Format Display": "Только визуально",
"ext_regex_only_format_prompt_desc": "История чата не изменится, замена будет осуществляться только в промпте (при генерации)",
"Only Format Prompt (?)": "Только промпт",
"Run On Edit": "Выполнять при редактировании",
"Substitute Regex": "Заменить в рег. выражении",
"ext_regex_substitute_regex_desc": "Перед выполнением заменять {{макросы}} в рег. выражении",
"Test Mode": "Протестировать",
"ext_regex_affects": "Затрагивает",
"ext_regex_user_input": "Ваши сообщения",
"ext_regex_ai_output": "Ответы ИИ",
"ext_regex_disable_script": "Отключить скрипт",
"ext_regex_enable_script": "Включить скрипт",
"ext_regex_edit_script": "Редактировать",
"ext_regex_export_script": "Экспортировать",
"ext_regex_delete_script": "Удалить",
"ext_sum_with": "Для пересказа использовать:",
"ext_sum_main_api": "Основное API",
"ext_sum_current_summary": "Текущий пересказ:",
"ext_sum_restore_previous": "Восстановить предыдущий",
"ext_sum_memory_placeholder": "Сгенерированный пересказ будет здесь...",
"ext_sum_force_text": "Пересказать сейчас",
"ext_sum_force_tip": "Сгенерировать пересказ прямо сейчас.",
"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)",
"ext_sum_pause": "Приостановить",
"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 всегда их исключает.",
"ext_sum_no_wi_an": "Без мира и заметок",
"ext_sum_settings_tip": "Изменить промпт пересказа, место для инжекта и т.д.",
"ext_sum_settings": "Настройки пересказа",
"ext_sum_prompt_builder": "Алгоритм формирования промпта",
"ext_sum_prompt_builder_1_desc": "Расширение само составит промпт с учётом непересказанных сообщений. Во время генерации чат недоступен.",
"ext_sum_prompt_builder_1": "Прямой, блокирующий",
"ext_sum_prompt_builder_2_desc": "Расширение само составит промпт с учётом непересказанных сообщений. Во время генерации чат доступен. Может не поддерживаться некоторыми бэкендами.",
"ext_sum_prompt_builder_2": "Прямой, неблокирующий",
"ext_sum_prompt_builder_3_desc": "Расширение будет использовать стандартные основные настройки промпта, и добавит свой промпт в качестве последнего системного сообщения.",
"ext_sum_prompt_builder_3": "Классический, блокирующий",
"Summary Prompt": "Промпт для пересказа",
"ext_sum_restore_default_prompt_tip": "Восстановить стандартный промпт",
"ext_sum_prompt_placeholder": "Этот промпт будет отправлен ИИ при запросе на генерацию пересказа. Макрос {{words}} будет заменён на значение параметра \"Количество слов\".",
"ext_sum_target_length_1": "Целевая длина пересказа (слов):",
"ext_sum_target_length_2": "",
"ext_sum_target_length_3": "",
"ext_sum_api_response_length_1": "Длина ответа от API (токенов):",
"ext_sum_api_response_length_2": "",
"ext_sum_api_response_length_3": " ",
"ext_sum_0_default": "по умолчанию = 0",
"ext_sum_raw_max_msg": "[Прямое форматирование] Макс. сообщений в запросе",
"ext_sum_0_unlimited": "неограничено = 0",
"Update frequency": "Частота обновления",
"ext_sum_update_every_messages_1": "Интервал обновления (кол-во сообщений):",
"ext_sum_update_every_messages_2": "",
"ext_sum_pause": "Приостановить",
"ext_sum_update_every_words_1": "Интервал обновления (кол-во слов):",
"ext_sum_update_every_words_2": "",
"ext_sum_0_disable": "для отключения поставьте 0",
"ext_sum_auto_adjust_desc": "Попытаться автоматически рассчитать значение интервала, исходя из статистики чата",
"ext_sum_both_sliders": "Если оба ползунка отличны от нуля, то оба будут триггерить генерацию пересказа с соответствующей периодичностью.",
"ext_sum_injection_template": "Шаблон для инжекта",
"ext_sum_memory_template_placeholder": "Макрос {{summary}} будет заменён на содержимое пересказа",
"ext_sum_injection_position": "Куда инжектить",
"How many messages before the current end of the chat.": "Сколько сообщений от конца чата."
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Налаштування Kobold",
"guikoboldaisettings": "З інтерфейсу KoboldAI",
"novelaipreserts": "Налаштування NovelAI",
"default": "За замовчуванням",
"openaipresets": "Налаштування OpenAI",
"text gen webio(ooba) presets": "Налаштування Text Completion",
"response legth(tokens)": "Відповідь (токени)",
@@ -495,7 +494,6 @@
"Global Lore First": "Глобальна інформація першою",
"Recursive Scan": "Рекурсивне сканування",
"Case Sensitive": "Чутливість до регістру",
"Match whole words": "Відповідність цілим словам",
"Alert On Overflow": "Сповіщення при переповненні",
"World/Lore Editor": "Редактор світу/книги",
"--- None ---": "--- Нічого ---",

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Cài đặt trước Kobold",
"guikoboldaisettings": "Cài đặt giao diện KoboldAI",
"novelaipreserts": "Cài đặt trước NovelAI",
"default": "Mặc định",
"openaipresets": "Cài đặt trước OpenAI",
"text gen webio(ooba) presets": "Cài đặt trước WebUI(ooba) của máy tạo văn bản",
"response legth(tokens)": "Độ dài phản hồi (trong các token)",
@@ -62,7 +61,7 @@
"Temperature": "Nhiệt độ",
"Frequency Penalty": "Phạt Tần số",
"Presence Penalty": "Phạt Sự hiện",
"Top-p": "Top-p",
"Top-p": "Top-p",
"Display bot response text chunks as they are generated": "Hiển thị các phần văn bản phản hồi của bot khi chúng được tạo ra",
"Top A": "Top A",
"Typical Sampling": "Mẫu Đại diện",
@@ -141,7 +140,7 @@
"Influences bot behavior in its responses": "Ảnh hưởng đến hành vi của bot trong các phản hồi của nó",
"Connect": "Kết nối",
"Test Message": "Tin nhắn kiểm tra",
"API": "Giao diện lập trình ứng dụng (API)",
"API": "Giao diện lập trình ứng dụng (API)",
"KoboldAI": "KoboldAI",
"Use Horde": "Sử dụng Horde",
"API url": "URL API",
@@ -206,7 +205,7 @@
"Scale API Key": "Khóa API của Scale",
"Alt Method": "Phương pháp thay thế",
"AI21 API Key": "Khóa API của AI21",
"AI21 Model": "Mô hình AI21",
"AI21 Model": "Mô hình AI21",
"View API Usage Metrics": "Xem số liệu sử dụng API",
"Show External models (provided by API)": "Hiển thị các mô hình bên ngoài (do API cung cấp)",
"Bot": "Bot:",
@@ -495,7 +494,6 @@
"Global Lore First": "Sử liệu toàn cầu đầu tiên",
"Recursive Scan": "Quét đệ quy",
"Case Sensitive": "Phân biệt chữ hoa chữ thường",
"Match whole words": "Khớp toàn bộ từ",
"Alert On Overflow": "Cảnh báo khi tràn",
"World/Lore Editor": "Trình soạn thảo Thế giới/Sử liệu",
"--- None ---": "--- Không ---",
@@ -915,5 +913,5 @@
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Sử dụng bộ mã hóa phù hợp cho các mô hình của Google thông qua API của họ. Xử lý lời mời chậm hơn, nhưng cung cấp đếm token chính xác hơn nhiều.",
"Load koboldcpp order": "Tải đơn hàng koboldcpp",
"Use Google Tokenizer": "Sử dụng bộ mã hóa của Google"
}
}

View File

@@ -3,7 +3,6 @@
"kobldpresets": "Kobold 预设",
"guikoboldaisettings": "KoboldAI 用户界面设置",
"novelaipreserts": "NovelAI 预设",
"default": "默认",
"openaipresets": "对话补全预设",
"text gen webio(ooba) presets": "WebUI(ooba) 预设",
"response legth(tokens)": "响应长度(以词符数计)",
@@ -495,7 +494,6 @@
"Global Lore First": "全局世界书优先",
"Recursive Scan": "递归扫描",
"Case Sensitive": "区分大小写",
"Match whole words": "完整匹配单词",
"Alert On Overflow": "溢出警报",
"World/Lore Editor": "世界书编辑器",
"--- None ---": "--- 无 ---",

1192
public/locales/zh-tw.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,13 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="darkreader-lock">
<meta name="robots" content="noindex, nofollow" />
<style>
/* Put critical CSS here. The rest should go in stylesheets. */
body {
background-color: rgb(36, 36, 37);
}
</style>
<link rel="preload" as="style" href="style.css">
<link rel="apple-touch-icon" sizes="57x57" href="img/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="img/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="img/apple-icon-114x114.png" />
@@ -20,8 +27,8 @@
<link rel="manifest" crossorigin="use-credentials" href="manifest.json">
<link href="webfonts/NotoSans/stylesheet.css" rel="stylesheet">
<!-- fontawesome webfonts-->
<link href="css/fontawesome.css" rel="stylesheet">
<link href="css/solid.css" rel="stylesheet">
<link href="css/fontawesome.min.css" rel="stylesheet">
<link href="css/solid.min.css" rel="stylesheet">
<link href="css/user.css" rel="stylesheet">
<script src="lib/jquery-3.5.1.min.js"></script>
<script src="scripts/login.js"></script>

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ import { is_group_generating } from './group-chats.js';
import { Message, TokenHandler } from './openai.js';
import { power_user } from './power-user.js';
import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
import { debounce_timeout } from './constants.js';
function debouncePromise(func, delay) {
let timeoutId;
@@ -294,7 +295,7 @@ class PromptManager {
this.handleCharacterReset = () => { };
/** Debounced version of render */
this.renderDebounced = debounce(this.render.bind(this), 1000);
this.renderDebounced = debounce(this.render.bind(this), debounce_timeout.relaxed);
}
@@ -776,7 +777,7 @@ class PromptManager {
const promptOrder = this.getPromptOrderForCharacter(character);
const index = promptOrder.findIndex(entry => entry.identifier === prompt.identifier);
if (-1 === index) promptOrder.push({ identifier: prompt.identifier, enabled: false });
if (-1 === index) promptOrder.unshift({ identifier: prompt.identifier, enabled: false });
}
/**
@@ -1286,7 +1287,7 @@ class PromptManager {
} else if (!entry.enabled && entry.identifier === 'main') {
// Some extensions require main prompt to be present for relative inserts.
// So we make a GMO-free vegan replacement.
const prompt = this.getPromptById(entry.identifier);
const prompt = structuredClone(this.getPromptById(entry.identifier));
prompt.content = '';
if (prompt) promptCollection.add(this.preparePrompt(prompt));
}

View File

@@ -36,6 +36,7 @@ import { debounce, getStringHash, isValidUrl } from './utils.js';
import { chat_completion_sources, oai_settings } from './openai.js';
import { getTokenCountAsync } from './tokenizers.js';
import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer } from './textgen-settings.js';
import { debounce_timeout } from './constants.js';
import Bowser from '../lib/bowser.min.js';
@@ -54,7 +55,7 @@ var retry_delay = 500;
let counterNonce = Date.now();
const observerConfig = { childList: true, subtree: true };
const countTokensDebounced = debounce(RA_CountCharTokens, 1000);
const countTokensDebounced = debounce(RA_CountCharTokens, debounce_timeout.relaxed);
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
@@ -377,6 +378,7 @@ function RA_autoconnect(PrevApi) {
|| (secret_state[SECRET_KEYS.MISTRALAI] && oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI)
|| (secret_state[SECRET_KEYS.COHERE] && oai_settings.chat_completion_source == chat_completion_sources.COHERE)
|| (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)
|| (secret_state[SECRET_KEYS.GROQ] && oai_settings.chat_completion_source == chat_completion_sources.GROQ)
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
) {
$('#api_button_openai').trigger('click');
@@ -422,7 +424,7 @@ function restoreUserInput() {
const userInput = LoadLocal('userInput');
if (userInput) {
$('#send_textarea').val(userInput).trigger('input');
$('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles:true }));
}
}
@@ -700,12 +702,12 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
*/
function autoFitSendTextArea() {
const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight);
if (sendTextArea.scrollHeight == sendTextArea.offsetHeight) {
if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) {
// Needs to be pulled dynamically because it is affected by font size changes
const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height');
sendTextArea.style.height = sendTextAreaMinHeight;
}
sendTextArea.style.height = sendTextArea.scrollHeight + 0.3 + 'px';
sendTextArea.style.height = sendTextArea.scrollHeight + 3 + 'px';
if (!isFirefox) {
const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom));
@@ -1131,6 +1133,11 @@ export function initRossMods() {
return;
}
if ($('#dialogue_del_mes_cancel').is(':visible')) {
$('#dialogue_del_mes_cancel').trigger('click');
return;
}
if ($('.drawer-content')
.not('#WorldInfo')
.not('#left-nav-panel')

View File

@@ -9,9 +9,12 @@ import {
} from '../script.js';
import { selected_group } from './group-chats.js';
import { extension_settings, getContext, saveMetadataDebounced } from './extensions.js';
import { registerSlashCommand } from './slash-commands.js';
import { getCharaFilename, debounce, delay } from './utils.js';
import { getTokenCountAsync } from './tokenizers.js';
import { debounce_timeout } from './constants.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
export { MODULE_NAME as NOTE_MODULE_NAME };
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
@@ -84,9 +87,9 @@ function updateSettings() {
setFloatingPrompt();
}
const setMainPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_prompt_token_counter').text(await getTokenCountAsync(value)), 1000);
const setCharaPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_chara_token_counter').text(await getTokenCountAsync(value)), 1000);
const setDefaultPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_default_token_counter').text(await getTokenCountAsync(value)), 1000);
const setMainPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_prompt_token_counter').text(await getTokenCountAsync(value)), debounce_timeout.relaxed);
const setCharaPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_chara_token_counter').text(await getTokenCountAsync(value)), debounce_timeout.relaxed);
const setDefaultPromptTokenCounterDebounced = debounce(async (value) => $('#extension_floating_default_token_counter').text(await getTokenCountAsync(value)), debounce_timeout.relaxed);
async function onExtensionFloatingPromptInput() {
chat_metadata[metadata_keys.prompt] = $(this).val();
@@ -454,9 +457,59 @@ export function initAuthorsNote() {
});
$('#option_toggle_AN').on('click', onANMenuItemClick);
registerSlashCommand('note', setNoteTextCommand, [], '<span class=\'monospace\'>(text)</span> sets an author\'s note for the currently selected chat', true, true);
registerSlashCommand('depth', setNoteDepthCommand, [], '<span class=\'monospace\'>(number)</span> sets an author\'s note depth for in-chat positioning', true, true);
registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], '<span class=\'monospace\'>(number)</span> sets an author\'s note insertion frequency', true, true);
registerSlashCommand('pos', setNotePositionCommand, ['position'], '(<span class=\'monospace\'>chat</span> or <span class=\'monospace\'>scenario</span>) sets an author\'s note position', true, true);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'note',
callback: setNoteTextCommand,
unnamedArgumentList: [
new SlashCommandArgument(
'text', [ARGUMENT_TYPE.STRING], true,
),
],
helpString: `
<div>
Sets an author's note for the currently selected chat.
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'depth',
callback: setNoteDepthCommand,
unnamedArgumentList: [
new SlashCommandArgument(
'number', [ARGUMENT_TYPE.NUMBER], true,
),
],
helpString: `
<div>
Sets an author's note depth for in-chat positioning.
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'freq',
callback: setNoteIntervalCommand,
namedArgumentList: [],
unnamedArgumentList: [
new SlashCommandArgument(
'number', [ARGUMENT_TYPE.NUMBER], true,
),
],
helpString: `
<div>
Sets an author's note insertion frequency.
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pos',
callback: setNotePositionCommand,
namedArgumentList: [],
unnamedArgumentList: [
new SlashCommandArgument(
'position', [ARGUMENT_TYPE.STRING], true, false, null, ['chat', 'scenario'],
),
],
helpString: `
<div>
Sets an author's note position.
</div>
`,
}));
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
}

View File

@@ -0,0 +1,792 @@
import { power_user } from '../power-user.js';
import { debounce, escapeRegex } from '../utils.js';
import { AutoCompleteOption } from './AutoCompleteOption.js';
import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
import { BlankAutoCompleteOption } from './BlankAutoCompleteOption.js';
// eslint-disable-next-line no-unused-vars
import { AutoCompleteNameResult } from './AutoCompleteNameResult.js';
import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';
/**@readonly*/
/**@enum {Number}*/
export const AUTOCOMPLETE_WIDTH = {
'INPUT': 0,
'CHAT': 1,
'FULL': 2,
};
export class AutoComplete {
/**@type {HTMLTextAreaElement}*/ textarea;
/**@type {boolean}*/ isFloating = false;
/**@type {()=>boolean}*/ checkIfActivate;
/**@type {(text:string, index:number) => Promise<AutoCompleteNameResult>}*/ getNameAt;
/**@type {boolean}*/ isActive = false;
/**@type {boolean}*/ isReplaceable = false;
/**@type {boolean}*/ isShowingDetails = false;
/**@type {boolean}*/ wasForced = false;
/**@type {boolean}*/ isForceHidden = false;
/**@type {boolean}*/ canBeAutoHidden = false;
/**@type {string}*/ text;
/**@type {AutoCompleteNameResult}*/ parserResult;
/**@type {AutoCompleteSecondaryNameResult}*/ secondaryParserResult;
get effectiveParserResult() { return this.secondaryParserResult ?? this.parserResult; }
/**@type {string}*/ name;
/**@type {boolean}*/ startQuote;
/**@type {boolean}*/ endQuote;
/**@type {number}*/ selectionStart;
/**@type {RegExp}*/ fuzzyRegex;
/**@type {AutoCompleteOption[]}*/ result = [];
/**@type {AutoCompleteOption}*/ selectedItem = null;
/**@type {HTMLElement}*/ clone;
/**@type {HTMLElement}*/ domWrap;
/**@type {HTMLElement}*/ dom;
/**@type {HTMLElement}*/ detailsWrap;
/**@type {HTMLElement}*/ detailsDom;
/**@type {function}*/ renderDebounced;
/**@type {function}*/ renderDetailsDebounced;
/**@type {function}*/ updatePositionDebounced;
/**@type {function}*/ updateDetailsPositionDebounced;
/**@type {function}*/ updateFloatingPositionDebounced;
get matchType() {
return power_user.stscript.matching ?? 'fuzzy';
}
get autoHide() {
return power_user.stscript.autocomplete.autoHide ?? false;
}
/**
* @param {HTMLTextAreaElement} textarea The textarea to receive autocomplete.
* @param {() => boolean} checkIfActivate Function should return true only if under the current conditions, autocomplete should display (e.g., for slash commands: autoComplete.text[0] == '/')
* @param {(text: string, index: number) => Promise<AutoCompleteNameResult>} getNameAt Function should return (unfiltered, matching against input is done in AutoComplete) information about name options at index in text.
* @param {boolean} isFloating Whether autocomplete should float at the keyboard cursor.
*/
constructor(textarea, checkIfActivate, getNameAt, isFloating = false) {
this.textarea = textarea;
this.checkIfActivate = checkIfActivate;
this.getNameAt = getNameAt;
this.isFloating = isFloating;
this.domWrap = document.createElement('div'); {
this.domWrap.classList.add('autoComplete-wrap');
if (isFloating) this.domWrap.classList.add('isFloating');
}
this.dom = document.createElement('ul'); {
this.dom.classList.add('autoComplete');
this.domWrap.append(this.dom);
}
this.detailsWrap = document.createElement('div'); {
this.detailsWrap.classList.add('autoComplete-detailsWrap');
if (isFloating) this.detailsWrap.classList.add('isFloating');
}
this.detailsDom = document.createElement('div'); {
this.detailsDom.classList.add('autoComplete-details');
this.detailsWrap.append(this.detailsDom);
}
this.renderDebounced = debounce(this.render.bind(this), 10);
this.renderDetailsDebounced = debounce(this.renderDetails.bind(this), 10);
this.updatePositionDebounced = debounce(this.updatePosition.bind(this), 10);
this.updateDetailsPositionDebounced = debounce(this.updateDetailsPosition.bind(this), 10);
this.updateFloatingPositionDebounced = debounce(this.updateFloatingPosition.bind(this), 10);
textarea.addEventListener('input', ()=>this.text != this.textarea.value && this.show(true, this.wasForced));
textarea.addEventListener('keydown', (evt)=>this.handleKeyDown(evt));
textarea.addEventListener('click', ()=>this.isActive ? this.show() : null);
textarea.addEventListener('selectionchange', ()=>this.show());
textarea.addEventListener('blur', ()=>this.hide());
if (isFloating) {
textarea.addEventListener('scroll', ()=>this.updateFloatingPositionDebounced());
}
window.addEventListener('resize', ()=>this.updatePositionDebounced());
}
/**
*
* @param {AutoCompleteOption} option
*/
makeItem(option) {
const li = option.renderItem();
// gotta listen to pointerdown (happens before textarea-blur)
li.addEventListener('pointerdown', (evt)=>{
evt.preventDefault();
this.selectedItem = this.result.find(it=>it.name == li.getAttribute('data-name'));
this.select();
});
return li;
}
/**
*
* @param {AutoCompleteOption} item
*/
updateName(item) {
const chars = Array.from(item.dom.querySelector('.name').children);
switch (this.matchType) {
case 'strict': {
chars.forEach((it, idx)=>{
if (idx + item.nameOffset < item.name.length) {
it.classList.add('matched');
} else {
it.classList.remove('matched');
}
});
break;
}
case 'includes': {
const start = item.name.toLowerCase().search(this.name);
chars.forEach((it, idx)=>{
if (idx + item.nameOffset < start) {
it.classList.remove('matched');
} else if (idx + item.nameOffset < start + item.name.length) {
it.classList.add('matched');
} else {
it.classList.remove('matched');
}
});
break;
}
case 'fuzzy': {
item.name.replace(this.fuzzyRegex, (_, ...parts)=>{
parts.splice(-2, 2);
if (parts.length == 2) {
chars.forEach(c=>c.classList.remove('matched'));
} else {
let cIdx = item.nameOffset;
parts.forEach((it, idx)=>{
if (it === null || it.length == 0) return '';
if (idx % 2 == 1) {
chars.slice(cIdx, cIdx + it.length).forEach(c=>c.classList.add('matched'));
} else {
chars.slice(cIdx, cIdx + it.length).forEach(c=>c.classList.remove('matched'));
}
cIdx += it.length;
});
}
return '';
});
}
}
return item;
}
/**
* Calculate score for the fuzzy match.
* @param {AutoCompleteOption} option
* @returns The option.
*/
fuzzyScore(option) {
const parts = this.fuzzyRegex.exec(option.name).slice(1, -1);
let start = null;
let consecutive = [];
let current = '';
let offset = 0;
parts.forEach((part, idx) => {
if (idx % 2 == 0) {
if (part.length > 0) {
if (current.length > 0) {
consecutive.push(current);
}
current = '';
}
} else {
if (start === null) {
start = offset;
}
current += part;
}
offset += part.length;
});
if (current.length > 0) {
consecutive.push(current);
}
consecutive.sort((a,b)=>b.length - a.length);
option.score = new AutoCompleteFuzzyScore(start, consecutive[0]?.length ?? 0);
return option;
}
/**
* Compare two auto complete options by their fuzzy score.
* @param {AutoCompleteOption} a
* @param {AutoCompleteOption} b
*/
fuzzyScoreCompare(a, b) {
if (a.score.start < b.score.start) return -1;
if (a.score.start > b.score.start) return 1;
if (a.score.longestConsecutive > b.score.longestConsecutive) return -1;
if (a.score.longestConsecutive < b.score.longestConsecutive) return 1;
return a.name.localeCompare(b.name);
}
basicAutoHideCheck() {
// auto hide only if at least one char has been typed after the name + space
return this.textarea.selectionStart > this.parserResult.start
+ this.parserResult.name.length
+ (this.startQuote ? 1 : 0)
+ (this.endQuote ? 1 : 0)
+ 1
;
}
/**
* Show the autocomplete.
* @param {boolean} isInput Whether triggered by input.
* @param {boolean} isForced Whether force-showing (ctrl+space).
* @param {boolean} isSelect Whether an autocomplete option was just selected.
*/
async show(isInput = false, isForced = false, isSelect = false) {
//TODO check if isInput and isForced are both required
this.text = this.textarea.value;
this.isReplaceable = false;
if (document.activeElement != this.textarea) {
// only show with textarea in focus
return this.hide();
}
if (!this.checkIfActivate()) {
// only show if provider wants to
return this.hide();
}
// disable force-hide if trigger was forced
if (isForced) this.isForceHidden = false;
// request provider to get name result (potentially "incomplete", i.e. not an actual existing name) for
// cursor position
this.parserResult = await this.getNameAt(this.text, this.textarea.selectionStart);
this.secondaryParserResult = null;
if (!this.parserResult) {
// don't show if no name result found, e.g., cursor's area is not a command
return this.hide();
}
// need to know if name can be inside quotes, and then check if quotes are already there
if (this.parserResult.canBeQuoted) {
this.startQuote = this.text[this.parserResult.start] == '"';
this.endQuote = this.startQuote && this.text[this.parserResult.start + this.parserResult.name.length + 1] == '"';
} else {
this.startQuote = false;
this.endQuote = false;
}
// use lowercase name for matching
this.name = this.parserResult.name.toLowerCase() ?? '';
const isCursorInNamePart = this.textarea.selectionStart >= this.parserResult.start && this.textarea.selectionStart <= this.parserResult.start + this.parserResult.name.length + (this.startQuote ? 1 : 0);
if (isForced || isInput) {
// if forced (ctrl+space) or user input...
if (isCursorInNamePart) {
// ...and cursor is somewhere in the name part (including right behind the final char)
// -> show autocomplete for the (partial if cursor in the middle) name
this.name = this.name.slice(0, this.textarea.selectionStart - (this.parserResult.start) - (this.startQuote ? 1 : 0));
this.parserResult.name = this.name;
this.isReplaceable = true;
this.isForceHidden = false;
this.canBeAutoHidden = false;
} else {
this.isReplaceable = false;
this.canBeAutoHidden = this.basicAutoHideCheck();
}
} else {
// if not forced and no user input -> just show details
this.isReplaceable = false;
this.canBeAutoHidden = this.basicAutoHideCheck();
}
if (isForced || isInput || isSelect) {
// is forced or user input or just selected autocomplete option...
if (!isCursorInNamePart) {
// ...and cursor is not somwehere in the main name part -> check for secondary options (e.g., named arguments)
const result = this.parserResult.getSecondaryNameAt(this.text, this.textarea.selectionStart, isSelect);
if (result && (isForced || result.isRequired)) {
this.secondaryParserResult = result;
this.name = this.secondaryParserResult.name;
this.isReplaceable = isForced || this.secondaryParserResult.isRequired;
this.isForceHidden = false;
this.canBeAutoHidden = false;
} else {
this.isReplaceable = false;
this.canBeAutoHidden = this.basicAutoHideCheck();
}
}
}
if (this.matchType == 'fuzzy') {
// only build the fuzzy regex if match type is set to fuzzy
this.fuzzyRegex = new RegExp(`^(.*?)${this.name.split('').map(char=>`(${escapeRegex(char)})`).join('(.*?)')}(.*?)$`, 'i');
}
//TODO maybe move the matchers somewhere else; a single match function? matchType is available as property
const matchers = {
'strict': (name) => name.toLowerCase().startsWith(this.name),
'includes': (name) => name.toLowerCase().includes(this.name),
'fuzzy': (name) => this.fuzzyRegex.test(name),
};
this.result = this.effectiveParserResult.optionList
// filter the list of options by the partial name according to the matching type
.filter(it => this.isReplaceable || it.name == '' ? matchers[this.matchType](it.name) : it.name.toLowerCase() == this.name)
// remove aliases
.filter((it,idx,list) => list.findIndex(opt=>opt.value == it.value) == idx);
if (this.result.length == 0 && this.effectiveParserResult != this.parserResult && isForced) {
// no matching secondary results and forced trigger -> show current command details
this.secondaryParserResult = null;
this.result = [this.effectiveParserResult.optionList.find(it=>it.name == this.effectiveParserResult.name)];
this.name = this.effectiveParserResult.name;
this.fuzzyRegex = /(.*)(.*)(.*)/;
}
this.result = this.result
// update remaining options
.map(option => {
// build element
option.dom = this.makeItem(option);
// update replacer and add quotes if necessary
if (this.effectiveParserResult.canBeQuoted) {
option.replacer = option.name.includes(' ') || this.startQuote || this.endQuote ? `"${option.name}"` : `${option.name}`;
} else {
option.replacer = option.name;
}
// calculate fuzzy score if matching is fuzzy
if (this.matchType == 'fuzzy') this.fuzzyScore(option);
// update the name to highlight the matched chars
this.updateName(option);
return option;
})
// sort by fuzzy score or alphabetical
.toSorted(this.matchType == 'fuzzy' ? this.fuzzyScoreCompare : (a, b) => a.name.localeCompare(b.name))
;
if (this.isForceHidden) {
// hidden with escape
return this.hide();
}
if (this.autoHide && this.canBeAutoHidden && !isForced && this.effectiveParserResult == this.parserResult && this.result.length == 1) {
// auto hide user setting enabled and somewhere after name part and would usually show command details
return this.hide();
}
if (this.result.length == 0) {
if (!isInput) {
// no result and no input? hide autocomplete
return this.hide();
}
// otherwise add "no match" notice
const option = new BlankAutoCompleteOption(
this.name.length ?
this.effectiveParserResult.makeNoMatchText()
: this.effectiveParserResult.makeNoOptionstext()
,
);
this.result.push(option);
} else if (this.result.length == 1 && this.effectiveParserResult && this.result[0].name == this.effectiveParserResult.name) {
// only one result that is exactly the current value? just show hint, no autocomplete
this.isReplaceable = false;
this.isShowingDetails = false;
} else if (!this.isReplaceable && this.result.length > 1) {
return this.hide();
}
this.selectedItem = this.result[0];
this.isActive = true;
this.wasForced = isForced;
this.renderDebounced();
}
/**
* Hide autocomplete.
*/
hide() {
this.domWrap?.remove();
this.detailsWrap?.remove();
this.isActive = false;
this.isShowingDetails = false;
this.wasForced = false;
}
/**
* Create updated DOM.
*/
render() {
if (!this.isActive) return this.domWrap.remove();
if (this.isReplaceable) {
this.dom.innerHTML = '';
const frag = document.createDocumentFragment();
for (const item of this.result) {
if (item == this.selectedItem) {
item.dom.classList.add('selected');
} else {
item.dom.classList.remove('selected');
}
frag.append(item.dom);
}
this.dom.append(frag);
this.updatePosition();
document.body.append(this.domWrap);
} else {
this.domWrap.remove();
}
this.renderDetailsDebounced();
}
/**
* Create updated DOM for details.
*/
renderDetails() {
if (!this.isActive) return this.detailsWrap.remove();
if (!this.isShowingDetails && this.isReplaceable) return this.detailsWrap.remove();
this.detailsDom.innerHTML = '';
this.detailsDom.append(this.selectedItem?.renderDetails() ?? 'NO ITEM');
document.body.append(this.detailsWrap);
this.updateDetailsPositionDebounced();
}
/**
* Update position of DOM.
*/
updatePosition() {
if (this.isFloating) {
this.updateFloatingPosition();
} else {
const rect = {};
rect[AUTOCOMPLETE_WIDTH.INPUT] = this.textarea.getBoundingClientRect();
rect[AUTOCOMPLETE_WIDTH.CHAT] = document.querySelector('#sheld').getBoundingClientRect();
rect[AUTOCOMPLETE_WIDTH.FULL] = document.body.getBoundingClientRect();
this.domWrap.style.setProperty('--bottom', `${window.innerHeight - rect[AUTOCOMPLETE_WIDTH.INPUT].top}px`);
this.dom.style.setProperty('--bottom', `${window.innerHeight - rect[AUTOCOMPLETE_WIDTH.INPUT].top}px`);
this.domWrap.style.bottom = `${window.innerHeight - rect[AUTOCOMPLETE_WIDTH.INPUT].top}px`;
if (this.isShowingDetails) {
this.domWrap.style.setProperty('--leftOffset', '1vw');
this.domWrap.style.setProperty('--leftOffset', `max(1vw, ${rect[power_user.stscript.autocomplete.width.left].left}px)`);
this.domWrap.style.setProperty('--rightOffset', `calc(100vw - min(${rect[power_user.stscript.autocomplete.width.right].right}px, ${this.isShowingDetails ? 74 : 0}vw)`);
} else {
this.domWrap.style.setProperty('--leftOffset', `max(1vw, ${rect[power_user.stscript.autocomplete.width.left].left}px)`);
this.domWrap.style.setProperty('--rightOffset', `calc(100vw - min(99vw, ${rect[power_user.stscript.autocomplete.width.right].right}px)`);
}
this.updateDetailsPosition();
}
}
/**
* Update position of details DOM.
*/
updateDetailsPosition() {
if (this.isShowingDetails || !this.isReplaceable) {
if (this.isFloating) {
this.updateFloatingDetailsPosition();
} else {
const rect = {};
rect[AUTOCOMPLETE_WIDTH.INPUT] = this.textarea.getBoundingClientRect();
rect[AUTOCOMPLETE_WIDTH.CHAT] = document.querySelector('#sheld').getBoundingClientRect();
rect[AUTOCOMPLETE_WIDTH.FULL] = document.body.getBoundingClientRect();
if (this.isReplaceable) {
this.detailsWrap.classList.remove('full');
const selRect = this.selectedItem.dom.children[0].getBoundingClientRect();
this.detailsWrap.style.setProperty('--targetOffset', `${selRect.top}`);
this.detailsWrap.style.setProperty('--rightOffset', '1vw');
this.detailsWrap.style.setProperty('--bottomOffset', `calc(100vh - ${rect[AUTOCOMPLETE_WIDTH.INPUT].top}px)`);
this.detailsWrap.style.setProperty('--leftOffset', `calc(100vw - ${this.domWrap.style.getPropertyValue('--rightOffset')}`);
} else {
this.detailsWrap.classList.add('full');
this.detailsWrap.style.setProperty('--targetOffset', `${rect[AUTOCOMPLETE_WIDTH.INPUT].top}`);
this.detailsWrap.style.setProperty('--bottomOffset', `calc(100vh - ${rect[AUTOCOMPLETE_WIDTH.INPUT].top}px)`);
this.detailsWrap.style.setProperty('--leftOffset', `${rect[power_user.stscript.autocomplete.width.left].left}px`);
this.detailsWrap.style.setProperty('--rightOffset', `calc(100vw - ${rect[power_user.stscript.autocomplete.width.right].right}px)`);
}
}
}
}
/**
* Update position of floating autocomplete.
*/
updateFloatingPosition() {
const location = this.getCursorPosition();
const rect = this.textarea.getBoundingClientRect();
// cursor is out of view -> hide
if (location.bottom < rect.top || location.top > rect.bottom || location.left < rect.left || location.left > rect.right) {
return this.hide();
}
const left = Math.max(rect.left, location.left);
this.domWrap.style.setProperty('--targetOffset', `${left}`);
if (location.top <= window.innerHeight / 2) {
// if cursor is in lower half of window, show list above line
this.domWrap.style.top = `${location.bottom}px`;
this.domWrap.style.bottom = 'auto';
this.domWrap.style.maxHeight = `calc(${location.bottom}px - 1vh)`;
} else {
// if cursor is in upper half of window, show list below line
this.domWrap.style.top = 'auto';
this.domWrap.style.bottom = `calc(100vh - ${location.top}px)`;
this.domWrap.style.maxHeight = `calc(${location.top}px - 1vh)`;
}
}
updateFloatingDetailsPosition(location = null) {
if (!location) location = this.getCursorPosition();
const rect = this.textarea.getBoundingClientRect();
if (location.bottom < rect.top || location.top > rect.bottom || location.left < rect.left || location.left > rect.right) {
return this.hide();
}
const left = Math.max(rect.left, location.left);
this.detailsWrap.style.setProperty('--targetOffset', `${left}`);
if (this.isReplaceable) {
this.detailsWrap.classList.remove('full');
if (left < window.innerWidth / 4) {
// if cursor is in left part of screen, show details on right of list
this.detailsWrap.classList.add('right');
this.detailsWrap.classList.remove('left');
} else {
// if cursor is in right part of screen, show details on left of list
this.detailsWrap.classList.remove('right');
this.detailsWrap.classList.add('left');
}
} else {
this.detailsWrap.classList.remove('left');
this.detailsWrap.classList.remove('right');
this.detailsWrap.classList.add('full');
}
if (location.top <= window.innerHeight / 2) {
// if cursor is in lower half of window, show list above line
this.detailsWrap.style.top = `${location.bottom}px`;
this.detailsWrap.style.bottom = 'auto';
this.detailsWrap.style.maxHeight = `calc(${location.bottom}px - 1vh)`;
} else {
// if cursor is in upper half of window, show list below line
this.detailsWrap.style.top = 'auto';
this.detailsWrap.style.bottom = `calc(100vh - ${location.top}px)`;
this.detailsWrap.style.maxHeight = `calc(${location.top}px - 1vh)`;
}
}
/**
* Calculate (keyboard) cursor coordinates within textarea.
* @returns {{left:number, top:number, bottom:number}}
*/
getCursorPosition() {
const inputRect = this.textarea.getBoundingClientRect();
const style = window.getComputedStyle(this.textarea);
if (!this.clone) {
this.clone = document.createElement('div');
for (const key of style) {
this.clone.style[key] = style[key];
}
this.clone.style.position = 'fixed';
this.clone.style.visibility = 'hidden';
document.body.append(this.clone);
const mo = new MutationObserver(muts=>{
if (muts.find(it=>Array.from(it.removedNodes).includes(this.textarea))) {
this.clone.remove();
}
});
mo.observe(this.textarea.parentElement, { childList:true });
}
this.clone.style.height = `${inputRect.height}px`;
this.clone.style.left = `${inputRect.left}px`;
this.clone.style.top = `${inputRect.top}px`;
this.clone.style.whiteSpace = style.whiteSpace;
this.clone.style.tabSize = style.tabSize;
const text = this.textarea.value;
const before = text.slice(0, this.textarea.selectionStart);
this.clone.textContent = before;
const locator = document.createElement('span');
locator.textContent = text[this.textarea.selectionStart];
this.clone.append(locator);
this.clone.append(text.slice(this.textarea.selectionStart + 1));
this.clone.scrollTop = this.textarea.scrollTop;
this.clone.scrollLeft = this.textarea.scrollLeft;
const locatorRect = locator.getBoundingClientRect();
const location = {
left: locatorRect.left,
top: locatorRect.top,
bottom: locatorRect.bottom,
};
return location;
}
/**
* Toggle details view alongside autocomplete list.
*/
toggleDetails() {
this.isShowingDetails = !this.isShowingDetails;
this.renderDetailsDebounced();
this.updatePosition();
}
/**
* Select an item for autocomplete and put text into textarea.
*/
async select() {
if (this.isReplaceable && this.selectedItem.value !== null) {
this.textarea.value = `${this.text.slice(0, this.effectiveParserResult.start)}${this.selectedItem.replacer}${this.text.slice(this.effectiveParserResult.start + this.effectiveParserResult.name.length + (this.startQuote ? 1 : 0) + (this.endQuote ? 1 : 0))}`;
this.textarea.selectionStart = this.effectiveParserResult.start + this.selectedItem.replacer.length;
this.textarea.selectionEnd = this.textarea.selectionStart;
this.show(false, false, true);
} else {
const selectionStart = this.textarea.selectionStart;
const selectionEnd = this.textarea.selectionDirection;
this.textarea.selectionStart = selectionStart;
this.textarea.selectionDirection = selectionEnd;
}
this.wasForced = false;
this.textarea.dispatchEvent(new Event('input', { bubbles:true }));
}
/**
* Mark the item at newIdx in the autocomplete list as selected.
* @param {number} newIdx
*/
selectItemAtIndex(newIdx) {
this.selectedItem.dom.classList.remove('selected');
this.selectedItem = this.result[newIdx];
this.selectedItem.dom.classList.add('selected');
const rect = this.selectedItem.dom.children[0].getBoundingClientRect();
const rectParent = this.dom.getBoundingClientRect();
if (rect.top < rectParent.top || rect.bottom > rectParent.bottom ) {
this.dom.scrollTop += rect.top < rectParent.top ? rect.top - rectParent.top : rect.bottom - rectParent.bottom;
}
this.renderDetailsDebounced();
}
/**
* Handle keyboard events.
* @param {KeyboardEvent} evt The event.
*/
async handleKeyDown(evt) {
// autocomplete is shown and cursor at end of current command name (or inside name and typed or forced)
if (this.isActive && this.isReplaceable) {
// actions in the list
switch (evt.key) {
case 'ArrowUp': {
// select previous item
if (evt.ctrlKey || evt.altKey || evt.shiftKey) return;
evt.preventDefault();
evt.stopPropagation();
const idx = this.result.indexOf(this.selectedItem);
let newIdx;
if (idx == 0) newIdx = this.result.length - 1;
else newIdx = idx - 1;
this.selectItemAtIndex(newIdx);
return;
}
case 'ArrowDown': {
// select next item
if (evt.ctrlKey || evt.altKey || evt.shiftKey) return;
evt.preventDefault();
evt.stopPropagation();
const idx = this.result.indexOf(this.selectedItem);
const newIdx = (idx + 1) % this.result.length;
this.selectItemAtIndex(newIdx);
return;
}
case 'Enter': {
// pick the selected item to autocomplete
if (evt.ctrlKey || evt.altKey || evt.shiftKey || this.selectedItem.value == '') break;
if (this.selectedItem.name == this.name) break;
evt.preventDefault();
evt.stopImmediatePropagation();
this.select();
return;
}
case 'Tab': {
// pick the selected item to autocomplete
if (evt.ctrlKey || evt.altKey || evt.shiftKey || this.selectedItem.value == '') break;
evt.preventDefault();
evt.stopImmediatePropagation();
this.select();
return;
}
}
}
// details are shown, cursor can be anywhere
if (this.isActive) {
switch (evt.key) {
case 'Escape': {
// close autocomplete
if (evt.ctrlKey || evt.altKey || evt.shiftKey) return;
evt.preventDefault();
evt.stopPropagation();
this.isForceHidden = true;
this.wasForced = false;
this.hide();
return;
}
case 'Enter': {
// hide autocomplete on enter (send, execute, ...)
if (!evt.shiftKey) {
this.hide();
return;
}
break;
}
}
}
// autocomplete shown or not, cursor anywhere
switch (evt.key) {
case ' ': {
if (evt.ctrlKey) {
if (this.isActive && this.isReplaceable) {
// ctrl-space to toggle details for selected item
this.toggleDetails();
} else {
// ctrl-space to force show autocomplete
this.show(false, true);
}
return;
}
break;
}
}
if (['Control', 'Shift', 'Alt'].includes(evt.key)) {
// ignore keydown on modifier keys
return;
}
switch (evt.key) {
case 'ArrowUp':
case 'ArrowDown':
case 'ArrowRight':
case 'ArrowLeft': {
if (this.isActive) {
// keyboard navigation, wait for keyup to complete cursor move
const oldText = this.textarea.value;
await new Promise(resolve=>{
window.addEventListener('keyup', resolve, { once:true });
});
if (this.selectionStart != this.textarea.selectionStart) {
this.selectionStart = this.textarea.selectionStart;
this.show(this.isReplaceable || oldText != this.textarea.value);
}
}
break;
}
default: {
if (this.isActive) {
this.text != this.textarea.value && this.show(this.isReplaceable);
}
break;
}
}
}
}

View File

@@ -0,0 +1,16 @@
export class AutoCompleteFuzzyScore {
/**@type {number}*/ start;
/**@type {number}*/ longestConsecutive;
/**
* @param {number} start
* @param {number} longestConsecutive
*/
constructor(start, longestConsecutive) {
this.start = start;
this.longestConsecutive = longestConsecutive;
}
}

View File

@@ -0,0 +1,44 @@
import { SlashCommandNamedArgumentAutoCompleteOption } from '../slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js';
import { AutoCompleteOption } from './AutoCompleteOption.js';
// import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';
export class AutoCompleteNameResult {
/**@type {string} */ name;
/**@type {number} */ start;
/**@type {AutoCompleteOption[]} */ optionList = [];
/**@type {boolean} */ canBeQuoted = false;
/**@type {()=>string} */ makeNoMatchText = ()=>`No matches found for "${this.name}"`;
/**@type {()=>string} */ makeNoOptionstext = ()=>'No options';
/**
* @param {string} name Name (potentially partial) of the name at the requested index.
* @param {number} start Index where the name starts.
* @param {AutoCompleteOption[]} optionList A list of autocomplete options found in the current scope.
* @param {boolean} canBeQuoted Whether the name can be inside quotes.
* @param {()=>string} makeNoMatchText Function that returns text to show when no matches where found.
* @param {()=>string} makeNoOptionsText Function that returns text to show when no options are available to match against.
*/
constructor(name, start, optionList = [], canBeQuoted = false, makeNoMatchText = null, makeNoOptionsText = null) {
this.name = name;
this.start = start;
this.optionList = optionList;
this.canBeQuoted = canBeQuoted;
this.noMatchText = makeNoMatchText ?? this.makeNoMatchText;
this.noOptionstext = makeNoOptionsText ?? this.makeNoOptionstext;
}
/**
*
* @param {string} text The whole text
* @param {number} index Cursor index within text
* @param {boolean} isSelect Whether autocomplete was triggered by selecting an autocomplete option
* @returns {AutoCompleteSecondaryNameResult}
*/
getSecondaryNameAt(text, index, isSelect) {
return null;
}
}

View File

@@ -0,0 +1,206 @@
import { SlashCommand } from '../slash-commands/SlashCommand.js';
import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
export class AutoCompleteOption {
/**@type {string}*/ name;
/**@type {string}*/ typeIcon;
/**@type {number}*/ nameOffset = 0;
/**@type {AutoCompleteFuzzyScore}*/ score;
/**@type {string}*/ replacer;
/**@type {HTMLElement}*/ dom;
/**
* Used as a comparison value when removing duplicates (e.g., when a SlashCommand has aliases).
* @type {any}
* */
get value() {
return this.name;
}
/**
* @param {string} name
*/
constructor(name, typeIcon = ' ') {
this.name = name;
this.typeIcon = typeIcon;
}
makeItem(key, typeIcon, noSlash, namedArguments = [], unnamedArguments = [], returnType = 'void', helpString = '', aliasList = []) {
const li = document.createElement('li'); {
li.classList.add('item');
const type = document.createElement('span'); {
type.classList.add('type');
type.classList.add('monospace');
type.textContent = typeIcon;
li.append(type);
}
const specs = document.createElement('span'); {
specs.classList.add('specs');
const name = document.createElement('span'); {
name.classList.add('name');
name.classList.add('monospace');
name.textContent = noSlash ? '' : '/';
key.split('').forEach(char=>{
const span = document.createElement('span'); {
span.textContent = char;
name.append(span);
}
});
specs.append(name);
}
const body = document.createElement('span'); {
body.classList.add('body');
const args = document.createElement('span'); {
args.classList.add('arguments');
for (const arg of namedArguments) {
const argItem = document.createElement('span'); {
argItem.classList.add('argument');
argItem.classList.add('namedArgument');
if (!arg.isRequired || (arg.defaultValue ?? false)) argItem.classList.add('optional');
if (arg.acceptsMultiple) argItem.classList.add('multiple');
const name = document.createElement('span'); {
name.classList.add('argument-name');
name.textContent = arg.name;
argItem.append(name);
}
if (arg.enumList.length > 0) {
const enums = document.createElement('span'); {
enums.classList.add('argument-enums');
for (const e of arg.enumList) {
const enumItem = document.createElement('span'); {
enumItem.classList.add('argument-enum');
enumItem.textContent = e;
enums.append(enumItem);
}
}
argItem.append(enums);
}
} else {
const types = document.createElement('span'); {
types.classList.add('argument-types');
for (const t of arg.typeList) {
const type = document.createElement('span'); {
type.classList.add('argument-type');
type.textContent = t;
types.append(type);
}
}
argItem.append(types);
}
}
args.append(argItem);
}
}
for (const arg of unnamedArguments) {
const argItem = document.createElement('span'); {
argItem.classList.add('argument');
argItem.classList.add('unnamedArgument');
if (!arg.isRequired || (arg.defaultValue ?? false)) argItem.classList.add('optional');
if (arg.acceptsMultiple) argItem.classList.add('multiple');
if (arg.enumList.length > 0) {
const enums = document.createElement('span'); {
enums.classList.add('argument-enums');
for (const e of arg.enumList) {
const enumItem = document.createElement('span'); {
enumItem.classList.add('argument-enum');
enumItem.textContent = e;
enums.append(enumItem);
}
}
argItem.append(enums);
}
} else {
const types = document.createElement('span'); {
types.classList.add('argument-types');
for (const t of arg.typeList) {
const type = document.createElement('span'); {
type.classList.add('argument-type');
type.textContent = t;
types.append(type);
}
}
argItem.append(types);
}
}
args.append(argItem);
}
}
body.append(args);
}
const returns = document.createElement('span'); {
returns.classList.add('returns');
returns.textContent = returnType ?? 'void';
// body.append(returns);
}
specs.append(body);
}
li.append(specs);
}
const help = document.createElement('span'); {
help.classList.add('help');
const content = document.createElement('span'); {
content.classList.add('helpContent');
content.innerHTML = helpString;
const text = content.textContent;
content.innerHTML = '';
content.textContent = text;
help.append(content);
}
li.append(help);
}
if (aliasList.length > 0) {
const aliases = document.createElement('span'); {
aliases.classList.add('aliases');
aliases.append(' (alias: ');
for (const aliasName of aliasList) {
const alias = document.createElement('span'); {
alias.classList.add('monospace');
alias.textContent = `/${aliasName}`;
aliases.append(alias);
}
}
aliases.append(')');
// li.append(aliases);
}
}
}
return li;
}
/**
* @returns {HTMLElement}
*/
renderItem() {
// throw new Error(`${this.constructor.name}.renderItem() is not implemented`);
let li;
li = this.makeItem(this.name, this.typeIcon, true);
li.setAttribute('data-name', this.name);
return li;
}
/**
* @returns {DocumentFragment}
*/
renderDetails() {
// throw new Error(`${this.constructor.name}.renderDetails() is not implemented`);
const frag = document.createDocumentFragment();
const specs = document.createElement('div'); {
specs.classList.add('specs');
const name = document.createElement('div'); {
name.classList.add('name');
name.classList.add('monospace');
name.textContent = this.name;
specs.append(name);
}
frag.append(specs);
}
return frag;
}
}

View File

@@ -0,0 +1,5 @@
import { AutoCompleteNameResult } from './AutoCompleteNameResult.js';
export class AutoCompleteSecondaryNameResult extends AutoCompleteNameResult {
/**@type {boolean}*/ isRequired = false;
}

View File

@@ -0,0 +1,29 @@
import { AutoCompleteOption } from './AutoCompleteOption.js';
export class BlankAutoCompleteOption extends AutoCompleteOption {
/**
* @param {string} name
*/
constructor(name) {
super(name);
this.dom = this.renderItem();
}
get value() { return null; }
renderItem() {
const li = document.createElement('li'); {
li.classList.add('item');
li.classList.add('blank');
li.textContent = this.name;
}
return li;
}
renderDetails() {
const frag = document.createDocumentFragment();
return frag;
}
}

View File

@@ -0,0 +1,44 @@
import { AutoCompleteOption } from './AutoCompleteOption.js';
export class MacroAutoCompleteOption extends AutoCompleteOption {
/**@type {string}*/ fullName;
/**@type {string}*/ description;
constructor(name, fullName, description) {
super(name, '{}');
this.fullName = fullName;
this.description = description;
this.nameOffset = 2;
}
renderItem() {
let li;
li = this.makeItem(`${this.fullName}`, '{}', true, [], [], null, this.description);
li.setAttribute('data-name', this.name);
li.setAttribute('data-option-type', 'macro');
return li;
}
renderDetails() {
const frag = document.createDocumentFragment();
const specs = document.createElement('div'); {
specs.classList.add('specs');
const name = document.createElement('div'); {
name.classList.add('name');
name.classList.add('monospace');
name.textContent = this.fullName;
specs.append(name);
}
frag.append(specs);
}
const help = document.createElement('span'); {
help.classList.add('help');
help.innerHTML = this.description;
frag.append(help);
}
return frag;
}
}

View File

@@ -1,7 +1,8 @@
import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js';
import { saveMetadataDebounced } from './extensions.js';
import { registerSlashCommand } from './slash-commands.js';
import { stringFormat } from './utils.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { flashHighlight, stringFormat } from './utils.js';
const BG_METADATA_KEY = 'custom_background';
const LIST_METADATA_KEY = 'chat_backgrounds';
@@ -453,8 +454,7 @@ function highlightNewBackground(bg) {
const newBg = $(`.bg_example[bgfile="${bg}"]`);
const scrollOffset = newBg.offset().top - newBg.parent().offset().top;
$('#Backgrounds').scrollTop(scrollOffset);
newBg.addClass('flash animated');
setTimeout(() => newBg.removeClass('flash animated'), 2000);
flashHighlight(newBg);
}
function onBackgroundFilterInput() {
@@ -481,7 +481,20 @@ export function initBackgrounds() {
$('#auto_background').on('click', autoBackgroundCommand);
$('#add_bg_button').on('change', onBackgroundUploadSelected);
$('#bg-filter').on('input', onBackgroundFilterInput);
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], ' locks a background for the currently selected chat', true, true);
registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], ' unlocks a background for the currently selected chat', true, true);
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], ' automatically changes the background based on the chat context using the AI request prompt', true, true);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg',
callback: onLockBackgroundClick,
aliases: ['bglock'],
helpString: 'Locks a background for the currently selected chat',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg',
callback: onUnlockBackgroundClick,
aliases: ['bgunlock'],
helpString: 'Unlocks a background for the currently selected chat',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'autobg',
callback: autoBackgroundCommand,
aliases: ['bgauto'],
helpString: 'Automatically changes the background based on the chat context using the AI request prompt',
}));
}

View File

@@ -0,0 +1,98 @@
/**
* @typedef {object} v2DataWorldInfoEntry
* @property {string[]} keys - An array of primary keys associated with the entry.
* @property {string[]} secondary_keys - An array of secondary keys associated with the entry (optional).
* @property {string} comment - A human-readable description or explanation for the entry.
* @property {string} content - The main content or data associated with the entry.
* @property {boolean} constant - Indicates if the entry's content is fixed and unchangeable.
* @property {boolean} selective - Indicates if the entry's inclusion is controlled by specific conditions.
* @property {number} insertion_order - Defines the order in which the entry is inserted during processing.
* @property {boolean} enabled - Controls whether the entry is currently active and used.
* @property {string} position - Specifies the location or context where the entry applies.
* @property {v2DataWorldInfoEntryExtensionInfos} extensions - An object containing additional details for extensions associated with the entry.
* @property {number} id - A unique identifier assigned to the entry.
*/
/**
* @typedef {object} v2DataWorldInfoEntryExtensionInfos
* @property {number} position - The order in which the extension is applied relative to other extensions.
* @property {boolean} exclude_recursion - Prevents the extension from being applied recursively.
* @property {number} probability - The chance (between 0 and 1) of the extension being applied.
* @property {boolean} useProbability - Determines if the `probability` property is used.
* @property {number} depth - The maximum level of nesting allowed for recursive application of the extension.
* @property {number} selectiveLogic - Defines the logic used to determine if the extension is applied selectively.
* @property {string} group - A category or grouping for the extension.
* @property {boolean} group_override - Overrides any existing group assignment for the extension.
* @property {number} group_weight - A value used for prioritizing extensions within the same group.
* @property {boolean} prevent_recursion - Completely disallows recursive application of the extension.
* @property {boolean} delay_until_recursion - Will only be checked during recursion.
* @property {number} scan_depth - The maximum depth to search for matches when applying the extension.
* @property {boolean} match_whole_words - Specifies if only entire words should be matched during extension application.
* @property {boolean} use_group_scoring - Indicates if group weight is considered when selecting extensions.
* @property {boolean} case_sensitive - Controls whether case sensitivity is applied during matching for the extension.
* @property {string} automation_id - An identifier used for automation purposes related to the extension.
* @property {number} role - The specific function or purpose of the extension.
* @property {boolean} vectorized - Indicates if the extension is optimized for vectorized processing.
* @property {number} display_index - The order in which the extension should be displayed for user interfaces.
*/
/**
* @typedef {object} v2WorldInfoBook
* @property {string} name - the name of the book
* @property {v2DataWorldInfoEntry[]} entries - the entries of the book
*/
/**
* @typedef {object} v2CharData
* @property {string} name - The character's name.
* @property {string} description - A brief description of the character.
* @property {string} character_version - The character's data version.
* @property {string} personality - A short summary of the character's personality traits.
* @property {string} scenario - A description of the character's background or setting.
* @property {string} first_mes - The character's opening message in a conversation.
* @property {string} mes_example - An example message demonstrating the character's conversation style.
* @property {string} creator_notes - Internal notes or comments left by the character's creator.
* @property {string[]} tags - A list of keywords or labels associated with the character.
* @property {string} system_prompt - The system prompt used to interact with the character.
* @property {string} post_history_instructions - Instructions for handling the character's conversation history.
* @property {string} creator - The name of the person who created the character.
* @property {string[]} alternate_greetings - Additional greeting messages the character can use.
* @property {v2WorldInfoBook} character_book - Data about the character's world or story (if applicable).
* @property {v2CharDataExtensionInfos} extensions - Additional details specific to the character.
*/
/**
* @typedef {object} v2CharDataExtensionInfos
* @property {number} talkativeness - A numerical value indicating the character's propensity to talk.
* @property {boolean} fav - A flag indicating whether the character is a favorite.
* @property {string} world - The fictional world or setting where the character exists (if applicable).
* @property {object} depth_prompt - Prompts used to explore the character's depth and complexity.
* @property {number} depth_prompt.depth - The level of detail or nuance targeted by the prompt.
* @property {string} depth_prompt.prompt - The actual prompt text used for deeper character interaction.
* @property {"system" | "user" | "assistant"} depth_prompt.role - The role the character takes on during the prompted interaction (system, user, or assistant).
* // Non-standard extensions added by external tools
* @property {string} [pygmalion_id] - The unique identifier assigned to the character by the Pygmalion.chat.
* @property {string} [github_repo] - The gitHub repository associated with the character.
* @property {string} [source_url] - The source URL associated with the character.
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
*/
/**
* @typedef {object} v1CharData
* @property {string} name - the name of the character
* @property {string} description - the description of the character
* @property {string} personality - a short personality description of the character
* @property {string} scenario - a scenario description of the character
* @property {string} first_mes - the first message in the conversation
* @property {string} mes_example - the example message in the conversation
* @property {string} creatorcomment - creator's notes of the character
* @property {string[]} tags - the tags of the character
* @property {number} talkativeness - talkativeness
* @property {boolean|string} fav - fav
* @property {string} create_date - create_date
* @property {v2CharData} data - v2 data extension
* // Non-standard extensions added by the ST server (not part of the original data)
* @property {string} chat - name of the current chat file chat
* @property {string} avatar - file name of the avatar image (acts as a unique identifier)
* @property {string} json_data - the full raw JSON data of the character
*/
export default 0;// now this file is a module

View File

@@ -53,11 +53,11 @@ import { ScraperManager } from './scrapers.js';
* @returns {Promise<string>} Converted file text
*/
const fileSizeLimit = 1024 * 1024 * 10; // 10 MB
const fileSizeLimit = 1024 * 1024 * 100; // 100 MB
const ATTACHMENT_SOURCE = {
GLOBAL: 'global',
CHAT: 'chat',
CHARACTER: 'character',
CHAT: 'chat',
};
/**
@@ -592,9 +592,10 @@ async function deleteMessageImage() {
/**
* Deletes file from the server.
* @param {string} url Path to the file on the server
* @param {boolean} [silent=false] If true, do not show error messages
* @returns {Promise<boolean>} True if file was deleted, false otherwise.
*/
async function deleteFileFromServer(url) {
async function deleteFileFromServer(url, silent = false) {
try {
const result = await fetch('/api/files/delete', {
method: 'POST',
@@ -602,7 +603,7 @@ async function deleteFileFromServer(url) {
body: JSON.stringify({ path: url }),
});
if (!result.ok) {
if (!result.ok && !silent) {
const error = await result.text();
throw new Error(error);
}
@@ -669,6 +670,79 @@ async function editAttachment(attachment, source, callback) {
callback();
}
/**
* Downloads an attachment to the user's device.
* @param {FileAttachment} attachment Attachment to download
*/
async function downloadAttachment(attachment) {
const fileText = attachment.text || (await getFileAttachment(attachment.url));
const blob = new Blob([fileText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = attachment.name;
a.click();
URL.revokeObjectURL(url);
}
/**
* Removes an attachment from the disabled list.
* @param {FileAttachment} attachment Attachment to enable
* @param {function} callback Success callback
*/
function enableAttachment(attachment, callback) {
ensureAttachmentsExist();
extension_settings.disabled_attachments = extension_settings.disabled_attachments.filter(url => url !== attachment.url);
saveSettingsDebounced();
callback();
}
/**
* Adds an attachment to the disabled list.
* @param {FileAttachment} attachment Attachment to disable
* @param {function} callback Success callback
*/
function disableAttachment(attachment, callback) {
ensureAttachmentsExist();
extension_settings.disabled_attachments.push(attachment.url);
saveSettingsDebounced();
callback();
}
/**
* Moves a file attachment to a different source.
* @param {FileAttachment} attachment Attachment to moves
* @param {string} source Source of the attachment
* @param {function} callback Success callback
* @returns {Promise<void>} A promise that resolves when the attachment is moved.
*/
async function moveAttachment(attachment, source, callback) {
let selectedTarget = source;
const targets = getAvailableTargets();
const template = $(await renderExtensionTemplateAsync('attachments', 'move-attachment', { name: attachment.name, targets }));
template.find('.moveAttachmentTarget').val(source).on('input', function () {
selectedTarget = String($(this).val());
});
const result = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', { wide: false, large: false, okButton: 'Move', cancelButton: 'Cancel' });
if (result !== POPUP_RESULT.AFFIRMATIVE) {
console.debug('Move attachment cancelled');
return;
}
if (selectedTarget === source) {
console.debug('Move attachment cancelled: same source and target');
return;
}
const content = await getFileAttachment(attachment.url);
const file = new File([content], attachment.name, { type: 'text/plain' });
await deleteAttachment(attachment, source, () => { }, false);
await uploadFileAttachmentToServer(file, selectedTarget);
callback();
}
/**
* Deletes an attachment from the server and the chat.
* @param {FileAttachment} attachment Attachment to delete
@@ -702,10 +776,25 @@ async function deleteAttachment(attachment, source, callback, confirm = true) {
break;
}
await deleteFileFromServer(attachment.url);
if (Array.isArray(extension_settings.disabled_attachments) && extension_settings.disabled_attachments.includes(attachment.url)) {
extension_settings.disabled_attachments = extension_settings.disabled_attachments.filter(url => url !== attachment.url);
saveSettingsDebounced();
}
const silent = confirm === false;
await deleteFileFromServer(attachment.url, silent);
callback();
}
/**
* Determines if the attachment is disabled.
* @param {FileAttachment} attachment Attachment to check
* @returns {boolean} True if attachment is disabled, false otherwise.
*/
function isAttachmentDisabled(attachment) {
return extension_settings.disabled_attachments.some(url => url === attachment?.url);
}
/**
* Opens the attachment manager.
*/
@@ -755,13 +844,20 @@ async function openAttachmentManager() {
const sortedAttachmentList = attachments.slice().filter(filterFn).sort(sortFn);
for (const attachment of sortedAttachmentList) {
const isDisabled = isAttachmentDisabled(attachment);
const attachmentTemplate = template.find('.attachmentListItemTemplate .attachmentListItem').clone();
attachmentTemplate.toggleClass('disabled', isDisabled);
attachmentTemplate.find('.attachmentFileIcon').attr('title', attachment.url);
attachmentTemplate.find('.attachmentListItemName').text(attachment.name);
attachmentTemplate.find('.attachmentListItemSize').text(humanFileSize(attachment.size));
attachmentTemplate.find('.attachmentListItemCreated').text(new Date(attachment.created).toLocaleString());
attachmentTemplate.find('.viewAttachmentButton').on('click', () => openFilePopup(attachment));
attachmentTemplate.find('.editAttachmentButton').on('click', () => editAttachment(attachment, source, renderAttachments));
attachmentTemplate.find('.deleteAttachmentButton').on('click', () => deleteAttachment(attachment, source, renderAttachments));
attachmentTemplate.find('.downloadAttachmentButton').on('click', () => downloadAttachment(attachment));
attachmentTemplate.find('.moveAttachmentButton').on('click', () => moveAttachment(attachment, source, renderAttachments));
attachmentTemplate.find('.enableAttachmentButton').toggle(isDisabled).on('click', () => enableAttachment(attachment, renderAttachments));
attachmentTemplate.find('.disableAttachmentButton').toggle(!isDisabled).on('click', () => disableAttachment(attachment, renderAttachments));
template.find(sources[source]).append(attachmentTemplate);
}
}
@@ -786,7 +882,13 @@ async function openAttachmentManager() {
}
const buttonTemplate = template.find('.actionButtonTemplate .actionButton').clone();
buttonTemplate.find('.actionButtonIcon').addClass(scraper.iconClass);
if (scraper.iconAvailable) {
buttonTemplate.find('.actionButtonIcon').addClass(scraper.iconClass);
buttonTemplate.find('.actionButtonImg').remove();
} else {
buttonTemplate.find('.actionButtonImg').attr('src', scraper.iconClass);
buttonTemplate.find('.actionButtonIcon').remove();
}
buttonTemplate.find('.actionButtonText').text(scraper.name);
buttonTemplate.attr('title', scraper.description);
buttonTemplate.on('click', () => {
@@ -860,6 +962,50 @@ async function openAttachmentManager() {
template.find('.chatAttachmentsName').text(chatName);
}
function addDragAndDrop() {
$(document.body).on('dragover', '.dialogue_popup', (event) => {
event.preventDefault();
event.stopPropagation();
$(event.target).closest('.dialogue_popup').addClass('dragover');
});
$(document.body).on('dragleave', '.dialogue_popup', (event) => {
event.preventDefault();
event.stopPropagation();
$(event.target).closest('.dialogue_popup').removeClass('dragover');
});
$(document.body).on('drop', '.dialogue_popup', async (event) => {
event.preventDefault();
event.stopPropagation();
$(event.target).closest('.dialogue_popup').removeClass('dragover');
const files = Array.from(event.originalEvent.dataTransfer.files);
let selectedTarget = ATTACHMENT_SOURCE.GLOBAL;
const targets = getAvailableTargets();
const targetSelectTemplate = $(await renderExtensionTemplateAsync('attachments', 'files-dropped', { count: files.length, targets: targets }));
targetSelectTemplate.find('.droppedFilesTarget').on('input', function () {
selectedTarget = String($(this).val());
});
const result = await callGenericPopup(targetSelectTemplate, POPUP_TYPE.CONFIRM, '', { wide: false, large: false, okButton: 'Upload', cancelButton: 'Cancel' });
if (result !== POPUP_RESULT.AFFIRMATIVE) {
console.log('File upload cancelled');
return;
}
for (const file of files) {
await uploadFileAttachmentToServer(file, selectedTarget);
}
renderAttachments();
});
}
function removeDragAndDrop() {
$(document.body).off('dragover', '.shadow_popup');
$(document.body).off('dragleave', '.shadow_popup');
$(document.body).off('drop', '.shadow_popup');
}
let sortField = localStorage.getItem('DataBank_sortField') || 'created';
let sortOrder = localStorage.getItem('DataBank_sortOrder') || 'desc';
let filterString = '';
@@ -883,10 +1029,34 @@ async function openAttachmentManager() {
});
const cleanupFn = await renderButtons();
await verifyAttachments();
await renderAttachments();
addDragAndDrop();
await callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, okButton: 'Close' });
cleanupFn();
removeDragAndDrop();
}
/**
* Gets a list of available targets for attachments.
* @returns {string[]} List of available targets
*/
function getAvailableTargets() {
const targets = Object.values(ATTACHMENT_SOURCE);
const isNotCharacter = this_chid === undefined || selected_group;
const isNotInChat = getCurrentChatId() === undefined;
if (isNotCharacter) {
targets.splice(targets.indexOf(ATTACHMENT_SOURCE.CHARACTER), 1);
}
if (isNotInChat) {
targets.splice(targets.indexOf(ATTACHMENT_SOURCE.CHAT), 1);
}
return targets;
}
/**
@@ -989,6 +1159,10 @@ export async function uploadFileAttachmentToServer(file, target) {
}
function ensureAttachmentsExist() {
if (!Array.isArray(extension_settings.disabled_attachments)) {
extension_settings.disabled_attachments = [];
}
if (!Array.isArray(extension_settings.attachments)) {
extension_settings.attachments = [];
}
@@ -1009,7 +1183,7 @@ function ensureAttachmentsExist() {
}
/**
* Gets all currently available attachments.
* Gets all currently available attachments. Ignores disabled attachments.
* @returns {FileAttachment[]} List of attachments
*/
export function getDataBankAttachments() {
@@ -1018,11 +1192,11 @@ export function getDataBankAttachments() {
const chatAttachments = chat_metadata.attachments ?? [];
const characterAttachments = extension_settings.character_attachments?.[characters[this_chid]?.avatar] ?? [];
return [...globalAttachments, ...chatAttachments, ...characterAttachments];
return [...globalAttachments, ...chatAttachments, ...characterAttachments].filter(x => !isAttachmentDisabled(x));
}
/**
* Gets all attachments for a specific source.
* Gets all attachments for a specific source. Includes disabled attachments.
* @param {string} source Attachment source
* @returns {FileAttachment[]} List of attachments
*/
@@ -1037,6 +1211,50 @@ export function getDataBankAttachmentsForSource(source) {
case ATTACHMENT_SOURCE.CHARACTER:
return extension_settings.character_attachments?.[characters[this_chid]?.avatar] ?? [];
}
return [];
}
/**
* Verifies all attachments in the Data Bank.
* @returns {Promise<void>} A promise that resolves when attachments are verified.
*/
async function verifyAttachments() {
for (const source of Object.values(ATTACHMENT_SOURCE)) {
await verifyAttachmentsForSource(source);
}
}
/**
* Verifies all attachments for a specific source.
* @param {string} source Attachment source
* @returns {Promise<void>} A promise that resolves when attachments are verified.
*/
async function verifyAttachmentsForSource(source) {
try {
const attachments = getDataBankAttachmentsForSource(source);
const urls = attachments.map(a => a.url);
const response = await fetch('/api/files/verify', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ urls }),
});
if (!response.ok) {
const error = await response.text();
throw new Error(error);
}
const verifiedUrls = await response.json();
for (const attachment of attachments) {
if (verifiedUrls[attachment.url] === false) {
console.log('Deleting orphaned attachment', attachment);
await deleteAttachment(attachment, source, () => { }, false);
}
}
} catch (error) {
console.error('Attachment verification failed', error);
}
}
/**
@@ -1116,6 +1334,7 @@ jQuery(function () {
const textarea = document.createElement('textarea');
textarea.value = String(bro.val());
textarea.classList.add('height100p', 'wide100p');
bro.hasClass('monospace') && textarea.classList.add('monospace');
textarea.addEventListener('input', function () {
bro.val(textarea.value).trigger('input');
});

View File

@@ -0,0 +1,14 @@
/**
* Common debounce timeout values to use with `debounce` calls.
* @enum {number}
*/
export const debounce_timeout = {
/** [100 ms] For ultra-fast responses, typically for keypresses or executions that might happen multiple times in a loop or recursion. */
quick: 100,
/** [300 ms] Default time for general use, good balance between responsiveness and performance. */
standard: 300,
/** [1.000 ms] For situations where the function triggers more intensive tasks. */
relaxed: 1000,
/** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
extended: 5000,
};

View File

@@ -145,8 +145,18 @@ const extension_settings = {
variables: {
global: {},
},
/**
* @type {import('./chats.js').FileAttachment[]}
*/
attachments: [],
/**
* @type {Record<string, import('./chats.js').FileAttachment[]>}
*/
character_attachments: {},
/**
* @type {string[]}
*/
disabled_attachments: [],
};
let modules = [];

View File

@@ -0,0 +1,9 @@
<div class="characterAsset">
<div class="characterAssetName">{{name}}</div>
<img class="characterAssetImage" alt="{{name}}" src="{{url}}" />
<div class="characterAssetDescription" title="{{description}}">{{description}}</div>
<div class="characterAssetButtons flex-container">
<div class="characterAssetDownloadButton right_menu_button fa-fw fa-solid fa-download" title="Download"></div>
<div class="characterAssetCheckMark right_menu_button fa-fw fa-solid fa-check" title="Installed"></div>
</div>
</div>

View File

@@ -3,8 +3,9 @@ TODO:
*/
//const DEBUG_TONY_SAMA_FORK_MODE = true
import { getRequestHeaders, callPopup, processDroppedFiles } from '../../../script.js';
import { getRequestHeaders, callPopup, processDroppedFiles, eventSource, event_types } from '../../../script.js';
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
import { executeSlashCommands } from '../../slash-commands.js';
import { getStringHash, isValidUrl } from '../../utils.js';
export { MODULE_NAME };
@@ -108,7 +109,7 @@ function downloadAssetsList(url) {
</div>`);
}
for (const i in availableAssets[assetType]) {
for (const i in availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['name'].localeCompare(b['name']))) {
const asset = availableAssets[assetType][i];
const elemId = `assets_install_${assetType}_${i}`;
let element = $('<div />', { id: elemId, class: 'asset-download-button right_menu_button' });
@@ -199,6 +200,9 @@ function downloadAssetsList(url) {
</div>`);
if (assetType === 'character') {
if (asset.highlight) {
assetBlock.find('.asset-name').append('<i class="fa-solid fa-sm fa-trophy"></i>');
}
assetBlock.find('.asset-name').prepend(`<div class="avatar"><img src="${asset['url']}" alt="${displayName}"></div>`);
}
@@ -328,6 +332,41 @@ async function deleteAsset(assetType, filename) {
}
}
async function openCharacterBrowser(forceDefault) {
const url = forceDefault ? ASSETS_JSON_URL : String($('#assets-json-url-field').val());
const fetchResult = await fetch(url, { cache: 'no-cache' });
const json = await fetchResult.json();
const characters = json.filter(x => x.type === 'character');
if (!characters.length) {
toastr.error('No characters found in the assets list', 'Character browser');
return;
}
const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'market', {}));
for (const character of characters.sort((a, b) => a.name.localeCompare(b.name))) {
const listElement = template.find(character.highlight ? '.contestWinnersList' : '.featuredCharactersList');
const characterElement = $(await renderExtensionTemplateAsync(MODULE_NAME, 'character', character));
const downloadButton = characterElement.find('.characterAssetDownloadButton');
const checkMark = characterElement.find('.characterAssetCheckMark');
const isInstalled = isAssetInstalled('character', character.id);
downloadButton.toggle(!isInstalled).on('click', async () => {
downloadButton.toggleClass('fa-download fa-spinner fa-spin');
await installAsset(character.url, 'character', character.id);
downloadButton.hide();
checkMark.show();
});
checkMark.toggle(isInstalled);
listElement.append(characterElement);
}
callGenericPopup(template, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, allowVerticalScrolling: true, allowHorizontalScrolling: false });
}
//#############################//
// API Calls //
//#############################//
@@ -361,6 +400,11 @@ jQuery(async () => {
const assetsJsonUrl = windowHtml.find('#assets-json-url-field');
assetsJsonUrl.val(ASSETS_JSON_URL);
const charactersButton = windowHtml.find('#assets-characters-button');
charactersButton.on('click', async function () {
openCharacterBrowser(false);
});
const connectButton = windowHtml.find('#assets-connect-button');
connectButton.on('click', async function () {
const url = String(assetsJsonUrl.val());
@@ -397,4 +441,8 @@ jQuery(async () => {
windowHtml.find('#assets_filters').hide();
$('#extensions_settings').append(windowHtml);
eventSource.on(event_types.OPEN_CHARACTER_LIBRARY, async (forceDefault) => {
openCharacterBrowser(forceDefault);
});
});

View File

@@ -0,0 +1,19 @@
<div class="flex-container flexFlowColumn padding5">
<div class="contestWinners flex-container flexFlowColumn">
<h3 class="flex-container alignItemsBaseline justifyCenter" title="These characters are the winners of character design contests and have outstandable quality.">
<span data-i18n="Contest Winners">Contest Winners</span>
<i class="fa-solid fa-star"></i>
</h3>
<div class="contestWinnersList characterAssetList">
</div>
</div>
<hr>
<div class="featuredCharacters flex-container flexFlowColumn">
<h3 class="flex-container alignItemsBaseline justifyCenter" title="These characters are the finalists of character design contests and have remarkable quality.">
<span data-i18n="Featured Characters">Featured Characters</span>
<i class="fa-solid fa-thumbs-up"></i>
</h3>
<div class="featuredCharactersList characterAssetList">
</div>
</div>
</div>

View File

@@ -105,3 +105,54 @@
transform: rotate(1turn);
}
}
.characterAssetList {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.characterAsset {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
gap: 10px;
border: 1px solid var(--SmartThemeBorderColor);
background-color: var(--black30a);
border-radius: 10px;
width: 17%;
min-width: 150px;
margin: 5px;
overflow: hidden;
}
.characterAssetName {
font-size: 1.2em;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.characterAssetImage {
max-height: 140px;
object-fit: scale-down;
border-radius: 5px;
}
.characterAssetDescription {
font-size: 0.75em;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
flex: 1;
}
.characterAssetButtons {
display: flex;
flex-direction: row;
gap: 5px;
align-items: center;
}

View File

@@ -14,6 +14,10 @@
<select id="assets_type_select" class="text_pole flex1">
</select>
<input id="assets_search" class="text_pole flex1" placeholder="Search" type="search">
<div id="assets-characters-button" class="menu_button menu_button_icon">
<i class="fa-solid fa-image-portrait"></i>
Characters
</div>
</div>
<div class="inline-drawer-content" id="assets_menu">
</div>

View File

@@ -4,7 +4,7 @@
Enter a URL or the ID of a Fandom wiki page to scrape:
</label>
<small>
<span data-i18n=Examples:">Examples:</span>
<span data-i18n="Examples:">Examples:</span>
<code>https://harrypotter.fandom.com/</code>
<span data-i18n="or">or</span>
<code>harrypotter</code>

View File

@@ -0,0 +1,8 @@
<div class="flex-container justifyCenter alignItemsBaseline">
<span>Save <span class="droppedFilesCount">{{count}}</span> file(s) to...</span>
<select class="droppedFilesTarget">
{{#each targets}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>

View File

@@ -1,6 +1,15 @@
import { renderExtensionTemplateAsync } from '../../extensions.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
jQuery(async () => {
const buttons = await renderExtensionTemplateAsync('attachments', 'buttons', {});
$('#extensionsMenu').prepend(buttons);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'db',
callback: () => document.getElementById('manageAttachments')?.click(),
aliases: ['databank', 'data-bank'],
helpString: 'Open the data bank',
}));
});

View File

@@ -7,8 +7,13 @@
<div data-i18n="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).
</div>
<div data-i18n="Supported file types: Plain Text, PDF, Markdown, HTML, EPUB." class="marginTopBot5">
Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.
<div class="marginTopBot5">
<span data-i18n="Supported file types: Plain Text, PDF, Markdown, HTML, EPUB." >
Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.
</span>
<span data-i18n="Drag and drop files here to upload.">
Drag and drop files here to upload.
</span>
</div>
<div class="flex-container marginTopBot5">
<input type="search" id="attachmentSearch" class="attachmentSearch text_pole margin0 flex1" placeholder="Search...">
@@ -101,15 +106,20 @@
<div class="attachmentListItemName flex1"></div>
<small class="attachmentListItemCreated"></small>
<small class="attachmentListItemSize"></small>
<div class="viewAttachmentButton right_menu_button fa-solid fa-magnifying-glass" title="View attachment content"></div>
<div class="editAttachmentButton right_menu_button fa-solid fa-pencil" title="Edit attachment"></div>
<div class="deleteAttachmentButton right_menu_button fa-solid fa-trash" title="Delete attachment"></div>
<div class="viewAttachmentButton right_menu_button fa-fw fa-solid fa-magnifying-glass" title="View attachment content"></div>
<div class="disableAttachmentButton right_menu_button fa-fw fa-solid fa-comment" title="Disable attachment"></div>
<div class="enableAttachmentButton right_menu_button fa-fw fa-solid fa-comment-slash" title="Enable attachment"></div>
<div class="moveAttachmentButton right_menu_button fa-fw fa-solid fa-arrows-alt" title="Move attachment"></div>
<div class="editAttachmentButton right_menu_button fa-fw fa-solid fa-pencil" title="Edit attachment"></div>
<div class="downloadAttachmentButton right_menu_button fa-fw fa-solid fa-download" title="Download attachment"></div>
<div class="deleteAttachmentButton right_menu_button fa-fw fa-solid fa-trash" title="Delete attachment"></div>
</div>
</div>
<div class="actionButtonTemplate">
<div class="actionButton list-group-item flex-container flexGap5" title="">
<div class="actionButton list-group-item flex-container flexGap5" style="align-items: center;" title="">
<i class="actionButtonIcon"></i>
<img class="actionButtonImg"/>
<span class="actionButtonText"></span>
</div>
</div>

View File

@@ -1,5 +1,5 @@
{
"display_name": "Chat Attachments",
"display_name": "Data Bank (Chat Attachments)",
"loading_order": 3,
"requires": [],
"optional": [],

View File

@@ -0,0 +1,54 @@
<div>
<div class="flex-container flexFlowColumn">
<label for="scrapeInput" data-i18n="Enter a base URL of the MediaWiki to scrape.">
Enter a <strong>base URL</strong> of the MediaWiki to scrape.
</label>
<i data-i18n="Don't include the page name!">
Don't include the page name!
</i>
<small>
<span data-i18n="Examples:">Examples:</span>
<code>https://streetcat.wiki/index.php</code>
<span data-i18n="or">or</span>
<code>https://tcrf.net</code>
</small>
<input type="text" id="scrapeInput" name="scrapeInput" class="text_pole" placeholder="">
</div>
<div class="flex-container flexFlowColumn">
<label for="scrapeFilter">
Optional regex to pick the content by its title:
</label>
<small>
<span data-i18n="Example:">Example:</span>
<code>/Mr. (Fresh|Snack)/gi</code>
</small>
<input type="text" id="scrapeFilter" name="scrapeFilter" class="text_pole" placeholder="">
</div>
<div class="flex-container flexFlowColumn">
<label>
Output format:
</label>
<label class="checkbox_label justifyLeft" for="scrapeOutputSingle">
<input id="scrapeOutputSingle" type="radio" name="scrapeOutput" value="single" checked>
<div class="flex-container flexFlowColumn flexNoGap">
<span data-i18n="Single file">
Single file
</span>
<small data-i18n="All articles will be concatenated into a single file.">
All articles will be concatenated into a single file.
</small>
</div>
</label>
<label class="checkbox_label justifyLeft" for="scrapeOutputMulti">
<input id="scrapeOutputMulti" type="radio" name="scrapeOutput" value="multi">
<div class="flex-container flexFlowColumn flexNoGap">
<span data-i18n="File per article">
File per article
</span>
<small data-i18n="Each article will be saved as a separate file.">
Not recommended. Each article will be saved as a separate file.
</small>
</div>
</label>
</div>
</div>

View File

@@ -0,0 +1,8 @@
<div class="flex-container justifyCenter alignItemsBaseline">
<span>Move <strong class="moveAttachmentName">{{name}}</strong> to...</span>
<select class="moveAttachmentTarget">
{{#each targets}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>

View File

@@ -19,6 +19,16 @@
padding: 10px;
}
.attachmentListItem.disabled .attachmentListItemName {
text-decoration: line-through;
opacity: 0.75;
}
.attachmentListItem.disabled .attachmentFileIcon {
opacity: 0.75;
cursor: not-allowed;
}
.attachmentListItemSize {
min-width: 4em;
text-align: right;

View File

@@ -5,7 +5,9 @@ import { getMessageTimeStamp } from '../../RossAscends-mods.js';
import { SECRET_KEYS, secret_state } from '../../secrets.js';
import { getMultimodalCaption } from '../shared.js';
import { textgen_types, textgenerationwebui_settings } from '../../textgen-settings.js';
import { registerSlashCommand } from '../../slash-commands.js';
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
export { MODULE_NAME };
const MODULE_NAME = 'caption';
@@ -254,6 +256,19 @@ async function onSelectImage(e, prompt, quiet) {
return '';
}
const caption = await getCaptionForFile(file, prompt, quiet);
form && form.reset();
return caption;
}
/**
* Gets a caption for an image file.
* @param {File} file Input file
* @param {string} prompt Caption prompt
* @param {boolean} quiet Suppresses sending a message
* @returns {Promise<string>} Generated caption
*/
async function getCaptionForFile(file, prompt, quiet) {
try {
setSpinnerIcon();
const context = getContext();
@@ -273,7 +288,6 @@ async function onSelectImage(e, prompt, quiet) {
return '';
}
finally {
form && form.reset();
setImageIcon();
}
}
@@ -288,9 +302,26 @@ function onRefineModeInput() {
* @param {object} args Named parameters
* @param {string} prompt Caption prompt
*/
function captionCommandCallback(args, prompt) {
async function captionCommandCallback(args, prompt) {
const quiet = isTrueBoolean(args?.quiet);
const id = args?.id;
if (!isNaN(Number(id))) {
const message = getContext().chat[id];
if (message?.extra?.image) {
try {
const fetchResult = await fetch(message.extra.image);
const blob = await fetchResult.blob();
const file = new File([blob], 'image.jpg', { type: blob.type });
return await getCaptionForFile(file, prompt, quiet);
} catch (error) {
toastr.error('Failed to get image from the message. Make sure the image is accessible.');
return '';
}
}
}
return new Promise(resolve => {
const quiet = isTrueBoolean(args?.quiet);
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
@@ -404,12 +435,17 @@ jQuery(function () {
<select id="caption_multimodal_model" class="flex1 text_pole">
<option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option>
<option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option>
<option data-type="openai" value="gpt-4o">gpt-4o</option>
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
<option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
<option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
<option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
<option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
<option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>
<option data-type="openrouter" value="haotian-liu/llava-13b">haotian-liu/llava-13b</option>
<option data-type="openrouter" value="fireworks/firellava-13b">fireworks/firellava-13b</option>
<option data-type="openrouter" value="anthropic/claude-3-haiku">anthropic/claude-3-haiku</option>
<option data-type="openrouter" value="anthropic/claude-3-sonnet">anthropic/claude-3-sonnet</option>
<option data-type="openrouter" value="anthropic/claude-3-opus">anthropic/claude-3-opus</option>
@@ -418,6 +454,8 @@ jQuery(function () {
<option data-type="openrouter" value="anthropic/claude-3-opus:beta">anthropic/claude-3-opus:beta</option>
<option data-type="openrouter" value="nousresearch/nous-hermes-2-vision-7b">nousresearch/nous-hermes-2-vision-7b</option>
<option data-type="openrouter" value="google/gemini-pro-vision">google/gemini-pro-vision</option>
<option data-type="openrouter" value="google/gemini-flash-1.5">google/gemini-flash-1.5</option>
<option data-type="openrouter" value="liuhaotian/llava-yi-34b">liuhaotian/llava-yi-34b</option>
<option data-type="ollama" value="ollama_current">[Currently selected]</option>
<option data-type="ollama" value="bakllava:latest">bakllava:latest</option>
<option data-type="ollama" value="llava:latest">llava:latest</option>
@@ -492,5 +530,35 @@ jQuery(function () {
saveSettingsDebounced();
});
registerSlashCommand('caption', captionCommandCallback, [], '<span class="monospace">quiet=true/false [prompt]</span> - caption an image with an optional prompt and passes the caption down the pipe. Only multimodal sources support custom prompts. Set the "quiet" argument to true to suppress sending a captioned message, default: false.', true, true);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'caption',
callback: captionCommandCallback,
returns: 'caption',
namedArgumentList: [
new SlashCommandNamedArgument(
'quiet', 'suppress sending a captioned message', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'],
),
new SlashCommandNamedArgument(
'id', 'get image from a message with this ID', [ARGUMENT_TYPE.NUMBER], false, false,
),
],
unnamedArgumentList: [
new SlashCommandArgument(
'prompt', [ARGUMENT_TYPE.STRING], false,
),
],
helpString: `
<div>
Caption an image with an optional prompt and passes the caption down the pipe.
</div>
<div>
Only multimodal sources support custom prompts.
</div>
<div>
Provide a message ID to get an image from a message instead of uploading one.
</div>
<div>
Set the "quiet" argument to true to suppress sending a captioned message, default: false.
</div>
`,
}));
});

View File

@@ -2,10 +2,13 @@ import { callPopup, eventSource, event_types, generateQuietPrompt, getRequestHea
import { dragElement, isMobile } from '../../RossAscends-mods.js';
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplateAsync } from '../../extensions.js';
import { loadMovingUIState, power_user } from '../../power-user.js';
import { registerSlashCommand } from '../../slash-commands.js';
import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence } from '../../utils.js';
import { hideMutedSprites } from '../../group-chats.js';
import { isJsonSchemaSupported } from '../../textgen-settings.js';
import { debounce_timeout } from '../../constants.js';
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from '../../slash-commands/SlashCommandArgument.js';
export { MODULE_NAME };
const MODULE_NAME = 'expressions';
@@ -94,7 +97,7 @@ async function forceUpdateVisualNovelMode() {
}
}
const updateVisualNovelModeDebounced = debounce(forceUpdateVisualNovelMode, 100);
const updateVisualNovelModeDebounced = debounce(forceUpdateVisualNovelMode, debounce_timeout.quick);
async function updateVisualNovelMode(name, expression) {
const container = $('#visual-novel-wrapper');
@@ -905,8 +908,10 @@ async function setSpriteSetCommand(_, folder) {
$('#expression_override').val(folder.trim());
onClickExpressionOverrideButton();
removeExpression();
moduleWorker();
// removeExpression();
// moduleWorker();
const vnMode = isVisualNovelMode();
await sendExpressionCall(folder, lastExpression, true, vnMode);
}
async function classifyCommand(_, text) {
@@ -971,8 +976,8 @@ function sampleClassifyText(text) {
return text;
}
// Remove asterisks and quotes
let result = text.replace(/[*"]/g, '');
// Replace macros, remove asterisks and quotes
let result = substituteParams(text).replace(/[*"]/g, '');
const SAMPLE_THRESHOLD = 500;
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
@@ -1015,12 +1020,12 @@ function parseLlmResponse(emotionResponse, labels) {
const parsedEmotion = JSON.parse(emotionResponse);
return parsedEmotion?.emotion ?? fallbackExpression;
} catch {
const fuse = new Fuse([emotionResponse]);
for (const label of labels) {
const result = fuse.search(label);
if (result.length > 0) {
return label;
}
const fuse = new Fuse(labels, { includeScore: true });
console.debug('Using fuzzy search in labels:', labels);
const result = fuse.search(emotionResponse);
if (result.length > 0) {
console.debug(`fuzzy search found: ${result[0].item} as closest for the LLM response:`, emotionResponse);
return result[0].item;
}
}
@@ -1270,13 +1275,10 @@ async function getExpressionsList() {
* @returns {Promise<string[]>}
*/
async function resolveExpressionsList() {
// get something for offline mode (default images)
if (!modules.includes('classify') && extension_settings.expressions.api == EXPRESSION_API.extras) {
return DEFAULT_EXPRESSIONS;
}
// See if we can retrieve a specific expression list from the API
try {
if (extension_settings.expressions.api == EXPRESSION_API.extras) {
// Check Extras api first, if enabled and that module active
if (extension_settings.expressions.api == EXPRESSION_API.extras && modules.includes('classify')) {
const url = new URL(getApiUrl());
url.pathname = '/api/classify/labels';
@@ -1291,7 +1293,10 @@ async function getExpressionsList() {
expressionsList = data.labels;
return expressionsList;
}
} else {
}
// If running the local classify model (not using the LLM), we ask that one
if (extension_settings.expressions.api == EXPRESSION_API.local) {
const apiResult = await fetch('/api/extra/classify/labels', {
method: 'POST',
headers: getRequestHeaders(),
@@ -1303,11 +1308,12 @@ async function getExpressionsList() {
return expressionsList;
}
}
}
catch (error) {
} catch (error) {
console.log(error);
return [];
}
// If there was no specific list, or an error, just return the default expressions
return DEFAULT_EXPRESSIONS;
}
const result = await resolveExpressionsList();
@@ -1965,9 +1971,61 @@ function migrateSettings() {
});
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
registerSlashCommand('sprite', setSpriteSlashCommand, ['emote'], '<span class="monospace">(spriteId)</span> force sets the sprite for the current character', true, true);
registerSlashCommand('spriteoverride', setSpriteSetCommand, ['costume'], '<span class="monospace">(optional folder)</span> sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.', true, true);
registerSlashCommand('lastsprite', (_, value) => lastExpression[value.trim()] ?? '', [], '<span class="monospace">(charName)</span> Returns the last set sprite / expression for the named character.', true, true);
registerSlashCommand('th', toggleTalkingHeadCommand, ['talkinghead'], ' Character Expressions: toggles <i>Image Type - talkinghead (extras)</i> on/off.', true, true);
registerSlashCommand('classify', classifyCommand, [], '<span class="monospace">(text)</span> performs an emotion classification of the given text and returns a label.', true, true);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sprite',
aliases: ['emote'],
callback: setSpriteSlashCommand,
unnamedArgumentList: [
new SlashCommandArgument(
'spriteId', [ARGUMENT_TYPE.STRING], true,
),
],
helpString: 'Force sets the sprite for the current character.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'spriteoverride',
aliases: ['costume'],
callback: setSpriteSetCommand,
unnamedArgumentList: [
new SlashCommandArgument(
'optional folder', [ARGUMENT_TYPE.STRING], false,
),
],
helpString: 'Sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lastsprite',
callback: (_, value) => lastExpression[value.trim()] ?? '',
returns: 'sprite',
unnamedArgumentList: [
new SlashCommandArgument(
'charName', [ARGUMENT_TYPE.STRING], true,
),
],
helpString: 'Returns the last set sprite / expression for the named character.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'th',
callback: toggleTalkingHeadCommand,
aliases: ['talkinghead'],
helpString: 'Character Expressions: toggles <i>Image Type - talkinghead (extras)</i> on/off.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'classify',
callback: classifyCommand,
unnamedArgumentList: [
new SlashCommandArgument(
'text', [ARGUMENT_TYPE.STRING], true,
),
],
returns: 'emotion classification label for the given text',
helpString: `
<div>
Performs an emotion classification of the given text and returns a label.
</div>
<div>
<strong>Example:</strong>
<ul>
<li>
<pre><code>/classify I am so happy today!</code></pre>
</li>
</ul>
</div>
`,
}));
})();

View File

@@ -64,10 +64,6 @@
<input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
<input id="expression_override_button" class="menu_button" type="submit" value="Submit" />
</div>
<h3 id="image_list_header">
<strong>Sprite set:</strong>&nbsp;<span id="image_list_header_name"></span>
</h3>
<div id="image_list"></div>
<div class="expression_buttons flex-container spaceEvenly">
<div id="expression_upload_pack_button" class="menu_button">
<i class="fa-solid fa-file-zipper"></i>
@@ -78,8 +74,13 @@
<span>Remove all image overrides</span>
</div>
</div>
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character.
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>/characters/</b> folder of your user data directory and name it as the name of the character.
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
<h3 id="image_list_header">
<strong>Sprite set:</strong>&nbsp;<span id="image_list_header_name"></span>
</h3>
<div id="image_list"></div>
</div>
</div>
</div>

View File

@@ -14,7 +14,6 @@
display: flex;
height: calc(100vh - var(--topBarBlockSize));
width: 100vw;
position: relative;
overflow: hidden;
}

Some files were not shown because too many files have changed in this diff Show More