7.6 KiB
Here’s a complete README draft you can use for your project:
🖌️ tldraw Interactive UI Controllers
This project extends tldraw to support interactive UI components (similar to Balsamiq) that can be dropped into the canvas and interacted with directly.
We’ve built 10 controllers:
- ✅ Checkbox
- 🔘 Radio Group
- ✏️ Text Input
- 📝 Textarea
- ⏹ Button
- 🔄 Toggle Switch
- 📅 Date Picker
- 🔽 ComboBox (Select Dropdown)
- 📊 Data Grid (Table)
- 📦 Form Container (groups other controls)
All controllers are fully interactive inside the canvas, not just static wireframes.
🚀 Features
- Drag & drop controllers into the tldraw canvas.
- Controls retain state (e.g., checkbox checked, input text, dropdown selection).
- Controls are resizable & draggable like normal shapes.
- Real HTML elements embedded in SVG via
foreignObject. - Can be extended with new components easily.
📂 Project Structure
src/
├─ shapes/
│ ├─ ButtonShape.tsx
│ ├─ CheckboxShape.tsx
│ ├─ ComboBoxShape.tsx
│ ├─ DataGridShape.tsx
│ ├─ DatePickerShape.tsx
│ ├─ FormShape.tsx
│ ├─ InputShape.tsx
│ ├─ RadioGroupShape.tsx
│ ├─ TextAreaShape.tsx
│ └─ ToggleShape.tsx
├─ components/
│ └─ ControlsPalette.tsx
├─ App.tsx
└─ main.tsx
⚡ Installation
git clone https://github.com/your-org/tldraw-ui-controllers.git
cd tldraw-ui-controllers
npm install
npm run dev
Open http://localhost:5173 in your browser.
🛠️ Usage
Adding a Control
Each control is implemented as a custom shape. From the palette sidebar, you can click any control to insert it:
editor.createShape({
type: "checkbox",
x: 200,
y: 200,
props: { checked: false, label: "Accept Terms" },
});
Example: Checkbox Implementation
type CheckboxShape = TLBaseShape<"checkbox", { checked: boolean; label: string }>;
class CheckboxShapeUtil extends ShapeUtil<CheckboxShape> {
static override type = "checkbox";
override render(shape: CheckboxShape) {
return (
<foreignObject width={200} height={40}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<input
type="checkbox"
checked={shape.props.checked}
onChange={(e) =>
this.editor.updateShape({
...shape,
props: { ...shape.props, checked: e.target.checked },
})
}
/>
<span>{shape.props.label}</span>
</div>
</foreignObject>
);
}
}
🎛️ Controllers
| Control | Description | Example Props |
|---|---|---|
| Button | Clickable button | { label: "Submit" } |
| Checkbox | Standard checkbox | { checked: false, label: "Accept Terms" } |
| Radio Group | Multiple exclusive options | { options: ["A", "B", "C"], selected: "A" } |
| Text Input | Single-line input | { value: "", placeholder: "Enter text" } |
| Textarea | Multi-line input | { value: "", placeholder: "Write here..." } |
| Toggle Switch | On/Off toggle | { on: true } |
| Date Picker | Calendar input | { date: "2025-09-01" } |
| ComboBox | Dropdown list | { options: ["One", "Two"], selected: "One" } |
| Data Grid | Simple editable table | { rows: [["A1","B1"],["A2","B2"]] } |
| Form Container | Holds other shapes | { title: "User Form" } |
🧩 Extending with New Controls
To add a new control:
- Create a new
ShapeUtilsubclass insrc/shapes/. - Use
<foreignObject>to render any HTML element. - Update
App.tsxto register it inshapeUtils. - Add it to the ControlsPalette.
📸 Preview
- Palette on the left with draggable controllers.
- tldraw canvas on the right.
- Controls behave just like Balsamiq but real & interactive.
Got it ✅ I see your Prompt-to-Wireframe (tldraw) app running locally — it already generates wireframes on the canvas. Now you want to integrate the interactive controllers (button, forms, data grid, date picker, etc.) into this environment.
Here’s how you can integrate the two:
🔹 Integration Plan
-
Extend your current tldraw setup
- Right now your app renders
<Tldraw />with AI-generated wireframes. - You’ll register your 10 custom controllers (shapes) into the same editor.
- Right now your app renders
-
Add Controllers Palette
- Create a sidebar/panel with the controllers (like Balsamiq’s top bar).
- Each controller button inserts its shape into the tldraw canvas.
-
Register Custom Shapes
- In your
App.tsx(or wherever<Tldraw />is rendered), passshapeUtilswith all the controllers you built:
import { ButtonShapeUtil, CheckboxShapeUtil, ComboBoxShapeUtil, DataGridShapeUtil, DatePickerShapeUtil, FormShapeUtil, InputShapeUtil, RadioGroupShapeUtil, TextAreaShapeUtil, ToggleShapeUtil, } from "./shapes"; <Tldraw shapeUtils={[ ButtonShapeUtil, CheckboxShapeUtil, ComboBoxShapeUtil, DataGridShapeUtil, DatePickerShapeUtil, FormShapeUtil, InputShapeUtil, RadioGroupShapeUtil, TextAreaShapeUtil, ToggleShapeUtil, ]} /> - In your
-
Connect Palette → Shape Creation Example for a button in your palette:
function ControlsPalette({ editor }) { return ( <div className="palette"> <button onClick={() => editor.createShape({ type: "button", x: 200, y: 200, props: { label: "Click Me" }, }) } > ➕ Button </button> </div> ); }Add similar buttons for checkbox, date picker, grid, etc.
-
Combine With Prompt-to-Wireframe Flow
- When your AI generates wireframes, they appear as usual.
- The user can then drag in interactive controllers to replace/augment them.
- Example: AI generates a rectangle with label "DATA TABLE" → user deletes it and inserts a real DataGridShape.
🔹 Updated Project Structure
src/
├─ shapes/ # all 10 controllers
│ ├─ ButtonShape.tsx
│ ├─ CheckboxShape.tsx
│ ├─ ...
├─ components/
│ ├─ ControlsPalette.tsx
│ └─ WireframeGenerator.tsx # your existing AI integration
├─ App.tsx
└─ main.tsx
🔹 User Flow After Integration
- User enters a prompt → AI generates a wireframe layout (as in your screenshot).
- User sees a palette of interactive controllers.
- User drags/drops or clicks to insert real interactive controls (button, forms, date pickers, data grid).
- Wireframe evolves into a clickable mockup, not just static boxes.
📜 License
MIT License © 2025
👉 Do you want me to include example code for all 10 controllers in the README (full implementations), or just keep this README as a setup + usage guide and document the shape types in a separate file?