Desktop Material

Single-flight user actions

Desktop Material prevents a rapid double-click, repeated Enter or Space key, or a second control from starting the same consequential asynchronous action twice. The protection follows the real operation lifetime. It is not a fixed delay and it does not slow ordinary navigation, toggles, pagination, filter chips, or other deliberately repeatable controls.

Behavior

The shared renderer registry claims an action key synchronously before the callback runs. A second claim for that key does not invoke its callback. The claim remains active while the callback's returned promise is pending and is released after fulfillment or rejection. A synchronous callback releases its claim before returning, so existing repeatable controls retain their behavior.

Each shared Button, callback-style LinkButton, Material icon or text button, toolbar button, and shared Form receives a stable per-instance key. Callers may provide an explicit activationKey when two separate controls, menus, shortcuts, or command-palette entries start the same operation. The control exposes aria-busy and aria-disabled while its key is active without moving keyboard focus. Cancel and stop actions use their own keys and remain available.

The Linux terminal edition uses the same contract around Textual workers. The initiating button is disabled before the worker starts, another press for the same action ID is ignored, and the button is restored when the worker settles. Synchronous terminal actions release immediately.

Every generated documentation page loads the shared docs-action-flight.js module before its page controller. Clipboard actions on the documentation hub and screenshot pages use one key per source or capture command, publish aria-busy and aria-disabled while the browser clipboard promise is pending, and reopen after either result. Pages without the module retain their existing copy path rather than presenting an inert control.

Existing operation-level protections remain important. External editor and file-manager launches, destructive operations, Git mutations, uploads, downloads, installers, and release actions can have several entry paths. Those operations keep their existing exact-target validation or idempotency boundary in addition to the control-level claim.

Configuration

There is no user-facing cooldown setting. The default key is local to one control. Use an explicit semantic key only when separate entry paths represent the same exact action and target. Include the target identity in the key when different rows or files may run concurrently.

Failure modes and recovery

No timeout guesses that an operation is finished. A slow action stays guarded, and a fast failed action reopens as soon as its real result arrives.

Security considerations

Action keys contain stable action and target identifiers, never credentials, tokens, document contents, or user-entered secret values. Single-flight coordination is a consistency boundary, not authorization. Every protected operation still performs its normal permission, path, stale-object, and destructive-confirmation checks.

Verification

Focused renderer tests cover same-key suppression, independent keys, synchronous repeatability, rejection and throw release, target-scoped subscriptions, accessible busy state, shared keys across separate controls, and the existing external-open process guard. UI tests exercise shared, Material, link-style, and toolbar controls.

Focused terminal tests cover exact action claims, repeated Textual button presses during a worker, button restoration after completion, and synchronous repeatability. The real built-application interaction and capture records are tracked separately in the universal-feature completeness inventory.

Documentation tests execute the real page module in jsdom and cover duplicate promise suppression, independent and synchronous keys, rejection, throw release, and busy semantics. The screenshot-page generator asserts one current script entry across all generated pages.

Suggested articles