refac: suggestion prompts

This commit is contained in:
Timothy Jaeryang Baek 2025-07-04 23:40:31 +04:00
parent db0d2ae6d4
commit 198b89faa5
7 changed files with 32 additions and 64 deletions

View file

@ -193,6 +193,15 @@
}
};
const onSelect = async (e) => {
const { type, data } = e;
if (type === 'prompt') {
// Handle prompt selection
messageInput?.setText(data);
}
};
$: if (selectedModels && chatIdProp !== '') {
saveSessionSelectedModels();
}
@ -2117,6 +2126,7 @@
{chatActionHandler}
{addMessages}
bottomPadding={files.length > 0}
{onSelect}
/>
</div>
</div>
@ -2202,6 +2212,7 @@
toolServers={$toolServers}
{stopResponse}
{createMessagePair}
{onSelect}
on:upload={async (e) => {
const { type, data } = e.detail;

View file

@ -18,7 +18,7 @@
export let models = [];
export let atSelectedModel;
export let submitPrompt;
export let onSelect = (e) => {};
let mounted = false;
let selectedModelIdx = 0;
@ -135,9 +135,7 @@
models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
$config?.default_prompt_suggestions ??
[]}
on:select={(e) => {
submitPrompt(e.detail);
}}
{onSelect}
/>
</div>
</div>

View file

@ -119,6 +119,7 @@
if (chatInput) {
if ($settings?.richTextInput ?? true) {
chatInputElement.setText(text);
chatInputElement.focus();
} else {
chatInput.value = text;
prompt = text;

View file

@ -51,6 +51,8 @@
export let bottomPadding = false;
export let autoScroll;
export let onSelect = (e) => {};
let messagesCount = 20;
let messagesLoading = false;
@ -395,25 +397,7 @@
<div class={className}>
{#if Object.keys(history?.messages ?? {}).length == 0}
<ChatPlaceholder
modelIds={selectedModels}
{atSelectedModel}
submitPrompt={async (p) => {
let text = p;
if (p.includes('{{CLIPBOARD}}')) {
const clipboardText = await navigator.clipboard.readText().catch((err) => {
toast.error($i18n.t('Failed to read clipboard contents'));
return '{{CLIPBOARD}}';
});
text = p.replaceAll('{{CLIPBOARD}}', clipboardText);
}
prompt = text;
await tick();
}}
/>
<ChatPlaceholder modelIds={selectedModels} {atSelectedModel} {onSelect} />
{:else}
<div class="w-full pt-2">
{#key chatId}

View file

@ -43,45 +43,12 @@
export let codeInterpreterEnabled = false;
export let webSearchEnabled = false;
export let onSelect = (e) => {};
export let toolServers = [];
let models = [];
const selectSuggestionPrompt = async (p) => {
let text = p;
if (p.includes('{{CLIPBOARD}}')) {
const clipboardText = await navigator.clipboard.readText().catch((err) => {
toast.error($i18n.t('Failed to read clipboard contents'));
return '{{CLIPBOARD}}';
});
text = p.replaceAll('{{CLIPBOARD}}', clipboardText);
console.log('Clipboard text:', clipboardText, text);
}
prompt = text;
console.log(prompt);
await tick();
const chatInputContainerElement = document.getElementById('chat-input-container');
const chatInputElement = document.getElementById('chat-input');
if (chatInputContainerElement) {
chatInputContainerElement.scrollTop = chatInputContainerElement.scrollHeight;
}
await tick();
if (chatInputElement) {
chatInputElement.focus();
chatInputElement.dispatchEvent(new Event('input'));
}
await tick();
};
let selectedModelIdx = 0;
$: if (selectedModels.length > 0) {
@ -255,9 +222,7 @@
$config?.default_prompt_suggestions ??
[]}
inputValue={prompt}
on:select={(e) => {
selectSuggestionPrompt(e.detail);
}}
{onSelect}
/>
</div>
</div>

View file

@ -1,16 +1,16 @@
<script lang="ts">
import Fuse from 'fuse.js';
import Bolt from '$lib/components/icons/Bolt.svelte';
import { onMount, getContext, createEventDispatcher } from 'svelte';
import { onMount, getContext } from 'svelte';
import { settings, WEBUI_NAME } from '$lib/stores';
import { WEBUI_VERSION } from '$lib/constants';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
export let suggestionPrompts = [];
export let className = '';
export let inputValue = '';
export let onSelect = (e) => {};
let sortedPrompts = [];
@ -85,13 +85,14 @@
{#if filteredPrompts.length > 0}
<div role="list" class="max-h-40 overflow-auto scrollbar-none items-start {className}">
{#each filteredPrompts as prompt, idx (prompt.id || prompt.content)}
<!-- svelte-ignore a11y-no-interactive-element-to-noninteractive-role -->
<button
role="listitem"
class="waterfall flex flex-col flex-1 shrink-0 w-full justify-between
px-3 py-2 rounded-xl bg-transparent hover:bg-black/5
dark:hover:bg-white/5 transition group"
style="animation-delay: {idx * 60}ms"
on:click={() => dispatch('select', prompt.content)}
on:click={() => onSelect({ type: 'prompt', data: prompt.content })}
>
<div class="flex flex-col text-left">
{#if prompt.title && prompt.title[0] !== ''}

View file

@ -207,6 +207,14 @@
selectNextTemplate(editor.view.state, editor.view.dispatch);
};
export const focus = () => {
if (editor) {
editor.view.focus();
// Scroll to the top of the editor
editor.view.dispatch(editor.view.state.tr.scrollIntoView());
}
};
// Function to find the next template in the document
function findNextTemplate(doc, from = 0) {
const patterns = [{ start: '{{', end: '}}' }];