/** * MCP Toolbar Semantic HTML Template System * Professional, accessible HTML structure with no hardcoded styling */ export interface McpToolbarConfig { projectName: string; sessionId: string; clientInfo: string; startTime: number; position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; minimized: boolean; showDetails: boolean; themeId: string; opacity: number; } export interface McpToolbarState { isMinimized: boolean; isDragging: boolean; position: { x: number; y: number }; uptime: string; hostname: string; } /** * Generate semantic HTML structure for MCP toolbar * Uses BEM methodology for CSS classes and proper ARIA attributes */ export function generateToolbarHTML(config: McpToolbarConfig, state: McpToolbarState): string { const shortSessionId = config.sessionId.substring(0, 8); return `
`; } /** * Generate base CSS framework with CSS custom properties * This provides the complete styling foundation that works with any theme */ export function generateToolbarCSS(): string { return ` /* ========================================= MCP Toolbar Base Styles ========================================= */ .mcp-toolbar { /* Layout & Positioning */ position: fixed; z-index: 2147483647; /* Base Dimensions */ min-width: var(--mcp-toolbar-min-width); max-width: var(--mcp-toolbar-max-width); /* Visual Foundation */ background: var(--mcp-surface); color: var(--mcp-text-primary); border: 1px solid var(--mcp-border); border-radius: var(--mcp-border-radius-md); box-shadow: var(--mcp-shadow-lg); /* Backdrop Effects */ backdrop-filter: blur(var(--mcp-backdrop-blur)); -webkit-backdrop-filter: blur(var(--mcp-backdrop-blur)); /* Typography */ font-family: var(--mcp-font-family); font-size: var(--mcp-font-size-sm); line-height: 1.4; /* Interaction */ cursor: grab; user-select: none; /* Transitions */ transition: transform var(--mcp-transition-fast), box-shadow var(--mcp-transition-fast), opacity var(--mcp-transition-fast); } /* Position Variants */ .mcp-toolbar[data-position="top-left"] { top: var(--mcp-spacing-lg); left: var(--mcp-spacing-lg); } .mcp-toolbar[data-position="top-right"] { top: var(--mcp-spacing-lg); right: var(--mcp-spacing-lg); } .mcp-toolbar[data-position="bottom-left"] { bottom: var(--mcp-spacing-lg); left: var(--mcp-spacing-lg); } .mcp-toolbar[data-position="bottom-right"] { bottom: var(--mcp-spacing-lg); right: var(--mcp-spacing-lg); } /* Minimized State */ .mcp-toolbar[data-minimized="true"] { border-radius: var(--mcp-border-radius-pill); min-width: auto; max-width: 280px; } /* Dragging State */ .mcp-toolbar[data-dragging="true"] { cursor: grabbing; transform: translateY(0px) !important; box-shadow: var(--mcp-shadow-xl); } /* Hover Enhancement */ .mcp-toolbar:hover { transform: translateY(-1px); box-shadow: var(--mcp-shadow-xl); opacity: 1 !important; } .mcp-toolbar:active { transform: translateY(0px); } /* Focus State for Accessibility */ .mcp-toolbar:focus-visible { outline: 2px solid var(--mcp-border-focus); outline-offset: 2px; } /* ========================================= Container & Layout ========================================= */ .mcp-toolbar__container { padding: var(--mcp-spacing-md) var(--mcp-spacing-lg); display: flex; flex-direction: column; gap: var(--mcp-spacing-sm); } .mcp-toolbar[data-minimized="true"] .mcp-toolbar__container { padding: var(--mcp-spacing-sm) var(--mcp-spacing-md); gap: 0; } /* ========================================= Header Section ========================================= */ .mcp-toolbar__header { display: flex; align-items: center; justify-content: space-between; gap: var(--mcp-spacing-sm); min-height: 24px; } .mcp-toolbar__status { display: flex; align-items: center; gap: var(--mcp-spacing-sm); flex: 1; min-width: 0; /* Allows text truncation */ } .mcp-toolbar__status-indicator { width: 8px; height: 8px; border-radius: var(--mcp-border-radius-full); background: var(--mcp-success); flex-shrink: 0; /* Pulse Animation */ box-shadow: 0 0 0 2px color-mix(in srgb, var(--mcp-success) 20%, transparent); animation: mcp-pulse 2s infinite; } @keyframes mcp-pulse { 0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--mcp-success) 20%, transparent); } 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--mcp-success) 10%, transparent); } } .mcp-toolbar__project-info { display: flex; align-items: center; gap: var(--mcp-spacing-xs); flex: 1; min-width: 0; } .mcp-toolbar[data-minimized="true"] .mcp-toolbar__project-info { flex-direction: row; } .mcp-toolbar__project-name { font-size: var(--mcp-font-size-sm); font-weight: 600; margin: 0; color: var(--mcp-text-primary); /* Text Truncation */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; } .mcp-toolbar[data-minimized="false"] .mcp-toolbar__project-name { font-size: var(--mcp-font-size-base); } .mcp-toolbar__session-badge { font-family: var(--mcp-font-family-mono); font-size: var(--mcp-font-size-xs); color: var(--mcp-text-secondary); background: var(--mcp-bg-hover); padding: 2px var(--mcp-spacing-xs); border-radius: var(--mcp-border-radius-sm); flex-shrink: 0; } /* ========================================= Controls Section ========================================= */ .mcp-toolbar__controls { display: flex; align-items: center; gap: var(--mcp-spacing-xs); } .mcp-toolbar__toggle-btn { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; min-width: 24px; /* Ensure minimum touch target */ background: transparent; border: none; border-radius: var(--mcp-border-radius-sm); color: var(--mcp-text-secondary); cursor: pointer; font-size: var(--mcp-font-size-xs); transition: all var(--mcp-transition-fast); } .mcp-toolbar__toggle-btn:hover { background: var(--mcp-bg-hover); color: var(--mcp-text-primary); transform: scale(1.05); } .mcp-toolbar__toggle-btn:active { transform: scale(0.95); background: var(--mcp-bg-active); } .mcp-toolbar__toggle-btn:focus-visible { outline: 2px solid var(--mcp-border-focus); outline-offset: 1px; } .mcp-toolbar__toggle-icon { display: block; line-height: 1; } /* ========================================= Details Section ========================================= */ .mcp-toolbar__details { border-top: 1px solid var(--mcp-border-subtle); padding-top: var(--mcp-spacing-sm); margin-top: var(--mcp-spacing-xs); } .mcp-toolbar__details-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--mcp-spacing-xs); } .mcp-toolbar__detail-item { display: flex; justify-content: space-between; align-items: center; gap: var(--mcp-spacing-sm); } .mcp-toolbar__detail-label { font-size: var(--mcp-font-size-xs); color: var(--mcp-text-secondary); font-weight: 400; margin: 0; flex-shrink: 0; } .mcp-toolbar__detail-value { font-size: var(--mcp-font-size-xs); color: var(--mcp-text-primary); font-weight: 500; margin: 0; text-align: right; /* Allow value to wrap if needed */ word-break: break-all; min-width: 0; } .mcp-toolbar__detail-value--mono { font-family: var(--mcp-font-family-mono); } /* ========================================= Screen Reader & Accessibility ========================================= */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* ========================================= Responsive Design ========================================= */ @media (max-width: 768px) { .mcp-toolbar { font-size: var(--mcp-font-size-xs); min-width: 240px; max-width: 300px; } .mcp-toolbar__container { padding: var(--mcp-spacing-sm) var(--mcp-spacing-md); } .mcp-toolbar__project-name { font-size: var(--mcp-font-size-sm); } .mcp-toolbar[data-minimized="false"] .mcp-toolbar__project-name { font-size: var(--mcp-font-size-sm); } .mcp-toolbar__detail-label, .mcp-toolbar__detail-value { font-size: 10px; } } /* ========================================= Reduced Motion Support ========================================= */ @media (prefers-reduced-motion: reduce) { .mcp-toolbar, .mcp-toolbar__toggle-btn, .mcp-toolbar__status-indicator { animation: none !important; transition: none !important; } .mcp-toolbar:hover { transform: none !important; } } /* ========================================= High Contrast Support ========================================= */ @media (prefers-contrast: high) { .mcp-toolbar { border-width: 2px; border-style: solid; } .mcp-toolbar__toggle-btn:focus-visible { outline-width: 3px; } .mcp-toolbar__status-indicator { border: 2px solid var(--mcp-text-primary); } } /* ========================================= Dark Mode Support (system level) ========================================= */ @media (prefers-color-scheme: dark) { .mcp-toolbar[data-theme="auto"] { /* Themes handle this through CSS variables */ /* This is just a placeholder for system-level overrides */ } } `; } /** * Utility function to escape HTML content */ function escapeHTML(text: string): string { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Generate the complete toolbar component with theme integration */ export function generateCompleteToolbar(config: McpToolbarConfig, themeCSS: string): string { const formatUptime = (startTime: number): string => { const uptime = Math.floor((Date.now() - startTime) / 1000); const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); const seconds = uptime % 60; if (hours > 0) return `${hours}h ${minutes}m`; if (minutes > 0) return `${minutes}m ${seconds}s`; return `${seconds}s`; }; const state: McpToolbarState = { isMinimized: config.minimized, isDragging: false, position: { x: 0, y: 0 }, uptime: formatUptime(config.startTime), hostname: typeof window !== 'undefined' ? (window.location.hostname || 'local') : 'local' }; const toolbarHTML = generateToolbarHTML(config, state); const baseCSS = generateToolbarCSS(); return ` ${toolbarHTML} `; }