fix(gpui): draw the tab context menu once — shadow no longer stacks per tab

`ContextMenuExt::context_menu()` wraps the host element and keys its element
state by a hardcoded "context-menu" id. The wrapper's id enters the
GlobalElementId stack *before* the wrapped row's own `("sidebar-tab", ix)`,
so every sidebar tab row resolved to the same state — the same
`Rc<RefCell<ContextMenuSharedState>>`. Opening one menu set `open = true` for
all rows, and each row then re-rendered the same PopupMenu entity at the same
anchor. The panel is opaque so the stack was invisible, but the popover shadow
is not: its alpha composited once per tab. That is the whole of "the more tabs,
the thicker the right-click shadow" — no packaging or config involvement.

The tab menu now uses the same carrier as the file tree: rows only record the
anchor and index, and a single `deferred(anchored(PopupMenu))` is drawn from
the workspace root, so the component's own popover shadow stays single-layer.
The menu is dropped when the TABS section folds, and skipped when its captured
index no longer exists.

Alongside it:

- CHANGELOG: 1.1.0 gains this fix and issue #38 (two notify wrappers wrapping
  each other until Codex's config.toml reached 130MB); release date moves to
  2026-08-18.
- docs/release-notes/v1.1.0.md: release notes in the 1.0.0 format, with PR
  credits, reporter credits, and a pointer to the issue templates.
- README / THIRD-PARTY-NOTICES: GPUI and gpui-component are credited and
  declared — both Apache-2.0, compiled as lightly patched forks.
- .gitignore: ignore the whole `.probe-*/` family instead of one name at a time.
This commit is contained in:
kuddev committed 2026-08-18 22:37:16 +08:00
1 parent 95a79e7255
commit 0eddfaf2f1
7 files changed
+238 -37

No files matched your search

+3 -1
View File
@@ -129,4 +129,6 @@ impeccable/
# Claude packaging/probe scratch (this branch)
dist-pack-*/
.probe-pack/
# 每次 A/B 对照都会开一个新的探针目录(.probe-pack、.probe-layout-daily、
# .probe-tabmenu…),逐个补规则永远漏,所以整族忽略。
.probe-*/
+5 -1
View File
@@ -4,7 +4,7 @@ Every release entry is provided in English and Simplified Chinese.
每个版本条目均同时提供英文和简体中文说明。
## 1.1.0 - 2026-08-17
## 1.1.0 - 2026-08-18
### English
@@ -39,6 +39,8 @@ Every release entry is provided in English and Simplified Chinese.
- **Markdown reader fills the reading column (GPUI shell)** — soft line breaks inside a paragraph (hand-wrapped README prose) rendered as hard line breaks, leaving the right side of the column empty and ragged. They now merge into spaces per CommonMark, with CJK neighbours joining directly.
- **Markdown images actually load (GPUI shell)** — the document tab now resolves relative image paths against the document's own directory, loads absolute local paths from disk, and fetches `http(s)` images through a ureq HTTP client on the background executor (gpui ships a null client by default), so README logos, shields badges (SVG included) and screenshots finally display. Animated GIF/WebP (local or remote) are flattened to a still PNG first, because gpui panics when a multi-frame image's frame index is reused on a static image.
- **Raw HTML in Markdown renders like GitHub (GPUI shell)** — README-style HTML now matches GitHub's semantics: `align="center"` on `<p>` / `<div>` / headings centers the block, consecutive `<img>` badges inside one paragraph flow on a single centered row instead of stacking one per line, inline `<br/>` breaks in document order (it used to be hoisted above the text it followed), and `<a><img/></a>` linked badges are no longer dropped — the image renders and carries its link.
- **Codex `notify` no longer wraps itself into an unusable config** — when another notify wrapper (codex-computer-use) re-registered without recognizing Nebula's helper, it serialized the whole existing array into its own `--previous-notify` argument; Nebula wrapped that again, and the escaped backslashes doubled every round until `config.toml` reached 130 MB and Codex refused to start. The helper mark now counts as "already wired" wherever it appears — including inside a JSON string — so Nebula only heals its own path and never re-wraps. A serialized `notify` over 8 KB is refused outright as a backstop against any other form of inflation, and `nebula setup-ai --remove` persists `ai_hooks=0` so auto-wiring stays off across launches. (#38)
- **Tab context-menu shadow no longer thickens with the tab count (GPUI shell)** — the component library's context-menu extension keys its state by a fixed element id, so every tab row resolved to the same state and re-rendered the same open menu at the same anchor. The menu panel is opaque, but its shadow is not: with eight tabs the drop shadow was composited eight times. The tab menu now draws exactly once from the workspace root, like the file-tree menu.
#### Improved
@@ -78,6 +80,8 @@ Every release entry is provided in English and Simplified Chinese.
- **Markdown 阅读器铺满阅读列(GPUI 壳)** — 段落内的软换行(README 手工折行的正文)此前渲染成硬换行,右侧留出一大片参差空白。现按 CommonMark 合并为空格,中日韩相邻行直接相连不插空格。
- **Markdown 图片真正能加载(GPUI 壳)** — 文档 tab 现在把相对图片路径按文档所在目录解析、绝对路径直接读盘、`http(s)` 图源经后台 ureq 客户端拉取(gpui 默认装的是空客户端),README 的 logo、shields 徽章(含 SVG)与截图终于都能显示。本地和网络的 GIF / 动画 WebP 会先压成单帧 PNG:gpui 在多帧图的帧下标被静态图复用时会直接 panic。
- **Markdown 里的原生 HTML 按 GitHub 语义渲染(GPUI 壳)** — README 常用的 HTML 写法现在与 GitHub 渲染一致:`<p>` / `<div>` / 标题上的 `align="center"` 让整块居中;同一段落里连续的 `<img>` 徽章在同一行居中横排(此前一枚一行竖着摞);行内 `<br/>` 按文档顺序断行(此前会被提到所跟文本之前);`<a><img/></a>` 链接徽章不再整个丢失——图片正常显示且带链接。
- **Codex `notify` 不再自我包装成起不来的配置** — 另一个 notify 包装器(codex-computer-use)重新注册时若认不出 Nebula 的 helper,会把整个旧数组序列化进自己的 `--previous-notify` 参数;Nebula 再包一层,转义反斜杠每轮翻倍,最终把 `config.toml` 撑到 130 MB,Codex 直接起不来。现在只要 helper 标记出现在**任何位置**(含 JSON 字符串内部)就算已接线,Nebula 只自愈自己的路径、绝不再包。序列化后超过 8 KB 的 `notify` 一律拒写,兜住其他形态的膨胀;`nebula setup-ai --remove` 会持久化 `ai_hooks=0`,重启后也不再自动装回。(#38)
- **Tab 右键菜单阴影不再随标签数变厚(GPUI 壳)** — 组件库的右键菜单扩展用固定元素 id 存状态,于是每个标签行都解析到同一份状态、把同一个已打开的菜单重复画在同一锚点上。菜单面板不透明,阴影不是:8 个标签就叠 8 层投影。现在 Tab 菜单与文件树菜单一样,由 workspace 根只画一次。
#### 改进
+19
View File
@@ -241,6 +241,16 @@ Windows/Linux paths, arrays, modules, and reload behavior.
Questions and feedback: [fickleheartedkeys@163.com](mailto:fickleheartedkeys@163.com)
### 🙏 Acknowledgements
Nebula's shell is built on **[GPUI](https://github.com/zed-industries/zed)** — the
GPU-accelerated UI framework developed by the [Zed](https://zed.dev) team — together with
**[gpui-component](https://github.com/longbridge/gpui-component)** from
[Longbridge](https://github.com/longbridge), which supplies the widget library. Both are
Apache-2.0 licensed and are used through lightly patched forks pinned in the workspace
manifest. Thank you for releasing this work as open source — Nebula's interface would not
look the way it does without it.
### 📜 License
Released under the [GNU General Public License v3.0](LICENSE).
@@ -420,6 +430,15 @@ Nebula 内置 Lua 5.4,使用 `require 'nebula'` 与
问题与反馈:[fickleheartedkeys@163.com](mailto:fickleheartedkeys@163.com)
### 🙏 致谢
Nebula 的界面层构建在 **[GPUI](https://github.com/zed-industries/zed)** 之上——由
[Zed](https://zed.dev) 团队开发的 GPU 加速 UI 框架——并使用
[Longbridge](https://github.com/longbridge) 的
**[gpui-component](https://github.com/longbridge/gpui-component)** 作为组件库。两者均以
Apache-2.0 许可发布,Nebula 通过带少量补丁的 fork 使用,版本在 workspace 清单中精确锁定。
感谢把这些工作开源——没有它,Nebula 现在的界面不会是这个样子。
### 📜 许可证
基于 [GNU 通用公共许可证 v3.0(GPL-3.0)](LICENSE) 发布。
+8
View File
@@ -29,6 +29,14 @@ The bundled conpty.dll and OpenConsole.exe come from the Windows
Terminal project (github.com/microsoft/terminal), distributed under the
MIT License.
The GPUI interface layer is built with gpui 0.2.2, developed by Zed
Industries (github.com/zed-industries/zed), and gpui-component 0.5.1,
developed by Longbridge (github.com/longbridge/gpui-component). Both are
distributed under the Apache License 2.0, whose text is reproduced below.
Nebula compiles modified forks of both crates; the fork baselines and the
applied patches are recorded in the workspace Cargo.toml under
[patch.crates-io].
---------------------------------------------------------------------
Apache License
+107
View File
@@ -0,0 +1,107 @@
# Nebula Terminal 1.1.0
## English
### Added
- Added remote backup destinations — Push the password-protected archive to a local or network folder, WebDAV, S3-compatible storage, or SFTP, and restore the newest one.
- Added a popup completion list — Choose between inline ghost text and a popup list of history, directories, PATH commands, and file matches.
- Added a real color picker for the custom background — Pick continuously on a saturation/value plane with a hue bar and live preview.
- Added clipboard screenshot paste — Pasting an image-only clipboard writes a PNG and pastes its path, uploading it first on SSH panes.
- Added configurable new-tab placement — Place new tabs after the active tab or at the end of the list. Contributed by @Sakyvo in #25.
- Added configurable terminal cell width — Choose Compact or Relaxed cell width, applied immediately. Contributed by @Sakyvo in #26.
- Added ordered font fallback chains — `font_family` accepts comma-separated families, and later families fill missing glyphs in order. (#33)
- Added middle-click to close tabs.
- Added an audible terminal bell — BEL plays the system notification sound, throttled and optional. (#37)
- Added AI conversation resume across restarts — Restored panes retype the exact claude or codex resume command.
- Added a system tray icon with agent attention — The tray shows an amber dot when an AI CLI waits for input and jumps to that pane.
- Added line numbers for plain-text files — `.txt`, `.log`, `.json`, and `.jsonl` now open in the code viewer.
- Added clickable file and URL links — Hovering shows a preview, and Ctrl+click opens the path or URI.
- Added a configurable terminal bell — Choose Off, Flash, Sound, or Flash + sound.
- Added a WYSIWYG font picker and a folder-picker startup directory.
- Added the GPUI window as the default in 1.1.0 packages — Pass `--legacy-shell` for the previous UI.
### Fixed
- Fixed "Open in Nebula" always starting a separate window instead of joining the running instance.
- Fixed imported terminals not being selectable in the default-shell dropdown.
- Fixed the Git panel blanking on repositories owned by another user.
- Fixed visible windows getting stuck without repainting. (#21, #32)
- Fixed "Open in Nebula" failing on drive roots such as `D:\`. (#36)
- Fixed multi-line paste interrupting codex and other bracketed-paste applications. (#35)
- Fixed formulas not rendering when an AI CLI removed the delimiter backslashes.
- Fixed the Markdown reader leaving the right side of the reading column ragged.
- Fixed Markdown images not loading, including remote badges and screenshots.
- Fixed raw HTML in Markdown not matching GitHub's centering, badge rows, and linked images.
- Fixed Codex `notify` growing `config.toml` to 130 MB when two notify wrappers wrapped each other. (#38)
- Fixed the tab context-menu shadow getting thicker with each open tab.
### Improved
- Improved File/Git side-panel refreshes — Snapshots are built on a worker thread, so the view stays usable.
- Improved tab disclosure and file-tree menus — Linear chevrons replace Nerd Font glyphs, and menus no longer pick up the drawer shadow.
## 中文
### 新增
- 新增远程备份目的地 — 把口令保护的归档推送到本地或网络目录、WebDAV、S3 兼容存储或 SFTP,并可恢复最新一份。
- 新增弹窗补全列表 — 在行内灰字与弹窗列表之间选择,弹窗汇总历史命令、常用目录、PATH 命令与文件匹配。
- 新增真正的背景色调色盘 — 在饱和度/明度取色面与色相条上连续取色,终端实时预览。
- 新增剪贴板截图粘贴 — 剪贴板只有图片时,粘贴会写出 PNG 并粘出路径;SSH 会话先上传再敲入远端路径。
- 新增可配置的新标签页插入位置 — 新标签可以插在当前标签之后或列表末尾。由 @Sakyvo 在 #25 中贡献。
- 新增可配置的终端单元格宽度 — 可选“紧凑”或“宽松”,切换后立即生效。由 @Sakyvo 在 #26 中贡献。
- 新增有序字体 fallback 链 — `font_family` 支持逗号分隔的字体族,后续字体族按顺序补齐缺失字形。(#33)
- 新增中键关闭标签页。
- 新增终端铃声 — BEL 会播放系统提示音,带节流且可关闭。(#37)
- 新增 AI 对话跨重启接续 — 恢复出的终端会自动重敲 claude 或 codex 的 resume 命令。
- 新增托盘图标 agent 提醒 — AI CLI 等待输入时托盘显示橙点,点击直达该终端。
- 新增纯文本文件行号 — `.txt`、`.log`、`.json`、`.jsonl` 现在用代码查看器打开。
- 新增可点击的文件与 URL 链接 — 悬停显示预览,Ctrl+点击打开路径或链接。
- 新增可配置的终端铃声 — 可选关、闪烁、声音、闪烁 + 声音。
- 新增所见即所得字体选择器与文件夹式启动目录选择。
- 新增 1.1.0 安装包默认进入 GPUI 主窗 — 旧界面用 `--legacy-shell` 启动。
### 修复
- 修复“在 Nebula 中打开”总是新开独立窗口、不并入已运行实例的问题。
- 修复导入的终端在默认 Shell 下拉中无法选中的问题。
- 修复 Git 面板在他人所有的仓库上空白的问题。
- 修复可见窗口卡住不重绘的问题。(#21、#32)
- 修复在 `D:\` 这类盘符根目录下“在 Nebula 中打开”报错的问题。(#36)
- 修复多行粘贴打断 codex 等 bracketed paste 应用的问题。(#35)
- 修复 AI CLI 去掉定界符反斜杠后公式不渲染的问题。
- 修复 Markdown 阅读器右侧参差、铺不满阅读列的问题。
- 修复 Markdown 图片无法加载的问题,包括网络徽章与截图。
- 修复 Markdown 中原生 HTML 的居中、徽章横排与链接图片与 GitHub 不一致的问题。
- 修复两个 notify 包装器互相包装、把 Codex 的 `config.toml` 撑到 130 MB 的问题。(#38)
- 修复标签右键菜单阴影随打开的标签数逐层变厚的问题。
### 改进
- 改进文件/Git 侧栏刷新 — 快照在工作线程中生成,刷新期间视图仍可使用。
- 改进标签折叠控件与文件树菜单 — 改用线性箭头替代 Nerd Font 字形,菜单不再叠上抽屉阴影。
## Contributors
- @Kuddev
- @Sakyvo:可配置新标签页插入位置(#25)、可配置终端单元格宽度(#26)。
Thanks for the reports / 感谢反馈:@Lawwaikit (#21)、@yellatiamo (#32)、
@Mr-Second (#35)、@Mikachu2333 (#36)、@Andyear (#37)、@Pancakefog (#38)。
## Reporting issues / 反馈问题
Bug reports and feature ideas are welcome at
[Issues → New issue](https://github.com/Kuddev/nebula/issues/new/choose) —
the templates ask for the version, Windows build, and the shell or AI CLI involved.
欢迎通过 [Issues → New issue](https://github.com/Kuddev/nebula/issues/new/choose)
提交问题与建议,模板会询问版本、Windows 版本和涉及的 shell 或 AI CLI。
---
**SHA256**
- `NebulaTerminal-v1.1.0-windows-x64.zip`: `1213FB61F5DEAD02A0442E77801D9A2E92382F18E26B1E77E0A0BE366F8B6FD7`
- `NebulaTerminal-1.1.0-windows-x64-setup.exe`: `492A79808042FE88D054366A5586B0E44AD205B3E3BEC024FB6C44FC78AE823F`
+17 -5
View File
@@ -767,6 +767,9 @@ pub struct NebulaWorkspace {
side_panel_anim_armed: bool,
/// 文件树右键:画在 workspace 根上,不进抽屉子孙树。见 `file_tree.rs`。
file_tree_menu: Option<file_tree::FileTreeContextMenu>,
/// 标签右键:同样画在根上。挂进标签行会让每一行都渲染同一份菜单,
/// popover 阴影按标签数叠厚。见 `tab_menu.rs` 模块头。
tab_menu: Option<tab_menu::TabContextMenu>,
/// 复用旧壳随包分发的 AI 品牌图,不用近似字体图标替代。
sidebar_logo_images: HashMap<(crate::display::AiLogo, bool), Arc<RenderImage>>,
/// 品牌图缓存对应的整数物理像素边长;窗口跨 DPI 显示器时据此重建。
@@ -908,6 +911,7 @@ impl NebulaWorkspace {
side_panel_polling: false,
side_panel_anim_armed: false,
file_tree_menu: None,
tab_menu: None,
sidebar_logo_images: sidebar_logo_images(sidebar_logo_target_px),
sidebar_logo_target_px,
_appearance_sub: appearance_sub,
@@ -3898,8 +3902,6 @@ impl NebulaWorkspace {
},
_ => None,
});
let workspace = cx.entity().downgrade();
let context_workspace = workspace.clone();
let hover_group: SharedString = format!("sidebar-tab-hover-{ix}").into();
let shell_tag = (is_terminal && activity == SidebarActivity::Idle)
.then(|| self.meta(ix).shell_tag)
@@ -4152,9 +4154,16 @@ impl NebulaWorkspace {
),
),
)
.context_menu(move |menu, window, cx| {
Self::tab_context_menu(menu, context_workspace.clone(), ix, window, cx)
});
// 右键只记锚点:菜单由 workspace 根上唯一一份宿主画。挂
// `.context_menu()` 会让每个标签行都渲染同一个 PopupMenu,
// 阴影按标签数叠厚(见 `tab_menu.rs` 模块头)。
.on_mouse_down(
MouseButton::Right,
cx.listener(move |this, event: &MouseDownEvent, window, cx| {
cx.stop_propagation();
this.open_tab_context_menu(ix, event.position, window, cx);
}),
);
if dragged {
// 骑指针 + 提到最上层画(deferred 只延后绘制、不动布局),
// 阴影给"拿起来"的抬升感。
@@ -4350,6 +4359,8 @@ impl NebulaWorkspace {
/// 列表锁成一行滚动区。
fn toggle_tabs_section(&mut self, cx: &mut Context<Self>) {
self.tabs_section_collapsed = !self.tabs_section_collapsed;
// 卷帘一动,菜单锚定的那一行就不在原处了。
self.tab_menu = None;
self.tabs_fold_armed = true;
self.tabs_fold_seq = self.tabs_fold_seq.wrapping_add(1).max(1);
let seq = self.tabs_fold_seq;
@@ -5143,6 +5154,7 @@ impl Render for NebulaWorkspace {
.when_some(self.render_file_tree_context_menu(), |root, menu| {
root.child(menu)
})
.when_some(self.render_tab_context_menu(), |root, menu| root.child(menu))
// 组件库的模态/通知层不会自己上屏:`Root::render` 只画宿主视图,
// dialog/notification 两层由宿主显式挂。挂在最外层链尾=盖住命令
// 面板和所有拖拽罩层;dialog 在下、notification 在上,确认框弹着
+79 -30
View File
@@ -3,10 +3,27 @@
//! 「分叉 AI 会话」必须在菜单打开当下按 hook 身份 + 官方 fork 语法重算,
//! 不能吃侧栏上一帧的快照——否则刚接到 session_id、或冷恢复种下身份后
//! 右键仍看不到这一行。
//!
//! # 为什么不用 `ContextMenuExt::context_menu()`
//!
//! 2026-08-18:那个扩展会把 `ContextMenu` 包在被挂载元素的**外面**,而它的
//! `ElementId` 在 fork 里是硬编码的 `"context-menu"`
//! (`crates/ui/src/menu/context_menu.rs`)。包裹层的 id 先于内层行 id 入
//! `GlobalElementId` 栈,于是每个标签行的 `ContextMenu` 算出来的 id 路径完全
//! 相同、共享同一份 element state(含那个 `Rc<RefCell<ContextMenuSharedState>>`)。
//! 菜单一打开,`open == true` 对**所有**标签行同时成立,每一行都渲染同一个
//! `PopupMenu` entity、落在同一个锚点上。面板底不透明,叠 N 次看不出来;
//! popover 阴影是半透明的,alpha 叠 N 层——这就是「标签越多,右键菜单阴影
//! 越厚」的全部原因,与打包资源和用户配置都无关。
//!
//! 所以这里跟 `file_tree.rs` 用同一套载体:标签行只记锚点和下标,菜单由
//! workspace 根上唯一一份 `deferred(anchored)` 画,组件自带的 popover 阴影
//! 因此保持单层。
use gpui::{
App, InteractiveElement as _, MouseButton, ParentElement as _, Styled as _, Window, div,
prelude::FluentBuilder as _, px,
AnyElement, App, Context, Corner, DismissEvent, Entity, Focusable as _,
InteractiveElement as _, IntoElement as _, MouseButton, ParentElement as _, Pixels, Point,
Styled as _, Subscription, Window, anchored, deferred, div, prelude::FluentBuilder as _, px,
};
use gpui_component::menu::PopupMenuItem;
@@ -19,6 +36,17 @@ use super::{
CloseActiveTerminal, NebulaWorkspace, RenameActiveTab, SplitDown, SplitRight, WorkspaceTab,
};
/// 标签右键菜单的宿主。画在 workspace 根上,不进标签行的子孙树——理由见
/// 模块头。
pub(super) struct TabContextMenu {
menu: Entity<PopupMenu>,
position: Point<Pixels>,
/// 菜单里每条命令捕获的都是下标。标签集合在菜单挂着的时候变了(远端
/// 或 AI 侧自动关标签),这一份就不该再画。
ix: usize,
_subscription: Subscription,
}
/// 旧壳 `sync_chrome_tabs`:只有 Default / 检测 Shell 的 tab 才允许分叉。
/// Profile 可能直接启动 agent;SSH 会把命令打进认证提示。
pub(super) fn tab_launch_allows_ai_fork(launch: Option<&LaunchSession>) -> bool {
@@ -38,36 +66,57 @@ pub(super) fn tab_ai_fork_enabled(workspace: &NebulaWorkspace, ix: usize, cx: &A
}
impl NebulaWorkspace {
/// 右键打开当下现查 fork 资格,并先选中该行(旧壳 chrome 同惯例)。
pub(super) fn tab_context_menu(
menu: PopupMenu,
workspace: gpui::WeakEntity<Self>,
/// 右键:先选中该行(旧壳 chrome 同惯例),再按当下的 hook 身份现查
/// fork 资格,最后把菜单交给根上那一份宿主。
pub(super) fn open_tab_context_menu(
&mut self,
ix: usize,
position: Point<Pixels>,
window: &mut Window,
cx: &mut App,
) -> PopupMenu {
let (terminal, ai_fork, color) = workspace
.upgrade()
.map(|entity| {
entity.update(cx, |this, cx| {
if ix < this.tabs.len() {
this.activate_tab(ix, window, cx);
}
(
this.tabs.get(ix).is_some_and(WorkspaceTab::is_terminal),
tab_ai_fork_enabled(this, ix, cx),
this.meta(ix).color,
)
})
})
.unwrap_or((false, false, None));
Self::tab_popup_menu(
menu.external_link_icon(false),
workspace,
ix,
terminal,
ai_fork,
color,
cx: &mut Context<Self>,
) {
if ix >= self.tabs.len() {
return;
}
self.activate_tab(ix, window, cx);
let terminal = self.tabs.get(ix).is_some_and(WorkspaceTab::is_terminal);
let ai_fork = tab_ai_fork_enabled(self, ix, cx);
let color = self.meta(ix).color;
let workspace = cx.entity().downgrade();
let menu = PopupMenu::build(window, cx, move |menu, _window, _cx| {
Self::tab_popup_menu(
menu.external_link_icon(false),
workspace,
ix,
terminal,
ai_fork,
color,
)
});
menu.focus_handle(cx).focus(window);
let subscription = cx.subscribe_in(&menu, window, |this, _, _: &DismissEvent, _, cx| {
this.tab_menu = None;
cx.notify();
});
self.tab_menu = Some(TabContextMenu { menu, position, ix, _subscription: subscription });
cx.notify();
}
pub(super) fn render_tab_context_menu(&self) -> Option<AnyElement> {
let state = self.tab_menu.as_ref()?;
if state.ix >= self.tabs.len() {
return None;
}
Some(
deferred(
anchored()
.position(state.position)
.snap_to_window_with_margin(px(8.0))
.anchor(Corner::TopLeft)
.child(state.menu.clone()),
)
.with_priority(1)
.into_any_element(),
)
}