Rework catalog and add trash
This commit is contained in:
@@ -595,4 +595,8 @@ export const languageEnglish = {
|
|||||||
dynamicAssets: "Dynamic Assets",
|
dynamicAssets: "Dynamic Assets",
|
||||||
dynamicAssetsEditDisplay: "Use Dynamic Assets in Display",
|
dynamicAssetsEditDisplay: "Use Dynamic Assets in Display",
|
||||||
longTermMemory: "Long Term Memory",
|
longTermMemory: "Long Term Memory",
|
||||||
|
grid: "Grid",
|
||||||
|
list: "List",
|
||||||
|
trash: "Trash",
|
||||||
|
trashDesc: "Deleted characters are moved to trash. you can restore or delete them permanently. deleted characters are automatically purged after 3 days.",
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { characterFormatUpdate, getCharImage } from "../../ts/characters";
|
import { characterFormatUpdate, getCharImage, removeChar } from "../../ts/characters";
|
||||||
import { DataBase } from "../../ts/storage/database";
|
import { DataBase, type Database } from "../../ts/storage/database";
|
||||||
import BarIcon from "../SideBars/BarIcon.svelte";
|
import BarIcon from "../SideBars/BarIcon.svelte";
|
||||||
import { User, Users } from "lucide-svelte";
|
import { ArrowLeft, User, Users, Inspect, TrashIcon, Undo2Icon } from "lucide-svelte";
|
||||||
import { selectedCharID } from "../../ts/stores";
|
import { selectedCharID } from "../../ts/stores";
|
||||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||||
|
import Button from "../UI/GUI/Button.svelte";
|
||||||
|
import { language } from "src/lang";
|
||||||
|
import { parseMultilangString } from "src/ts/util";
|
||||||
|
import { checkCharOrder } from "src/ts/storage/globalApi";
|
||||||
export let endGrid = () => {}
|
export let endGrid = () => {}
|
||||||
let search = ''
|
let search = ''
|
||||||
|
let selected = 0
|
||||||
|
|
||||||
function changeChar(index = -1){
|
function changeChar(index = -1){
|
||||||
characterFormatUpdate(index)
|
characterFormatUpdate(index)
|
||||||
@@ -14,59 +19,123 @@
|
|||||||
endGrid()
|
endGrid()
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatChars(search:string){
|
function formatChars(search:string, db:Database, trash = false){
|
||||||
let charas:{
|
let charas:{
|
||||||
image:string
|
image:string
|
||||||
index:number
|
index:number
|
||||||
type:string
|
type:string,
|
||||||
|
name:string
|
||||||
|
desc:string
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
for(let i=0;i<$DataBase.characters.length;i++){
|
for(let i=0;i<db.characters.length;i++){
|
||||||
const c = $DataBase.characters[i]
|
const c = db.characters[i]
|
||||||
|
if(c.trashTime && !trash){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(!c.trashTime && trash){
|
||||||
|
continue
|
||||||
|
}
|
||||||
if(c.name.replace(/ /g,"").toLocaleLowerCase().includes(search.toLocaleLowerCase().replace(/ /g,""))){
|
if(c.name.replace(/ /g,"").toLocaleLowerCase().includes(search.toLocaleLowerCase().replace(/ /g,""))){
|
||||||
charas.push({
|
charas.push({
|
||||||
image: c.image,
|
image: c.image,
|
||||||
index: i,
|
index: i,
|
||||||
type: c.type
|
type: c.type,
|
||||||
|
name: c.name,
|
||||||
|
desc: c.creatorNotes ?? 'No description'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return charas
|
return charas
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-full w-full flex justify-center">
|
<div class="h-full w-full flex justify-center">
|
||||||
<div class="h-full p-2 bg-darkbg max-w-full w-2xl flex items-center flex-col ">
|
<div class="h-full p-6 bg-darkbg max-w-full w-2xl flex flex-col overflow-y-auto">
|
||||||
<h1 class="text-textcolor text-2xl font-bold mt-2">Catalog</h1>
|
<h1 class="text-textcolor text-2xl font-bold mt-2 flex items-center mx-4 mb-2">
|
||||||
<TextInput placeholder="Search" bind:value={search} size="lg" autocomplete="off" marginBottom={true}/>
|
<button class="mr-2 hover:text-textcolor text-textcolor2"><ArrowLeft /></button>
|
||||||
<div class="w-full flex justify-center">
|
<span>Catalog</span>
|
||||||
<div class="flex flex-wrap gap-2 mx-auto container">
|
</h1>
|
||||||
{#each formatChars(search) as char}
|
<div class="mx-4 mb-6 flex flex-col">
|
||||||
<div class="flex items-center text-textcolor">
|
<TextInput placeholder="Search" bind:value={search} size="lg" autocomplete="off"/>
|
||||||
{#if char.image}
|
<div class="flex flex-wrap gap-2 mt-2">
|
||||||
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={getCharImage(char.image, 'css')}></BarIcon>
|
<Button selected={selected === 0} size="sm" on:click={() => {selected = 0}}>
|
||||||
{:else}
|
{language.grid}
|
||||||
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={char.index === $selectedCharID ? 'background:var(--risu-theme-selected)' : ''}>
|
</Button>
|
||||||
{#if char.type === 'group'}
|
<Button selected={selected === 1} size="sm" on:click={() => {selected = 1}}>
|
||||||
<Users />
|
{language.list}
|
||||||
{:else}
|
</Button>
|
||||||
<User/>
|
<Button selected={selected === 2} size="sm" on:click={() => {selected = 2}}>
|
||||||
{/if}
|
{language.trash}
|
||||||
</BarIcon>
|
</Button>
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if selected === 0}
|
||||||
|
<div class="w-full flex justify-center">
|
||||||
|
<div class="flex flex-wrap gap-2 w-full justify-center">
|
||||||
|
{#each formatChars(search, $DataBase) as char}
|
||||||
|
<div class="flex items-center text-textcolor">
|
||||||
|
{#if char.image}
|
||||||
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={getCharImage(char.image, 'css')}></BarIcon>
|
||||||
|
{:else}
|
||||||
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={char.index === $selectedCharID ? 'background:var(--risu-theme-selected)' : ''}>
|
||||||
|
{#if char.type === 'group'}
|
||||||
|
<Users />
|
||||||
|
{:else}
|
||||||
|
<User/>
|
||||||
|
{/if}
|
||||||
|
</BarIcon>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if selected === 1}
|
||||||
|
{#each formatChars(search, $DataBase) as char}
|
||||||
|
<div class="flex p-2 border border-darkborderc rounded-md mb-2">
|
||||||
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={getCharImage(char.image, 'css')}></BarIcon>
|
||||||
|
<div class="flex-1 flex flex-col ml-2">
|
||||||
|
<h4 class="text-textcolor font-bold text-lg mb-1">{char.name || "Unnamed"}</h4>
|
||||||
|
<span class="text-textcolor2">{parseMultilangString(char.desc)['en'] || parseMultilangString(char.desc)['xx'] || 'No description'}</span>
|
||||||
|
<div class="flex gap-2 justify-end">
|
||||||
|
<button class="hover:text-textcolor text-textcolor2" on:click={() => {
|
||||||
|
changeChar(char.index)
|
||||||
|
}}>
|
||||||
|
<Inspect />
|
||||||
|
</button>
|
||||||
|
<button class="hover:text-textcolor text-textcolor2" on:click={() => {
|
||||||
|
removeChar(char.index, char.name)
|
||||||
|
}}>
|
||||||
|
<TrashIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{:else if selected === 2}
|
||||||
|
<span class="text-textcolor2 text-sm mb-2">{language.trashDesc}</span>
|
||||||
|
{#each formatChars(search, $DataBase, true) as char}
|
||||||
|
<div class="flex p-2 border border-darkborderc rounded-md mb-2">
|
||||||
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={getCharImage(char.image, 'css')}></BarIcon>
|
||||||
|
<div class="flex-1 flex flex-col ml-2">
|
||||||
|
<h4 class="text-textcolor font-bold text-lg mb-1">{char.name || "Unnamed"}</h4>
|
||||||
|
<span class="text-textcolor2">{parseMultilangString(char.desc)['en'] || parseMultilangString(char.desc)['xx'] || 'No description'}</span>
|
||||||
|
<div class="flex gap-2 justify-end">
|
||||||
|
<button class="hover:text-textcolor text-textcolor2" on:click={() => {
|
||||||
|
$DataBase.characters[char.index].trashTime = undefined
|
||||||
|
checkCharOrder()
|
||||||
|
}}>
|
||||||
|
<Undo2Icon />
|
||||||
|
</button>
|
||||||
|
<button class="hover:text-textcolor text-textcolor2" on:click={() => {
|
||||||
|
removeChar(char.index, char.name, 'permanent')
|
||||||
|
}}>
|
||||||
|
<TrashIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.container {
|
|
||||||
justify-content: center;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
import { selectedCharID } from "../../ts/stores";
|
import { selectedCharID } from "../../ts/stores";
|
||||||
import { PlusIcon, SmileIcon, TrashIcon, UserIcon, ActivityIcon, BookIcon, User, CurlyBraces, Volume2Icon } from 'lucide-svelte'
|
import { PlusIcon, SmileIcon, TrashIcon, UserIcon, ActivityIcon, BookIcon, User, CurlyBraces, Volume2Icon } from 'lucide-svelte'
|
||||||
import Check from "../UI/GUI/CheckInput.svelte";
|
import Check from "../UI/GUI/CheckInput.svelte";
|
||||||
import { addCharEmotion, addingEmotion, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage } from "../../ts/characters";
|
import { addCharEmotion, addingEmotion, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage, removeChar } from "../../ts/characters";
|
||||||
import LoreBook from "./LoreBook/LoreBookSetting.svelte";
|
import LoreBook from "./LoreBook/LoreBookSetting.svelte";
|
||||||
import { alertConfirm, alertNormal, alertSelectChar, alertTOS, showHypaV2Alert } from "../../ts/alert";
|
import { alertConfirm, alertNormal, alertSelectChar, alertTOS, showHypaV2Alert } from "../../ts/alert";
|
||||||
import BarIcon from "./BarIcon.svelte";
|
import BarIcon from "./BarIcon.svelte";
|
||||||
@@ -874,20 +874,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<Button on:click={async () => {
|
<Button on:click={async () => {
|
||||||
const conf = await alertConfirm(language.removeConfirm + currentChar.data.name)
|
removeChar($selectedCharID, currentChar.data.name)
|
||||||
if(!conf){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const conf2 = await alertConfirm(language.removeConfirm2 + currentChar.data.name)
|
|
||||||
if(!conf2){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let chars = $DataBase.characters
|
|
||||||
chars.splice($selectedCharID, 1)
|
|
||||||
checkCharOrder()
|
|
||||||
$selectedCharID = -1
|
|
||||||
$DataBase.characters = chars
|
|
||||||
|
|
||||||
}} className="mt-2" size="sm">{ currentChar.type === 'group' ? language.removeGroup : language.removeCharacter}</Button>
|
}} className="mt-2" size="sm">{ currentChar.type === 'group' ? language.removeGroup : language.removeCharacter}</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { get, writable } from "svelte/store";
|
import { get, writable } from "svelte/store";
|
||||||
import { DataBase, saveImage, setDatabase, type character, type Chat, defaultSdDataFunc } from "./storage/database";
|
import { DataBase, saveImage, setDatabase, type character, type Chat, defaultSdDataFunc } from "./storage/database";
|
||||||
import { alertError, alertNormal, alertSelect, alertStore } from "./alert";
|
import { alertConfirm, alertError, alertNormal, alertSelect, alertStore } from "./alert";
|
||||||
import { language } from "../lang";
|
import { language } from "../lang";
|
||||||
import { decode as decodeMsgpack } from "msgpackr";
|
import { decode as decodeMsgpack } from "msgpackr";
|
||||||
import { checkNullish, findCharacterbyId, selectMultipleFile, selectSingleFile, sleep } from "./util";
|
import { checkNullish, findCharacterbyId, selectMultipleFile, selectSingleFile, sleep } from "./util";
|
||||||
@@ -508,4 +508,29 @@ export async function addDefaultCharacters() {
|
|||||||
type: 'none',
|
type: 'none',
|
||||||
msg: ''
|
msg: ''
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeChar(index:number,name:string, type:'normal'|'permanent'|'permanentForce' = 'normal'){
|
||||||
|
const db = get(DataBase)
|
||||||
|
if(type !== 'permanentForce'){
|
||||||
|
const conf = await alertConfirm(language.removeConfirm + name)
|
||||||
|
if(!conf){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const conf2 = await alertConfirm(language.removeConfirm2 + name)
|
||||||
|
if(!conf2){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let chars = db.characters
|
||||||
|
if(type === 'normal'){
|
||||||
|
chars[index].trashTime = Date.now()
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
chars.splice(index, 1)
|
||||||
|
}
|
||||||
|
checkCharOrder()
|
||||||
|
db.characters = chars
|
||||||
|
setDatabase(db)
|
||||||
|
selectedCharID.set(-1)
|
||||||
}
|
}
|
||||||
@@ -772,6 +772,7 @@ export interface character{
|
|||||||
vits?: OnnxModelFiles
|
vits?: OnnxModelFiles
|
||||||
realmId?:string
|
realmId?:string
|
||||||
imported?:boolean
|
imported?:boolean
|
||||||
|
trashTime?:number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -815,6 +816,7 @@ export interface groupChat{
|
|||||||
oneAtTime?:boolean
|
oneAtTime?:boolean
|
||||||
virtualscript?:string
|
virtualscript?:string
|
||||||
lorePlus?:boolean
|
lorePlus?:boolean
|
||||||
|
trashTime?:number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface botPreset{
|
export interface botPreset{
|
||||||
|
|||||||
@@ -977,6 +977,14 @@ async function checkNewFormat() {
|
|||||||
if(db.mainPrompt === oldJailbreak){
|
if(db.mainPrompt === oldJailbreak){
|
||||||
db.mainPrompt = defaultJailbreak
|
db.mainPrompt = defaultJailbreak
|
||||||
}
|
}
|
||||||
|
for(let i=0;i<db.characters.length;i++){
|
||||||
|
const trashTime = db.characters[i].trashTime
|
||||||
|
const targetTrashTime = trashTime ? trashTime + 1000 * 60 * 60 * 24 * 3 : 0
|
||||||
|
if(trashTime && targetTrashTime < Date.now()){
|
||||||
|
db.characters.splice(i,1)
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
setDatabase(db)
|
setDatabase(db)
|
||||||
checkCharOrder()
|
checkCharOrder()
|
||||||
}
|
}
|
||||||
@@ -997,10 +1005,13 @@ export function checkCharOrder() {
|
|||||||
let charIdList:string[] = []
|
let charIdList:string[] = []
|
||||||
|
|
||||||
for(let i=0;i<db.characters.length;i++){
|
for(let i=0;i<db.characters.length;i++){
|
||||||
const charId = db.characters[i].chaId
|
const char = db.characters[i]
|
||||||
charIdList.push(charId)
|
const charId = char.chaId
|
||||||
|
if(!char.trashTime){
|
||||||
|
charIdList.push(charId)
|
||||||
|
}
|
||||||
if(!ordered.includes(charId)){
|
if(!ordered.includes(charId)){
|
||||||
if(charId !== '§temp' && charId !== '§playground'){
|
if(charId !== '§temp' && charId !== '§playground' && !char.trashTime){
|
||||||
db.characterOrder.push(charId)
|
db.characterOrder.push(charId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user