feat: chat folder
This commit is contained in:
@@ -1,55 +1,116 @@
|
||||
<script lang="ts">
|
||||
import type { Chat, character, groupChat } from "src/ts/storage/database.svelte";
|
||||
|
||||
import { DBState } from 'src/ts/stores.svelte';
|
||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||
import { DownloadIcon, PencilIcon, FolderUpIcon, MenuIcon, TrashIcon, GitBranchIcon, SplitIcon } from "lucide-svelte";
|
||||
import { exportChat, importChat } from "src/ts/characters";
|
||||
import { alertChatOptions, alertConfirm, alertError, alertNormal, alertSelect, alertStore } from "src/ts/alert";
|
||||
import { language } from "src/lang";
|
||||
import Button from "../UI/GUI/Button.svelte";
|
||||
import { findCharacterbyId, parseKeyValue, sleep, sortableOptions } from "src/ts/util";
|
||||
import CheckInput from "../UI/GUI/CheckInput.svelte";
|
||||
import { createMultiuserRoom } from "src/ts/sync/multiuser";
|
||||
import { MobileGUI, ReloadGUIPointer, selectedCharID } from "src/ts/stores.svelte";
|
||||
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { v4 } from "uuid";
|
||||
import { getChatBranches } from "src/ts/gui/branches";
|
||||
import { getModuleToggles } from "src/ts/process/modules";
|
||||
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
|
||||
import { DownloadIcon, PencilIcon, FolderUpIcon, MenuIcon, TrashIcon, GitBranchIcon, SplitIcon, FolderPlusIcon } from "lucide-svelte";
|
||||
|
||||
interface Props {
|
||||
chara: character|groupChat;
|
||||
}
|
||||
import type { Chat, ChatFolder, character, groupChat } from "src/ts/storage/database.svelte";
|
||||
import { DBState } from 'src/ts/stores.svelte';
|
||||
import { MobileGUI, ReloadGUIPointer, selectedCharID } from "src/ts/stores.svelte";
|
||||
|
||||
let { chara = $bindable() }: Props = $props();
|
||||
import CheckInput from "../UI/GUI/CheckInput.svelte";
|
||||
import Button from "../UI/GUI/Button.svelte";
|
||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||
|
||||
import { exportChat, importChat } from "src/ts/characters";
|
||||
import { alertChatOptions, alertConfirm, alertError, alertNormal, alertSelect, alertStore } from "src/ts/alert";
|
||||
import { findCharacterbyId, parseKeyValue, sleep, sortableOptions } from "src/ts/util";
|
||||
import { createMultiuserRoom } from "src/ts/sync/multiuser";
|
||||
import { getChatBranches } from "src/ts/gui/branches";
|
||||
import { getModuleToggles } from "src/ts/process/modules";
|
||||
import { language } from "src/lang";
|
||||
|
||||
interface Props {
|
||||
chara: character|groupChat;
|
||||
}
|
||||
|
||||
let { chara = $bindable() }: Props = $props();
|
||||
let editMode = $state(false)
|
||||
|
||||
let stb: Sortable = null
|
||||
let ele: HTMLDivElement = $state()
|
||||
let chatsStb: Sortable[] = []
|
||||
let folderStb: Sortable = null
|
||||
|
||||
let folderEles: HTMLDivElement = $state()
|
||||
let listEle: HTMLDivElement = $state()
|
||||
let sorted = $state(0)
|
||||
let opened = 0
|
||||
|
||||
const createStb = () => {
|
||||
stb = Sortable.create(ele, {
|
||||
onEnd: async () => {
|
||||
let idx:number[] = []
|
||||
ele.querySelectorAll('[data-risu-idx]').forEach((e, i) => {
|
||||
idx.push(parseInt(e.getAttribute('data-risu-idx')))
|
||||
for (let chat of listEle.querySelectorAll('.risu-chat')) {
|
||||
chatsStb.push(new Sortable(chat, {
|
||||
group: 'chats',
|
||||
onEnd: async (event) => {
|
||||
const currentChatPage = chara.chatPage
|
||||
const newChats: Chat[] = []
|
||||
|
||||
// const chats: HTMLElement = event.to
|
||||
// chats.querySelectorAll()
|
||||
|
||||
listEle.querySelectorAll('[data-risu-chat-folder-idx]').forEach(folder => {
|
||||
const folderIdx = parseInt(folder.getAttribute('data-risu-chat-folder-idx'))
|
||||
folder.querySelectorAll('[data-risu-chat-idx]').forEach(chatInFolder => {
|
||||
const chatIdx = parseInt(chatInFolder.getAttribute('data-risu-chat-idx'))
|
||||
const newChat = chara.chats[chatIdx]
|
||||
newChat.folderId = chara.chatFolders[folderIdx].id
|
||||
newChats.push(newChat)
|
||||
})
|
||||
})
|
||||
|
||||
listEle.querySelectorAll('[data-risu-chat-idx]').forEach(chatEle => {
|
||||
const idx = parseInt(chatEle.getAttribute('data-risu-chat-idx'))
|
||||
const newChat = chara.chats[idx]
|
||||
if (newChats.includes(newChat) == false) {
|
||||
if (newChat.folderId != null)
|
||||
newChat.folderId = null
|
||||
newChats.push(newChat)
|
||||
}
|
||||
})
|
||||
|
||||
chara.chatPage = newChats.indexOf(chara.chats[currentChatPage])
|
||||
chara.chats = newChats
|
||||
|
||||
try {
|
||||
this.destroy()
|
||||
} catch (e) {}
|
||||
sorted += 1
|
||||
await sleep(1)
|
||||
createStb()
|
||||
},
|
||||
...sortableOptions
|
||||
}))
|
||||
}
|
||||
folderStb = Sortable.create(folderEles, {
|
||||
group: 'folders',
|
||||
onEnd: async (event) => {
|
||||
const newFolders: ChatFolder[] = []
|
||||
const newChats: Chat[] = []
|
||||
const folders: HTMLElement[] = Array.from<HTMLElement>(event.to.children)
|
||||
|
||||
const currentChatPage = chara.chatPage
|
||||
|
||||
folders.forEach(folder => {
|
||||
const folderIdx = parseInt(folder.getAttribute('data-risu-chat-folder-idx'))
|
||||
newFolders.push(chara.chatFolders[folderIdx])
|
||||
|
||||
folder.querySelectorAll('[data-risu-chat-idx]').forEach(chatEle => {
|
||||
const idx = parseInt(chatEle.getAttribute('data-risu-chat-idx'))
|
||||
newChats.push(chara.chats[idx])
|
||||
})
|
||||
})
|
||||
console.log(idx)
|
||||
let newValue:Chat[] = []
|
||||
let newChatPage = chara.chatPage
|
||||
idx.forEach((i) => {
|
||||
newValue.push(chara.chats[i])
|
||||
if(chara.chatPage === i){
|
||||
newChatPage = newValue.length - 1
|
||||
|
||||
listEle.querySelectorAll('[data-risu-chat-idx]').forEach(chatEle => {
|
||||
const idx = parseInt(chatEle.getAttribute('data-risu-chat-idx'))
|
||||
if (newChats.includes(chara.chats[idx]) == false) {
|
||||
newChats.push(chara.chats[idx])
|
||||
}
|
||||
})
|
||||
chara.chats = newValue
|
||||
chara.chatPage = newChatPage
|
||||
|
||||
chara.chatFolders = newFolders
|
||||
chara.chatPage = newChats.indexOf(chara.chats[currentChatPage])
|
||||
chara.chats = newChats
|
||||
try {
|
||||
stb.destroy()
|
||||
} catch (error) {}
|
||||
folderStb.destroy()
|
||||
} catch (e) {}
|
||||
sorted += 1
|
||||
await sleep(1)
|
||||
createStb()
|
||||
@@ -61,15 +122,19 @@
|
||||
onMount(createStb)
|
||||
|
||||
onDestroy(() => {
|
||||
if(stb){
|
||||
if (folderStb) {
|
||||
try {
|
||||
folderStb.destroy()
|
||||
} catch (error) {}
|
||||
}
|
||||
chatsStb.map(stb => {
|
||||
try {
|
||||
stb.destroy()
|
||||
} catch (error) {}
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<div class="flex flex-col w-full h-[calc(100%-2rem)] max-h-[calc(100%-2rem)]">
|
||||
|
||||
<Button className="relative bottom-2" onclick={() => {
|
||||
const cha = chara
|
||||
const len = chara.chats.length
|
||||
@@ -90,113 +155,281 @@
|
||||
chara.chatPage = 0
|
||||
$ReloadGUIPointer += 1
|
||||
}}>{language.newChat}</Button>
|
||||
<div class="flex flex-col w-full mt-2 overflow-y-auto flex-grow" bind:this={ele}>
|
||||
{#key sorted}
|
||||
{#each chara.chats as chat, i}
|
||||
<button data-risu-idx={i} onclick={() => {
|
||||
if(!editMode){
|
||||
chara.chatPage = i
|
||||
$ReloadGUIPointer += 1
|
||||
|
||||
}
|
||||
}} class="flex items-center text-textcolor border-solid border-0 border-darkborderc p-2 cursor-pointer rounded-md"class:bg-selected={i === chara.chatPage}>
|
||||
{#if editMode}
|
||||
<TextInput bind:value={chara.chats[i].name} className="flex-grow min-w-0" padding={false}/>
|
||||
{:else}
|
||||
<span>{chat.name}</span>
|
||||
{/if}
|
||||
<div class="flex-grow flex justify-end">
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async () => {
|
||||
const option = await alertChatOptions()
|
||||
switch(option){
|
||||
case 0:{
|
||||
const newChat = safeStructuredClone($state.snapshot(chara.chats[i]))
|
||||
newChat.name = `Copy of ${newChat.name}`
|
||||
chara.chats.unshift(newChat)
|
||||
chara.chatPage = 0
|
||||
chara.chats = chara.chats
|
||||
break
|
||||
}
|
||||
case 1:{
|
||||
const chat = chara.chats[i]
|
||||
if(chat.bindedPersona){
|
||||
const confirm = await alertConfirm(language.doYouWantToUnbindCurrentPersona)
|
||||
if(confirm){
|
||||
chat.bindedPersona = ''
|
||||
alertNormal(language.personaUnbindedSuccess)
|
||||
}
|
||||
}
|
||||
else{
|
||||
const confirm = await alertConfirm(language.doYouWantToBindCurrentPersona)
|
||||
if(confirm){
|
||||
if(!DBState.db.personas[DBState.db.selectedPersona].id){
|
||||
DBState.db.personas[DBState.db.selectedPersona].id = v4()
|
||||
}
|
||||
chat.bindedPersona = DBState.db.personas[DBState.db.selectedPersona].id
|
||||
console.log(DBState.db.personas[DBState.db.selectedPersona])
|
||||
alertNormal(language.personaBindedSuccess)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 2:{
|
||||
chara.chatPage = i
|
||||
createMultiuserRoom()
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<MenuIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={() => {
|
||||
editMode = !editMode
|
||||
}}>
|
||||
<PencilIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
exportChat(i)
|
||||
}}>
|
||||
<DownloadIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
if(chara.chats.length === 1){
|
||||
alertError(language.errors.onlyOneChat)
|
||||
return
|
||||
}
|
||||
const d = await alertConfirm(`${language.removeConfirm}${chat.name}`)
|
||||
if(d){
|
||||
chara.chatPage = 0
|
||||
{#key sorted}
|
||||
<div class="flex flex-col mt-2 overflow-y-auto flex-grow" bind:this={listEle}>
|
||||
<!-- folder div -->
|
||||
<div class="flex flex-col" bind:this={folderEles}>
|
||||
<!-- chat folder -->
|
||||
{#each chara.chatFolders as folder, i}
|
||||
<div data-risu-chat-folder-idx={i} class="flex flex-col mb-2 border-solid border-1 border-darkborderc cursor-pointer rounded-md">
|
||||
<!-- folder header -->
|
||||
<button onclick={() => {
|
||||
if(!editMode) {
|
||||
chara.chatFolders[i].folded = !folder.folded
|
||||
$ReloadGUIPointer += 1
|
||||
let chats = chara.chats
|
||||
chats.splice(i, 1)
|
||||
chara.chats = chats
|
||||
}
|
||||
}}>
|
||||
<TrashIcon size={18}/>
|
||||
}} class="flex items-center text-textcolor border-solid border-0 border-darkborderc p-2 cursor-pointer rounded-md">
|
||||
{#if editMode}
|
||||
<TextInput bind:value={chara.chatFolders[i].name} className="flex-grow min-w-0" padding={false}/>
|
||||
{:else}
|
||||
<span>{folder.name}</span>
|
||||
{/if}
|
||||
<div class="flex-grow flex justify-end">
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={() => {
|
||||
editMode = !editMode
|
||||
}}>
|
||||
<PencilIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
const d = await alertConfirm(`${language.removeConfirm}${folder.name}`)
|
||||
if (d) {
|
||||
$ReloadGUIPointer += 1
|
||||
const folders = chara.chatFolders
|
||||
folders.splice(i, 1)
|
||||
chara.chats.forEach(chat => {
|
||||
if (chat.folderId == folder.id) {
|
||||
chat.folderId = null
|
||||
}
|
||||
})
|
||||
chara.chatFolders = folders
|
||||
}
|
||||
}}>
|
||||
<TrashIcon size={18}/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<!-- chats in folder -->
|
||||
<div class="risu-chat flex flex-col w-full text-textcolor border-solid border-0 border-darkborderc p-2 cursor-pointer rounded-md {folder.folded ? 'hidden' : ''}">
|
||||
{#if chara.chats.filter(chat => chat.folderId == chara.chatFolders[i].id).length == 0}
|
||||
<span class="no-sort flex justify-center text-textcolor2">Empty</span>
|
||||
<div></div>
|
||||
{:else}
|
||||
{#each chara.chats.filter(chat => chat.folderId == chara.chatFolders[i].id) as chat}
|
||||
<button data-risu-chat-idx={chara.chats.indexOf(chat)} onclick={() => {
|
||||
if(!editMode){
|
||||
chara.chatPage = chara.chats.indexOf(chat)
|
||||
$ReloadGUIPointer += 1
|
||||
}
|
||||
}} class="risu-chats flex items-center text-textcolor border-solid border-0 border-darkborderc p-2 cursor-pointer rounded-md"class:bg-selected={chara.chats.indexOf(chat) === chara.chatPage}>
|
||||
{#if editMode}
|
||||
<TextInput bind:value={chat.name} className="flex-grow min-w-0" padding={false}/>
|
||||
{:else}
|
||||
<span>{chat.name}</span>
|
||||
{/if}
|
||||
<div class="flex-grow flex justify-end">
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async () => {
|
||||
const option = await alertChatOptions()
|
||||
switch(option){
|
||||
case 0:{
|
||||
const newChat = safeStructuredClone($state.snapshot(chara.chats[chara.chats.indexOf(chat)]))
|
||||
newChat.name = `Copy of ${newChat.name}`
|
||||
chara.chats.unshift(newChat)
|
||||
chara.chatPage = 0
|
||||
chara.chats = chara.chats
|
||||
break
|
||||
}
|
||||
case 1:{
|
||||
if(chat.bindedPersona){
|
||||
const confirm = await alertConfirm(language.doYouWantToUnbindCurrentPersona)
|
||||
if(confirm){
|
||||
chat.bindedPersona = ''
|
||||
alertNormal(language.personaUnbindedSuccess)
|
||||
}
|
||||
}
|
||||
else{
|
||||
const confirm = await alertConfirm(language.doYouWantToBindCurrentPersona)
|
||||
if(confirm){
|
||||
if(!DBState.db.personas[DBState.db.selectedPersona].id){
|
||||
DBState.db.personas[DBState.db.selectedPersona].id = v4()
|
||||
}
|
||||
chat.bindedPersona = DBState.db.personas[DBState.db.selectedPersona].id
|
||||
console.log(DBState.db.personas[DBState.db.selectedPersona])
|
||||
alertNormal(language.personaBindedSuccess)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 2:{
|
||||
chara.chatPage = chara.chats.indexOf(chat)
|
||||
createMultiuserRoom()
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<MenuIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={() => {
|
||||
editMode = !editMode
|
||||
}}>
|
||||
<PencilIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
exportChat(chara.chats.indexOf(chat))
|
||||
}}>
|
||||
<DownloadIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
if(chara.chats.length === 1){
|
||||
alertError(language.errors.onlyOneChat)
|
||||
return
|
||||
}
|
||||
const d = await alertConfirm(`${language.removeConfirm}${chat.name}`)
|
||||
if(d){
|
||||
chara.chatPage = 0
|
||||
$ReloadGUIPointer += 1
|
||||
let chats = chara.chats
|
||||
chats.splice(chara.chats.indexOf(chat), 1)
|
||||
chara.chats = chats
|
||||
}
|
||||
}}>
|
||||
<TrashIcon size={18}/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
{/key}
|
||||
{/each}
|
||||
</div>
|
||||
<!-- chat without folder div -->
|
||||
<div class="risu-chat flex flex-col">
|
||||
{#each chara.chats as chat, i}
|
||||
{#if chat.folderId == null}
|
||||
<button data-risu-chat-idx={i} onclick={() => {
|
||||
if(!editMode){
|
||||
chara.chatPage = i
|
||||
$ReloadGUIPointer += 1
|
||||
}
|
||||
}}
|
||||
class="flex items-center text-textcolor border-solid border-0 border-darkborderc p-2 cursor-pointer rounded-md"
|
||||
class:bg-selected={i === chara.chatPage}>
|
||||
{#if editMode}
|
||||
<TextInput bind:value={chara.chats[i].name} className="flex-grow min-w-0" padding={false}/>
|
||||
{:else}
|
||||
<span>{chat.name}</span>
|
||||
{/if}
|
||||
<div class="flex-grow flex justify-end">
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async () => {
|
||||
const option = await alertChatOptions()
|
||||
switch(option){
|
||||
case 0:{
|
||||
const newChat = safeStructuredClone($state.snapshot(chara.chats[i]))
|
||||
newChat.name = `Copy of ${newChat.name}`
|
||||
chara.chats.unshift(newChat)
|
||||
chara.chatPage = 0
|
||||
chara.chats = chara.chats
|
||||
break
|
||||
}
|
||||
case 1:{
|
||||
const chat = chara.chats[i]
|
||||
if(chat.bindedPersona){
|
||||
const confirm = await alertConfirm(language.doYouWantToUnbindCurrentPersona)
|
||||
if(confirm){
|
||||
chat.bindedPersona = ''
|
||||
alertNormal(language.personaUnbindedSuccess)
|
||||
}
|
||||
}
|
||||
else{
|
||||
const confirm = await alertConfirm(language.doYouWantToBindCurrentPersona)
|
||||
if(confirm){
|
||||
if(!DBState.db.personas[DBState.db.selectedPersona].id){
|
||||
DBState.db.personas[DBState.db.selectedPersona].id = v4()
|
||||
}
|
||||
chat.bindedPersona = DBState.db.personas[DBState.db.selectedPersona].id
|
||||
console.log(DBState.db.personas[DBState.db.selectedPersona])
|
||||
alertNormal(language.personaBindedSuccess)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 2:{
|
||||
chara.chatPage = i
|
||||
createMultiuserRoom()
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<MenuIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={() => {
|
||||
editMode = !editMode
|
||||
}}>
|
||||
<PencilIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 mr-1 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
exportChat(i)
|
||||
}}>
|
||||
<DownloadIcon size={18}/>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}} class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={async (e) => {
|
||||
e.stopPropagation()
|
||||
if(chara.chats.length === 1){
|
||||
alertError(language.errors.onlyOneChat)
|
||||
return
|
||||
}
|
||||
const d = await alertConfirm(`${language.removeConfirm}${chat.name}`)
|
||||
if(d){
|
||||
chara.chatPage = 0
|
||||
$ReloadGUIPointer += 1
|
||||
let chats = chara.chats
|
||||
chats.splice(i, 1)
|
||||
chara.chats = chats
|
||||
}
|
||||
}}>
|
||||
<TrashIcon size={18}/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/key}
|
||||
|
||||
<div class="border-t border-selected mt-2">
|
||||
<div class="flex mt-2 ml-2 items-center">
|
||||
<button class="text-textcolor2 hover:text-green-500 mr-2 cursor-pointer" onclick={() => {
|
||||
@@ -217,11 +450,22 @@
|
||||
}}>
|
||||
<SplitIcon size={18}/>
|
||||
</button>
|
||||
<button class="ml-auto text-textcolor2 hover:text-green-500 mr-2 cursor-pointer" onclick={() => {
|
||||
const folders = chara.chatFolders
|
||||
const length = chara.chatFolders.length
|
||||
folders.unshift({
|
||||
id: v4(),
|
||||
name: `New Folder ${length + 1}`,
|
||||
folded: false,
|
||||
})
|
||||
chara.chatFolders = folders
|
||||
$ReloadGUIPointer += 1
|
||||
}}>
|
||||
<FolderPlusIcon size={18}/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'}
|
||||
|
||||
|
||||
{#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'}
|
||||
{#if parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()).length > 4}
|
||||
<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}>
|
||||
@@ -268,10 +512,9 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if chara.type === 'group'}
|
||||
<div class="flex mt-2 items-center">
|
||||
<CheckInput bind:check={chara.orderByOrder} name={language.orderByOrder}/>
|
||||
</div>
|
||||
<div class="flex mt-2 items-center">
|
||||
<CheckInput bind:check={chara.orderByOrder} name={language.orderByOrder}/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -32,7 +32,9 @@ export function createNewGroup(){
|
||||
note: '',
|
||||
name: 'Chat 1',
|
||||
localLore: []
|
||||
}], chatPage: 0,
|
||||
}],
|
||||
chatFolders: [],
|
||||
chatPage: 0,
|
||||
viewScreen: 'none',
|
||||
globalLore: [],
|
||||
characters: [],
|
||||
@@ -585,6 +587,7 @@ export function createBlankChar():character{
|
||||
name: 'Chat 1',
|
||||
localLore: []
|
||||
}],
|
||||
chatFolders: [],
|
||||
chatPage: 0,
|
||||
emotionImages: [],
|
||||
bias: [],
|
||||
|
||||
@@ -958,6 +958,7 @@ export interface character{
|
||||
desc:string
|
||||
notes:string
|
||||
chats:Chat[]
|
||||
chatFolders: ChatFolder[]
|
||||
chatPage: number
|
||||
viewScreen: 'emotion'|'none'|'imggen'|'vn',
|
||||
bias: [string, number][]
|
||||
@@ -1096,6 +1097,7 @@ export interface groupChat{
|
||||
image?:string
|
||||
firstMessage:string
|
||||
chats:Chat[]
|
||||
chatFolders: ChatFolder[]
|
||||
chatPage: number
|
||||
name:string
|
||||
viewScreen: 'single'|'multiple'|'none'|'emp',
|
||||
@@ -1302,6 +1304,14 @@ export interface Chat{
|
||||
bindedPersona?:string
|
||||
fmIndex?:number
|
||||
hypaV3Data?:SerializableHypaV3Data
|
||||
folderId?:string
|
||||
}
|
||||
|
||||
export interface ChatFolder{
|
||||
id:string
|
||||
name?:string
|
||||
color?:number
|
||||
folded:boolean
|
||||
}
|
||||
|
||||
export interface Message{
|
||||
|
||||
@@ -1015,4 +1015,7 @@ export const sortableOptions = {
|
||||
delay: 300, // time in milliseconds to define when the sorting should start
|
||||
delayOnTouchOnly: true,
|
||||
filter: '.no-sort',
|
||||
onMove: (event) => {
|
||||
return event.related.className.indexOf('no-sort') === -1
|
||||
}
|
||||
} as const
|
||||
Reference in New Issue
Block a user