1
0
Fork 0
tsgames/src/games/ai/components/header/header.tsx

123 lines
5.2 KiB
TypeScript

import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
import { useBool } from "@common/hooks/useBool";
import { Modal } from "@common/components/modal/modal";
import { Instruct, StateContext } from "../../contexts/state";
import { LLMContext } from "../../contexts/llm";
import { MiniChat } from "../minichat/minichat";
import { AutoTextarea } from "../autoTextarea";
import styles from './header.module.css';
import { Ace } from "../ace";
export const Header = () => {
const { modelName, modelTemplate, contextLength, promptTokens, blockConnection } = useContext(LLMContext);
const {
messages, connectionUrl, systemPrompt, lore, userPrompt, bannedWords, instruct,
setConnectionUrl, setSystemPrompt, setLore, setUserPrompt, addSwipe, setBannedWords, setInstruct
} = useContext(StateContext);
const loreOpen = useBool();
const promptsOpen = useBool();
const assistantOpen = useBool();
const bannedWordsInput = useMemo(() => bannedWords.join('\n'), [bannedWords]);
const urlValid = useMemo(() => contextLength > 0, [contextLength]);
const handleBlurUrl = useCallback(() => {
const regex = /^(?:http(s?):\/\/)?(.*?)\/?$/i
const normalizedConnectionUrl = connectionUrl.replace(regex, 'http$1://$2');
setConnectionUrl(normalizedConnectionUrl);
blockConnection.setFalse();
}, [connectionUrl, setConnectionUrl, blockConnection]);
const handleAssistantAddSwipe = useCallback((answer: string) => {
const index = messages.findLastIndex(m => m.role === 'assistant');
addSwipe(index, answer);
assistantOpen.setFalse();
}, [addSwipe, messages]);
const handleSetBannedWords = useCallback((e: Event) => {
if (e.target instanceof HTMLTextAreaElement) {
const words = e.target.value.split('\n');
setBannedWords(words);
}
}, [setBannedWords]);
const handleBlurBannedWords = useCallback((e: Event) => {
if (e.target instanceof HTMLTextAreaElement) {
const words = e.target.value.split('\n').sort();
setBannedWords(words);
}
}, [setBannedWords]);
return (
<div class={styles.header}>
<div class={styles.inputs}>
<input value={connectionUrl}
onInput={setConnectionUrl}
onFocus={blockConnection.setTrue}
onBlur={handleBlurUrl}
class={blockConnection.value ? '' : urlValid ? styles.valid : styles.invalid}
/>
<select value={instruct} onChange={setInstruct}>
{modelName && modelTemplate && <option value={modelTemplate}>{modelName}</option>}
{Object.entries(Instruct).map(([label, value]) => (
<option value={value} key={value}>
{label.toLowerCase()}
</option>
))}
</select>
<div class={styles.info}>
{promptTokens} / {contextLength}
</div>
</div>
<div class={styles.buttons}>
<button class='icon color' title='Edit lore' onClick={loreOpen.setTrue}>
🌍
</button>
<button class='icon color' title='Edit prompts' onClick={promptsOpen.setTrue}>
📃
</button>
</div>
<div class={styles.buttons}>
<button class='icon' onClick={assistantOpen.setTrue} title='Ask assistant'>
</button>
</div>
<Modal open={loreOpen.value} onClose={loreOpen.setFalse}>
<h3 class={styles.modalTitle}>Lore Editor</h3>
<AutoTextarea
value={lore}
onInput={setLore}
placeholder="Describe your world, for example: World of Awoo has big mountains and wide rivers."
/>
</Modal>
<Modal open={promptsOpen.value} onClose={promptsOpen.setFalse}>
<h3 class={styles.modalTitle}>Prompts Editor</h3>
<div className={styles.scrollPane}>
<h4 class={styles.modalTitle}>System prompt</h4>
<AutoTextarea value={systemPrompt} onInput={setSystemPrompt} />
<hr />
<h4 class={styles.modalTitle}>User prompt template</h4>
<Ace value={userPrompt} onInput={setUserPrompt} />
<hr />
<h4 class={styles.modalTitle}>Banned phrases</h4>
<AutoTextarea
placeholder="Each phrase on separate line"
value={bannedWordsInput}
onInput={handleSetBannedWords}
onBlur={handleBlurBannedWords}
class={styles.template}
/>
</div>
</Modal>
<MiniChat
history={messages}
open={assistantOpen.value}
onClose={assistantOpen.setFalse}
buttons={{ 'Add swipe': handleAssistantAddSwipe }}
/>
</div>
);
}