Electron + CLI 双形态工具实战:Homebrew 与 winget 跨平台分发指南
1. 从 t3code 这个标题说起它到底想解决什么问题第一次看到 “t3code” 这个标题我脑子里蹦出来的第一个念头是这大概率是一个把Electron 桌面端、CLI 命令行工具和跨平台包管理揉在一起的项目。为什么这么判断因为热搜词里同时出现了 Electron、CLI、Homebrew、winget 这几个关键词它们分别对应桌面应用运行时、终端交互入口、macOS 包管理器和 Windows 包管理器。一个项目如果同时踩中这四个点那它想做的事情基本就清晰了——用一套代码既能在终端里跑又能以桌面应用的形式交付还要让 macOS 和 Windows 用户都能用最顺手的方式装上。我做过好几个类似形态的工具说实话这种“CLI GUI 双形态”的项目在架构设计上是最容易翻车的。纯 CLI 工具轻量、好分发但普通用户看到黑框就发怵纯 Electron 应用体验好但体积动辄上百兆启动慢而且很多开发者根本不习惯离开终端。t3code 这个标题背后我理解它想走的是中间路线核心逻辑用 CLI 承载Electron 作为一层友好的外壳包管理交给 Homebrew 和 winget 去处理安装分发。这个思路在最近两年的开发者工具里越来越常见因为大家终于意识到强迫用户改变习惯是成本最高的事情。这篇文章我打算把 t3code 这类项目从设计思路到落地实操完整拆一遍。不管你是想自己做一个类似形态的工具还是想搞清楚 Electron 和 CLI 怎么协同、Homebrew 和 winget 怎么配置都能从里面找到能直接抄的东西。我会尽量说人话把那些官方文档里不会写的坑都摆出来。适合有 Node.js 基础、做过一点命令行工具或者 Electron 项目的开发者纯小白也能看懂但部分操作需要你动手敲命令。2. 整体架构设计为什么是 Electron CLI 这套组合2.1 双形态交付的核心逻辑先说说为什么这类项目会选择 Electron 加 CLI 的双形态而不是二选一。我早期做过一个纯 CLI 的工具功能没问题但推广的时候发现一个尴尬的现象目标用户里大概有六成的人看到“请打开终端执行以下命令”就关掉页面了。后来我又尝试把它包成一个 Electron 应用结果老用户抱怨说“我就想写个脚本调用一下你给我整个 GUI 干嘛”。这两个教训让我明白不同用户对同一个工具的交互预期是完全不同的。t3code 这类项目的解法是把真正的业务逻辑抽成一个独立的 core 模块CLI 和 Electron 都只是这个 core 的“调用方”。CLI 负责脚本化、自动化、CI 集成这些场景Electron 负责可视化配置、结果预览、文件拖拽这些场景。两者共享同一套配置文件和同一份数据用户在终端里改的配置打开桌面端能看到在桌面端点的按钮回到终端执行命令也生效。这个设计的关键在于core 层不能依赖任何 Electron 的 API否则 CLI 就跑不起来了。具体到目录结构我通常会这样组织t3code/ ├── packages/ │ ├── core/ # 纯 Node.js 逻辑无任何 GUI 依赖 │ ├── cli/ # 命令行入口依赖 core │ └── desktop/ # Electron 应用依赖 core ├── package.json # workspace 根配置 └── scripts/ # 构建、打包、发布脚本用 monorepo 的好处是 core 的改动能同时被 CLI 和 desktop 引用不用手动同步代码。坏处是构建链路会复杂一些尤其是 Electron 打包的时候要把 core 的依赖正确打进去这个后面会细说。2.2 Electron 在这里扮演什么角色很多人对 Electron 有误解觉得它就是“套壳浏览器”。这话对了一半。Electron 确实基于 Chromium但它提供的主进程与渲染进程分离的模型恰好适合 t3code 这种需要调用系统能力的工具。主进程负责文件读写、调用 CLI 的 core 逻辑、管理窗口生命周期渲染进程只负责界面展示通过 IPC 和主进程通信。我为什么强调这个分离因为如果你把文件操作直接写在渲染进程里会遇到两个问题一是安全策略限制二是打包后路径解析会出错。正确做法是渲染进程发一个 IPC 请求主进程收到后调用 core 模块处理再把结果返回去。这样 CLI 和 Electron 走的是完全相同的 core 代码路径行为一致测试也好写。还有一个细节是localhost 的使用。热搜词里出现了 “electron localhost”这通常是指 Electron 在开发模式下加载http://localhost:xxxx的开发服务器生产模式下加载本地 HTML 文件。这个切换逻辑必须写对否则打包出来的应用会白屏。我一般会在主进程里这样判断const isDev !app.isPackaged; if (isDev) { win.loadURL(http://localhost:5173); } else { win.loadFile(path.join(__dirname, renderer/index.html)); }app.isPackaged这个判断比读环境变量可靠得多因为打包后环境变量可能丢失但isPackaged是 Electron 自己维护的状态。2.3 CLI 入口的设计取舍CLI 部分我踩过最大的坑是参数解析库的选择。早期用commander后来换到yargs再后来发现对于 t3code 这种子命令比较多的工具cac或者原生的util.parseArgs反而更清爽。选哪个不是重点重点是CLI 的启动速度。Node.js 启动本身就有 100ms 左右的开销如果你在入口文件里require了一堆重量级依赖用户敲个t3code --help都要等一秒体验就很差。我的做法是把重依赖延迟加载。比如只有真正执行某个子命令时才去require对应的模块--help和--version走轻量路径。实测下来这样能把冷启动时间从 800ms 压到 200ms 以内。对于经常在终端里敲命令的人来说这个差别是能感知到的。3. 核心细节解析Electron 打包与 CLI 分发的关键点3.1 Electron 打包的常见坑与参数计算Electron 打包我用过electron-builder和electron-forge两套方案最后长期留在electron-builder原因是它对多平台产物的配置更直观而且和 Homebrew、winget 的发布流程衔接更顺。打包时最容易出问题的是asar 归档。默认情况下 Electron 会把源码打成一个app.asar文件这能加快加载速度但如果你的 core 模块里有需要读取外部文件的逻辑路径就会错乱。解决办法是在electron-builder配置里用asarUnpack把需要真实文件路径的资源排除出去{ build: { asar: true, asarUnpack: [ **/node_modules/你的原生模块/** ] } }另一个关键参数是压缩级别。Electron 安装包动辄 80MB 到 150MB如果用默认压缩用户下载会很痛苦。我一般会开启compression: maximum代价是打包时间变长但产物能小 20% 左右。还有一个技巧是把files字段写精确只打包真正需要的文件别把测试用例、文档、源码映射都塞进去。关于 “electron 打包 apk” 这个热搜词我得说明一下Electron 本身不支持直接打包成 Android 的 apk。如果你看到有人这么说要么是他用了额外的桥接方案要么是概念混淆了。t3code 这类工具如果真要上移动端通常是另起一个 React Native 或者 Capacitor 项目复用 core 逻辑而不是硬把 Electron 塞进 apk。3.2 Homebrew 分发从 formula 到 tapmacOS 用户装命令行工具第一反应就是brew install。t3code 要支持 Homebrew 安装需要创建一个formula文件放到自己的 tap 仓库里。formula 本质上是一个 Ruby 脚本告诉 Homebrew 去哪里下载、怎么安装。一个最简的 formula 长这样class T3code Formula desc T3code CLI and desktop tool homepage https://example.com/t3code url https://example.com/t3code-1.0.0.tar.gz sha256 下载文件的sha256校验值 license MIT def install bin.install t3code end end这里有几个实操要点。第一sha256必须和实际下载文件一致否则安装会报错我每次发版都要重新算一遍。第二如果你的工具依赖 Node.jsformula 里要声明depends_on node但更好的做法是把 Node 运行时一起打包进去避免用户环境差异。第三bin.install只是把可执行文件放到 PATH 里如果 CLI 还需要其他资源文件要用libexec.install配合包装脚本。热搜词里提到 “homebrew 取消 10.15 的支持”这是 Homebrew 官方在某个版本后不再支持 macOS Catalina 及更早系统。对 t3code 的影响是如果你的 formula 里用了新版本 Ruby 的语法老系统上的 Homebrew 可能解析不了。我的建议是 formula 尽量用保守的语法别用太新的特性兼容性优先。3.3 winget 分发Windows 侧的清单文件Windows 这边对应的是 winget。你需要提交一个manifest文件到 winget-pkgs 仓库包含版本、安装包地址、安装类型等信息。winget 的清单是 YAML 格式分三个文件版本清单、安装清单、locale 清单。安装清单里最关键的是InstallerType和InstallerSwitches。如果 t3code 的 Windows 安装包是 NSIS 类型的就写nsis如果是 MSI就写wix。静默安装参数也要配对否则用户装的时候会弹出一堆向导窗口。我一般用 NSIS 打包静默参数是/S配置如下Installers: - Architecture: x64 InstallerType: nsis InstallerUrl: https://example.com/t3code-setup.exe InstallerSha256: 校验值 InstallerSwitches: Silent: /S SilentWithProgress: /Swinget 的审核比较严格第一次提交可能要等几天而且 manifest 的 schema 版本要对上。我建议先用wingetcreate这个官方工具生成模板再手动改比从零写省事很多。4. 实操过程从零搭建 t3code 的完整流程4.1 环境准备与依赖安装动手之前先把环境理清楚。你需要 Node.js 18 以上版本pnpm 作为包管理器monorepo 场景下 pnpm 的 workspace 支持最省心以及各平台的打包工具链。macOS 上要装 Xcode Command Line ToolsWindows 上要装 Visual Studio Build Tools这些是编译原生模块的前提。初始化项目我用的是mkdir t3code cd t3code pnpm init pnpm add -D typescript electron electron-builder然后配置pnpm-workspace.yamlpackages: - packages/*接着分别创建 core、cli、desktop 三个子包。core 里写纯逻辑cli 里写命令入口desktop 里写 Electron 主进程和渲染进程。这个阶段别急着写业务代码先把三个包之间的引用关系跑通确认pnpm --filter cli build和pnpm --filter desktop dev都能正常工作。4.2 core 模块的接口设计core 是整个项目的根基它的接口设计决定了 CLI 和 Electron 好不好用。我的经验是core 只暴露纯函数和类不碰任何 I/O 副作用。比如读取配置这件事core 提供一个parseConfig(rawString)的纯函数至于这个字符串是从文件读的还是从网络来的交给调用方决定。这样做的好处是 core 的单元测试极其好写不需要 mock 文件系统。而且 CLI 和 Electron 可以各自决定配置来源CLI 从~/.t3code/config.json读Electron 从用户选择的文件读但解析逻辑是同一份。core 的导出我一般这样组织// packages/core/src/index.ts export { parseConfig, validateConfig } from ./config; export { runTask, TaskResult } from ./task; export { formatOutput } from ./format;注意别在 core 里import任何 Electron 或 CLI 相关的包一旦引入另一端的构建就会失败。这个约束要靠 lint 规则强制光靠自觉迟早会破防。4.3 CLI 命令注册与参数处理CLI 部分我用cac来注册命令因为它足够轻API 也直观。一个典型的子命令注册长这样import { cac } from cac; import { runTask } from t3code/core; const cli cac(t3code); cli .command(run input, 执行任务) .option(--format type, 输出格式, { default: json }) .action(async (input, options) { const result await runTask(input, options); console.log(result); }); cli.help(); cli.version(1.0.0); cli.parse();这里有个细节action里的异步错误一定要捕获否则 Node 会直接抛未处理的 Promise rejection用户看到的是一堆堆栈信息。我一般会在最外层包一层 try-catch把错误格式化成友好的提示。关于热搜词里的 “codex cli 命令哪些 /compact /model /resume”这其实是另一个工具的交互模式。t3code 如果要做类似的交互式命令可以用prompts或者inquirer来做选择菜单。但要注意交互式命令在 CI 环境里会卡住所以必须提供--no-interactive之类的开关让脚本化调用能绕过交互。4.4 Electron 主进程与 CLI 的通信Electron 主进程要调用 core 逻辑最直接的方式就是importcore 模块。但这里有个坑Electron 的主进程运行在 Node 环境而 core 如果是 ESM 格式可能会遇到模块解析问题。我的做法是把 core 编译成 CommonJS 和 ESM 双格式通过package.json的exports字段区分{ exports: { .: { import: ./dist/index.mjs, require: ./dist/index.cjs } } }这样 CLI 用 ESM 引入Electron 主进程用 CJS 引入各取所需。渲染进程如果需要调用 core不能直接 import必须通过 IPC 转发到主进程。这个限制是 Electron 的安全模型决定的别想着绕过。IPC 的通道命名我建议用命名空间:动作的格式比如task:run、config:load避免不同模块之间通道名冲突。主进程用ipcMain.handle注册渲染进程用ipcRenderer.invoke调用这套 Promise 风格的 API 比老的send/on好用太多。5. 常见问题与排查技巧实录5.1 安装与分发环节的典型故障Homebrew 安装失败是最高频的问题。我整理了一张速查表覆盖了大部分场景现象可能原因解决思路sha256 mismatch下载文件与 formula 记录不一致重新计算 sha256 并更新 formulacommand not foundbin 未正确链接检查bin.install路径安装卡住不动网络下载源慢换用国内镜像或自建 CDN老系统报语法错误formula 用了新 Ruby 特性降级语法兼容旧版winget 这边最常见的是 manifest schema 校验不通过。官方有个winget validate命令提交前一定要本地跑一遍。另外InstallerSha256的大小写敏感必须全小写这个坑我踩过一次排查了半天。5.2 Electron 运行时的疑难杂症白屏是 Electron 最经典的故障。排查顺序我一般是先看主进程有没有报错再看渲染进程的控制台最后检查loadURL或loadFile的路径对不对。开发模式下白屏八成是开发服务器没起来生产模式下白屏八成是loadFile的相对路径算错了。还有一个问题是打包后 core 模块找不到。这通常是因为electron-builder没有把 workspace 里的本地包打进去。解决办法是在 desktop 的package.json里把 core 声明为依赖并且在electron-builder配置里加上files: [node_modules/t3code/core/**]。monorepo 的软链接在打包时经常出问题必要时可以用pnpm deploy生成一个扁平化的产物再打包。5.3 CLI 与桌面端状态不同步如果用户同时在终端和桌面端操作配置可能会互相覆盖。我的做法是引入一个文件锁机制写配置前先获取锁写完释放。Node 里可以用proper-lockfile这个库简单可靠。另外配置文件里加一个lastModified时间戳读取时对比如果发现比自己内存里的新就重新加载。这样能避免大部分冲突。提示配置文件千万别用 JSON 直接覆盖写先写临时文件再 rename防止写入过程中断电导致文件损坏。这个技巧在桌面端尤其重要因为用户随时可能关掉应用。5.4 关于 codex cli 相关问题的借鉴热搜词里有一堆关于 codex cli 的问题比如 “model not found”、“没有可用的终端或文件读取工具”、“node 安装很慢”。这些问题虽然属于另一个工具但排查思路是通用的。model not found本质是配置里的模型标识和实际可用的对不上检查配置文件和运行时环境是否一致即可。安装慢是 npm 源的问题换源或者用 pnpm 的缓存能缓解。这些经验放到 t3code 的 CLI 上同样适用配置校验要做在启动阶段别等到执行到一半才报错。6. 我在这类项目上的一些实操心得做 t3code 这种双形态工具最大的体会是别追求一次做全。我见过太多项目一上来就想把 CLI、桌面端、自动更新、多平台分发全做完结果每个都半成品。我的建议是先做 CLI把 core 逻辑跑通确认功能有价值再加 Electron 外壳最后才搞 Homebrew 和 winget 的分发。每一步都能独立交付用户也能尽早用上。另一个心得是关于版本号管理。CLI、core、desktop 三个包的版本号最好保持一致用changesets这类工具统一管理。否则用户装到的 CLI 是 1.2.0桌面端是 1.1.0core 又是 1.3.0出了问题根本没法定位。发版的时候三个包一起发tag 打一个就行。最后说个分发上的小技巧。Homebrew 和 winget 的审核都有延迟如果你急着发版可以先用npm publish把 CLI 发出去让用户通过npx t3code或者pnpm dlx t3code先用上等包管理器审核通过再引导用户切换。这样既不阻塞发版节奏也不影响用户体验。我自己几个工具都是这么过渡的实测下来很顺。

相关新闻

pstack-claude:本地进程栈智能诊断实践

pstack-claude:本地进程栈智能诊断实践

1. 项目概述:pstack-claude 是什么,它解决的是哪类真实痛点?pstack-claude 这个名字乍看像一个工具组合词,但拆开来看,它其实指向一个非常具体、且在当前开发实践中高频出现的协作场景:用 pstack&#xff0…

2026/10/9 9:26:29 阅读更多 →
Modbus协议实战:帧结构、寄存器映射与PLC通讯排障指南

Modbus协议实战:帧结构、寄存器映射与PLC通讯排障指南

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

2026/10/9 9:26:29 阅读更多 →
手搓CCSwitch高定版:一键切换六大CodingPlan,把settings改到TaoToken

手搓CCSwitch高定版:一键切换六大CodingPlan,把settings改到TaoToken

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

2026/10/9 9:26:29 阅读更多 →

最新新闻

SpringBoot医疗管理系统毕设:核心代码与踩坑全解析

SpringBoot医疗管理系统毕设:核心代码与踩坑全解析

我用 SpringBoot 把医疗管理系统卷成了毕设模板,核心代码和踩坑都在这里每年到了毕业季,总有一批人卡在选题上:既要难度适中能独立完成,又不能太水让答辩老师一眼看穿,还得有实际业务场景可以讲故事。我的建议是&#…

2026/10/9 12:42:09 阅读更多 →
Java SpringBoot医疗管理系统毕业设计:模块设计、流程实现与避坑指南

Java SpringBoot医疗管理系统毕业设计:模块设计、流程实现与避坑指南

毕业设计选医疗管理系统,等于选了一个永远不会错的安全牌。我带毕设这些年经常和学生说,Java SpringBoot 这套组合下的医院综合管理平台,业务链条完整、老师一听就懂、工作量也撑得起一篇论文,而且 Web 版这个词听起来就比“XX管…

2026/10/9 12:42:09 阅读更多 →
UltralSO制作Linux启动盘的底层原理与工程实践

UltralSO制作Linux启动盘的底层原理与工程实践

1. 为什么现在还要亲手做Linux启动盘?——被低估的底层掌控力“UltralSO软碟通制作Linux系统盘”这个标题,乍看像十年前的老操作,但最近三个月,我在某高校开源实验室带学生做嵌入式开发实训时,连续遇到7个真实案例&…

2026/10/9 12:42:09 阅读更多 →
C#超市管理系统开发指南:WinForms+SQL Server实现进销存与库存管理

C#超市管理系统开发指南:WinForms+SQL Server实现进销存与库存管理

简介:基于C#开发的超市管理系统源码与数据库压缩包,面向超市管理者、C#初学者及毕业设计人员,提供一套完整的信息化解决方案。系统涵盖商品管理、采购管理、销售管理、会员管理、库存预警与报表生成等核心模块,配合SQL Server 200…

2026/10/9 12:42:09 阅读更多 →
jstat实战:从JVM内存模型到GC全过程解析

jstat实战:从JVM内存模型到GC全过程解析

线上排查Java服务内存问题时,我最先跑的命令几乎永远是 jps 加 jstat 。有一次同事盯着监控面板说老年代快满了,但又说不清对象究竟是怎么分配进去的,我让他 jstat -gc 连续采了十几秒,问题立刻缩小到“大对象直接晋升老年代…

2026/10/9 12:42:09 阅读更多 →
达梦云原生大数据平台在高校实训系统中的实时数据实践

达梦云原生大数据平台在高校实训系统中的实时数据实践

简介:本资源是一套基于达梦云原生大数据平台构建的校园智能实训系统完整源码,面向高校计算机、大数据、软件工程等专业师生,旨在解决传统实训中数据环境脱离真实生产、技术栈陈旧、教学与产业脱节等问题,支撑数据思维培养、全栈开…

2026/10/9 12:41:08 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/9 6:17:20 阅读更多 →