mcpField
S
scene-menu-bar

scene-menu-bar

github.com/posthog设计与 UI40.5k141 次安装今天更新
代码仓库

关于 scene-menu-bar

面向 PostHog 开发的场景菜单栏设计与迁移规范,说明菜单结构、组件用法、交互规则和迁移检查要点。

主要功能

  1. 01规范 File、Edit、View 和 Metadata 菜单
  2. 02说明破坏性操作与切换项规则
  3. 03支持通过弹出层承载富表单控件
  4. 04提供键盘导航与布局指引
  5. 05提供迁移和场景审查清单

适用场景

  1. 01将 ScenePanel 迁移为菜单栏
  2. 02确定场景操作应归属的菜单
  3. 03添加标签或其他富输入控件
  4. 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(破坏性操作放在最底部)
EditDuplicate,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 布局),父级无法检测弹出菜单是否为空。你必须选择以下一种做法:

  1. 根据子项依赖的同一值,显式传入 disabled,例如:

    const { hasFileItems } = useValues(sceneFileLogic)
    <SceneMenuBarMenu label="File" disabled={!hasFileItems}>
        {hasFileItems && <SceneMenuBarFileItems dataAttrKey="…" />}
    </SceneMenuBarMenu>
    
  2. 如果菜单无法提供任何有用内容,则在父级控制整个菜单是否渲染:

    {
      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,并执行以下操作:

  1. 立即进行乐观更新(setX + updateX),然后再调用 API。
  2. 使用 await breakpoint(250) 对快速更改进行防抖。
  3. API 响应后调用 breakpoint(),如果有更新的调用取代了当前调用,则提前退出。
  4. 与服务器响应协调前,先检查集合是否相等,避免服务器返回的值顺序不同导致重新排序。
  5. 重新抛出 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,并将每一项映射到:

  1. 它属于哪个菜单(File / Edit / View / Metadata / Staff only)。
  2. 它是否需要 variant="destructive"(删除/归档/移除)。
  3. 它是否需要 opensFloatingUi(打开模态框/popover/对话框/侧边面板)。
  4. 它是否是开关(使用 SceneMenuBarCheckboxItem)。
  5. 适用哪些门控(已保存状态、FF、AccessControl、多项目)。

查看 /Users/adamleithp/Desktop/scene-menu-bar-grouping.md,了解 PostHog 中所有 ScenePanel 项的持续清单及其建议的菜单位置。

评论

支持 Markdown