refac
This commit is contained in:
@@ -12,7 +12,20 @@
|
||||
|
||||
export let onSelect: (e: any) => void = () => {};
|
||||
export let onUpload: (e: any) => void = () => {};
|
||||
export let onCompact: () => void = () => {};
|
||||
export let onStatus: () => void = () => {};
|
||||
export let insertTextHandler: (text: string) => void = () => {};
|
||||
export let canCompact: boolean | (() => boolean) = false;
|
||||
export let compactDisabled: boolean | (() => boolean) = false;
|
||||
export let canStatus: boolean | (() => boolean) = false;
|
||||
export let contextUsage = null;
|
||||
|
||||
$: compactAvailable = typeof canCompact === 'function' ? canCompact() : canCompact;
|
||||
$: isCompactDisabled =
|
||||
typeof compactDisabled === 'function' ? compactDisabled() : compactDisabled;
|
||||
$: statusAvailable = typeof canStatus === 'function' ? canStatus() : canStatus;
|
||||
$: resolvedContextUsage = typeof contextUsage === 'function' ? contextUsage() : contextUsage;
|
||||
$: contextPercent = Math.max(0, Math.round(resolvedContextUsage?.percent ?? 0));
|
||||
|
||||
let suggestionElement: any = null;
|
||||
let filteredItems: any[] = [];
|
||||
@@ -61,11 +74,21 @@
|
||||
bind:this={suggestionElement}
|
||||
{query}
|
||||
bind:filteredItems
|
||||
canCompact={compactAvailable}
|
||||
compactDisabled={isCompactDisabled}
|
||||
canStatus={statusAvailable}
|
||||
{contextPercent}
|
||||
onSelect={(e) => {
|
||||
const { type, data } = e;
|
||||
|
||||
if (type === 'prompt') {
|
||||
insertTextHandler(data.content);
|
||||
} else if (type === 'command' && data.id === 'compact') {
|
||||
insertTextHandler('');
|
||||
onCompact();
|
||||
} else if (type === 'command' && data.id === 'status') {
|
||||
insertTextHandler('');
|
||||
onStatus();
|
||||
} else if (type === 'skill') {
|
||||
command({
|
||||
id: `${data.id}|${data.name}`,
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
export let query = '';
|
||||
export let onSelect = (e) => {};
|
||||
export let canCompact = false;
|
||||
export let compactDisabled = false;
|
||||
export let canStatus = false;
|
||||
export let contextPercent = 0;
|
||||
|
||||
let selectedIdx = 0;
|
||||
export let filteredItems = [];
|
||||
@@ -17,11 +21,24 @@
|
||||
let skills = [];
|
||||
let searchDebounceTimer: ReturnType<typeof setTimeout>;
|
||||
|
||||
$: contextCirclePercent = Math.min(Math.max(0, Math.round(contextPercent)), 100);
|
||||
$: contextCircleOffset = 50.27 * (1 - contextCirclePercent / 100);
|
||||
|
||||
$: commandItems = [
|
||||
...(canCompact && 'compact'.startsWith(query.toLowerCase())
|
||||
? [{ type: 'command', data: { id: 'compact' } }]
|
||||
: []),
|
||||
...(canStatus && 'status'.startsWith(query.toLowerCase())
|
||||
? [{ type: 'command', data: { id: 'status' } }]
|
||||
: [])
|
||||
];
|
||||
|
||||
$: filteredPrompts = prompts
|
||||
.filter((p) => p.command.toLowerCase().includes(query.toLowerCase()))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
$: filteredItems = [
|
||||
...commandItems,
|
||||
...filteredPrompts.map((data) => ({ type: 'prompt', data })),
|
||||
...skills.map((data) => ({ type: 'skill', data }))
|
||||
];
|
||||
@@ -66,6 +83,9 @@
|
||||
|
||||
export const select = async () => {
|
||||
const item = filteredItems[selectedIdx];
|
||||
if (item?.type === 'command' && item.data?.id === 'compact' && compactDisabled) {
|
||||
return;
|
||||
}
|
||||
if (item) {
|
||||
onSelect(item);
|
||||
}
|
||||
@@ -89,15 +109,118 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if commandItems.length > 0}
|
||||
<div class="app-muted mb-0.5 px-2 pt-1 pb-0.5 text-[0.625rem] leading-none">
|
||||
{$i18n.t('Commands')}
|
||||
</div>
|
||||
|
||||
{#each commandItems as item, commandIdx}
|
||||
{#if item.data.id === 'compact'}
|
||||
<Tooltip content="Shorten older messages so this chat can keep going." placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Compact: shorten older messages so this chat can keep going."
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''} disabled:opacity-50"
|
||||
disabled={compactDisabled}
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
on:click={() => {
|
||||
if (!compactDisabled) {
|
||||
onSelect(item);
|
||||
}
|
||||
}}
|
||||
on:mouseenter={() => {
|
||||
selectedIdx = commandIdx;
|
||||
}}
|
||||
on:focus={() => {}}
|
||||
data-selected={commandIdx === selectedIdx}
|
||||
>
|
||||
<span class="app-icon-muted flex items-center justify-center w-4 shrink-0">
|
||||
<svg class="size-3.5 -rotate-90" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<circle
|
||||
cx="10"
|
||||
cy="10"
|
||||
r="8"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="opacity-20"
|
||||
/>
|
||||
<circle
|
||||
cx="10"
|
||||
cy="10"
|
||||
r="8"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-dasharray="50.27"
|
||||
style={`stroke-dashoffset: ${contextCircleOffset};`}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Compact</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
{contextCirclePercent}% full
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'status'}
|
||||
<Tooltip content="Check what is running in this chat." placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Status: check what is running in this chat."
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''}"
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
on:click={() => {
|
||||
onSelect(item);
|
||||
}}
|
||||
on:mouseenter={() => {
|
||||
selectedIdx = commandIdx;
|
||||
}}
|
||||
on:focus={() => {}}
|
||||
data-selected={commandIdx === selectedIdx}
|
||||
>
|
||||
<span class="app-icon-muted flex items-center justify-center w-4 shrink-0">
|
||||
<svg
|
||||
class="size-3.5"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M12 14l4-4" />
|
||||
<path d="M3.34 19a10 10 0 1 1 17.32 0" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Status</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
Check what is running in this chat.
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if filteredPrompts.length > 0}
|
||||
<div class="px-2 py-1 text-[11px] text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('Prompts')}
|
||||
</div>
|
||||
|
||||
{#each filteredPrompts as promptItem, promptIdx}
|
||||
{@const itemIdx = commandItems.length + promptIdx}
|
||||
<Tooltip content={promptItem.name} placement="top-start">
|
||||
<button
|
||||
class="flex h-[1.6875rem] w-full items-center gap-1.5 rounded-xl px-2 text-left text-[13px] hover:bg-gray-50/40 dark:hover:bg-gray-800/40 {promptIdx ===
|
||||
class="flex h-[1.6875rem] w-full items-center gap-1.5 rounded-xl px-2 text-left text-[13px] hover:bg-gray-50/40 dark:hover:bg-gray-800/40 {itemIdx ===
|
||||
selectedIdx
|
||||
? 'bg-gray-50/40 dark:bg-gray-800/40 selected-command-option-button'
|
||||
: ''}"
|
||||
@@ -106,10 +229,10 @@
|
||||
onSelect({ type: 'prompt', data: promptItem });
|
||||
}}
|
||||
on:mousemove={() => {
|
||||
selectedIdx = promptIdx;
|
||||
selectedIdx = itemIdx;
|
||||
}}
|
||||
on:focus={() => {}}
|
||||
data-selected={promptIdx === selectedIdx}
|
||||
data-selected={itemIdx === selectedIdx}
|
||||
>
|
||||
<span class="shrink-0 font-normal text-black dark:text-gray-100">
|
||||
{promptItem.command}
|
||||
@@ -129,7 +252,7 @@
|
||||
</div>
|
||||
|
||||
{#each skills as skill, skillIdx}
|
||||
{@const itemIdx = filteredPrompts.length + skillIdx}
|
||||
{@const itemIdx = commandItems.length + filteredPrompts.length + skillIdx}
|
||||
<Tooltip
|
||||
content={getSkillTooltipContent(skill)}
|
||||
placement="top-start"
|
||||
|
||||
Reference in New Issue
Block a user