feat: reintroduce module integration (#756)

# PR Checklist
- [ ] Have you checked if it works normally in all models? *Ignore this
if it doesn't use models.*
- [ ] Have you checked if it works normally in all web, local, and node
hosted versions? If it doesn't, have you blocked it in those versions?
- [ ] Have you added type definitions?

# Description
This PR reintroduces the module integration feature and includes the
following changes:

1. Modules activated via integration, rather than global activation from
the module menu, are now visually distinguished with a separate color.

![image](https://github.com/user-attachments/assets/641cbd41-e858-4206-8007-66778d06a220)

2. When a module is globally activated after being enabled through
integration, it will no longer be applied twice.
3. Added a button in the developer tools to view a list of all currently
active modules.

![image](https://github.com/user-attachments/assets/02119ea0-4de7-4eae-90ac-dd9a20ddf2de)

![image](https://github.com/user-attachments/assets/05411ea9-35bc-4fca-abf5-14e464171483)


4. Fixed an issue where the `module_enabled` cbs was operating based on
module names instead of namespaces, contrary to the documentation. It
now also detects modules activated through integration.
5. Fixed issues where module integration did not properly sync with
module additions, modifications, and deletions.

Let me know if any further adjustments are needed!
This commit is contained in:
kwaroran
2025-02-13 02:24:01 +09:00
committed by GitHub
6 changed files with 60 additions and 15 deletions

View File

@@ -643,6 +643,10 @@
{/if}
</Arcodion>
<Arcodion styled name={language.moduleIntergration} help="moduleIntergration">
<TextAreaInput bind:value={DBState.db.moduleIntergration} fullwidth height={"32"} autocomplete="off"/>
</Arcodion>
<Arcodion styled name={language.regexScript}>
<RegexList bind:value={DBState.db.presetRegex} buttons />
</Arcodion>

View File

@@ -4,13 +4,14 @@
import { DBState } from 'src/ts/stores.svelte';
import Button from "src/lib/UI/GUI/Button.svelte";
import ModuleMenu from "src/lib/Setting/Pages/Module/ModuleMenu.svelte";
import { exportModule, importModule, type RisuModule } from "src/ts/process/modules";
import { exportModule, importModule, refreshModules, type RisuModule } from "src/ts/process/modules";
import { DownloadIcon, Edit, TrashIcon, Globe, Share2Icon } from "lucide-svelte";
import { v4 } from "uuid";
import { tooltip } from "src/ts/gui/tooltip";
import { alertCardExport, alertConfirm, alertError } from "src/ts/alert";
import TextInput from "src/lib/UI/GUI/TextInput.svelte";
import { ShowRealmFrameStore } from "src/ts/stores.svelte";
import { onDestroy } from "svelte";
let tempModule:RisuModule = $state({
name: '',
description: '',
@@ -30,6 +31,12 @@
return score
})
}
onDestroy(() => {
if(DBState.db.moduleIntergration){
refreshModules()
}
})
</script>
{#if mode === 0}
<h2 class="mb-2 text-2xl font-bold mt-2">{language.modules}</h2>
@@ -48,9 +55,12 @@
<div class="pl-3 pt-3 text-left flex">
<span class="text-lg">{rmodule.name}</span>
<div class="flex-grow flex justify-end">
<button class={(!DBState.db.enabledModules.includes(rmodule.id)) ?
"text-textcolor2 hover:text-green-500 mr-2 cursor-pointer" :
"mr-2 cursor-pointer text-blue-500"
<button class={(DBState.db.enabledModules.includes(rmodule.id)) ?
"mr-2 cursor-pointer text-blue-500" :
rmodule.namespace &&
DBState.db.moduleIntergration.split(',').map((s) => s.trim()).includes(rmodule.namespace) ?
"text-amber-500 hover:text-green-500 mr-2 cursor-pointer" :
"text-textcolor2 hover:text-green-500 mr-2 cursor-pointer"
} use:tooltip={language.enableGlobal} onclick={async (e) => {
e.stopPropagation()
if(DBState.db.enabledModules.includes(rmodule.id)){

View File

@@ -18,6 +18,7 @@
import { applyChatTemplate, chatTemplates } from "src/ts/process/templates/chatTemplate";
import OptionInput from "../UI/GUI/OptionInput.svelte";
import { loadLoreBookV3Prompt } from "src/ts/process/lorebook.svelte";
import { getModules } from "src/ts/process/modules";
let previewMode = $state('chat')
let previewJoin = $state('yes')
@@ -281,6 +282,16 @@
}}>Match Sources</Button>
</Arcodion>
<Button className="mt-2" onclick={() => {
const modules = getModules()
const html = `
${modules.map((v) => {
return `## ${v.name}\n\n\`\`\`\n${v.description}\n\`\`\`\n`
}).join('\n')}
`.trim()
alertMd(html)
}}>Preview Module</Button>
<Button className="mt-2" onclick={() => {
alertMd(getRequestLog())
}}>Request Log</Button>

View File

@@ -10,7 +10,7 @@ import { SizeStore, selectedCharID } from './stores.svelte';
import { calcString } from './process/infunctions';
import { findCharacterbyId, getPersonaPrompt, getUserIcon, getUserName, parseKeyValue, sfc32, sleep, uuidtoNumber } from './util';
import { getInlayAsset } from './process/files/inlays';
import { getModuleAssets, getModuleLorebooks } from './process/modules';
import { getModuleAssets, getModuleLorebooks, getModules } from './process/modules';
import type { OpenAIChat } from './process/index.svelte';
import hljs from 'highlight.js/lib/core'
import 'highlight.js/styles/atom-one-dark.min.css'
@@ -1531,16 +1531,13 @@ function basicMatcher (p1:string,matcherArg:matcherArg,vars:{[key:string]:string
return dateTimeFormat(arra[1],t)
}
case 'module_enabled':{
const selchar = db.characters[get(selectedCharID)]
let enabledChatModules:string[] = []
if(!selchar){
enabledChatModules = selchar.chats[selchar.chatPage].modules ?? []
const modules = getModules()
for(const module of modules){
if(module.namespace === arra[1]){
return '1'
}
}
const moduleId = db.modules.find((f) => {
return f.name === arra[1]
}).id
return (db.enabledModules.includes(moduleId) || enabledChatModules.includes(moduleId)) ? '1' : '0'
return '0'
}
case 'filter':{
const array = parseArray(arra[1])

View File

@@ -268,7 +268,20 @@ function getModuleByIds(ids:string[]){
modules.push(module)
}
}
return modules
return deduplicateModuleById(modules)
}
function deduplicateModuleById(modules:RisuModule[]){
let ids:string[] = []
let newModules:RisuModule[] = []
for(let i=0;i<modules.length;i++){
if(ids.includes(modules[i].id)){
continue
}
ids.push(modules[i].id)
newModules.push(modules[i])
}
return newModules
}
let lastModules = ''
@@ -280,6 +293,10 @@ export function getModules(){
if (currentChat){
ids = ids.concat(currentChat.modules ?? [])
}
if(db.moduleIntergration){
const intList = db.moduleIntergration.split(',').map((s) => s.trim())
ids = ids.concat(intList)
}
const idsJoined = ids.join('-')
if(lastModules === idsJoined){
return lastModuleData
@@ -440,4 +457,9 @@ export function moduleUpdate(){
ReloadGUIPointer.set(get(ReloadGUIPointer) + 1)
lastModuleIds = ids
}
}
export function refreshModules(){
lastModules = ''
lastModuleData = []
}

View File

@@ -115,6 +115,7 @@ $effect.root(() => {
DBState?.db?.characters?.[selIdState.selId]?.chats?.[DBState?.db?.characters?.[selIdState.selId]?.chatPage]?.modules?.length
DBState?.db?.characters?.[selIdState.selId]?.hideChatIcon
DBState?.db?.characters?.[selIdState.selId]?.backgroundHTML
DBState?.db?.moduleIntergration
moduleUpdate()
})
})