88 Sparkles ,
99 Search ,
1010 Check ,
11- Briefcase ,
12- CheckSquare ,
1311 Zap ,
1412 BarChart3 ,
1513 FileText ,
@@ -24,7 +22,7 @@ import {
2422} from "lucide-react"
2523import { useState , useEffect , useCallback } from "react"
2624import { ObjectStackClient } from '@objectstack/client' ;
27- import type { AppPackage } from "@/mocks/browser" ;
25+ import type { InstalledPackage } from '@objectstack/spec/kernel' ;
2826
2927import {
3028 Sidebar ,
@@ -53,11 +51,6 @@ import {
5351 DropdownMenuTrigger ,
5452} from "@/components/ui/dropdown-menu"
5553
56- const APP_ICONS : Record < string , React . ElementType > = {
57- 'check-square' : CheckSquare ,
58- 'briefcase' : Briefcase ,
59- } ;
60-
6154/** Icon & label hints for well-known metadata types */
6255const META_TYPE_HINTS : Record < string , { label : string ; icon : LucideIcon } > = {
6356 object : { label : 'Objects' , icon : Package } ,
@@ -89,18 +82,32 @@ function getTypeIcon(type: string): LucideIcon {
8982/** Types that are internal / should be hidden from the sidebar */
9083const HIDDEN_TYPES = new Set ( [ 'plugin' , 'plugins' , 'kind' , 'app' , 'apps' , 'package' ] ) ;
9184
85+ /** Icon mapping for package types */
86+ const PKG_TYPE_ICONS : Record < string , LucideIcon > = {
87+ app : AppWindow ,
88+ plugin : Layers ,
89+ driver : Database ,
90+ server : Globe ,
91+ ui : Sparkles ,
92+ theme : Sparkles ,
93+ agent : Bot ,
94+ module : Package ,
95+ objectql : Database ,
96+ adapter : Zap ,
97+ } ;
98+
9299interface AppSidebarProps extends React . ComponentProps < typeof Sidebar > {
93100 client : ObjectStackClient | null ;
94101 selectedObject : string | null ;
95102 onSelectObject : ( name : string ) => void ;
96- apps : AppPackage [ ] ;
97- selectedApp : AppPackage | null ;
98- onSelectApp : ( app : AppPackage ) => void ;
103+ packages : InstalledPackage [ ] ;
104+ selectedPackage : InstalledPackage | null ;
105+ onSelectPackage : ( pkg : InstalledPackage ) => void ;
99106 onSelectView ?: ( view : 'dashboard' | 'packages' ) => void ;
100107 selectedView ?: 'dashboard' | 'packages' | 'object' ;
101108}
102109
103- export function AppSidebar ( { client, selectedObject, onSelectObject, apps , selectedApp , onSelectApp , onSelectView, selectedView, ...props } : AppSidebarProps ) {
110+ export function AppSidebar ( { client, selectedObject, onSelectObject, packages , selectedPackage , onSelectPackage , onSelectView, selectedView, ...props } : AppSidebarProps ) {
104111 const [ loading , setLoading ] = useState ( false ) ;
105112 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
106113 // Dynamic metadata: type -> items[]
@@ -165,55 +172,60 @@ export function AppSidebar({ client, selectedObject, onSelectObject, apps, selec
165172 label . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ||
166173 name . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ;
167174
168- const AppIcon = selectedApp ?. icon ? ( APP_ICONS [ selectedApp . icon ] || Sparkles ) : Sparkles ;
175+ const SelectedPkgIcon = selectedPackage ? ( PKG_TYPE_ICONS [ selectedPackage . manifest ?. type ] || Package ) : Sparkles ;
169176
170177 return (
171178 < Sidebar { ...props } >
172179 < SidebarHeader className = "border-b" >
173- { /* App Switcher */ }
180+ { /* Package Switcher */ }
174181 < DropdownMenu >
175182 < DropdownMenuTrigger asChild >
176183 < button className = "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left hover:bg-sidebar-accent transition-colors" >
177184 < div className = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground" >
178- < AppIcon className = "h-4 w-4" />
185+ < SelectedPkgIcon className = "h-4 w-4" />
179186 </ div >
180187 < div className = "flex flex-1 flex-col gap-0.5 leading-none overflow-hidden" >
181188 < span className = "truncate font-semibold text-sm" >
182- { selectedApp ? ( selectedApp . label || selectedApp . name ) : 'ObjectStack' }
189+ { selectedPackage ? ( selectedPackage . manifest ?. name || selectedPackage . manifest ?. id ) : 'ObjectStack' }
183190 </ span >
184191 < span className = "truncate text-xs text-muted-foreground" >
185- { selectedApp ? ( selectedApp . description || 'No description' ) : 'Loading apps ...' }
192+ { selectedPackage ? `v ${ selectedPackage . manifest ?. version } · ${ selectedPackage . manifest ?. type } ` : 'Loading packages ...' }
186193 </ span >
187194 </ div >
188195 < ChevronsUpDown className = "ml-auto h-4 w-4 shrink-0 text-muted-foreground" />
189196 </ button >
190197 </ DropdownMenuTrigger >
191198 < DropdownMenuContent className = "w-[--radix-dropdown-menu-trigger-width] min-w-64" align = "start" sideOffset = { 4 } >
192- < DropdownMenuLabel > Applications </ DropdownMenuLabel >
199+ < DropdownMenuLabel > Installed Packages </ DropdownMenuLabel >
193200 < DropdownMenuSeparator />
194- { apps . map ( ( app ) => {
195- const Icon = app . icon ? ( APP_ICONS [ app . icon ] || Package ) : Package ;
201+ { packages . map ( ( pkg ) => {
202+ const Icon = PKG_TYPE_ICONS [ pkg . manifest ?. type ] || Package ;
203+ const isSelected = selectedPackage ?. manifest ?. id === pkg . manifest ?. id ;
196204 return (
197205 < DropdownMenuItem
198- key = { app . name }
199- onClick = { ( ) => onSelectApp ( app ) }
206+ key = { pkg . manifest ?. id }
207+ onClick = { ( ) => onSelectPackage ( pkg ) }
200208 className = "gap-2 py-2"
201209 >
202210 < div className = "flex h-6 w-6 shrink-0 items-center justify-center rounded bg-primary/10 text-primary" >
203211 < Icon className = "h-3.5 w-3.5" />
204212 </ div >
205213 < div className = "flex flex-1 flex-col leading-tight" >
206- < span className = "text-sm font-medium" > { app . label || app . name } </ span >
207- { app . description && (
208- < span className = "text-xs text-muted-foreground" > { app . description } </ span >
209- ) }
214+ < span className = "text-sm font-medium" > { pkg . manifest ?. name || pkg . manifest ?. id } </ span >
215+ < span className = "text-xs text-muted-foreground" >
216+ v{ pkg . manifest ?. version } · { pkg . manifest ?. type }
217+ { ! pkg . enabled && ' · disabled' }
218+ </ span >
210219 </ div >
211- { selectedApp ?. name === app . name && (
220+ { isSelected && (
212221 < Check className = "h-4 w-4 text-primary" />
213222 ) }
214223 </ DropdownMenuItem >
215224 ) ;
216225 } ) }
226+ { packages . length === 0 && (
227+ < div className = "px-2 py-4 text-center text-xs text-muted-foreground" > No packages installed</ div >
228+ ) }
217229 </ DropdownMenuContent >
218230 </ DropdownMenu >
219231 </ SidebarHeader >
0 commit comments