()
| 18 | 'inline-flex h-9 items-center rounded-[var(--radius-sm)] px-[var(--space-2_5)] text-[var(--text-sm)] leading-none whitespace-nowrap transition-colors duration-[var(--duration-faster)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'; |
| 19 | |
| 20 | export function TopBar() { |
| 21 | const t = useT(); |
| 22 | const setView = useCodesignStore((s) => s.setView); |
| 23 | const view = useCodesignStore((s) => s.view); |
| 24 | const previousView = useCodesignStore((s) => s.previousView); |
| 25 | const currentDesignId = useCodesignStore((s) => s.currentDesignId); |
| 26 | const designs = useCodesignStore((s) => s.designs); |
| 27 | const currentDesign = designs.find((d) => d.id === currentDesignId); |
| 28 | const hubTab = useCodesignStore((s) => s.hubTab); |
| 29 | const setHubTab = useCodesignStore((s) => s.setHubTab); |
| 30 | const unreadErrorCount = useCodesignStore((s) => s.unreadErrorCount); |
| 31 | const refreshDiagnosticEvents = useCodesignStore((s) => s.refreshDiagnosticEvents); |
| 32 | const openSettingsTab = useCodesignStore((s) => s.openSettingsTab); |
| 33 | |
| 34 | // Pull-based: refresh the diagnostic counter on mount so a page reload |
| 35 | // surfaces errors recorded while the window was closed. No polling. |
| 36 | // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only effect |
| 37 | useEffect(() => { |
| 38 | void refreshDiagnosticEvents(); |
| 39 | }, []); |
| 40 | |
| 41 | return ( |
| 42 | <header |
| 43 | className="h-[var(--size-titlebar-height)] shrink-0 flex items-center gap-[var(--space-3)] pr-[var(--space-5)] select-none" |
| 44 | style={{ |
| 45 | ...dragStyle, |
| 46 | paddingLeft: 'var(--size-titlebar-pad-left)', |
| 47 | borderBottom: '1px solid oklch(0.22 0.025 50 / 0.08)', |
| 48 | background: 'var(--color-background)', |
| 49 | }} |
| 50 | > |
| 51 | <div className="flex items-center gap-[var(--space-6)] min-w-0 h-full"> |
| 52 | <div className="shrink-0"> |
| 53 | <Wordmark badge={`v${__APP_VERSION__}`} size="titlebar" /> |
| 54 | </div> |
| 55 | |
| 56 | {view === 'settings' ? ( |
| 57 | <div className="flex items-center gap-[var(--space-2)] min-w-0"> |
| 58 | <span style={{ color: 'oklch(0.22 0.025 50 / 0.2)' }}>/</span> |
| 59 | <button |
| 60 | type="button" |
| 61 | onClick={() => setView(previousView === 'settings' ? 'hub' : previousView)} |
| 62 | aria-label={t('topbar.closeSettings')} |
| 63 | className={`${topbarButtonClass} gap-[6px] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)]`} |
| 64 | style={{ |
| 65 | ...noDragStyle, |
| 66 | }} |
| 67 | > |
| 68 | <ArrowLeft className="w-4 h-4 shrink-0" aria-hidden /> |
| 69 | <span className="truncate">{t('topbar.settingsLabel')}</span> |
| 70 | </button> |
| 71 | </div> |
| 72 | ) : view === 'hub' ? ( |
| 73 | <nav |
| 74 | className="flex h-full min-w-max items-center gap-[var(--space-1)]" |
| 75 | aria-label={t('hub.tabs.all')} |
| 76 | > |
| 77 | {HUB_TABS.map((tab) => { |
nothing calls this directly
no test coverage detected