Skip to main content
Glama

create_ui_component

Generate Webasyst 2.0 UI components like tables, forms, modals, and cards for application development, including optional JavaScript initialization.

Instructions

Сгенерировать UI-компонент Webasyst 2.0

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
component_typeYesТип компонента: table, form, modal, drawer, chips, bricks, upload, bottombar, userpic_list, slider, toggle, switch, skeleton, tabs, progressbar, tooltip, autocomplete, menu, alert, paging, breadcrumbs, spinner, dropdown, card
component_nameYesУникальное имя компонента (латиница, без пробелов)
target_pathYesПуть к приложению/плагину
with_jsNoСоздать JS-инициализацию (для modal, drawer, upload, slider, toggle, tabs, progressbar, tooltip, autocomplete, switch, dropdown)

Implementation Reference

  • The handler function that creates Webasyst UI 2.0 components. It generates HTML templates for various component types (table, form, modal, drawer, chips, etc.) and optional JavaScript initialization files in the templates/components and js/components directories of the target_path.
    async function createUIComponentTool({ component_type, component_name, target_path, with_js = true }) { const cmpDir = path.join(target_path, 'templates', 'components'); await ensureDir(cmpDir); let tpl = '{* Webasyst UI component *}\n'; let js = ''; switch (component_type) { case 'table': tpl += `<table class="zebra">\n <thead>\n <tr><th>{_w('ID')}</th><th>{_w('Name')}</th></tr>\n </thead>\n <tbody>\n {foreach $items as $i}\n <tr><td>{$i.id}</td><td>{$i.name|escape}</td></tr>\n {/foreach}\n </tbody>\n</table>\n`; break; case 'form': tpl += `<form method="post" class="wa-form">\n <div class="field">\n <div class="name">{_w('Name')}</div>\n <div class="value">\n <input type="text" name="name" value="{$name|escape}" class="bold">\n </div>\n </div>\n <div class="field">\n <div class="name">{_w('Description')}</div>\n <div class="value">\n <textarea name="description" class="full-width">{$description|escape}</textarea>\n </div>\n </div>\n <div class="field">\n <div class="value submit">\n <button class="button blue" type="submit">{_w('Save')}</button>\n <button class="button light-gray" type="button" data-role="cancel">{_w('Cancel')}</button>\n </div>\n </div>\n</form>\n`; break; case 'modal': tpl += `<div class="dialog" id="${component_name}-dialog">\n <div class="dialog-background"></div>\n <div class="dialog-body">\n <a href="#" class="dialog-close js-close-dialog" aria-label="{_w('Close')}">&times;</a>\n <header class="dialog-header">\n <h1>{_w('Modal title')}</h1>\n </header>\n <div class="dialog-content">\n {_w('Content goes here')}\n </div>\n <footer class="dialog-footer">\n <button class="button blue">{_w('Save')}</button>\n <button class="button light-gray js-close-dialog">{_w('Cancel')}</button>\n </footer>\n </div>\n</div>\n`; if (with_js) { js = `(function($) { $(function() { var dialog_html = $('#${component_name}-dialog').prop('outerHTML'); $('#${component_name}-dialog').remove(); window.${component_name.replace(/-/g, '_')}Dialog = { open: function() { return $.wa_ui.dialog.create({ html: dialog_html, onOpen: function($dialog, dialog) { // Инициализация при открытии } }); } }; }); })(jQuery); `; } break; case 'drawer': tpl += `<div class="drawer" id="${component_name}-drawer">\n <div class="drawer-background"></div>\n <div class="drawer-body">\n <a href="#" class="drawer-close js-close-drawer" aria-label="{_w('Close')}">&times;</a>\n <div class="drawer-block">\n <header class="drawer-header">\n <h1>{_w('Drawer title')}</h1>\n </header>\n <div class="drawer-content">\n {_w('Content goes here')}\n </div>\n <footer class="drawer-footer">\n <button class="button blue">{_w('Save')}</button>\n <button class="button light-gray js-close-drawer">{_w('Close')}</button>\n </footer>\n </div>\n </div>\n</div>\n`; if (with_js) { js = `(function($) { $(function() { var drawer_html = $('#${component_name}-drawer').prop('outerHTML'); $('#${component_name}-drawer').remove(); window.${component_name.replace(/-/g, '_')}Drawer = { open: function(options) { return $.waDrawer({ html: drawer_html, direction: (options && options.direction) || 'right', width: (options && options.width) || '600px', onOpen: function($drawer, drawer) { // Инициализация при открытии } }); } }; }); })(jQuery); `; } break; case 'chips': tpl += `<ul class="chips" id="${component_name}-chips">\n {foreach $items as $item}\n <li{if $item.selected} class="selected"{/if}>\n <a href="{$item.url|escape}">\n {if $item.icon}<i class="{$item.icon}"></i>{/if}\n {$item.name|escape}\n {if $item.count}<span class="count">{$item.count}</span>{/if}\n </a>\n </li>\n {/foreach}\n</ul>\n`; break; case 'bricks': tpl += `<div class="bricks" id="${component_name}-bricks">\n {foreach $items as $item}\n <div class="brick{if $item.selected} selected{/if}{if $item.accented} accented{/if}{if $item.full_width} full-width{/if}">\n {if $item.icon}<span class="icon"><i class="{$item.icon}"></i></span>{/if}\n {if $item.count}<span class="count">{$item.count}</span>{/if}\n {$item.name|escape}\n </div>\n {/foreach}\n</div>\n`; break; case 'upload': tpl += `<div id="${component_name}-upload" class="box uploadbox">\n <div class="upload">\n <label class="link">\n <span>{_w('Select file')}</span>\n <input name="files[]" type="file" multiple autocomplete="off">\n </label>\n </div>\n <p class="hint small">{_w('or drag and drop files here')}</p>\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $('#${component_name}-upload').waUpload({ is_uploadbox: true, show_file_name: true }); }); })(jQuery); `; } break; case 'bottombar': tpl += `<div class="bottombar" id="${component_name}-bottombar">\n <ul>\n {foreach $menu_items as $item}\n <li{if $item.selected} class="selected"{/if}>\n <a href="{$item.url|escape}">\n {if $item.icon}<span class="menu-icon {$item.icon}" aria-hidden="true"></span>{/if}\n <span>{$item.name|escape}</span>\n {if $item.badge}<span class="badge">{$item.badge}</span>{/if}\n </a>\n </li>\n {/foreach}\n </ul>\n</div>\n`; break; case 'userpic_list': tpl += `<div class="flexbox wrap" id="${component_name}-userpics">\n {foreach $users as $user}\n <div class="inline-block margin-right">\n <span class="userpic userpic-48{if $user.outlined} outlined{/if}">\n {if $user.photo}\n <img src="{$user.photo}" alt="{$user.name|escape}">\n {else}\n <img src="{$wa_url}wa-content/img/userpic.svg" alt="">\n {/if}\n {if $user.status}\n <span class="userstatus{if $user.status_color} bg-{$user.status_color}{/if}">\n {if $user.status_icon}<i class="{$user.status_icon}"></i>{/if}\n </span>\n {/if}\n </span>\n <div class="small align-center">{$user.name|escape}</div>\n </div>\n {/foreach}\n</div>\n`; break; case 'slider': tpl += `<div class="slider" id="${component_name}-slider">\n <input type="text" id="${component_name}-min" class="short" value="{$min_value|default:0}">\n <input type="text" id="${component_name}-max" class="short" value="{$max_value|default:100}">\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-slider").waSlider({ \\$input_min: $("#${component_name}-min"), \\$input_max: $("#${component_name}-max"), limit: { min: 0, max: 100 }, move: function(values, slider) { console.log($_('Move'), values); }, change: function(values, slider) { console.log($_('Change'), values); } }); }); })(jQuery); `; } break; case 'toggle': tpl += `<div class="toggle" id="${component_name}-toggle">\n {foreach $options as $option}\n <span{if $option.selected} class="selected"{/if} data-value="{$option.value|escape}">{$option.name|escape}</span>\n {/foreach}\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-toggle").waToggle({ change: function(event, target, toggle) { var value = $(target).data('value'); console.log($_('Selected'), value); } }); }); })(jQuery); `; } break; case 'skeleton': tpl += `<div class="skeleton" id="${component_name}-skeleton">\n <div class="flexbox">\n <div class="sidebar blank height-auto">\n <span class="skeleton-custom-circle size-96" style="margin: 0 auto 1rem;"></span>\n <span class="skeleton-line"></span>\n <span class="skeleton-list"></span>\n <span class="skeleton-list"></span>\n <span class="skeleton-list"></span>\n </div>\n <div class="content">\n <div class="article">\n <div class="article-body">\n <span class="skeleton-header" style="width: 70%;"></span>\n <span class="skeleton-line"></span>\n <span class="skeleton-line"></span>\n <span class="skeleton-line" style="width: 60%;"></span>\n </div>\n </div>\n </div>\n </div>\n</div>\n`; break; case 'tabs': tpl += `<ul class="tabs" id="${component_name}-tabs">\n {foreach $tabs as $tab}\n <li{if $tab.selected} class="selected"{/if}>\n <a href="{$tab.url|escape}">{$tab.name|escape}</a>\n </li>\n {/foreach}\n</ul>\n<div id="${component_name}-tab-content">\n {$tab_content}\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $('#${component_name}-tabs a').on('click', function(e) { e.preventDefault(); var \\$tab = $(this).closest('li'); $('#${component_name}-tabs li').removeClass('selected'); \\$tab.addClass('selected'); // Load content via AJAX or show/hide }); }); })(jQuery); `; } break; case 'progressbar': tpl += `<div class="progressbar" id="${component_name}-progressbar"></div>\n`; if (with_js) { js = `(function($) { $(function() { var \\$bar = $("#${component_name}-progressbar").waProgressbar({ percentage: 0, color: "var(--accent-color)", "display-text": true, "text-inside": false }); window.${component_name.replace(/-/g, '_')}Progressbar = { instance: \\$bar.data("progressbar"), set: function(percentage, text) { this.instance.set({ percentage: percentage, text: text || (percentage + "%") }); } }; }); })(jQuery); `; } break; case 'tooltip': tpl += `<span class="wa-tooltip" id="${component_name}-tooltip" data-wa-tooltip-content="{$tooltip_text|escape}" data-wa-tooltip-placement="top">?</span>\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-tooltip").waTooltip({ placement: "top", trigger: "mouseenter focus" }); }); })(jQuery); `; } break; case 'autocomplete': tpl += `<input type="text" id="${component_name}-autocomplete" class="long" placeholder="{_w('Start typing...')}">\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-autocomplete").waAutocomplete({ source: [], // Array or URL for AJAX minLength: 2, delay: 300, select: function(event, ui) { console.log($_('Selected'), ui.item.value); } }); }); })(jQuery); `; } break; case 'menu': tpl += `<ul class="menu" id="${component_name}-menu">\n {foreach $menu_items as $item}\n <li{if $item.selected} class="selected"{/if}{if $item.accented} class="accented"{/if}>\n <a href="{$item.url|escape}">\n {if $item.count}<span class="count">{$item.count}</span>{/if}\n {if $item.icon}<i class="{$item.icon}"></i>{/if}\n <span>{$item.name|escape}{if $item.hint} <span class="hint">{$item.hint|escape}</span>{/if}</span>\n </a>\n {if $item.children}\n <ul class="menu">\n {foreach $item.children as $child}\n <li{if $child.selected} class="selected"{/if}>\n <a href="{$child.url|escape}">\n {if $child.icon}<i class="{$child.icon}"></i>{/if}\n <span>{$child.name|escape}</span>\n </a>\n </li>\n {/foreach}\n </ul>\n {/if}\n </li>\n {/foreach}\n</ul>\n`; break; case 'alert': tpl += `{if $alert_type && $alert_message}\n<div class="alert {$alert_type}" id="${component_name}-alert">\n {$alert_message|escape}\n</div>\n{/if}\n`; break; case 'breadcrumbs': tpl += `<ul class="breadcrumbs" id="${component_name}-breadcrumbs">\n {foreach $breadcrumbs as $crumb}\n <li{if $crumb.active} class="active"{/if}><a href="{$crumb.url|escape}">{$crumb.name|escape}</a></li>\n {/foreach}\n</ul>\n`; break; case 'spinner': tpl += `<div class="spinner" id="${component_name}-spinner"></div>\n`; break; case 'switch': tpl += `<div class="switch-with-text" id="${component_name}-switch-wrapper">\n <span class="switch" id="${component_name}-switch">\n <input type="checkbox" id="input-${component_name}" checked>\n </span>\n <label for="input-${component_name}" data-active-text="{_w('On')}" data-inactive-text="{_w('Off')}">{_w('On')}</label>\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-switch").waSwitch({ change: function(active, wa_switch) { console.log("Switch active:", active); } }); }); })(jQuery); `; } break; case 'dropdown': tpl += `<div class="dropdown" id="${component_name}-dropdown">\n <button class="dropdown-toggle button outlined" type="button">{_w('Dropdown')}</button>\n <div class="dropdown-body">\n <ul class="menu">\n {foreach $items as $item}\n <li><a href="{$item.url|escape}">{$item.name|escape}</a></li>\n {/foreach}\n </ul>\n </div>\n</div>\n`; if (with_js) { js = `(function($) { $(function() { $("#${component_name}-dropdown").waDropdown(); }); })(jQuery); `; } break; case 'card': tpl += `<figure class="card" style="width: 300px;">\n <div class="image">\n <img src="{$image_url|default:'https://via.placeholder.com/300x200'}" alt="">\n </div>\n <div class="details">\n <h5>{$title|escape}</h5>\n <p class="small">{$description|escape}</p>\n </div>\n</figure>\n`; break; case 'paging': tpl += `{if $pages > 1}\n<ul class="paging" id="${component_name}-paging">\n {if $current_page > 1}\n <li><a href="{$base_url}page={$current_page - 1}">&larr;</a></li>\n {/if}\n {foreach $page_numbers as $p}\n <li{if $p == $current_page} class="selected"{/if}>\n {if $p == '...'}<span>...</span>{else}<a href="{$base_url}page={$p}">{$p}</a>{/if}\n </li>\n {/foreach}\n {if $current_page < $pages}\n <li><a href="{$base_url}page={$current_page + 1}">&rarr;</a></li>\n {/if}\n</ul>\n{/if}\n`; break; default: tpl += `<!-- Unknown component type: ${component_type} -->\n`; break; } await fs.writeFile(path.join(cmpDir, `${component_name}.html`), tpl); if (js) { const jsDir = path.join(target_path, 'js', 'components'); await ensureDir(jsDir); await fs.writeFile(path.join(jsDir, `${component_name}.js`), js); } return { content: [{ type: 'text', text: `Компонент ${component_type} "${component_name}" создан` + (js ? ` с JS инициализацией` : '') }] }; }
  • Registration of the 'create_ui_component' tool in the listTools response, including description and detailed input schema with enum for component types.
    { name: 'create_ui_component', description: 'Сгенерировать UI-компонент Webasyst 2.0', inputSchema: { type: 'object', properties: { component_type: { type: 'string', enum: ['table', 'form', 'modal', 'drawer', 'chips', 'bricks', 'upload', 'bottombar', 'userpic_list', 'slider', 'toggle', 'switch', 'skeleton', 'tabs', 'progressbar', 'tooltip', 'autocomplete', 'menu', 'alert', 'paging', 'breadcrumbs', 'spinner', 'dropdown', 'card'], description: 'Тип компонента: table, form, modal, drawer, chips, bricks, upload, bottombar, userpic_list, slider, toggle, switch, skeleton, tabs, progressbar, tooltip, autocomplete, menu, alert, paging, breadcrumbs, spinner, dropdown, card' }, component_name: { type: 'string', description: 'Уникальное имя компонента (латиница, без пробелов)' }, target_path: { type: 'string', description: 'Путь к приложению/плагину' }, with_js: { type: 'boolean', default: true, description: 'Создать JS-инициализацию (для modal, drawer, upload, slider, toggle, tabs, progressbar, tooltip, autocomplete, switch, dropdown)' } }, required: ['component_type', 'component_name', 'target_path'] } },
  • Dispatch case in the CallToolRequestSchema handler that routes calls to the createUIComponentTool function.
    case 'create_ui_component': return await createUIComponentTool(args);
  • Input schema definition for the create_ui_component tool, specifying parameters like component_type (with enum of supported UI components), component_name, target_path, and optional with_js.
    { name: 'create_ui_component', description: 'Сгенерировать UI-компонент Webasyst 2.0', inputSchema: { type: 'object', properties: { component_type: { type: 'string', enum: ['table', 'form', 'modal', 'drawer', 'chips', 'bricks', 'upload', 'bottombar', 'userpic_list', 'slider', 'toggle', 'switch', 'skeleton', 'tabs', 'progressbar', 'tooltip', 'autocomplete', 'menu', 'alert', 'paging', 'breadcrumbs', 'spinner', 'dropdown', 'card'], description: 'Тип компонента: table, form, modal, drawer, chips, bricks, upload, bottombar, userpic_list, slider, toggle, switch, skeleton, tabs, progressbar, tooltip, autocomplete, menu, alert, paging, breadcrumbs, spinner, dropdown, card' }, component_name: { type: 'string', description: 'Уникальное имя компонента (латиница, без пробелов)' }, target_path: { type: 'string', description: 'Путь к приложению/плагину' }, with_js: { type: 'boolean', default: true, description: 'Создать JS-инициализацию (для modal, drawer, upload, slider, toggle, tabs, progressbar, tooltip, autocomplete, switch, dropdown)' } }, required: ['component_type', 'component_name', 'target_path'] } },

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/emmy-design/webasyst-mcp'

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