Skip to main content

app

Agent-to-App: Generate native cross-platform applications from natural language. User describes what they want, AI generates Splash code, Canvas renders it as a native app in real-time. Supports screenshot-based self-healing loop. Triggers: /app, "make me an app", "build an app", "create an app", "generate app", "做个app", "生成应用", "帮我做个", "写个应用"

설치로 이동

소스 정보

저장소
ZhangHanDong/makepad
최근 소스 활동
2026년 7월 10일 09:10
감지된 SKILL.md 언어
영어
스타
1
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
app
version
1.0.0
description
Agent-to-App: Generate native cross-platform applications from natural language. User describes what they want, AI generates Splash code, Canvas renders it as a native app in real-time. Supports screenshot-based self-healing loop. Triggers: /app, "make me an app", "build an app", "create an app", "generate app", "做个app", "生成应用", "帮我做个", "写个应用"
allowed-tools
["Bash","Read","Write","Glob","Grep","AskUserQuestion"]
# /app — Agent-to-App Generator You are an app generator. The user describes an app in natural language, you generate Splash code, push it to Canvas, verify it renders correctly via screenshot, and iterate until the app works. **This is a NON-INTERACTIVE workflow after the user provides their request.** Generate → Push → Screenshot → Verify → Fix if needed. Do not ask for confirmation between steps. --- ## Step 1: Canvas Detection ```bash PORT="" if [ -f /tmp/makepad-canvas.port ]; then PORT=$(cat /tmp/makepad-canvas.port) curl -sf --max-time 2 "http://127.0.0.1:$PORT/ping" >/dev/null 2>&1 || PORT="" fi echo "CANVAS_PORT=${PORT:-NOT_FOUND}" ``` If Canvas is not running, tell the user: > "Canvas is not running. Start it with: > `cd /path/to/makepad && cargo run -p makepad-canvas &` > Then try again." Report **BLOCKED** and stop. --- ## Step 2: Understand the Request Parse the user's request into: - **App type** (timer, dashboard, calculator, music player, notepad, custom) - **Key features** (what buttons, what data, what interactions) - **Style preference** (dark/light, colors, minimal/rich) If the request is empty or too vague, ask: > "What kind of app would you like? For example: pomodoro timer, calculator, dashboard, music player, or describe your idea." --- ## Step 3: Generate Splash Code Generate a complete Splash script that runs as a standalone app in Canvas. ### CRITICAL: Canvas Splash Syntax Rules You MUST follow these rules exactly. Violating them causes silent render failures. **1. Use dot-path properties, NOT nested blocks:** ``` // WRONG — backgrounds will not render RoundedView{draw_bg: { color: #x1a1a2e border_radius: 8.0 }} // CORRECT RoundedView{draw_bg.color: #x1a1a2e draw_bg.radius: 8.} ``` **2. Border radius is `draw_bg.radius`, NOT `draw_bg.border_radius`:** ``` draw_bg.radius: 8. ``` **3. Padding uses `Inset{}` with trailing-dot floats:** ``` padding: Inset{left: 20. right: 20. top: 16. bottom: 16.} ``` **4. Align uses `Align{}` type:** ``` align: Align{y: 0.5} align: Center ``` **5. Float values use trailing dot:** ``` 8. (not 8.0) 16. (not 16.0) ``` **6. No commas, no semicolons.** Whitespace-delimited. **7. Hex colors with 'e' need `#x` prefix:** ``` #x1e1e2e #xe0e0e0 #x4466ee ``` **8. `SolidView` and `RoundedView` render backgrounds automatically.** No `show_bg: true` needed. **9. Use `--data-binary` for HTTP POST** (not `-d`) to preserve newlines. **10. Negation `!expr` does NOT work.** Use `if x { false } else { true }`. **11. Division `/` produces float.** For integer division, use while loops. **12. `math.floor()` does NOT exist.** Use while loops for integer math. ### Splash App Structure Every app follows this structure: ```splash // 1. State let state = { count: 0 running: false } // 2. Functions fn update_display() { ui.count_label.set_text("" + state.count) } fn tick() { // Called every 1 second automatically if defined if state.running { state.count = state.count + 1 update_display() } } // 3. UI SolidView{width: Fill height: Fit draw_bg.color: #x0c0c18 flow: Down padding: Inset{left: 32. right: 32. top: 24. bottom: 24.} spacing: 16 Label{text: "My App" draw_text.color: #xeeeeff draw_text.text_style.font_size: 24} count_label := Label{text: "0" draw_text.color: #x4fc3f7 draw_text.text_style.font_size: 48} View{width: Fit height: Fit flow: Right spacing: 12 Button{text: "Start" draw_bg.color: #x51cf66 draw_text.color: #x111111 padding: Inset{left: 24. right: 24. top: 12. bottom: 12.} draw_bg.radius: 6. on_click: ||{ state.running = true update_display() } } Button{text: "Reset" draw_bg.color: #x444466 draw_text.color: #xccccdd padding: Inset{left: 24. right: 24. top: 12. bottom: 12.} draw_bg.radius: 6. on_click: ||{ state.count = 0 state.running = false update_display() } } } } ``` ### Widget Quick Reference | Widget | Usage | |--------|-------| | `SolidView{}` | Container with solid background color | | `RoundedView{}` | Container with rounded corners | | `View{}` | Transparent container for layout | | `Label{}` | Text display | | `Button{}` | Clickable button with `on_click: \|\|{...}` | | `TextInput{}` | Text input with `on_return: \|\|{...}` | | `Slider{}` | Slider with `on_change: \|val\|{...}` min/max/value | | `Hr{}` | Horizontal rule | | `Filler{}` | Fills remaining space (use between Fit-sized siblings) | ### Layout Quick Reference | Property | Values | |----------|--------| | `width` | `Fill`, `Fit`, or pixel number | | `height` | `Fill`, `Fit`, or pixel number (ALWAYS set `height: Fit` on content containers) | | `flow` | `Down` (vertical), `Right` (horizontal) | | `spacing` | Gap between children (number) | | `padding` | `Inset{left: N. right: N. top: N. bottom: N.}` | | `align` | `Center`, `Align{x: 0.5}`, `Align{y: 0.5}`, `Align{x: 0.5 y: 0.5}` | ### API Quick Reference | Splash API | Usage | |------------|-------| | `ui.name.set_text("...")` | Update label/button text | | `ui.name.text()` | Read current text | | `ui.view.render()` | Trigger on_render callback | | `fn tick()` | Auto-called every 1 second | | `on_click: \|\|{...}` | Button click handler | | `on_return: \|\|{...}` | TextInput enter key | | `on_change: \|val\|{...}` | Slider value change | | `agent.notify("event", {...})` | Send a typed event to the Canvas bridge | ### Canvas Event Bridge Use `agent.notify(event_id, json_payload)` when generated UI needs to send a structured event to the host: ```splash Button{text: "+1" on_click: || agent.notify("inc", {})} Button{text: "Save" on_click: || agent.notify("save", {title: ui.title.text()})} ``` Canvas broadcasts notify events to WebSocket clients and `GET /event` waiters as one JSON line: ```json {"type":"notify","event":"inc","payload":{}} ``` Named button clicks are still broadcast for compatibility, but they also use a typed envelope: ```json {"type":"click","name":"start_btn"} ``` ### Reference: Pomodoro Timer (proven working) ```splash let pomo = { remaining: 1500 running: false sessions: 0 mode: "work" } fn fmt_time() { let total = pomo.remaining let m = 0 while total >= 60 { m = m + 1 total = total - 60 } let s = total let ms = if m < 10 { "0" + m } else { "" + m } let ss = if s < 10 { "0" + s } else { "" + s } ms + ":" + ss } fn refresh() { ui.timer_label.set_text(fmt_time()) if pomo.running { ui.start_btn.set_text("Pause") } else { ui.start_btn.set_text("Start") } } fn tick() { if pomo.running { if pomo.remaining > 0 { pomo.remaining = pomo.remaining - 1 } if pomo.remaining <= 0 { pomo.running = false } refresh() } } SolidView{width: Fill height: Fit draw_bg.color: #x0a0a12 flow: Down align: Center spacing: 20 padding: Inset{left: 40. right: 40. top: 50. bottom: 40.} Label{text: "FOCUS TIME" draw_text.color: #xff6b6b draw_text.text_style.font_size: 14} timer_label := Label{text: "25:00" draw_text.color: #xffffff draw_text.text_style.font_size: 64} View{height: 20} View{width: Fit height: Fit flow: Right spacing: 16 align: Center start_btn := Button{text: "Start" draw_bg.color: #x51cf66 draw_text.color: #x111111 padding: Inset{left: 24. right: 24. top: 12. bottom: 12.} draw_bg.radius: 6. on_click: ||{ if pomo.running { pomo.running = false } else { pomo.running = true } refresh() } } Button{text: "Reset" draw_bg.color: #x444466 draw_text.color: #xccccdd padding: Inset{left: 24. right: 24. top: 12. bottom: 12.} draw_bg.radius: 6. on_click: ||{ pomo.running = false pomo.remaining = 1500 refresh() } } } } ``` --- ## Step 4: Push to Canvas Write the generated Splash to a temp file and POST it: ```bash PORT=$(cat /tmp/makepad-canvas.port) # Write Splash to temp file (preserves newlines) cat > /tmp/canvas-app.splash << 'SPLASH_EOF' ... generated splash code ... SPLASH_EOF # Push to Canvas curl -sf -X POST "http://127.0.0.1:$PORT/splash" --data-binary @/tmp/canvas-app.splash ``` **ALWAYS use `--data-binary @file`**, never inline `-d` for multi-line Splash. --- ## Step 5: Screenshot & Verify After pushing, take a screenshot of the Canvas window to verify the render: ```bash # Find Canvas window and screenshot screencapture -l $(osascript -e 'tell application "System Events" to tell process "makepad-canvas" to return id of window 1') /tmp/canvas-screenshot.png 2>/dev/null ``` If screencapture fails, try alternative methods or use the makepad-screenshot skill. Read the screenshot. Check: 1. **Is the UI visible?** (not blank/white) 2. **Are backgrounds rendering?** (colored cards, not just text) 3. **Is the layout correct?** (elements properly aligned) 4. **Are all expected elements present?** (buttons, labels, inputs) --- ## Step 6: Self-Healing Loop If the screenshot shows problems: 1. **Identify the issue** from the screenshot (blank screen, missing backgrounds, broken layout, text overlap) 2. **Diagnose the cause** (wrong property name? missing height: Fit? wrong Inset syntax?) 3. **Fix the Splash code** in `/tmp/canvas-app.splash` 4. **Re-POST** to Canvas 5. **Re-screenshot** to verify Maximum 3 fix iterations. If still broken after 3 attempts, show the user the screenshot and explain what went wrong. Common fixes: - Blank screen → Add `height: Fit` to root container, check `width: Fill` - No backgrounds → Use `draw_bg.radius` not `draw_bg.border_radius`, use dot-path syntax - Text invisible → Set `draw_text.color` to a contrasting color - Layout broken → Check `flow: Down` vs `flow: Right`, check `Inset{}` padding syntax --- ## Step 7: Report Success Once the app renders correctly: 1. Tell the user the app is ready on Canvas 2. Briefly describe what was generated (features, interactions) 3. Mention that they can ask to modify it ("change the color", "add a feature", "make it bigger") **DONE** — App generated and rendered on Canvas. --- ## Iterative Modification If the user asks to modify an existing app: 1. Read the current Splash from `/tmp/canvas-app.splash`
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기