| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- import { isDeepEqual } from "remeda"
- import type { ParsedKey } from "@opentui/core"
- export namespace Keybind {
- /**
- * Keybind info derived from OpenTUI's ParsedKey with our custom `leader` field.
- * This ensures type compatibility and catches missing fields at compile time.
- */
- export type Info = Pick<ParsedKey, "name" | "ctrl" | "meta" | "shift" | "super"> & {
- leader: boolean // our custom field
- }
- export function match(a: Info | undefined, b: Info): boolean {
- if (!a) return false
- const normalizedA = { ...a, super: a.super ?? false }
- const normalizedB = { ...b, super: b.super ?? false }
- return isDeepEqual(normalizedA, normalizedB)
- }
- /**
- * Convert OpenTUI's ParsedKey to our Keybind.Info format.
- * This helper ensures all required fields are present and avoids manual object creation.
- */
- export function fromParsedKey(key: ParsedKey, leader = false): Info {
- return {
- name: key.name,
- ctrl: key.ctrl,
- meta: key.meta,
- shift: key.shift,
- super: key.super ?? false,
- leader,
- }
- }
- export function toString(info: Info | undefined): string {
- if (!info) return ""
- const parts: string[] = []
- if (info.ctrl) parts.push("ctrl")
- if (info.meta) parts.push("alt")
- if (info.super) parts.push("super")
- if (info.shift) parts.push("shift")
- if (info.name) {
- if (info.name === "delete") parts.push("del")
- else parts.push(info.name)
- }
- let result = parts.join("+")
- if (info.leader) {
- result = result ? `<leader> ${result}` : `<leader>`
- }
- return result
- }
- export function parse(key: string): Info[] {
- if (key === "none") return []
- return key.split(",").map((combo) => {
- // Handle <leader> syntax by replacing with leader+
- const normalized = combo.replace(/<leader>/g, "leader+")
- const parts = normalized.toLowerCase().split("+")
- const info: Info = {
- ctrl: false,
- meta: false,
- shift: false,
- leader: false,
- name: "",
- }
- for (const part of parts) {
- switch (part) {
- case "ctrl":
- info.ctrl = true
- break
- case "alt":
- case "meta":
- case "option":
- info.meta = true
- break
- case "super":
- info.super = true
- break
- case "shift":
- info.shift = true
- break
- case "leader":
- info.leader = true
- break
- case "esc":
- info.name = "escape"
- break
- default:
- info.name = part
- break
- }
- }
- return info
- })
- }
- }
|