- Sidebar collapse/expand toggle with icon-only rail mode (persisted) - Sidebar category/tag clicks navigate to /trees with URL params - TreeLibraryPage syncs filters from URL search params bidirectionally - Workspace create modal with icon picker and auto-slug generation - TopBar logo adapts to collapsed sidebar state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
import { create } from 'zustand'
|
|
import type { Workspace } from '@/types'
|
|
import { workspacesApi } from '@/api/workspaces'
|
|
|
|
interface WorkspaceState {
|
|
workspaces: Workspace[]
|
|
activeWorkspaceId: string | null
|
|
loading: boolean
|
|
sidebarCollapsed: boolean
|
|
setActiveWorkspace: (id: string) => void
|
|
fetchWorkspaces: () => Promise<void>
|
|
getActiveWorkspace: () => Workspace | undefined
|
|
toggleSidebar: () => void
|
|
}
|
|
|
|
export const useWorkspaceStore = create<WorkspaceState>((set, get) => ({
|
|
workspaces: [],
|
|
activeWorkspaceId: localStorage.getItem('active-workspace-id'),
|
|
loading: false,
|
|
sidebarCollapsed: localStorage.getItem('sidebar-collapsed') === 'true',
|
|
|
|
setActiveWorkspace: (id: string) => {
|
|
localStorage.setItem('active-workspace-id', id)
|
|
set({ activeWorkspaceId: id })
|
|
},
|
|
|
|
fetchWorkspaces: async () => {
|
|
set({ loading: true })
|
|
try {
|
|
const workspaces = await workspacesApi.list()
|
|
const state = get()
|
|
let activeId = state.activeWorkspaceId
|
|
|
|
// If no active workspace or active workspace doesn't exist, use default
|
|
if (!activeId || !workspaces.find(w => w.id === activeId)) {
|
|
const defaultWs = workspaces.find(w => w.is_default) || workspaces[0]
|
|
if (defaultWs) {
|
|
activeId = defaultWs.id
|
|
localStorage.setItem('active-workspace-id', activeId)
|
|
}
|
|
}
|
|
|
|
set({ workspaces, activeWorkspaceId: activeId, loading: false })
|
|
} catch {
|
|
set({ loading: false })
|
|
}
|
|
},
|
|
|
|
getActiveWorkspace: () => {
|
|
const { workspaces, activeWorkspaceId } = get()
|
|
return workspaces.find(w => w.id === activeWorkspaceId)
|
|
},
|
|
|
|
toggleSidebar: () => {
|
|
const next = !get().sidebarCollapsed
|
|
localStorage.setItem('sidebar-collapsed', String(next))
|
|
set({ sidebarCollapsed: next })
|
|
},
|
|
}))
|