feat: content header with collapse toggle + realtime indicator

- New SidebarProvider context for shared collapse state
- ContentHeader visible on desktop: collapse icon left, "Live" right
- Collapse button removed from sidebar bottom (moved to header)
- Keyboard shortcut [ handled by context, not sidebar
- Realtime indicator polls every 5s, ticks every 1s for freshness
This commit is contained in:
Usman Baig
2026-03-24 22:57:41 +01:00
parent b74742e15e
commit 102551b1ce
4 changed files with 144 additions and 83 deletions

68
lib/sidebar-context.tsx Normal file
View File

@@ -0,0 +1,68 @@
'use client'
import { createContext, useContext, useState, useCallback, useEffect } from 'react'
const SIDEBAR_KEY = 'pulse_sidebar_collapsed'
interface SidebarState {
collapsed: boolean
toggle: () => void
expand: () => void
collapse: () => void
}
const SidebarContext = createContext<SidebarState>({
collapsed: true,
toggle: () => {},
expand: () => {},
collapse: () => {},
})
export function SidebarProvider({ children }: { children: React.ReactNode }) {
const [collapsed, setCollapsed] = useState(() => {
if (typeof window === 'undefined') return true
return localStorage.getItem(SIDEBAR_KEY) !== 'false'
})
const toggle = useCallback(() => {
setCollapsed((prev) => {
const next = !prev
localStorage.setItem(SIDEBAR_KEY, String(next))
return next
})
}, [])
const expand = useCallback(() => {
setCollapsed(false)
localStorage.setItem(SIDEBAR_KEY, 'false')
}, [])
const collapse = useCallback(() => {
setCollapsed(true)
localStorage.setItem(SIDEBAR_KEY, 'true')
}, [])
// Keyboard shortcut: [ to toggle
useEffect(() => {
const handler = (e: KeyboardEvent) => {
const tag = (e.target as HTMLElement)?.tagName
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return
if (e.key === '[' && !e.metaKey && !e.ctrlKey && !e.altKey) {
e.preventDefault()
toggle()
}
}
document.addEventListener('keydown', handler)
return () => document.removeEventListener('keydown', handler)
}, [toggle])
return (
<SidebarContext.Provider value={{ collapsed, toggle, expand, collapse }}>
{children}
</SidebarContext.Provider>
)
}
export function useSidebar() {
return useContext(SidebarContext)
}