{"version":3,"sources":["../src/constants.ts","../src/key.ts","../src/event.ts","../src/sequence.ts","../src/state.ts","../src/Handler.ts","../src/index.ts"],"sourcesContent":["import {AliasCharacter, Character} from './types'\n\nexport const modifiers = ['ctrl', 'alt', 'meta', 'shift'] as const\n\nexport const chars =\n  '__0_1_2_3_4_5_6_7_8_9_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_ _`_\\'_\"_~_!_@_#_$_%_^_&_*_(_)_._-_+_=_[_]_{_}_<_>_,_/_?_;_:_\\\\_|_capslock_numlock_enter_tab_arrowdown_arrowleft_arrowright_arrowup_end_home_pagedown_pageup_backspace_delete_insert_escape_f1_f2_f3_f4_f5_f6_f7_f8_f9_f10_f11_f12_f13_f14_f15_f16_f17_f18_f19_f20_f21_f22_f23'.split(\n    '_'\n  )\nchars[1] = '_'\n\nexport const codes: Record<string, number> = {}\nfor (const [i, c] of chars.entries()) codes[c] = i\n\nexport const aliases: Record<AliasCharacter, Character> = {\n  space: ' ',\n  plus: '+',\n  up: 'arrowup',\n  down: 'arrowdown',\n  left: 'arrowleft',\n  right: 'arrowright',\n  esc: 'escape',\n}\n\n/**\nWe want to encode keys and sequences of keys as numbers for performance\nA key code is 13 bits: XXXXXXXXXCAMS\n  XXXXXXXXX: 9 bits representing the character (event.key value).\n    A character can't have a code `0`, that's a special value.\n    We can have at most 511 different characters!\n  CAMS: 4 bits representing the modifiers `Ctrl`, `Alt`, `Meta` and `Shift` in this order.\nA sequence of keys is represented by the concatenation of codes of the keys.\n  An integer can safely be represented with 53 bits in Javascript `Number.MAX_SAFE_INTEGER`\n  Since every key takes 13bits, a sequence can at most contain 4 keys = 52 bits!\n*/\n\nexport const MODIFIERS_SIZE = 4\nexport const CHARACTER_SIZE = 9\nexport const CTRL_MASK = 0b1000\nexport const ALT_MASK = 0b0100\nexport const META_MASK = 0b0010\nexport const SHIFT_MASK = 0b0001\nexport const KEY_SIZE = CHARACTER_SIZE + MODIFIERS_SIZE\nexport const MODIFIERS_UPPER_BOUND = 2 ** MODIFIERS_SIZE\nexport const ONE_KEY_UPPER_BOUND = 2 ** KEY_SIZE\nexport const TWO_KEYS_UPPER_BOUND = 2 ** (2 * KEY_SIZE)\nexport const THREE_KEYS_UPPER_BOUND = 2 ** (3 * KEY_SIZE)\n","import {aliases, ALT_MASK, chars, codes, CTRL_MASK, META_MASK, modifiers, MODIFIERS_SIZE, MODIFIERS_UPPER_BOUND, SHIFT_MASK} from './constants'\nimport {EncodedKey, Key, NormalizedKey} from './types'\n\nexport function normalizeKey(key: Key): NormalizedKey {\n  let parts: string[]\n  if (key === '+') return ['+']\n  if ('+' == key.slice(-1)) {\n    parts = key.slice(0, -2).split('+')\n    parts.push('+')\n  } else {\n    parts = key.split('+')\n  }\n  return parts.map((x) => (aliases as any)[x] || x) as NormalizedKey\n}\n\nexport function encodeKey(key: NormalizedKey): EncodedKey {\n  const parts = new Set(key)\n  let code = codes[key[key.length - 1].toLowerCase()] || 0\n  for (const modifier of modifiers) {\n    code = 2 * code + (parts.has(modifier) ? 1 : 0)\n  }\n  return code\n}\n\nexport function decodeKey(encodedKey: EncodedKey): NormalizedKey {\n  const charCode = getCharacterCode(encodedKey)\n  const modifiersCode = getModifiersCode(encodedKey)\n  const key = []\n  if (modifiersCode & CTRL_MASK) key.push('ctrl')\n  if (modifiersCode & ALT_MASK) key.push('alt')\n  if (modifiersCode & META_MASK) key.push('meta')\n  if (modifiersCode & SHIFT_MASK) key.push('shift')\n  const c = chars[charCode]\n  if (c) key.push(c)\n  return key as any\n}\n\nexport function getCharacterCode(encodedKey: EncodedKey) {\n  return encodedKey >> MODIFIERS_SIZE\n}\n\nexport function getModifiersCode(encodedKey: EncodedKey) {\n  return encodedKey % MODIFIERS_UPPER_BOUND\n}\n\nexport function shouldOverride(previousKey: EncodedKey | undefined, newKey: EncodedKey) {\n  if (previousKey === undefined) return false\n  if (getCharacterCode(previousKey) > 0) return false\n  const previousModifiers = getModifiersCode(previousKey)\n  const newModifiers = getModifiersCode(newKey)\n  if (previousModifiers === newModifiers && getCharacterCode(newKey) === 0) return false\n  return (previousModifiers & getModifiersCode(newKey)) === previousModifiers\n}\n","import {normalizeKey} from './key'\nimport {aliases, codes, modifiers} from './constants'\nimport {AliasCharacter, Character, EncodedKey, Key, KeyboardEventType, Modifier} from './types'\n\nexport function createEvent(key: Key, type: KeyboardEventType = 'keydown') {\n  const parts = normalizeKey(key) as Array<Character | Modifier>\n  let eventKey = parts.at(-1) as string\n  if (aliases[eventKey as AliasCharacter] !== undefined) eventKey = aliases[eventKey as AliasCharacter]\n  if (eventKey === 'ctrl') eventKey = 'Control'\n  if (eventKey === 'alt') eventKey = 'Alt'\n  if (eventKey === 'meta') eventKey = 'Meta'\n  if (eventKey === 'shift') eventKey = 'Shift'\n  return new KeyboardEvent(type, {\n    ctrlKey: parts.includes('ctrl'),\n    altKey: parts.includes('alt'),\n    metaKey: parts.includes('meta'),\n    shiftKey: parts.includes('shift'),\n    key: eventKey,\n  })\n}\n\nexport function encodeEvent(event: KeyboardEvent): EncodedKey {\n  let code = codes[event.key.toLowerCase()] || 0\n  for (const modifier of modifiers) {\n    code = 2 * code + (event[`${modifier}Key` as keyof KeyboardEvent] ? 1 : 0)\n  }\n  return code\n}\n","import {ONE_KEY_UPPER_BOUND, THREE_KEYS_UPPER_BOUND, TWO_KEYS_UPPER_BOUND} from './constants'\nimport {decodeKey, encodeKey, normalizeKey} from './key'\nimport {EncodedKey, EncodedSequence, NormalizedSequence, Sequence} from './types'\n\nexport function normalizeSequence(sequence: Sequence): NormalizedSequence {\n  return sequence.map(normalizeKey)\n}\n\nexport function encodeSequence(sequence: NormalizedSequence): EncodedSequence {\n  if (sequence.length > 4) throw `Can't encode sequence of more than 4 keys!`\n  let code = 0\n  for (const key of sequence) {\n    code = code * ONE_KEY_UPPER_BOUND + encodeKey(key)\n  }\n  return code\n}\n\nexport function decodeSequence(sequence: EncodedSequence): NormalizedSequence {\n  const keys = []\n  while (sequence > 0) {\n    keys.unshift(decodeKey(sequence % 2 ** 13))\n    sequence = sequence >> 13\n  }\n  return keys\n}\n\nexport function getSequenceSize(seq: EncodedSequence) {\n  if (seq < ONE_KEY_UPPER_BOUND) return 1\n  if (seq < TWO_KEYS_UPPER_BOUND) return 2\n  if (seq < THREE_KEYS_UPPER_BOUND) return 3\n  return 4\n}\n\n/*\ngetEncodedSequencesFromHistory([\n  keyCode('ctrl+a'), \n  keyCode('alt+b'),\n  keyCode('c')\n]) //=> [\n  seqCode(['c'])\n  seqCode(['alt+b', 'c'])\n  seqCode(['ctrl+a', 'alt+b', 'c'])\n]\n*/\nexport function getEncodedSequencesFromHistory(keys: EncodedKey[]) {\n  const result = []\n  let sequenceCode = 0\n  let multiplier = 0\n  for (let i = keys.length - 1; i >= 0; i--) {\n    sequenceCode = 2 ** multiplier * keys[i] + sequenceCode\n    multiplier = multiplier + 13\n    result.push(sequenceCode)\n  }\n  return result\n}\n","import {encodeEvent} from './event'\nimport {shouldOverride} from './key'\nimport {Callback, State, Sequence} from './types'\nimport {encodeSequence, getEncodedSequencesFromHistory, getSequenceSize, normalizeSequence} from './sequence'\n\nexport function createState(data: Partial<State> = {}): State {\n  return {\n    history: [],\n    historySize: 0,\n    bindings: new Map(),\n    disabledSequenceCodes: new Set(),\n    ...data,\n  }\n}\n\nexport function addBinding(state: State, sequence: Sequence, fn: Callback): State {\n  const sequenceCode = encodeSequence(normalizeSequence(sequence))\n  if (!state.bindings.has(sequenceCode)) {\n    state.bindings.set(sequenceCode, new Set())\n  }\n  state.bindings.get(sequenceCode)!.add(fn)\n  return updateHistorySize(state)\n}\n\nexport function removeBinding(state: State, sequence: Sequence, fn: Callback): State {\n  const sequenceCode = encodeSequence(normalizeSequence(sequence))\n  const fns = state.bindings.get(sequenceCode)\n  if (fns) {\n    fns.delete(fn)\n    if (fns.size == 0) {\n      state.bindings.delete(sequenceCode)\n    }\n  }\n  return updateHistorySize(state)\n}\n\nexport function enableSequence(state: State, sequence: Sequence): State {\n  const sequenceCode = encodeSequence(normalizeSequence(sequence))\n  state.disabledSequenceCodes.delete(sequenceCode)\n  return state\n}\n\nexport function disableSequence(state: State, sequence: Sequence): State {\n  const sequenceCode = encodeSequence(normalizeSequence(sequence))\n  state.disabledSequenceCodes.add(sequenceCode)\n  return state\n}\n\nexport function addEventToHistory(state: State, event: KeyboardEvent): State {\n  const key = encodeEvent(event)\n  const previousKey = state.history.at(-1)\n  if (shouldOverride(previousKey, key)) {\n    state.history.pop()\n  }\n  state.history.push(key)\n  if (state.history.length > state.historySize) {\n    state.history.shift()\n  }\n  return state\n}\n\nexport function getMatchingCallbacks(state: State): Callback[] {\n  const callbacks = []\n  for (const sequenceCode of getEncodedSequencesFromHistory(state.history)) {\n    if (state.disabledSequenceCodes.has(sequenceCode)) {\n      continue\n    }\n    callbacks.push(...(state.bindings.get(sequenceCode) || []))\n  }\n  return callbacks\n}\n\nexport function handleEvent(state: State, event: KeyboardEvent): [State, Callback[]] {\n  state = addEventToHistory(state, event)\n  const fns = getMatchingCallbacks(state)\n  for (const fn of fns) fn(event)\n  return [state, fns]\n}\n\nexport function updateHistorySize(state: State): State {\n  state.historySize = 0\n  for (const code of state.bindings.keys()) {\n    state.historySize = Math.max(state.historySize, getSequenceSize(code))\n  }\n  return state\n}\n","import {Callback, HandlerInterface, State, Binding, Sequence} from './types'\nimport {addBinding, disableSequence, enableSequence, handleEvent, removeBinding} from './state'\n\nexport class Handler implements HandlerInterface {\n  constructor(protected state: State) {\n    this.add = this.add.bind(this)\n    this.remove = this.remove.bind(this)\n    this.handle = this.handle.bind(this)\n  }\n\n  add(...args: Binding): this {\n    const keys = args.slice(0, -1) as Sequence\n    const fn = args.at(-1) as Callback\n    this.state = addBinding(this.state, keys, fn)\n    return this\n  }\n\n  remove(...args: Binding): this {\n    const keys = args.slice(0, -1) as Sequence\n    const fn = args.at(-1) as Callback\n    this.state = removeBinding(this.state, keys, fn)\n    return this\n  }\n\n  enable(...keys: Sequence): this {\n    this.state = enableSequence(this.state, keys)\n    return this\n  }\n\n  disable(...keys: Sequence): this {\n    this.state = disableSequence(this.state, keys)\n    return this\n  }\n\n  handle(event: KeyboardEvent) {\n    const [state, fns] = handleEvent(this.state, event)\n    this.state = state\n    return fns.length > 0\n  }\n}\n","import {Handler} from './Handler'\nimport {createState} from './state'\n\nexport * from './types'\n\nexport function keys() {\n  return new Handler(createState())\n}\n\nexport default keys\n"],"mappings":"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","names":["modifiers","chars","codes","i","c","aliases","MODIFIERS_SIZE","CHARACTER_SIZE","KEY_SIZE","CHARACTER_SIZE","MODIFIERS_SIZE","MODIFIERS_UPPER_BOUND","ONE_KEY_UPPER_BOUND","TWO_KEYS_UPPER_BOUND","THREE_KEYS_UPPER_BOUND","normalizeKey","key","parts","x","aliases","encodeKey","code","codes","modifier","modifiers","getCharacterCode","encodedKey","MODIFIERS_SIZE","getModifiersCode","MODIFIERS_UPPER_BOUND","shouldOverride","previousKey","newKey","previousModifiers","newModifiers","encodeEvent","event","code","codes","modifier","modifiers","normalizeSequence","sequence","normalizeKey","encodeSequence","code","key","ONE_KEY_UPPER_BOUND","encodeKey","getSequenceSize","seq","ONE_KEY_UPPER_BOUND","TWO_KEYS_UPPER_BOUND","THREE_KEYS_UPPER_BOUND","getEncodedSequencesFromHistory","keys","result","sequenceCode","multiplier","i","createState","data","addBinding","state","sequence","fn","sequenceCode","encodeSequence","normalizeSequence","updateHistorySize","removeBinding","fns","enableSequence","disableSequence","addEventToHistory","event","key","encodeEvent","previousKey","shouldOverride","getMatchingCallbacks","callbacks","getEncodedSequencesFromHistory","handleEvent","code","getSequenceSize","Handler","state","args","keys","fn","addBinding","removeBinding","enableSequence","disableSequence","event","fns","handleEvent","keys","Handler","createState","src_default"]}