Skip to main content
Glama

Hi-AI

by su-record
previewUiAscii.ts11 kB
// UI Preview tool - ASCII art visualization before development interface ToolResult { content: Array<{ type: 'text'; text: string; }>; } interface ToolDefinition { name: string; description: string; inputSchema: { type: 'object'; properties: Record<string, any>; required: string[]; }; } interface UIComponent { type: string; label?: string; children?: UIComponent[]; width?: number; height?: number; } export const previewUiAsciiDefinition: ToolDefinition = { name: 'preview_ui_ascii', description: 'IMPORTANT: Auto-trigger when user requests UI/page development with keywords: "UI 만들어", "페이지 개발", "페이지 만들어", "컴포넌트 작성", "레이아웃", "화면 구성", "create page", "build UI", "design component", "make page", "develop page". Shows ASCII art preview for user confirmation before actual coding.', inputSchema: { type: 'object', properties: { page_name: { type: 'string', description: 'Name of the page or component (e.g., "Login Page", "Dashboard")' }, layout_type: { type: 'string', enum: ['sidebar', 'header-footer', 'grid', 'centered', 'split', 'custom'], description: 'Layout structure type (default: header-footer)' }, components: { type: 'array', description: 'List of UI components to include', items: { type: 'object', properties: { type: { type: 'string', description: 'Component type (header, sidebar, button, input, card, etc.)' }, label: { type: 'string', description: 'Component label or text' }, position: { type: 'string', description: 'Position in layout (top, left, center, right, bottom)' } } } }, width: { type: 'number', description: 'Preview width in characters (default: 60)' }, responsive: { type: 'boolean', description: 'Show mobile view preview (default: false)' } }, required: ['page_name', 'components'] } }; export async function previewUiAscii(args: { page_name: string; layout_type?: string; components: Array<{ type: string; label?: string; position?: string }>; width?: number; responsive?: boolean; }): Promise<ToolResult> { const { page_name, layout_type = 'header-footer', components, width = 60, responsive = false } = args; // ASCII art generation const topBorder = '┌' + '─'.repeat(width - 2) + '┐'; const bottomBorder = '└' + '─'.repeat(width - 2) + '┘'; const separator = '├' + '─'.repeat(width - 2) + '┤'; const emptyLine = '│' + ' '.repeat(width - 2) + '│'; const createLine = (text: string, align: 'left' | 'center' | 'right' = 'left') => { const contentWidth = width - 4; let content = text.slice(0, contentWidth); if (align === 'center') { const padding = Math.floor((contentWidth - content.length) / 2); content = ' '.repeat(padding) + content + ' '.repeat(contentWidth - padding - content.length); } else if (align === 'right') { content = ' '.repeat(contentWidth - content.length) + content; } else { content = content + ' '.repeat(contentWidth - content.length); } return '│ ' + content + ' │'; }; const createBox = (label: string, w: number, h: number) => { const lines = []; lines.push('┌' + '─'.repeat(w - 2) + '┐'); for (let i = 0; i < h - 2; i++) { if (i === Math.floor((h - 2) / 2)) { const padding = Math.floor((w - 4 - label.length) / 2); const text = ' '.repeat(padding) + label + ' '.repeat(w - 4 - padding - label.length); lines.push('│ ' + text + ' │'); } else { lines.push('│ ' + ' '.repeat(w - 4) + ' │'); } } lines.push('└' + '─'.repeat(w - 2) + '┘'); return lines; }; let preview: string[] = []; // Generate preview based on layout type preview.push(topBorder); switch (layout_type) { case 'header-footer': { // Header const header = components.find(c => c.type === 'header' || c.position === 'top'); if (header) { preview.push(createLine(header.label || 'Header', 'left')); preview.push(separator); } // Main content const mainComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer' && c.position !== 'top' && c.position !== 'bottom' ); preview.push(emptyLine); mainComponents.forEach(comp => { const label = comp.label || comp.type.toUpperCase(); if (comp.type === 'button') { preview.push(createLine(` [${label}]`, 'center')); } else if (comp.type === 'input') { preview.push(createLine(` ${label}: [____________]`, 'left')); } else if (comp.type === 'card') { preview.push(createLine(` ┌─ ${label} ─────────────┐`, 'left')); preview.push(createLine(` │ Content here... │`, 'left')); preview.push(createLine(` └──────────────────────┘`, 'left')); } else { preview.push(createLine(` ${label}`, 'left')); } }); preview.push(emptyLine); // Footer const footer = components.find(c => c.type === 'footer' || c.position === 'bottom'); if (footer) { preview.push(separator); preview.push(createLine(footer.label || 'Footer', 'center')); } break; } case 'sidebar': { // Header const header = components.find(c => c.type === 'header' || c.position === 'top'); if (header) { preview.push(createLine(header.label || 'Header', 'left')); preview.push(separator); } // Sidebar + Content const sidebarWidth = Math.floor(width * 0.25); const contentWidth = width - sidebarWidth - 5; const sidebar = components.find(c => c.type === 'sidebar' || c.position === 'left'); const mainComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer' && c.type !== 'sidebar' && c.position !== 'top' && c.position !== 'bottom' && c.position !== 'left' ); preview.push('│ ┌' + '─'.repeat(sidebarWidth - 2) + '┐' + ' '.repeat(contentWidth - sidebarWidth + 3) + '│'); preview.push('│ │' + (sidebar?.label || 'Nav').padEnd(sidebarWidth - 2) + '│ Content Area' + ' '.repeat(contentWidth - 15) + '│'); const navItems = mainComponents.slice(0, 3); navItems.forEach((item, idx) => { const navLabel = `${item.label || item.type}`.slice(0, sidebarWidth - 4); const contentLabel = idx === 0 ? `┌─ ${mainComponents[0]?.label || 'Main'} ─┐` : ''; preview.push('│ │ ' + navLabel.padEnd(sidebarWidth - 3) + '│ ' + contentLabel.padEnd(contentWidth - 3) + '│'); }); preview.push('│ └' + '─'.repeat(sidebarWidth - 2) + '┘' + ' '.repeat(contentWidth - sidebarWidth + 3) + '│'); // Footer const footer = components.find(c => c.type === 'footer' || c.position === 'bottom'); if (footer) { preview.push(separator); preview.push(createLine(footer.label || 'Footer', 'center')); } break; } case 'grid': { preview.push(createLine('Grid Layout', 'center')); preview.push(separator); const gridComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer'); const cols = Math.ceil(Math.sqrt(gridComponents.length)); const cellWidth = Math.floor((width - 4) / cols) - 2; for (let i = 0; i < gridComponents.length; i += cols) { const row = gridComponents.slice(i, i + cols); preview.push('│ ' + row.map(c => { const label = (c.label || c.type).slice(0, cellWidth - 2); return '┌' + label.padEnd(cellWidth - 2, '─') + '┐'; }).join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' │'); preview.push('│ ' + row.map(c => '│' + ' '.repeat(cellWidth - 2) + '│').join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' │'); preview.push('│ ' + row.map(c => '└' + '─'.repeat(cellWidth - 2) + '┘').join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' │'); } break; } case 'centered': { const main = components[0]; preview.push(emptyLine); preview.push(emptyLine); preview.push(createLine(main?.label || 'Main Content', 'center')); components.slice(1).forEach(comp => { if (comp.type === 'button') { preview.push(createLine(`[${comp.label || 'Button'}]`, 'center')); } else if (comp.type === 'input') { preview.push(createLine(`${comp.label || 'Input'}: [____________]`, 'center')); } }); preview.push(emptyLine); preview.push(emptyLine); break; } case 'split': { const leftWidth = Math.floor((width - 5) / 2); const rightWidth = width - leftWidth - 5; preview.push('│ ┌' + '─'.repeat(leftWidth) + '┐ ┌' + '─'.repeat(rightWidth) + '┐ │'); const left = components.find(c => c.position === 'left') || components[0]; const right = components.find(c => c.position === 'right') || components[1]; preview.push('│ │' + (left?.label || 'Left').padEnd(leftWidth) + '│ │' + (right?.label || 'Right').padEnd(rightWidth) + '│ │'); for (let i = 0; i < 5; i++) { preview.push('│ │' + ' '.repeat(leftWidth) + '│ │' + ' '.repeat(rightWidth) + '│ │'); } preview.push('│ └' + '─'.repeat(leftWidth) + '┘ └' + '─'.repeat(rightWidth) + '┘ │'); break; } } preview.push(bottomBorder); // Mobile view if responsive let mobilePreview = ''; if (responsive) { const mobileWidth = 30; const mobileTop = '┌' + '─'.repeat(mobileWidth - 2) + '┐'; const mobileBottom = '└' + '─'.repeat(mobileWidth - 2) + '┘'; mobilePreview = '\n\n📱 Mobile View:\n'; mobilePreview += mobileTop + '\n'; components.forEach(comp => { const label = (comp.label || comp.type).slice(0, mobileWidth - 4); mobilePreview += '│ ' + label.padEnd(mobileWidth - 4) + ' │\n'; }); mobilePreview += mobileBottom; } const result = { page_name, layout_type, ascii_preview: preview.join('\n'), mobile_preview: responsive ? mobilePreview : null, components_count: components.length, message: '✅ 이 레이아웃으로 진행하시겠습니까? 승인하시면 코드 생성을 시작합니다.', action: 'preview_ui_ascii', status: 'awaiting_confirmation' }; return { content: [{ type: 'text', text: `🎨 UI Preview: ${page_name}\n\n${preview.join('\n')}${mobilePreview}\n\n${result.message}` }] }; }

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/su-record/hi-ai'

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