Skip to content

Studio 布局重构:Supabase 风格的 TopBar + LeftSidebar + Content #1203

@hotlong

Description

@hotlong

Context

当前 Studio 的布局是 Shadcn SidebarProvider + 左侧边栏(GlobalSidebarAppSidebar)+ 每个页面内部再自行 <SiteHeader /> 的模式。问题:

  1. Org / Env / Package 切换分散在 SiteHeader(Org、Env)和 AppSidebar 头部(Package),同一认知层级被切成两处。
  2. 每个路由都要手动 import SiteHeader,重复且容易漂移(breadcrumb 逻辑、操作按钮样式都不一致)。
  3. 视觉上缺少 Supabase 那种"顶部全局 chrome + 左侧功能导航 + 右侧纯内容区"的清晰三段式。

目标:改造为 顶部栏(全局 chrome)+ 左侧栏(保留 metadata 树)+ 右侧内容区,让全局身份上下文集中在顶部,页面只关注 body。

Image

方案

1. 新增 TopBar 全局组件

文件:apps/studio/src/components/top-bar.tsx(新建)

承载:

  • 左段:Logo + OrganizationSwitcher(复用 components/organization-switcher.tsx)+ EnvironmentSwitcher(复用 components/environment-switcher.tsx)+ Package 切换器(从 app-sidebar.tsx 里 SidebarHeader 的 DropdownMenu 抽出,命名为 PackageSwitcher,放到 components/package-switcher.tsx
  • 中段:SidebarTrigger(折叠左栏)+ 面包屑(吸收 SiteHeaderviewLabels / selectedMeta / API 路径 Badge 逻辑;通过读取 useLocation + useParams 动态计算,不再需要每页传 props)
  • 右段:全局搜索框(⌘K 占位,后续接入;暂用 shadcn Input + kbd 样式)、环境模式 Badge(config.mode)、ThemeToggleUserMenu

高度仍为 h-12border-b,与现有 SiteHeader 对齐以减少视觉跳变。

2. 改造 routes/__root.tsx

  • RootComponentSidebarProvider 内部、<Outlet /> 外层加一个 flex 列容器:<TopBar /> 在上,下方是 <div className="flex flex-1 min-h-0">{sidebar}{outlet}</div>
  • TopBar 仅在已登录且非 public 路由时渲染(沿用 RequireAuth 的逻辑)。
  • GlobalSidebar / AppSidebar 的选择逻辑保留(isGlobalShellPath),但它们从此只负责"功能导航 + metadata 树",不再承担 Org/Env/Package 切换。
    • global-sidebar.tsx:删除 OrgHeader(Org 切换器移到 TopBar),SidebarHeader 可直接省略或仅留 Logo。
    • app-sidebar.tsx:删除顶部 Package Switcher SidebarHeader 块,把 Package 下拉菜单抽到新的 PackageSwitcher 组件给 TopBar 使用。

3. 删除每页 <SiteHeader />

影响文件(共 11 个路由):

  • routes/index.tsx
  • routes/api-console.tsx
  • routes/orgs.index.tsx / orgs.new.tsx / orgs.$orgId.tsx
  • routes/environments.index.tsx / environments.$environmentId.index.tsx / environments.$environmentId.packages.tsx
  • routes/environments.$environmentId.$package.index.tsx
  • routes/environments.$environmentId.$package.objects.$name.tsx
  • routes/environments.$environmentId.$package.metadata.$type.$name.tsx

每个文件:

  • 删除 import { SiteHeader } ...<SiteHeader ... />
  • 页面顶层容器 <div className="flex h-svh flex-col ..."> 去掉 h-svh(高度交由根布局的 flex container 接管),或者改成 flex-1 min-h-0 overflow-auto
  • SiteHeader 里传入的 selectedObject / selectedMeta / selectedView 信息由 TopBar 通过路由参数自行推断,不再需要页面显式传值。

4. 删除 site-header.tsx

所有用处被 TopBar 吸收后,删除 apps/studio/src/components/site-header.tsx

5. environments.$environmentId.$package.tsx 调整

当前 <main className="flex min-w-0 flex-1 flex-col h-svh overflow-hidden ..."> 里的 h-svh 会和新的根 flex 冲突,改为 flex min-w-0 flex-1 flex-col overflow-hidden

关键文件

路径 操作
apps/studio/src/components/top-bar.tsx 新建
apps/studio/src/components/package-switcher.tsx 新建(从 app-sidebar 抽出)
apps/studio/src/routes/__root.tsx 加入 TopBar + flex 列布局
apps/studio/src/components/global-sidebar.tsx 删除 OrgHeader
apps/studio/src/components/app-sidebar.tsx 删除顶部 Package Switcher header
apps/studio/src/components/site-header.tsx 删除
上述 11 个路由文件 移除 <SiteHeader /> 引用 & 调整外层高度 class

可复用的既有资产

  • components/organization-switcher.tsxcomponents/environment-switcher.tsxcomponents/environment-badge.tsxcomponents/theme-toggle.tsxcomponents/user-menu.tsx 全部直接复用。
  • Shadcn SidebarProvider / SidebarTrigger 保留,继续提供折叠能力。
  • hooks/useSessionuseEnvironmentDetailuseEnvAwarePackages 已提供 TopBar 需要的所有上下文。
  • 面包屑渲染可复用 components/ui/breadcrumb.tsx

验证

  1. pnpm --filter @objectstack/studio dev 启动,依次访问:
    • /login → 无 TopBar / Sidebar(public 路由)。
    • /environments → TopBar 显示 Org 切换器 + Env 切换器(Env 未选时 disabled);左侧 GlobalSidebar 仅剩功能入口,不再有 Org 下拉。
    • /environments/:id → Env 切换器显示当前 env;左侧 GlobalSidebar;无页面级二级 header。
    • /environments/:id/:package/objects/:name → TopBar 出现 Package Switcher + 面包屑 Package › Object › <name> + API 路径 Badge;左侧 AppSidebar 的 metadata 树正常展开,顶部不再重复 Package 下拉。
  2. 折叠 SidebarTrigger,右侧内容区无抖动。
  3. 切换主题、切换 Org、切换 Env、切换 Package 全部通过 TopBar 触发,并正确更新 URL / session。
  4. pnpm --filter @objectstack/studio test 通过;全量 pnpm build 通过(TypeScript 校验会捕获未清理的 <SiteHeader /> 引用)。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions