generate-framelayout
Generate FrameLayoutKit code for iOS layouts by specifying layout type, views, and configuration. Supports VStack, HStack, ZStack, Grid, and more.
Instructions
Generate FrameLayoutKit code for iOS layouts
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| layoutType | Yes | ||
| views | Yes | ||
| configuration | No |
Implementation Reference
- src/index.js:27-72 (schema)Input schema for the generate-framelayout tool, defining layoutType (enum of 8 layout types), views array (with name, type, properties, text, image), and optional configuration (axis, spacing, padding, distribution, rows, columns, etc.)
inputSchema: { type: "object", properties: { layoutType: { type: "string", enum: ["FrameLayout", "VStackLayout", "HStackLayout", "ZStackLayout", "DoubleFrameLayout", "GridFrameLayout", "ScrollStackView", "FlowFrameLayout"] }, views: { type: "array", items: { type: "object", properties: { name: { type: "string" }, type: { type: "string", enum: ["UILabel", "UIButton", "UIImageView", "UITextField", "UITextView", "UIView", "UIStackView", "UIScrollView", "UITableView", "UICollectionView"] }, properties: { type: "object" }, text: { type: "string" }, image: { type: "string" } }, required: ["name", "type"] } }, configuration: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical"] }, spacing: { type: "number" }, padding: { type: "number" }, distribution: { type: "string", enum: ["left", "center", "right", "top", "bottom", "equal", "fill", "justified"] }, rows: { type: "number" }, columns: { type: "number" }, interItemSpacing: { type: "number" }, lineSpacing: { type: "number" }, isOverlapped: { type: "boolean" } } } }, required: ["layoutType", "views"] } - src/index.js:24-73 (registration)Registration of the generate-framelayout tool in the MCP server's ListToolsRequestSchema handler, listing its name, description, and input schema
{ name: "generate-framelayout", description: "Generate FrameLayoutKit code for iOS layouts", inputSchema: { type: "object", properties: { layoutType: { type: "string", enum: ["FrameLayout", "VStackLayout", "HStackLayout", "ZStackLayout", "DoubleFrameLayout", "GridFrameLayout", "ScrollStackView", "FlowFrameLayout"] }, views: { type: "array", items: { type: "object", properties: { name: { type: "string" }, type: { type: "string", enum: ["UILabel", "UIButton", "UIImageView", "UITextField", "UITextView", "UIView", "UIStackView", "UIScrollView", "UITableView", "UICollectionView"] }, properties: { type: "object" }, text: { type: "string" }, image: { type: "string" } }, required: ["name", "type"] } }, configuration: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical"] }, spacing: { type: "number" }, padding: { type: "number" }, distribution: { type: "string", enum: ["left", "center", "right", "top", "bottom", "equal", "fill", "justified"] }, rows: { type: "number" }, columns: { type: "number" }, interItemSpacing: { type: "number" }, lineSpacing: { type: "number" }, isOverlapped: { type: "boolean" } } } }, required: ["layoutType", "views"] } }, - src/index.js:176-187 (handler)The CallToolRequest handler for generate-framelayout. It destructures layoutType, views, and configuration from args, creates a FrameLayoutGenerator instance, calls generateLayout(), and returns the generated code as text content
case "generate-framelayout": { const { layoutType, views, configuration = {} } = args; const generator = new FrameLayoutGenerator(); const code = generator.generateLayout(layoutType, views, configuration); return { content: [{ type: "text", text: code }] }; } - src/index.js:246-805 (helper)FrameLayoutGenerator class containing the core generation logic. The generateLayout() method dispatches to specialized generators (generateFrameLayout, generateStackLayout, generateDoubleFrameLayout, generateGridLayout, generateScrollStackView, generateFlowLayout, generateZStackLayout) based on the layoutType
// Main FrameLayout code generator class class FrameLayoutGenerator { generateLayout(layoutType, views, config) { switch (layoutType) { case "FrameLayout": return this.generateFrameLayout(views[0], config); case "VStackLayout": return this.generateStackLayout("VStackLayout", views, config); case "HStackLayout": return this.generateStackLayout("HStackLayout", views, config); case "ZStackLayout": return this.generateZStackLayout(views, config); case "DoubleFrameLayout": return this.generateDoubleFrameLayout(views, config); case "GridFrameLayout": return this.generateGridLayout(views, config); case "ScrollStackView": return this.generateScrollStackView(views, config); case "FlowFrameLayout": return this.generateFlowLayout(views, config); default: throw new Error(`Unknown layout type: ${layoutType}`); } } generateFrameLayout(view, config) { let code = ""; // Create view code += "// Create view\n"; code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; // Create layout with chainable DSL syntax code += "\n// Create layout with chainable DSL\n"; code += `let ${view.name}Layout = FrameLayout {\n`; // Add view with individual configuration inside closure let viewConfig = ""; // Check for individual view properties if (view.properties) { const viewChainMethods = []; if (view.properties.padding !== undefined) { if (typeof view.properties.padding === "number") { viewChainMethods.push(`padding(${view.properties.padding})`); } else { viewChainMethods.push(`padding(top: ${view.properties.padding.top}, left: ${view.properties.padding.left}, bottom: ${view.properties.padding.bottom}, right: ${view.properties.padding.right})`); } } if (view.properties.fixedHeight !== undefined) { viewChainMethods.push(`fixedHeight(${view.properties.fixedHeight})`); } if (view.properties.fixedWidth !== undefined) { viewChainMethods.push(`fixedWidth(${view.properties.fixedWidth})`); } if (view.properties.flexible !== undefined) { viewChainMethods.push(`flexible(${view.properties.flexible})`); } if (view.properties.alignment) { viewChainMethods.push(`aligns(.${view.properties.alignment.vertical || "center"}, .${view.properties.alignment.horizontal || "center"})`); } if (viewChainMethods.length > 0) { viewConfig = "." + viewChainMethods.join("."); } } code += ` $0 + ${view.name}${viewConfig}\n`; code += "}"; // Build layout configuration chain const chainMethods = []; if (config.padding !== undefined) { if (typeof config.padding === "number") { chainMethods.push(`padding(${config.padding})`); } else { chainMethods.push(`padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})`); } } if (config.alignment) { chainMethods.push(`aligns(.${config.alignment.vertical || "center"}, .${config.alignment.horizontal || "center"})`); } if (config.fitToSuperview !== undefined && config.fitToSuperview) { chainMethods.push("fitToSuperview()"); } // Apply layout configuration chain if (chainMethods.length > 0) { code += "." + chainMethods.join("."); } return code; } generateStackLayout(stackType, views, config) { let code = ""; // Create views section code += "// Create views\n"; views.forEach(view => { code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; }); // Create layout with chainable DSL syntax code += "\n// Create layout with chainable DSL\n"; code += `let stackLayout = ${stackType} {\n`; // Add views with individual configurations inside closure views.forEach(view => { let viewConfig = ""; // Check for individual view properties if (view.properties) { const viewChainMethods = []; if (view.properties.padding !== undefined) { if (typeof view.properties.padding === "number") { viewChainMethods.push(`padding(${view.properties.padding})`); } else { viewChainMethods.push(`padding(top: ${view.properties.padding.top}, left: ${view.properties.padding.left}, bottom: ${view.properties.padding.bottom}, right: ${view.properties.padding.right})`); } } if (view.properties.fixedHeight !== undefined) { viewChainMethods.push(`fixedHeight(${view.properties.fixedHeight})`); } if (view.properties.fixedWidth !== undefined) { viewChainMethods.push(`fixedWidth(${view.properties.fixedWidth})`); } if (view.properties.fixedContentHeight !== undefined) { viewChainMethods.push(`fixedContentHeight(${view.properties.fixedContentHeight})`); } if (view.properties.fixedContentWidth !== undefined) { viewChainMethods.push(`fixedContentWidth(${view.properties.fixedContentWidth})`); } if (view.properties.flexible !== undefined) { viewChainMethods.push(`flexible(${view.properties.flexible})`); } if (view.properties.alignment) { viewChainMethods.push(`aligns(.${view.properties.alignment.vertical || "center"}, .${view.properties.alignment.horizontal || "center"})`); } if (viewChainMethods.length > 0) { viewConfig = "." + viewChainMethods.join("."); } } code += ` ($0 + ${view.name})${viewConfig}\n`; }); code += "}"; // Build layout configuration chain const chainMethods = []; if (config.spacing !== undefined) { chainMethods.push(`spacing(${config.spacing})`); } if (config.distribution) { chainMethods.push(`distribution(.${config.distribution})`); } if (config.padding !== undefined) { if (typeof config.padding === "number") { chainMethods.push(`padding(${config.padding})`); } else { chainMethods.push(`padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})`); } } if (config.alignment) { chainMethods.push(`aligns(.${config.alignment.vertical || "center"}, .${config.alignment.horizontal || "center"})`); } // Apply layout configuration chain if (chainMethods.length > 0) { code += "." + chainMethods.join("."); } return code; } generateDoubleFrameLayout(views, config) { if (views.length !== 2) { throw new Error("DoubleFrameLayout requires exactly 2 views"); } let code = ""; // Create views code += "// Create views\n"; code += `let ${views[0].name} = ${this.generateViewCreation(views[0])}\n`; code += `let ${views[1].name} = ${this.generateViewCreation(views[1])}\n`; // Create and configure layout code += "\n// Create and configure layout\n"; code += "let doubleLayout = DoubleFrameLayout()\n"; // Build method chain const chainMethods = []; if (config.axis) { chainMethods.push(`.axis(.${config.axis})`); } if (config.spacing !== undefined) { chainMethods.push(`.spacing(${config.spacing})`); } if (config.distribution) { chainMethods.push(`.distribution(.${config.distribution})`); } if (config.isOverlapped) { chainMethods.push(".isOverlapped(true)"); } // Apply method chain if we have any configurations if (chainMethods.length > 0) { code += "doubleLayout\n"; chainMethods.forEach(method => { code += ` ${method}\n`; }); } // Add views to layout code += "\n// Add views to layout\n"; code += `doubleLayout <+ ${views[0].name}\n`; code += `doubleLayout +> ${views[1].name}\n`; // Configure individual frame layouts if needed if (config.padding) { code += "\n// Configure padding for individual layouts\n"; if (typeof config.padding === "number") { code += `doubleLayout.leftFrameLayout.padding(${config.padding})\n`; code += `doubleLayout.rightFrameLayout.padding(${config.padding})\n`; } else { code += `doubleLayout.leftFrameLayout.padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})\n`; code += `doubleLayout.rightFrameLayout.padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})\n`; } } return code; } generateGridLayout(views, config) { let code = "let gridLayout = GridFrameLayout()\n"; // Configure grid structure code += `gridLayout.rows = ${config.rows || 2}\n`; code += `gridLayout.columns = ${config.columns || 3}\n`; if (config.axis) { code += `gridLayout.axis = .${config.axis}\n`; } if (config.spacing !== undefined) { code += `gridLayout.verticalSpacing = ${config.spacing}\n`; code += `gridLayout.horizontalSpacing = ${config.spacing}\n`; } // Create views array code += "\n// Create views\n"; code += "var gridViews: [UIView] = []\n"; views.forEach(view => { code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; code += `gridViews.append(${view.name})\n`; }); code += "\n// Assign views to grid\n"; code += "gridLayout.views = gridViews\n"; return code; } generateScrollStackView(views, config) { let code = ""; // Create views code += "// Create views\n"; views.forEach(view => { code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; }); // Create layout with chainable DSL syntax code += "\n// Create layout with chainable DSL\n"; code += "let scrollStackView = ScrollStackView {\n"; // Add views with individual configurations inside closure views.forEach(view => { let viewConfig = ""; // Check for individual view properties if (view.properties) { const viewChainMethods = []; if (view.properties.padding !== undefined) { if (typeof view.properties.padding === "number") { viewChainMethods.push(`padding(${view.properties.padding})`); } else { viewChainMethods.push(`padding(top: ${view.properties.padding.top}, left: ${view.properties.padding.left}, bottom: ${view.properties.padding.bottom}, right: ${view.properties.padding.right})`); } } if (view.properties.fixedHeight !== undefined) { viewChainMethods.push(`fixedHeight(${view.properties.fixedHeight})`); } if (view.properties.fixedWidth !== undefined) { viewChainMethods.push(`fixedWidth(${view.properties.fixedWidth})`); } if (view.properties.flexible !== undefined) { viewChainMethods.push(`flexible(${view.properties.flexible})`); } if (view.properties.alignment) { viewChainMethods.push(`aligns(.${view.properties.alignment.vertical || "center"}, .${view.properties.alignment.horizontal || "center"})`); } if (viewChainMethods.length > 0) { viewConfig = "." + viewChainMethods.join("."); } } code += ` ($0 + ${view.name})${viewConfig}\n`; }); code += "}"; // Build layout configuration chain const chainMethods = []; if (config.axis) { chainMethods.push(`axis(.${config.axis})`); } if (config.spacing !== undefined) { chainMethods.push(`spacing(${config.spacing})`); } if (config.distribution) { chainMethods.push(`distribution(.${config.distribution})`); } if (config.padding !== undefined) { if (typeof config.padding === "number") { chainMethods.push(`padding(${config.padding})`); } else { chainMethods.push(`padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})`); } } // Apply layout configuration chain if (chainMethods.length > 0) { code += "." + chainMethods.join("."); } return code; } generateFlowLayout(views, config) { let code = ""; // Create views code += "// Create views\n"; views.forEach(view => { code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; }); // Create layout with chainable DSL syntax code += "\n// Create layout with chainable DSL\n"; code += "let flowLayout = FlowFrameLayout {\n"; // Add views with individual configurations inside closure views.forEach(view => { let viewConfig = ""; // Check for individual view properties if (view.properties) { const viewChainMethods = []; if (view.properties.padding !== undefined) { if (typeof view.properties.padding === "number") { viewChainMethods.push(`padding(${view.properties.padding})`); } else { viewChainMethods.push(`padding(top: ${view.properties.padding.top}, left: ${view.properties.padding.left}, bottom: ${view.properties.padding.bottom}, right: ${view.properties.padding.right})`); } } if (view.properties.fixedHeight !== undefined) { viewChainMethods.push(`fixedHeight(${view.properties.fixedHeight})`); } if (view.properties.fixedWidth !== undefined) { viewChainMethods.push(`fixedWidth(${view.properties.fixedWidth})`); } if (view.properties.fixedContentHeight !== undefined) { viewChainMethods.push(`fixedContentHeight(${view.properties.fixedContentHeight})`); } if (view.properties.fixedContentWidth !== undefined) { viewChainMethods.push(`fixedContentWidth(${view.properties.fixedContentWidth})`); } if (view.properties.flexible !== undefined) { viewChainMethods.push(`flexible(${view.properties.flexible})`); } if (view.properties.alignment) { viewChainMethods.push(`aligns(.${view.properties.alignment.vertical || "center"}, .${view.properties.alignment.horizontal || "center"})`); } if (viewChainMethods.length > 0) { viewConfig = "." + viewChainMethods.join("."); } } code += ` ($0 + ${view.name})${viewConfig}\n`; }); code += "}"; // Build layout configuration chain const chainMethods = []; if (config.axis) { chainMethods.push(`axis(.${config.axis})`); } if (config.interItemSpacing !== undefined) { chainMethods.push(`interItemSpacing(${config.interItemSpacing})`); } if (config.lineSpacing !== undefined) { chainMethods.push(`lineSpacing(${config.lineSpacing})`); } if (config.distribution) { chainMethods.push(`distribution(.${config.distribution})`); } if (config.padding !== undefined) { if (typeof config.padding === "number") { chainMethods.push(`padding(${config.padding})`); } else { chainMethods.push(`padding(top: ${config.padding.top}, left: ${config.padding.left}, bottom: ${config.padding.bottom}, right: ${config.padding.right})`); } } // Apply layout configuration chain if (chainMethods.length > 0) { code += "." + chainMethods.join("."); } return code; } generateZStackLayout(views, config) { let code = "let zStackLayout = ZStackLayout()\n"; if (config.spacing !== undefined) { code += `zStackLayout.spacing = ${config.spacing}\n`; } // Add views (they will overlap) code += "\n// Add overlapping views\n"; views.forEach(view => { code += `let ${view.name} = ${this.generateViewCreation(view)}\n`; code += `zStackLayout + ${view.name}\n`; }); return code; } generateViewCreation(view) { switch (view.type) { case "UILabel": { let labelCode = "{\n let label = UILabel()\n"; if (view.text) { labelCode += ` label.text = "${view.text}"\n`; } labelCode += " return label\n}()"; return labelCode; } case "UIButton": { let buttonCode = "{\n let button = UIButton(type: .system)\n"; if (view.text) { buttonCode += ` button.setTitle("${view.text}", for: .normal)\n`; } buttonCode += " return button\n}()"; return buttonCode; } case "UIImageView": { let imageCode = "{\n let imageView = UIImageView()\n"; if (view.image) { imageCode += ` imageView.image = UIImage(${view.image.startsWith("system") ? "systemName: " : "named: "}"${view.image}")\n`; } imageCode += " return imageView\n}()"; return imageCode; } case "UITextField": { let textFieldCode = "{\n let textField = UITextField()\n"; if (view.text) { textFieldCode += ` textField.placeholder = "${view.text}"\n`; } textFieldCode += " return textField\n}()"; return textFieldCode; } case "UITextView": { let textViewCode = "{\n let textView = UITextView()\n"; if (view.text) { textViewCode += ` textView.text = "${view.text}"\n`; } textViewCode += " return textView\n}()"; return textViewCode; } default: return `{\n let view = ${view.type}()\n return view\n}()`; } } generatePadding(layoutName, padding) { if (typeof padding === "number") { return `${layoutName}.padding(${padding})\n`; } else { return `${layoutName}.padding(top: ${padding.top}, left: ${padding.left}, bottom: ${padding.bottom}, right: ${padding.right})\n`; } } paddingValue(padding) { if (typeof padding === "number") { return `${padding}`; } else { return `top: ${padding.top}, left: ${padding.left}, bottom: ${padding.bottom}, right: ${padding.right}`; } } } - src/index.js:737-787 (helper)generateViewCreation helper method that creates Swift code for initializing UI components (UILabel, UIButton, UIImageView, UITextField, UITextView, or generic UIView) with optional text/image properties
generateViewCreation(view) { switch (view.type) { case "UILabel": { let labelCode = "{\n let label = UILabel()\n"; if (view.text) { labelCode += ` label.text = "${view.text}"\n`; } labelCode += " return label\n}()"; return labelCode; } case "UIButton": { let buttonCode = "{\n let button = UIButton(type: .system)\n"; if (view.text) { buttonCode += ` button.setTitle("${view.text}", for: .normal)\n`; } buttonCode += " return button\n}()"; return buttonCode; } case "UIImageView": { let imageCode = "{\n let imageView = UIImageView()\n"; if (view.image) { imageCode += ` imageView.image = UIImage(${view.image.startsWith("system") ? "systemName: " : "named: "}"${view.image}")\n`; } imageCode += " return imageView\n}()"; return imageCode; } case "UITextField": { let textFieldCode = "{\n let textField = UITextField()\n"; if (view.text) { textFieldCode += ` textField.placeholder = "${view.text}"\n`; } textFieldCode += " return textField\n}()"; return textFieldCode; } case "UITextView": { let textViewCode = "{\n let textView = UITextView()\n"; if (view.text) { textViewCode += ` textView.text = "${view.text}"\n`; } textViewCode += " return textView\n}()"; return textViewCode; } default: return `{\n let view = ${view.type}()\n return view\n}()`; } }