명령 팔레트
어디로 갈지 · 무엇을 할지를 한 칸에서 찾는 대화상자입니다. 대화상자 층(50)의 판지 한 장이라 오른쪽 위 모서리를 꺾고, 포커스는 검색칸에 머문 채 ↑ ↓ 로 결과를 고릅니다.
- import
import { CommandPalette, CommandInput, CommandList, CommandGroup, CommandItem } from "@uyoolabs/ui/command";- 쓰는 경우
- 화면 이동 · 항목 열기(문서 · 작업 · 채널 · 사람) · 동작 실행(새 작업 · 사이드바 접기 · 테마)을 한 칸에서 찾습니다. 빈 칸에는 최근 · 지금 층의 이동 · 자주 쓰는 동작만 두고, 치면 종류별로 묶어 그룹당 5개까지 보입니다. 결과가 없어도 ‘전체 검색 · AI 에게 묻기’ 를 끝에 남깁니다. 단축키가 있으면 키캡으로 보여 팔레트가 단축키를 가르칩니다.
- 쓰지 않는 경우
- 폼 칸에서 값 하나를 고르는 데 쓰지 않습니다(콤보박스 · 선택 목록). 설정 폼 · 긴 편집을 팔레트 안에서 하지 않습니다(대화상자 · 페이지). 일치 표시를 파랑 · 굵기로 하지 않습니다. 다른 대화상자 위에 겹쳐 열지 않습니다. 하위 단계를 세 단계 넘게 파거나 항목을 두 줄로 쓰지 않습니다.
- 껍데기만
- 팔레트는 칸 · 목록 · 키 · 거르기 · 알림만 맡습니다. 무엇을 보일지(항목 · 서버 검색 · 에이전트 질문)는 앱이 넘깁니다. 서버가 거른 결과만 보이면 filter={false} 로 둡니다.
기본
버튼으로 열거나 ⌘K · Ctrl+K 를 누릅니다. 닫으면 연 자리로 포커스가 돌아갑니다.
또는 컨트롤 K
하위 단계
고른 항목 안으로 한 칸 들어갑니다. 하위 단계의 빈 칸에서 숫자 키(1–4)는 그 항목을 고릅니다.
위 데모에서 ‘상태 바꾸기’ 를 고르면 들어갑니다.
키보드
APG Combobox(listbox 팝업)를 모달 Dialog 안에 둔 모양입니다. 활성 줄은 aria-activedescendant, 결과 수는 입력이 멈추고 500ms 뒤 polite 로 한 번 읽힙니다.
| 키 | 동작 |
|---|---|
| ⌘ K · Ctrl K | 열기 / 닫기. 다른 대화상자가 떠 있으면 열지 않습니다. 브라우저의 빠른 찾기를 막습니다 |
| ↑ · ↓ | 활성 이동. 끝에서 돌고 비활성 줄도 지나갑니다(이유를 읽습니다) |
| PgUp · PgDn | 8칸씩 옮깁니다 |
| Enter | 실행합니다. 한 · 일 조합 중이면 무시합니다(isComposing). 비활성이면 아무것도 하지 않습니다 |
| ⌘ / Ctrl + Enter | 링크 항목을 새 탭으로 엽니다 |
| Backspace | 빈 칸에서 한 단계 위로 |
| Esc | 단계와 상관없이 한 번에 닫고 연 자리로 포커스를 돌립니다 |
| Home · End | 칸 안의 글자 이동으로 남깁니다(목록이 가로채지 않습니다) |
| ? | 칸에 쓰는 글자입니다(단축키 목록을 열지 않습니다) |
내장 문구
lang(ko · en · ja)으로 고르고 labels 로 일부를 바꿉니다(ADR 0011).
| 키 | ko | en | ja |
|---|---|---|---|
| title | 명령 팔레트 | Command palette | コマンドパレット |
| placeholder | 무엇을 찾거나 실행할까요? | Search or run a command… | 検索またはコマンドを実行… |
| results | 결과 | Results | 結果 |
| resultCount(n) | 결과 {n}개 | {n} results | 結果 {n}件 |
| noResults(n) | 결과 없음. 다른 길 {n}개 | No results. {n} other options | 結果なし。ほかの方法 {n}件 |
| inPage(scope, n) | {scope}, 결과 {n}개 | {scope}, {n} results | {scope}、結果 {n}件 |
| empty(q) | ‘{q}’ 에 맞는 결과가 없습니다 | No results for “{q}” | 「{q}」に一致する結果はありません |
| emptyHint | 철자를 확인하거나 다른 낱말로 찾습니다. | Check the spelling or try other words. | 綴りを確認するか、別の言葉で検索してください。 |
| loading | 찾는 중입니다 | Searching… | 検索しています |
| failed · retry | 실행하지 못했습니다. · 다시 시도 | Couldn’t run the command. · Try again | 実行できませんでした。 · 再試行 |
| footer* | 이동 · 열기 · 새 탭 · 한 단계 위로 · 단축키 | Move · Open · New tab · Back · Shortcuts | 移動 · 開く · 新しいタブ · 戻る · ショートカット |
props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
CommandPalette
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| open | boolean | — | |
| defaultOpen | boolean | false | |
| onOpenChange | (open: boolean) => void | — | |
| shortcut | string | null | "mod+k" | 전역 단축키. 기본 "mod+k"(mac ⌘ · 그 밖 Ctrl, 어느 쪽이든 받는다). null 이면 걸지 않는다 |
| canOpen | () => boolean | — | 열어도 되는가. 기본은 열린 모달(다른 대화상자 · 시트)이 없을 때 |
| page | string | null | — | 하위 단계(CommandPage 의 id). null 이 첫 단계 |
| defaultPage | string | null | null | |
| onPageChange | (page: string | null) => void | — | |
| filter | boolean | true | 친 글로 항목을 거른다(기본 true). 서버가 거른 결과만 보일 때 false |
| matchOptions | MatchOptions | — | 거르기 · 하이라이트 정규화. 기본: 한국어면 초성 검색, 일본어면 가나 접기 |
| returnFocusRef | RefObject<HTMLElement | null> | — | 닫은 뒤 포커스를 돌려줄 요소. 없으면 열 때 포커스가 있던 요소(트리거) |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<CommandPaletteLabels> | — | |
| className | string | — | |
| children필수 | ReactNode | — | CommandInput · CommandList · CommandFooter |
CommandInput
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value | string | — | 칸의 글(제어) |
| defaultValue | string | — | 처음 칸의 글(비제어) |
| onValueChange | (value: string) => void | — |
이 밖에 <input> props(value · onChange · defaultValue 제외)를 그대로 받습니다.
CommandGroup
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| heading필수 | ReactNode | — | 그룹 머리(12/600 muted). role=group 의 이름이다 |
| children | ReactNode | — | |
| busy | boolean | string | — | 이 그룹이 서버 결과를 기다린다(300ms 뒤 스피너 줄, aria-busy). 문자열이면 그 줄의 글 |
| error | ReactNode | — | 이 그룹을 불러오지 못했다. 그룹 안 오류 줄 + 다시 시도(↑ ↓ 로 닿는 줄이다) |
| onRetry | () => void | — | |
| className | string | — |
CommandItem
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value필수 | string | — | 고유 키(activedescendant id 의 바탕) |
| keywords | readonly string[] | — | 보이지 않는 찾기어(별칭 · 로마자 · 영어 이름). 초성은 한국어 화면에서 저절로 맞는다 |
| icon | ReactNode | — | 왼쪽 20 칸의 아이콘 · 아바타 |
| hint | ReactNode | — | 오른쪽 보조 글자(경로 · 종류). 45% 에서 자른다 |
| shortcut | readonly string[] | — | 키마다 키캡. 하위 단계의 빈 칸에서 한 글자 단축키(1–4)는 그 항목을 고른다 |
| disabled | boolean | string | — | 고를 수 없다. 문자열이면 그 이유를 힌트 자리에 쓴다(툴팁이 아니다) |
| href | string | — | 있으면 링크로 그린다(LinkProvider 의 라우터). ⌘/Ctrl+Enter 는 새 탭 |
| onSelect | () => void | Promise<void> | — | 고르면 부른다. Promise 면 끝날 때까지 그 줄에 스피너, 실패하면 그룹 오류 줄로. 끝나면 닫는다 |
| forceMount | boolean | false | 거르지 않고 늘 보인다(‘다른 길’ — 전체 검색 · AI 에게 묻기). 결과 수 대신 ‘다른 길’ 로 센다 |
| keepOpen | boolean | false | 고른 뒤 닫지 않는다(하위 단계로 들어가는 항목은 저절로 열려 있다) |
| textValue | string | — | 거르기에 쓸 글. 없으면 children 이 문자열일 때 그것 |
| children필수 | ReactNode | — | 이름. 문자열이면 맞은 글자를 칠한다(Highlight) |
| className | string | — |
CommandPage
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| id필수 | string | — | 단계 id. 팔레트의 page 가 이 값이면 보인다 |
| scope필수 | ReactNode | — | 칸 앞 범위 칩(‘PAY-142 › 상태 바꾸기’) |
| scopeLabel | string | — | 범위의 읽는 이름. 들어갈 때 ‘상태 바꾸기, 결과 4개’ 로 알린다 |
| placeholder | string | — | 이 단계의 칸 자리 표시 |
| parent | string | — | Backspace(빈 칸)로 돌아갈 단계. 없으면 첫 단계(두 단계까지만 판다) |
| children | ReactNode | — |
CommandEmpty
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| children | ReactNode | — | |
| className | string | — |
CommandLoading
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| children | ReactNode | — | |
| className | string | — |
CommandFooter
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| newTab | boolean | true | ⌘↵ 새 탭 안내(링크 항목이 있을 때, 기본 true) |
| shortcuts | boolean | false | 오른쪽 끝 ‘? 단축키’ 안내 |
| children | ReactNode | — | 안내를 직접 그린다 |
| className | string | — |