opencode-skills-collection 4.0.69 → 4.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +39 -1
  2. package/bundled-skills/api-integration-architect/SKILL.md +241 -0
  3. package/bundled-skills/apify-generate-output-schema/SKILL.md +438 -0
  4. package/bundled-skills/apify-integration-development/SKILL.md +168 -0
  5. package/bundled-skills/apify-integration-development/references/ai-framework-package.md +158 -0
  6. package/bundled-skills/apify-integration-development/references/ai-harness-plugin.md +192 -0
  7. package/bundled-skills/apify-integration-development/references/sdk-integration.md +236 -0
  8. package/bundled-skills/apify-integration-development/references/workflow-automation.md +163 -0
  9. package/bundled-skills/architecture-review/README.md +42 -0
  10. package/bundled-skills/architecture-review/SKILL.md +77 -0
  11. package/bundled-skills/architecture-review/examples.md +11 -0
  12. package/bundled-skills/architecture-review/reference/best-practices.md +7 -0
  13. package/bundled-skills/architecture-review/reference/capabilities.md +20 -0
  14. package/bundled-skills/architecture-review/reference/fallbacks.md +11 -0
  15. package/bundled-skills/architecture-review/reference/graph.md +15 -0
  16. package/bundled-skills/architecture-review/reference/mcp.md +14 -0
  17. package/bundled-skills/architecture-review/reference/workflow.md +15 -0
  18. package/bundled-skills/architecture-review/templates/architecture-review.md +21 -0
  19. package/bundled-skills/code-review-sensei/SKILL.md +177 -0
  20. package/bundled-skills/codebase-onboarding/README.md +42 -0
  21. package/bundled-skills/codebase-onboarding/SKILL.md +77 -0
  22. package/bundled-skills/codebase-onboarding/examples.md +11 -0
  23. package/bundled-skills/codebase-onboarding/reference/best-practices.md +7 -0
  24. package/bundled-skills/codebase-onboarding/reference/capabilities.md +20 -0
  25. package/bundled-skills/codebase-onboarding/reference/fallbacks.md +11 -0
  26. package/bundled-skills/codebase-onboarding/reference/graph.md +15 -0
  27. package/bundled-skills/codebase-onboarding/reference/mcp.md +14 -0
  28. package/bundled-skills/codebase-onboarding/reference/workflow.md +15 -0
  29. package/bundled-skills/codebase-onboarding/templates/repository-onboarding.md +21 -0
  30. package/bundled-skills/connection-auth-rules/SKILL.md +199 -0
  31. package/bundled-skills/connection-auth-rules/fetch_schema.py +320 -0
  32. package/bundled-skills/dependency-analysis/README.md +42 -0
  33. package/bundled-skills/dependency-analysis/SKILL.md +76 -0
  34. package/bundled-skills/dependency-analysis/examples.md +11 -0
  35. package/bundled-skills/dependency-analysis/reference/best-practices.md +7 -0
  36. package/bundled-skills/dependency-analysis/reference/capabilities.md +20 -0
  37. package/bundled-skills/dependency-analysis/reference/fallbacks.md +11 -0
  38. package/bundled-skills/dependency-analysis/reference/graph.md +15 -0
  39. package/bundled-skills/dependency-analysis/reference/mcp.md +14 -0
  40. package/bundled-skills/dependency-analysis/reference/workflow.md +15 -0
  41. package/bundled-skills/dependency-analysis/templates/dependency-review.md +21 -0
  42. package/bundled-skills/devops-pipeline-builder/SKILL.md +200 -0
  43. package/bundled-skills/eas-app-stores/SKILL.md +197 -0
  44. package/bundled-skills/eas-app-stores/agents/openai.yaml +4 -0
  45. package/bundled-skills/eas-app-stores/references/app-store-metadata.md +497 -0
  46. package/bundled-skills/eas-app-stores/references/ios-app-store.md +376 -0
  47. package/bundled-skills/eas-app-stores/references/native-ios.md +167 -0
  48. package/bundled-skills/eas-app-stores/references/play-store.md +244 -0
  49. package/bundled-skills/eas-app-stores/references/testflight.md +62 -0
  50. package/bundled-skills/eas-app-stores/references/workflows.md +120 -0
  51. package/bundled-skills/eas-hosting/SKILL.md +448 -0
  52. package/bundled-skills/eas-hosting/agents/openai.yaml +4 -0
  53. package/bundled-skills/eas-observe/SKILL.md +75 -0
  54. package/bundled-skills/eas-observe/agents/openai.yaml +4 -0
  55. package/bundled-skills/eas-observe/references/metrics.md +98 -0
  56. package/bundled-skills/eas-observe/references/queries.md +403 -0
  57. package/bundled-skills/eas-observe/references/setup.md +476 -0
  58. package/bundled-skills/eas-observe/references/third-party.md +136 -0
  59. package/bundled-skills/eas-simulator/SKILL.md +251 -0
  60. package/bundled-skills/eas-simulator/agents/openai.yaml +4 -0
  61. package/bundled-skills/eas-simulator/references/controllers.md +135 -0
  62. package/bundled-skills/eas-simulator/references/run-your-app.md +240 -0
  63. package/bundled-skills/eas-simulator/references/troubleshooting.md +47 -0
  64. package/bundled-skills/eas-workflows/SKILL.md +119 -0
  65. package/bundled-skills/eas-workflows/agents/openai.yaml +4 -0
  66. package/bundled-skills/eas-workflows/scripts/fetch.js +109 -0
  67. package/bundled-skills/expo-animation/LICENSE +21 -0
  68. package/bundled-skills/expo-animation/RECIPES.md +385 -0
  69. package/bundled-skills/expo-animation/SKILL.md +295 -0
  70. package/bundled-skills/expo-animation/agents/openai.yaml +4 -0
  71. package/bundled-skills/fact-check-x-unified/SKILL.md +178 -0
  72. package/bundled-skills/fact-check-x-unified/agents/openai.yaml +4 -0
  73. package/bundled-skills/fact-check-x-unified/references/acceptance-criteria.md +44 -0
  74. package/bundled-skills/fact-check-x-unified/references/contracts.md +39 -0
  75. package/bundled-skills/fact-check-x-unified/scripts/common.py +31 -0
  76. package/bundled-skills/fact-check-x-unified/scripts/fact_check_x.py +1832 -0
  77. package/bundled-skills/fact-check-x-unified/scripts/trusted_search_config.py +324 -0
  78. package/bundled-skills/fact-check-x-unified/tests/anchor_downgrade_test.py +90 -0
  79. package/bundled-skills/fact-check-x-unified/tests/multi_platform_test.py +369 -0
  80. package/bundled-skills/fact-check-x-unified/tests/smoke_test.py +740 -0
  81. package/bundled-skills/fact-check-x-unified/tests/stage_checkpoint_test.py +103 -0
  82. package/bundled-skills/fact-check-x-unified/tests/trusted_search_config_test.py +156 -0
  83. package/bundled-skills/gpt-taste/SKILL.md +8 -1
  84. package/bundled-skills/hf-cli/SKILL.md +263 -0
  85. package/bundled-skills/huggingface-community-evals/SKILL.md +228 -0
  86. package/bundled-skills/huggingface-community-evals/examples/.env.example +3 -0
  87. package/bundled-skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md +101 -0
  88. package/bundled-skills/huggingface-community-evals/scripts/inspect_eval_uv.py +104 -0
  89. package/bundled-skills/huggingface-community-evals/scripts/inspect_vllm_uv.py +306 -0
  90. package/bundled-skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py +297 -0
  91. package/bundled-skills/huggingface-datasets/SKILL.md +130 -0
  92. package/bundled-skills/jev-social/SKILL.md +182 -0
  93. package/bundled-skills/longbridge-derivatives/SKILL.md +117 -0
  94. package/bundled-skills/longbridge-derivatives/references/option.md +36 -0
  95. package/bundled-skills/longbridge-derivatives/references/options-advanced.md +101 -0
  96. package/bundled-skills/longbridge-derivatives/references/options-pnl.md +74 -0
  97. package/bundled-skills/longbridge-derivatives/references/options-strategy.md +82 -0
  98. package/bundled-skills/longbridge-derivatives/references/options-volatility.md +70 -0
  99. package/bundled-skills/longbridge-derivatives/references/warrant.md +12 -0
  100. package/bundled-skills/longbridge-quant/SKILL.md +151 -0
  101. package/bundled-skills/longbridge-quant/references/correlation.md +51 -0
  102. package/bundled-skills/longbridge-quant/references/execution-model.md +68 -0
  103. package/bundled-skills/longbridge-quant/references/factor-research.md +95 -0
  104. package/bundled-skills/longbridge-quant/references/factor-screen.md +101 -0
  105. package/bundled-skills/longbridge-quant/references/hedging.md +136 -0
  106. package/bundled-skills/longbridge-quant/references/ml-strategy.md +77 -0
  107. package/bundled-skills/longbridge-quant/references/multifactor.md +68 -0
  108. package/bundled-skills/longbridge-quant/references/pairs-trading.md +61 -0
  109. package/bundled-skills/longbridge-quant/references/quant-cli.md +133 -0
  110. package/bundled-skills/longbridge-quant/references/quant-stats.md +150 -0
  111. package/bundled-skills/longbridge-quant/references/seasonality.md +50 -0
  112. package/bundled-skills/longbridge-quant/references/strategy-optimizer.md +68 -0
  113. package/bundled-skills/longbridge-quant/references/volatility-strategy.md +52 -0
  114. package/bundled-skills/longbridge-research/SKILL.md +187 -0
  115. package/bundled-skills/longbridge-research/references/company-profile.md +96 -0
  116. package/bundled-skills/longbridge-research/references/company-tearsheet.md +82 -0
  117. package/bundled-skills/longbridge-research/references/competitive-analysis.md +81 -0
  118. package/bundled-skills/longbridge-research/references/consensus.md +92 -0
  119. package/bundled-skills/longbridge-research/references/coverage-initiation.md +76 -0
  120. package/bundled-skills/longbridge-research/references/defi-yield.md +60 -0
  121. package/bundled-skills/longbridge-research/references/finance-calendar.md +165 -0
  122. package/bundled-skills/longbridge-research/references/financial-planning.md +77 -0
  123. package/bundled-skills/longbridge-research/references/forecast-eps.md +39 -0
  124. package/bundled-skills/longbridge-research/references/fund-holder.md +44 -0
  125. package/bundled-skills/longbridge-research/references/hkipo-analysis.md +101 -0
  126. package/bundled-skills/longbridge-research/references/industry-peers.md +46 -0
  127. package/bundled-skills/longbridge-research/references/industry-rank.md +62 -0
  128. package/bundled-skills/longbridge-research/references/insider-trades.md +48 -0
  129. package/bundled-skills/longbridge-research/references/institution-rating.md +62 -0
  130. package/bundled-skills/longbridge-research/references/investment-ideas.md +69 -0
  131. package/bundled-skills/longbridge-research/references/investment-proposal.md +95 -0
  132. package/bundled-skills/longbridge-research/references/investors.md +87 -0
  133. package/bundled-skills/longbridge-research/references/onchain.md +70 -0
  134. package/bundled-skills/longbridge-research/references/post-investment.md +76 -0
  135. package/bundled-skills/longbridge-research/references/shareholder.md +72 -0
  136. package/bundled-skills/longbridge-research/references/short-positions.md +50 -0
  137. package/bundled-skills/longbridge-research/references/short-trades.md +50 -0
  138. package/bundled-skills/longbridge-research/references/stock-research.md +61 -0
  139. package/bundled-skills/longbridge-research/references/thesis-tracker.md +64 -0
  140. package/bundled-skills/makepad-2-0-animation/SKILL.md +318 -0
  141. package/bundled-skills/makepad-2-0-animation/references/animator-reference.md +433 -0
  142. package/bundled-skills/makepad-2-0-dsl/SKILL.md +492 -0
  143. package/bundled-skills/makepad-2-0-dsl/references/dsl-syntax-reference.md +511 -0
  144. package/bundled-skills/makepad-2-0-dsl/references/extended-guide.md +56 -0
  145. package/bundled-skills/makepad-2-0-dsl/references/property-system.md +757 -0
  146. package/bundled-skills/makepad-2-0-events/SKILL.md +497 -0
  147. package/bundled-skills/makepad-2-0-events/references/event-patterns.md +802 -0
  148. package/bundled-skills/makepad-2-0-events/references/extended-guide.md +590 -0
  149. package/bundled-skills/makepad-2-0-layout/SKILL.md +499 -0
  150. package/bundled-skills/makepad-2-0-layout/references/extended-guide.md +243 -0
  151. package/bundled-skills/makepad-2-0-layout/references/layout-patterns.md +881 -0
  152. package/bundled-skills/makepad-2-0-widgets/SKILL.md +261 -0
  153. package/bundled-skills/makepad-2-0-widgets/references/widget-advanced.md +648 -0
  154. package/bundled-skills/makepad-2-0-widgets/references/widget-catalog.md +547 -0
  155. package/bundled-skills/meeting-distiller-pro/SKILL.md +120 -0
  156. package/bundled-skills/monte-carlo-analyze-root-cause/SKILL.md +12 -1
  157. package/bundled-skills/monte-carlo-asset-health/SKILL.md +12 -1
  158. package/bundled-skills/monte-carlo-context-detection/SKILL.md +170 -0
  159. package/bundled-skills/monte-carlo-context-detection/references/signal-definitions.md +46 -0
  160. package/bundled-skills/remotion-captions/SKILL.md +57 -0
  161. package/bundled-skills/remotion-captions/agents/openai.yaml +7 -0
  162. package/bundled-skills/remotion-captions/assets/remotion-icon.svg +4 -0
  163. package/bundled-skills/remotion-captions/display-captions.md +190 -0
  164. package/bundled-skills/remotion-captions/import-srt-captions.md +73 -0
  165. package/bundled-skills/remotion-captions/transcribe-captions.md +70 -0
  166. package/bundled-skills/remotion-create/SKILL.md +106 -0
  167. package/bundled-skills/remotion-create/agents/openai.yaml +7 -0
  168. package/bundled-skills/remotion-create/assets/remotion-icon.svg +4 -0
  169. package/bundled-skills/remotion-create/tailwind.md +11 -0
  170. package/bundled-skills/remotion-create/video-layout.md +9 -0
  171. package/bundled-skills/remotion-docs/SKILL.md +67 -0
  172. package/bundled-skills/remotion-docs/agents/openai.yaml +7 -0
  173. package/bundled-skills/remotion-docs/assets/remotion-icon.svg +4 -0
  174. package/bundled-skills/remotion-interactivity/SKILL.md +270 -0
  175. package/bundled-skills/remotion-interactivity/agents/openai.yaml +7 -0
  176. package/bundled-skills/remotion-interactivity/assets/remotion-icon.svg +4 -0
  177. package/bundled-skills/remotion-render/SKILL.md +48 -0
  178. package/bundled-skills/remotion-render/agents/openai.yaml +7 -0
  179. package/bundled-skills/remotion-render/assets/remotion-icon.svg +4 -0
  180. package/bundled-skills/remotion-render/transparent-videos.md +106 -0
  181. package/bundled-skills/saas-pricing-strategist/SKILL.md +169 -0
  182. package/bundled-skills/score-eval/SKILL.md +35 -0
  183. package/bundled-skills/writing-guidelines/SKILL.md +60 -0
  184. package/package.json +1 -1
  185. package/skills_index.json +980 -3
@@ -0,0 +1,802 @@
1
+ # Makepad 2.0 Event Patterns -- Working Examples
2
+
3
+ This reference contains complete working patterns extracted from the Makepad 2.0 codebase.
4
+ All Splash code uses Makepad 2.0 syntax (no commas, no semicolons, colon properties).
5
+ All Rust code uses `makepad_widgets::*` imports.
6
+
7
+ ---
8
+
9
+ ## 1. Counter App -- Button Click -> State Update -> Re-render
10
+
11
+ The simplest complete event flow. A button click in Rust triggers `script_eval!`
12
+ which updates Splash state and calls `.render()` to refresh the view.
13
+
14
+ **Source**: `examples/counter/src/app.rs`
15
+
16
+ ### Splash side (inside script_mod!)
17
+
18
+ ```splash
19
+ use mod.prelude.widgets.*
20
+ let state = {
21
+ counter: 0
22
+ }
23
+ mod.state = state
24
+ startup() do #(App::script_component(vm)){
25
+ ui: Root{
26
+ on_startup: ||{
27
+ ui.main_view.render()
28
+ }
29
+ main_window := Window{
30
+ window.inner_size: vec2(420, 220)
31
+ body +: {
32
+ main_view := View{
33
+ width: Fill
34
+ height: Fill
35
+ flow: Down
36
+ spacing: 12
37
+ align: Center
38
+ on_render: ||{
39
+ counter_label := Label{
40
+ text: "Count: " + state.counter
41
+ draw_text.text_style.font_size: 24
42
+ }
43
+ }
44
+ }
45
+ increment_button := Button{
46
+ text: "Increment"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### Rust side
55
+
56
+ ```rust
57
+ use makepad_widgets::*;
58
+
59
+ app_main!(App);
60
+
61
+ // script_mod! { ... } (Splash code above)
62
+
63
+ impl App {
64
+ fn run(vm: &mut ScriptVm) -> Self {
65
+ crate::makepad_widgets::script_mod(vm);
66
+ App::from_script_mod(vm, self::script_mod)
67
+ }
68
+ }
69
+
70
+ #[derive(Script, ScriptHook)]
71
+ pub struct App {
72
+ #[live]
73
+ ui: WidgetRef,
74
+ }
75
+
76
+ impl MatchEvent for App {
77
+ fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
78
+ if self.ui.button(cx, ids!(increment_button)).clicked(actions) {
79
+ script_eval!(cx, {
80
+ mod.state.counter += 1
81
+ ui.main_view.render()
82
+ });
83
+ }
84
+ }
85
+ }
86
+
87
+ impl AppMain for App {
88
+ fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
89
+ self.match_event(cx, event);
90
+ self.ui.handle_event(cx, event, &mut Scope::empty());
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### Event flow
96
+
97
+ 1. User clicks the "Increment" button
98
+ 2. Button widget emits `ButtonAction::Clicked` into `Actions`
99
+ 3. `AppMain::handle_event` calls `self.match_event(cx, event)`
100
+ 4. `MatchEvent::match_event` dispatches `Event::Actions` to `handle_actions`
101
+ 5. `self.ui.button(cx, ids!(increment_button)).clicked(actions)` returns `true`
102
+ 6. `script_eval!` runs Splash code: increments `state.counter`, calls `render()`
103
+ 7. `render()` schedules `main_view` for redraw
104
+ 8. On next draw pass, `on_render` fires, creating a Label with the new count
105
+
106
+ ---
107
+
108
+ ## 2. Todo App -- List Rendering with Per-Item Events
109
+
110
+ Demonstrates dynamic list rendering with `for` loops, per-item click handlers,
111
+ and multiple Splash functions coordinating state.
112
+
113
+ **Source**: `examples/todo/src/app.rs`
114
+
115
+ ### State and functions
116
+
117
+ ```splash
118
+ let todos = []
119
+
120
+ // Seed sample data
121
+ todos.push({text: "Get AI to control UI", tag: "dev", done: true})
122
+
123
+ fn add_todo(text, tag){
124
+ todos.push({text: text, tag: tag, done: false})
125
+ ui.todo_list.render()
126
+ }
127
+
128
+ fn toggle_todo(index, checked){
129
+ todos[index].done = checked
130
+ }
131
+
132
+ fn delete_todo(index){
133
+ todos.remove(index)
134
+ ui.todo_list.render()
135
+ }
136
+
137
+ fn count_remaining(){
138
+ let n = 0
139
+ for todo in todos {
140
+ if !todo.done { n = n + 1 }
141
+ }
142
+ n
143
+ }
144
+ ```
145
+
146
+ ### Template definition
147
+
148
+ ```splash
149
+ let TodoItem = RoundedView{
150
+ width: Fill height: Fit
151
+ padding: theme.mspace_2{left: theme.space_3, right: theme.space_3}
152
+ flow: Right spacing: theme.space_2
153
+ align: Align{y: 0.5}
154
+ draw_bg.color: theme.color_bg_container
155
+ draw_bg.border_radius: 10.0
156
+
157
+ check := CheckBox{text: ""}
158
+ label := Label{
159
+ width: Fill
160
+ text: "task"
161
+ draw_text.color: theme.color_label_inner
162
+ }
163
+ delete := ButtonFlatter{
164
+ text: "x"
165
+ width: 28 height: 28
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### Input bar with on_return and on_click
171
+
172
+ ```splash
173
+ todo_input := TextInput{
174
+ width: Fill height: 9. * theme.space_1
175
+ empty_text: "What needs to be done?"
176
+ on_return: || ui.add_button.on_click()
177
+ }
178
+ add_button := Button{
179
+ text: "+"
180
+ width: 40 height: 34
181
+ on_click: ||{
182
+ let text = ui.todo_input.text()
183
+ if text != "" {
184
+ add_todo(text, "")
185
+ ui.todo_input.set_text("")
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ ### Dynamic list with on_render
192
+
193
+ ```splash
194
+ todo_list := ScrollYView{
195
+ width: Fill height: Fill
196
+ padding: theme.mspace_2{left: theme.space_3, right: theme.space_3}
197
+ flow: Down spacing: theme.space_1
198
+ new_batch: true
199
+ on_render: ||{
200
+ if todos.len() == 0
201
+ EmptyState{}
202
+ else for i, todo in todos {
203
+ TodoItem{
204
+ label.text: todo.text
205
+ check.active: todo.done
206
+ check.on_click: |checked| toggle_todo(i, checked)
207
+ delete.on_click: || delete_todo(i)
208
+ }
209
+ }
210
+ }
211
+ EmptyState{}
212
+ }
213
+ ```
214
+
215
+ ### Clear completed with array retain
216
+
217
+ ```splash
218
+ clear_done := ButtonFlatter{
219
+ text: "Clear completed"
220
+ on_click: ||{
221
+ todos.retain(|todo| !todo.done)
222
+ ui.todo_list.render()
223
+ }
224
+ }
225
+ ```
226
+
227
+ ### Key patterns demonstrated
228
+
229
+ - `on_return` on TextInput delegates to another widget's `on_click`
230
+ - `on_render` with `for i, todo in todos` creates per-item UI with closures
231
+ - Each closure captures the loop variable `i` for per-item actions
232
+ - `CheckBox.on_click: |checked|` receives the new boolean state
233
+ - `todos.retain(|todo| !todo.done)` filters the array using a predicate
234
+ - `ui.todo_list.render()` triggers re-render after any state mutation
235
+
236
+ ---
237
+
238
+ ## 3. Form Validation Pattern
239
+
240
+ A pattern for input validation before submission, using Splash-only event handlers.
241
+
242
+ ```splash
243
+ let form_errors = {
244
+ name: ""
245
+ email: ""
246
+ }
247
+
248
+ fn validate_email(email){
249
+ // Simple check -- Splash does not have regex by default
250
+ if email == "" "Email is required"
251
+ else if !email.contains("@") "Invalid email address"
252
+ else ""
253
+ }
254
+
255
+ fn validate_name(name){
256
+ if name == "" "Name is required"
257
+ else if name.len() < 2 "Name too short"
258
+ else ""
259
+ }
260
+
261
+ fn submit_form(){
262
+ let name = ui.name_input.text()
263
+ let email = ui.email_input.text()
264
+
265
+ form_errors.name = validate_name(name)
266
+ form_errors.email = validate_email(email)
267
+
268
+ ui.error_view.render()
269
+
270
+ if form_errors.name == "" && form_errors.email == "" {
271
+ // All valid -- process submission
272
+ ui.name_input.set_text("")
273
+ ui.email_input.set_text("")
274
+ ui.success_view.render()
275
+ }
276
+ }
277
+
278
+ // UI definition
279
+ View{
280
+ width: Fill height: Fit
281
+ flow: Down spacing: theme.space_2
282
+
283
+ name_input := TextInput{
284
+ width: Fill
285
+ empty_text: "Your name"
286
+ }
287
+
288
+ email_input := TextInput{
289
+ width: Fill
290
+ empty_text: "your@email.com"
291
+ on_return: || submit_form()
292
+ }
293
+
294
+ error_view := View{
295
+ width: Fill height: Fit
296
+ flow: Down
297
+ on_render: ||{
298
+ if form_errors.name != "" {
299
+ Label{text: form_errors.name draw_text.color: #f44}
300
+ }
301
+ if form_errors.email != "" {
302
+ Label{text: form_errors.email draw_text.color: #f44}
303
+ }
304
+ }
305
+ }
306
+
307
+ submit_button := Button{
308
+ text: "Submit"
309
+ on_click: || submit_form()
310
+ }
311
+
312
+ success_view := View{
313
+ width: Fill height: Fit
314
+ on_render: ||{
315
+ Label{text: "Submitted!" draw_text.color: #4f4}
316
+ }
317
+ }
318
+ }
319
+ ```
320
+
321
+ ---
322
+
323
+ ## 4. Custom Widget Event Handling
324
+
325
+ When building a custom widget in Rust, use the `Hit` enum to handle raw input events
326
+ and emit widget-specific actions.
327
+
328
+ ```rust
329
+ use makepad_widgets::*;
330
+
331
+ // Define custom action
332
+ #[derive(Clone, Debug, DefaultNone)]
333
+ pub enum ColorPickerAction {
334
+ Changed(Vec4),
335
+ DragStart,
336
+ DragEnd,
337
+ None,
338
+ }
339
+
340
+ #[derive(Script, ScriptHook, Widget)]
341
+ pub struct ColorPicker {
342
+ #[deref]
343
+ view: View,
344
+ #[live]
345
+ draw_bg: DrawQuad,
346
+ #[rust]
347
+ current_color: Vec4,
348
+ #[rust]
349
+ dragging: bool,
350
+ }
351
+
352
+ impl Widget for ColorPicker {
353
+ fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
354
+ let uid = self.widget_uid();
355
+
356
+ match event.hits(cx, self.draw_bg.area()) {
357
+ Hit::FingerDown(fd) => {
358
+ self.dragging = true;
359
+ cx.set_key_focus(self.draw_bg.area());
360
+ self.update_color_from_pos(fd.rel);
361
+ cx.widget_action(uid, ColorPickerAction::DragStart);
362
+ cx.widget_action(uid, ColorPickerAction::Changed(self.current_color));
363
+ self.draw_bg.redraw(cx);
364
+ }
365
+ Hit::FingerMove(fm) => {
366
+ if self.dragging {
367
+ self.update_color_from_pos(fm.rel);
368
+ cx.widget_action(uid, ColorPickerAction::Changed(self.current_color));
369
+ self.draw_bg.redraw(cx);
370
+ }
371
+ }
372
+ Hit::FingerUp(_fu) => {
373
+ if self.dragging {
374
+ self.dragging = false;
375
+ cx.widget_action(uid, ColorPickerAction::DragEnd);
376
+ }
377
+ }
378
+ Hit::FingerHoverIn(_) => {
379
+ cx.set_cursor(MouseCursor::Crosshair);
380
+ }
381
+ Hit::FingerHoverOut(_) => {
382
+ cx.set_cursor(MouseCursor::Default);
383
+ }
384
+ _ => {}
385
+ }
386
+ }
387
+
388
+ fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
389
+ self.draw_bg.begin(cx, walk, Layout::default());
390
+ self.draw_bg.end(cx);
391
+ DrawStep::done()
392
+ }
393
+ }
394
+
395
+ impl ColorPicker {
396
+ fn update_color_from_pos(&mut self, pos: DVec2) {
397
+ // Convert position to color...
398
+ self.current_color = vec4(pos.x as f32, pos.y as f32, 0.5, 1.0);
399
+ }
400
+ }
401
+
402
+ // Consuming the custom action from the App
403
+ impl MatchEvent for App {
404
+ fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
405
+ if let Some(item) = actions.find_widget_action(
406
+ self.ui.widget(cx, ids!(color_picker)).widget_uid()
407
+ ) {
408
+ if let ColorPickerAction::Changed(color) = item.cast() {
409
+ // Use the selected color
410
+ script_eval!(cx, {
411
+ mod.state.selected_color = #(color)
412
+ ui.preview.render()
413
+ });
414
+ }
415
+ }
416
+ }
417
+ }
418
+ ```
419
+
420
+ ---
421
+
422
+ ## 5. Keyboard Shortcut Handling
423
+
424
+ Global keyboard shortcuts are handled through `handle_key_down` on the `MatchEvent` trait.
425
+
426
+ ```rust
427
+ impl MatchEvent for App {
428
+ fn handle_key_down(&mut self, cx: &mut Cx, event: &KeyEvent) {
429
+ // Ctrl+S / Cmd+S -- Save
430
+ if event.modifiers.control || event.modifiers.logo {
431
+ match event.key_code {
432
+ KeyCode::KeyS => {
433
+ self.save_document(cx);
434
+ }
435
+ KeyCode::KeyZ => {
436
+ if event.modifiers.shift {
437
+ self.redo(cx);
438
+ } else {
439
+ self.undo(cx);
440
+ }
441
+ }
442
+ KeyCode::KeyN => {
443
+ self.new_document(cx);
444
+ }
445
+ _ => {}
446
+ }
447
+ }
448
+
449
+ // Plain keys
450
+ match event.key_code {
451
+ KeyCode::Escape => {
452
+ self.cancel_current_operation(cx);
453
+ }
454
+ KeyCode::F5 => {
455
+ self.refresh(cx);
456
+ }
457
+ _ => {}
458
+ }
459
+ }
460
+ }
461
+ ```
462
+
463
+ ### Widget-level key handling (inside a custom widget)
464
+
465
+ ```rust
466
+ impl Widget for MyEditor {
467
+ fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
468
+ match event.hits(cx, self.draw_bg.area()) {
469
+ Hit::KeyDown(ke) => {
470
+ match ke.key_code {
471
+ KeyCode::ReturnKey => {
472
+ let uid = self.widget_uid();
473
+ cx.widget_action(uid, EditorAction::Submit);
474
+ }
475
+ KeyCode::Escape => {
476
+ let uid = self.widget_uid();
477
+ cx.widget_action(uid, EditorAction::Cancel);
478
+ }
479
+ KeyCode::Tab => {
480
+ // Insert tab or move focus
481
+ }
482
+ _ => {}
483
+ }
484
+ }
485
+ Hit::KeyFocus(_) => {
486
+ self.animator_play(cx, ids!(focus.on));
487
+ }
488
+ Hit::KeyFocusLost(_) => {
489
+ self.animator_play(cx, ids!(focus.off));
490
+ }
491
+ _ => {}
492
+ }
493
+ }
494
+ }
495
+ ```
496
+
497
+ ---
498
+
499
+ ## 6. Timer and Animation Events
500
+
501
+ ### Polling timer (from Git example)
502
+
503
+ **Source**: `examples/git/src/app.rs`
504
+
505
+ ```rust
506
+ #[derive(Script, ScriptHook)]
507
+ pub struct App {
508
+ #[live]
509
+ ui: WidgetRef,
510
+ #[rust]
511
+ poll_timer: Timer,
512
+ #[rust]
513
+ active_request_id: Option<LiveId>,
514
+ }
515
+
516
+ impl MatchEvent for App {
517
+ fn handle_startup(&mut self, cx: &mut Cx) {
518
+ // Start polling every 30 seconds
519
+ self.poll_timer = cx.start_interval(30.0);
520
+ self.poll_remote(cx);
521
+ }
522
+
523
+ fn handle_timer(&mut self, cx: &mut Cx, event: &TimerEvent) {
524
+ if self.poll_timer.is_timer(event).is_some() {
525
+ self.poll_remote(cx);
526
+ }
527
+ }
528
+ }
529
+ ```
530
+
531
+ ### One-shot delay timer
532
+
533
+ ```rust
534
+ #[derive(Script, ScriptHook)]
535
+ pub struct App {
536
+ #[live]
537
+ ui: WidgetRef,
538
+ #[rust]
539
+ debounce_timer: Timer,
540
+ #[rust]
541
+ pending_search: String,
542
+ }
543
+
544
+ impl MatchEvent for App {
545
+ fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
546
+ // Debounce text input -- wait 300ms after last keystroke
547
+ if let Some(text) = self.ui.text_input(cx, ids!(search_input)).changed(actions) {
548
+ self.pending_search = text;
549
+ // Cancel previous timer, start new one
550
+ cx.stop_timer(self.debounce_timer);
551
+ self.debounce_timer = cx.start_timeout(0.3);
552
+ }
553
+ }
554
+
555
+ fn handle_timer(&mut self, cx: &mut Cx, event: &TimerEvent) {
556
+ if self.debounce_timer.is_timer(event).is_some() {
557
+ // Timer fired -- execute the search
558
+ self.execute_search(cx, &self.pending_search.clone());
559
+ }
560
+ }
561
+ }
562
+ ```
563
+
564
+ ### NextFrame animation
565
+
566
+ For smooth per-frame animations, use `handle_next_frame`:
567
+
568
+ ```rust
569
+ #[derive(Script, ScriptHook)]
570
+ pub struct App {
571
+ #[live]
572
+ ui: WidgetRef,
573
+ #[rust]
574
+ animating: bool,
575
+ #[rust]
576
+ animation_progress: f64,
577
+ }
578
+
579
+ impl MatchEvent for App {
580
+ fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
581
+ if self.ui.button(cx, ids!(animate_button)).clicked(actions) {
582
+ self.animating = true;
583
+ self.animation_progress = 0.0;
584
+ cx.request_next_frame();
585
+ }
586
+ }
587
+
588
+ fn handle_next_frame(&mut self, cx: &mut Cx, _event: &NextFrameEvent) {
589
+ if self.animating {
590
+ self.animation_progress += 0.016; // ~60fps
591
+ if self.animation_progress >= 1.0 {
592
+ self.animation_progress = 1.0;
593
+ self.animating = false;
594
+ } else {
595
+ cx.request_next_frame(); // Request another frame
596
+ }
597
+
598
+ let progress = self.animation_progress;
599
+ script_eval!(cx, {
600
+ mod.state.progress = #(progress)
601
+ ui.animated_view.render()
602
+ });
603
+ }
604
+ }
605
+ }
606
+ ```
607
+
608
+ ---
609
+
610
+ ## 7. Network Response Handling (Full Pattern)
611
+
612
+ Complete pattern showing HTTP requests initiated from Rust, with response handling
613
+ that updates Splash UI state.
614
+
615
+ **Source**: adapted from `examples/git/src/app.rs`
616
+
617
+ ```rust
618
+ use makepad_widgets::*;
619
+
620
+ app_main!(App);
621
+
622
+ script_mod! {
623
+ use mod.prelude.widgets.*
624
+
625
+ let state = {
626
+ loading: false
627
+ data: ""
628
+ error: ""
629
+ }
630
+ mod.state = state
631
+
632
+ startup() do #(App::script_component(vm)){
633
+ ui: Root{
634
+ on_startup: ||{
635
+ ui.content_view.render()
636
+ }
637
+ main_window := Window{
638
+ window.inner_size: vec2(600, 400)
639
+ body +: {
640
+ width: Fill height: Fill
641
+ flow: Down spacing: 12
642
+ padding: 20
643
+
644
+ fetch_button := Button{
645
+ text: "Fetch Data"
646
+ }
647
+
648
+ content_view := View{
649
+ width: Fill height: Fill
650
+ flow: Down
651
+ on_render: ||{
652
+ if state.loading {
653
+ Label{text: "Loading..." draw_text.color: #888}
654
+ } else if state.error != "" {
655
+ Label{text: "Error: " + state.error draw_text.color: #f44}
656
+ } else if state.data != "" {
657
+ Label{text: state.data}
658
+ } else {
659
+ Label{text: "Click Fetch to load data" draw_text.color: #888}
660
+ }
661
+ }
662
+ }
663
+ }
664
+ }
665
+ }
666
+ }
667
+ }
668
+
669
+ impl App {
670
+ fn run(vm: &mut ScriptVm) -> Self {
671
+ crate::makepad_widgets::script_mod(vm);
672
+ App::from_script_mod(vm, self::script_mod)
673
+ }
674
+ }
675
+
676
+ #[derive(Script, ScriptHook)]
677
+ pub struct App {
678
+ #[live]
679
+ ui: WidgetRef,
680
+ #[rust]
681
+ active_request: Option<LiveId>,
682
+ }
683
+
684
+ impl MatchEvent for App {
685
+ fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
686
+ if self.ui.button(cx, ids!(fetch_button)).clicked(actions) {
687
+ // Show loading state
688
+ script_eval!(cx, {
689
+ mod.state.loading = true
690
+ mod.state.error = ""
691
+ ui.content_view.render()
692
+ });
693
+
694
+ // Fire HTTP request
695
+ let request_id = live_id!(fetch_data);
696
+ let req = HttpRequest::new(
697
+ "https://api.example.com/data",
698
+ HttpMethod::Get,
699
+ );
700
+ cx.http_request(request_id, req);
701
+ self.active_request = Some(request_id);
702
+ }
703
+ }
704
+
705
+ fn handle_http_response(
706
+ &mut self,
707
+ cx: &mut Cx,
708
+ request_id: LiveId,
709
+ response: &HttpResponse,
710
+ ) {
711
+ if Some(request_id) != self.active_request {
712
+ return;
713
+ }
714
+ self.active_request = None;
715
+
716
+ let body = String::from_utf8_lossy(&response.body).to_string();
717
+ script_eval!(cx, {
718
+ mod.state.loading = false
719
+ mod.state.data = #(body)
720
+ ui.content_view.render()
721
+ });
722
+ }
723
+
724
+ fn handle_http_request_error(
725
+ &mut self,
726
+ cx: &mut Cx,
727
+ request_id: LiveId,
728
+ err: &HttpError,
729
+ ) {
730
+ if Some(request_id) != self.active_request {
731
+ return;
732
+ }
733
+ self.active_request = None;
734
+
735
+ let message = err.message.clone();
736
+ script_eval!(cx, {
737
+ mod.state.loading = false
738
+ mod.state.error = #(message)
739
+ ui.content_view.render()
740
+ });
741
+ }
742
+
743
+ fn handle_http_progress(
744
+ &mut self,
745
+ cx: &mut Cx,
746
+ request_id: LiveId,
747
+ progress: &HttpProgress,
748
+ ) {
749
+ if Some(request_id) != self.active_request {
750
+ return;
751
+ }
752
+ let loaded = progress.loaded;
753
+ let total = progress.total;
754
+ log!("Download progress: {} / {} bytes", loaded, total);
755
+ }
756
+ }
757
+
758
+ impl AppMain for App {
759
+ fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
760
+ self.match_event(cx, event);
761
+ self.ui.handle_event(cx, event, &mut Scope::empty());
762
+ }
763
+ }
764
+ ```
765
+
766
+ ---
767
+
768
+ ## Summary of Key Rules
769
+
770
+ 1. **Splash handlers** (`on_click`, `on_render`, `on_return`, `on_startup`) are
771
+ written inline in `script_mod!` and handle UI-level interactions.
772
+
773
+ 2. **Rust handlers** (`handle_actions`, `handle_timer`, `handle_http_response`, etc.)
774
+ handle business logic, I/O, and anything that needs typed Rust code.
775
+
776
+ 3. **Bridge macros**: Use `script_eval!(cx, { ... })` to push state changes from
777
+ Rust into Splash and trigger re-renders.
778
+
779
+ 4. **Widget actions**: Access widgets with `self.ui.widget_type(cx, ids!(name))`
780
+ and query actions with `.clicked(actions)`, `.changed(actions)`, etc.
781
+
782
+ 5. **on_render is not automatic**: You must call `ui.widget_name.render()` to
783
+ trigger a view's `on_render` handler. Set `new_batch: true` to clear old content.
784
+
785
+ 6. **Closures capture loop variables**: In `for i, item in list { ... }`, event
786
+ handlers like `on_click: || delete(i)` correctly capture the current `i`.
787
+
788
+ 7. **AppMain::handle_event must call both**:
789
+ - `self.match_event(cx, event)` -- dispatches to MatchEvent handlers
790
+ - `self.ui.handle_event(cx, event, &mut Scope::empty())` -- propagates to widgets
791
+
792
+ 8. **`fn tick()` auto-timer**: Define `fn tick()` in Splash code and the Splash widget
793
+ automatically starts a 1-second interval timer that calls it. No setup needed.
794
+
795
+ 9. **`fn on_audio()` callback**: Canvas injects audio state globals (`_playing`, `_pos`,
796
+ `_dur`, `_amp`, `_b0`–`_b15`) and calls `fn on_audio()` ~10Hz during playback.
797
+ Use this for real-time audio-reactive UI updates.
798
+
799
+ 10. **Splash button events don't reach `handle_actions`**: Dynamically created Splash
800
+ buttons (via `POST /splash`) have unstable UIDs. The `uid_map` may not rebuild
801
+ in time. Always use `on_click:` handlers inside Splash for button interactions.
802
+ The HTTP `GET /event` bridge is unreliable for this reason.