news 2026/9/19 11:27:40

BrewUI:给Homebrew套上可视化外衣,让软件管理更轻松

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BrewUI:给Homebrew套上可视化外衣,让软件管理更轻松

如果你手里有一台 Mac,并且习惯用终端装软件,那你大概率知道 Homebrew 这个名字。但如果你刚接触 macOS,或者平时不太爱碰命令行,那一串brew installbrew updatebrew cleanup敲下来,确实容易劝退。BrewUI 就是冲着这个痛点来的:它把 Homebrew 的核心操作装进一个图形界面里,让你用鼠标点击就能完成软件包的搜索、安装、升级和清理,同时保留了命令行工具的灵活性。这篇文章我会从工具定位、功能拆解、技术实现、实操配置到问题排查,把 BrewUI 完整过一遍,无论你是刚转向 Mac 的新手,还是整天泡在终端里的老手,都能找到值得参考的东西。

1. 项目定位与核心价值

1.1 Homebrew 到底是什么,为什么需要图形界面

说 BrewUI 之前,必须先搞清楚它服务的对象。Homebrew 是 macOS 上使用最广泛的包管理器,它的工作方式类似于 Linux 世界里的 apt 或 yum:你只需要在终端里输入一条命令,它就会自动下载、编译、安装软件,并处理依赖关系。比如你要装 wget,输入brew install wget,它会自动把 OpenSSL、libidn2 这些依赖一起装好,省去了一堆手动配置的麻烦。

但命令行的门槛是真实存在的。第一次用 Homebrew 的人,往往会遇到几个问题:不知道有哪些包可以装,只能去网上搜;安装后想看某个软件装在哪,得先搞懂 Formula 和 Cask 的区别;系统提示有更新时,不知道哪些该升级、哪些升级了可能出问题;依赖关系复杂时,靠brew deps --tree输出的字符树又不够直观。这些问题本身不影响 Homebrew 的强大,但确实抬高了使用门槛。

BrewUI 的定位就是给 Homebrew 套上一层“可视化外衣”。它把最常用的操作放到图形界面里,把原本靠记忆和文档才能完成的事情变成点击按钮。同时,它没有替换 Homebrew,而是在 Homebrew 之上做封装——底层依然是那套经过千锤百炼的命令行逻辑,界面只是帮你去掉了记忆负担和误操作风险。

1.2 BrewUI 解决的核心痛点

从实际使用角度看,BrewUI 最让人满意的不是“好看”,而是它把几类高频场景变得特别顺手。

第一类是包搜索与发现。命令行里找包靠brew search 关键词,结果是一长串文本,排列顺序和分类都不直观。在 BrewUI 里,所有 Formula 和 Cask 会被分门别类列出,还有搜索框、安装量排序、更新时间等维度,你很容易就能找到想要的软件。

第二类是更新管理。系统里装了上百个包之后,brew outdated会列出一大串待更新列表。命令行只能看到版本号,没法快速判断哪些是重要更新、哪些可能影响现有环境。BrewUI 会把待更新包的详情、依赖范围、更新日志入口放到一个界面里,帮你做决策。

第三类是依赖关系可视化。Homebrew 的包依赖树在终端里输出是一坨缩进的字符,深一点的依赖关系看起来非常费劲。BrewUI 把依赖关系画成图形,谁是根节点、谁依赖谁、升级某个包会影响哪些软件,一眼就能看明白。

第四类是环境维护。brew cleanup释放磁盘空间、brew doctor检查环境异常,这些操作在命令行里要记参数和输出格式,而 BrewUI 把它们做成了“一键体检”的形式,对非深度用户特别友好。

1.3 适用人群与实际使用场景

说实话,BrewUI 并不适合所有人。如果你是一个天天泡在终端里的开发者,brew命令已经形成了肌肉记忆,那么多一个图形界面反而会打断节奏。但如果你是下面这几类人,BrewUI 的价值会非常明显。

第一类是刚刚从 Windows 切换到 Mac 的用户。他们习惯图形界面操作,对包管理器的概念还不熟,终端输入命令本身就有点心理门槛。BrewUI 能帮他们无痛上手 Homebrew,等熟悉了再慢慢接触命令行。

第二类是把 Mac 当工作机的设计师、产品经理、运营等非技术岗位。他们需要装一些效率工具,比如 Chromium 浏览器、截图工具、输入法、字体等,但不想在这些事情上花时间读文档,也不想被依赖问题困扰。

第三类是已经用了很久 Homebrew、但包数量膨胀到难以管理的开发者。你装了三四百个包,很多已经忘了是干嘛的,用 BrewUI 的列表和分类功能,可以做一次“大扫除”,把不再需要的包清理掉。

我自己就属于第三类。每次在终端里看到一个陌生的包名,点进去看依赖关系后才能想起来当初为什么装它。用 BrewUI 之后,这类“考古”工作变得轻松了很多。

2. 核心功能拆解与模块设计

2.1 软件包管理:搜索、安装、安装源选择与卸载

包管理是 BrewUI 最基础的功能模块。在它的主界面上,你可以浏览全部可安装的 Formula(命令行工具)和 Cask(图形化应用),也可以直接搜索。

这里有个细节值得说一下:Homebrew 的 Formula 和 Cask 是两套不同的体系。Formula 是命令行工具,比如 git、wget、python;Cask 是完整的图形应用,比如 Google Chrome、Visual Studio Code、WeChat。在终端里,它们的安装命令不同,卸载方式也不同。在 BrewUI 里,两者会被明确区分开,界面上会标注类型标签,你可以一次筛选出所有带图形界面的 App,也可以只搜索命令行工具。

安装操作本身非常直观:点进某个软件包详情页,能看到版本号、简介、依赖列表、安装大小、许可证信息,点击安装按钮即可。BrewUI 会自动调用底层 Homebrew 执行安装,并在界面里实时展示进度日志。

这里我想给新手一个建议:Cask 安装的软件,本质上是把应用拖入 “/Applications” 目录的过程。BrewUI 可以帮你完成这一步,但如果你需要手动安装一个没有 Cask 支持的软件,还是要回终端用其他方式。BrewUI 不能替代所有安装路径,但它能把大多数常见情况覆盖掉。

卸载功能对老手同样有价值。你可以在 BrewUI 里多选几个软件包,一键批量卸载,不用再一个个敲brew uninstall。而且卸载前它会显示“这个包被其他多少个包依赖”,如果发现是其他软件的重要依赖,你可以先取消操作,避免卸载完导致其他软件运行异常。

2.2 更新提醒与批量升级策略

系统里包多了之后,最头疼的是更新管理。默认情况下,Homebrew 不会自动升级任何包,你需要手动执行brew upgrade。但这个命令默认会升级所有能升级的包,某些时候并不安全。

BrewUI 把更新管理做成了可视化的“待办列表”。界面上会列出所有有新版本的包,并显示当前版本和目标版本,你可以逐个查看更新日志、依赖关系,然后选择升级单个包、某几个包,或者全部升级。

我通常不建议一上来就“全部升级”。原因很简单:Homebrew 的依赖链可能会导致升级 A 后要求升级 B,而 B 的新版本可能跟你正在用的另一个工具不兼容。在 BrewUI 里,我会先看依赖关系图,评估升级影响范围,再决定一次升级多少。这个评估过程在终端里太费劲了,但在图形界面里,只需点几下就能完成。

另外要提醒一点:Homebrew 自己的升级和包的升级是两回事。brew update是更新 Homebrew 自身和它的 Formula 索引,brew upgrade才是更新已安装的软件。BrewUI 界面上一般会把这两者分开,先执行“更新索引”,再显示“可升级软件包”。如果界面上只有一个按钮,也请你先确认已经执行过索引更新,再去升级软件包,否则会看不到最新版本的列表。

2.3 依赖关系图:看清包与包之间的联系

在终端里敲brew deps --tree git,你会看到一坨嵌套的字符,层级多的时候得瞪大眼睛才能看懂。BrewUI 把依赖关系画成了可交互的图形界面,每个节点是一个软件包,连线代表依赖关系。

这个功能实际使用中解决了很多问题。比如我想卸载某个不常用的包,但又担心它被其他包依赖,提前在依赖图里搜索一下,马上就能看到哪些包引用了它。再比如,升级某个包之前,我可以通过依赖图判断它会影响哪些下游软件,从而决定是否需要一起升级。

依赖图还有一个进阶用途:排查环境问题。如果你发现某个软件运行报错,怀疑是动态库冲突,可以在 BrewUI 里查看它的依赖链,看看有没有两个包依赖了同一个库的不同版本。虽然 BrewUI 不会直接给出修复命令,但它能帮你缩小排查范围。

2.4 一键诊断与系统清理

Homebrew 自带的brew doctor是个好东西,它会检查环境变量、目录权限、残留进程等问题,但输出是全英文的长文本,新手看了容易慌。BrewUI 把brew doctorbrew cleanup做成了可视化的“体检报告”。

体检报告会分类列出发现的问题:哪些是警告级别,哪些是错误级别,哪些只是提示。对每个问题,BrewUI 会给出一个“自动修复”或“查看详情”的入口。你不需要理解每一行英文日志,只要按照界面提示点击处理即可。

清理功能对应的是brew cleanup,它会清理已卸载包的残留文件、过旧版本的缓存,释放磁盘空间。在 BrewUI 里,清理前它会扫描并预估可释放的空间大小,让你决定是否真的要执行。我见过太多人盲目在终端里执行brew cleanup --prune=all,结果把一些想留作回滚的旧版本也清掉了。在 BrewUI 里,你至少能看到要清理什么,再决定是否动手。

3. 技术方案与实现思路

3.1 技术栈选型:为什么选择桌面应用框架而不是脚本

BrewUI 在技术实现上有一个绕不开的选择:是做成终端里的 TUI(Text User Interface),还是做成独立的桌面应用?TUI 的好处是依赖少、实现快,坏处是交互体验和 Windows/macOS 原生应用差距明显,对目标用户不友好。所以 BrewUI 这类工具通常会选择桌面应用框架。

桌面应用的实现路径大致有三条:Electron、Tauri、SwiftUI 原生应用。

Electron 的优点是生态成熟、跨平台一致,用 Web 技术就能开发,社区有大量现成组件。缺点是体积大、内存占用高,一个包管理器工具动不动吃掉几百 MB 内存,实在有点浪费。

Tauri 是后起之秀,核心用 Rust 编写,前端依然用 Web 技术,但体积和内存占用远小于 Electron。缺点是生态相对年轻,一些组件需要自己造轮子。

SwiftUI 是原生方案,性能和体验最好,但只支持 Apple 平台,而且意味着开发语言被锁定在 Swift。

从我了解到的情况来看,很多同类工具选择 Tauri 或 Electron,主要原因是前端技术栈上手快、开发效率高,并且方便未来扩展到 Windows/Linux 版本。如果你是打算自己从零写一个 BrewUI 这样的工具,我建议优先考虑 Tauri,除非你对 Rust 完全陌生,那才退而选择 Electron 保底。

3.2 命令封装与数据解析:如何稳定地和 Homebrew 交互

BrewUI 本质上是一个封装器(Wrapper),所有底层操作都通过调用 Homebrew 的命令行工具完成。但“调用命令”这件事,说起来容易,做起来要处理的细节非常多。

首先是命令输出格式的解析。Homebrew 默认的输出是给人看的文本,颜色、缩进、对齐都是为终端设计的。程序要解析这种文本,稳定性很差,因为任何一个版本升级导致的输出文案变化,都可能让解析逻辑失效。所以 BrewUI 这类工具会优先使用 Homebrew 的 JSON 输出模式。

brew info --jsonbrew list --json可以输出结构化数据,包含包名、版本、依赖、安装路径、许可证、描述等字段。用jq或者内置的 JSON 解析库处理这些数据,既稳定又高效。我自己在写相关脚本时也习惯用--json,因为它把“人类可读”和“机器可读”彻底分离了。

其次是命令执行时的环境变量。BrewUI 作为图形应用启动时,继承的环境变量可能跟终端不一样。PATH 是最典型的坑:如果用户的 Homebrew 安装在非默认路径,比如 Apple Silicon Mac 上的 “/opt/homebrew”,那 BrewUI 启动时需要把 “/opt/homebrew/bin” 加进 PATH,否则它找不到brew命令。

第三是长任务的异步处理。安装大软件包可能要几分钟,如果界面卡住等待命令执行完毕,体验会非常差。好的实现方式是把命令放进后台队列,用异步回调更新界面上的进度日志。BrewUI 这类工具通常在界面上会显示实时日志,就是这个原因。

3.3 权限与路径设计:sudo 与目录安全

Homebrew 的大部分操作都不需要 root 权限,因为它的安装目录一般属于当前用户。但在某些特殊情况下,比如系统包的权限被改过,或者你要执行某些影响全局的操作,就可能需要管理员密码。

BrewUI 在设计时通常会把“需要 sudo 的操作”和“不需要 sudo 的操作”严格分开。默认情况下,普通安装、卸载、更新都不弹密码框;只有遇到权限错误时,才会提示输入密码执行sudo修复。

这里有一个安全原则值得强调:图形工具不应随意用sudo执行命令。因为sudo代表着权限提升,一旦命令拼错或者被恶意变量影响,后果比普通用户权限下严重得多。我在实际使用中,如果发现某个操作明确报权限错误,会去终端手动执行一遍,确认为什么是权限问题,再用 BrewUI 的修复接口去处理,而不是直接给它更高权限。

3.4 与命令行操作的状态同步机制

BrewUI 是架在 Homebrew 之上的 GUI,但你在终端里执行的brew install xxx,BrewUI 界面不一定能实时感知。这就是“状态同步”问题。

设计良好的 BrewUI 会在每次界面激活或刷新时,重新读取brew listbrew outdatedbrew services list等信息,重建界面状态。如果你在终端里装了一个包,回到 BrewUI 没看到,不要急着卸载重装,先手动刷新一下界面。

如果你想在终端和 GUI 之间保持流畅切换,这里有一个小技巧:在终端里执行完任何brew命令后,切到 BrewUI 按一次刷新快捷键。等你习惯了这种配合方式,你会发现 GUI 和 CLI 并不矛盾,反而能互补。

4. 实操过程与使用配置

4.1 安装与首次启动检查清单

先说安装。BrewUI 本身的安装方式取决于你是从哪里获取的。如果你是从 GitHub Releases 下载的安装包,直接解压后将应用拖入 “Applications” 目录即可。如果你是通过brew install --cask brewui这类方式安装,那它会走 Cask 的标准流程。安装完成后首次启动,建议按下面的清单做一次检查。

首先,确认系统里已经装好了 Homebrew。BrewUI 只是个壳,壳下面必须有真正的brew工具。在终端执行brew --version,能看到版本号说明 Homebrew 就绪。如果还没有,先去 Homebrew 官网找到安装命令,安装完成后再启动 BrewUI。

其次,检查 Homebrew 的安装路径。Intel Mac 上默认是 “/usr/local”,Apple Silicon Mac 上默认是 “/opt/homebrew”。如果你的 Homebrew 不在默认路径,需要在 BrewUI 的设置里手动指定brew可执行文件的位置。

第三,核对网络环境。BrewUI 需要访问 GitHub 和 Homebrew 的官方仓库来获取索引和下载软件包。如果网络状况不佳,装包速度会非常慢。这一点在后面的“源切换”部分我详细展开。

4.2 第一次搜索安装与卸载的完整流程

我用一个实际例子来演示 BrewUI 的基本操作流程。假设我想安装一个终端下载工具 wget。

打开 BrewUI 主界面,在搜索框输入 “wget”,结果列表里会出现对应的 Formula。点进去看详情,界面会显示当前版本、依赖了哪些库、安装后被放置在哪个目录等信息。确认无误后,点击“安装”按钮。

此时界面底部会展开一个日志区域,逐行显示brew install wget的输出。如果是第一次安装,Homebrew 会先更新索引,然后下载依赖包,整个过程在几十秒到几分钟不等。安装完成后,状态会从“未安装”变为“已安装”,wget 也出现在已安装列表里。

卸载流程更简单:在已安装列表里找到 wget,点击卸载,BrewUI 会弹出确认框,询问是否同时清理残留的依赖包。这里我提醒一句:如果某个包被其他包依赖,卸载时不要在确认框里勾选“清理未使用的依赖”,否则可能连带卸载掉你还需要的库。

4.3 软件源切换与国内网络优化方案

国内用户使用 Homebrew 最大的痛点是速度:默认源指向 GitHub,下载大文件时经常断断续续。BrewUI 本身不解决网络问题,但你可以通过修改 Homebrew 的源配置来加速,BrewUI 会同步受益。

常见做法是把 Homebrew 的 core 仓库和 cask 仓库替换成国内镜像源。以清华、中科大、阿里云等镜像站为例,步骤大致是:

进入终端,执行命令替换 Homebrew 的远程仓库地址。以替换 core 仓库为清华源为例:git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git

cask 仓库同理:git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git

之后执行brew update,让本地索引和镜像源同步。

这里要提醒一点:镜像源和官方源之间存在同步延迟,有时候你用镜像源查到的版本比官方晚一两天。对大多数软件来说影响不大,但如果你很在意第一时间获得新版,可以保留官方源,只在下载瓶颈时临时切换。我个人更推荐的做法是保留官方源,但给 Homebrew 配置合适的代理或者合理调整 Git 缓冲参数。不展开细说,搜索引擎有很多资料。

无论你选择哪种方案,修改完源之后,请务必重启 BrewUI,再执行一次“刷新索引”,确保界面显示的数据和镜像源一致。

4.4 与终端结合的进阶用法

BrewUI 虽然提供了图形界面,但它不是孤立工具。熟悉 Homebrew 的人可以把终端和 BrewUI 结合使用,发挥各自优势。

比如,BrewUI 适合做浏览、搜索、依赖分析、批量卸载这些交互复杂的操作。而终端适合跑一些批处理脚本、修改 Formula 的具体配置、处理 brew services 这种服务管理任务。

举个实际场景:我想查看某个软件包的启动服务状态。在 BrewUI 里看到 brew 服务列表也许只显示“是否启动”,但在终端里执行brew services info mysql能看到更详细的日志路径和启动参数。遇到这种需求,我会在 BrewUI 里确认服务状态,去终端查看详细日志。

还有一个具体的配合技巧:BrewUI 里显示的软件包名,在终端里就是brew install的参数。我在 BrewUI 里发现一个可疑的陌生包时,会复制它的名字,在终端里执行brew home 包名打开它的官网,判断这个包是否还安全、是否值得保留。

5. 常见问题与排查技巧

5.1 权限报错:Operation not permitted

使用 BrewUI 时最常见的报错就是权限不足,错误信息里通常带 “Operation not permitted” 或是某个目录 “Permission denied”。

遇到这类问题,先不要急着给 BrewUI 授权 sudo。第一件事是检查目录归属:在终端执行ls -l /usr/localls -l /opt/homebrew,看目录的所有者和用户组是否正常。Homebrew 要求这些关键目录归当前用户所有,如果变成 root 所有,绝大部分操作都会失败。

修复归属的命令是:sudo chown -R "$(whoami)" /opt/homebrew(Intel 机器把路径换成/usr/local

执行完再回到 BrewUI 刷新界面,重新操作。如果问题依旧,再考虑是不是系统完整性保护(SIP)或安全策略导致的限制,这类情况相对少见。

5.2 界面卡顿与进程残留

长时间使用后,BrewUI 可能表现“卡顿”。常见原因是界面里存的临时数据太多,或者某个后台进程没退出。

最直接的排查办法是打开“活动监视器”,搜索 BrewUI 进程,看看它是否在持续占用 CPU。如果占满,可能是它在反复读取 Homebrew 数据。你可以先强制退出应用,重新打开。如果每次打开都卡,检查一下是不是安装的包数量特别多,导致启动时加载列表缓慢。此时可以在 BrewUI 设置里减少启动加载的包类型范围,比如默认只加载 Formula,不加载 Cask。

另一个隐蔽问题是命令残留进程。比如你在 BrewUI 里发起了安装任务,但中途取消了,底层可能还残留着brew install的子进程。遇到这种情况,使用pkill -f "brew install"清理残留进程,再重新操作。不要担心误杀,因为它只会杀掉 brew 安装相关的进程。

5.3 更新源缓慢与安装失败

如果你没有修改过源配置,安装大软件时经常失败,十有八九是网络问题。GitHub 下载大文件不稳定,严格来说这是国内网络环境的常态。

处理思路是分层级的。第一层:重试。很多下载失败是偶发性的,点几次重试可能会成功。第二层:修改 Git 缓冲大小。针对 git 协议的下载,在终端执行git config --global http.postBuffer 524288000能缓解一部分问题。第三层:切换源。按前面说的方式把核心仓库源切换为国内镜像,这是见效最明显的方案。

安装失败后还有一个细节:Homebrew 会留下部分下载缓存。如果重试一直失败,建议先在 BrewUI 里执行一次清理,再用终端执行brew cleanup清空缓存目录,避免损坏的缓存文件影响后续下载。

5.4 其他高频问题速查

我把平时遇到的其他问题整理成一张速查表,方便你对照排查:

现象可能原因处理办法
BrewUI 显示“brew 命令未找到”Homebrew 安装路径不在 PATH在设置里手动指定 brew 完整路径
搜索结果和终端不一致索引未更新执行索引更新后刷新界面
升级后软件无法启动依赖库版本冲突查看依赖图,回滚其中一个包版本
清理后仍然没释放多少空间缓存目录默认保留旧版本使用brew cleanup --prune=all并按提示二次确认
界面显示已安装,但终端说未安装安装目录不一致检查 Homebrew 前缀路径是否设置正确
批量安装过程中部分包失败依赖缺失或网络波动先看日志,按失败包名依次重试

最后再分享一个我在长期使用中总结的体会:BrewUI 这类图形工具最大的价值,不是把命令行“变没”,而是把命令行里那些难以理解和记忆的部分,转化成清晰可见的信息。我在面对复杂依赖问题时,依然会打开终端去追根溯源,但日常的软件管理和维护,用 BrewUI 确实省了不少事。如果你也是那种“明明会命令行,但有时候就想省点脑力”的人,非常值得花半小时把 BrewUI 用熟,它不会让你失望。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 11:26:17

SUMO智能网联车仿真:Python驱动的确定性交通流建模

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 11:24:18

区块链扩容技术:Rollup原理与Rust实践

1. 为什么我们需要区块链扩容?区块链技术发展到今天,性能瓶颈已经成为制约其大规模应用的主要障碍。以太坊主网每秒只能处理15-45笔交易,这个数字在传统金融系统面前简直微不足道。我去年参与的一个DeFi项目就因为网络拥堵导致用户支付了高达…

作者头像 李华
网站建设 2026/9/19 11:21:45

iPhone零App投屏电脑:Mac原生与Windows接收端设置详解

想把手里的iPhone画面投到电脑上,很多人第一反应就是去App Store翻投屏软件。其实大多数场景下,手机端完全可以保持零安装,真正卡住你的反而是电脑端——你的电脑到底站在哪条协议阵营里、有没有开启对应的接收端。这句话我放到最前面&#x…

作者头像 李华
网站建设 2026/9/19 11:19:34

TP4056锂电池充电管理芯片从原理到STM32工程实践全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 11:19:31

YOLOv5s目标检测实战:从数据标注到ONNX部署全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华