Add branches

This commit is contained in:
Kwaroran
2025-01-06 00:24:03 +09:00
parent aacb60eed9
commit c0e1311568
5 changed files with 201 additions and 5 deletions

View File

@@ -21,12 +21,20 @@
import ModuleChatMenu from "../Setting/Pages/Module/ModuleChatMenu.svelte";
import { ColorSchemeTypeStore } from "src/ts/gui/colorscheme";
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 input = $state('')
let cardExportType = $state('realm')
let cardExportType2 = $state('')
let cardLicense = $state('')
let generationInfoMenuIndex = $state(0)
let branchHover:null|{
x:number,
y:number,
content:string,
} = $state(null)
$effect.pre(() => {
if(btn){
btn.focus()
@@ -34,6 +42,9 @@
if($alertStore.type !== 'input'){
input = ''
}
if($alertStore.type !== 'branches'){
branchHover = null
}
if($alertStore.type !== 'cardexport'){
cardExportType = 'realm'
cardExportType2 = ''
@@ -69,7 +80,7 @@
}
}}></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="bg-darkbg p-4 break-any rounded-md flex flex-col max-w-3xl max-h-full overflow-y-auto">
{#if $alertStore.type === 'error'}
@@ -551,7 +562,78 @@
</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}
<style>

View File

@@ -3,9 +3,9 @@
import { DBState } from 'src/ts/stores.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 { 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 Button from "../UI/GUI/Button.svelte";
import { findCharacterbyId, parseKeyValue, sleep, sortableOptions } from "src/ts/util";
@@ -15,6 +15,7 @@
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";
interface Props {
chara: character|groupChat;
@@ -202,11 +203,19 @@
}}>
<FolderUpIcon size={18}/>
</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
}}>
<PencilIcon size={18}/>
</button>
<button class="text-textcolor2 hover:text-green-500 cursor-pointer" onclick={() => {
alertStore.set({
type: "branches",
msg: ""
})
}}>
<SplitIcon size={18}/>
</button>
</div>
{#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'}

View File

@@ -10,7 +10,7 @@ export interface alertData{
type: 'error'|'normal'|'none'|'ask'|'wait'|'selectChar'
|'input'|'toast'|'wait2'|'markdown'|'select'|'login'
|'tos'|'cardexport'|'requestdata'|'addchar'|'hypaV2'|'selectModule'
|'chatOptions'|'pukmakkurit',
|'chatOptions'|'pukmakkurit'|'branches',
msg: string,
submsg?: string
}

104
src/ts/gui/branches.ts Normal file
View 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);
}

View File

@@ -346,6 +346,7 @@ export async function hypaMemoryV2(
memory?: SerializableHypaV2Data;
}> {
const db = getDatabase();
currentTokens -= db.maxResponse
let data: HypaV2Data = {
lastMainChunkID: 0,
chunks: [],