feat: Add 'divider' and 'group'/'groupEnd' type toggle (#856)
# PR Checklist - [ ] Have you checked if it works normally in all models? *Ignore this if it doesn't use models.* - [x] 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? - [x] Have you added type definitions? # Description ## Dividers | Before | After | | --- | --- | | | | Instead of pseudo-dividers with unwanted checkboxes, this allows creators to define a "divider" *toggle*.  Dividers are created by keyless `=name=divider`, or keyless valueless `==divider`. ``` =My Module=divider ==divider ``` Multiple dividers with equal label will merge into one to prevent too many dividers from multiple modules covering everything up.  ``` // these won't merge ==divider =K=divider // these will merge ==divider ==divider ``` ## Groups  Groups are opened by keyless `=name=group` and closed by keyless valueless `==groupEnd`. Nesting is not supported. | Opened groups | MobileGUI | |---|---| |  |  |
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getModuleToggles } from "src/ts/process/modules";
|
import { getModuleToggles } from "src/ts/process/modules";
|
||||||
import { DBState, MobileGUI } from "src/ts/stores.svelte";
|
import { DBState, MobileGUI } from "src/ts/stores.svelte";
|
||||||
import { parseToggleSyntax } from "src/ts/util";
|
import { parseToggleSyntax, type sidebarToggle, type sidebarToggleGroup } from "src/ts/util";
|
||||||
import CheckInput from "../UI/GUI/CheckInput.svelte";
|
|
||||||
import { language } from "src/lang";
|
import { language } from "src/lang";
|
||||||
import type { character, groupChat } from "src/ts/storage/database.svelte";
|
import type { character, groupChat } from "src/ts/storage/database.svelte";
|
||||||
|
import Arcodion from '../UI/Arcodion.svelte'
|
||||||
|
import CheckInput from "../UI/GUI/CheckInput.svelte";
|
||||||
import SelectInput from "../UI/GUI/SelectInput.svelte";
|
import SelectInput from "../UI/GUI/SelectInput.svelte";
|
||||||
import OptionInput from "../UI/GUI/OptionInput.svelte";
|
import OptionInput from "../UI/GUI/OptionInput.svelte";
|
||||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||||
@@ -16,16 +17,38 @@
|
|||||||
|
|
||||||
let { chara = $bindable(), noContainer }: Props = $props();
|
let { chara = $bindable(), noContainer }: Props = $props();
|
||||||
|
|
||||||
let parsedKv = $derived(parseToggleSyntax(DBState.db.customPromptTemplateToggle + getModuleToggles()))
|
let groupedToggles = $derived.by(() => {
|
||||||
|
const ungrouped = parseToggleSyntax(DBState.db.customPromptTemplateToggle + getModuleToggles())
|
||||||
|
|
||||||
|
let groupOpen = false
|
||||||
|
// group toggles together between group ... groupEnd
|
||||||
|
return ungrouped.reduce<sidebarToggle[]>((acc, toggle) => {
|
||||||
|
if (toggle.type === 'group') {
|
||||||
|
groupOpen = true
|
||||||
|
acc.push(toggle)
|
||||||
|
} else if (toggle.type === 'groupEnd') {
|
||||||
|
groupOpen = false
|
||||||
|
} else if (groupOpen) {
|
||||||
|
(acc.at(-1) as sidebarToggleGroup).children.push(toggle)
|
||||||
|
} else {
|
||||||
|
acc.push(toggle)
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet toggles(reverse: boolean = false)}
|
{#snippet toggles(items: sidebarToggle[], reverse: boolean = false)}
|
||||||
{#each parsedKv as toggle}
|
{#each items as toggle, index}
|
||||||
{#if toggle.type === 'select'}
|
{#if toggle.type === 'group' && toggle.children.length > 0}
|
||||||
<div class="flex gap-2 mt-2 items-center" class:flex-row-reverse={!reverse} class:justify-end={!reverse}>
|
<div class="w-full">
|
||||||
|
<Arcodion styled name={toggle.value}>
|
||||||
|
{@render toggles((toggle as sidebarToggleGroup).children, reverse)}
|
||||||
|
</Arcodion>
|
||||||
|
</div>
|
||||||
|
{:else if toggle.type === 'select'}
|
||||||
|
<div class="w-full flex gap-2 mt-2 items-center" class:justify-end={$MobileGUI} >
|
||||||
<span>{toggle.value}</span>
|
<span>{toggle.value}</span>
|
||||||
|
|
||||||
<SelectInput className="w-32" bind:value={DBState.db.globalChatVariables[`toggle_${toggle.key}`]}>
|
<SelectInput className="w-32" bind:value={DBState.db.globalChatVariables[`toggle_${toggle.key}`]}>
|
||||||
{#each toggle.options as option, i}
|
{#each toggle.options as option, i}
|
||||||
<OptionInput value={i.toString()}>{option}</OptionInput>
|
<OptionInput value={i.toString()}>{option}</OptionInput>
|
||||||
@@ -33,12 +56,23 @@
|
|||||||
</SelectInput>
|
</SelectInput>
|
||||||
</div>
|
</div>
|
||||||
{:else if toggle.type === 'text'}
|
{:else if toggle.type === 'text'}
|
||||||
<div class="flex gap-2 mt-2 items-center" class:flex-row-reverse={!reverse} class:justify-end={!reverse}>
|
<div class="w-full flex gap-2 mt-2 items-center" class:justify-end={$MobileGUI}>
|
||||||
<span>{toggle.value}</span>
|
<span>{toggle.value}</span>
|
||||||
<TextInput className="w-32" bind:value={DBState.db.globalChatVariables[`toggle_${toggle.key}`]} />
|
<TextInput className="w-32" bind:value={DBState.db.globalChatVariables[`toggle_${toggle.key}`]} />
|
||||||
</div>
|
</div>
|
||||||
|
{:else if toggle.type === 'divider'}
|
||||||
|
{@const prevToggle = groupedToggles[index - 1]}
|
||||||
|
<!-- Prevent multiple dividers appearing in a row -->
|
||||||
|
{#if index === 0 || prevToggle.type !== 'divider' || prevToggle.value !== toggle.value}
|
||||||
|
<div class="w-full min-h-5 flex gap-2 mt-2 items-center" class:justify-end={!reverse}>
|
||||||
|
{#if toggle.value}
|
||||||
|
<span>{toggle.value}</span>
|
||||||
|
{/if}
|
||||||
|
<hr class="border-t border-darkborderc m-0 min-w-32 flex-grow" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex mt-2 items-center">
|
<div class="w-full flex mt-2 items-center" class:justify-end={$MobileGUI}>
|
||||||
<CheckInput check={DBState.db.globalChatVariables[`toggle_${toggle.key}`] === '1'} reverse={reverse} name={toggle.value} onChange={() => {
|
<CheckInput check={DBState.db.globalChatVariables[`toggle_${toggle.key}`] === '1'} reverse={reverse} name={toggle.value} onChange={() => {
|
||||||
DBState.db.globalChatVariables[`toggle_${toggle.key}`] = DBState.db.globalChatVariables[`toggle_${toggle.key}`] === '1' ? '0' : '1'
|
DBState.db.globalChatVariables[`toggle_${toggle.key}`] = DBState.db.globalChatVariables[`toggle_${toggle.key}`] === '1' ? '0' : '1'
|
||||||
}} />
|
}} />
|
||||||
@@ -47,12 +81,12 @@
|
|||||||
{/each}
|
{/each}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if !noContainer && parsedKv.length > 4}
|
{#if !noContainer && groupedToggles.length > 4}
|
||||||
<div class="h-48 border-darkborderc p-2 border rounded flex flex-col items-start mt-2 overflow-y-auto">
|
<div class="h-48 border-darkborderc p-2 border rounded flex flex-col items-start mt-2 overflow-y-auto">
|
||||||
<div class="flex mt-2 items-center w-full" class:justify-end={$MobileGUI}>
|
<div class="flex mt-2 items-center w-full" class:justify-end={$MobileGUI}>
|
||||||
<CheckInput bind:check={DBState.db.jailbreakToggle} name={language.jailbreakToggle} reverse />
|
<CheckInput bind:check={DBState.db.jailbreakToggle} name={language.jailbreakToggle} reverse />
|
||||||
</div>
|
</div>
|
||||||
{@render toggles(true)}
|
{@render toggles(groupedToggles, true)}
|
||||||
{#if DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypaV3}
|
{#if DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypaV3}
|
||||||
<div class="flex mt-2 items-center w-full" class:justify-end={$MobileGUI}>
|
<div class="flex mt-2 items-center w-full" class:justify-end={$MobileGUI}>
|
||||||
<CheckInput bind:check={chara.supaMemory} reverse name={DBState.db.hypaV3 ? language.ToggleHypaMemory : DBState.db.hanuraiEnable ? language.hanuraiMemory : DBState.db.hypaMemory ? language.ToggleHypaMemory : language.ToggleSuperMemory}/>
|
<CheckInput bind:check={chara.supaMemory} reverse name={DBState.db.hypaV3 ? language.ToggleHypaMemory : DBState.db.hanuraiEnable ? language.hanuraiMemory : DBState.db.hypaMemory ? language.ToggleHypaMemory : language.ToggleSuperMemory}/>
|
||||||
@@ -63,7 +97,7 @@
|
|||||||
<div class="flex mt-2 items-center">
|
<div class="flex mt-2 items-center">
|
||||||
<CheckInput bind:check={DBState.db.jailbreakToggle} name={language.jailbreakToggle}/>
|
<CheckInput bind:check={DBState.db.jailbreakToggle} name={language.jailbreakToggle}/>
|
||||||
</div>
|
</div>
|
||||||
{@render toggles()}
|
{@render toggles(groupedToggles)}
|
||||||
{#if DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypaV3}
|
{#if DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypaV3}
|
||||||
<div class="flex mt-2 items-center">
|
<div class="flex mt-2 items-center">
|
||||||
<CheckInput bind:check={chara.supaMemory} name={DBState.db.hypaV3 ? language.ToggleHypaMemory : DBState.db.hanuraiEnable ? language.hanuraiMemory : DBState.db.hypaMemory ? language.ToggleHypaMemory : language.ToggleSuperMemory}/>
|
<CheckInput bind:check={chara.supaMemory} name={DBState.db.hypaV3 ? language.ToggleHypaMemory : DBState.db.hanuraiEnable ? language.hanuraiMemory : DBState.db.hypaMemory ? language.ToggleHypaMemory : language.ToggleSuperMemory}/>
|
||||||
|
|||||||
@@ -1009,33 +1009,69 @@ export function parseKeyValue(template:string){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type sidebarToggleGroup = {
|
||||||
|
key?:string,
|
||||||
|
value?:string,
|
||||||
|
type:'group',
|
||||||
|
children:sidebarToggle[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type sidebarToggleGroupEnd = {
|
||||||
|
key?:string,
|
||||||
|
value?:string,
|
||||||
|
type:'groupEnd',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type sidebarToggle =
|
||||||
|
| sidebarToggleGroup
|
||||||
|
| sidebarToggleGroupEnd
|
||||||
|
| {
|
||||||
|
key?:string,
|
||||||
|
value?:string,
|
||||||
|
type:'divider',
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
key:string,
|
||||||
|
value:string,
|
||||||
|
type:'select',
|
||||||
|
options:string[]
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
key:string,
|
||||||
|
value:string,
|
||||||
|
type:'text'|undefined,
|
||||||
|
options?:string[]
|
||||||
|
}
|
||||||
|
|
||||||
export function parseToggleSyntax(template:string){
|
export function parseToggleSyntax(template:string){
|
||||||
try {
|
try {
|
||||||
console.log(template)
|
|
||||||
if(!template){
|
if(!template){
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyValue:{
|
const keyValue:sidebarToggle[] = []
|
||||||
key:string,
|
|
||||||
value:string,
|
|
||||||
type?:string,
|
|
||||||
options?:string[]
|
|
||||||
}[] = []
|
|
||||||
|
|
||||||
const splited = template.split('\n')
|
const splited = template.split('\n')
|
||||||
|
|
||||||
for(const line of splited){
|
for(const line of splited){
|
||||||
const [key, value, type, option] = line.split('=')
|
const [key, value, type, option] = line.split('=')
|
||||||
if(key && value){
|
if(type === 'group' || type === 'groupEnd' || type === 'divider'){
|
||||||
keyValue.push({
|
keyValue.push({
|
||||||
key, value, type, options: option ? option.split(',') : []
|
key,
|
||||||
|
value,
|
||||||
|
type,
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
} else if((key && value)){
|
||||||
|
keyValue.push({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
type: type === 'select' || type === 'text' ? type : undefined,
|
||||||
|
options: option?.split(',') ?? []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(keyValue)
|
|
||||||
|
|
||||||
return keyValue
|
return keyValue
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|||||||
Reference in New Issue
Block a user