General
robius-app-architecture - Claude MCP Skill
CRITICAL: Use for Robius app architecture patterns. Triggers on: Tokio, async, submit_async_request, 异步, 架构, SignalToUI, Cx::post_action, worker task, app structure, MatchEvent, handle_startup
SEO Guide: Enhance your AI agent with the robius-app-architecture tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to critical: use for robius app architecture patterns. triggers on: tokio, async, submit_async_request,... Download and configure this skill to unlock new capabilities for your AI workflow.
Documentation
SKILL.md# Robius App Architecture Skill
Best practices for structuring Makepad applications based on the Robrix and Moly codebases - production applications built with Makepad and Robius framework.
**Source codebases:**
- **Robrix**: Matrix chat client - complex sync/async with background subscriptions
- **Moly**: AI chat application - cross-platform (native + WASM) with streaming APIs
## Triggers
Use this skill when:
- Building a Makepad application with async backend integration
- Designing sync/async communication patterns in Makepad
- Structuring a Robius-style application
- Keywords: robrix, robius, makepad app structure, async makepad, tokio makepad
## Production Patterns
For production-ready async patterns, see the `_base/` directory:
| Pattern | Description |
|---------|-------------|
| [08-async-loading](./_base/08-async-loading.md) | Async data loading with loading states |
| [09-streaming-results](./_base/09-streaming-results.md) | Incremental results with SignalToUI |
| [13-tokio-integration](./_base/13-tokio-integration.md) | Full tokio runtime integration |
## Core Architecture Pattern
```
┌─────────────────────────────────────────────────────────────┐
│ UI Thread (Makepad) │
│ ┌─────────┐ ┌──────────┐ ┌──────────────────────┐ │
│ │ App │────▶│ WidgetRef │────▶│ Widget Tree (View) │ │
│ │ State │ │ ui │ │ Scope::with_data() │ │
│ └────┬────┘ └──────────┘ └──────────────────────┘ │
│ │ │
│ │ submit_async_request() │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ REQUEST_SENDER │─────────▶│ Crossbeam SegQueue │ │
│ │ (MPSC Channel) │ │ (Lock-free Updates) │ │
│ └─────────────────┘ └─────────────────────────┘ │
└───────────────────────────────────┬─────────────────────────┘
│
SignalToUI::set_ui_signal()
│
┌───────────────────────────────────┴─────────────────────────┐
│ Tokio Runtime (Async) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ worker_task (Request Handler) │ │
│ │ - Receives Request from UI │ │
│ │ - Spawns async tasks per request │ │
│ │ - Posts actions back via Cx::post_action() │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Per-Item Subscriber Tasks │ │
│ │ - Listens to external data stream │ │
│ │ - Sends Update via crossbeam channel │ │
│ │ - Calls SignalToUI::set_ui_signal() to wake UI │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## App Structure
### Top-Level App Definition
```rust
use makepad_widgets::*;
live_design! {
use link::theme::*;
use link::widgets::*;
App = {{App}} {
ui: <Root>{
main_window = <Window> {
window: {inner_size: vec2(1280, 800), title: "MyApp"},
body = {
// Main content here
}
}
}
}
}
app_main!(App);
#[derive(Live)]
pub struct App {
#[live] ui: WidgetRef,
#[rust] app_state: AppState,
}
impl LiveRegister for App {
fn live_register(cx: &mut Cx) {
// Order matters: register base widgets first
makepad_widgets::live_design(cx);
// Then shared/common widgets
crate::shared::live_design(cx);
// Then feature modules
crate::home::live_design(cx);
}
}
impl LiveHook for App {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
// One-time initialization after widget tree is created
}
}
```
### AppMain Implementation
```rust
impl AppMain for App {
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
// Forward to MatchEvent trait
self.match_event(cx, event);
// Pass AppState through widget tree via Scope
let scope = &mut Scope::with_data(&mut self.app_state);
self.ui.handle_event(cx, event, scope);
}
}
```
## Tokio Runtime Integration
### Static Runtime Initialization
```rust
use std::sync::Mutex;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
static TOKIO_RUNTIME: Mutex<Option<tokio::runtime::Runtime>> = Mutex::new(None);
static REQUEST_SENDER: Mutex<Option<UnboundedSender<AppRequest>>> = Mutex::new(None);
pub fn start_async_runtime() -> Result<tokio::runtime::Handle> {
let (request_sender, request_receiver) = tokio::sync::mpsc::unbounded_channel();
let rt_handle = TOKIO_RUNTIME.lock().unwrap()
.get_or_insert_with(|| {
tokio::runtime::Runtime::new()
.expect("Failed to create Tokio runtime")
})
.handle()
.clone();
// Store sender for UI thread to use
*REQUEST_SENDER.lock().unwrap() = Some(request_sender);
// Spawn the main worker task
rt_handle.spawn(worker_task(request_receiver));
Ok(rt_handle)
}
```
### Request Submission Pattern
```rust
pub enum AppRequest {
FetchData { id: String },
SendMessage { content: String },
// ... other request types
}
/// Submit a request from UI thread to async runtime
pub fn submit_async_request(req: AppRequest) {
if let Some(sender) = REQUEST_SENDER.lock().unwrap().as_ref() {
sender.send(req)
.expect("BUG: worker task receiver has died!");
}
}
```
### Worker Task Pattern
```rust
async fn worker_task(mut request_receiver: UnboundedReceiver<AppRequest>) -> Result<()> {
while let Some(request) = request_receiver.recv().await {
match request {
AppRequest::FetchData { id } => {
// Spawn a new task for each request
let _task = tokio::spawn(async move {
let result = fetch_data(&id).await;
// Post result back to UI thread
Cx::post_action(DataFetchedAction { id, result });
});
}
AppRequest::SendMessage { content } => {
let _task = tokio::spawn(async move {
match send_message(&content).await {
Ok(()) => Cx::post_action(MessageSentAction::Success),
Err(e) => Cx::post_action(MessageSentAction::Failed(e)),
}
});
}
}
}
Ok(())
}
```
## Lock-Free Update Queue Pattern
For high-frequency updates from background tasks:
```rust
use crossbeam_queue::SegQueue;
use makepad_widgets::SignalToUI;
pub enum DataUpdate {
NewItem { item: Item },
ItemChanged { id: String, changes: Changes },
Status { message: String },
}
static PENDING_UPDATES: SegQueue<DataUpdate> = SegQueue::new();
/// Called from background async tasks
pub fn enqueue_update(update: DataUpdate) {
PENDING_UPDATES.push(update);
SignalToUI::set_ui_signal(); // Wake UI thread
}
// In widget's handle_event:
impl Widget for MyWidget {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
// Poll for updates on Signal events
if let Event::Signal = event {
while let Some(update) = PENDING_UPDATES.pop() {
match update {
DataUpdate::NewItem { item } => {
self.items.push(item);
self.redraw(cx);
}
// ... handle other updates
}
}
}
}
}
```
## Startup Sequence
```rust
impl MatchEvent for App {
fn handle_startup(&mut self, cx: &mut Cx) {
// 1. Initialize logging
let _ = tracing_subscriber::fmt::try_init();
// 2. Initialize app data directory
let _app_data_dir = crate::app_data_dir();
// 3. Load persisted state
if let Err(e) = persistence::load_window_state(
self.ui.window(ids!(main_window)), cx
) {
error!("Failed to load window state: {}", e);
}
// 4. Update UI based on loaded state
self.update_ui_visibility(cx);
// 5. Start async runtime
let _rt_handle = crate::start_async_runtime().unwrap();
}
}
```
## Shutdown Sequence
```rust
impl AppMain for App {
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
if let Event::Shutdown = event {
// Save window geometry
let window_ref = self.ui.window(ids!(main_window));
if let Err(e) = persistence::save_window_state(window_ref, cx) {
error!("Failed to save window state: {e}");
}
// Save app state
if let Some(user_id) = current_user_id() {
if let Err(e) = persistence::save_app_state(
self.app_state.clone(), user_id
) {
error!("Failed to save app state: {e}");
}
}
}
// ... rest of event handling
}
}
```
## Best Practices
1. **Separation of Concerns**: Keep UI logic on the main thread, async operations in Tokio runtime
2. **Request/Response Pattern**: Use typed enums for requests and actions
3. **Lock-Free Updates**: Use `crossbeam::SegQueue` for high-frequency background updates
4. **SignalToUI**: Always call `SignalToUI::set_ui_signal()` after enqueueing updates
5. **Cx::post_action()**: Use for async task results that need action handling
6. **Scope::with_data()**: Pass shared state through widget tree
7. **Module Registration Order**: Register base widgets before dependent modules in `live_register()`
## Reference Files
- `references/tokio-integration.md` - Detailed Tokio runtime patterns (Robrix)
- `references/channel-patterns.md` - Channel communication patterns (Robrix)
- `references/moly-async-patterns.md` - Cross-platform async patterns (Moly)
- `PlatformSend` trait for native/WASM compatibility
- `UiRunner` for async defer operations
- `AbortOnDropHandle` for task cancellation
- `ThreadToken` for non-Send types on WASM
- `spawn()` platform-agnostic functionSignals
Information
- Repository
- ZhangHanDong/makepad-skills
- Author
- ZhangHanDong
- Last Sync
- 3/12/2026
- Repo Updated
- 3/11/2026
- Created
- 1/21/2026
Reviews (0)
No reviews yet. Be the first to review this skill!
Related Skills
upgrade-nodejs
Upgrading Bun's Self-Reported Node.js Version
cursorrules
CrewAI Development Rules
cn-check
Install and run the Continue CLI (`cn`) to execute AI agent checks on local code changes. Use when asked to "run checks", "lint with AI", "review my changes with cn", or set up Continue CI locally.
CLAUDE
CLAUDE.md
Related Guides
Bear Notes Claude Skill: Your AI-Powered Note-Taking Assistant
Learn how to use the bear-notes Claude skill. Complete guide with installation instructions and examples.
Mastering tmux with Claude: A Complete Guide to the tmux Claude Skill
Learn how to use the tmux Claude skill. Complete guide with installation instructions and examples.
OpenAI Whisper API Claude Skill: Complete Guide to AI-Powered Audio Transcription
Learn how to use the openai-whisper-api Claude skill. Complete guide with installation instructions and examples.