← Back to PRs

#8678: fix(control-ui): restore useDefineForClassFields for Lit reactivity

by chainyoda open 2026-02-04 08:25 View on GitHub →
app: web-ui stale
## Summary - Restores `ui/tsconfig.json` with `useDefineForClassFields: false`, which was dropped in 1f2f79a7a ("Merge tsconfigs") - This setting is required for Lit's `@state()` decorators to work with `experimentalDecorators: true` - Without it, ES2023 class fields create own data properties that shadow Lit's prototype getter/setter, so reactive state changes (e.g. `host.connected = true` after WebSocket hello-ok) silently bypass Lit's change detection and never trigger a re-render - This caused the Control UI to permanently show "Disconnected from gateway" even though the WebSocket connection was fully established and functional ## Root cause With `target: "es2023"`, TypeScript defaults `useDefineForClassFields` to `true`. This emits class fields as `Object.defineProperty(this, 'connected', { value: false, writable: true, ... })` which creates an own data property that shadows Lit's prototype accessor created by `@state()`. Property writes then go directly to the own property without triggering `requestUpdate()`. ## Test plan - [x] Verified `document.querySelector('openclaw-app').connected` was `true` but UI showed "Disconnected" (before fix) - [x] Verified `requestUpdate()` forced correct render (confirming state was correct, reactivity was broken) - [x] Rebuilt UI with restored `useDefineForClassFields: false` — Control UI now shows "Connected" and "Health OK" immediately after WebSocket hello-ok 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR restores a dedicated `ui/tsconfig.json` that extends the repo root `tsconfig.json` but overrides `compilerOptions.useDefineForClassFields` to `false` (and sets `types: ["vite/client"]`), matching Lit’s requirements for decorator-driven reactivity when using legacy/experimental decorators. In this codebase, the root `tsconfig.json` targets ES2023 and uses `experimentalDecorators: true`; with that combination, TypeScript’s default `useDefineForClassFields: true` can emit own class field initializers that shadow Lit’s accessor installed by `@state()`/`@property()`, preventing `requestUpdate()` from being triggered on state writes. Reintroducing this UI-specific override aligns the Control UI build output with Lit’s reactive property semantics. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - The change is isolated to a UI-specific tsconfig override and directly addresses a well-understood Lit/TypeScript decorator emission interaction; no runtime code paths are modified. - No files require special attention <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs