import React from 'react'; import { Button } from '@/components/ui/button'; import { Icon } from '@/components/icon/Icon'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { useI18n } from '@/lib/i18n'; import { BrowserAddressSuggestions } from './BrowserAddressSuggestions'; import type { BrowserHistoryEntry } from '@/lib/browser/history'; import { cn } from '@/lib/utils'; import type { IconName } from '@/components/icon/icons'; type ToolbarButtonProps = { icon: IconName; label: string; onClick: () => void; disabled?: boolean; pressed?: boolean; }; const ToolbarButton: React.FC = ({ icon, label, onClick, disabled, pressed }) => ( {label} ); export type BrowserToolbarProps = { address: string; /** Addresses already visited in this project, offered while typing. */ suggestions?: readonly BrowserHistoryEntry[]; onForgetSuggestion?: (url: string) => void; onAddressChange: (value: string) => void; onSubmit: (value: string) => void; onBack: () => void; onForward: () => void; onReload: () => void; onOpenExternal: () => void; canGoBack: boolean; canGoForward: boolean; isLoading: boolean; /** These need a real Chromium host; hidden without one. */ onAnnotate?: () => void; onOpenDevTools?: () => void; isAnnotating?: boolean; onHardReload?: () => void; onZoomIn?: () => void; onZoomOut?: () => void; onZoomReset?: () => void; /** Whole percent, e.g. 110. Controls hide at 100 to keep the bar quiet. */ zoomPercent?: number; onClearCookies?: () => void; onClearCache?: () => void; onToggleDeviceBar?: () => void; isDeviceBarOpen?: boolean; }; export const BrowserToolbar: React.FC = ({ address, suggestions = [], onForgetSuggestion, onAddressChange, onSubmit, onBack, onForward, onReload, onOpenExternal, canGoBack, canGoForward, isLoading, onAnnotate, onOpenDevTools, isAnnotating, onHardReload, onZoomIn, onZoomOut, onZoomReset, zoomPercent = 100, onClearCookies, onClearCache, onToggleDeviceBar, isDeviceBarOpen, }) => { const { t } = useI18n(); const [isAddressFocused, setIsAddressFocused] = React.useState(false); const [activeSuggestion, setActiveSuggestion] = React.useState(-1); const visibleSuggestions = isAddressFocused ? suggestions : []; // A new list is a new choice; keeping an old index would highlight whatever // happens to sit in that position now. React.useEffect(() => { setActiveSuggestion(-1); }, [address, isAddressFocused]); const submitAddress = (value: string) => { setIsAddressFocused(false); setActiveSuggestion(-1); onSubmit(value); }; const onAddressKeyDown = (event: React.KeyboardEvent) => { if (visibleSuggestions.length === 0) return; if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { event.preventDefault(); const step = event.key === 'ArrowDown' ? 1 : -1; const count = visibleSuggestions.length; // Wraps through "nothing selected", so the typed address stays reachable. setActiveSuggestion((current) => { const next = current + step; if (next < -1) return count - 1; if (next >= count) return -1; return next; }); return; } if (event.key === 'Escape' && activeSuggestion >= 0) { event.preventDefault(); setActiveSuggestion(-1); return; } if (event.key === 'Enter' && activeSuggestion >= 0) { event.preventDefault(); const chosen = visibleSuggestions[activeSuggestion]; if (chosen) submitAddress(chosen.url); } }; return (
{onHardReload ? ( ) : null}
{ event.preventDefault(); submitAddress(address); }} > onAddressChange(event.target.value)} onFocus={() => setIsAddressFocused(true)} onBlur={() => setIsAddressFocused(false)} onKeyDown={onAddressKeyDown} spellCheck={false} autoComplete="off" role="combobox" aria-expanded={visibleSuggestions.length > 0} aria-controls="openchamber-browser-address-suggestions" className={cn( 'h-6 w-full rounded-full border border-border/50 bg-[var(--surface-elevated)] px-3', 'typography-micro text-foreground outline-none focus:border-[var(--interactive-focus-ring)]', )} aria-label={t('contextPanel.browser.addressAria')} />
onForgetSuggestion?.(url)} onHighlight={setActiveSuggestion} />
{onZoomOut && onZoomIn ? (
{zoomPercent !== 100 && onZoomReset ? ( {t('contextPanel.browser.zoomReset')} ) : null}
) : null} {onClearCookies ? ( ) : null} {onClearCache ? ( ) : null} {onToggleDeviceBar ? ( ) : null} {onAnnotate ? ( ) : null} {onOpenDevTools ? ( ) : null}
); };