123 lines
5.2 KiB
TypeScript
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>
|
|
);
|
|
} |