Repository navigation
feat(ui): add onblur to plugin markup fields - #204
Conversation
A markup <field> could hand its text back only on Enter (onsubmit) or on every keystroke (onchange), so a value typed and then left by clicking elsewhere was lost. onblur binds an Action<string> that receives the field's text whenever it loses keyboard focus: a click on another control or empty space, Escape, Tab, or the panel hiding or closing. The focus loss an Enter submit causes is skipped when onsubmit has just received the text or clearonsubmit has just emptied the field, so a field binding both never applies the same text twice or reports the cleared text. A field with onblur alone still applies its text on Enter. Claude-Session: https://claude.ai/code/session_014hCYK7XAeS88JWnMdEqgyb
eriknihlen
left a comment
There was a problem hiding this comment.
Thanks for adding this. I found one case we should fix before merging.
If an onblur callback hides its own panel, hiding the panel triggers focus loss again while the first callback is still running. That calls onblur repeatedly and can crash the client.
I reproduced the repeated callbacks with a guard to prevent an actual crash.
Could you make focus loss safe when the callback hides or removes its own panel, and add a regression test confirming the callback runs only once? The feature itself looks useful; this is the blocker I found.
An onblur callback that hid or removed its own panel lost focus again while it was still running, so the callback ran repeatedly until the stack overflowed. UiRoot now clears keyboard focus before telling the old element it lost it, and keeps any focus the handler moves elsewhere. UiField also ignores a focus loss that arrives while its callback runs. Claude-Session: https://claude.ai/code/session_01SY57mHJLZ6djpWZ5nP37Tw
|
Thanks for catching this. Fixed in 7471831. Cause: Fix:
Regression tests:
These tests fail on the previous commit with the callback running repeatedly. They stop re-entering after a few calls, so a regression fails the test instead of crashing the host. The branch merges cleanly into current main. I also dropped the stray lock-file changes from a local macOS restore (07c4f69), so the PR no longer touches them. |
What this changes
Adds an
onblurattribute to<field>in plugin panel markup.A plugin text field could hand its value back only on Enter (
onsubmit) or onevery keystroke (
onchange), so a value typed and then left by clickingelsewhere was lost.
onblurbinds anAction<string>that receives thefield's current text whenever it loses keyboard focus: a click on another
control or on empty panel space, Escape, Tab, or the panel hiding or closing
while the field is focused. All of these already raised
FocusLostonUiField; markup simply never boundOnFocusLost.releases focus.
UiField.LosingFocusToSubmitis true for that submit and itsfocus release when the text went to
OnSubmitorclearonsubmitemptied thefield, and markup's
onblurskips the focus loss then. A field binding bothcallbacks never applies the same text twice and never reports the cleared
empty text. The same holds when an
onsubmithandler hides its own panel.onbluron its own. A field withonblurand noonsubmitstillapplies its text when Enter ends the edit.
FormatExceptionasonsubmit:<field onblur="{X}"> did not resolve to an Action<string> property on <Binding>.onblurbehaves exactly as before. This is aplugin-facing markup addition, but
AcDream.Plugin.Abstractionsis unchangedand the headless host does not handle markup, so neither needs anything.
docs/plugin-ui-markup.mdlistsonblurin the binding table andthe
fieldattributes, and describes whenonchange,onsubmitandonblurfire.MarkupDocumentTestsdrives a realUiRoot(clicks, typedcharacters, keys) through all six focus-loss paths. It also covers:
onsubmitonce withoutonblur, and a later edit plusclick-away firing
onblur;clearonsubmitnot reporting empty text;onblurwithoutonsubmit;onsubmithandler that hides the panel.It was verified live with a plugin panel against a local ACE server: click-away,
Escape, Tab and panel close each apply the value, and Enter applies it once.
Original behavior
This doesn't change how the client behaves in the world. It only adds a plugin
UI markup attribute.
Checklist
dotnet build AcDream.slnx -c Releaseis greenHostParitypeer IPC tests and 2LauncherControlTestspipe tests, which also fail on cleanmain(fd4c3a3). Those are test bugs, a peer hub startup race and socket paths over the macOS length limit, fixed separately in test: fix peer IPC and launcher pipe tests on macOS #205.AcDream.App.Testspasses 8594/8594.https://claude.ai/code/session_014hCYK7XAeS88JWnMdEqgyb