diff --git a/tools/ui/src/lib/components/app/badges/BadgesModality.svelte b/tools/ui/src/lib/components/app/badges/BadgesModality.svelte index 4eb3e7838d1..89a7a1f4ffc 100644 --- a/tools/ui/src/lib/components/app/badges/BadgesModality.svelte +++ b/tools/ui/src/lib/components/app/badges/BadgesModality.svelte @@ -1,5 +1,5 @@
@@ -93,54 +96,32 @@ } }} > - - - - - - - + attachmentMenu.callbacks[AttachmentAction.FILE_UPLOAD]()} + > + + Add files - - - - {#each ATTACHMENT_FILE_ITEMS as item (item.id)} - {@const enabled = attachmentMenu.isItemEnabled(item.enabledWhen)} - {#if enabled} - attachmentMenu.callbacks[item.action]()} - > - - - {item.label} - - {:else if item.disabledTooltip} - - - {#snippet child({ props })} -
- - - - {item.label} - -
- {/snippet} -
- - -

{item.disabledTooltip}

-
-
- {/if} - {/each} -
-
+ + {#if supportedModalities.length > 0} + + {#each supportedModalities as modality (modality.label)} + + + + + + +

{modality.label}

+
+
+ {/each} +
+ {/if} + + - - - {#if chatFormActions.hasMcpPromptsSupport} - - - - - - MCP Prompt - - {/if} - - {#if chatFormActions.hasMcpResourcesSupport} - - + + - MCP Resources - - {/if} + MCP Servers +
diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte deleted file mode 100644 index bceb43d2abd..00000000000 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - MCP Servers - - - - {#if hasMcpServers} - -
- {#each filteredMcpServers as server (server.id)} - {@const healthState = mcpStore.getHealthCheckState(server.id)} - {@const hasError = healthState.status === HealthCheckStatus.ERROR} - {@const isEnabledForChat = isServerEnabledForChat(server.id)} - {@const displayName = getServerLabel(server)} - {@const faviconUrl = mcpStore.getServerFavicon(server.id)} - - - {/each} -
- - {#snippet footer()} - - - - Manage MCP Servers - - {/snippet} -
- {:else} -
- No MCP servers configured -
- - - - - - - Add MCP Servers - - {/if} -
-
-
diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpSubmenu.svelte new file mode 100644 index 00000000000..07439afd617 --- /dev/null +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpSubmenu.svelte @@ -0,0 +1,51 @@ + + + + + + + MCP + + + + + + + Servers + + + {#if chatFormActions.hasMcpPromptsSupport} + + + + Prompts + + {/if} + + {#if chatFormActions.hasMcpResourcesSupport} + + + + Resources + + {/if} + + diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte index 1b6fc4b0209..197d6c2e590 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte @@ -8,70 +8,68 @@ const reasoning = useReasoningMenu(); -{#if reasoning.modelSupportsThinking} - - - {#if reasoning.thinkingEnabled} - - {:else if reasoning.isOff} - - {:else} - - {/if} + + + {#if reasoning.isReasoningActive} + + {:else if reasoning.isOff} + + {:else} + + {/if} - - Reasoning + + Reasoning - - {reasoning.currentEffort} - + + {reasoning.currentEffort} - + + - - {#each reasoning.levels as level (level.value)} - {@const tokenLabel = reasoning.tokenLabel(level)} - reasoning.select(level)} - > - {#if reasoning.isSelected(level)} - - {:else} -
- {/if} + + {#each reasoning.levels as level (level.value)} + {@const tokenLabel = reasoning.tokenLabel(level)} + reasoning.select(level)} + > + {#if reasoning.isSelected(level)} + + {:else} +
+ {/if} - {level.label} + {level.label} - {#if tokenLabel} - - {tokenLabel} - - {/if} + {#if tokenLabel} + + {tokenLabel} + + {/if} - {#if level.hasInfo} - - - - + {#if level.hasInfo} + + + + - -

Maximum reasoning effort with extended context usage

-
-
- {/if} -
- {/each} -
-
-{/if} + +

Maximum reasoning effort with extended context usage

+
+ + {/if} + + {/each} + + diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte index 2f69dc96de7..e8da0af6b9e 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte @@ -1,18 +1,10 @@
@@ -88,65 +73,6 @@
- {#if reasoning.modelSupportsThinking} - (reasoningExpanded = open)} - open={reasoningExpanded} - > - - {#if reasoningExpanded} - - {:else} - - {/if} - - {#if reasoning.thinkingEnabled} - - {:else if reasoning.isOff} - - {:else} - - {/if} - - Reasoning - - - {reasoning.currentEffort} - - - - -
- {#each reasoning.levels as level (level.value)} - {@const tokenLabel = reasoning.tokenLabel(level)} - - {/each} -
-
-
- {/if} - (filesExpanded = open)} open={filesExpanded}> {#if filesExpanded} @@ -194,80 +120,15 @@ - (mcpExpanded = open)} open={mcpExpanded}> - - {#if mcpExpanded} - - {:else} - - {/if} - - - - MCP Servers - - - {mcpServers.length} server{mcpServers.length !== 1 ? 's' : ''} - - - - -
- {#each mcpServers as server (server.id)} - {@const healthState = mcpStore.getHealthCheckState(server.id)} - {@const hasError = healthState.status === HealthCheckStatus.ERROR} - {@const displayName = mcpStore.getServerLabel(server)} - {@const faviconUrl = mcpStore.getServerFavicon(server.id)} - {@const isEnabled = conversationsStore.preferences.isMcpServerEnabledForChat( - server.id - )} - - - {/each} +
-
-
+ System Message + {#if toolsPanel.totalToolCount > 0} (toolsExpanded = open)} open={toolsExpanded}> @@ -289,40 +150,12 @@
- {#each toolsPanel.activeGroups as group (group.key)} - {@const checked = toolsPanel.isGroupChecked(group)} - {@const enabledCount = toolsPanel.getEnabledToolCount(group)} - {@const favicon = toolsPanel.getFavicon(group)} + {#each toolsPanel.categoryGroups as group (group.key)} + {@render sheetGroupRow(group)} + {/each} - + {#each toolsPanel.mcpGroups as group (group.key)} + {@render sheetGroupRow(group)} {/each}
@@ -331,38 +164,54 @@ - - {#if chatFormActions.hasMcpPromptsSupport} - - {/if} - - {#if chatFormActions.hasMcpResourcesSupport} - - {/if}
+ +{#snippet sheetGroupRow(group: ToolGroup)} + {@const checked = toolsPanel.isGroupChecked(group)} + {@const enabledCount = toolsPanel.getEnabledToolCount(group)} + {@const favicon = toolsPanel.getFavicon(group)} + {@const groupDisabled = toolsPanel.isGroupDisabled(group)} + + +{/snippet} diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte index 40fed27c70a..79f533b22fc 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte @@ -7,6 +7,7 @@ import { CLI_FLAGS, ICON_CLASS_DEFAULT } from '$lib/constants'; import { useToolsPanel } from '$lib/hooks/use-tools-panel.svelte'; import { mcpStore, toolsStore } from '$lib/stores'; + import type { ToolGroup } from '$lib/types'; const toolsPanel = useToolsPanel(); const hasMcpServersAvailable = $derived(mcpStore.getServers().length > 0); @@ -62,95 +63,107 @@ {/if} {:else}
- {#each toolsPanel.activeGroups as group (group.key)} - {@const isExpanded = toolsPanel.expandedGroups.has(group.key)} - {@const checked = toolsPanel.isGroupChecked(group)} - {@const favicon = toolsPanel.getFavicon(group)} - - toolsPanel.toggleGroupExpanded(group.key)} - open={isExpanded} - > -
- - {#if isExpanded} - - {:else} - - {/if} - - - {#if favicon} - { - (e.currentTarget as HTMLImageElement).style.display = 'none'; - }} - src={favicon} - /> - {/if} - - {group.label} - - - - {toolsPanel.getEnabledToolCount(group)}/{group.tools.length} - - - - - - {#snippet child({ props })} - toolsPanel.toggleGroupByKey(group.key)} - /> - {/snippet} - - - -

- {checked ? 'Disable' : 'Enable'} - {group.tools.length} tool{group.tools.length !== 1 ? 's' : ''} -

-
-
-
- - -
- {#each group.tools as entry (entry.key)} - {@const enabled = toolsStore.isToolEnabled(entry.key)} - - {/each} -
-
-
+ {#each toolsPanel.categoryGroups as group (group.key)} + {@render groupRow(group)} + {/each} + + {#each toolsPanel.mcpGroups as group (group.key)} + {@render groupRow(group)} {/each}
{/if} + +{#snippet groupRow(group: ToolGroup)} + {@const isExpanded = toolsPanel.expandedGroups.has(group.key)} + {@const checked = toolsPanel.isGroupChecked(group)} + {@const favicon = toolsPanel.getFavicon(group)} + {@const groupDisabled = toolsPanel.isGroupDisabled(group)} + + toolsPanel.toggleGroupExpanded(group.key)} + open={isExpanded} + > +
+ + {#if isExpanded} + + {:else} + + {/if} + + + {#if favicon} + { + (e.currentTarget as HTMLImageElement).style.display = 'none'; + }} + src={favicon} + /> + {/if} + + {group.label} + + + + {toolsPanel.getEnabledToolCount(group)}/{group.tools.length} + + + + + + {#snippet child({ props })} + toolsPanel.toggleGroupByKey(group.key)} + /> + {/snippet} + + + +

+ {checked ? 'Disable' : 'Enable'} + {group.tools.length} tool{group.tools.length !== 1 ? 's' : ''} +

+
+
+
+ + +
+ {#each group.tools as entry (entry.key)} + {@const enabled = toolsPanel.isToolEnabled(entry)} + {@const parentDisabled = toolsPanel.isToolParentDisabled(entry)} + + {/each} +
+
+
+{/snippet} diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte index 97351b3a6cb..2fcb1ed5f8e 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte @@ -1,6 +1,5 @@ - + diff --git a/tools/ui/src/lib/components/app/dialogs/DialogMcpServerAddNew.svelte b/tools/ui/src/lib/components/app/dialogs/DialogMcpServerAddNew.svelte index ae8b24cb2d3..bc28a754c61 100644 --- a/tools/ui/src/lib/components/app/dialogs/DialogMcpServerAddNew.svelte +++ b/tools/ui/src/lib/components/app/dialogs/DialogMcpServerAddNew.svelte @@ -10,7 +10,7 @@ RECOMMENDED_MCP_SERVERS } from '$lib/constants'; import { BooleanString, HealthCheckStatus } from '$lib/enums'; - import { conversationsStore, mcpStore } from '$lib/stores'; + import { mcpStore } from '$lib/stores'; import { canonicalizeServerUrl, parseHeadersToArray, uuid } from '$lib/utils'; interface Props { @@ -234,8 +234,6 @@ useProxy: newServerUseProxy }); - conversationsStore.preferences.setMcpServerOverride(newServerId, true); - handleOpenChange(false); } @@ -246,7 +244,7 @@ - + Add New MCP Server diff --git a/tools/ui/src/lib/components/app/dialogs/DialogMcpServers.svelte b/tools/ui/src/lib/components/app/dialogs/DialogMcpServers.svelte new file mode 100644 index 00000000000..2eaaa0a6577 --- /dev/null +++ b/tools/ui/src/lib/components/app/dialogs/DialogMcpServers.svelte @@ -0,0 +1,33 @@ + + + + + + + + + MCP Servers + + + + + + diff --git a/tools/ui/src/lib/components/app/dialogs/DialogMermaidPreview.svelte b/tools/ui/src/lib/components/app/dialogs/DialogMermaidPreview.svelte index 09e53442ac6..e741373497a 100644 --- a/tools/ui/src/lib/components/app/dialogs/DialogMermaidPreview.svelte +++ b/tools/ui/src/lib/components/app/dialogs/DialogMermaidPreview.svelte @@ -14,6 +14,7 @@ diff --git a/tools/ui/src/lib/components/app/dialogs/DialogSettingsChat.svelte b/tools/ui/src/lib/components/app/dialogs/DialogSettingsChat.svelte new file mode 100644 index 00000000000..5e0046aae52 --- /dev/null +++ b/tools/ui/src/lib/components/app/dialogs/DialogSettingsChat.svelte @@ -0,0 +1,34 @@ + + + + + + + + + Settings + + + + (open = false)} onSectionChange={() => {}} /> + + diff --git a/tools/ui/src/lib/components/app/dialogs/index.ts b/tools/ui/src/lib/components/app/dialogs/index.ts index 38aa0993549..1ffa7a2b9e2 100644 --- a/tools/ui/src/lib/components/app/dialogs/index.ts +++ b/tools/ui/src/lib/components/app/dialogs/index.ts @@ -18,6 +18,23 @@ */ export { default as DialogMcpServerAddNew } from './DialogMcpServerAddNew.svelte'; +/** + * **DialogMcpServers** - MCP servers dialog shown from the chat form + * + * Shows the same MCP server list as the `/mcp-servers` route inside a modal + * dialog. + */ +export { default as DialogMcpServers } from './DialogMcpServers.svelte'; + +/** + * **DialogSettingsChat** - Chat settings shown in a modal dialog + * + * Wraps the full SettingsChat layout (sidebar, mobile header, fields, footer) + * inside a ShadCN Dialog instead of a dedicated route. Section switching is + * handled in-app via `onSectionChange` rather than URL navigation. + */ +export { default as DialogSettingsChat } from './DialogSettingsChat.svelte'; + /** * **DialogExportSettings** - Settings export dialog with sensitive data warning * diff --git a/tools/ui/src/lib/components/app/mcp/McpActiveServersAvatars.svelte b/tools/ui/src/lib/components/app/mcp/McpActiveServersAvatars.svelte index ea274d5aa79..5ac7a7fae6a 100644 --- a/tools/ui/src/lib/components/app/mcp/McpActiveServersAvatars.svelte +++ b/tools/ui/src/lib/components/app/mcp/McpActiveServersAvatars.svelte @@ -3,7 +3,7 @@ import * as Tooltip from '$lib/components/ui/tooltip'; import { ICON_CLASS_DEFAULT, MAX_DISPLAYED_MCP_AVATARS } from '$lib/constants'; import { HealthCheckStatus } from '$lib/enums'; - import { conversationsStore, mcpStore } from '$lib/stores'; + import { mcpStore } from '$lib/stores'; interface Props { class?: string; @@ -13,11 +13,7 @@ let { class: className = '', onclick }: Props = $props(); let mcpServers = $derived(mcpStore.getServers().filter((s) => s.enabled)); - let enabledMcpServersForChat = $derived( - mcpServers.filter( - (s) => conversationsStore.preferences.isMcpServerEnabledForChat(s.id) && s.url.trim() - ) - ); + let enabledMcpServersForChat = $derived(mcpServers.filter((s) => s.url.trim())); let healthyEnabledMcpServers = $derived( enabledMcpServersForChat.filter((s) => { const healthState = mcpStore.getHealthCheckState(s.id); diff --git a/tools/ui/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte b/tools/ui/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte index 632cd3e8a72..59695470365 100644 --- a/tools/ui/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte +++ b/tools/ui/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte @@ -1,13 +1,22 @@ {#if capabilities} @@ -20,10 +29,24 @@ {/if} {#if capabilities.server.resources} - + { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onBrowseResources?.(); + } + }} + role="button" + tabindex={0} + variant="outline" + > Resources + + {/if} diff --git a/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte b/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte index 84383d59eac..6e9a9953851 100644 --- a/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte +++ b/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte @@ -22,9 +22,10 @@ onToggle: (enabled: boolean) => void; onUpdate: (updates: Partial) => void; onDelete: () => void; + onBrowseResources?: () => void; } - let { enabled, onDelete, onToggle, onUpdate, server }: Props = $props(); + let { enabled, onBrowseResources, onDelete, onToggle, onUpdate, server }: Props = $props(); let healthState = $derived(mcpStore.getHealthCheckState(server.id)); let displayName = $derived(mcpStore.getServerLabel(server)); @@ -125,6 +126,7 @@ {displayName} enabled={enabled ?? server.enabled} {faviconUrl} + {onBrowseResources} {onToggle} {serverInfo} {transportType} @@ -191,12 +193,14 @@ {/if} - +
+ +
{/if} diff --git a/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte b/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte index 30369d6509f..9baded678e0 100644 --- a/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte +++ b/tools/ui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte @@ -12,6 +12,7 @@ enabled: boolean; disabled?: boolean; onToggle: (enabled: boolean) => void; + onBrowseResources?: () => void; serverInfo?: MCPServerInfo; capabilities?: MCPCapabilitiesInfo; transportType?: MCPTransportType; @@ -23,6 +24,7 @@ displayName, enabled, faviconUrl, + onBrowseResources, onToggle, serverInfo, transportType @@ -57,7 +59,7 @@ {/if} {#if capabilities} - + {/if} {/if} diff --git a/tools/ui/src/lib/components/app/models/ModelId.svelte b/tools/ui/src/lib/components/app/models/ModelId.svelte index cae0a7e3ed1..ea700580a0a 100644 --- a/tools/ui/src/lib/components/app/models/ModelId.svelte +++ b/tools/ui/src/lib/components/app/models/ModelId.svelte @@ -1,16 +1,22 @@
onSelect(option.id)} @@ -81,7 +85,10 @@ aliases={option.aliases} class="flex-1" {hideOrgName} + {modalities} modelId={option.model} + showRawTooltip + {supportsThinking} tags={option.tags} /> diff --git a/tools/ui/src/lib/components/app/models/utils.ts b/tools/ui/src/lib/components/app/models/utils.ts index b78e7085b70..35bfb58a257 100644 --- a/tools/ui/src/lib/components/app/models/utils.ts +++ b/tools/ui/src/lib/components/app/models/utils.ts @@ -1,3 +1,4 @@ +import { ModelModality } from '$lib/enums'; import type { ModelOption } from '$lib/types/models'; import { SvelteMap } from 'svelte/reactivity'; @@ -17,6 +18,23 @@ export interface GroupedModelOptions { available: OrgGroup[]; } +function matchesModality(option: ModelOption, term: string): boolean { + const modalities = option.modalities; + + if (!modalities) return false; + + switch (term) { + case ModelModality.VISION.toLowerCase(): + return modalities.vision; + case ModelModality.AUDIO.toLowerCase(): + return modalities.audio; + case ModelModality.VIDEO.toLowerCase(): + return modalities.video; + default: + return false; + } +} + export function filterModelOptions(options: ModelOption[], searchTerm: string): ModelOption[] { const term = searchTerm.trim().toLowerCase(); @@ -27,7 +45,8 @@ export function filterModelOptions(options: ModelOption[], searchTerm: string): option.model.toLowerCase().includes(term) || option.name?.toLowerCase().includes(term) || option.aliases?.some((alias: string) => alias.toLowerCase().includes(term)) || - option.tags?.some((tag: string) => tag.toLowerCase().includes(term)) + option.tags?.some((tag: string) => tag.toLowerCase().includes(term)) || + matchesModality(option, term) ); } diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index dfe55b5e290..8bf12279e3c 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -5,6 +5,7 @@ import { ActionIcon, DialogConversationRename, + DialogSettingsChat, Logo, SidebarNavigationActions, SidebarNavigationConversationList @@ -91,6 +92,7 @@ let selectedIds = new SvelteSet(); let renameDialogOpen = $state(false); + let settingsDialogOpen = $state(false); let renameTargetConversationId = $state(null); let renameDraft = $state(''); let renameOriginalTitle = $state(''); @@ -308,7 +310,7 @@ -{#if innerWidth > 768 || (!page.url.hash.includes(ROUTES.SETTINGS) && !page.url.hash.includes(ROUTES.MCP_SERVERS) && !page.url.hash.includes(ROUTES.SEARCH))} +{#if innerWidth > 768 || !page.url.hash.includes(ROUTES.SEARCH)}