From e222b9f7a2fe365e70e6129f27f4a70238388eab Mon Sep 17 00:00:00 2001 From: p-sw Date: Fri, 7 Jun 2024 21:54:34 +0900 Subject: [PATCH] feat(cli): improve search command and user interaction The search command in the CLI is enhanced to recognize search queries as command arguments. An improved interaction with the search box is also introduced, including key event handlers for up and down arrows and escape key, allowing users to navigate more intuitively. keyboard-based selection of suggestions is now implemented and the helper message is updated to match these interaction changes. --- packages/cli/src/commands/search.tsx | 19 +++++++++++++++---- packages/cli/src/components/SearchBox.tsx | 21 ++++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/commands/search.tsx b/packages/cli/src/commands/search.tsx index b26e786..a120d26 100644 --- a/packages/cli/src/commands/search.tsx +++ b/packages/cli/src/commands/search.tsx @@ -1,17 +1,21 @@ -import {Command} from '@oclif/core' +import {Command, Args} from '@oclif/core' import {render} from 'ink' import {SearchBox} from '../components/SearchBox.js' import {getAvailableComponentNames, getRegistry} from '../helpers/registry.js' import React from 'react' export default class Search extends Command { + static override args = { + query: Args.string({description: 'search query'}), + } + static override description = 'Search components.' static override examples = ['<%= config.bin %> <%= command.id %>'] - static override flags = {} - public async run(): Promise { + const {args} = await this.parse(Search) + const registryResult = await getRegistry() if (!registryResult.ok) { this.error(registryResult.message) @@ -19,6 +23,13 @@ export default class Search extends Command { const registry = registryResult.registry const componentNames = await getAvailableComponentNames(registry) - render() + await render( + k.escape && app.exit()} + />, + ).waitUntilExit() } } diff --git a/packages/cli/src/components/SearchBox.tsx b/packages/cli/src/components/SearchBox.tsx index e40ee21..4d1e9ff 100644 --- a/packages/cli/src/components/SearchBox.tsx +++ b/packages/cli/src/components/SearchBox.tsx @@ -3,20 +3,23 @@ import {getSuggestion} from '../helpers/search.js' import Input from 'ink-text-input' import {Divider} from './Divider.js' import Spinner from 'ink-spinner' -import {Box, Text} from 'ink' +import {Box, Text, useInput, useApp, type Key} from 'ink' export function SearchBox({ components, helper, initialQuery, + onKeyDown, }: { components: string[] helper: string initialQuery?: string + onKeyDown?: (i: string, k: Key, app: ReturnType) => void }) { const [query, setQuery] = useState(initialQuery ?? '') const [isLoading, setLoading] = useState(false) const [suggestions, setSuggestions] = useState([]) + const [selected, setSelected] = useState(0) useEffect(() => { setLoading(true) @@ -26,6 +29,18 @@ export function SearchBox({ }) }, [query]) + const app = useApp() + + useInput((i, k) => { + if (k.downArrow) { + setSelected((p) => (p >= suggestions.length - 1 ? 0 : p + 1)) + } + if (k.upArrow) { + setSelected((p) => (p <= 0 ? suggestions.length - 1 : p - 1)) + } + onKeyDown?.(i, k, app) + }) + return ( {helper} @@ -40,10 +55,10 @@ export function SearchBox({ ) : ( - {suggestions.map((name) => { + {suggestions.map((name, index) => { return ( - {name} + {name} ) })}