Download src/components/features/plugins/plugins-toolbar.tsx from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 3.28 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/plugins/plugins-toolbar.tsx
- Command line
-
hf download hf://SaylorTwift/openhands/src/components/features/plugins/plugins-toolbar.tsx
-
curl -L -o plugins-toolbar.tsx https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/plugins/plugins-toolbar.tsx
3.28 kB
| import { Search, X } from "lucide-react"; | |
| import { useTranslation } from "react-i18next"; | |
| import { I18nKey } from "#/i18n/declaration"; | |
| import { cn } from "#/utils/utils"; | |
| import type { PluginStatusFilter } from "./build-plugins-view-model"; | |
| interface PluginsToolbarProps { | |
| search: string; | |
| onSearchChange: (value: string) => void; | |
| statusFilter: PluginStatusFilter; | |
| onStatusFilterChange: (filter: PluginStatusFilter) => void; | |
| } | |
| const STATUS_FILTERS: ReadonlyArray<{ | |
| value: PluginStatusFilter; | |
| labelKey: I18nKey; | |
| }> = [ | |
| { value: "all", labelKey: I18nKey.SETTINGS$PLUGINS_FILTER_ALL }, | |
| { value: "installed", labelKey: I18nKey.SETTINGS$PLUGINS_FILTER_INSTALLED }, | |
| { value: "available", labelKey: I18nKey.SETTINGS$PLUGINS_FILTER_AVAILABLE }, | |
| { value: "local", labelKey: I18nKey.SETTINGS$PLUGINS_FILTER_LOCAL }, | |
| ]; | |
| export function PluginsToolbar({ | |
| search, | |
| onSearchChange, | |
| statusFilter, | |
| onStatusFilterChange, | |
| }: PluginsToolbarProps) { | |
| const { t } = useTranslation("openhands"); | |
| return ( | |
| <div data-testid="plugins-toolbar" className="flex items-stretch gap-2"> | |
| <div | |
| className={cn( | |
| "relative flex flex-1 min-w-0 items-center", | |
| "rounded-lg border border-[var(--oh-border)] bg-base-secondary", | |
| "focus-within:border-white/40 focus-within:ring-1 focus-within:ring-white/20", | |
| "transition-colors", | |
| )} | |
| > | |
| <Search | |
| className="ml-3 h-4 w-4 shrink-0 text-tertiary-alt" | |
| aria-hidden | |
| /> | |
| <input | |
| data-testid="plugins-search-input" | |
| type="search" | |
| value={search} | |
| onChange={(e) => onSearchChange(e.target.value)} | |
| placeholder={t(I18nKey.SETTINGS$PLUGINS_SEARCH_PLACEHOLDER)} | |
| aria-label={t(I18nKey.SETTINGS$PLUGINS_SEARCH_PLACEHOLDER)} | |
| className={cn( | |
| "flex-1 min-w-0 bg-transparent border-0 outline-none", | |
| "px-3 py-2 text-sm placeholder:text-tertiary-alt", | |
| "[&::-webkit-search-cancel-button]:hidden", | |
| )} | |
| /> | |
| {search ? ( | |
| <button | |
| type="button" | |
| onClick={() => onSearchChange("")} | |
| aria-label={t(I18nKey.MCP$SEARCH_CLEAR)} | |
| className="mr-2 p-1 rounded text-tertiary-alt hover:text-white cursor-pointer" | |
| > | |
| <X className="h-4 w-4" aria-hidden /> | |
| </button> | |
| ) : null} | |
| </div> | |
| <div | |
| data-testid="plugins-status-filter" | |
| className="flex shrink-0 items-center gap-1 rounded-lg border border-[var(--oh-border)] bg-base-secondary p-1" | |
| > | |
| {STATUS_FILTERS.map((filter) => ( | |
| <button | |
| key={filter.value} | |
| type="button" | |
| data-testid={`plugins-filter-${filter.value}`} | |
| aria-pressed={statusFilter === filter.value} | |
| onClick={() => onStatusFilterChange(filter.value)} | |
| className={cn( | |
| "cursor-pointer rounded-md px-3 py-1 text-xs transition-colors", | |
| statusFilter === filter.value | |
| ? "bg-surface-raised text-white" | |
| : "text-tertiary-alt hover:text-white", | |
| )} | |
| > | |
| {t(filter.labelKey)} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |