enhancement
major
minor
major
minor
major
#29631
TL Views: Escape closes a <window> dialog while a <start-job> it started is still running; the job runs on with nothing left to show or cancel it
Problem
The Trac sync dialog of the issue tracker tl-dev (trac-sync.view.xml) is a <window> whose Sync command runs <start-job job="syncJob" cancelable="true"> (#29600) and whose content is a <switch> over the job channel showing <job-status input="syncJob"/> while the job runs. close-on-backdrop is at its default false, so a click beside the dialog leaves it open.
Observed (engine snapshot build 39 of 2026-09-18): pressing Escape while the job runs closes the dialog. The job runs on to its end on its worker thread (the application log shows the completion), but the reader has no display of it any more and no way to cancel it, and a second Sync command can be started beside it. TLWindow binds Escape to close unconditionally; WindowElement.Config has no way to refuse it.
Request
A dialog that hosts a suspended command (a <start-job>, a <confirm>) is not closed by Escape while that command is suspended, or <window> gets a close-on-escape attribute beside close-on-backdrop so the view can say so. Either way the cancel of the job remains the one way to leave a running job, and a dialog closed with a running job in it is not possible.
Lösung
A dialog hosting a suspended command cannot be closed until that command has settled - automatically, without an attribute on the view.
- Every close path of a dialog (Escape and the X button of the window chrome, Escape and the backdrop click of the overlay, cancel-dialog, <close-dialog>, the cascade of a reveal) ends in the dialog manager, which now refuses to close a dialog marked as not closable. The flag is pushed to the browser as well: the overlay ignores backdrop and Escape, the window disables its X button and ignores Escape.
- The view layer tracks the commands suspended inside a dialog: the action chain reports once when it settles (completion, abort or failure, synchronous or later), and a generic command whose chain returned before settling registers in the scope of the dialog it runs in until the chain settles. The dialog opener listens to that scope and flips the closable flag; the cancel-dialog command is disabled by a built-in rule while a command is suspended, so its button says why instead of doing nothing.
- A <confirm> opens its own dialog on top, so Escape reaches the confirmation, not the hosting dialog; nothing changes there.
The React demo app's long-running job page gets a button starting the job inside a dialog.