File size: 3,959 Bytes
cfb0fa4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | <script lang="ts">
import Fuse from 'fuse.js';
import Bolt from '$lib/components/icons/Bolt.svelte';
import { onMount, getContext } from 'svelte';
import { settings, WEBUI_NAME } from '$lib/stores';
import { WEBUI_VERSION } from '$lib/constants';
const i18n = getContext('i18n');
export let suggestionPrompts = [];
export let className = '';
export let inputValue = '';
export let onSelect = (e) => {};
let sortedPrompts = [];
const fuseOptions = {
keys: ['content', 'title'],
threshold: 0.5
};
let fuse;
let filteredPrompts = [];
// Initialize Fuse
$: fuse = new Fuse(sortedPrompts, fuseOptions);
// Update the filteredPrompts if inputValue changes
// Only increase version if something wirklich geändert hat
$: getFilteredPrompts(inputValue);
// Helper function to check if arrays are the same
// (based on unique IDs oder content)
function arraysEqual(a, b) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if ((a[i].id ?? a[i].content) !== (b[i].id ?? b[i].content)) {
return false;
}
}
return true;
}
const getFilteredPrompts = (inputValue) => {
if (inputValue.length > 500) {
filteredPrompts = [];
} else {
const newFilteredPrompts =
inputValue.trim() && fuse
? fuse.search(inputValue.trim()).map((result) => result.item)
: sortedPrompts;
// Compare with the oldFilteredPrompts
// If there's a difference, update array + version
if (!arraysEqual(filteredPrompts, newFilteredPrompts)) {
filteredPrompts = newFilteredPrompts;
}
}
};
$: if (suggestionPrompts) {
sortedPrompts = [...(suggestionPrompts ?? [])].sort(() => Math.random() - 0.5);
getFilteredPrompts(inputValue);
}
</script>
<div class="mb-1 flex gap-1 text-xs font-medium items-center text-gray-600 dark:text-gray-400">
{#if filteredPrompts.length > 0}
<Bolt />
{$i18n.t('Suggested')}
{:else}
<!-- Keine Vorschläge -->
<div
class="flex w-full {$settings?.landingPageMode === 'chat'
? ' -mt-1'
: 'text-center items-center justify-center'} self-start text-gray-600 dark:text-gray-400"
>
{$WEBUI_NAME} ‧ v{WEBUI_VERSION}
</div>
{/if}
</div>
<div class="h-40 w-full">
{#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}-${idx}`)}
<!-- 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={() => onSelect({ type: 'prompt', data: prompt.content })}
>
<div class="flex flex-col text-left">
{#if prompt.title && prompt.title[0] !== ''}
<div
class="font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
>
{prompt.title[0]}
</div>
<div class="text-xs text-gray-600 dark:text-gray-400 font-normal line-clamp-1">
{prompt.title[1]}
</div>
{:else}
<div
class="font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
>
{prompt.content}
</div>
<div class="text-xs text-gray-600 dark:text-gray-400 font-normal line-clamp-1">
{$i18n.t('Prompt')}
</div>
{/if}
</div>
</button>
{/each}
</div>
{/if}
</div>
<style>
/* Waterfall animation for the suggestions */
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.waterfall {
opacity: 0;
animation-name: fadeInUp;
animation-duration: 200ms;
animation-fill-mode: forwards;
animation-timing-function: ease;
}
</style>
|