mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-04-28 19:50:34 +00:00
Feat skills (#1221)
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Pre-commit / pre-commit (push) Waiting to run
Test / Run Python Tests (push) Waiting to run
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Pre-commit / pre-commit (push) Waiting to run
Test / Run Python Tests (push) Waiting to run
Co-authored-by: Pakchoioioi <happy.regina.bai@gmail.com> Co-authored-by: Douglas Lai <115660088+Douglasymlai@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Douglas <douglas.ym.lai@gmail.com> Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com> Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
This commit is contained in:
parent
d6142b5607
commit
a23c30db13
84 changed files with 4411 additions and 159 deletions
|
|
@ -12,24 +12,161 @@
|
|||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Search } from 'lucide-react';
|
||||
import { TooltipSimple } from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { Search, X } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type SearchInputVariant = 'default' | 'icon';
|
||||
|
||||
interface SearchInputProps {
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
placeholder?: string;
|
||||
variant?: SearchInputVariant;
|
||||
/** Optional: called when user presses Enter in the field (e.g. to submit search) */
|
||||
onSearch?: () => void;
|
||||
/** Tooltip for the search icon button (icon variant). Defaults to agents.search-tooltip */
|
||||
searchTooltip?: string;
|
||||
/** Tooltip for the clear (X) button (icon variant). Defaults to agents.clear-search-tooltip */
|
||||
clearTooltip?: string;
|
||||
}
|
||||
|
||||
export default function SearchInput({ value, onChange }: SearchInputProps) {
|
||||
const COLLAPSED_WIDTH = 40;
|
||||
const EXPANDED_WIDTH = 240;
|
||||
|
||||
export default function SearchInput({
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
variant = 'default',
|
||||
onSearch,
|
||||
searchTooltip,
|
||||
clearTooltip,
|
||||
}: SearchInputProps) {
|
||||
const { t } = useTranslation();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [userExpanded, setUserExpanded] = useState(false);
|
||||
const isExpanded = userExpanded || value.length > 0;
|
||||
|
||||
const expand = useCallback(() => {
|
||||
setUserExpanded(true);
|
||||
}, []);
|
||||
|
||||
const collapse = useCallback(() => {
|
||||
setUserExpanded(false);
|
||||
onChange({ target: { value: '' } } as React.ChangeEvent<HTMLInputElement>);
|
||||
}, [onChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userExpanded && inputRef.current) {
|
||||
const id = requestAnimationFrame(() => {
|
||||
inputRef.current?.focus();
|
||||
});
|
||||
return () => cancelAnimationFrame(id);
|
||||
}
|
||||
}, [userExpanded]);
|
||||
|
||||
const searchLabel = searchTooltip ?? t('agents.search-tooltip');
|
||||
const clearLabel = clearTooltip ?? t('agents.clear-search-tooltip');
|
||||
const place = placeholder ?? t('setting.search-mcp');
|
||||
|
||||
if (variant === 'icon') {
|
||||
return (
|
||||
<motion.div
|
||||
className={cn(
|
||||
'flex items-center justify-center py-0.5 overflow-hidden rounded-lg border border-solid border-transparent bg-transparent',
|
||||
'focus-within:border-input-border-focus focus-within:bg-input-bg-input',
|
||||
'hover:border-transparent hover:bg-surface-tertiary'
|
||||
)}
|
||||
initial={false}
|
||||
animate={{ width: isExpanded ? EXPANDED_WIDTH : COLLAPSED_WIDTH }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 400,
|
||||
damping: 30,
|
||||
}}
|
||||
>
|
||||
<AnimatePresence mode="wait">
|
||||
{!isExpanded ? (
|
||||
<motion.div
|
||||
key="icon"
|
||||
className="flex shrink-0 items-center justify-center"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
<TooltipSimple content={searchLabel}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={expand}
|
||||
aria-label={searchLabel}
|
||||
>
|
||||
<Search />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="input"
|
||||
className="flex min-w-0 flex-1 items-center gap-0 pr-1"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
<span className="pointer-events-none ml-2 inline-flex h-4 w-4 shrink-0 items-center justify-center text-icon-secondary">
|
||||
<Search className="h-4 w-4" />
|
||||
</span>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={place}
|
||||
onBlur={() => {
|
||||
if (value.length === 0) setUserExpanded(false);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSearch?.();
|
||||
}
|
||||
}}
|
||||
className="h-6 min-w-0 flex-1 bg-transparent pl-2 text-label-sm text-text-heading outline-none placeholder:text-text-label"
|
||||
/>
|
||||
<TooltipSimple content={clearLabel}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="shrink-0 rounded-full text-icon-secondary"
|
||||
onClick={collapse}
|
||||
aria-label={clearLabel}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
size="sm"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={t('setting.search-mcp')}
|
||||
placeholder={place}
|
||||
leadingIcon={<Search className="h-5 w-5 text-icon-secondary" />}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue