#8678: fix(control-ui): restore useDefineForClassFields for Lit reactivity
app: web-ui
stale
Cluster:
UI Improvements and Fixes
## 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
#9628: fix: resolve tsconfig rootDir errors by separating UI config (AI-as...
by KGBos · 2026-02-05
78.9%
#9195: Fix: Control UI fails to render new messages after chat.history Web...
by vishaltandale00 · 2026-02-05
77.6%
#21293: fix(ui): use live() directive for agent Primary model select to fix...
by soohanpark · 2026-02-19
71.2%
#15215: fix(UI): Prevent config layout panel from overlapping description text
by Chityalaakhil · 2026-02-13
69.9%
#20408: Control UI: show refresh in-flight state on Overview
by tanyabrownmac · 2026-02-18
69.9%
#11868: control-ui: pairing required guided flow (Clairephone V2 Candidate A)
by shojikumaru · 2026-02-08
69.8%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
69.4%
#9220: Fix: TUI drops API responses silently when runID already finalized
by vishaltandale00 · 2026-02-05
69.4%
#9218: Fix Control UI chat resync on gaps and terminal events
by figitaki · 2026-02-05
69.4%
#13960: fix(ui): preserve structured config validation error details
by constansino · 2026-02-11
68.9%