From 25fe053be139475ea4db14a477a41da63c03692d Mon Sep 17 00:00:00 2001 From: Beyond Better Date: Sat, 25 Oct 2025 11:11:12 +1100 Subject: [PATCH 01/11] Implemented client selector with dynamic client list, refresh, and selection functionality Applied changes from BB to 1 file Files modified: - fresh-ui/components/ClientSelector.tsx layout updates --- fresh-ui/components/ClientSelector.tsx | 285 ++++++++++-------------- fresh-ui/components/CommandPanel.tsx | 6 +- fresh-ui/components/ElicitationForm.tsx | 2 +- 3 files changed, 123 insertions(+), 170 deletions(-) diff --git a/fresh-ui/components/ClientSelector.tsx b/fresh-ui/components/ClientSelector.tsx index 6adf7f0..20515c8 100644 --- a/fresh-ui/components/ClientSelector.tsx +++ b/fresh-ui/components/ClientSelector.tsx @@ -1,12 +1,12 @@ /** * Client Selector Component - * + * * Displays and manages selection of connected MCP clients. * Updates when client_list messages are received via WebSocket. */ import { useEffect, useRef } from "preact/hooks"; -import { sendCommand, wsMessages } from "../hooks/useWebSocket.ts"; +import { wsMessages, sendCommand } from "../hooks/useWebSocket.ts"; import { selectedClientId } from "../hooks/useConsoleState.ts"; import type { ClientInfo, SessionId } from "@shared/types/index.ts"; import { isClientList } from "@shared/types/index.ts"; @@ -15,10 +15,10 @@ export default function ClientSelector() { const detailsRef = useRef(null); const dropdownContentRef = useRef(null); const summaryRef = useRef(null); - + // Extract clients from messages const clientListMessage = wsMessages.value.find(isClientList); - const clients = clientListMessage + const clients = clientListMessage ? (clientListMessage.payload as { clients: ClientInfo[] }).clients : []; @@ -42,7 +42,7 @@ export default function ClientSelector() { // Check if click is on the summary or dropdown content const clickedSummary = summaryRef.current?.contains(target); const clickedContent = dropdownContentRef.current?.contains(target); - + // Close if clicked outside both summary and content if (!clickedSummary && !clickedContent) { detailsRef.current.open = false; @@ -51,9 +51,9 @@ export default function ClientSelector() { }; // Use capture phase to catch events before they're stopped - document.addEventListener("click", handleClickOutside, true); + document.addEventListener('click', handleClickOutside, true); return () => { - document.removeEventListener("click", handleClickOutside, true); + document.removeEventListener('click', handleClickOutside, true); }; }, []); @@ -70,191 +70,144 @@ export default function ClientSelector() { }; // Get selected client for display in trigger button - const selectedClient = clients.find((c) => - c.sessionId === selectedClientId.value - ); + const selectedClient = clients.find(c => c.sessionId === selectedClientId.value); // deno-lint-ignore no-explicit-any const selectedMetadata = selectedClient?.metadata as any; - const selectedClientName = selectedMetadata?.clientInfo?.name || - selectedClient?.sessionId; + const selectedClientName = selectedMetadata?.clientInfo?.name || selectedClient?.sessionId; return (
-

📱 Connected Clients

+ {/* Header with Refresh Button */} +
+

📱 Connected Clients

+ +
- {clients.length === 0 - ? ( -
+ {clients.length === 0 ? ( +
+ + + + No clients connected. Start an MCP client and connect to the server. +
+ ) : ( +
- ) - : ( - - )} - - {/* Refresh Button */} -
- -
- + + {/* Selection Indicator */} + {isSelected && ( +
+ + + +
+ )} +
+ + + ); + })} + +
+ + )} + {/* Selection Info */} {selectedClientId.value && (
diff --git a/fresh-ui/components/CommandPanel.tsx b/fresh-ui/components/CommandPanel.tsx index 97b8dbe..904cd97 100644 --- a/fresh-ui/components/CommandPanel.tsx +++ b/fresh-ui/components/CommandPanel.tsx @@ -52,7 +52,7 @@ export default function CommandPanel() {
{/* Tab Content */} -
+
{activeTab.value === "notifications" && } {activeTab.value === "sampling" && } @@ -62,14 +62,14 @@ export default function CommandPanel() { {/* Response Display (shown when sampling or elicitation tab is active) */} {activeTab.value === "sampling" && ( -
+
Latest Response
)} {activeTab.value === "elicitation" && ( -
+
Latest Response
diff --git a/fresh-ui/components/ElicitationForm.tsx b/fresh-ui/components/ElicitationForm.tsx index ac8879a..1f40d0a 100644 --- a/fresh-ui/components/ElicitationForm.tsx +++ b/fresh-ui/components/ElicitationForm.tsx @@ -153,7 +153,7 @@ export default function ElicitationForm() { }; return ( -
+
{/* Mode Toggle */}