feat: Prompt Picker and Prompt Attachment UI/UX improvements
This commit is contained in:
parent
5fe03b2bdc
commit
3a64334f6c
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
{#if !readonly && onRemove}
|
||||
<div
|
||||
class="absolute top-8 right-2 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"
|
||||
class="absolute top-10 right-2 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"
|
||||
>
|
||||
<ActionIconRemove id={prompt.name} onRemove={() => onRemove?.()} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
let selectedIndex = $state(0);
|
||||
let internalSearchQuery = $state('');
|
||||
let promptError = $state<string | null>(null);
|
||||
let selectedIndexBeforeArgumentForm = $state<number | null>(null);
|
||||
|
||||
let suggestions = $state<Record<string, string[]>>({});
|
||||
let loadingSuggestions = $state<Record<string, boolean>>({});
|
||||
|
|
@ -99,12 +100,20 @@
|
|||
}
|
||||
|
||||
function handlePromptClick(prompt: MCPPromptInfo) {
|
||||
const requiredArgs = prompt.arguments?.filter((arg) => arg.required) ?? [];
|
||||
const args = prompt.arguments ?? [];
|
||||
|
||||
if (requiredArgs.length > 0) {
|
||||
if (args.length > 0) {
|
||||
selectedIndexBeforeArgumentForm = selectedIndex;
|
||||
selectedPrompt = prompt;
|
||||
promptArgs = {};
|
||||
promptError = null;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const firstInput = document.querySelector(`#arg-${args[0].name}`) as HTMLInputElement;
|
||||
if (firstInput) {
|
||||
firstInput.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
executePrompt(prompt, {});
|
||||
}
|
||||
|
|
@ -212,6 +221,14 @@
|
|||
function handleArgKeydown(event: KeyboardEvent, argName: string) {
|
||||
const argSuggestions = suggestions[argName] ?? [];
|
||||
|
||||
// Handle Escape - return to prompt selection list
|
||||
if (event.key === KeyboardKey.ESCAPE) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleCancelArgumentForm();
|
||||
return;
|
||||
}
|
||||
|
||||
if (argSuggestions.length === 0 || activeAutocomplete !== argName) return;
|
||||
|
||||
if (event.key === KeyboardKey.ARROW_DOWN) {
|
||||
|
|
@ -224,10 +241,6 @@
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
selectSuggestion(argName, argSuggestions[autocompleteIndex]);
|
||||
} else if (event.key === KeyboardKey.ESCAPE) {
|
||||
event.preventDefault();
|
||||
suggestions[argName] = [];
|
||||
activeAutocomplete = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,6 +261,11 @@
|
|||
}
|
||||
|
||||
function handleCancelArgumentForm() {
|
||||
// Restore the previously selected prompt index
|
||||
if (selectedIndexBeforeArgumentForm !== null) {
|
||||
selectedIndex = selectedIndexBeforeArgumentForm;
|
||||
selectedIndexBeforeArgumentForm = null;
|
||||
}
|
||||
selectedPrompt = null;
|
||||
promptArgs = {};
|
||||
promptError = null;
|
||||
|
|
@ -259,8 +277,8 @@
|
|||
if (event.key === KeyboardKey.ESCAPE) {
|
||||
event.preventDefault();
|
||||
if (selectedPrompt) {
|
||||
selectedPrompt = null;
|
||||
promptArgs = {};
|
||||
// Return to prompt selection list, keeping the selected prompt active
|
||||
handleCancelArgumentForm();
|
||||
} else {
|
||||
onClose?.();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
if (selectedElement) {
|
||||
selectedElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
block: 'center',
|
||||
inline: 'nearest'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,11 +79,10 @@
|
|||
|
||||
let showArgBadges = $derived(hasArguments && !isLoading && !loadError);
|
||||
let isAttachment = $derived(variant === McpPromptVariant.ATTACHMENT);
|
||||
let textSizeClass = $derived(isAttachment ? 'text-sm' : 'text-md');
|
||||
let textSizeClass = $derived(isAttachment ? 'text-xs' : 'text-md');
|
||||
let paddingClass = $derived(isAttachment ? 'px-3 py-2' : 'px-3.75 py-2.5');
|
||||
let maxHeightStyle = $derived(
|
||||
isAttachment
|
||||
? 'max-height: 10rem;'
|
||||
: 'min-height: var(--min-message-height); max-height: var(--max-message-height);'
|
||||
isAttachment ? 'max-height: 6rem;' : 'max-height: var(--max-message-height);'
|
||||
);
|
||||
|
||||
const serverFavicon = $derived(mcpStore.getServerFavicon(prompt.serverName));
|
||||
|
|
@ -146,7 +145,7 @@
|
|||
class="relative overflow-hidden rounded-[1.125rem] border border-destructive/50 bg-destructive/10 backdrop-blur-md"
|
||||
>
|
||||
<div
|
||||
class="overflow-y-auto px-3.75 py-2.5"
|
||||
class="overflow-y-auto {paddingClass}"
|
||||
style="{maxHeightStyle} overflow-wrap: anywhere; word-break: break-word;"
|
||||
>
|
||||
<span class="{textSizeClass} text-destructive">{loadError}</span>
|
||||
|
|
@ -154,15 +153,17 @@
|
|||
</Card>
|
||||
{:else if isLoading}
|
||||
<Card
|
||||
class="relative overflow-hidden rounded-[1.125rem] border border-purple-200 bg-purple-500/10 text-foreground backdrop-blur-md dark:border-purple-800 dark:bg-purple-500/20"
|
||||
class="relative overflow-hidden rounded-[1.125rem] border border-purple-200 bg-purple-500/10 backdrop-blur-md dark:border-purple-800 dark:bg-purple-500/20 py-2 px-1"
|
||||
>
|
||||
<div
|
||||
class="overflow-y-auto px-3.75 py-2.5"
|
||||
class="overflow-y-auto {paddingClass}"
|
||||
style="{maxHeightStyle} overflow-wrap: anywhere; word-break: break-word;"
|
||||
>
|
||||
<span class="{textSizeClass} text-purple-500 italic dark:text-purple-400">
|
||||
Loading prompt content...
|
||||
</span>
|
||||
<div class="space-y-2">
|
||||
<div class="h-3 w-3/4 animate-pulse rounded bg-foreground/20"></div>
|
||||
<div class="h-3 w-full animate-pulse rounded bg-foreground/20"></div>
|
||||
<div class="h-3 w-5/6 animate-pulse rounded bg-foreground/20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
{:else if hasContent}
|
||||
|
|
@ -170,7 +171,7 @@
|
|||
class="relative overflow-hidden rounded-[1.125rem] border border-purple-200 bg-purple-500/10 py-0 text-foreground backdrop-blur-md dark:border-purple-800 dark:bg-purple-500/20"
|
||||
>
|
||||
<div
|
||||
class="overflow-y-auto px-3.75 py-2.5"
|
||||
class="overflow-y-auto {paddingClass}"
|
||||
style="{maxHeightStyle} overflow-wrap: anywhere; word-break: break-word;"
|
||||
>
|
||||
<span class="{textSizeClass} whitespace-pre-wrap">
|
||||
|
|
|
|||
Loading…
Reference in New Issue