Something went wrong. Try again.
ATProto-based AI Assistant Chat interface.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980import { useSyncExternalStore } from 'react'
/** Below the theme's wide breakpoint the sidebar is a drawer over the page, not a column beside it. */export const NARROW = '(width < 45rem)'
const KEY = 'scn-chat:sidebar-collapsed'
/** * Runs in the page's head before the first paint, so a collapsed sidebar never shows open first. * The collapsed state is an attribute on the root, which the layout's styles follow and the hook * keeps current, so the server's page, which can't know the choice, needs no change on hydrating. */export const sidebarScript = `try{if(localStorage.getItem('${KEY}')==='1')document.documentElement.dataset.sidebarCollapsed=''}catch(e){}`
const listeners = new Set<() => void>()
/** * Whether the sidebar is collapsed on a wide screen, a preference kept in this browser, and * whether the drawer is open on a narrow one, which starts closed on each page load. */type SidebarState = { collapsed: boolean; drawerOpen: boolean }
function readCollapsed(): boolean { try { return localStorage.getItem(KEY) === '1' } catch { return false }}
let state: SidebarState = { collapsed: false, drawerOpen: false }let loaded = false
function current(): SidebarState { if (!loaded && typeof window !== 'undefined') { loaded = true state = { ...state, collapsed: readCollapsed() } } return state}
function set(next: Partial<SidebarState>) { state = { ...current(), ...next } if (next.collapsed !== undefined) { document.documentElement.toggleAttribute('data-sidebar-collapsed', next.collapsed) try { localStorage.setItem(KEY, next.collapsed ? '1' : '0') } catch { // Without storage the choice lasts until the page reloads. } } for (const listener of listeners) listener()}
function subscribe(listener: () => void) { listeners.add(listener) return () => listeners.delete(listener)}
const isNarrow = () => typeof window !== 'undefined' && window.matchMedia(NARROW).matches
const SERVER_STATE: SidebarState = { collapsed: false, drawerOpen: false }
/** The sidebar's state, and actions that act on the drawer or the column depending on width. */export function useSidebar() { const sidebar = useSyncExternalStore(subscribe, current, () => SERVER_STATE) return { ...sidebar, /** Show or hide the sidebar: the drawer on a narrow screen, the column on a wide one. */ toggle: () => { if (isNarrow()) set({ drawerOpen: !current().drawerOpen }) else set({ collapsed: !current().collapsed }) }, /** Close the drawer, as after picking a chat. The column on a wide screen stays as it is. */ closeDrawer: () => { if (current().drawerOpen) set({ drawerOpen: false }) }, }}