[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-8eee3749-d70e-43a2-a1cd-4956b0e694e1":3,"$fBsCRMOURgEgRkpynINVeydbmcbKPPWmb6O3EjrTxUDw":42},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":33},"8eee3749-d70e-43a2-a1cd-4956b0e694e1","robius-app-architecture","|","cat_coding_backend","mod_coding","sickn33,coding","---\nname: robius-app-architecture\ndescription: |\n  CRITICAL: Use for Robius app architecture patterns. Triggers on:\n  Tokio, async, submit_async_request, 异步, 架构,\n  SignalToUI, Cx::post_action, worker task,\n  app structure, MatchEvent, handle_startup\nrisk: unknown\nsource: community\n---\n\n# Robius App Architecture Skill\n\nBest practices for structuring Makepad applications based on the Robrix and Moly codebases - production applications built with Makepad and Robius framework.\n\n**Source codebases:**\n- **Robrix**: Matrix chat client - complex sync\u002Fasync with background subscriptions\n- **Moly**: AI chat application - cross-platform (native + WASM) with streaming APIs\n\n## When to Use\nUse this skill when:\n- Building a Makepad application with async backend integration\n- Designing sync\u002Fasync communication patterns in Makepad\n- Structuring a Robius-style application\n- Keywords: robrix, robius, makepad app structure, async makepad, tokio makepad\n\n## Production Patterns\n\nFor production-ready async patterns, see the `_base\u002F` directory:\n\n| Pattern | Description |\n|---------|-------------|\n| 08-async-loading | Async data loading with loading states |\n| 09-streaming-results | Incremental results with SignalToUI |\n| 13-tokio-integration | Full tokio runtime integration |\n\n## Core Architecture Pattern\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                     UI Thread (Makepad)                     │\n│  ┌─────────┐     ┌──────────┐     ┌──────────────────────┐ │\n│  │   App   │────▶│ WidgetRef │────▶│ Widget Tree (View)  │ │\n│  │ State   │     │    ui     │     │ Scope::with_data()  │ │\n│  └────┬────┘     └──────────┘     └──────────────────────┘ │\n│       │                                                     │\n│       │ submit_async_request()                              │\n│       ▼                                                     │\n│  ┌─────────────────┐          ┌─────────────────────────┐  │\n│  │ REQUEST_SENDER  │─────────▶│  Crossbeam SegQueue     │  │\n│  │ (MPSC Channel)  │          │  (Lock-free Updates)    │  │\n│  └─────────────────┘          └─────────────────────────┘  │\n└───────────────────────────────────┬─────────────────────────┘\n                                    │\n                    SignalToUI::set_ui_signal()\n                                    │\n┌───────────────────────────────────┴─────────────────────────┐\n│                   Tokio Runtime (Async)                      │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │           worker_task (Request Handler)               │   │\n│  │  - Receives Request from UI                           │   │\n│  │  - Spawns async tasks per request                     │   │\n│  │  - Posts actions back via Cx::post_action()           │   │\n│  └──────────────────────────────────────────────────────┘   │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │         Per-Item Subscriber Tasks                     │   │\n│  │  - Listens to external data stream                    │   │\n│  │  - Sends Update via crossbeam channel                 │   │\n│  │  - Calls SignalToUI::set_ui_signal() to wake UI       │   │\n│  └──────────────────────────────────────────────────────┘   │\n└─────────────────────────────────────────────────────────────┘\n```\n\n## App Structure\n\n### Top-Level App Definition\n\n```rust\nuse makepad_widgets::*;\n\nlive_design! {\n    use link::theme::*;\n    use link::widgets::*;\n\n    App = {{App}} {\n        ui: \u003CRoot>{\n            main_window = \u003CWindow> {\n                window: {inner_size: vec2(1280, 800), title: \"MyApp\"},\n                body = {\n                    \u002F\u002F Main content here\n                }\n            }\n        }\n    }\n}\n\napp_main!(App);\n\n#[derive(Live)]\npub struct App {\n    #[live] ui: WidgetRef,\n    #[rust] app_state: AppState,\n}\n\nimpl LiveRegister for App {\n    fn live_register(cx: &mut Cx) {\n        \u002F\u002F Order matters: register base widgets first\n        makepad_widgets::live_design(cx);\n        \u002F\u002F Then shared\u002Fcommon widgets\n        crate::shared::live_design(cx);\n        \u002F\u002F Then feature modules\n        crate::home::live_design(cx);\n    }\n}\n\nimpl LiveHook for App {\n    fn after_new_from_doc(&mut self, cx: &mut Cx) {\n        \u002F\u002F One-time initialization after widget tree is created\n    }\n}\n```\n\n### AppMain Implementation\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        \u002F\u002F Forward to MatchEvent trait\n        self.match_event(cx, event);\n\n        \u002F\u002F Pass AppState through widget tree via Scope\n        let scope = &mut Scope::with_data(&mut self.app_state);\n        self.ui.handle_event(cx, event, scope);\n    }\n}\n```\n\n## Tokio Runtime Integration\n\n### Static Runtime Initialization\n\n```rust\nuse std::sync::Mutex;\nuse tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};\n\nstatic TOKIO_RUNTIME: Mutex\u003COption\u003Ctokio::runtime::Runtime>> = Mutex::new(None);\nstatic REQUEST_SENDER: Mutex\u003COption\u003CUnboundedSender\u003CAppRequest>>> = Mutex::new(None);\n\npub fn start_async_runtime() -> Result\u003Ctokio::runtime::Handle> {\n    let (request_sender, request_receiver) = tokio::sync::mpsc::unbounded_channel();\n\n    let rt_handle = TOKIO_RUNTIME.lock().unwrap()\n        .get_or_insert_with(|| {\n            tokio::runtime::Runtime::new()\n                .expect(\"Failed to create Tokio runtime\")\n        })\n        .handle()\n        .clone();\n\n    \u002F\u002F Store sender for UI thread to use\n    *REQUEST_SENDER.lock().unwrap() = Some(request_sender);\n\n    \u002F\u002F Spawn the main worker task\n    rt_handle.spawn(worker_task(request_receiver));\n\n    Ok(rt_handle)\n}\n```\n\n### Request Submission Pattern\n\n```rust\npub enum AppRequest {\n    FetchData { id: String },\n    SendMessage { content: String },\n    \u002F\u002F ... other request types\n}\n\n\u002F\u002F\u002F Submit a request from UI thread to async runtime\npub fn submit_async_request(req: AppRequest) {\n    if let Some(sender) = REQUEST_SENDER.lock().unwrap().as_ref() {\n        sender.send(req)\n            .expect(\"BUG: worker task receiver has died!\");\n    }\n}\n```\n\n### Worker Task Pattern\n\n```rust\nasync fn worker_task(mut request_receiver: UnboundedReceiver\u003CAppRequest>) -> Result\u003C()> {\n    while let Some(request) = request_receiver.recv().await {\n        match request {\n            AppRequest::FetchData { id } => {\n                \u002F\u002F Spawn a new task for each request\n                let _task = tokio::spawn(async move {\n                    let result = fetch_data(&id).await;\n                    \u002F\u002F Post result back to UI thread\n                    Cx::post_action(DataFetchedAction { id, result });\n                });\n            }\n            AppRequest::SendMessage { content } => {\n                let _task = tokio::spawn(async move {\n                    match send_message(&content).await {\n                        Ok(()) => Cx::post_action(MessageSentAction::Success),\n                        Err(e) => Cx::post_action(MessageSentAction::Failed(e)),\n                    }\n                });\n            }\n        }\n    }\n    Ok(())\n}\n```\n\n## Lock-Free Update Queue Pattern\n\nFor high-frequency updates from background tasks:\n\n```rust\nuse crossbeam_queue::SegQueue;\nuse makepad_widgets::SignalToUI;\n\npub enum DataUpdate {\n    NewItem { item: Item },\n    ItemChanged { id: String, changes: Changes },\n    Status { message: String },\n}\n\nstatic PENDING_UPDATES: SegQueue\u003CDataUpdate> = SegQueue::new();\n\n\u002F\u002F\u002F Called from background async tasks\npub fn enqueue_update(update: DataUpdate) {\n    PENDING_UPDATES.push(update);\n    SignalToUI::set_ui_signal();  \u002F\u002F Wake UI thread\n}\n\n\u002F\u002F In widget's handle_event:\nimpl Widget for MyWidget {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {\n        \u002F\u002F Poll for updates on Signal events\n        if let Event::Signal = event {\n            while let Some(update) = PENDING_UPDATES.pop() {\n                match update {\n                    DataUpdate::NewItem { item } => {\n                        self.items.push(item);\n                        self.redraw(cx);\n                    }\n                    \u002F\u002F ... handle other updates\n                }\n            }\n        }\n    }\n}\n```\n\n## Startup Sequence\n\n```rust\nimpl MatchEvent for App {\n    fn handle_startup(&mut self, cx: &mut Cx) {\n        \u002F\u002F 1. Initialize logging\n        let _ = tracing_subscriber::fmt::try_init();\n\n        \u002F\u002F 2. Initialize app data directory\n        let _app_data_dir = crate::app_data_dir();\n\n        \u002F\u002F 3. Load persisted state\n        if let Err(e) = persistence::load_window_state(\n            self.ui.window(ids!(main_window)), cx\n        ) {\n            error!(\"Failed to load window state: {}\", e);\n        }\n\n        \u002F\u002F 4. Update UI based on loaded state\n        self.update_ui_visibility(cx);\n\n        \u002F\u002F 5. Start async runtime\n        let _rt_handle = crate::start_async_runtime().unwrap();\n    }\n}\n```\n\n## Shutdown Sequence\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        if let Event::Shutdown = event {\n            \u002F\u002F Save window geometry\n            let window_ref = self.ui.window(ids!(main_window));\n            if let Err(e) = persistence::save_window_state(window_ref, cx) {\n                error!(\"Failed to save window state: {e}\");\n            }\n\n            \u002F\u002F Save app state\n            if let Some(user_id) = current_user_id() {\n                if let Err(e) = persistence::save_app_state(\n                    self.app_state.clone(), user_id\n                ) {\n                    error!(\"Failed to save app state: {e}\");\n                }\n            }\n        }\n        \u002F\u002F ... rest of event handling\n    }\n}\n```\n\n## Best Practices\n\n1. **Separation of Concerns**: Keep UI logic on the main thread, async operations in Tokio runtime\n2. **Request\u002FResponse Pattern**: Use typed enums for requests and actions\n3. **Lock-Free Updates**: Use `crossbeam::SegQueue` for high-frequency background updates\n4. **SignalToUI**: Always call `SignalToUI::set_ui_signal()` after enqueueing updates\n5. **Cx::post_action()**: Use for async task results that need action handling\n6. **Scope::with_data()**: Pass shared state through widget tree\n7. **Module Registration Order**: Register base widgets before dependent modules in `live_register()`\n\n## Reference Files\n\n- `references\u002Ftokio-integration.md` - Detailed Tokio runtime patterns (Robrix)\n- `references\u002Fchannel-patterns.md` - Channel communication patterns (Robrix)\n- `references\u002Fmoly-async-patterns.md` - Cross-platform async patterns (Moly)\n  - `PlatformSend` trait for native\u002FWASM compatibility\n  - `UiRunner` for async defer operations\n  - `AbortOnDropHandle` for task cancellation\n  - `ThreadToken` for non-Send types on WASM\n  - `spawn()` platform-agnostic function\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,172,558,"2026-05-16 13:37:19",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"编程开发","coding","mdi-code-braces","代码生成、调试、审查，提升开发效率",2,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":25,"skillCount":32,"createdAt":26},"后端开发","backend","mdi-server","API、数据库、服务端架构",296,[34],{"id":35,"skillId":4,"version":36,"fileName":37,"fileSize":38,"filePath":39,"fileHash":40,"manifest":41,"createdAt":19},"894f5ebd-1206-41cb-82cf-e59246c48d64","1.0.0","robius-app-architecture.zip",3892,"uploads\u002Fskills\u002F8eee3749-d70e-43a2-a1cd-4956b0e694e1\u002Frobius-app-architecture.zip","344fcc689a35bacae8cab952ec39234230413d8e13d82f2b62308817ff5ae719","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":12573}]",{"code":43,"message":44,"data":45},200,"success",{"items":46,"stats":47,"page":50},[],{"averageRating":48,"totalRatings":48,"ratingCounts":49},0,[48,48,48,48,48],{"limit":51,"offset":48,"hasMore":52,"nextOffset":51,"ratedOnly":16},15,false]