diff --git a/apps/studio/src/components/app-sidebar.tsx b/apps/studio/src/components/app-sidebar.tsx index 7dda189e4..358839b5c 100644 --- a/apps/studio/src/components/app-sidebar.tsx +++ b/apps/studio/src/components/app-sidebar.tsx @@ -4,10 +4,7 @@ import { Database, Package, LayoutDashboard, - ChevronsUpDown, - Sparkles, Search, - Check, Zap, BarChart3, FileText, @@ -33,6 +30,7 @@ import { ChevronRight, Settings, Wrench, + Sparkles, type LucideIcon, } from "lucide-react" import { useState, useEffect, useCallback, useMemo } from "react" @@ -43,6 +41,7 @@ import type { InstalledPackage } from '@objectstack/spec/kernel'; import { Sidebar, SidebarContent, + SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, @@ -50,26 +49,18 @@ import { SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, - SidebarHeader, SidebarSeparator, SidebarInput, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton, + SidebarTrigger, } from "@/components/ui/sidebar" import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" // ─── Icon & label hints ────────────────────────────────────────────── const META_TYPE_HINTS: Record = { @@ -306,59 +297,10 @@ export function AppSidebar({ return { ...group, visibleTypes, totalItems }; }).filter(g => g.totalItems > 0); - // Package switcher state - const SelectedPkgIcon = selectedPackage ? (PKG_TYPE_ICONS[selectedPackage.manifest?.type] || Package) : Sparkles; + // Package switcher state (no longer used in AppSidebar, moved to TopBar) return ( - {/* ── Package Switcher ── */} - - - - - - - Installed Packages - - {packages.map((pkg) => { - const Icon = PKG_TYPE_ICONS[pkg.manifest?.type] || Package; - const isSelected = selectedPackage?.manifest?.id === pkg.manifest?.id; - return ( - onSelectPackage(pkg)} className="gap-2 py-2"> -
- -
-
- {pkg.manifest?.name || pkg.manifest?.id} - - v{pkg.manifest?.version} · {pkg.manifest?.type} - {!pkg.enabled && ' · disabled'} - -
- {isSelected && } -
- ); - })} - {packages.length === 0 && ( -
No packages installed
- )} -
-
-
- {/* ── Overview ── */} @@ -607,6 +549,13 @@ export function AppSidebar({ + + + + + + +
) } diff --git a/apps/studio/src/components/global-sidebar.tsx b/apps/studio/src/components/global-sidebar.tsx index 705ef653a..31315fc97 100644 --- a/apps/studio/src/components/global-sidebar.tsx +++ b/apps/studio/src/components/global-sidebar.tsx @@ -18,15 +18,14 @@ * (`/environments/:envId/:package/*`), the package-scoped {@link AppSidebar} * takes over instead. The two sidebars are mutually exclusive and share the * same `SidebarProvider` in `routes/__root.tsx`. + * + * Organization switching is now handled in the TopBar, so this sidebar only + * focuses on functional navigation. */ import { useMemo } from 'react'; import { Link, useLocation } from '@tanstack/react-router'; import { - Building2, - Check, - ChevronsUpDown, - Plus, Boxes, Globe, Package as PackageIcon, @@ -36,126 +35,16 @@ import { import { Sidebar, SidebarContent, + SidebarFooter, SidebarGroup, SidebarGroupContent, - SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarSeparator, + SidebarTrigger, } from '@/components/ui/sidebar'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { useOrganizations, useSession } from '@/hooks/useSession'; -import { toast } from '@/hooks/use-toast'; - -/** Header: active organization + switcher. */ -function OrgHeader() { - const { organizations, loading, reload } = useOrganizations(); - const { session, setActiveOrganization } = useSession(); - const activeId = session?.activeOrganizationId ?? undefined; - const active = useMemo( - () => organizations.find((o) => o.id === activeId) ?? null, - [organizations, activeId], - ); - - const handleSelect = async (id: string) => { - if (id === activeId) return; - try { - await setActiveOrganization(id); - await reload(); - toast({ title: 'Organization switched' }); - } catch (err) { - toast({ - title: 'Failed to switch organization', - description: (err as Error).message, - variant: 'destructive', - }); - } - }; - - return ( - - - - - - -
- -
-
- - {active?.name ?? (loading ? 'Loading…' : 'Select organization')} - - {active?.slug && ( - - {active.slug} - - )} -
- -
-
- - - Organizations - - {organizations.map((org) => ( - { - e.preventDefault(); - handleSelect(org.id); - }} - className="gap-2" - > -
-
{org.name}
- {org.slug && ( - - {org.slug} - - )} -
- {org.id === activeId && ( - - )} -
- ))} - - - - - New organization… - - - - - Manage organizations - - -
-
-
-
-
- ); -} +import { useSession } from '@/hooks/useSession'; /** * Extract the `:envId` segment from the current pathname when the user is @@ -184,7 +73,6 @@ export function GlobalSidebar() { return ( - @@ -271,6 +159,13 @@ export function GlobalSidebar() { + + + + + + + ); } diff --git a/apps/studio/src/components/package-switcher.tsx b/apps/studio/src/components/package-switcher.tsx new file mode 100644 index 000000000..6999ee14f --- /dev/null +++ b/apps/studio/src/components/package-switcher.tsx @@ -0,0 +1,128 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * PackageSwitcher + * + * Extracted from AppSidebar's SidebarHeader dropdown. + * Displays the current package and provides a dropdown menu to switch packages. + * Used in the TopBar for global chrome. + */ + +import { + Package, + AppWindow, + Layers, + Database, + Globe, + Sparkles, + Bot, + Zap, + ChevronsUpDown, + Check, + type LucideIcon, +} from 'lucide-react'; +import type { InstalledPackage } from '@objectstack/spec/kernel'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { Button } from '@/components/ui/button'; + +/** Icon mapping for package types */ +const PKG_TYPE_ICONS: Record = { + app: AppWindow, + plugin: Layers, + driver: Database, + server: Globe, + ui: Sparkles, + theme: Sparkles, + agent: Bot, + module: Package, + objectql: Database, + adapter: Zap, +}; + +interface PackageSwitcherProps { + packages: InstalledPackage[]; + selectedPackage: InstalledPackage | null; + onSelectPackage: (pkg: InstalledPackage) => void; +} + +export function PackageSwitcher({ + packages, + selectedPackage, + onSelectPackage, +}: PackageSwitcherProps) { + const SelectedPkgIcon = selectedPackage + ? PKG_TYPE_ICONS[selectedPackage.manifest?.type] || Package + : Sparkles; + + return ( + + + + + + + Installed Packages + + + {packages.map((pkg) => { + const Icon = PKG_TYPE_ICONS[pkg.manifest?.type] || Package; + const isSelected = selectedPackage?.manifest?.id === pkg.manifest?.id; + return ( + { + e.preventDefault(); + onSelectPackage(pkg); + }} + className="gap-2 py-2" + > +
+ +
+
+ + {pkg.manifest?.name || pkg.manifest?.id} + + + v{pkg.manifest?.version} · {pkg.manifest?.type} + {!pkg.enabled && ' · disabled'} + +
+ {isSelected && } +
+ ); + })} + {packages.length === 0 && ( +
+ No packages installed +
+ )} +
+
+ ); +} diff --git a/apps/studio/src/components/site-header.tsx b/apps/studio/src/components/site-header.tsx deleted file mode 100644 index b152f159c..000000000 --- a/apps/studio/src/components/site-header.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { SidebarTrigger } from "@/components/ui/sidebar" -import { Separator } from "@/components/ui/separator" -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - BreadcrumbList, - BreadcrumbPage, - BreadcrumbSeparator, -} from "@/components/ui/breadcrumb" -import { ThemeToggle } from "@/components/theme-toggle" -import { Badge } from "@/components/ui/badge" -import { Cpu } from 'lucide-react' -import { config } from '@/lib/config' -import { EnvironmentSwitcher } from '@/components/environment-switcher' -import { OrganizationSwitcher } from '@/components/organization-switcher' -import { UserMenu } from '@/components/user-menu' - -interface SiteHeaderProps { - selectedObject?: string | null; - selectedMeta?: { type: string; name: string } | null; - selectedView: 'overview' | 'packages' | 'object' | 'metadata' | 'api-console' | 'environments' | 'templates' | 'examples'; - packageLabel?: string; -} - -const META_TYPE_LABELS: Record = { - actions: 'Actions', - dashboards: 'Dashboards', - reports: 'Reports', - flows: 'Flows', - agents: 'Agents', - apis: 'APIs', - ragPipelines: 'RAG Pipelines', - profiles: 'Profiles', - sharingRules: 'Sharing Rules', - data: 'Seed Data', -}; - -export function SiteHeader({ selectedObject = null, selectedMeta, selectedView, packageLabel }: SiteHeaderProps) { - const viewLabels: Record = { - overview: 'Overview', - packages: 'Package Manager', - 'api-console': 'API Console', - environments: 'Environments', - templates: 'Templates', - examples: 'Examples', - object: selectedObject || 'Object', - metadata: selectedMeta ? (META_TYPE_LABELS[selectedMeta.type] || selectedMeta.type) : 'Metadata', - }; - - return ( -
-
- - - - - - - - - {packageLabel && ( - <> - - {packageLabel} - - - - )} - - - {viewLabels[selectedView] || 'Overview'} - - - {selectedView === 'object' && selectedObject && ( - <> - - - - {selectedObject} - - - - )} - {selectedView === 'metadata' && selectedMeta && ( - <> - - - - {selectedMeta.name} - - - - )} - - -
-
- {selectedView === 'object' && selectedObject && ( - - /api/v1/data/{selectedObject} - - )} - {selectedView === 'metadata' && selectedMeta && ( - - /api/v1/meta/{selectedMeta.type}/{selectedMeta.name} - - )} - - - {config.mode.toUpperCase()} - - - -
-
- ) -} diff --git a/apps/studio/src/components/top-bar.tsx b/apps/studio/src/components/top-bar.tsx new file mode 100644 index 000000000..321c328eb --- /dev/null +++ b/apps/studio/src/components/top-bar.tsx @@ -0,0 +1,248 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * TopBar + * + * Global chrome component in Supabase style. + * Renders at the top of authenticated Studio layouts with: + * - Left segment: OrganizationSwitcher + EnvironmentSwitcher + PackageSwitcher + * - Center segment: SidebarTrigger + dynamic breadcrumbs (inferred from URL) + * - Right segment: Global search placeholder, mode badge, ThemeToggle, UserMenu + */ + +import { useLocation, useParams } from '@tanstack/react-router'; +import { useMemo } from 'react'; +import { Separator } from '@/components/ui/separator'; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb'; +import { ThemeToggle } from '@/components/theme-toggle'; +import { Badge } from '@/components/ui/badge'; +import { Input } from '@/components/ui/input'; +import { Cpu, Search } from 'lucide-react'; +import { config } from '@/lib/config'; +import { EnvironmentSwitcher } from '@/components/environment-switcher'; +import { OrganizationSwitcher } from '@/components/organization-switcher'; +import { UserMenu } from '@/components/user-menu'; +import { PackageSwitcher } from '@/components/package-switcher'; +import type { InstalledPackage } from '@objectstack/spec/kernel'; + +const META_TYPE_LABELS: Record = { + action: 'Actions', + dashboard: 'Dashboards', + report: 'Reports', + flow: 'Flows', + agent: 'Agents', + api: 'APIs', + ragPipeline: 'RAG Pipelines', + profile: 'Profiles', + sharingRule: 'Sharing Rules', + data: 'Seed Data', + view: 'Views', + app: 'Apps', + tool: 'Tools', + workflow: 'Workflows', + approval: 'Approvals', + webhook: 'Webhooks', + role: 'Roles', + permission: 'Permissions', + policy: 'Policies', + connector: 'Connectors', + object: 'Objects', + hook: 'Hooks', + mapping: 'Mappings', + analyticsCube: 'Analytics Cubes', + page: 'Pages', + theme: 'Themes', +}; + +interface TopBarProps { + /** List of installed packages for the PackageSwitcher dropdown */ + packages?: InstalledPackage[]; + /** Currently selected package */ + selectedPackage?: InstalledPackage | null; + /** Callback when a package is selected from the dropdown */ + onSelectPackage?: (pkg: InstalledPackage) => void; +} + +export function TopBar({ + packages = [], + selectedPackage = null, + onSelectPackage = () => {}, +}: TopBarProps) { + const location = useLocation(); + const params = useParams({ strict: false }) as { + package?: string; + environmentId?: string; + name?: string; + type?: string; + orgId?: string; + }; + + // Infer view type from pathname + const viewType = useMemo(() => { + const pathname = location.pathname; + + if (pathname === '/') return 'home'; + if (pathname === '/api-console' || pathname.startsWith('/api-console/')) return 'api-console'; + if (pathname === '/environments') return 'environments'; + if (pathname === '/orgs' || pathname === '/orgs/new' || params.orgId) return 'orgs'; + if (params.environmentId && pathname.includes('/packages')) return 'packages'; + if (params.environmentId && !params.package) return 'environment-overview'; + if (params.package && params.name && !params.type) return 'object'; + if (params.package && params.type && params.name) return 'metadata'; + if (params.package && !params.name && !params.type) return 'package-overview'; + + return 'default'; + }, [location.pathname, params]); + + // Compute breadcrumb items based on current route + const breadcrumbs = useMemo(() => { + const items: Array<{ label: string; href?: string }> = []; + + switch (viewType) { + case 'home': + items.push({ label: 'Home' }); + break; + case 'api-console': + items.push({ label: 'API Console' }); + break; + case 'environments': + items.push({ label: 'Environments' }); + break; + case 'orgs': + if (params.orgId) { + items.push({ label: 'Organizations', href: '/orgs' }); + items.push({ label: 'Settings' }); + } else { + items.push({ label: 'Organizations' }); + } + break; + case 'packages': + items.push({ label: 'Package Manager' }); + break; + case 'environment-overview': + items.push({ label: 'Overview' }); + break; + case 'package-overview': + if (selectedPackage?.manifest?.name) { + items.push({ label: selectedPackage.manifest.name }); + } + items.push({ label: 'Overview' }); + break; + case 'object': + if (selectedPackage?.manifest?.name) { + items.push({ label: selectedPackage.manifest.name }); + } + items.push({ label: 'Objects' }); + if (params.name) { + items.push({ label: params.name }); + } + break; + case 'metadata': + if (selectedPackage?.manifest?.name) { + items.push({ label: selectedPackage.manifest.name }); + } + if (params.type) { + items.push({ label: META_TYPE_LABELS[params.type] || params.type }); + } + if (params.name) { + items.push({ label: params.name }); + } + break; + default: + items.push({ label: 'Studio' }); + } + + return items; + }, [viewType, params, selectedPackage]); + + // Compute API badge for object/metadata views + const apiBadge = useMemo(() => { + if (viewType === 'object' && params.name) { + return `/api/v1/data/${params.name}`; + } + if (viewType === 'metadata' && params.type && params.name) { + return `/api/v1/meta/${params.type}/${params.name}`; + } + return null; + }, [viewType, params]); + + // Show PackageSwitcher only when we're in a package context + const showPackageSwitcher = params.package && packages.length > 0; + + return ( +
+ {/* Left segment: Org + Env + Package switchers */} +
+ + + + {showPackageSwitcher && ( + <> + + + + )} + + + + {breadcrumbs.map((item, index) => ( +
+ {index > 0 && } + + {item.href ? ( + {item.label} + ) : ( + {item.label} + )} + +
+ ))} +
+
+
+ + {/* Right segment: Search + Mode + Theme + User */} +
+ {/* Global search placeholder */} +
+ + + + K + +
+ + {/* API Badge */} + {apiBadge && ( + + {apiBadge} + + )} + + {/* Mode Badge */} + + + {config.mode.toUpperCase()} + + + + +
+
+ ); +} diff --git a/apps/studio/src/components/ui/sidebar.tsx b/apps/studio/src/components/ui/sidebar.tsx index 19ea78d2a..dca65a702 100644 --- a/apps/studio/src/components/ui/sidebar.tsx +++ b/apps/studio/src/components/ui/sidebar.tsx @@ -142,7 +142,7 @@ const SidebarProvider = React.forwardRef< } as React.CSSProperties } className={cn( - "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex h-svh w-full overflow-hidden", + "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-0 w-full", className )} ref={ref} diff --git a/apps/studio/src/routes/__root.tsx b/apps/studio/src/routes/__root.tsx index 9ddca52d6..5bd9b94d2 100644 --- a/apps/studio/src/routes/__root.tsx +++ b/apps/studio/src/routes/__root.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. -import { createRootRoute, Outlet, useLocation, useNavigate } from '@tanstack/react-router'; -import { useEffect } from 'react'; +import { createRootRoute, Outlet, useLocation, useNavigate, useParams } from '@tanstack/react-router'; +import { useEffect, useMemo } from 'react'; import { ObjectStackProvider } from '@objectstack/client-react'; import { ErrorBoundary } from '../components/ErrorBoundary'; import { SidebarProvider } from '@/components/ui/sidebar'; @@ -9,10 +9,13 @@ import { Toaster } from '@/components/ui/toaster'; import { AiChatPanel } from '@/components/AiChatPanel'; import { ProductionGuardProvider } from '@/components/production-guard'; import { GlobalSidebar } from '@/components/global-sidebar'; +import { TopBar } from '@/components/top-bar'; import { PluginRegistryProvider } from '../plugins'; import { builtInPlugins } from '../plugins/built-in'; import { useObjectStackClient } from '../hooks/useObjectStackClient'; import { SessionProvider, useSession } from '../hooks/useSession'; +import { useEnvAwarePackages } from '../hooks/useEnvAwarePackages'; +import type { InstalledPackage } from '@objectstack/spec/kernel'; /** Routes that don't require authentication. */ const PUBLIC_ROUTES = new Set(['/login', '/register']); @@ -62,8 +65,47 @@ function RequireAuth({ children }: { children: React.ReactNode }) { const { user, loading } = useSession(); const navigate = useNavigate(); const location = useLocation(); + const params = useParams({ strict: false }) as { environmentId?: string; package?: string }; const isPublic = PUBLIC_ROUTES.has(location.pathname); + // Get packages for TopBar PackageSwitcher + const { packages, selectedPackage, setSelectedPackage } = + useEnvAwarePackages(params.environmentId); + + // Extract the $package segment from the URL + const activePackageId = useMemo(() => { + if (!params.package) return undefined; + // Reserved segments that are NOT package ids + if (params.package === 'packages') return undefined; + return params.package; + }, [params.package]); + + // Sync selectedPackage with URL + useEffect(() => { + if (!activePackageId) { + if (selectedPackage) setSelectedPackage(null); + return; + } + if (!packages.length) return; + const pkg = packages.find( + (p) => + p.manifest?.id === activePackageId || + p.manifest?.id?.endsWith('.' + activePackageId), + ); + if (pkg && pkg !== selectedPackage) setSelectedPackage(pkg); + }, [activePackageId, packages, selectedPackage, setSelectedPackage]); + + const handleSelectPackage = (pkg: InstalledPackage) => { + const nextId = pkg.manifest?.id; + if (!nextId) return; + if (params.environmentId) { + navigate({ + to: '/environments/$environmentId/$package', + params: { environmentId: params.environmentId, package: nextId }, + }); + } + }; + // Redirect to environment picker when the user hits a route that requires // an environment context (e.g. /$package/*) but isn't already under /environments. useEffect(() => { @@ -92,15 +134,26 @@ function RequireAuth({ children }: { children: React.ReactNode }) { return null; } - // On global shell paths (non-package routes), render the GlobalSidebar - // alongside children. Package-scoped routes keep rendering their own - // AppSidebar from within their layout component. - if (user && isGlobalShellPath(location.pathname)) { + // Authenticated layout with TopBar + Sidebar + Content + if (user) { + const showGlobalSidebar = isGlobalShellPath(location.pathname); + return ( - <> - - {children} - + +
+ +
+ {showGlobalSidebar && } +
+ {children} +
+
+
+
); } @@ -132,15 +185,13 @@ function RootComponent() { - - - - - - - - - + + + + + + + diff --git a/apps/studio/src/routes/api-console.tsx b/apps/studio/src/routes/api-console.tsx index edb8faae1..827610665 100644 --- a/apps/studio/src/routes/api-console.tsx +++ b/apps/studio/src/routes/api-console.tsx @@ -1,19 +1,11 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { createFileRoute } from '@tanstack/react-router'; -import { SiteHeader } from '@/components/site-header'; import { ApiConsolePage } from '../components/ApiConsolePage'; -import { usePackages } from '../hooks/usePackages'; function ApiConsoleComponent() { - const { selectedPackage } = usePackages(); - return ( -
- +
diff --git a/apps/studio/src/routes/environments.$environmentId.$package.index.tsx b/apps/studio/src/routes/environments.$environmentId.$package.index.tsx index 3db9a66d4..e5667ac07 100644 --- a/apps/studio/src/routes/environments.$environmentId.$package.index.tsx +++ b/apps/studio/src/routes/environments.$environmentId.$package.index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { createFileRoute } from '@tanstack/react-router'; -import { SiteHeader } from '@/components/site-header'; import { DeveloperOverview } from '../components/DeveloperOverview'; import { useEnvAwarePackages } from '../hooks/useEnvAwarePackages'; @@ -10,15 +9,9 @@ function EnvPackageIndexComponent() { const { packages, selectedPackage } = useEnvAwarePackages(environmentId); return ( - <> - -
- -
- +
+ +
); } diff --git a/apps/studio/src/routes/environments.$environmentId.$package.metadata.$type.$name.tsx b/apps/studio/src/routes/environments.$environmentId.$package.metadata.$type.$name.tsx index 18f4efc1a..d51571c87 100644 --- a/apps/studio/src/routes/environments.$environmentId.$package.metadata.$type.$name.tsx +++ b/apps/studio/src/routes/environments.$environmentId.$package.metadata.$type.$name.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { createFileRoute } from '@tanstack/react-router'; -import { SiteHeader } from '@/components/site-header'; import { PluginHost } from '../plugins'; import { useEnvAwarePackages } from '../hooks/useEnvAwarePackages'; @@ -12,20 +11,13 @@ function EnvMetadataViewComponent() { const resolvedPkgId = selectedPackage?.manifest?.id ?? packageId; return ( - <> - + -
- -
- + ); } diff --git a/apps/studio/src/routes/environments.$environmentId.$package.objects.$name.tsx b/apps/studio/src/routes/environments.$environmentId.$package.objects.$name.tsx index 7926593c1..2a51af529 100644 --- a/apps/studio/src/routes/environments.$environmentId.$package.objects.$name.tsx +++ b/apps/studio/src/routes/environments.$environmentId.$package.objects.$name.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { createFileRoute } from '@tanstack/react-router'; -import { SiteHeader } from '@/components/site-header'; import { PluginHost } from '../plugins'; import { useEnvAwarePackages } from '../hooks/useEnvAwarePackages'; @@ -13,20 +12,13 @@ function EnvObjectViewComponent() { const resolvedPkgId = selectedPackage?.manifest?.id ?? packageId; return ( - <> - + -
- -
- + ); } diff --git a/apps/studio/src/routes/environments.$environmentId.$package.tsx b/apps/studio/src/routes/environments.$environmentId.$package.tsx index a8ff23836..f0d0433b5 100644 --- a/apps/studio/src/routes/environments.$environmentId.$package.tsx +++ b/apps/studio/src/routes/environments.$environmentId.$package.tsx @@ -14,7 +14,7 @@ import { createFileRoute, Outlet } from '@tanstack/react-router'; function EnvPackageLayoutComponent() { return ( -
+
); diff --git a/apps/studio/src/routes/environments.$environmentId.index.tsx b/apps/studio/src/routes/environments.$environmentId.index.tsx index cd7ee782b..5c7d60668 100644 --- a/apps/studio/src/routes/environments.$environmentId.index.tsx +++ b/apps/studio/src/routes/environments.$environmentId.index.tsx @@ -23,7 +23,6 @@ import { Loader2, Package, } from 'lucide-react'; -import { SiteHeader } from '@/components/site-header'; import { EnvironmentBadge } from '@/components/environment-badge'; import { EnvironmentStatusBadge } from '@/components/environment-status-badge'; import { Card } from '@/components/ui/card'; @@ -116,9 +115,7 @@ function EnvironmentOverviewComponent() { }; return ( -
- - +
{loading && !env && ( diff --git a/apps/studio/src/routes/environments.$environmentId.packages.tsx b/apps/studio/src/routes/environments.$environmentId.packages.tsx index 53ad1fbe1..47d0a4334 100644 --- a/apps/studio/src/routes/environments.$environmentId.packages.tsx +++ b/apps/studio/src/routes/environments.$environmentId.packages.tsx @@ -12,8 +12,7 @@ import { createFileRoute, useParams, useNavigate } from '@tanstack/react-router' import { useState, useEffect, useCallback } from 'react'; import { useClient } from '@objectstack/client-react'; import { Package, Power, PowerOff, Trash2, Plus, RefreshCw, ArrowRight } from 'lucide-react'; -import { SiteHeader } from '@/components/site-header'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; @@ -100,9 +99,7 @@ function EnvironmentPackagesComponent() { }; return ( -
- - +
diff --git a/apps/studio/src/routes/environments.index.tsx b/apps/studio/src/routes/environments.index.tsx index c92d028df..8d5a04698 100644 --- a/apps/studio/src/routes/environments.index.tsx +++ b/apps/studio/src/routes/environments.index.tsx @@ -15,7 +15,6 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router'; import { useState } from 'react'; import { Plus, Database, MapPin, RefreshCw } from 'lucide-react'; -import { SiteHeader } from '@/components/site-header'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; @@ -30,9 +29,7 @@ function EnvironmentsListComponent() { const [createOpen, setCreateOpen] = useState(false); return ( -
- - +
diff --git a/apps/studio/src/routes/index.tsx b/apps/studio/src/routes/index.tsx index c0edfb25e..543608652 100644 --- a/apps/studio/src/routes/index.tsx +++ b/apps/studio/src/routes/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { createFileRoute } from '@tanstack/react-router'; -import { SiteHeader } from '@/components/site-header'; import { DeveloperOverview } from '../components/DeveloperOverview'; import { usePackages } from '../hooks/usePackages'; @@ -9,11 +8,7 @@ function IndexComponent() { const { packages, selectedPackage } = usePackages(); return ( -
- +
- -
+
+