{"id":"76ec95be-2cc2-4732-8be6-127a8aa41121","shortId":"QmJDNH","kind":"skill","title":"robius-state-management","tagline":"CRITICAL: Use for Robius state management patterns. Triggers on:\nAppState, persistence, theme switch, 状态管理,\nScope::with_data, save state, load state, serde,\n状态持久化, 主题切换","description":"# Robius State Management Skill\n\nBest practices for state management and persistence in Makepad applications based on Robrix and Moly codebases.\n\n**Source codebases:**\n- **Robrix**: Matrix chat client - AppState, SelectedRoom, persistence via serde\n- **Moly**: AI chat application - Central Store pattern, async initialization, Preferences\n\n## When to Use\nUse this skill when:\n- Designing application state structure\n- Implementing state persistence\n- Passing state through widget tree\n- Managing UI state across sessions\n- Keywords: app state, makepad state, persistence, Scope::with_data, save state, load state\n\n## Production Patterns\n\nFor production-ready state management patterns, see the `_base/` directory:\n\n| Pattern | Description |\n|---------|-------------|\n| 06-global-registry | Global widget registry with Cx::set_global |\n| 07-radio-navigation | Tab-style navigation with radio buttons |\n| 10-state-machine | Enum-based state machine widgets |\n| 11-theme-switching | Multi-theme support with apply_over |\n| 12-local-persistence | Save/load user preferences |\n\n## AppState Structure\n\n### Core State Definition\n\n```rust\nuse serde::{Serialize, Deserialize};\nuse std::collections::HashMap;\nuse matrix_sdk::ruma::OwnedRoomId;\n\n/// App-wide state that is stored persistently across multiple app runs\n/// and shared/updated across various parts of the app.\n#[derive(Clone, Default, Debug, Serialize, Deserialize)]\npub struct AppState {\n    /// The currently-selected room\n    pub selected_room: Option<SelectedRoom>,\n\n    /// Saved UI layout state for main view\n    pub saved_layout_state: SavedLayoutState,\n\n    /// Per-item saved states (e.g., per-space dock layouts)\n    pub saved_state_per_item: HashMap<OwnedRoomId, SavedLayoutState>,\n\n    /// Whether a user is currently logged in\n    #[serde(skip)]  // Don't persist login state\n    pub logged_in: bool,\n}\n\n/// Represents a currently selected item\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub enum SelectedRoom {\n    JoinedRoom { room_name_id: RoomNameId },\n    InvitedRoom { room_name_id: RoomNameId },\n    Space { space_name_id: RoomNameId },\n}\n\nimpl SelectedRoom {\n    pub fn room_id(&self) -> &OwnedRoomId {\n        match self {\n            Self::JoinedRoom { room_name_id } => room_name_id.room_id(),\n            Self::InvitedRoom { room_name_id } => room_name_id.room_id(),\n            Self::Space { space_name_id } => space_name_id.room_id(),\n        }\n    }\n\n    /// Upgrade from invited to joined state\n    pub fn upgrade_invite_to_joined(&mut self, room_id: &RoomId) -> bool {\n        match self {\n            Self::InvitedRoom { room_name_id } if room_name_id.room_id() == room_id => {\n                let name = room_name_id.clone();\n                *self = Self::JoinedRoom { room_name_id: name };\n                true\n            }\n            _ => false,\n        }\n    }\n}\n\n// Equality based on room_id only\nimpl PartialEq for SelectedRoom {\n    fn eq(&self, other: &Self) -> bool {\n        self.room_id() == other.room_id()\n    }\n}\nimpl Eq for SelectedRoom {}\n```\n\n### Layout/Dock State Persistence\n\n```rust\n/// A snapshot of UI layout state for restoration\n#[derive(Clone, Default, Debug, Serialize, Deserialize)]\npub struct SavedLayoutState {\n    /// All items contained in the layout, keyed by ID\n    pub layout_items: HashMap<LiveIdSerde, LayoutItemSerde>,\n\n    /// Items currently open, keyed by ID\n    pub open_items: HashMap<LiveIdSerde, SelectedRoom>,\n\n    /// Order items were opened (chronological)\n    pub item_order: Vec<SelectedRoom>,\n\n    /// Currently selected item when state was saved\n    pub selected_item: Option<SelectedRoom>,\n}\n\n/// Serializable wrapper for LiveId\n#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]\npub struct LiveIdSerde(pub u64);\n\nimpl From<LiveId> for LiveIdSerde {\n    fn from(id: LiveId) -> Self {\n        Self(id.0)\n    }\n}\n\nimpl From<LiveIdSerde> for LiveId {\n    fn from(s: LiveIdSerde) -> Self {\n        LiveId(s.0)\n    }\n}\n```\n\n## State Propagation via Scope\n\n### Passing State Through Widget Tree\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        // Forward to MatchEvent\n        self.match_event(cx, event);\n\n        // Create Scope with AppState data\n        let scope = &mut Scope::with_data(&mut self.app_state);\n\n        // Pass to widget tree - all children can access AppState\n        self.ui.handle_event(cx, event, scope);\n    }\n}\n```\n\n### Accessing State in Child Widgets\n\n```rust\nimpl Widget for RoomScreen {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {\n        // Access AppState from scope\n        if let Some(app_state) = scope.data.get::<AppState>() {\n            if let Some(selected) = &app_state.selected_room {\n                self.update_for_room(cx, selected);\n            }\n        }\n\n        self.view.handle_event(cx, event, scope);\n    }\n}\n```\n\n### Modifying State\n\n```rust\nimpl Widget for RoomsList {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {\n        // Mutable access to AppState\n        if let Some(app_state) = scope.data.get_mut::<AppState>() {\n            if self.selection_changed {\n                app_state.selected_room = self.get_selected();\n            }\n        }\n    }\n}\n```\n\n## Persistence Layer\n\n### File Paths\n\n```rust\nuse std::path::{Path, PathBuf};\n\nconst LATEST_APP_STATE_FILE_NAME: &str = \"latest_app_state.json\";\nconst WINDOW_GEOM_STATE_FILE_NAME: &str = \"window_geom_state.json\";\n\n/// Get user-specific persistent state directory\nfn persistent_state_dir(user_id: &UserId) -> PathBuf {\n    app_data_dir()\n        .join(\"users\")\n        .join(user_id.to_string().replace(':', \"_\"))\n}\n\n/// Get app-wide data directory\nfn app_data_dir() -> &'static Path {\n    // Platform-specific app data location\n    static APP_DATA_DIR: OnceLock<PathBuf> = OnceLock::new();\n    APP_DATA_DIR.get_or_init(|| {\n        dirs::data_dir()\n            .unwrap_or_else(|| PathBuf::from(\".\"))\n            .join(\"myapp\")\n    })\n}\n```\n\n### Saving State\n\n```rust\nuse std::io::Write;\n\npub fn save_app_state(\n    app_state: AppState,\n    user_id: OwnedUserId,\n) -> anyhow::Result<()> {\n    let file = std::fs::File::create(\n        persistent_state_dir(&user_id).join(LATEST_APP_STATE_FILE_NAME)\n    )?;\n    let mut writer = std::io::BufWriter::new(file);\n    serde_json::to_writer(&mut writer, &app_state)?;\n    writer.flush()?;\n    log!(\"Successfully saved app state to persistent storage.\");\n    Ok(())\n}\n\n/// Save window geometry state (user-agnostic)\npub fn save_window_state(window_ref: WindowRef, cx: &Cx) -> anyhow::Result<()> {\n    let inner_size = window_ref.get_inner_size(cx);\n    let position = window_ref.get_position(cx);\n    let window_geom = WindowGeomState {\n        inner_size: (inner_size.x, inner_size.y),\n        position: (position.x, position.y),\n        is_fullscreen: window_ref.is_fullscreen(cx),\n    };\n    std::fs::write(\n        app_data_dir().join(WINDOW_GEOM_STATE_FILE_NAME),\n        serde_json::to_string(&window_geom)?,\n    )?;\n    Ok(())\n}\n```\n\n### Loading State\n\n```rust\n/// Load app state with graceful fallback\npub async fn load_app_state(user_id: &UserId) -> anyhow::Result<AppState> {\n    let state_path = persistent_state_dir(user_id).join(LATEST_APP_STATE_FILE_NAME);\n\n    // Read file\n    let file_bytes = match tokio::fs::read(&state_path).await {\n        Ok(fb) => fb,\n        Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n            log!(\"No saved app state found, using default.\");\n            return Ok(AppState::default());\n        }\n        Err(e) => return Err(e.into()),\n    };\n\n    // Deserialize with fallback\n    match serde_json::from_slice(&file_bytes) {\n        Ok(app_state) => {\n            log!(\"Successfully loaded app state.\");\n            Ok(app_state)\n        }\n        Err(e) => {\n            error!(\"Failed to deserialize: {e}. May be incompatible format.\");\n\n            // Backup old file\n            let backup_path = state_path.with_extension(\"json.bak\");\n            if let Err(backup_err) = tokio::fs::rename(&state_path, &backup_path).await {\n                error!(\"Failed to backup old state: {}\", backup_err);\n            } else {\n                log!(\"Old state backed up to: {:?}\", backup_path);\n            }\n\n            log!(\"Using default app state.\");\n            Ok(AppState::default())\n        }\n    }\n}\n\n/// Load window geometry (synchronous, on UI thread)\npub fn load_window_state(window_ref: WindowRef, cx: &mut Cx) -> anyhow::Result<()> {\n    let file = match std::fs::File::open(app_data_dir().join(WINDOW_GEOM_STATE_FILE_NAME)) {\n        Ok(file) => file,\n        Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),\n        Err(e) => return Err(e.into()),\n    };\n\n    let window_geom: WindowGeomState = serde_json::from_reader(file)?;\n    log!(\"Restoring window geometry: {window_geom:?}\");\n\n    window_ref.configure_window(\n        cx,\n        dvec2(window_geom.inner_size.0, window_geom.inner_size.1),\n        dvec2(window_geom.position.0, window_geom.position.1),\n        window_geom.is_fullscreen,\n        \"MyApp\".to_string(),\n    );\n    Ok(())\n}\n```\n\n### Startup/Shutdown Integration\n\n```rust\nimpl MatchEvent for App {\n    fn handle_startup(&mut self, cx: &mut Cx) {\n        // Load window geometry (sync, on UI thread)\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        // Trigger async app state load\n        let user_id = get_current_user_id();\n        tokio::spawn(async move {\n            match persistence::load_app_state(&user_id).await {\n                Ok(app_state) => {\n                    Cx::post_action(AppStateAction::RestoreFromPersistence(app_state));\n                    SignalToUI::set_ui_signal();\n                }\n                Err(e) => error!(\"Failed to load app state: {}\", e),\n            }\n        });\n    }\n}\n\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        if let Event::Shutdown = event {\n            // Save window state (sync)\n            if let Err(e) = persistence::save_window_state(\n                self.ui.window(ids!(main_window)), cx\n            ) {\n                error!(\"Failed to save window state: {e}\");\n            }\n\n            // Save app state (sync)\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        // ...\n    }\n}\n```\n\n## Thread-Local State (UI-Only)\n\n```rust\nuse std::{cell::RefCell, rc::Rc, collections::HashMap};\n\nthread_local! {\n    /// UI-thread-only cache\n    static UI_CACHE: Rc<RefCell<HashMap<OwnedRoomId, CachedData>>> =\n        Rc::new(RefCell::new(HashMap::new()));\n}\n\n/// Get cache reference (requires Cx to ensure UI thread)\npub fn get_ui_cache(_cx: &mut Cx) -> Rc<RefCell<HashMap<OwnedRoomId, CachedData>>> {\n    UI_CACHE.with(Rc::clone)\n}\n\n/// Clear cache (requires Cx)\npub fn clear_ui_cache(_cx: &mut Cx) {\n    UI_CACHE.with(|cache| cache.borrow_mut().clear());\n}\n```\n\n## Best Practices\n\n1. **Separate persistent vs runtime state**: Use `#[serde(skip)]` for non-persistent fields\n2. **Use Scope::with_data() for tree propagation**: Don't pass state through widget refs\n3. **Graceful deserialization fallback**: Handle format changes between versions\n4. **Backup old state files**: Preserve user data when format changes\n5. **User-specific persistent paths**: Separate state per user account\n6. **Sync window state, async app state**: Window geometry loads sync on UI thread\n7. **Thread-local for UI-only caches**: Use `thread_local!` with Cx parameter guard\n\n## Reference Files\n\n- `references/persistence-patterns.md` - Additional persistence patterns (Robrix)\n- `references/state-structures.md` - State structure examples (Robrix)\n- `references/moly-state-patterns.md` - Moly-specific patterns\n  - Central Store struct containing all state\n  - Async Store initialization with `load_into_app()`\n  - App state check pattern (early return if not loaded)\n  - Submodule state managers (Search, Downloads, Chats)\n  - Provider syncing status tracking\n  - Store action forwarding to submodules\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.","tags":["robius","state","management","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding"],"capabilities":["skill","source-sickn33","skill-robius-state-management","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/robius-state-management","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34583 github stars · SKILL.md body (12,734 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T18:52:09.140Z","embedding":null,"createdAt":"2026-04-18T21:43:40.779Z","updatedAt":"2026-04-22T18:52:09.140Z","lastSeenAt":"2026-04-22T18:52:09.140Z","tsv":"'0':1107 '06':122 '07':133 '1':1109,1359 '10':144 '11':154 '12':165 '2':1373 '3':1388 '4':1397 '5':1408 '6':1419 '7':1433 'access':562,569,592,639 'account':1418 'across':92,199,205 'action':1187,1499 'addit':1452 'agnost':813 'ai':61 'anyhow':762,824,893,1046 'app':95,192,201,210,523,599,645,668,697,708,713,721,725,754,756,777,795,801,859,879,888,905,935,960,965,968,1023,1055,1122,1160,1177,1183,1190,1202,1208,1249,1266,1275,1424,1478,1479 'app-wid':191,707 'app_data_dir.get':731 'app_state.selected':606,652 'appli':163 'applic':42,63,78 'appmain':521,1206 'appstat':14,55,172,219,544,563,593,641,758,942,1026 'appstateact':1188 'ask':1536 'async':67,885,1159,1172,1423,1472 'await':920,1002,1181 'back':1015 'backup':981,985,993,1000,1006,1009,1018,1398 'base':43,118,150,380 'best':33,1357 'bool':277,354,394 'boundari':1544 'bufwrit':786 'button':143 'byte':913,958 'cach':1300,1303,1316,1328,1341,1348,1353,1441 'cache.borrow':1354 'cacheddata':1308,1336 'cell':1288 'central':64,1466 'chang':651,1394,1407 'chat':53,62,1493 'check':1481 'child':572 'children':560 'chronolog':455 'clarif':1538 'clear':1340,1346,1356,1511 'client':54 'clone':212,284,416,476,1339 'codebas':48,50 'collect':184,1292 'const':666,674 'contain':426,1469 'core':174 'creat':541,769 'criteria':1547 'critic':5 'current':222,264,280,440,460,1167,1257 'currently-select':221 'cx':130,529,531,539,566,584,586,611,615,630,632,822,823,832,837,855,1043,1045,1099,1128,1130,1150,1185,1214,1216,1240,1319,1329,1331,1343,1349,1351,1446 'data':21,102,545,551,698,710,714,722,726,735,860,1056,1377,1404 'debug':214,285,418,477 'default':213,417,939,943,1022,1027 'definit':176 'deriv':211,283,415,475 'describ':1515 'descript':121 'deseri':181,216,287,420,482,949,975,1390 'design':77 'dir':692,699,715,727,734,736,772,861,900,1057 'directori':119,688,711 'dock':250 'download':1492 'dvec2':1100,1105 'e':925,945,971,976,1068,1078,1141,1157,1197,1204,1231,1247,1263,1277 'e.g':246 'e.into':948,1081 'e.kind':927,1070 'earli':1483 'els':739,1011 'ensur':1321 'enum':149,289 'enum-bas':148 'environ':1527 'environment-specif':1526 'eq':390,400,479 'equal':379 'err':924,944,947,970,992,994,1010,1067,1077,1080,1140,1196,1230,1262 'error':972,1003,1151,1198,1241,1271 'errorkind':930,1073 'event':526,532,533,538,540,565,567,581,587,588,614,616,627,633,634,1211,1217,1218,1221,1223 'exampl':1459 'expert':1532 'extens':988 'fail':973,1004,1152,1199,1242,1272 'fallback':883,951,1391 'fals':378 'fb':922,923 'field':1372 'file':658,670,678,765,768,779,788,866,907,910,912,957,983,1049,1053,1062,1065,1066,1090,1401,1450 'fn':309,344,389,492,503,524,579,625,689,712,752,815,886,1036,1123,1209,1325,1345 'format':980,1393,1406 'forward':534,1500 'found':937 'fs':767,857,916,996,1052 'fullscreen':852,854,1111 'geom':676,840,864,873,1060,1084,1096 'geometri':809,1030,1094,1133,1427 'get':682,706,1166,1315,1326 'global':124,126,132 'global-registri':123 'grace':882,1389 'guard':1448 'handl':525,580,626,1124,1210,1392 'hash':478 'hashmap':185,257,436,448,1293,1306,1313,1334 'id':294,299,304,311,320,322,327,329,334,336,352,361,364,366,375,383,396,398,432,444,494,694,760,774,891,902,1147,1165,1169,1180,1237,1256,1259,1270 'id.0':498 'impl':306,385,399,488,499,520,575,621,1119,1205 'implement':81 'incompat':979 'init':733 'initi':68,1474 'inner':827,830,842,844,846 'input':1541 'integr':1117 'invit':339,346 'invitedroom':296,324,358 'io':749,785,929,1072 'item':243,256,282,425,435,439,447,452,457,462,469 'join':341,348,700,702,742,775,862,903,1058 'joinedroom':291,317,372 'json':790,869,954,1087 'json.bak':989 'key':430,442 'keyword':94 'latest':667,776,904 'latest_app_state.json':673 'layer':657 'layout':231,238,251,411,429,434 'layout/dock':403 'layoutitemserd':438 'let':367,546,597,603,643,764,781,826,833,838,895,911,984,991,1048,1082,1139,1163,1220,1229,1253,1261 'limit':1503 'liveid':474,495,502,508 'liveidserd':437,449,485,491,506 'load':24,105,875,878,887,964,1028,1037,1131,1143,1154,1162,1176,1201,1428,1476,1487 'local':167,1280,1295,1436,1444 'local-persist':166 'locat':723 'log':265,275,798,932,962,1012,1020,1091 'login':272 'machin':147,152 'main':234,1148,1238 'makepad':41,97 'manag':4,10,31,37,89,114,1490 'match':314,355,914,952,1050,1174,1512 'matchev':536,1120 'matrix':52,187 'may':977 'miss':1549 'modifi':618 'moli':47,60,1463 'moly-specif':1462 'move':1173 'multi':159 'multi-them':158 'multipl':200 'mut':349,527,530,548,552,582,585,590,628,631,636,648,782,793,1044,1126,1129,1212,1215,1330,1350,1355 'mutabl':638 'myapp':743,1112 'name':293,298,303,319,326,333,360,368,374,376,671,679,780,867,908,1063 'navig':136,140 'new':730,787,1310,1312,1314 'non':1370 'non-persist':1369 'notfound':931,1074 'ok':806,874,921,941,959,967,1025,1064,1076,1115,1182 'old':982,1007,1013,1399 'oncelock':728,729 'open':441,446,454,1054 'option':228,470 'order':451,458 'other.room':397 'output':1521 'ownedroomid':190,258,313,1307,1335 'owneduserid':761 'paramet':1447 'part':207 'partialeq':386,480 'pass':84,514,555,1383 'path':659,663,664,717,897,919,986,999,1001,1019,1413 'pathbuf':665,696,740 'pattern':11,66,108,115,120,1454,1465,1482 'per':242,248,255,1416 'per-item':241 'per-spac':247 'permiss':1542 'persist':15,39,57,83,99,168,198,271,405,656,686,690,770,804,898,1142,1175,1232,1264,1361,1371,1412,1453 'platform':719 'platform-specif':718 'posit':834,836,848 'position.x':849 'position.y':850 'post':1186 'practic':34,1358 'prefer':69,171 'preserv':1402 'product':107,111 'production-readi':110 'propag':511,1380 'provid':1494 'pub':217,225,236,252,274,288,308,343,421,433,445,456,467,483,486,751,814,884,1035,1324,1344 'radio':135,142 'radio-navig':134 'rc':1290,1291,1304,1309,1332,1338 'read':909,917 'reader':1089 'readi':112 'ref':820,1041,1387 'refcel':1289,1305,1311,1333 'refer':1317,1449 'references/moly-state-patterns.md':1461 'references/persistence-patterns.md':1451 'references/state-structures.md':1456 'registri':125,128 'renam':997 'replac':705 'repres':278 'requir':1318,1342,1540 'restor':414,1092 'restorefrompersist':1189 'result':763,825,894,1047 'return':940,946,1075,1079,1484 'review':1533 'robius':2,8,29 'robius-state-manag':1 'robrix':45,51,1455,1460 'room':224,227,292,297,310,318,325,351,359,365,373,382,607,610,653 'room_name_id.clone':369 'room_name_id.room':321,328,363 'roomid':353 'roomnameid':295,300,305 'roomscreen':578 'roomslist':624 'ruma':189 'run':202 'runtim':1363 'rust':177,406,519,574,620,660,746,877,1118,1285 's.0':509 'safeti':1543 'save':22,103,229,237,244,253,466,744,753,800,807,816,934,1224,1233,1244,1248,1265,1274 'save/load':169 'savedlayoutst':240,259,423 'scope':19,100,513,542,547,549,568,589,591,595,617,635,637,1375,1514 'scope.data.get':601,647 'sdk':188 'search':1491 'see':116 'select':223,226,281,461,468,605,612,655 'selectedroom':56,290,307,388,402,450 'self':312,315,316,323,330,350,356,357,370,371,391,393,496,497,507,528,583,629,1127,1213 'self.app':553 'self.app_state.clone':1268 'self.get':654 'self.match':537 'self.room':395 'self.selection':650 'self.ui.handle':564 'self.ui.window':1146,1236 'self.update':608 'self.view.handle':613 'separ':1360,1414 'serd':26,59,179,267,789,868,953,1086,1366 'serial':180,215,286,419,481 'serializ':471 'session':93 'set':131,1193 'shared/updated':204 'shutdown':1222 'signal':1195 'signaltoui':1192 'size':828,831,843 'size.0':1102 'size.1':1104 'size.x':845 'size.y':847 'skill':32,75,1506 'skill-robius-state-management' 'skip':268,1367 'slice':956 'snapshot':408 'sourc':49 'source-sickn33' 'space':249,301,302,331,332 'space_name_id.room':335 'spawn':1171 'specif':685,720,1411,1464,1528 'startup':1125 'startup/shutdown':1116 'state':3,9,23,25,30,36,79,82,85,91,96,98,104,106,113,146,151,175,194,232,239,245,254,273,342,404,412,464,510,515,554,570,600,619,646,669,677,687,691,745,755,757,771,778,796,802,810,818,865,876,880,889,896,899,906,918,936,961,966,969,998,1008,1014,1024,1039,1061,1145,1156,1161,1178,1184,1191,1203,1226,1235,1246,1250,1267,1276,1281,1364,1384,1400,1415,1422,1425,1457,1471,1480,1489 'state-machin':145 'state_path.with':987 'static':716,724,1301 'status':1496 'std':183,662,748,766,784,856,928,1051,1071,1287 'stop':1534 'storag':805 'store':65,197,1467,1473,1498 'str':672,680 'string':704,871,1114 'struct':218,422,484,1468 'structur':80,173,1458 'style':139 'submodul':1488,1502 'substitut':1524 'success':799,963,1546 'support':161 'switch':17,157 'sync':1134,1227,1251,1420,1429,1495 'synchron':1031 'tab':138 'tab-styl':137 'task':1510 'test':1530 'theme':16,156,160 'theme-switch':155 'thread':1034,1137,1279,1294,1298,1323,1432,1435,1443 'thread-loc':1278,1434 'tokio':915,995,1170 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'track':1497 'treat':1519 'tree':88,518,558,1379 'trigger':12,1158 'true':377 'u64':487 'ui':90,230,410,1033,1136,1194,1283,1297,1302,1322,1327,1347,1431,1439 'ui-on':1282,1438 'ui-thread-on':1296 'ui_cache.with':1337,1352 'unwrap':737 'upgrad':337,345 'use':6,72,73,178,182,186,661,747,938,1021,1286,1365,1374,1442,1504 'user':170,262,684,693,701,759,773,812,890,901,1164,1168,1179,1255,1258,1269,1403,1410,1417 'user-agnost':811 'user-specif':683,1409 'user_id.to':703 'userid':695,892 'valid':1529 'various':206 'vec':459 'version':1396 'via':58,512 'view':235 'vs':1362 'whether':260 'wide':193,709 'widget':87,127,153,517,557,573,576,622,1386 'window':675,808,817,819,839,863,872,1029,1038,1040,1059,1083,1093,1095,1098,1132,1144,1149,1155,1225,1234,1239,1245,1421,1426 'window_geom.inner':1101,1103 'window_geom.is':1110 'window_geom.position':1106,1108 'window_geom_state.json':681 'window_ref.configure':1097 'window_ref.get':829,835 'window_ref.is':853 'windowgeomst':841,1085 'windowref':821,1042 'wrapper':472 'write':750,858 'writer':783,792,794 'writer.flush':797 '主题切换':28 '状态持久化':27 '状态管理':18","prices":[{"id":"f3545ea0-9c1f-494d-8ee2-2df9c28f269c","listingId":"76ec95be-2cc2-4732-8be6-127a8aa41121","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:43:40.779Z"}],"sources":[{"listingId":"76ec95be-2cc2-4732-8be6-127a8aa41121","source":"github","sourceId":"sickn33/antigravity-awesome-skills/robius-state-management","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/robius-state-management","isPrimary":false,"firstSeenAt":"2026-04-18T21:43:40.779Z","lastSeenAt":"2026-04-22T18:52:09.140Z"}],"details":{"listingId":"76ec95be-2cc2-4732-8be6-127a8aa41121","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"robius-state-management","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34583,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-22T06:40:00Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"9664d729a510dfc3e400242e8022d777af59bd12","skill_md_path":"skills/robius-state-management/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/robius-state-management"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"robius-state-management","description":"CRITICAL: Use for Robius state management patterns. Triggers on:\nAppState, persistence, theme switch, 状态管理,\nScope::with_data, save state, load state, serde,\n状态持久化, 主题切换"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/robius-state-management"},"updatedAt":"2026-04-22T18:52:09.140Z"}}