mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-14 02:51:43 +00:00
88 lines
3.8 KiB
TypeScript
88 lines
3.8 KiB
TypeScript
import type { KeyboardEvent as ReactKeyboardEvent } from 'react'
|
|
import type { Suggestion } from '@/hooks/useActiveSuggestions'
|
|
import { Autocomplete } from '@/components/ChatInput/Autocomplete'
|
|
import { FloatingOverlay } from '@/components/ChatInput/FloatingOverlay'
|
|
import { useTranslation } from '@/lib/use-translation'
|
|
|
|
export function DirectorySection(props: {
|
|
directory: string
|
|
suggestions: readonly Suggestion[]
|
|
selectedIndex: number
|
|
isDisabled: boolean
|
|
recentPaths: string[]
|
|
statusMessage?: string | null
|
|
statusTone?: 'warning' | 'error' | null
|
|
onDirectoryChange: (value: string) => void
|
|
onDirectoryFocus: () => void
|
|
onDirectoryBlur: () => void
|
|
onDirectoryKeyDown: (event: ReactKeyboardEvent<HTMLInputElement>) => void
|
|
onSuggestionSelect: (index: number) => void
|
|
onPathClick: (path: string) => void
|
|
}) {
|
|
const { t } = useTranslation()
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1.5 px-3 py-3">
|
|
<label className="text-xs font-medium text-[var(--app-hint)]">
|
|
{t('newSession.directory')}
|
|
</label>
|
|
<div className="relative">
|
|
<input
|
|
type="text"
|
|
placeholder={t('newSession.placeholder')}
|
|
value={props.directory}
|
|
onChange={(event) => props.onDirectoryChange(event.target.value)}
|
|
onKeyDown={props.onDirectoryKeyDown}
|
|
onFocus={props.onDirectoryFocus}
|
|
onBlur={props.onDirectoryBlur}
|
|
disabled={props.isDisabled}
|
|
className="w-full rounded-md border border-[var(--app-border)] bg-[var(--app-bg)] p-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--app-link)] disabled:opacity-50"
|
|
/>
|
|
{props.suggestions.length > 0 && (
|
|
<div className="absolute top-full left-0 right-0 z-10 mt-1">
|
|
<FloatingOverlay maxHeight={200}>
|
|
<Autocomplete
|
|
suggestions={props.suggestions}
|
|
selectedIndex={props.selectedIndex}
|
|
onSelect={props.onSuggestionSelect}
|
|
/>
|
|
</FloatingOverlay>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{props.recentPaths.length > 0 && (
|
|
<div className="flex flex-col gap-1 mt-1">
|
|
<span className="text-xs text-[var(--app-hint)]">{t('newSession.recent')}:</span>
|
|
<div className="flex flex-wrap gap-1">
|
|
{props.recentPaths.map((path) => (
|
|
<button
|
|
key={path}
|
|
type="button"
|
|
onClick={() => props.onPathClick(path)}
|
|
disabled={props.isDisabled}
|
|
className="rounded bg-[var(--app-subtle-bg)] px-2 py-1 text-xs text-[var(--app-fg)] hover:bg-[var(--app-secondary-bg)] transition-colors truncate max-w-[200px] disabled:opacity-50"
|
|
title={path}
|
|
>
|
|
{path}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{props.statusMessage ? (
|
|
<div
|
|
className={`mt-1 rounded-md px-2 py-1 text-xs ${
|
|
props.statusTone === 'error'
|
|
? 'bg-red-50 text-red-600 dark:bg-red-900/20 dark:text-red-400'
|
|
: 'bg-amber-500/10 text-[var(--app-hint)]'
|
|
}`}
|
|
>
|
|
{props.statusMessage}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|