Can the Escape key be configured to first blur focus from editable text fields before triggering OneDev's built-in pane visibility shortcut? #2880
Zak Siddiqui opened 5 days ago

Background

In standard browser behavior, pressing Escape removes focus from an editable text field. This is a widely relied-upon convention that enables users to:

  1. Keyboard power users: After editing text, press Escape to defocus the input, then use browser-level keyboard shortcuts (e.g., Cmd + Left Arrow on Mac or Alt + Left Arrow on PC for back-navigation) that are otherwise intercepted by the text field.

  2. Accessibility users: Users who cannot use a mouse depend on pressing Escape to exit text fields so they can activate assistive tools and accessibility features triggered by keyboard shortcuts — shortcuts that are blocked while focus remains on an editable element.

Observed Problem

In OneDev, pressing Escape while focus is inside an editable text field does not blur/defocus the field as expected. It appears that OneDev is intercepting (trapping) the Escape key event to trigger a built-in shortcut — likely the pane visibility toggle — before the browser's default blur behavior can occur.

This breaks the standard browser convention and creates a barrier for both keyboard-driven workflows and accessibility tool usage.

Investigation Requested

  1. Where is the Escape key event being intercepted in OneDev's frontend code? Identify the event handler(s) that capture Escape and determine whether preventDefault() or stopPropagation() is being called.

  2. What built-in shortcut(s) are bound to the Escape key? Confirm whether it is the pane visibility toggle or other actions.

  3. Can the Escape key behavior be made context-aware? Specifically, investigate implementing the following two-tier approach:

    • First Escape press (when focus is on an editable text field): Blur/defocus the text field (restoring standard browser behavior).
    • Second Escape press (when no editable element has focus): Trigger OneDev's built-in pane visibility toggle as it does today.
  4. Are there existing patterns or libraries in OneDev's codebase for managing keyboard shortcut priority based on focus context that could be leveraged?

Proposed Solution

Implement a focus-aware Escape key handler:

  • If document.activeElement is an editable element (<input>, <textarea>, [contenteditable]), the first Escape should call activeElement.blur() and not trigger the pane toggle.
  • If no editable element is focused, Escape should continue to toggle pane visibility as it currently does.

This preserves OneDev's built-in shortcut functionality while restoring expected browser behavior and improving accessibility compliance.

  • Service Desk commented 5 days ago

    test

  • Service Desk changed state to 'Closed' 5 days ago
    Previous Value Current Value
    Open
    Closed
  • Service Desk commented 5 days ago

    Reopening after accidental test close; posting the investigation answer next.

  • Service Desk changed state to 'Open' 5 days ago
    Previous Value Current Value
    Closed
    Open
  • Service Desk commented 5 days ago

    Investigation findings for the Escape-key questions:

    Clarification on browser default

    Pressing Escape does not blur focused <input> / <textarea> elements in Chrome, Firefox, or Safari by default. That blur-on-Escape behavior is an application convention (common in some desktop apps and accessibility workflows), not a browser built-in. What OneDev does today is still relevant to your request; the framing just isn't "restore browser default."

    1. Where Escape is intercepted

    Several document-level keydown handlers look for keyCode == 27. The global one is in server-core/.../web/page/base/base.js:

    $(document).keydown(function(e) {
        if (e.keyCode == 27) // ESC
            e.preventDefault();
    });
    

    It calls preventDefault() on every Escape press (no stopPropagation()). That blocks browser defaults such as exiting fullscreen; it does not itself blur or unblur fields.

    Other Escape handlers (also document keydown, typically without stopPropagation()):

    LocationAction
    layout.jsUndocks / closes the mobile sidebar (sidebar-docked)
    side-info.jsCloses the side-info panel when it is in overlay mode
    modal.jsCloses the topmost modal (with dirty-form confirm)
    floating.jsCloses floating / dropdown panels
    symbol-tooltip.js, source-view.js, etc.Close tooltips / popovers

    None of these call activeElement.blur().

    2. What Escape is bound to (not a general "pane toggle")

    Escape is used as a dismiss / close key for overlays, not a general pane-visibility toggle:

    • Mobile sidebar: Escape removes sidebar-docked (closes the drawer).
    • Side info: Escape closes it only in overlay mode (narrow viewport or .hide-side-info). On wide layouts the pinned side-info open/closed state is cookie-driven and is not toggled by Escape.
    • Modals / floating panels / tooltips: Escape closes them.

    So the behavior you hit while typing is usually "close an open overlay," not "toggle the main side pane."

    3. Can Escape be made context-aware (blur first, then dismiss)?

    Yes, technically. There is no existing setting to configure this today; it would be a product/code change.

    A practical approach:

    1. In a central Escape handler (or at the start of each dismiss handler), if document.activeElement is an editable control (input/textarea/select, [contenteditable], CodeMirror, etc.), call blur() and return without dismissing UI.
    2. On a later Escape with no editable focus, keep today's dismiss behavior.

    Caveats to design carefully:

    • Markdown / CodeMirror already use Escape for their own UI (hints, fullscreen, etc.).
    • Date pickers, Select2, color pickers already special-case Escape.
    • Blurring a dirty leave-confirm field before a modal close could change UX unexpectedly.

    4. Existing focus-aware shortcut patterns

    Yes. The bundled jQuery Hotkeys plugin (server-core/.../web/asset/hotkeys/jquery.hotkeys.js) skips shortcuts when focus is in input-accepting elements / contenteditable (filterInputAcceptingElements, filterTextInputs, filterContentEditable). Markdown and several pickers use that.

    The Escape dismiss handlers above do not use that filter; they listen with raw $(document).on("keydown", ...) and only exclude a few overlay widgets (flatpickr, select2, etc.).

    Bottom line

    • Escape is intentionally handled by OneDev UI dismiss logic; the global handler also preventDefault()s it.
    • It is not currently configurable, and it does not blur focused text fields.
    • A two-step "blur editable first, then dismiss overlays" behavior is feasible and could reuse the same focus checks Hotkeys already uses, but it would be a new enhancement (not a setting today).

    If this answers your investigation questions, we can close the issue. If you want the two-tier Escape behavior implemented, say so and we can follow up as an improvement/feature request.

  • Zak Siddiqui commented 5 days ago

    Apologies, blur was not the correct use of the terminology, I should have meant "lose focus".

  • Service Desk commented 5 days ago

    No worries — in the DOM, blur() is exactly the API that makes an element lose focus, so the investigation above still applies under that wording.

    If that answers your questions, we can close this. If you'd like the two-tier Escape behavior (lose focus on first Escape, then dismiss overlays on a later Escape) implemented as an enhancement, say so and we can follow up.

  • Zak Siddiqui commented 5 days ago

    I want the two-tier Escape behavior implemented

  • Service Desk commented 5 days ago

    Thanks — implementing the two-tier Escape behavior is an enhancement beyond the scope of this question. Escalating to @robin for follow-up.

  • Zak Siddiqui commented 5 days ago

    I'll create this as a separate issue, now that I have some context.

  • Service Desk commented 5 days ago

    Sounds good — closing this question since the investigation is answered. Thanks for opening the enhancement separately.

  • Service Desk changed state to 'Closed' 5 days ago
    Previous Value Current Value
    Open
    Closed
1/1
Type
Question
Priority
Normal
Assignee
Labels
No labels
Issue Votes (0)
Watchers (4)
Reference
OD-2880
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover