useKeymap으로 키보드 단축키 설정
useKeymap으로 바인딩을 한 번 선언하면 Text.js가 키 입력을 전달하고, ? 도움말 화면에 나열하고, 푸터에 표시하고, Ctrl+K 명령 팔레트에 추가해요.
지금까지 Text.js 앱은 useInput에서 키를 처리하고, <KeyHint> 푸터에 같은 키 목록을 수동으로 한 번 더 작성했어요. useKeymap은 선언 하나로 둘 다 처리해요.
import { CommandPalette, KeyHelp, KeyHints, StatusBar, Text, useKeymap } from "@textjs/react";export default function Sessions() { useKeymap( { "j, down": { label: "Next", run: next, hint: true }, "k, up": { label: "Previous", run: previous }, "g g": { label: "Go to top", run: top }, "ctrl+s": { label: "Save", run: save, hint: true }, }, { group: "Navigation" }, ); return ( <> <StatusBar right={<Text color="subtle">main</Text>}> <KeyHints /> </StatusBar> <CommandPalette commands={commands} /> <KeyHelp /> </> );}j를 누르면 next가 실행돼요. macOS에서는 해당 바인딩에 hint가 설정되어 있어서 <KeyHints>가 푸터에 j/↓ Next와 ⌃S Save를 표시해요. ?는 Navigation 아래에 4개 바인딩을 모두 모은 도움말 화면을 열고, Ctrl+K는 이 바인딩을 검색할 수 있는 팔레트를 열어요.
키 문법
| 문법 | 의미 |
|---|---|
ctrl+k | 보조 키: ctrl, alt, shift, meta, mod |
mod+k | ⌘ 또는 Ctrl 중 누르고 있는 키 |
j, down | 대체 키 |
g g | 키 사이 간격이 최대 1초인 시퀀스 |
G | 대문자는 Shift를 포함한다는 의미 |
? | 출력 가능한 키는 해당 문자와 일치 |
바인딩은 핸들러이거나 label, group, hint, command, disabled, noRepeat가 있는 객체예요. command: false는 레이블이 있는 바인딩을 팔레트에서 제외해요. 레이블이 없는 바인딩도 작동하지만 도움말 화면과 팔레트에는 표시되지 않아요.
라우팅
각 키맵은 useInput 핸들러를 하나 등록하므로 기존 입력 규칙이 적용돼요. 에디터는 우선순위 10에서, 키맵은 0에서 입력을 수신하므로 포커스된 <Input>이 키를 먼저 처리해요. 일치하는 키는 전파를 중단해요. 키맵이 global: true를 전달하지 않으면 열린 <Modal>은 바깥쪽 바인딩을 비활성화해요.
시퀀스는 해당 핸들러 안에서 매칭돼요. 시퀀스를 시작하는 키는 대기 상태로 유지되며 전파를 중단하고, 다음 키가 sequenceTimeout(기본값 1,000ms)보다 늦게 들어오면 시퀀스가 초기화돼요:
if (p.events.length > 0 && now - p.at > sequenceTimeout) p.events = [];루트마다 하나의 레지스트리
레이블이 있는 바인딩은 콘솔 루트에 속한 레지스트리에 등록돼요. <KeyHelp>, <KeyHints>, <CommandPalette>는 이 레지스트리에서 바인딩을 읽고, 직접 만든 컴포넌트도 useKeymapBindings()로 읽을 수 있어요. 모달이 열려 있는 동안 레지스트리는 모달 내부의 바인딩만 반환하므로 도움말 화면에는 현재 작동하는 키가 표시돼요.
키맵은 보통 인라인으로 작성하므로 렌더링할 때마다 새 객체가 생겨요. 이 비용을 낮게 유지하기 위해 키맵은 화면에 표시되는 형태가 바뀔 때만 레이블을 다시 등록해요:
// Publish labels only when the visible shape changes (inline keymap objects are fine).const signature = compiled .map((c) => `${c.keys}\u0000${c.binding.label ?? ""}\u0000${c.binding.group ?? ""}\u0000${c.binding.hint ? 1 : 0}${c.binding.command === false ? 0 : 1}\u0000${c.binding.disabled ? 1 : 0}`) .join("\u0001");키가 들어올 때 ref에서 핸들러를 읽으므로 렌더링마다 새로 생기는 클로저가 오래된 상태를 참조하지 않아요.
도움말 화면과 팔레트
<KeyHelp>는 ?로 열리며 현재 활성화된 모든 레이블 있는 바인딩을 그룹별로 표시해요. keys와 title로 트리거와 제목을 바꿔요.
<CommandPalette>는 Ctrl+K 또는 Ctrl+P로 열려요. commands와 레이블이 있는 모든 바인딩을 퍼지 매칭으로 검색하고, 일치하는 글자를 강조해요. 명령을 실행하기 전에 닫히므로 명령이 자체 모달을 열 수 있어요.
formatKeys는 표시용 바인딩을 렌더링해요. formatKeys("ctrl+shift+p")는 macOS에서 ⌃⇧P를, 다른 환경에서는 ctrl+shift+P를 반환하며, <Kbd>는 같은 형식으로 단일 키를 렌더링해요.
useKeymap, <KeyHelp>, <KeyHints>, <StatusBar>, <CommandPalette>는 Text.js 1.0에 포함돼요. 레퍼런스는 키보드에 있어요.