This commit is contained in:
Timothy Jaeryang Baek committed 2026-06-29 11:35:08 -05:00
1 parent 815446d5bb
commit 61cee42ded
10 files changed
+275 -141

No files matched your search

+2 -2
View File
@@ -127,14 +127,14 @@
<div <div
use:portal use:portal
bind:this={popupElement} bind:this={popupElement}
class="fixed w-48 max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white p-0.5 shadow-lg dark:border-gray-800 dark:bg-gray-850" class="fixed w-48 max-h-48 overflow-y-auto rounded-2xl border border-gray-200 bg-white p-0.5 shadow-lg dark:border-gray-800 dark:bg-gray-850"
role="listbox" role="listbox"
style="z-index: 9999; top: 0; left: 0;" style="z-index: 9999; top: 0; left: 0;"
> >
{#each filteredSuggestionTags as tag (tag)} {#each filteredSuggestionTags as tag (tag)}
<button <button
type="button" type="button"
class="flex w-full items-center rounded-md px-2 py-1 text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800" class="flex w-full items-center rounded-xl px-2 py-[5px] text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800"
role="option" role="option"
on:mousedown={(event) => { on:mousedown={(event) => {
event.preventDefault(); event.preventDefault();
@@ -1,57 +1,70 @@
<script lang="ts"> <script lang="ts">
import { getContext, onMount } from 'svelte'; import { getContext } from 'svelte';
import Checkbox from '$lib/components/common/Checkbox.svelte'; import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import TypeaheadSelector from './TypeaheadSelector.svelte';
const i18n = getContext('i18n'); type Action = {
id: string;
name?: string;
is_global?: boolean;
meta?: {
description?: string;
};
};
export let actions = []; const i18n = getContext('i18n') as any;
export let selectedActionIds = [];
let _actions = {}; export let actions: Action[] = [];
export let selectedActionIds: string[] = [];
onMount(() => { $: selectedActions = actions.filter(
_actions = actions.reduce((acc, action) => { (action) => action.is_global || selectedActionIds.includes(action.id)
acc[action.id] = { );
...action, $: availableActions = actions.filter(
selected: selectedActionIds.includes(action.id) (action) => !action.is_global && !selectedActionIds.includes(action.id)
}; );
return acc; const selectAction = (action: Action) => {
}, {}); selectedActionIds = [...selectedActionIds, action.id];
}); };
</script> </script>
{#if actions.length > 0} {#if actions.length > 0}
<div> <div>
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs font-medium text-gray-500">{$i18n.t('Actions')}</div> <div class=" self-center text-xs text-gray-500">{$i18n.t('Actions')}</div>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<TypeaheadSelector
id="model-actions-selector"
items={availableActions}
className="w-48 max-w-full"
placeholder={$i18n.t('Search actions')}
on:select={(e) => {
selectAction(e.detail);
}}
/>
<div class=" flex items-center flex-wrap"> <div class=" flex items-center flex-wrap">
{#each Object.keys(_actions) as action, actionIdx} {#each selectedActions as action, actionIdx}
<div class=" flex items-center gap-2 mr-3"> <div class=" flex items-center gap-2 mr-3">
<div class="self-center flex items-center"> <div class="self-center flex items-center">
<Checkbox <Checkbox
state={_actions[action].is_global state="checked"
? 'checked' disabled={action.is_global}
: _actions[action].selected
? 'checked'
: 'unchecked'}
disabled={_actions[action].is_global}
on:change={(e) => { on:change={(e) => {
if (!_actions[action].is_global) { if (!action.is_global && e.detail === 'unchecked') {
_actions[action].selected = e.detail === 'checked'; selectedActionIds = selectedActionIds.filter((id) => id !== action.id);
selectedActionIds = Object.keys(_actions).filter((t) => _actions[t].selected);
} }
}} }}
/> />
</div> </div>
<div class=" py-0.5 text-sm w-full capitalize font-medium"> <div class=" py-0.5 text-xs capitalize">
<Tooltip content={_actions[action].meta.description}> <Tooltip content={action.meta?.description ?? action.id}>
{_actions[action].name} {action.name}
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
@@ -2,22 +2,51 @@
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import Checkbox from '$lib/components/common/Checkbox.svelte'; import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import TypeaheadSelector from './TypeaheadSelector.svelte';
const i18n = getContext('i18n'); type Filter = {
id: string;
name?: string;
meta?: {
description?: string;
};
};
export let filters = []; const i18n = getContext('i18n') as any;
export let selectedFilterIds = [];
export let filters: Filter[] = [];
export let selectedFilterIds: string[] = [];
$: selectedFilters = filters.filter((filter) => selectedFilterIds.includes(filter.id));
$: availableFilters = filters.filter((filter) => !selectedFilterIds.includes(filter.id));
const selectFilter = (filter: Filter) => {
selectedFilterIds = [...selectedFilterIds, filter.id];
};
</script> </script>
<div> <div>
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs text-gray-500 font-medium">{$i18n.t('Default Filters')}</div> <div class=" self-center text-xs text-gray-500">{$i18n.t('Default Filters')}</div>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
{#if filters.length > 0} {#if filters.length > 0}
<TypeaheadSelector
id="model-default-filters-selector"
items={availableFilters.map((filter) => ({
...filter,
description: filter.meta?.description
}))}
className="w-48 max-w-full"
placeholder={$i18n.t('Search filters')}
on:select={(e) => {
selectFilter(e.detail);
}}
/>
<div class=" flex items-center flex-wrap"> <div class=" flex items-center flex-wrap">
{#each filters as filter} {#each selectedFilters as filter}
{@const isSelected = selectedFilterIds.includes(filter.id)} {@const isSelected = selectedFilterIds.includes(filter.id)}
<div class=" flex items-center gap-2 mr-3"> <div class=" flex items-center gap-2 mr-3">
<div class="self-center flex items-center"> <div class="self-center flex items-center">
@@ -35,8 +64,8 @@
/> />
</div> </div>
<div class=" py-0.5 text-sm w-full capitalize font-medium"> <div class=" py-0.5 text-xs capitalize">
<Tooltip content={filter.meta.description}> <Tooltip content={filter.meta?.description ?? filter.id}>
{filter.name} {filter.name}
</Tooltip> </Tooltip>
</div> </div>
@@ -2,23 +2,57 @@
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import Checkbox from '$lib/components/common/Checkbox.svelte'; import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import TypeaheadSelector from './TypeaheadSelector.svelte';
const i18n = getContext('i18n'); type Filter = {
id: string;
name?: string;
is_global?: boolean;
meta?: {
description?: string;
};
};
export let filters = []; const i18n = getContext('i18n') as any;
export let selectedFilterIds = [];
export let filters: Filter[] = [];
export let selectedFilterIds: string[] = [];
$: selectedFilters = filters.filter(
(filter) => filter.is_global || selectedFilterIds.includes(filter.id)
);
$: availableFilters = filters.filter(
(filter) => !filter.is_global && !selectedFilterIds.includes(filter.id)
);
const selectFilter = (filter: Filter) => {
selectedFilterIds = [...selectedFilterIds, filter.id];
};
</script> </script>
{#if filters.length > 0} {#if filters.length > 0}
<div> <div>
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs font-medium text-gray-500">{$i18n.t('Filters')}</div> <div class=" self-center text-xs text-gray-500">{$i18n.t('Filters')}</div>
</div> </div>
<!-- TODO: Filter order matters --> <!-- TODO: Filter order matters -->
<div class="flex flex-col"> <div class="flex flex-col">
<TypeaheadSelector
id="model-filters-selector"
items={availableFilters.map((filter) => ({
...filter,
description: filter.meta?.description
}))}
className="w-48 max-w-full"
placeholder={$i18n.t('Search filters')}
on:select={(e) => {
selectFilter(e.detail);
}}
/>
<div class=" flex items-center flex-wrap"> <div class=" flex items-center flex-wrap">
{#each filters as filter} {#each selectedFilters as filter}
{@const isSelected = filter.is_global || selectedFilterIds.includes(filter.id)} {@const isSelected = filter.is_global || selectedFilterIds.includes(filter.id)}
<div class=" flex items-center gap-2 mr-3"> <div class=" flex items-center gap-2 mr-3">
<div class="self-center flex items-center"> <div class="self-center flex items-center">
@@ -39,8 +73,8 @@
/> />
</div> </div>
<div class=" py-0.5 text-sm w-full capitalize font-medium"> <div class=" py-0.5 text-xs capitalize">
<Tooltip content={filter.meta.description}> <Tooltip content={filter.meta?.description ?? filter.id}>
{filter.name} {filter.name}
</Tooltip> </Tooltip>
</div> </div>
@@ -158,7 +158,7 @@
<slot name="label"> <slot name="label">
<div class="mb-2"> <div class="mb-2">
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs font-medium text-gray-500"> <div class=" self-center text-xs text-gray-500">
{$i18n.t('Knowledge')} {$i18n.t('Knowledge')}
</div> </div>
</div> </div>
@@ -190,36 +190,41 @@
{/if} {/if}
{#if loaded} {#if loaded}
<div class="flex flex-wrap flex-row text-sm gap-1"> <div class="flex items-center gap-2">
<KnowledgeSelector <div class="min-w-0">
on:select={(e) => { <KnowledgeSelector
const item = e.detail; on:select={(e) => {
const item = e.detail;
if (!selectedItems.find((k) => k.id === item.id)) { if (!selectedItems.find((k) => k.id === item.id)) {
selectedItems = [ selectedItems = [
...selectedItems, ...selectedItems,
{ {
...item ...item
} }
]; ];
} }
}} }}
>
<div
class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-850 rounded-3xl"
> >
{$i18n.t('Select Knowledge')} <div
</div> class="flex min-w-0 items-center bg-transparent text-xs text-gray-500 outline-hidden hover:underline dark:text-gray-400"
</KnowledgeSelector> >
<span class="truncate">{$i18n.t('Select Knowledge')}</span>
</div>
</KnowledgeSelector>
</div>
{#if $user?.role === 'admin' || $user?.permissions?.chat?.file_upload} {#if $user?.role === 'admin' || $user?.permissions?.chat?.file_upload}
<button <button
class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-850 rounded-3xl" class="self-center bg-transparent text-xs text-gray-500 hover:underline dark:text-gray-400"
type="button" type="button"
aria-label={$i18n.t('Upload Files')}
on:click={() => { on:click={() => {
filesInputElement.click(); filesInputElement.click();
}}>{$i18n.t('Upload Files')}</button }}
> >
{$i18n.t('Upload')}
</button>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -119,15 +119,15 @@
<div slot="content"> <div slot="content">
<div <div
class="z-[10000] text-black dark:text-white rounded-2xl shadow-lg border border-gray-200 dark:border-gray-800 flex flex-col bg-white dark:bg-gray-850 w-70 p-1.5" class="z-[10000] text-black dark:text-white rounded-2xl shadow-lg border border-gray-200 dark:border-gray-800 flex flex-col bg-white dark:bg-gray-850 w-64 p-1"
> >
<div class=" flex w-full space-x-2 px-2 pb-0.5"> <div class=" flex w-full space-x-1.5 px-1.5 pb-0.5">
<div class="flex flex-1"> <div class="flex flex-1">
<div class=" self-center mr-2"> <div class=" self-center mr-1.5">
<Search className="size-3.5" /> <Search className="size-3.5" />
</div> </div>
<input <input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent" class=" w-full text-sm py-0.5 outline-hidden bg-transparent"
bind:value={query} bind:value={query}
on:input={handleSearchInput} on:input={handleSearchInput}
placeholder={$i18n.t('Search')} placeholder={$i18n.t('Search')}
@@ -143,7 +143,7 @@
{:else} {:else}
{#each items as item, i} {#each items as item, i}
{#if i === 0 || item?.type !== items[i - 1]?.type} {#if i === 0 || item?.type !== items[i - 1]?.type}
<div class="px-2 text-xs text-gray-500 py-1"> <div class="px-1.5 text-xs text-gray-500 py-0.5">
{#if item?.type === 'note'} {#if item?.type === 'note'}
{$i18n.t('Notes')} {$i18n.t('Notes')}
{:else if item?.type === 'collection'} {:else if item?.type === 'collection'}
@@ -155,7 +155,7 @@
{/if} {/if}
<div <div
class=" px-2.5 py-1 rounded-xl w-full text-left flex justify-between items-center text-sm hover:bg-gray-50 hover:dark:bg-gray-800 hover:dark:text-gray-100 selected-command-option-button" class=" px-1.5 py-0.5 rounded-xl w-full text-left flex justify-between items-center text-sm hover:bg-gray-50 hover:dark:bg-gray-800 hover:dark:text-gray-100 selected-command-option-button"
> >
<button <button
class="w-full flex-1" class="w-full flex-1"
@@ -1,69 +1,62 @@
<script lang="ts"> <script lang="ts">
import Checkbox from '$lib/components/common/Checkbox.svelte'; import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import { getContext, onMount } from 'svelte'; import TypeaheadSelector from './TypeaheadSelector.svelte';
import { getContext } from 'svelte';
export let skills = []; type Skill = {
id: string;
let _skills = {}; name?: string;
let searchQuery = ''; description?: string;
};
export let skills: Skill[] = [];
export let selectedSkillIds: string[] = []; export let selectedSkillIds: string[] = [];
const i18n = getContext('i18n'); const i18n = getContext('i18n') as any;
$: filteredSkillKeys = Object.keys(_skills).filter((id) => { $: selectedSkills = skills.filter((skill) => selectedSkillIds.includes(skill.id));
if (!searchQuery.trim()) return true; $: availableSkills = skills.filter((skill) => !selectedSkillIds.includes(skill.id));
const q = searchQuery.toLowerCase();
return _skills[id].name?.toLowerCase().includes(q) || _skills[id].id?.toLowerCase().includes(q);
});
onMount(() => { const selectSkill = (skill: Skill) => {
_skills = skills.reduce((acc, skill) => { selectedSkillIds = [...selectedSkillIds, skill.id];
acc[skill.id] = { };
...skill,
selected: selectedSkillIds.includes(skill.id)
};
return acc;
}, {});
});
</script> </script>
<div> <div>
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs font-medium text-gray-500">{$i18n.t('Skills')}</div> <div class=" self-center text-xs text-gray-500">{$i18n.t('Skills')}</div>
</div> </div>
{#if Object.keys(_skills).length > 10}
<div class="mb-2">
<input
class="w-full text-sm bg-transparent outline-none border border-gray-100 dark:border-gray-800 rounded-lg px-3 py-1.5 placeholder-gray-400"
type="text"
placeholder={$i18n.t('Search skills...')}
bind:value={searchQuery}
/>
</div>
{/if}
<div class="flex flex-col mb-1"> <div class="flex flex-col mb-1">
{#if skills.length > 0} {#if skills.length > 0}
<TypeaheadSelector
id="model-skills-selector"
items={availableSkills}
className="w-48 max-w-full"
placeholder={$i18n.t('Search skills')}
on:select={(e) => {
selectSkill(e.detail);
}}
/>
<div class=" flex items-center flex-wrap"> <div class=" flex items-center flex-wrap">
{#each filteredSkillKeys as skill, skillIdx} {#each selectedSkills as skill, skillIdx}
<div class=" flex items-center gap-2 mr-3"> <div class=" flex items-center gap-2 mr-3">
<div class="self-center flex items-center"> <div class="self-center flex items-center">
<Checkbox <Checkbox
state={_skills[skill].selected ? 'checked' : 'unchecked'} state="checked"
on:change={(e) => { on:change={(e) => {
_skills[skill].selected = e.detail === 'checked'; if (e.detail === 'unchecked') {
selectedSkillIds = Object.keys(_skills).filter((s) => _skills[s].selected); selectedSkillIds = selectedSkillIds.filter((id) => id !== skill.id);
}
}} }}
/> />
</div> </div>
<Tooltip content={_skills[skill]?.description ?? _skills[skill].id}> <Tooltip content={skill.description ?? skill.id}>
<div class=" py-0.5 text-sm w-full capitalize font-medium"> <div class=" py-0.5 text-xs capitalize">
{_skills[skill].name} {skill.name}
</div> </div>
</Tooltip> </Tooltip>
</div> </div>
@@ -1,16 +1,23 @@
<script lang="ts"> <script lang="ts">
import { tick } from 'svelte'; import { createEventDispatcher, tick } from 'svelte';
type Voice = { type Voice = {
id: string; id: string;
name?: string; name?: string;
description?: string;
meta?: {
description?: string;
};
}; };
export let id = 'tts-voice';
export let value = ''; export let value = '';
export let voices: Voice[] = []; export let voices: Voice[] = [];
export let placeholder = ''; export let placeholder = '';
export let className = 'w-full';
const listboxId = 'tts-voice-options'; const dispatch = createEventDispatcher<{ select: Voice }>();
const listboxId = `${id}-options`;
let inputElement: HTMLInputElement | null = null; let inputElement: HTMLInputElement | null = null;
let popupElement: HTMLDivElement | null = null; let popupElement: HTMLDivElement | null = null;
@@ -21,8 +28,11 @@
.filter((voice) => { .filter((voice) => {
const id = voice.id.toLowerCase(); const id = voice.id.toLowerCase();
const name = (voice.name ?? '').toLowerCase(); const name = (voice.name ?? '').toLowerCase();
const description = (voice.description ?? voice.meta?.description ?? '').toLowerCase();
return query === '' || id.includes(query) || name.includes(query); return (
query === '' || id.includes(query) || name.includes(query) || description.includes(query)
);
}) })
.slice(0, 8); .slice(0, 8);
$: if (suggestionsOpen && filteredVoices.length > 0) { $: if (suggestionsOpen && filteredVoices.length > 0) {
@@ -52,6 +62,7 @@
const selectVoice = (voice: Voice) => { const selectVoice = (voice: Voice) => {
value = voice.id; value = voice.id;
suggestionsOpen = false; suggestionsOpen = false;
dispatch('select', voice);
}; };
const handlePointerDown = (event: PointerEvent) => { const handlePointerDown = (event: PointerEvent) => {
@@ -75,7 +86,8 @@
<input <input
bind:this={inputElement} bind:this={inputElement}
bind:value bind:value
class="w-full text-sm bg-transparent outline-hidden" id={`${id}-input`}
class="{className} text-sm bg-transparent outline-hidden"
type="text" type="text"
{placeholder} {placeholder}
role="combobox" role="combobox"
@@ -110,14 +122,14 @@
use:portal use:portal
bind:this={popupElement} bind:this={popupElement}
id={listboxId} id={listboxId}
class="fixed max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white p-0.5 shadow-lg dark:border-gray-800 dark:bg-gray-850" class="fixed max-h-48 overflow-y-auto rounded-2xl border border-gray-200 bg-white p-0.5 shadow-lg dark:border-gray-800 dark:bg-gray-850"
role="listbox" role="listbox"
style="z-index: 9999; top: 0; left: 0;" style="z-index: 9999; top: 0; left: 0;"
> >
{#each filteredVoices as voice (voice.id)} {#each filteredVoices as voice (voice.id)}
<button <button
type="button" type="button"
class="flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800" class="flex w-full items-center justify-between gap-3 rounded-xl px-2 py-[5px] text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800"
role="option" role="option"
aria-selected={value === voice.id} aria-selected={value === voice.id}
on:mousedown={(event) => { on:mousedown={(event) => {
@@ -1,51 +1,64 @@
<script lang="ts"> <script lang="ts">
import Checkbox from '$lib/components/common/Checkbox.svelte'; import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import { getContext, onMount } from 'svelte'; import TypeaheadSelector from './TypeaheadSelector.svelte';
import { getContext } from 'svelte';
export let tools = []; type Tool = {
id: string;
name?: string;
meta?: {
description?: string;
};
};
let _tools = {}; export let tools: Tool[] = [];
export let selectedToolIds: string[] = [];
export let selectedToolIds = []; const i18n = getContext('i18n') as any;
const i18n = getContext('i18n'); $: selectedTools = tools.filter((tool) => selectedToolIds.includes(tool.id));
$: availableTools = tools.filter((tool) => !selectedToolIds.includes(tool.id));
onMount(() => { const selectTool = (tool: Tool) => {
_tools = tools.reduce((acc, tool) => { selectedToolIds = [...selectedToolIds, tool.id];
acc[tool.id] = { };
...tool,
selected: selectedToolIds.includes(tool.id)
};
return acc;
}, {});
});
</script> </script>
<div> <div>
<div class="flex w-full justify-between mb-1"> <div class="flex w-full justify-between mb-1">
<div class=" self-center text-xs font-medium text-gray-500">{$i18n.t('Tools')}</div> <div class=" self-center text-xs text-gray-500">{$i18n.t('Tools')}</div>
</div> </div>
<div class="flex flex-col mb-1"> <div class="flex flex-col mb-1">
{#if tools.length > 0} {#if tools.length > 0}
<TypeaheadSelector
id="model-tools-selector"
items={availableTools}
className="w-48 max-w-full"
placeholder={$i18n.t('Search tools')}
on:select={(e) => {
selectTool(e.detail);
}}
/>
<div class=" flex items-center flex-wrap"> <div class=" flex items-center flex-wrap">
{#each Object.keys(_tools) as tool, toolIdx} {#each selectedTools as tool, toolIdx}
<div class=" flex items-center gap-2 mr-3"> <div class=" flex items-center gap-2 mr-3">
<div class="self-center flex items-center"> <div class="self-center flex items-center">
<Checkbox <Checkbox
state={_tools[tool].selected ? 'checked' : 'unchecked'} state="checked"
on:change={(e) => { on:change={(e) => {
_tools[tool].selected = e.detail === 'checked'; if (e.detail === 'unchecked') {
selectedToolIds = Object.keys(_tools).filter((t) => _tools[t].selected); selectedToolIds = selectedToolIds.filter((id) => id !== tool.id);
}
}} }}
/> />
</div> </div>
<Tooltip content={_tools[tool]?.meta?.description ?? _tools[tool].id}> <Tooltip content={tool.meta?.description ?? tool.id}>
<div class=" py-0.5 text-sm w-full capitalize font-medium"> <div class=" py-0.5 text-xs capitalize">
{_tools[tool].name} {tool.name}
</div> </div>
</Tooltip> </Tooltip>
</div> </div>
@@ -0,0 +1,35 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import TTSVoiceInput from './TTSVoiceInput.svelte';
type Item = {
id: string;
name?: string;
description?: string;
meta?: {
description?: string;
};
};
export let items: Item[] = [];
export let placeholder = '';
export let id = 'typeahead-selector';
export let className = 'w-full';
const dispatch = createEventDispatcher<{ select: Item }>();
let value = '';
</script>
<div class="mb-1 block">
<TTSVoiceInput
{id}
voices={items}
{placeholder}
{className}
bind:value
on:select={(e) => {
dispatch('select', e.detail);
value = '';
}}
/>
</div>