diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte similarity index 56% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker.svelte rename to tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte index b865fdd26d..7c9d76e38c 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte @@ -2,11 +2,13 @@ import { mcpClient } from '$lib/clients/mcp.client'; import { conversationsStore } from '$lib/stores/conversations.svelte'; import { mcpStore } from '$lib/stores/mcp.svelte'; - import { getFaviconUrl, debounce } from '$lib/utils'; + import { debounce } from '$lib/utils'; import type { MCPPromptInfo, GetPromptResult, MCPServerSettingsEntry } from '$lib/types'; import { fly } from 'svelte/transition'; import { SvelteMap } from 'svelte/reactivity'; - import { SearchInput } from '$lib/components/app'; + import ChatFormPromptPickerList from './ChatFormPromptPickerList.svelte'; + import ChatFormPromptPickerHeader from './ChatFormPromptPickerHeader.svelte'; + import ChatFormPromptPickerArgumentForm from './ChatFormPromptPickerArgumentForm.svelte'; interface Props { class?: string; @@ -237,6 +239,18 @@ }, 150); } + function handleArgFocus(argName: string) { + if ((suggestions[argName]?.length ?? 0) > 0) { + activeAutocomplete = argName; + } + } + + function handleCancelArgumentForm() { + selectedPrompt = null; + promptArgs = {}; + promptError = null; + } + export function handleKeydown(event: KeyboardEvent): boolean { if (!isOpen) return false; @@ -316,202 +330,39 @@
{#if selectedPrompt} {@const server = serverSettingsMap.get(selectedPrompt.serverName)} - {@const faviconUrl = server ? getFaviconUrl(server.url) : null} + {@const serverLabel = server ? mcpStore.getServerLabel(server) : selectedPrompt.serverName}
-
- {#if faviconUrl} - { - (e.currentTarget as HTMLImageElement).style.display = 'none'; - }} - /> - {/if} + -
-
- {server ? mcpStore.getServerLabel(server) : selectedPrompt.serverName} -
- -
- - {selectedPrompt.title || selectedPrompt.name} - - - {#if selectedPrompt.arguments?.length} - - {selectedPrompt.arguments.length} arg{selectedPrompt.arguments.length > 1 - ? 's' - : ''} - - {/if} -
- {#if selectedPrompt.description} -

- {selectedPrompt.description} -

- {/if} -
-
- -
- {#each selectedPrompt.arguments ?? [] as arg (arg.name)} -
- - - handleArgInput(arg.name, e.currentTarget.value)} - onkeydown={(e) => handleArgKeydown(e, arg.name)} - onblur={() => handleArgBlur(arg.name)} - onfocus={() => { - if ((suggestions[arg.name]?.length ?? 0) > 0) { - activeAutocomplete = arg.name; - } - }} - placeholder={arg.description || arg.name} - required={arg.required} - autocomplete="off" - class="w-full rounded-lg border border-border/50 bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none" - /> - - {#if activeAutocomplete === arg.name && (suggestions[arg.name]?.length ?? 0) > 0} -
- {#each suggestions[arg.name] ?? [] as suggestion, i (suggestion)} - - {/each} -
- {/if} -
- {/each} - - {#if promptError} - - {/if} - -
- - - -
-
+
{:else} -
- {#if showSearchInput} -
- -
- {/if} - -
- {#if isLoading} -
- Loading prompts... -
- {:else if filteredPrompts.length === 0} -
- {prompts.length === 0 ? 'No MCP prompts available' : 'No prompts found'} -
- {:else} - {#each filteredPrompts as prompt, index (prompt.serverName + ':' + prompt.name)} - {@const server = serverSettingsMap.get(prompt.serverName)} - {@const faviconUrl = server ? getFaviconUrl(server.url) : null} - - - {/each} - {/if} -
-
+ mcpStore.getServerLabel(server)} + onPromptClick={handlePromptClick} + /> {/if}
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentForm.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentForm.svelte new file mode 100644 index 0000000000..cb5c8c79ea --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentForm.svelte @@ -0,0 +1,83 @@ + + +
+ {#each prompt.arguments ?? [] as arg (arg.name)} + onArgInput(arg.name, value)} + onKeydown={(e) => onArgKeydown(e, arg.name)} + onBlur={() => onArgBlur(arg.name)} + onFocus={() => onArgFocus(arg.name)} + onSelectSuggestion={(value) => onSelectSuggestion(arg.name, value)} + /> + {/each} + + {#if promptError} + + {/if} + +
+ + + +
+ diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentInput.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentInput.svelte new file mode 100644 index 0000000000..38fa6f4167 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentInput.svelte @@ -0,0 +1,86 @@ + + +
+ + + onInput(e.currentTarget.value)} + onkeydown={onKeydown} + onblur={onBlur} + onfocus={onFocus} + placeholder={argument.description || argument.name} + required={argument.required} + autocomplete="off" + class="w-full rounded-lg border border-border/50 bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none" + /> + + {#if isAutocompleteActive && suggestions.length > 0} +
+ {#each suggestions as suggestion, i (suggestion)} + + {/each} +
+ {/if} +
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerHeader.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerHeader.svelte new file mode 100644 index 0000000000..0b8fe10f0e --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerHeader.svelte @@ -0,0 +1,50 @@ + + +
+ {#if faviconUrl} + { + (e.currentTarget as HTMLImageElement).style.display = 'none'; + }} + /> + {/if} + +
+
+ {serverLabel} +
+ +
+ + {prompt.title || prompt.name} + + + {#if prompt.arguments?.length} + + {prompt.arguments.length} arg{prompt.arguments.length > 1 ? 's' : ''} + + {/if} +
+ {#if prompt.description} +

+ {prompt.description} +

+ {/if} +
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerList.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerList.svelte new file mode 100644 index 0000000000..0de443be00 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerList.svelte @@ -0,0 +1,60 @@ + + +
+ {#if showSearchInput} +
+ +
+ {/if} + +
+ {#if isLoading} + + {:else if prompts.length === 0} +
+ {prompts.length === 0 ? 'No MCP prompts available' : 'No prompts found'} +
+ {:else} + {#each prompts as prompt, index (prompt.serverName + ':' + prompt.name)} + {@const server = serverSettingsMap.get(prompt.serverName)} + {@const serverLabel = server ? getServerLabel(server) : prompt.serverName} + + onPromptClick(prompt)} + /> + {/each} + {/if} +
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItem.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItem.svelte new file mode 100644 index 0000000000..ca4ae90dc0 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItem.svelte @@ -0,0 +1,57 @@ + + + diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItemSkeleton.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItemSkeleton.svelte new file mode 100644 index 0000000000..da9ebdc0a1 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItemSkeleton.svelte @@ -0,0 +1,18 @@ +
+
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
diff --git a/tools/server/webui/src/lib/components/app/index.ts b/tools/server/webui/src/lib/components/app/index.ts index dc2146a63a..1889cb26ac 100644 --- a/tools/server/webui/src/lib/components/app/index.ts +++ b/tools/server/webui/src/lib/components/app/index.ts @@ -14,7 +14,13 @@ export { default as ChatFormActions } from './chat/ChatForm/ChatFormActions/Chat export { default as ChatFormActionSubmit } from './chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte'; export { default as ChatFormFileInputInvisible } from './chat/ChatForm/ChatFormFileInputInvisible.svelte'; export { default as ChatFormHelperText } from './chat/ChatForm/ChatFormHelperText.svelte'; -export { default as ChatFormPromptPicker } from './chat/ChatForm/ChatFormPromptPicker.svelte'; +export { default as ChatFormPromptPicker } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte'; +export { default as ChatFormPromptPickerArgumentForm } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentForm.svelte'; +export { default as ChatFormPromptPickerArgumentInput } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentInput.svelte'; +export { default as ChatFormPromptPickerHeader } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerHeader.svelte'; +export { default as ChatFormPromptPickerList } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerList.svelte'; +export { default as ChatFormPromptPickerListItem } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItem.svelte'; +export { default as ChatFormPromptPickerListItemSkeleton } from './chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerListItemSkeleton.svelte'; export { default as ChatFormTextarea } from './chat/ChatForm/ChatFormTextarea.svelte'; export { default as ChatMessage } from './chat/ChatMessages/ChatMessage.svelte';