Skip to content

Keyboard shortcuts (KeyboardControl)

Overview

KeyboardControl registers shortcuts in the page, listens for keys and chords, and can register global shortcuts in the suite environment.

Import

ts
import { KeyboardControl } from '@ugreen-nas/core/plugins'

DefineKeyAction fields

Each shortcut is an object. Besides the table below you may add custom fields (e.g. propname) for your own use.

PropertyTypeRequiredDescription
keystringYesShortcut string; use + for chords, e.g. ctrl+a; no spaces around +
actionstringNoAction id in the callback; either this or command
commandstringNoAlias of action
disabledbooleanNoIf true, this entry is not registered
hiddenbooleanNoIf true, this entry is not registered
longPressnumberNoLong-press threshold in seconds
immediatelybooleanNoIf true, repeated chord triggers (e.g. hold Ctrl and press c/v); throttling prevents default automatically
globalbooleanNoIf true, register as cross-iframe global shortcut; only effective in app tray/card environments (see “Global shortcuts”)

Constructor

KeyboardControl()

Creates an instance and attaches keydown / keyup listeners.

Parameters:

NameTypeRequiredDefaultDescription
defineActionDefineKeyAction[]YesShortcut definitions
enableInFormElementsbooleanNotrueIf true, shortcuts also fire inside input, textarea, etc.; if false, they do not, to avoid breaking typing
triggerIntervalnumberNo300Minimum interval (ms) between triggers for immediately throttling

Returns: KeyboardControl instance.

Static methods

haveGlobalKey()

Whether a normalized chord string already exists in the global shortcut table.

NameTypeRequiredDescription
keystringYesNormalized chord (lowercase, ctrl/meta rules, etc.)

Returns: boolean

Instance methods

destroy()

Removes listeners and clears global entries registered by this instance.

Returns: void

updateKeyMap()

Replaces definitions: clears prior global registrations, then rebuilds from the new array.

NameTypeRequiredDescription
defineActionDefineKeyAction[]NoNew array; if omitted, nothing is updated

Returns: void

setTriggerInterval()

Sets throttle interval (ms) for immediately and similar.

NameTypeRequiredDescription
intervalnumberYesUsed when ≥ 0

Returns: void

syncRemoteGlobalKeys()

Syncs the global shortcut list from the host.

NameTypeRequiredDescription
keysstring[]YesNormalized chord list

Returns: void

Getter / setter

beforeHandle (setter)

Interceptor before handling; if it returns true, the shortcut is not handled.

Assign a function or undefined.

NameTypeRequiredDescription
func(() => boolean) | undefinedNoReturn true to block

Events

action

Fired when a registered shortcut is recognized (in-iframe or after global/host forwarding).

NameTypeDescription
actionstringaction / command from config (or internal placeholder)
eventKeyboardEventFor long-press edge cases, type may be keyup, longKeydown, keydown, etc. (see notes)

keyPress

Fired when a key participates in the current logic (including keyCache branches).

NameTypeDescription
eventKeyboardEventKeyboard event

keyEvent

Fired on any key down or up.

NameTypeDescription
eventKeyboardEventKeyboard event
type'up' | 'down'Down or up

debug

Debug: logs the declared chord string during handling.

NameTypeDescription
pressstringNormalized chord string

Global shortcuts (cross iframe)

In multi-iframe suite setups, some shortcuts can be global: if another iframe does not define the same key, the registrant handles it.

Requirements

  • Set global: true on the entry
  • Environment must be app tray/card

Behavior

  • Priority: If the current iframe defines the same key, local wins; global is not used
  • Cross-origin children: host syncs the shortcut table to child windows
  • Lifecycle: destroy() / updateKeyMap() clear global entries; call destroy() when the suite closes to avoid leaks

Example

ts
import { KeyboardControl } from '@ugreen-nas/core/plugins'

const kb = new KeyboardControl([
  { key: 'Ctrl+S', action: 'save' },
  { key: 'ArrowLeft', action: 'globalLeft', global: true },
])

kb.on('action', (action, event) => {
  if (action === 'globalLeft') {
    /* ... */
  }
})

kb.destroy()

Key definitions

Rules

  1. Key names are case-insensitive; normalized to lowercase internally
  2. Chords use +, e.g. ctrl+a
  3. Multiple modifiers allowed, e.g. ctrl+shift+a
  4. No spaces around +

Modifiers

NameMeaningPlatform notes
ctrlControlOn Mac, mapped to Command (meta)
altAltOn Mac, Option
optionOptionNormalized to alt; Mac only
shiftShiftUniversal
metaMetaWindows: Win; Mac: Command; non-Mac may map to ctrl
commandCommandMaps to meta on Mac or ctrl elsewhere

Special keys

NameKeyNotes
spaceSpaceDo not use a literal space character
enterEnter
tabTab
escEscNormalized to escape
escapeEscFull form
backspaceBackspace
deleteDelete

Arrows

NameKey
arrowupUp
arrowdownDown
arrowleftLeft
arrowrightRight

Function keys

NameKey
f1 - f12F1 - F12

Example: define and listen

ts
import { KeyboardControl } from '@ugreen-nas/core/plugins'

const keyboardControl = new KeyboardControl([
  { key: 'Ctrl+c', action: 'copy', disabled: true },
  { key: 'Shift+c', action: 'custom', hidden: true },
  { key: 'Ctrl+v', action: 'paste' },
])

keyboardControl.on('action', (action, event) => {
  // ...
})

keyboardControl.destroy()

immediately mode

Note

With the snippet below you can keep Ctrl held and press C / V repeatedly. Throttling prevents default automatically.

ts
import { KeyboardControl } from '@ugreen-nas/core/plugins'

const keyboardControl = new KeyboardControl([
  { key: 'Ctrl+c', action: 'copy', immediately: true },
  { key: 'Ctrl+v', action: 'custom', immediately: true },
])

keyboardControl.on('action', (action, event) => {
  // ...
})

Notes

  • On macOS, Ctrl is converted per the modifier rules above (typically to Command).
  • With longPress: a satisfied long press fires action once with a possible longKeydown type; a short press may still fire on keyup with event.type === 'keyup'; while held without satisfying long press, behavior matches normal keydown.
  • With global: true, the environment must be tray/card; otherwise it is not added to the global table.