关于 scene-menu-bar
面向 PostHog 开发的场景菜单栏设计与迁移规范,说明菜单结构、组件用法、交互规则和迁移检查要点。
主要功能
- 01规范 File、Edit、View 和 Metadata 菜单
- 02说明破坏性操作与切换项规则
- 03支持通过弹出层承载富表单控件
- 04提供键盘导航与布局指引
- 05提供迁移和场景审查清单
适用场景
- 01将 ScenePanel 迁移为菜单栏
- 02确定场景操作应归属的菜单
- 03添加标签或其他富输入控件
- 04检查菜单行为与可访问性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ posthog/ posthog --skill scene-menu-bar安装后在 Claude Code 中输入/scene-menu-bar确认已加载。
详细介绍
SceneMenuBar 约定
PostHog 场景会在 <SceneTitleSection> 上方渲染一个 Mac 风格菜单栏,将
ScenePanel 操作整合到一个可发现、可键盘导航的界面中。本技能文档说明
其分类体系、可用基础组件以及接线规则。
整个菜单栏由 SCENE_MENU_BAR 功能标志控制(见 lib/constants.tsx)。
组件
所有基础组件都位于 frontend/src/layout/scenes/components/SceneMenuBar.tsx。
| 组件 | 用途 |
|---|---|
<SceneMenuBar> | 顶层包装器;渲染在 <SceneTitleSection> 上方。包含通用右侧集群(PostHog AI / Docs / Support)。 |
<SceneMenuBarMenu label> | 顶层菜单(File / Edit / View / Metadata / Staff only)。 |
<SceneMenuBarSubMenu label> | SceneMenuBarMenu 内的嵌套子菜单(例如 Create、Export、Add to notebook)。会自动在触发器前添加一个空白图标占位,使标签与带图标的同级项对齐——传入 withIconBlank={false} 可退出此行为。 |
<SceneMenuBarItem> | 标准菜单项(操作 / 导航)。 |
<SceneMenuBarCheckboxItem> | 切换项——通过勾选标记反映布尔状态。 |
<SceneMenuBarRadioGroup> + <SceneMenuBarRadioItem> | 多选一的互斥选项。 |
<SceneMenuBarSeparator> | 组之间的水平分隔线。 |
<SceneMenuBarShortcut> | 右对齐的键盘快捷键提示。 |
<SceneMenuBarPopover label> | 当内容需要丰富表单控件(文本输入框、组合框)时,可替代 SceneMenuBarMenu 直接使用。底层使用 Popover——Menu.Popup 会拦截按键并阻止输入。触发器不参与 CompositeRoot 键盘导航。 |
规范菜单集(按顺序)
| 菜单 | 条目 |
|---|---|
| File | + Create 子菜单(顶部),── ,项目树条目(Open / Move / Star),Copy to another project,Manage with Terraform,场景专属文件操作,Export 子菜单,──,Delete / Archive / Restore(破坏性操作放在最底部) |
| Edit | Duplicate,Rename,Edit in SQL editor,场景专属编辑,状态变更(Pause/Resume、Activate/Deactivate),──,切换组(复选框/单选项,通过 <SceneMenuBarSeparator> 与常规条目分隔) |
| View (条件性) | 跨资源查看 — View recordings、View metalytics、View related insights 等。任何用于导航以_查看_与当前资源间接相关内容的条目。为空时跳过。 |
Metadata (使用 SceneMenuBarPopover) | Tags、Evaluation contexts、Stage、Activity indicator、External references、Comments 等。 |
| Staff only (条件性) | 调试面板、仅员工可见的开关。仅对 staff / superpowers / impersonated / non-cloud 用户渲染。 |
状态位于 Edit 中。 Pause/Resume、Activate/Deactivate、Favorite/Pin 开关及类似 状态变更全部位于 Edit 菜单内 — 通常位于底部,若存在切换组,则放在
<SceneMenuBarSeparator>之后。不要创建单独的State菜单。
右侧集群固定且通用:PostHog AI · Docs · Support。不要向其添加内容。
规则
破坏性操作(Delete / Archive / Restore)
- 位于 File 的底部,通过
<SceneMenuBarSeparator>与上方的文件操作分隔。 - 传入
variant="destructive"以实现视觉效果(红色文字 + 图标)。 - 不要为破坏性条目添加
opensFloatingUi,即使它们会打开确认对话框 — 破坏性变体 + 清晰的标签已经足够作为信号。 - 在资源具有访问控制级别时,用
<AccessControlAction>包裹。
切换组
- 将所有
<SceneMenuBarCheckboxItem>和<SceneMenuBarRadioGroup>条目归为一组。 - 通过上方(若后面还有其他条目,则下方也)的
<SceneMenuBarSeparator>将它们与常规操作条目分隔。 - 使用稳定的标签 — 对勾传达状态:用
Pinned而非Pin/Unpin,用Show debug panel而非Show/Hide debug panel。 - 互斥选项使用单选项(
<SceneMenuBarRadioGroup value onValueChange>)。
opensFloatingUi 属性
为打开额外浮动 UI 的条目在标签后追加 …,作为 Mac 风格的提示:
- ✅ 使用于:模态框、弹出框、对话框、侧边面板的打开器
- ❌ 跳过:同页导航、直接操作、破坏性条目(破坏性样式已经传达了后果)、切换条目
+ Create 交叉销售
- 嵌套在 File 顶部的
<SceneMenuBarSubMenu label="Create">内。 - 在 Create 与 File 的其余部分之间放置一个
<SceneMenuBarSeparator>。 - 交叉销售:Cohort、Survey、Dashboard、Notebook、Endpoint、Subscription、Alert、Share or embed。
空菜单
SceneMenuBarMenu 会在编译时确定其子项不会渲染任何内容时,自动禁用触发器,例如 {false && <Item/>}、{null} 或空片段。触发器仍会显示(呈灰色,cursor-not-allowed),这样菜单栏仍能向用户传达其功能。
如果菜单的子项可能在运行时渲染为 null(最常见的情况是 <SceneMenuBarFileItems>:当 sceneFileLogic.hasFileItems 为 false 时会返回 null,也就是未注册项目树条目,或当前启用了 Today 布局),父级无法检测弹出菜单是否为空。你必须选择以下一种做法:
-
根据子项依赖的同一值,显式传入
disabled,例如:const { hasFileItems } = useValues(sceneFileLogic) <SceneMenuBarMenu label="File" disabled={!hasFileItems}> {hasFileItems && <SceneMenuBarFileItems dataAttrKey="…" />} </SceneMenuBarMenu> -
如果菜单无法提供任何有用内容,则在父级控制整个菜单是否渲染:
{ hasAnyEditItem && <SceneMenuBarMenu label="Edit">…</SceneMenuBarMenu> }
如果菜单是否存在本身就是场景特征的一部分(File 应始终显示,即使处于禁用状态),优先使用(1);如果菜单确实是可选的(仅 View、Staff),则使用(2)。
标签 / 评估上下文 / 其他富输入
- 必须放在
<SceneMenuBarPopover>中,而不是<SceneMenuBarMenu>中。菜单弹出层会拦截按键(类型搜索、方向键导航),导致文本输入无法接收输入。 - 多选标签输入请使用
<TagsCombobox>(lib/components/Scenes/TagsCombobox.tsx);默认仅允许选择,传入allowCustomValues可在底部显示“Create new {noun} '...'”条目。 - 使用
<SceneTagsCombobox>(lib/components/Scenes/SceneTagsCombobox.tsx>)作为现成封装,在SCENE_MENU_BAR下替代<SceneTags>。
带防抖的乐观保存
从菜单栏输入项(标签、切换项)自动保存时,请使用 kea listener,并执行以下操作:
- 立即进行乐观更新(
setX+updateX),然后再调用 API。 - 使用
await breakpoint(250)对快速更改进行防抖。 - API 响应后调用
breakpoint(),如果有更新的调用取代了当前调用,则提前退出。 - 与服务器响应协调前,先检查集合是否相等,避免服务器返回的值顺序不同导致重新排序。
- 重新抛出
error.isBreakpoint,让 kea 静默吞掉它。
参见 frontend/src/scenes/feature-flags/featureFlagLogic.ts 中的 saveTagsInline。
File 菜单 — 项目树条目
在 File 菜单底部(破坏性操作分隔线之前)使用 <SceneMenuBarFileItems dataAttrKey={RESOURCE_TYPE} />,以显示在项目树中打开、移动到文件夹、添加/移除星标。sceneFileLogic.hasFileItems 为 false 时会自动隐藏。
右侧分组
该分组位于 <Menubar> 包装器外,以确保实际菜单的 CompositeRoot 键盘导航正常工作。未经 UX 审核,请勿在此添加条目。
感知布局的边缘延伸
<SceneMenuBar> 会读取 sceneLayoutLogic.sceneLayoutConfig.layout,并且仅当布局带内边距时,才会延伸到场景容器内边距之外(-mx-4 -mt-4):app、app-container 或 app-full-scene-height。对于无内边距布局(app-raw、app-raw-no-header、plain),负值会导致延伸过度并破坏标题区域的视觉效果,因此会跳过。你无需在场景中进行任何操作,只需注册正确的 SceneConfig.layout,菜单栏便会自动适配。调试标记:包装器上会设置 data-scene-layout 属性。
已迁移的场景
- Feature Flag —
frontend/src/scenes/feature-flags/FeatureFlag.tsx - Insight —
products/product_analytics/frontend/insights/insightScene/sidePanel/InsightSceneMenuBar.tsx - Dashboard —
frontend/src/scenes/dashboard/DashboardSceneMenuBar.tsx
迁移新场景时,请遵循 <Scene>SceneMenuBar.tsx 模式(读取 featureFlagLogic 的组件;如果功能标志未启用,则提前返回 null,否则渲染菜单栏)。将其直接挂载在 <SceneTitleSection> 上方。
审计场景
在迁移场景时,取出现有的 ScenePanel,并将每一项映射到:
- 它属于哪个菜单(File / Edit / View / Metadata / Staff only)。
- 它是否需要
variant="destructive"(删除/归档/移除)。 - 它是否需要
opensFloatingUi(打开模态框/popover/对话框/侧边面板)。 - 它是否是开关(使用
SceneMenuBarCheckboxItem)。 - 适用哪些门控(已保存状态、FF、AccessControl、多项目)。
查看 /Users/adamleithp/Desktop/scene-menu-bar-grouping.md,了解 PostHog 中所有
ScenePanel 项的持续清单及其建议的菜单位置。