diff --git a/src/lang/cn.ts b/src/lang/cn.ts index 75365930..bf88199b 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -68,7 +68,7 @@ export const languageChinese = { "utilityBot": "启用后,它将忽略主要提示词、越狱和其他提示词。用于实用程序机器人,而不是用于角色扮演。", "loreSelective": "如果切换选择性的,则激活键和次要键都应有匹配项才能激活lorebook。", "loreRandomActivation": "如果启用了概率条件,那么当lorebook的其他条件都已满足时,每次发送聊天时lorebook将以“概率”设置的概率被激活。", - "additionalAssets": "在你的聊天中显示的额外资源。\n\n - 使用`{{raw::}}`作为路径\n - 使用`{{img::}}`作为图片\n - 使用`{{video::}}`作为视频\n - 使用`{{audio::}}` 作为音频\n - 建议放在背景 HTML中", + "additionalAssets": "在你的聊天中显示的额外资源。\n\n - 使用`{{raw::}}`作为路径\n - 使用`{{image::}}`作为图片\n - 使用`{{video::}}`作为视频\n - 使用`{{audio::}}` 作为音频\n - 建议放在背景 HTML中", "superMemory": "超级记忆通过给 AI 提供总结数据使你的角色记忆更多信息\n\n超级记忆模型是一个总结文本的模型。推荐使用Davinci,除非是具有超过2000个token数的高度总结能力的未经过滤模型,否则不推荐使用辅助模型。\n\n超级记忆提示决定了应发送什么提示词进行总结。如果你留空,它将使用默认提示词。建议留空。\n\n在所有设置都完成后,你可以在角色的设置中启用它。", "replaceGlobalNote": "如果不为空,则将当前全局注释替换为此。", "backgroundHTML": "将被注入聊天荧幕背景的Markdown/HTML数据。\n\n你也可以使用其他资源。例如,你可以使用`{{audio::}}`: 将背景注入资源", diff --git a/src/lang/de.ts b/src/lang/de.ts index 55d51942..8cf4e451 100644 --- a/src/lang/de.ts +++ b/src/lang/de.ts @@ -83,7 +83,7 @@ export const languageGerman = { scenario: "Eine kurze Beschreibung des Szenarios des Charakters. \n\n**Es wird nicht empfohlen, diese Option zu nutzen. Beschreiben Sie das Szenario stattdessen in der Charakterbeschreibung.**", loreSelective: "Wenn der selektive Modus aktiviert ist, müssen sowohl Trigger-Wort als auch auch das Co-Trigger-Wort übereinstimmen, um den Kontext zu aktivieren", loreRandomActivation: "Wenn die Bedingung 'Wahrscheinlichkeit verwenden' aktiviert ist, wird der Kontext mit einer festgelegten Wahrscheinlichkeit, die durch 'Wahrscheinlichkeit' festgelegt wird, jedes Mal aktiviert, wenn eine Chat-Nachricht gesendet wird und die anderen Bedingungen der Überlieferung alle erfüllt sind", - additionalAssets: "Zusätzliche Assets, die in Ihrem Chat angezeigt werden sollen. \n\n - verwenden Sie `{{raw::}}` um als Pfad zu verwenden.\n – verwenden Sie `{{img::}}` um als Bild zu verwenden\n - verwenden Sie `{{video::}}` um als Video zu verwenden\n – verwenden Sie `{{audio::}}` um als Audio zu verwenden\n – es wird empfohlen, dies im HTML Backend einzufügen", + additionalAssets: "Zusätzliche Assets, die in Ihrem Chat angezeigt werden sollen. \n\n - verwenden Sie `{{raw::}}` um als Pfad zu verwenden.\n – verwenden Sie `{{image::}}` um als Bild zu verwenden\n - verwenden Sie `{{video::}}` um als Video zu verwenden\n – verwenden Sie `{{audio::}}` um als Audio zu verwenden\n – es wird empfohlen, dies im HTML Backend einzufügen", superMemory: "SupaMemory ermöglicht es Ihrem Charakter, sich mehr Informationen zu 'merken', indem der KI zusammengefasste Daten zugeführt werden.\n\n" + "Das SupaMemory-Modell ist ein Modell, das die Zusammenfassung für einen Text erstellt. Davinci oder GPT-3.5-16k werden empfohlen. Hilfsmodelle hingegen werden nicht empfohlen, es sei denn, es handelt sich um ein ungefiltertes Modell mit mindestens 2000 Tokens Kontextgröße und einer ausgereiften Fähigkeiten, Texte zusammenfassen zu können.\n\n" + "Die SupaMemory-Anweisung entscheidet darüber, welche Anweisung konkret zur Zusammenfassung gesendet werden soll. Wenn Sie das Feld leer lassen, wird die Standard-Anweisung verwendet. Es wird empfohlen, es leer zu lassen.\n\n" diff --git a/src/lang/en.ts b/src/lang/en.ts index 73be9cb1..0c3c6685 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -58,7 +58,7 @@ export const languageEnglish = { bias:"bias is a key-value data which modifies the likelihood of string appearing.\nit can be -100 to 100, higher values will be more likely to appear, and lower values will be more unlikely to appear. \nAdditionaly, if its set to -101, it would work as 'strong ban word' for some models. \nWarning: if the tokenizer is wrong, it not work properly.", emotion: "Emotion Images option shows image depending at character's emotion which is analized by character's response. you must input emotion name as words *(like joy, happy, fear and etc.)* .emotion named **neutral** will be default emotion if it exists. must be more then 3 images to work properly.", imggen: "After analyzing the chat, apply the prompt to {{slot}}.", - regexScript: "Regex Script is a custom script that replaces string that matches IN to OUT.\n\nThere four type options." + regexScript: "Regex Script is a custom regex that replaces string that matches IN to OUT.\n\nThere four type options." + "\n\n- **Modify Input** modifys user's input" + "\n\n- **Modify Output** modifys character's output" + "\n\n- **Modify Request Data** modifys current chat data when sent." @@ -69,8 +69,14 @@ export const languageEnglish = { + "\n\n- $`\n\n - inserts the portion of the string that precedes the matched substring." + "\n\n- $1\n\n - inserts the first matching group. works with other number like 2, 3..." + "\n\n- $(name)\n\n - inserts the named group" - + "\n\nIf OUT starts with **@@**, it doesn't replaces the string, but instead does a special effect if matching string founds." - + "\n\n- @@emo (emotion name)\n\n - if character is Emotion Images mode, sets (emotion name) as emotion and prevents default.", + + "\n\nFor flags, you can not only use native supported flags, but also use these flags, which are designed for advanced users:" + + "\n\n- `` - injects the result to the current string." + + "\n- `` - moves the result to the top of the string." + + "\n- `` - moves the result to the bottom of the string." + + "\n- `` - if the match is not found, it carries the result from the previous match." + + "\n- `` - sets the order of the result. higher order will be shown first. `n` is a number. (like ``) if this flag is not set, it will be set to 0." + + "\n- `` - parses curly braced synatxes in IN." + + "\n\nTo use with native flags, you can use like `gi`.", experimental: "This is a experimental feature. it might be unstable.", oogaboogaURL: "If your WebUI supports older version of api, your url should look *like https:.../run/textgen*\n\n" + "If your WebUI supports newVersion of api, your url should look like *https://.../api/v1/generate* and use the api server as host, and add --api to arguments.", @@ -86,7 +92,7 @@ export const languageEnglish = { utilityBot: "When activated, it ignores main prompt, jailbreak and other prompts. used for bot made for utility, not for roleplay.", loreSelective: "If Selective mode is toggled, both Activation Key and Secondary key should have a match to activate the lore.", loreRandomActivation: "If Use Probability Condition is abled, if the lore's other conditions are all met, the lore will be activated with a set probability which is set by 'Probability' each time a chat is sent.", - additionalAssets: "Additional assets to display in your chat. \n\n - use `{{raw::}}` to use as path.\n - use `{{img::}}` to use as image\n - use `{{video::}}` to use as video\n - use `{{audio::}}` to use as audio\n - recommended to put in Background HTML", + additionalAssets: "Additional assets to display in your chat. \n\n - use `{{raw::}}` to use as path.\n - use `{{image::}}` to use as image\n - use `{{video::}}` to use as video\n - use `{{audio::}}` to use as audio\n - recommended to put in Background HTML", superMemory: "SuperMemory makes your character memorize more by giving summarized data to AI.\n\n" + "SuperMemory model is a model that summarizes that text. davinci is recommended, and Auxiliary models are not recommended unless it is an unfiltered model with over 2000 tokens with great summarizing skill.\n\n" + "SuperMemory Prompt decides what prompt should be sent to summarize. if you leave it blank, it will use the default prompt. leaving blank is recommended.\n\n" @@ -136,6 +142,7 @@ export const languageEnglish = { claudeCachingExperimental: "Caching in Claude is experimental feature that can reduce the cost of the model, but it can also increase the cost if you use it without reroll. since this is a experimental feature, it can be unstable and behavior can be changed in the future.", urllora: "You can use direct download link of the model file. you can make direct url from google drive like website like https://sites.google.com/site/gdocs2direct/ , or use civitai URL, copy the the AIR (looks like `urn:air:flux1:lora:civitai:180891@776656` or just `civitai:180891@776656`) and paste it.", namespace: "Namespace is a unique identifier for the module. it is used to prevent conflicts between modules, and for interaction of presets, other modules and etc. if you are not sure what to put, leave it blank.", + moduleIntergration: "You can enable modules by putting the module namespace in the module intergartion sections. if you want to enable multiple modules, you can seperate them by comma. for example, `module1,module2,module3`. this is for advanced users, who wants to vary the use of modules by presets.", }, setup: { chooseProvider: "Choose AI Provider", diff --git a/src/lang/es.ts b/src/lang/es.ts index 432d2bff..5d351e62 100644 --- a/src/lang/es.ts +++ b/src/lang/es.ts @@ -86,7 +86,7 @@ export const languageSpanish = { utilityBot: "Cuando está activado, ignora el prompt principal, jailbreak y otros prompts. Se utiliza para bots diseñados para utilidades, no para juegos de rol.", loreSelective: "Si el modo Selectivo está activado, tanto la Clave de Activación como la Clave Secundaria deben coincidir para activar el lore.", loreRandomActivation: "Si la Condición de Probabilidad está habilitada, si se cumplen todas las demás condiciones del lore, el lore se activará con una probabilidad establecida por 'Probabilidad' cada vez que se envíe un chat.", - additionalAssets: "Activos adicionales para mostrar en tu chat. \n\n - usa `{{raw::}}` para usar como ruta.\n - usa `{{img::}}` para usar como imagen\n - usa `{{video::}}` para usar como video\n - usa `{{audio::}}` para usar como audio\n - se recomienda poner en HTML de Fondo", + additionalAssets: "Activos adicionales para mostrar en tu chat. \n\n - usa `{{raw::}}` para usar como ruta.\n - usa `{{image::}}` para usar como imagen\n - usa `{{video::}}` para usar como video\n - usa `{{audio::}}` para usar como audio\n - se recomienda poner en HTML de Fondo", superMemory: "SuperMemoria hace que tu personaje memorice más dando datos resumidos a la IA.\n\n" + "El modelo de SuperMemoria es un modelo que resume ese texto. Se recomienda Davinci, y no se recomienda usar modelos auxiliares a menos que sea un modelo no filtrado con más de 2000 tokens y con una gran habilidad de resumen.\n\n" + "El Prompt de SuperMemoria decide qué prompt se debe enviar para resumir. Si lo dejas en blanco, usará el prompt predeterminado. Se recomienda dejarlo en blanco.\n\n" diff --git a/src/lang/ko.ts b/src/lang/ko.ts index c8bece83..7ce33eee 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -68,7 +68,7 @@ export const languageKorean = { "utilityBot": "활성화되면 메인 프롬프트, jailbreak 및 기타 프롬프트를 무시합니다. 역할극이 아닌 유틸리티용 봇에 사용됩니다.", "loreSelective": "선택 모드가 토글되면 활성화 키와 보조 키 모두 일치해야 로어가 활성화됩니다.", "loreRandomActivation": "확률 조건 사용이 활성화된 경우, 로어의 다른 조건이 모두 충족되면 로어가 활성화되며, 각 채팅을 보낼 때마다 설정된 확률에 따라 활성화됩니다.", - "additionalAssets": "채팅에 표시할 추가 에셋입니다.\n\n- `{{raw::<에셋 이름>}}`을 경로로 사용하려면\n- `{{img::<에셋 이름>}}`을 이미지로 사용하려면\n- `{{video::<에셋 이름>}}`을 비디오로 사용하려면\n- `{{audio::<에셋 이름>}}`을 오디오로 사용하려면\n - 배경 HTML에 넣는 것이 좋습니다.", + "additionalAssets": "채팅에 표시할 추가 에셋입니다.\n\n- 경로로 사용하려면 `{{raw::<에셋 이름>}}`을\n- 이미지로 사용하려면 `{{image::<에셋 이름>}}`을\n- 비디오로 사용하려면 `{{video::<에셋 이름>}}`을\n- 오디오로 사용하려면 `{{audio::<에셋 이름>}}`을 사용하세요.\n", "superMemory": "SuperMemory는 AI에게 요약된 데이터를 제공하여 캐릭터가 더 많이 기억하도록합니다.\n\nSuperMemory 모델은 해당 텍스트를 요약하는 모델입니다. 보조 모델은 2000개 이상의 토큰을 가진 필터되지 않은 모델이 아닌 경우 권장되지 않습니다.\n\nSuperMemory 프롬프트는 요약을 보내기 위해 어떤 프롬프트를 보내야 하는지 결정합니다. 비워두면 기본 프롬프트를 사용합니다. 비워두는 것이 권장됩니다.\n\n모두 설정한 후 캐릭터의 설정에서 활성화할 수 있습니다.", "replaceGlobalNote": "비어 있지 않으면 현재 글로벌 노트를 이로 대체합니다.", "backgroundHTML": "채팅 화면의 배경에 삽입 될 마크다운/HTML 데이터입니다.\n\n추가 에셋을 사용할 수도 있습니다. 예를 들어, 배경 음악에 `{{audio::<에셋 이름>}}`을 사용할 수 있습니다.\n\n또한 다음과 같은 추가 에셋을 사용할 수 있습니다:\n - `{{bg::<에셋 이름>}}`: 에셋으로 배경을 삽입합니다.", diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index fea7aa2f..85ca4464 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -563,7 +563,8 @@ }}/> {/if} - + + {#if submenu !== -1} diff --git a/src/ts/parser.ts b/src/ts/parser.ts index 47f4860e..432f781b 100644 --- a/src/ts/parser.ts +++ b/src/ts/parser.ts @@ -4,7 +4,7 @@ import { DataBase, setDatabase, type Database, type Message, type character, typ import { getFileSrc } from './storage/globalApi'; import { processScriptFull } from './process/scripts'; import { get } from 'svelte/store'; -import css from '@adobe/css-tools' +import css, { type CssAtRuleAST } from '@adobe/css-tools' import { CurrentCharacter, CurrentChat, SizeStore, selectedCharID } from './stores'; import { calcString } from './process/infunctions'; import { findCharacterbyId, getPersonaPrompt, getUserIcon, getUserName, parseKeyValue, sfc32, sleep, uuidtoNumber } from './util'; @@ -439,6 +439,32 @@ function encodeStyle(txt:string){ } const styleDecodeRegex = /\(.+?)\<\/risu-style\>/gms +function decodeStyleRule(rule:CssAtRuleAST){ + if(rule.type === 'rule'){ + if(rule.selectors){ + for(let i=0;i { + if(v.startsWith('.')){ + return ".x-risu-" + v.substring(1) + } + return v + }).join(' ') + + rule.selectors[i] = ".chattext " + selectors + } + } + } + } + if(rule.type === 'media' || rule.type === 'supports' || rule.type === 'document' || rule.type === 'host' || rule.type === 'container' ){ + for(let i=0;i { @@ -446,26 +472,10 @@ function decodeStyle(text:string){ const ast = css.parse(Buffer.from(txt, 'hex').toString('utf-8')) const rules = ast?.stylesheet?.rules if(rules){ - for(const rule of rules){ - - if(rule.type === 'rule'){ - if(rule.selectors){ - for(let i=0;i { - if(v.startsWith('.')){ - return ".x-risu-" + v.substring(1) - } - return v - }).join(' ') - - rule.selectors[i] = ".chattext " + selectors - } - } - } - } + for(let i=0;i${css.stringify(ast)}` diff --git a/src/ts/process/scripts.ts b/src/ts/process/scripts.ts index e3fe865b..93ea4875 100644 --- a/src/ts/process/scripts.ts +++ b/src/ts/process/scripts.ts @@ -16,6 +16,12 @@ const randomness = /\|\|\|/g export type ScriptMode = 'editinput'|'editoutput'|'editprocess'|'editdisplay' +type pScript = { + script: customscript, + order: number + actions: string[] +} + export async function processScript(char:character|groupChat, data:string, mode:ScriptMode){ return (await processScriptFull(char, data, mode)).data } @@ -73,7 +79,9 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter if(scripts.length === 0){ return {data, emoChanged} } - for (const script of scripts){ + function executeScript(pscript:pScript){ + const script = pscript.script + if(script.type === mode){ let outScript2 = script.out.replaceAll("$n", "\n") @@ -82,7 +90,7 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter if(script.ableFlag){ flag = script.flag || 'g' } - if(outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom')){ + if(outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom') || pscript.actions.includes('move_top') || pscript.actions.includes('move_bottom')){ flag = flag.replace('g', '') //temperary fix } //remove unsupported flag @@ -92,8 +100,13 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter flag = 'u' } - const reg = new RegExp(script.in, flag) - if(outScript.startsWith('@@')){ + let input = script.in + if(pscript.actions.includes('cbs')){ + input = risuChatParser(input, { chatID: chatID }) + } + + const reg = new RegExp(input, flag) + if(outScript.startsWith('@@') || pscript.actions.length > 0){ if(reg.test(data)){ if(outScript.startsWith('@@emo ')){ const emoName = script.out.substring(6).trim() @@ -118,12 +131,15 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } } } - if(outScript.startsWith('@@inject') && chatID !== -1){ + else if((outScript.startsWith('@@inject') || pscript.actions.includes('inject')) && chatID !== -1){ const selchar = db.characters[get(selectedCharID)] selchar.chats[selchar.chatPage].message[chatID].data = data data = data.replace(reg, "") } - if(outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom')){ + else if( + outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom') || + pscript.actions.includes('move_top') || pscript.actions.includes('move_bottom') + ){ const isGlobal = flag.includes('g') const matchAll = isGlobal ? data.matchAll(reg) : [data.match(reg)] data = data.replace(reg, "") @@ -148,7 +164,7 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter return v }) console.log(out) - if(outScript.startsWith('@@move_top')){ + if(outScript.startsWith('@@move_top') || pscript.actions.includes('move_top')){ data = out + '\n' +data } else{ @@ -157,9 +173,12 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } } } + else{ + data = risuChatParser(data.replace(reg, outScript), { chatID: chatID }) + } } else{ - if(outScript.startsWith('@@repeat_back') && chatID !== -1){ + if((outScript.startsWith('@@repeat_back') || pscript.actions.includes('repeat_back')) && chatID !== -1){ const v = outScript.split(' ', 2)[1] const selchar = db.characters[get(selectedCharID)] const chat = selchar.chats[selchar.chatPage] @@ -203,6 +222,52 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } } + let parsedScripts:pScript[] = [] + let orderChanged = false + for (const script of scripts){ + if(script.ableFlag && script.flag?.includes('<')){ + const rregex = /<(.+?)>/g + const scriptData = structuredClone(script) + let order = 0 + const actions:string[] = [] + scriptData.flag = scriptData.flag?.replace(rregex, (v:string, p1:string) => { + const meta = p1.split(',').map((v) => v.trim()) + for(const m of meta){ + if(m.startsWith('order ')){ + order = parseInt(m.substring(6)) + } + else{ + actions.push(m) + } + } + + return '' + }) + parsedScripts.push({ + script: scriptData, + order, + actions + }) + continue + } + parsedScripts.push({ + script, + order: 0, + actions: [] + }) + } + + console.log(parsedScripts) + + if(orderChanged){ + parsedScripts.sort((a, b) => b.order - a.order) //sort by order + } + for (const script of parsedScripts){ + executeScript(script) + } + + + if(db.dynamicAssets && (char.type === 'simple' || char.type === 'character') && char.additionalAssets && char.additionalAssets.length > 0){ if(!db.dynamicAssetsEditDisplay && mode === 'editdisplay'){ return {data, emoChanged}