Add branches
This commit is contained in:
@@ -21,12 +21,20 @@
|
|||||||
import ModuleChatMenu from "../Setting/Pages/Module/ModuleChatMenu.svelte";
|
import ModuleChatMenu from "../Setting/Pages/Module/ModuleChatMenu.svelte";
|
||||||
import { ColorSchemeTypeStore } from "src/ts/gui/colorscheme";
|
import { ColorSchemeTypeStore } from "src/ts/gui/colorscheme";
|
||||||
import Help from "./Help.svelte";
|
import Help from "./Help.svelte";
|
||||||
|
import { getChatBranches } from "src/ts/gui/branches";
|
||||||
|
import { getCurrentCharacter } from "src/ts/storage/database.svelte";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
let btn
|
let btn
|
||||||
let input = $state('')
|
let input = $state('')
|
||||||
let cardExportType = $state('realm')
|
let cardExportType = $state('realm')
|
||||||
let cardExportType2 = $state('')
|
let cardExportType2 = $state('')
|
||||||
let cardLicense = $state('')
|
let cardLicense = $state('')
|
||||||
let generationInfoMenuIndex = $state(0)
|
let generationInfoMenuIndex = $state(0)
|
||||||
|
let branchHover:null|{
|
||||||
|
x:number,
|
||||||
|
y:number,
|
||||||
|
content:string,
|
||||||
|
} = $state(null)
|
||||||
$effect.pre(() => {
|
$effect.pre(() => {
|
||||||
if(btn){
|
if(btn){
|
||||||
btn.focus()
|
btn.focus()
|
||||||
@@ -34,6 +42,9 @@
|
|||||||
if($alertStore.type !== 'input'){
|
if($alertStore.type !== 'input'){
|
||||||
input = ''
|
input = ''
|
||||||
}
|
}
|
||||||
|
if($alertStore.type !== 'branches'){
|
||||||
|
branchHover = null
|
||||||
|
}
|
||||||
if($alertStore.type !== 'cardexport'){
|
if($alertStore.type !== 'cardexport'){
|
||||||
cardExportType = 'realm'
|
cardExportType = 'realm'
|
||||||
cardExportType2 = ''
|
cardExportType2 = ''
|
||||||
@@ -69,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
}}></svelte:window>
|
}}></svelte:window>
|
||||||
|
|
||||||
{#if $alertStore.type !== 'none' && $alertStore.type !== 'toast' && $alertStore.type !== 'cardexport' && $alertStore.type !== 'selectModule' && $alertStore.type !== 'pukmakkurit'}
|
{#if $alertStore.type !== 'none' && $alertStore.type !== 'toast' && $alertStore.type !== 'cardexport' && $alertStore.type !== 'branches' && $alertStore.type !== 'selectModule' && $alertStore.type !== 'pukmakkurit'}
|
||||||
<div class="absolute w-full h-full z-50 bg-black bg-opacity-50 flex justify-center items-center" class:vis={ $alertStore.type === 'wait2'}>
|
<div class="absolute w-full h-full z-50 bg-black bg-opacity-50 flex justify-center items-center" class:vis={ $alertStore.type === 'wait2'}>
|
||||||
<div class="bg-darkbg p-4 break-any rounded-md flex flex-col max-w-3xl max-h-full overflow-y-auto">
|
<div class="bg-darkbg p-4 break-any rounded-md flex flex-col max-w-3xl max-h-full overflow-y-auto">
|
||||||
{#if $alertStore.type === 'error'}
|
{#if $alertStore.type === 'error'}
|
||||||
@@ -551,7 +562,78 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if $alertStore.type === 'branches'}
|
||||||
|
<div class="absolute w-full h-full z-50 bg-black bg-opacity-80 flex justify-center items-center overflow-x-auto overflow-y-auto">
|
||||||
|
{#if branchHover !== null}
|
||||||
|
<div class="z-30 whitespace-pre-wrap p-4 text-textcolor bg-darkbg border-darkborderc border rounded-md absolute text-white" style="top: {branchHover.y * 80 + 24}px; left: {(branchHover.x + 1) * 80 + 24}px">
|
||||||
|
{branchHover.content}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="x-50 right-2 top-2 absolute">
|
||||||
|
<button class="bg-darkbg border-darkborderc border p-2 rounded-md" onclick={() => {
|
||||||
|
alertStore.set({
|
||||||
|
type: 'none',
|
||||||
|
msg: ''
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<XIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#each getChatBranches() as obj}
|
||||||
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||||
|
<div
|
||||||
|
role="table"
|
||||||
|
class="peer w-12 h-12 z-20 bg-bgcolor border border-darkborderc rounded-full flex justify-center items-center overflow-y-auto absolute"
|
||||||
|
style="top: {obj.y * 80 + 24}px; left: {obj.x * 80 + 24}px"
|
||||||
|
onmouseenter={() => {
|
||||||
|
if(branchHover === null){
|
||||||
|
const char = getCurrentCharacter()
|
||||||
|
branchHover = {
|
||||||
|
x: obj.x,
|
||||||
|
y: obj.y,
|
||||||
|
content: char.chats[obj.chatId].message[obj.y - 1].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onclick={() => {
|
||||||
|
if(branchHover === null){
|
||||||
|
const char = getCurrentCharacter()
|
||||||
|
branchHover = {
|
||||||
|
x: obj.x,
|
||||||
|
y: obj.y,
|
||||||
|
content: char.chats[obj.chatId].message[obj.y - 1].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onmouseleave={() => {
|
||||||
|
branchHover = null
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{#if obj.connectX === obj.x}
|
||||||
|
{#if obj.multiChild}
|
||||||
|
<div class="w-0 h-20 border-x border-x-red-500 absolute" style="top: {(obj.y-1) * 80 + 24}px; left: {obj.x * 80 + 45}px">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="w-0 h-20 border-x border-x-blue-500 absolute" style="top: {(obj.y-1) * 80 + 24}px; left: {obj.x * 80 + 45}px">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else if obj.connectX !== -1}
|
||||||
|
<div class="w-0 h-10 border-x border-x-red-500 absolute" style="top: {(obj.y) * 80}px; left: {obj.x * 80 + 45}px">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="h-0 border-y border-y-red-500 absolute" style="top: {(obj.y) * 80}px; left: {obj.connectX * 80 + 46}px" style:width={Math.abs((obj.x - obj.connectX) * 80) + 'px'}>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
import { DBState } from 'src/ts/stores.svelte';
|
import { DBState } from 'src/ts/stores.svelte';
|
||||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||||
import { DownloadIcon, PencilIcon, FolderUpIcon, MenuIcon, TrashIcon } from "lucide-svelte";
|
import { DownloadIcon, PencilIcon, FolderUpIcon, MenuIcon, TrashIcon, GitBranchIcon, SplitIcon } from "lucide-svelte";
|
||||||
import { exportChat, importChat } from "src/ts/characters";
|
import { exportChat, importChat } from "src/ts/characters";
|
||||||
import { alertChatOptions, alertConfirm, alertError, alertNormal, alertSelect } from "src/ts/alert";
|
import { alertChatOptions, alertConfirm, alertError, alertNormal, alertSelect, alertStore } from "src/ts/alert";
|
||||||
import { language } from "src/lang";
|
import { language } from "src/lang";
|
||||||
import Button from "../UI/GUI/Button.svelte";
|
import Button from "../UI/GUI/Button.svelte";
|
||||||
import { findCharacterbyId, parseKeyValue, sleep, sortableOptions } from "src/ts/util";
|
import { findCharacterbyId, parseKeyValue, sleep, sortableOptions } from "src/ts/util";
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
|
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
|
||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
import { getChatBranches } from "src/ts/gui/branches";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chara: character|groupChat;
|
chara: character|groupChat;
|
||||||
@@ -202,11 +203,19 @@
|
|||||||
}}>
|
}}>
|
||||||
<FolderUpIcon size={18}/>
|
<FolderUpIcon size={18}/>
|
||||||
</button>
|
</button>
|
||||||
<button class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={() => {
|
<button class="text-textcolor2 hover:text-green-500 mr-2 cursor-pointer" onclick={() => {
|
||||||
editMode = !editMode
|
editMode = !editMode
|
||||||
}}>
|
}}>
|
||||||
<PencilIcon size={18}/>
|
<PencilIcon size={18}/>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={() => {
|
||||||
|
alertStore.set({
|
||||||
|
type: "branches",
|
||||||
|
msg: ""
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<SplitIcon size={18}/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'}
|
{#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export interface alertData{
|
|||||||
type: 'error'|'normal'|'none'|'ask'|'wait'|'selectChar'
|
type: 'error'|'normal'|'none'|'ask'|'wait'|'selectChar'
|
||||||
|'input'|'toast'|'wait2'|'markdown'|'select'|'login'
|
|'input'|'toast'|'wait2'|'markdown'|'select'|'login'
|
||||||
|'tos'|'cardexport'|'requestdata'|'addchar'|'hypaV2'|'selectModule'
|
|'tos'|'cardexport'|'requestdata'|'addchar'|'hypaV2'|'selectModule'
|
||||||
|'chatOptions'|'pukmakkurit',
|
|'chatOptions'|'pukmakkurit'|'branches',
|
||||||
msg: string,
|
msg: string,
|
||||||
submsg?: string
|
submsg?: string
|
||||||
}
|
}
|
||||||
|
|||||||
104
src/ts/gui/branches.ts
Normal file
104
src/ts/gui/branches.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { getCurrentCharacter } from "../storage/database.svelte";
|
||||||
|
|
||||||
|
type ChatBranch = {
|
||||||
|
children: Map<string, ChatBranch>,
|
||||||
|
maxChildren: number,
|
||||||
|
chatId: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(left: string[], branch: ChatBranch, chatId:number){
|
||||||
|
if(left.length === 0){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const current = left[0]
|
||||||
|
if(!branch.children.has(current)){
|
||||||
|
branch.children.set(current, {
|
||||||
|
children: new Map(),
|
||||||
|
maxChildren: 0,
|
||||||
|
chatId: chatId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
search(left.slice(1), branch.children.get(current)!, chatId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaxChildren(branch: ChatBranch){
|
||||||
|
let max = 0
|
||||||
|
if(branch.children.size === 0){
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const child of branch.children.values()){
|
||||||
|
max += (getMaxChildren(child))
|
||||||
|
}
|
||||||
|
branch.maxChildren = max
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenderedBranch = {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
connectX:number,
|
||||||
|
connectY:number,
|
||||||
|
content: string,
|
||||||
|
multiChild: boolean,
|
||||||
|
chatId: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBranch(branch: ChatBranch, x: number, y: number, connectX = -1, connectY = -1): RenderedBranch[]{
|
||||||
|
const rendered: RenderedBranch[] = []
|
||||||
|
for(const [key, child] of branch.children){
|
||||||
|
rendered.push({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
content: key,
|
||||||
|
connectX,
|
||||||
|
connectY,
|
||||||
|
multiChild: branch.children.size > 1,
|
||||||
|
chatId: child.chatId,
|
||||||
|
})
|
||||||
|
const childRendered = renderBranch(child, x, y + 1, x, y)
|
||||||
|
rendered.push(...childRendered)
|
||||||
|
x += child.maxChildren
|
||||||
|
}
|
||||||
|
return rendered
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getChatBranches(){
|
||||||
|
const character = getCurrentCharacter()
|
||||||
|
|
||||||
|
const mainBranch: ChatBranch = {
|
||||||
|
children: new Map(),
|
||||||
|
maxChildren: 0,
|
||||||
|
chatId: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for(const chat of character.chats){
|
||||||
|
const fm = chat.fmIndex === -1 ? character.firstMessage : character.alternateGreetings?.[chat.fmIndex ?? 0]
|
||||||
|
// const chatList = [fm].concat(chat.message.map((v) => v.data))
|
||||||
|
const chatList:string[] = [simpleHasher(fm)]
|
||||||
|
for(const message of chat.message){
|
||||||
|
chatList.push(simpleHasher(message.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
search(chatList, mainBranch, i++)
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxChildren(mainBranch)
|
||||||
|
|
||||||
|
return renderBranch(mainBranch, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function simpleHasher(str: string){
|
||||||
|
let hash = 0;
|
||||||
|
if (str.length == 0) return '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const char = str.charCodeAt(i);
|
||||||
|
hash = ((hash<<5)-hash)+char;
|
||||||
|
hash = hash & hash; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return hash.toString(36);
|
||||||
|
}
|
||||||
@@ -346,6 +346,7 @@ export async function hypaMemoryV2(
|
|||||||
memory?: SerializableHypaV2Data;
|
memory?: SerializableHypaV2Data;
|
||||||
}> {
|
}> {
|
||||||
const db = getDatabase();
|
const db = getDatabase();
|
||||||
|
currentTokens -= db.maxResponse
|
||||||
let data: HypaV2Data = {
|
let data: HypaV2Data = {
|
||||||
lastMainChunkID: 0,
|
lastMainChunkID: 0,
|
||||||
chunks: [],
|
chunks: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user