Adds revolutionary features for MCP client identification and browser automation: MCP Client Debug System: - Floating pill toolbar with client identification and session info - Theme system with 5 built-in themes (minimal, corporate, hacker, glass, high-contrast) - Custom theme creation API with CSS variable overrides - Cross-site validation ensuring toolbar persists across navigation - Session-based injection with persistence across page loads Voice Collaboration (Prototype): - Web Speech API integration for conversational browser automation - Bidirectional voice communication between AI and user - Real-time voice guidance during automation tasks - Documented architecture and future development roadmap Code Injection Enhancements: - Model collaboration API for notify, prompt, and inspector functions - Auto-injection and persistence options - Toolbar integration with code injection system Documentation: - Comprehensive technical achievement documentation - Voice collaboration architecture and implementation guide - Theme system integration documentation - Tool annotation templates for consistency This represents a major advancement in browser automation UX, enabling unprecedented visibility and interaction patterns for MCP clients.
562 lines
14 KiB
TypeScript
562 lines
14 KiB
TypeScript
/**
|
|
* 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 `
|
|
<div
|
|
class="mcp-toolbar"
|
|
data-theme="${config.themeId}"
|
|
data-position="${config.position}"
|
|
data-minimized="${state.isMinimized}"
|
|
data-dragging="${state.isDragging}"
|
|
role="toolbar"
|
|
aria-label="MCP Client Identification Toolbar for ${config.projectName}"
|
|
tabindex="0"
|
|
style="opacity: ${config.opacity}"
|
|
>
|
|
<div class="mcp-toolbar__container">
|
|
<header class="mcp-toolbar__header">
|
|
<div class="mcp-toolbar__status">
|
|
<div
|
|
class="mcp-toolbar__status-indicator"
|
|
aria-label="Active MCP session"
|
|
title="MCP session is active"
|
|
></div>
|
|
<div class="mcp-toolbar__project-info">
|
|
<h1 class="mcp-toolbar__project-name">${escapeHTML(config.projectName)}</h1>
|
|
${!state.isMinimized ? `
|
|
<span class="mcp-toolbar__session-badge" title="Session ID: ${config.sessionId}">
|
|
${shortSessionId}
|
|
</span>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mcp-toolbar__controls">
|
|
<button
|
|
class="mcp-toolbar__toggle-btn"
|
|
aria-expanded="${!state.isMinimized}"
|
|
aria-controls="mcp-toolbar-details"
|
|
title="${state.isMinimized ? 'Expand details' : 'Minimize toolbar'}"
|
|
data-action="toggle"
|
|
>
|
|
<span class="mcp-toolbar__toggle-icon" aria-hidden="true">
|
|
${state.isMinimized ? '⊞' : '⊟'}
|
|
</span>
|
|
<span class="sr-only">
|
|
${state.isMinimized ? 'Expand toolbar details' : 'Minimize toolbar'}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
${!state.isMinimized && config.showDetails ? `
|
|
<section
|
|
class="mcp-toolbar__details"
|
|
id="mcp-toolbar-details"
|
|
aria-labelledby="mcp-toolbar-details-heading"
|
|
>
|
|
<h2 id="mcp-toolbar-details-heading" class="sr-only">Session Details</h2>
|
|
|
|
<dl class="mcp-toolbar__details-list">
|
|
<div class="mcp-toolbar__detail-item">
|
|
<dt class="mcp-toolbar__detail-label">Session</dt>
|
|
<dd class="mcp-toolbar__detail-value mcp-toolbar__detail-value--mono">
|
|
${shortSessionId}
|
|
</dd>
|
|
</div>
|
|
|
|
<div class="mcp-toolbar__detail-item">
|
|
<dt class="mcp-toolbar__detail-label">Client</dt>
|
|
<dd class="mcp-toolbar__detail-value mcp-toolbar__detail-value--mono">
|
|
${escapeHTML(config.clientInfo)}
|
|
</dd>
|
|
</div>
|
|
|
|
<div class="mcp-toolbar__detail-item">
|
|
<dt class="mcp-toolbar__detail-label">Uptime</dt>
|
|
<dd class="mcp-toolbar__detail-value mcp-toolbar__detail-value--mono">
|
|
${state.uptime}
|
|
</dd>
|
|
</div>
|
|
|
|
<div class="mcp-toolbar__detail-item">
|
|
<dt class="mcp-toolbar__detail-label">Host</dt>
|
|
<dd class="mcp-toolbar__detail-value mcp-toolbar__detail-value--mono">
|
|
${escapeHTML(state.hostname)}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* 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 `
|
|
<!-- MCP Toolbar Theme Styles -->
|
|
<style id="mcp-toolbar-theme-styles">
|
|
${themeCSS}
|
|
</style>
|
|
|
|
<!-- MCP Toolbar Base Styles -->
|
|
<style id="mcp-toolbar-base-styles">
|
|
${baseCSS}
|
|
</style>
|
|
|
|
<!-- MCP Toolbar Component -->
|
|
${toolbarHTML}
|
|
`;
|
|
} |