287 lines
7.6 KiB
Svelte
287 lines
7.6 KiB
Svelte
<script lang="ts">
|
|
import {
|
|
CharEmotion,
|
|
SizeStore,
|
|
selectedCharID,
|
|
settingsOpen,
|
|
sideBarStore,
|
|
} from "../../ts/stores";
|
|
import { DataBase } from "../../ts/database";
|
|
import BarIcon from "./BarIcon.svelte";
|
|
import SidebarIndicator from "./SidebarIndicator.svelte";
|
|
import {
|
|
Plus,
|
|
User,
|
|
X,
|
|
Settings,
|
|
Users,
|
|
Edit3Icon,
|
|
ArrowUp,
|
|
ArrowDown,
|
|
ListIcon,
|
|
LayoutGridIcon,
|
|
PlusIcon,
|
|
} from "lucide-svelte";
|
|
import {
|
|
characterFormatUpdate,
|
|
createNewCharacter,
|
|
createNewGroup,
|
|
getCharImage,
|
|
} from "../../ts/characters";
|
|
import { importCharacter } from "src/ts/characterCards";
|
|
import CharConfig from "./CharConfig.svelte";
|
|
import { language } from "../../lang";
|
|
import Botpreset from "../Setting/botpreset.svelte";
|
|
import { onDestroy } from "svelte";
|
|
import { isEqual } from "lodash";
|
|
import SidebarAvatar from "./SidebarAvatar.svelte";
|
|
import BaseRoundedButton from "../UI/BaseRoundedButton.svelte";
|
|
let openPresetList = false;
|
|
let sideBarMode = 0;
|
|
let editMode = false;
|
|
let menuMode = 0;
|
|
export let openGrid = () => {};
|
|
|
|
function createScratch() {
|
|
reseter();
|
|
const cid = createNewCharacter();
|
|
selectedCharID.set(-1);
|
|
}
|
|
function createGroup() {
|
|
reseter();
|
|
const cid = createNewGroup();
|
|
selectedCharID.set(-1);
|
|
}
|
|
async function createImport() {
|
|
reseter();
|
|
const cid = await importCharacter();
|
|
selectedCharID.set(-1);
|
|
}
|
|
|
|
function changeChar(index: number) {
|
|
reseter();
|
|
characterFormatUpdate(index);
|
|
selectedCharID.set(index);
|
|
}
|
|
|
|
function reseter() {
|
|
menuMode = 0;
|
|
sideBarMode = 0;
|
|
editMode = false;
|
|
settingsOpen.set(false);
|
|
CharEmotion.set({});
|
|
}
|
|
|
|
let charImages: string[] = [];
|
|
|
|
const unsub = DataBase.subscribe((db) => {
|
|
let newCharImages: string[] = [];
|
|
for (const cha of db.characters) {
|
|
newCharImages.push(cha.image ?? "");
|
|
}
|
|
if (!isEqual(charImages, newCharImages)) {
|
|
charImages = newCharImages;
|
|
}
|
|
});
|
|
|
|
onDestroy(unsub);
|
|
</script>
|
|
|
|
<div
|
|
class="flex h-full w-20 min-w-20 flex-col items-center overflow-x-hidden overflow-y-scroll bg-bgcolor text-white shadow-lg gap-2"
|
|
class:editMode
|
|
>
|
|
<button
|
|
class="absolute top-0 flex h-8 w-14 min-w-14 cursor-pointer items-center justify-center rounded-b-md bg-gray-500 transition-colors hover:bg-green-500"
|
|
on:click={() => {
|
|
menuMode = 1 - menuMode;
|
|
}}><ListIcon /></button
|
|
>
|
|
<div class="h-8 min-h-8 w-14 min-w-14 bg-transparent" />
|
|
{#if menuMode === 0}
|
|
{#each charImages as charimg, i}
|
|
<div class="group relative flex items-center px-2">
|
|
<SidebarIndicator
|
|
isActive={$selectedCharID === i && sideBarMode !== 1}
|
|
/>
|
|
{#if charimg !== ""}
|
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
<div
|
|
on:click={() => {
|
|
changeChar(i);
|
|
}}
|
|
on:keydown={(e) => {
|
|
if (e.key === "Enter") {
|
|
changeChar(i);
|
|
}
|
|
}}
|
|
tabindex="0"
|
|
>
|
|
<SidebarAvatar src={getCharImage($DataBase.characters[i].image, "plain")} size="56" />
|
|
</div>
|
|
{:else}
|
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
<div
|
|
on:click={() => {
|
|
changeChar(i);
|
|
}}
|
|
on:keydown={(e) => {
|
|
if (e.key === "Enter") {
|
|
changeChar(i);
|
|
}
|
|
}}
|
|
tabindex="0"
|
|
>
|
|
<SidebarAvatar size="56" src="https://via.placeholder.com/150" />
|
|
</div>
|
|
{/if}
|
|
{#if editMode}
|
|
<div class="mt-2 flex flex-col">
|
|
<button
|
|
on:click={() => {
|
|
let chars = $DataBase.characters;
|
|
if (chars[i - 1]) {
|
|
const currentchar = chars[i];
|
|
chars[i] = chars[i - 1];
|
|
chars[i - 1] = currentchar;
|
|
$DataBase.characters = chars;
|
|
}
|
|
}}
|
|
>
|
|
<ArrowUp size={20} />
|
|
</button>
|
|
<button
|
|
on:click={() => {
|
|
let chars = $DataBase.characters;
|
|
if (chars[i + 1]) {
|
|
const currentchar = chars[i];
|
|
chars[i] = chars[i + 1];
|
|
chars[i + 1] = currentchar;
|
|
$DataBase.characters = chars;
|
|
}
|
|
}}
|
|
>
|
|
<ArrowDown size={22} />
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
<div class="flex flex-col items-center space-y-2 px-2">
|
|
<BaseRoundedButton
|
|
onClick={() => {
|
|
if (sideBarMode === 1) {
|
|
reseter();
|
|
sideBarMode = 0;
|
|
} else {
|
|
reseter();
|
|
sideBarMode = 1;
|
|
}
|
|
}}
|
|
><svg viewBox="0 0 24 24" width="1.2em" height="1.2em"
|
|
><path
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
|
/></svg
|
|
></BaseRoundedButton
|
|
>
|
|
</div>
|
|
{:else}
|
|
<BarIcon
|
|
onClick={() => {
|
|
if ($settingsOpen) {
|
|
reseter();
|
|
settingsOpen.set(false);
|
|
} else {
|
|
reseter();
|
|
settingsOpen.set(true);
|
|
}
|
|
}}><Settings /></BarIcon
|
|
>
|
|
<BarIcon
|
|
onClick={() => {
|
|
reseter();
|
|
openGrid();
|
|
}}><LayoutGridIcon /></BarIcon
|
|
>
|
|
{/if}
|
|
</div>
|
|
<div
|
|
class="setting-area flex w-96 flex-col overflow-y-auto overflow-x-hidden bg-darkbg p-6 text-gray-200"
|
|
class:flex-grow={$SizeStore.w <= 1000}
|
|
class:minw96={$SizeStore.w > 1000}
|
|
>
|
|
<button
|
|
class="flex w-full justify-end text-gray-200"
|
|
on:click={() => {
|
|
sideBarStore.set(false);
|
|
}}
|
|
>
|
|
<button class="border-none bg-transparent p-0 text-gray-200"><X /></button>
|
|
</button>
|
|
{#if sideBarMode === 0}
|
|
{#if $selectedCharID < 0 || $settingsOpen}
|
|
<div>
|
|
<h1 class="text-xl">Welcome to RisuAI!</h1>
|
|
<span class="text-xs text-gray-400">Click a bot to start chating</span>
|
|
</div>
|
|
{:else}
|
|
<CharConfig />
|
|
{/if}
|
|
{:else if sideBarMode === 1}
|
|
<h2 class="title mt-2 text-xl font-bold">Create</h2>
|
|
<button
|
|
on:click={createScratch}
|
|
class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-5 text-lg drop-shadow-lg hover:bg-selected"
|
|
>
|
|
{language.createfromScratch}
|
|
</button>
|
|
<button
|
|
on:click={createImport}
|
|
class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-5 text-lg drop-shadow-lg hover:bg-selected"
|
|
>
|
|
{language.importCharacter}
|
|
</button>
|
|
<button
|
|
on:click={createGroup}
|
|
class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-3 drop-shadow-lg hover:bg-selected"
|
|
>
|
|
{language.createGroup}
|
|
</button>
|
|
<h2 class="title mt-4 text-xl font-bold">Edit</h2>
|
|
<button
|
|
on:click={() => {
|
|
editMode = !editMode;
|
|
$selectedCharID = -1;
|
|
}}
|
|
class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-3 drop-shadow-lg hover:bg-selected"
|
|
>
|
|
{language.editOrder}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if openPresetList}
|
|
<Botpreset
|
|
close={() => {
|
|
openPresetList = false;
|
|
}}
|
|
/>
|
|
{/if}
|
|
|
|
<style>
|
|
.minw96 {
|
|
min-width: 24rem; /* 384px */
|
|
}
|
|
.title {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.editMode {
|
|
min-width: 6rem;
|
|
}
|
|
</style>
|