Summary
On the wasm/web target (what the playground hub compiles to), perry/ui's reactive state and imperative setText don't update the DOM. Button callbacks fire fine (callWasmClosure works), and by-handle widget mutation works — but anything that updates displayed text via state/setText is a silent no-op. This made a counter example look like "buttons don't work".
Repro
import { App, VStack, Text, Button, state } from 'perry/ui';
const count = state(0);
App({ title: 'c', width: 320, height: 180, body: VStack(12, [
Text('Counter'),
(count as any).text(),
Button('+', () => { (count as any).set(count.value + 1); console.log('val ' + count.value); }),
]) });
Compile --target wasm32-wasi (hub) or --target web, run in a browser:
- The bound
count.text() shows 0 and never changes after clicking +.
console.log('val ' + count.value) prints val undefined — count.value reads undefined.
Imperative path is also dead:
const label = Text('Count: 0', 'count'); // 2nd-arg id
setText('count', 'Count: 1'); // no-op
perry_ui_text_create(text) in crates/perry-codegen-wasm/src/wasm_runtime.js ignores the 2nd id arg, and there is no perry_ui_set_text in that runtime, so setText(id, value) dispatches to nothing.
What does work on wasm/web (for contrast)
- Button
onPress callbacks fire (callWasmClosure).
- By-handle widget mutation in callbacks:
widgetSetBackgroundColor(w, …), setCornerRadius, etc. (colors are 0..1 floats — set_background does r*255).
showToast, alert.
- Static layout (VStack/HStack/Text/Button) + static styling.
So an interactive demo is possible via by-handle mutation, but the documented state/setText reactive APIs need wiring on this target.
Likely area
crates/perry-codegen-wasm/src/wasm_runtime.js: no perry_ui_set_text; perry_ui_text_create drops the id; js_state_get/js_state_set not bound to the text-update path the way the native targets are.
- The
state_desugar HIR pass / state.text() desugar may not be emitting the bound-text wiring for the wasm backend (perry-codegen-wasm) the way perry-codegen-js does.
Found while building example programs for playground.perryts.com. Happy to test a fix.
Summary
On the wasm/web target (what the playground hub compiles to), perry/ui's reactive
stateand imperativesetTextdon't update the DOM. Button callbacks fire fine (callWasmClosure works), and by-handle widget mutation works — but anything that updates displayed text via state/setText is a silent no-op. This made a counter example look like "buttons don't work".Repro
Compile
--target wasm32-wasi(hub) or--target web, run in a browser:count.text()shows0and never changes after clicking+.console.log('val ' + count.value)printsval undefined—count.valuereadsundefined.Imperative path is also dead:
perry_ui_text_create(text)incrates/perry-codegen-wasm/src/wasm_runtime.jsignores the 2ndidarg, and there is noperry_ui_set_textin that runtime, sosetText(id, value)dispatches to nothing.What does work on wasm/web (for contrast)
onPresscallbacks fire (callWasmClosure).widgetSetBackgroundColor(w, …),setCornerRadius, etc. (colors are 0..1 floats —set_backgrounddoesr*255).showToast,alert.So an interactive demo is possible via by-handle mutation, but the documented
state/setTextreactive APIs need wiring on this target.Likely area
crates/perry-codegen-wasm/src/wasm_runtime.js: noperry_ui_set_text;perry_ui_text_createdrops the id;js_state_get/js_state_setnot bound to the text-update path the way the native targets are.state_desugarHIR pass /state.text()desugar may not be emitting the bound-text wiring for the wasm backend (perry-codegen-wasm) the way perry-codegen-js does.Found while building example programs for playground.perryts.com. Happy to test a fix.