Skip to main content
Glama
builtin_scripts.go35.8 kB
package repl import ( "fmt" "os" "path/filepath" "sync" ) // BuiltinScript represents a script that comes built-in with Brummer type BuiltinScript struct { Name string Metadata ScriptMetadata Code string } var ( // builtinScriptsOnce ensures builtin scripts are loaded only once builtinScriptsOnce sync.Once // builtinScripts stores the loaded builtin scripts builtinScripts []BuiltinScript ) // GetBuiltinScripts returns all built-in debugging scripts with lazy loading func GetBuiltinScripts() []BuiltinScript { builtinScriptsOnce.Do(func() { builtinScripts = loadBuiltinScripts() }) return builtinScripts } // loadBuiltinScripts loads the actual builtin scripts func loadBuiltinScripts() []BuiltinScript { return []BuiltinScript{ { Name: "getDetails", Metadata: ScriptMetadata{ Description: "Get comprehensive details about a DOM element including CSS rules, styles, event listeners, parents, and layout contexts", Category: "debugging", Tags: []string{"dom", "css", "layout", "debugging"}, Author: "Brummer", Version: "1.0.0", Examples: []string{ "getDetails('button')", "getDetails('#login-form')", "getDetails('.navbar')", }, Parameters: map[string]string{ "selector": "CSS selector string to identify the element", }, ReturnType: "Object with element details, styles, layout info, and parent chain", }, Code: `function getDetails(selector) { const element = document.querySelector(selector); if (!element) { return { error: 'Element not found: ' + selector }; } const computedStyle = window.getComputedStyle(element); const rect = element.getBoundingClientRect(); // Get event listeners (if possible) const getEventListeners = (el) => { try { // Try to use Chrome DevTools API if available if (window.getEventListeners) { return window.getEventListeners(el); } // Fallback: look for jQuery events if (window.jQuery && window.jQuery._data) { const data = window.jQuery._data(el); return data && data.events ? data.events : {}; } return 'Event listeners not accessible (try Chrome DevTools)'; } catch (e) { return 'Event listeners not accessible: ' + e.message; } }; // Check if element starts a new stacking context const startsStackingContext = () => { const style = computedStyle; return style.position !== 'static' && (style.zIndex !== 'auto' || style.opacity !== '1' || style.transform !== 'none' || style.filter !== 'none' || style.mixBlendMode !== 'normal' || style.isolation === 'isolate'); }; // Check if element is scrollable const isScrollable = () => { const style = computedStyle; return style.overflow === 'scroll' || style.overflow === 'auto' || style.overflowX === 'scroll' || style.overflowX === 'auto' || style.overflowY === 'scroll' || style.overflowY === 'auto'; }; // Get parent chain with layout info const getParentChain = () => { const parents = []; let current = element.parentElement; while (current && current !== document.body) { const parentStyle = window.getComputedStyle(current); const parentRect = current.getBoundingClientRect(); parents.push({ tagName: current.tagName.toLowerCase(), id: current.id || null, classes: Array.from(current.classList), position: parentStyle.position, display: parentStyle.display, zIndex: parentStyle.zIndex, overflow: parentStyle.overflow, bounds: { x: parentRect.x, y: parentRect.y, width: parentRect.width, height: parentRect.height }, startsStackingContext: parentStyle.position !== 'static' && parentStyle.zIndex !== 'auto', isScrollable: parentStyle.overflow === 'scroll' || parentStyle.overflow === 'auto' }); current = current.parentElement; } return parents; }; return { element: { tagName: element.tagName.toLowerCase(), id: element.id || null, classes: Array.from(element.classList), attributes: Array.from(element.attributes).reduce((acc, attr) => { acc[attr.name] = attr.value; return acc; }, {}) }, bounds: { x: rect.x, y: rect.y, width: rect.width, height: rect.height, top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left }, computedStyles: { position: computedStyle.position, display: computedStyle.display, visibility: computedStyle.visibility, opacity: computedStyle.opacity, zIndex: computedStyle.zIndex, transform: computedStyle.transform, overflow: computedStyle.overflow, boxSizing: computedStyle.boxSizing, margin: { top: computedStyle.marginTop, right: computedStyle.marginRight, bottom: computedStyle.marginBottom, left: computedStyle.marginLeft }, padding: { top: computedStyle.paddingTop, right: computedStyle.paddingRight, bottom: computedStyle.paddingBottom, left: computedStyle.paddingLeft }, border: { top: computedStyle.borderTopWidth, right: computedStyle.borderRightWidth, bottom: computedStyle.borderBottomWidth, left: computedStyle.borderLeftWidth } }, layoutContext: { startsStackingContext: startsStackingContext(), isScrollable: isScrollable(), containingBlock: computedStyle.position === 'absolute' || computedStyle.position === 'fixed' ? 'viewport' : 'parent' }, eventListeners: getEventListeners(element), parentChain: getParentChain(), viewport: { scrollX: window.scrollX, scrollY: window.scrollY, innerWidth: window.innerWidth, innerHeight: window.innerHeight } }; }`, }, { Name: "componentTree", Metadata: ScriptMetadata{ Description: "Display the React/Vue component hierarchy and structure for debugging component relationships", Category: "debugging", Tags: []string{"react", "vue", "components", "hierarchy"}, Author: "Brummer", Version: "1.0.0", Examples: []string{ "componentTree()", "componentTree(document.querySelector('#app'))", }, Parameters: map[string]string{ "rootElement": "Optional root element to start from (defaults to document.body)", }, ReturnType: "Object with component tree structure and debugging info", }, Code: `function componentTree(rootElement = document.body) { const getComponentInfo = (element) => { const info = { tagName: element.tagName?.toLowerCase(), id: element.id || null, classes: element.classList ? Array.from(element.classList) : [], componentType: null, props: null, state: null, hooks: null, children: [] }; // React component detection const reactFiber = element._reactInternalFiber || element._reactInternalInstance || Object.keys(element).find(key => key.startsWith('__reactInternalInstance')); if (reactFiber) { const fiber = typeof reactFiber === 'string' ? element[reactFiber] : reactFiber; if (fiber) { info.componentType = 'React'; info.reactFiber = { type: fiber.type?.name || fiber.elementType?.name || 'Unknown', key: fiber.key, props: fiber.memoizedProps || fiber.pendingProps, state: fiber.memoizedState, hooks: fiber.hooks || 'Not accessible' }; } } // Vue component detection if (element.__vue__ || element._vnode) { info.componentType = 'Vue'; const vueInstance = element.__vue__ || element._vnode?.context; if (vueInstance) { info.vueComponent = { name: vueInstance.$options?.name || vueInstance.constructor.name, props: vueInstance.$props, data: vueInstance.$data, computed: Object.keys(vueInstance.$options?.computed || {}), methods: Object.keys(vueInstance.$options?.methods || {}) }; } } // Vue 3 detection if (element.__vueParentComponent) { info.componentType = 'Vue 3'; const component = element.__vueParentComponent; info.vue3Component = { type: component.type?.name || 'Anonymous', props: component.props, setupState: component.setupState, ctx: component.ctx }; } // Angular detection (basic) if (element.ng || element.__ngContext__) { info.componentType = 'Angular'; info.angularContext = 'Detected (use Angular DevTools for details)'; } // Svelte detection if (element.__svelte_meta) { info.componentType = 'Svelte'; info.svelteComponent = element.__svelte_meta; } return info; }; const buildTree = (element, depth = 0) => { if (depth > 20) return { error: 'Max depth reached' }; // Prevent infinite recursion const componentInfo = getComponentInfo(element); // Process children const children = Array.from(element.children || []); componentInfo.children = children.map(child => buildTree(child, depth + 1)); return componentInfo; }; const tree = buildTree(rootElement); // Add summary statistics const getStats = (node) => { let total = 1; let byType = {}; if (node.componentType) { byType[node.componentType] = (byType[node.componentType] || 0) + 1; } if (node.children) { node.children.forEach(child => { const childStats = getStats(child); total += childStats.total; Object.keys(childStats.byType).forEach(type => { byType[type] = (byType[type] || 0) + childStats.byType[type]; }); }); } return { total, byType }; }; const stats = getStats(tree); return { tree, stats, summary: { totalElements: stats.total, componentFrameworks: Object.keys(stats.byType), componentCounts: stats.byType }, helpers: { findByType: (type) => { const results = []; const search = (node) => { if (node.componentType === type) results.push(node); if (node.children) node.children.forEach(search); }; search(tree); return results; }, findByName: (name) => { const results = []; const search = (node) => { const componentName = node.reactFiber?.type || node.vueComponent?.name || node.vue3Component?.type; if (componentName && componentName.toLowerCase().includes(name.toLowerCase())) { results.push(node); } if (node.children) node.children.forEach(search); }; search(tree); return results; } } }; }`, }, { Name: "traceEvents", Metadata: ScriptMetadata{ Description: "Trace and log all events of a specific type with detailed information for debugging event handling", Category: "debugging", Tags: []string{"events", "debugging", "monitoring"}, Author: "Brummer", Version: "1.0.0", Examples: []string{ "traceEvents('click')", "traceEvents('scroll', document.querySelector('#content'))", "const stop = traceEvents('resize'); /* later: */ stop()", }, Parameters: map[string]string{ "eventType": "Type of event to trace (click, scroll, resize, etc.)", "target": "Optional target element (defaults to document)", "options": "Optional event listener options", }, ReturnType: "Function to stop tracing", }, Code: `function traceEvents(eventType, target = document, options = { capture: true }) { console.log('🔍 Starting event trace for:', eventType); const events = []; const startTime = performance.now(); const handler = (event) => { const timestamp = performance.now(); const eventInfo = { type: event.type, timestamp: timestamp, relativeTime: timestamp - startTime, target: { tagName: event.target?.tagName?.toLowerCase(), id: event.target?.id || null, classes: event.target?.classList ? Array.from(event.target.classList) : [], textContent: event.target?.textContent?.substring(0, 50) + '...' || null }, currentTarget: { tagName: event.currentTarget?.tagName?.toLowerCase(), id: event.currentTarget?.id || null, classes: event.currentTarget?.classList ? Array.from(event.currentTarget.classList) : [] }, eventPhase: ['', 'CAPTURING_PHASE', 'AT_TARGET', 'BUBBLING_PHASE'][event.eventPhase] || event.eventPhase, bubbles: event.bubbles, cancelable: event.cancelable, defaultPrevented: event.defaultPrevented, isTrusted: event.isTrusted }; // Add event-specific details if (event.type === 'click' || event.type === 'mousedown' || event.type === 'mouseup') { eventInfo.mouse = { button: event.button, buttons: event.buttons, clientX: event.clientX, clientY: event.clientY, screenX: event.screenX, screenY: event.screenY, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey, metaKey: event.metaKey }; } if (event.type === 'keydown' || event.type === 'keyup' || event.type === 'keypress') { eventInfo.keyboard = { key: event.key, code: event.code, keyCode: event.keyCode, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey, metaKey: event.metaKey }; } if (event.type === 'scroll') { eventInfo.scroll = { scrollTop: event.target.scrollTop, scrollLeft: event.target.scrollLeft, scrollHeight: event.target.scrollHeight, scrollWidth: event.target.scrollWidth, clientHeight: event.target.clientHeight, clientWidth: event.target.clientWidth }; } if (event.type === 'resize') { eventInfo.resize = { innerWidth: window.innerWidth, innerHeight: window.innerHeight, outerWidth: window.outerWidth, outerHeight: window.outerHeight }; } if (event.type.startsWith('touch')) { eventInfo.touch = { touches: event.touches?.length || 0, targetTouches: event.targetTouches?.length || 0, changedTouches: event.changedTouches?.length || 0 }; } events.push(eventInfo); // Log with styling console.group('📍 Event:', event.type, '@', Math.round(eventInfo.relativeTime) + 'ms'); console.log('Target:', eventInfo.target); console.log('Phase:', eventInfo.eventPhase); console.log('Details:', eventInfo); console.groupEnd(); // Keep only last 100 events to prevent memory issues if (events.length > 100) { events.shift(); } }; // Add event listener target.addEventListener(eventType, handler, options); console.log('✅ Event tracing active for', eventType, 'on', target); // Return function to stop tracing const stopTracing = () => { target.removeEventListener(eventType, handler, options); console.log('🛑 Stopped tracing', eventType, '- captured', events.length, 'events'); // Return summary return { eventType, totalEvents: events.length, duration: performance.now() - startTime, events: events, summary: { targets: [...new Set(events.map(e => e.target.tagName))], phases: [...new Set(events.map(e => e.eventPhase))], avgTimeBetween: events.length > 1 ? (events[events.length - 1].relativeTime - events[0].relativeTime) / (events.length - 1) : 0 } }; }; // Add helper methods to the stop function stopTracing.getEvents = () => events; stopTracing.getStats = () => ({ count: events.length, duration: performance.now() - startTime, avgFrequency: events.length / ((performance.now() - startTime) / 1000) }); return stopTracing; }`, }, { Name: "getBoundingBoxTree", Metadata: ScriptMetadata{ Description: "Get the bounding box tree for layout debugging, showing element boundaries and layout relationships", Category: "layout", Tags: []string{"layout", "bounding-box", "debugging", "css"}, Author: "Brummer", Version: "1.0.0", Examples: []string{ "getBoundingBoxTree()", "getBoundingBoxTree(document.querySelector('#content'))", "getBoundingBoxTree(null, { highlightOverflows: true })", }, Parameters: map[string]string{ "rootElement": "Optional root element (defaults to document.body)", "options": "Options object { maxDepth: 10, highlightOverflows: false, minSize: 1 }", }, ReturnType: "Object with bounding box tree and layout analysis", }, Code: `function getBoundingBoxTree(rootElement = document.body, options = {}) { const opts = { maxDepth: 10, highlightOverflows: false, minSize: 1, ...options }; const getLayoutInfo = (element) => { const rect = element.getBoundingClientRect(); const style = window.getComputedStyle(element); // Check for potential layout issues const issues = []; // Element too small to be useful if (rect.width < opts.minSize || rect.height < opts.minSize) { issues.push('Element too small'); } // Element extends beyond viewport if (rect.right > window.innerWidth || rect.bottom > window.innerHeight) { issues.push('Extends beyond viewport'); } // Element has negative coordinates if (rect.x < 0 || rect.y < 0) { issues.push('Negative positioning'); } // Overflow issues const hasScrollableOverflow = style.overflow === 'scroll' || style.overflow === 'auto'; const hasHiddenOverflow = style.overflow === 'hidden'; if (element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight) { if (!hasScrollableOverflow && !hasHiddenOverflow) { issues.push('Content overflow without handling'); } } // Z-index without positioning if (style.zIndex !== 'auto' && style.position === 'static') { issues.push('Z-index on static element'); } return { tagName: element.tagName?.toLowerCase(), id: element.id || null, classes: element.classList ? Array.from(element.classList) : [], bounds: { x: Math.round(rect.x * 100) / 100, y: Math.round(rect.y * 100) / 100, width: Math.round(rect.width * 100) / 100, height: Math.round(rect.height * 100) / 100, top: Math.round(rect.top * 100) / 100, right: Math.round(rect.right * 100) / 100, bottom: Math.round(rect.bottom * 100) / 100, left: Math.round(rect.left * 100) / 100 }, style: { position: style.position, display: style.display, visibility: style.visibility, opacity: style.opacity, zIndex: style.zIndex, overflow: style.overflow, transform: style.transform !== 'none' ? style.transform : null }, scrollInfo: { scrollWidth: element.scrollWidth, scrollHeight: element.scrollHeight, clientWidth: element.clientWidth, clientHeight: element.clientHeight, scrollTop: element.scrollTop, scrollLeft: element.scrollLeft }, issues, children: [] }; }; const buildTree = (element, depth = 0) => { if (depth > opts.maxDepth) { return { error: 'Max depth reached' }; } const layoutInfo = getLayoutInfo(element); // Process visible children only const children = Array.from(element.children || []).filter(child => { const childStyle = window.getComputedStyle(child); return childStyle.display !== 'none' && childStyle.visibility !== 'hidden'; }); layoutInfo.children = children.map(child => buildTree(child, depth + 1)); return layoutInfo; }; const tree = buildTree(rootElement); // Analyze the tree for common layout issues const analyzeTree = (node) => { const analysis = { totalElements: 0, issueCount: 0, issueTypes: {}, largestElement: null, smallestElement: null, deepestNesting: 0 }; const traverse = (n, depth = 0) => { analysis.totalElements++; analysis.deepestNesting = Math.max(analysis.deepestNesting, depth); if (n.issues?.length > 0) { analysis.issueCount++; n.issues.forEach(issue => { analysis.issueTypes[issue] = (analysis.issueTypes[issue] || 0) + 1; }); } const area = n.bounds.width * n.bounds.height; if (!analysis.largestElement || area > analysis.largestElement.area) { analysis.largestElement = { ...n, area }; } if (!analysis.smallestElement || area < analysis.smallestElement.area) { analysis.smallestElement = { ...n, area }; } if (n.children) { n.children.forEach(child => traverse(child, depth + 1)); } }; traverse(node); return analysis; }; const analysis = analyzeTree(tree); // Optional: highlight elements with overflow issues if (opts.highlightOverflows) { const highlightOverflows = (node) => { if (node.issues?.includes('Content overflow without handling')) { const element = document.querySelector( node.id ? '#' + node.id : node.classes.length ? '.' + node.classes[0] : node.tagName ); if (element) { element.style.outline = '2px solid red'; setTimeout(() => element.style.outline = '', 3000); } } if (node.children) { node.children.forEach(highlightOverflows); } }; highlightOverflows(tree); } return { tree, analysis, viewport: { width: window.innerWidth, height: window.innerHeight, scrollX: window.scrollX, scrollY: window.scrollY }, helpers: { findByIssue: (issueType) => { const results = []; const search = (node) => { if (node.issues?.includes(issueType)) { results.push(node); } if (node.children) node.children.forEach(search); }; search(tree); return results; }, findOverflowing: () => { const results = []; const search = (node) => { if (node.scrollInfo.scrollWidth > node.scrollInfo.clientWidth || node.scrollInfo.scrollHeight > node.scrollInfo.clientHeight) { results.push(node); } if (node.children) node.children.forEach(search); }; search(tree); return results; }, findOffscreen: () => { const results = []; const search = (node) => { if (node.bounds.right < 0 || node.bounds.left > window.innerWidth || node.bounds.bottom < 0 || node.bounds.top > window.innerHeight) { results.push(node); } if (node.children) node.children.forEach(search); }; search(tree); return results; } } }; }`, }, { Name: "findLayoutIssues", Metadata: ScriptMetadata{ Description: "Comprehensive layout issue detection that requires less context than screenshots for debugging CSS problems", Category: "layout", Tags: []string{"layout", "css", "debugging", "issues"}, Author: "Brummer", Version: "1.0.0", Examples: []string{ "findLayoutIssues()", "findLayoutIssues({ checkAccessibility: true })", "findLayoutIssues({ rootElement: document.querySelector('#main') })", }, Parameters: map[string]string{ "options": "Options: { rootElement, checkAccessibility: false, checkPerformance: false }", }, ReturnType: "Object with categorized layout issues and recommendations", }, Code: `function findLayoutIssues(options = {}) { const opts = { rootElement: document.body, checkAccessibility: false, checkPerformance: false, ...options }; const issues = { critical: [], warning: [], info: [], performance: [], accessibility: [] }; const checkElement = (element) => { const rect = element.getBoundingClientRect(); const style = window.getComputedStyle(element); const tagName = element.tagName?.toLowerCase(); // Get element identifier for reporting const getElementId = (el) => { if (el.id) return '#' + el.id; if (el.className) return '.' + Array.from(el.classList)[0]; return el.tagName?.toLowerCase() || 'unknown'; }; const elementId = getElementId(element); // Critical Issues // 1. Elements with zero dimensions that should have content if ((rect.width === 0 || rect.height === 0) && element.textContent?.trim() && style.display !== 'none') { issues.critical.push({ type: 'Zero dimensions with content', element: elementId, description: 'Element has content but zero width or height', suggestion: 'Check CSS display, position, or sizing properties', bounds: rect }); } // 2. Overlapping elements (z-index issues) const siblings = Array.from(element.parentElement?.children || []); siblings.forEach(sibling => { if (sibling !== element && sibling.tagName) { const siblingRect = sibling.getBoundingClientRect(); const siblingStyle = window.getComputedStyle(sibling); // Check for overlap const overlap = !(rect.right <= siblingRect.left || rect.left >= siblingRect.right || rect.bottom <= siblingRect.top || rect.top >= siblingRect.bottom); if (overlap && style.position !== 'static' && siblingStyle.position !== 'static') { const zIndex1 = parseInt(style.zIndex) || 0; const zIndex2 = parseInt(siblingStyle.zIndex) || 0; if (zIndex1 === zIndex2) { issues.warning.push({ type: 'Overlapping positioned elements', element: elementId, sibling: getElementId(sibling), description: 'Positioned elements overlap with same z-index', suggestion: 'Set different z-index values or adjust positioning' }); } } } }); // 3. Text too small to read const fontSize = parseFloat(style.fontSize); if (fontSize < 12 && element.textContent?.trim()) { issues.warning.push({ type: 'Text too small', element: elementId, fontSize: fontSize + 'px', description: 'Font size below 12px may be hard to read', suggestion: 'Increase font-size for better readability' }); } // 4. Content overflow without proper handling const hasOverflow = element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight; if (hasOverflow && style.overflow === 'visible') { issues.warning.push({ type: 'Unhandled content overflow', element: elementId, description: 'Content overflows container without scroll or hidden', suggestion: 'Set overflow: auto, scroll, or hidden', scrollDimensions: { scrollWidth: element.scrollWidth, scrollHeight: element.scrollHeight, clientWidth: element.clientWidth, clientHeight: element.clientHeight } }); } // 5. Flexbox/Grid issues if (style.display === 'flex' || style.display === 'inline-flex') { const children = Array.from(element.children); // Check for flex items with conflicting sizing children.forEach(child => { const childStyle = window.getComputedStyle(child); if (childStyle.flexShrink === '0' && childStyle.minWidth === 'auto') { issues.info.push({ type: 'Flex item sizing issue', element: getElementId(child), parent: elementId, description: 'Flex item with flex-shrink: 0 but no explicit min-width', suggestion: 'Set min-width or allow flex-shrink' }); } }); } // 6. CSS Grid issues if (style.display === 'grid' || style.display === 'inline-grid') { // Check for implicit grid items const gridTemplateColumns = style.gridTemplateColumns; const gridTemplateRows = style.gridTemplateRows; const children = Array.from(element.children); if (gridTemplateColumns === 'none' && children.length > 0) { issues.info.push({ type: 'Implicit grid usage', element: elementId, description: 'Grid container without explicit grid-template-columns', suggestion: 'Define grid-template-columns for predictable layout' }); } } // Performance Issues if (opts.checkPerformance) { // 7. Expensive CSS properties const expensiveProps = { 'box-shadow': style.boxShadow !== 'none', 'border-radius': style.borderRadius !== '0px', 'transform': style.transform !== 'none', 'filter': style.filter !== 'none', 'backdrop-filter': style.backdropFilter !== 'none' }; const activeExpensive = Object.entries(expensiveProps) .filter(([prop, active]) => active) .map(([prop]) => prop); if (activeExpensive.length > 3) { issues.performance.push({ type: 'Multiple expensive CSS properties', element: elementId, properties: activeExpensive, description: 'Element uses multiple GPU-intensive CSS properties', suggestion: 'Consider reducing visual effects or using will-change' }); } // 8. Large images without optimization if (tagName === 'img') { const naturalWidth = element.naturalWidth; const naturalHeight = element.naturalHeight; const displayWidth = rect.width; const displayHeight = rect.height; if (naturalWidth > displayWidth * 2 || naturalHeight > displayHeight * 2) { issues.performance.push({ type: 'Oversized image', element: elementId, description: 'Image is much larger than display size', natural: { width: naturalWidth, height: naturalHeight }, display: { width: displayWidth, height: displayHeight }, suggestion: 'Use appropriately sized images or responsive images' }); } } } // Accessibility Issues if (opts.checkAccessibility) { // 9. Missing alt text on images if (tagName === 'img' && !element.getAttribute('alt')) { issues.accessibility.push({ type: 'Missing alt text', element: elementId, description: 'Image missing alt attribute', suggestion: 'Add descriptive alt text for screen readers' }); } // 10. Low color contrast (basic check) const color = style.color; const backgroundColor = style.backgroundColor; if (color !== backgroundColor && element.textContent?.trim()) { // This is a simplified check - real contrast calculation is more complex issues.accessibility.push({ type: 'Potential contrast issue', element: elementId, description: 'Text color and background may have low contrast', colors: { color, backgroundColor }, suggestion: 'Verify color contrast meets WCAG guidelines' }); } // 11. Interactive elements without proper focus indication const isInteractive = ['button', 'a', 'input', 'select', 'textarea'].includes(tagName) || element.getAttribute('tabindex') !== null || element.getAttribute('role') === 'button'; if (isInteractive && style.outline === 'none' && !style.boxShadow?.includes('focus')) { issues.accessibility.push({ type: 'Missing focus indicator', element: elementId, description: 'Interactive element may not show focus state', suggestion: 'Add visible focus styling for keyboard navigation' }); } } }; // Traverse DOM tree const traverse = (element) => { checkElement(element); Array.from(element.children || []).forEach(traverse); }; traverse(opts.rootElement); // Generate summary and recommendations const totalIssues = issues.critical.length + issues.warning.length + issues.info.length + issues.performance.length + issues.accessibility.length; const summary = { total: totalIssues, critical: issues.critical.length, warning: issues.warning.length, info: issues.info.length, performance: issues.performance.length, accessibility: issues.accessibility.length }; const recommendations = []; if (issues.critical.length > 0) { recommendations.push('🚨 Address critical layout issues first - these likely break functionality'); } if (issues.warning.length > 5) { recommendations.push('⚠️ Many layout warnings detected - review CSS structure'); } if (issues.performance.length > 0) { recommendations.push('⚡ Performance issues found - consider optimizing CSS and images'); } if (issues.accessibility.length > 0) { recommendations.push('♿ Accessibility improvements needed for better user experience'); } if (totalIssues === 0) { recommendations.push('✅ No obvious layout issues detected'); } return { issues, summary, recommendations, metadata: { checkedAt: new Date().toISOString(), rootElement: getElementId(opts.rootElement), checksPerformed: { basic: true, accessibility: opts.checkAccessibility, performance: opts.checkPerformance } }, helpers: { getByCriticality: (level) => issues[level] || [], getByType: (type) => { const allIssues = [...issues.critical, ...issues.warning, ...issues.info, ...issues.performance, ...issues.accessibility]; return allIssues.filter(issue => issue.type === type); }, exportReport: () => { return JSON.stringify({ issues, summary, recommendations }, null, 2); } } }; }`, }, } } // InstallBuiltinScripts installs all built-in scripts to the scripts directory func InstallBuiltinScripts() error { scriptsDir, err := getScriptsDirectory() if err != nil { return err } builtinScripts := GetBuiltinScripts() for _, script := range builtinScripts { filename := script.Name + ".ts" filePath := filepath.Join(scriptsDir, filename) // Check if script already exists if _, err := os.Stat(filePath); err == nil { fmt.Printf("Builtin script %s already exists, skipping...\n", script.Name) continue } // Save the script if err := saveScript(script.Name, script.Code, script.Metadata); err != nil { fmt.Printf("Warning: failed to install builtin script %s: %v\n", script.Name, err) continue } fmt.Printf("Installed builtin script: %s\n", script.Name) } return nil }

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/standardbeagle/brummer'

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