/** * Local Shell picker — choose Zsh / Bash (default marked) */ import { useEffect, useRef, useState, useCallback } from 'react' import { Laptop, ChevronDown } from 'lucide-react' import { useTabStore } from '../../../stores/tabStore' import { shellDisplayFromPath } from '../../../utils/shellName' import { fallbackLocalShells } from '../../../utils/platformShells' export interface ShellOption { path: string name: string kind: string isDefault: boolean } interface LocalShellMenuProps { /** Compact icon-only trigger (sidebar) */ compact?: boolean className?: string } export default function LocalShellMenu({ compact = false, className = '' }: LocalShellMenuProps) { const { addTab } = useTabStore() const [open, setOpen] = useState(false) const [shells, setShells] = useState([]) const rootRef = useRef(null) useEffect(() => { const fallback = () => setShells(fallbackLocalShells() as ShellOption[]) void window.opsxApi?.localListShells?.().then((res) => { if (res?.success && res.shells?.length) { setShells(res.shells) } else { fallback() } }).catch(fallback) }, []) useEffect(() => { if (!open) return const onDoc = (e: MouseEvent) => { if (rootRef.current && !rootRef.current.contains(e.target as Node)) { setOpen(false) } } document.addEventListener('mousedown', onDoc) return () => document.removeEventListener('mousedown', onDoc) }, [open]) const openShell = useCallback( (opt?: ShellOption) => { const chosen = opt || shells.find((s) => s.isDefault) || shells[0] || { path: '', name: 'Local', kind: 'unknown', isDefault: true, } const display = shellDisplayFromPath(chosen.path || chosen.name) addTab({ title: display.title, type: 'terminal', isLocal: true, localShellPath: chosen.path || undefined, localShellKind: display.kind, }) setOpen(false) }, [addTab, shells] ) const badgeClass = (kind: string) => { if (kind === 'zsh') return 'bg-teal-700 text-teal-100' if (kind === 'bash') return 'bg-dark-600 text-emerald-300' if (kind === 'fish') return 'bg-violet-700 text-violet-100' return 'bg-dark-600 text-content-secondary' } return (
{compact ? (
) : ( )} {open && (
本地 Shell
{shells.map((s) => ( ))}
)}
) } /** Programmatic open helper for menu / palette */ // eslint-disable-next-line react-refresh/only-export-components export function openLocalShellTab(shell?: { path?: string; kind?: string; name?: string }) { const { addTab } = useTabStore.getState() const display = shellDisplayFromPath(shell?.path || shell?.name) addTab({ title: display.title, type: 'terminal', isLocal: true, localShellPath: shell?.path, localShellKind: shell?.kind || display.kind, }) }