Skip to main content
Glama
InteractiveControls.ts6.68 kB
/** * Interactive Controls Component * * Generates HTML control panels for filtering, searching, and zooming artifacts. * Used by MCPUIAdapter to add interactive features to signal maps and hop dashboards. * * @module artifacts/controls/InteractiveControls */ /** * Component for generating interactive artifact controls (filters, search, zoom). * * Phase 2: Foundation for interactive MCP-UI artifacts. */ export class InteractiveControls { /** * Generate filter panel HTML for artifact type * * @param artifactType Type of artifact (signal_map or hop_dashboard) * @returns HTML string for filter panel */ generateFilterPanel(artifactType: 'signal_map' | 'hop_dashboard'): string { if (artifactType === 'signal_map') { return this.generateSignalMapFilterPanel(); } else { return this.generateHopDashboardFilterPanel(); } } /** * Generate filter panel specific to signal maps * * @returns HTML string for signal map filter panel */ private generateSignalMapFilterPanel(): string { return ` <div class="filter-panel" id="signal-map-filters"> <h3>Filters</h3> <div class="filter-group"> <label>Node Types:</label> <div class="filter-checkboxes"> <label><input type="checkbox" value="signal" checked> Signals</label> <label><input type="checkbox" value="connection" checked> Connections</label> <label><input type="checkbox" value="orphan" checked> Orphans</label> </div> </div> <div class="filter-group"> <label>Min Connections:</label> <input type="number" id="min-connections" min="0" value="0" style="width: 60px;"> </div> <button onclick="applyFilters()" class="apply-filters-btn">Apply Filters</button> </div> `; } /** * Generate filter panel specific to hop dashboards * * @returns HTML string for hop dashboard filter panel */ private generateHopDashboardFilterPanel(): string { return ` <div class="filter-panel" id="hop-dashboard-filters"> <h3>Filters</h3> <div class="filter-group"> <label>Status:</label> <div class="filter-checkboxes"> <label><input type="checkbox" value="pending" checked> Pending</label> <label><input type="checkbox" value="in_progress" checked> In Progress</label> <label><input type="checkbox" value="completed" checked> Completed</label> </div> </div> <button onclick="applyFilters()" class="apply-filters-btn">Apply Filters</button> </div> `; } /** * Generate search box HTML * * @returns HTML string for search box */ generateSearchBox(): string { return ` <div class="search-box"> <input type="search" id="artifact-search" placeholder="Search nodes..." oninput="handleSearch(this.value)" /> <span id="search-results-count"></span> </div> `; } /** * Generate zoom controls HTML * * @returns HTML string for zoom controls */ generateZoomControls(): string { return ` <div class="zoom-controls"> <button onclick="zoomIn()" title="Zoom In">+</button> <button onclick="zoomReset()" title="Reset Zoom">⊙</button> <button onclick="zoomOut()" title="Zoom Out">−</button> </div> `; } /** * Attach event handlers JavaScript * * Generates JavaScript code to wire up control events. * * @returns JavaScript string for event handlers */ attachEventHandlers(): string { return ` <script> // Filter state let currentFilters = { nodeTypes: ['signal', 'connection', 'orphan'], minConnections: 0 }; // Apply filters to graph function applyFilters() { const panel = document.getElementById('signal-map-filters') || document.getElementById('hop-dashboard-filters'); if (!panel) return; // Get checked node types const checkboxes = panel.querySelectorAll('input[type="checkbox"]:checked'); currentFilters.nodeTypes = Array.from(checkboxes).map(cb => cb.value); // Get min connections (signal map only) const minConnInput = document.getElementById('min-connections'); if (minConnInput) { currentFilters.minConnections = parseInt(minConnInput.value) || 0; } // Filter graph (implementation in visualization module) if (typeof filterGraph === 'function') { filterGraph(currentFilters); } } // Handle search input function handleSearch(query) { const resultsCount = document.getElementById('search-results-count'); if (!query || query.trim() === '') { resultsCount.textContent = ''; clearSearchHighlights(); return; } // Search and highlight (implementation in visualization module) if (typeof searchAndHighlight === 'function') { const matchCount = searchAndHighlight(query); resultsCount.textContent = matchCount > 0 ? \`\${matchCount} match\${matchCount !== 1 ? 'es' : ''}\` : 'No matches'; } } // Zoom controls let currentZoom = 1.0; const ZOOM_FACTOR = 1.2; const MIN_ZOOM = 0.5; const MAX_ZOOM = 5.0; function zoomIn() { currentZoom = Math.min(currentZoom * ZOOM_FACTOR, MAX_ZOOM); applyZoom(); } function zoomOut() { currentZoom = Math.max(currentZoom / ZOOM_FACTOR, MIN_ZOOM); applyZoom(); } function zoomReset() { currentZoom = 1.0; applyZoom(); } function applyZoom() { const container = document.getElementById('artifact-container'); if (container) { container.style.transform = \`scale(\${currentZoom})\`; container.style.transformOrigin = 'center center'; } // Apply to D3 visualization if available if (typeof applyD3Zoom === 'function') { applyD3Zoom(currentZoom); } } // Clear search highlights function clearSearchHighlights() { document.querySelectorAll('mark.search-highlight').forEach(mark => { const text = mark.textContent; mark.replaceWith(document.createTextNode(text)); }); } </script> `; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/EricA1019/CTS_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server