A library that converts ASCII wireframes into working HTML/UI with scene management
+---------------------------+
| 'WYREFRAME' | Draw in ASCII
| +---------------------+ | ↓
| | #email | | Convert to HTML!
| +---------------------+ |
| [ Login ] |
+---------------------------+
- ASCII to HTML: Convert simple ASCII art into interactive UI elements
- Scene Management: Multi-screen prototypes with transitions (fade, slide, zoom)
- Interaction DSL: Define button clicks, navigation, and form validation
- Device Preview: Responsive previews for mobile, tablet, and desktop
- Auto-Fix: Automatically correct common wireframe formatting issues
- TypeScript/ReScript: Full type safety with both language support
npm install wyreframeimport { createUI } from 'wyreframe';
const ui = `
@scene: login
+---------------------------+
| 'WYREFRAME' |
| +---------------------+ |
| | #email | |
| +---------------------+ |
| [ Login ] |
+---------------------------+
#email:
placeholder: "Enter your email"
[Login]:
@click -> goto(dashboard, slide-left)
`;
const result = createUI(ui);
if (result.success) {
document.getElementById('app').appendChild(result.root);
result.sceneManager.goto('login');
}| Syntax | Description | HTML Output |
|---|---|---|
+---+ |
Box/Container | <div> |
[ Text ] |
Button | <button> |
#id |
Input field | <input> |
"text" |
Link | <a> |
'text' |
Emphasis text | Title, Heading |
[x] / [ ] |
Checkbox | <input type="checkbox"> |
--- |
Divider | <hr> |
@scene: sceneId # Scene identifier (required)
@title: Page Title # Optional page title
@device: mobile # Device type for sizing
@transition: fade # Default transition effect| Device | Dimensions | Description |
|---|---|---|
desktop |
1440x900 | Desktop monitor |
laptop |
1280x800 | Laptop screen |
tablet |
768x1024 | Tablet portrait |
tablet-landscape |
1024x768 | Tablet landscape |
mobile |
375x812 | iPhone X ratio |
mobile-landscape |
812x375 | Mobile landscape |
#email:
placeholder: "Email"
[Login]:
variant: primary
@click -> goto(dashboard, slide-left)
"Forgot Password":
@click -> goto(reset)Actions:
| Action | Description | Example |
|---|---|---|
goto(scene, transition?) |
Navigate to scene | @click -> goto(home, fade) |
back() |
Navigate back | @click -> back() |
forward() |
Navigate forward | @click -> forward() |
validate(fields) |
Validate inputs | @submit -> validate(email, password) |
call(fn, args) |
Custom function | @click -> call(submit, form) |
Transitions: fade, slide-left, slide-right, zoom
Variants: primary, secondary, ghost
import {
parse,
parseOrThrow,
render,
createUI,
createUIOrThrow,
fix,
fixOnly
} from 'wyreframe';
// Parse only - returns { success, ast, warnings } or { success: false, errors }
const parseResult = parse(text);
// Parse and throw on error
const ast = parseOrThrow(text);
// Render AST to DOM (pass ast, not parseResult!)
if (parseResult.success) {
const { root, sceneManager } = render(parseResult.ast, options);
}
// Parse + Render combined (recommended)
const result = createUI(text, options);
// Parse + Render, throw on error
const { root, sceneManager } = createUIOrThrow(text, options);
// Auto-fix wireframe formatting issues
const fixResult = fix(text);
if (fixResult.success) {
console.log('Fixed:', fixResult.fixed.length, 'issues');
const cleanText = fixResult.text;
}
// Fix and return text only
const fixedText = fixOnly(text);const options = {
// Additional CSS class for container
containerClass: 'my-app',
// Inject default styles (default: true)
injectStyles: true,
// Override device type for all scenes
device: 'mobile',
// Scene change callback
onSceneChange: (fromScene, toScene) => {
console.log(`Navigated from ${fromScene} to ${toScene}`);
},
// Dead-end click callback (buttons/links without navigation)
onDeadEndClick: (info) => {
console.log(`Clicked: ${info.elementText} in scene ${info.sceneId}`);
// Show modal, custom logic, etc.
}
};
const result = createUI(text, options);const { sceneManager } = result;
sceneManager.goto('dashboard'); // Navigate to scene
sceneManager.goto('home', 'fade'); // Navigate with transition
sceneManager.back(); // Go back in history
sceneManager.forward(); // Go forward in history
sceneManager.getCurrentScene(); // Get current scene ID
sceneManager.getSceneIds(); // Get all scene IDs// Parse + Render
switch Renderer.createUI(ui, None) {
| Ok({root, sceneManager, _}) =>
sceneManager.goto("login")
| Error(errors) => Console.error(errors)
}
// With options
let options = {
device: Some(#mobile),
onSceneChange: Some((from, to) => Console.log2(from, to)),
onDeadEndClick: None,
containerClass: None,
injectStyles: None,
}
switch Renderer.createUI(ui, Some(options)) {
| Ok({root, sceneManager, _}) => ...
| Error(errors) => ...
}Wyreframe can automatically fix common formatting issues:
import { fix, fixOnly } from 'wyreframe';
const messyWireframe = `
+----------+
| Button | <- Misaligned pipe
+---------+ <- Width mismatch
`;
const result = fix(messyWireframe);
if (result.success) {
console.log('Fixed issues:', result.fixed);
console.log('Remaining issues:', result.remaining);
console.log('Clean wireframe:', result.text);
}
// Or just get the fixed text
const cleanText = fixOnly(messyWireframe);Fixable Issues:
- Misaligned pipes (|)
- Mismatched border widths
- Tabs instead of spaces
- Unclosed brackets
const app = `
@scene: login
@device: mobile
+---------------------------+
| 'Login' |
| +---------------------+ |
| | #email | |
| +---------------------+ |
| +---------------------+ |
| | #password | |
| +---------------------+ |
| [ Sign In ] |
| |
| "Create Account" |
+---------------------------+
---
@scene: signup
@device: mobile
+---------------------------+
| 'Sign Up' |
| +---------------------+ |
| | #name | |
| +---------------------+ |
| +---------------------+ |
| | #email | |
| +---------------------+ |
| [ Register ] |
| |
| "Back to Login" |
+---------------------------+
#email:
placeholder: "Email"
#password:
placeholder: "Password"
#name:
placeholder: "Full Name"
[Sign In]:
variant: primary
@click -> goto(signup, slide-left)
"Create Account":
@click -> goto(signup, slide-left)
[Register]:
variant: primary
@click -> goto(login, slide-right)
"Back to Login":
@click -> goto(login, slide-right)
`;
const result = createUI(app, {
onSceneChange: (from, to) => {
console.log(`Scene: ${from} -> ${to}`);
}
});
if (result.success) {
document.getElementById('app').appendChild(result.root);
result.sceneManager.goto('login');
}npm install
npm run res:build # ReScript build
npm run ts:build # TypeScript build
npm run build # Full build
npm run dev # Dev server (http://localhost:3000/examples)
npm test # Run tests
npm run test:watch # Test watch mode
npm run test:coverage # Generate coverage reportWyreframe uses a 3-stage parsing pipeline:
- Grid Scanner: Converts ASCII text to 2D character grid
- Shape Detector: Identifies boxes, nesting, and hierarchy
- Semantic Parser: Recognizes UI elements via pluggable parsers
The renderer generates pure DOM elements with CSS-based scene visibility and transitions.
GPL-3.0 License - see LICENSE for details.