当前位置: 首页 > article >正文

Dify插件调试效率提升300%:Chrome DevTools深度联动+本地热重载调试全链路揭秘

第一章Dify插件开发入门与核心架构解析Dify 插件机制是其扩展能力的核心支柱允许开发者以标准化方式接入外部服务、增强 LLM 应用的上下文感知与执行能力。插件基于 OpenAPI 3.0 规范定义接口契约并通过 Dify 平台统一注册、鉴权与编排实现低耦合、高内聚的集成体验。插件的基本结构一个合规插件由三部分组成OpenAPI 描述文件openapi.yaml、配置元数据plugin.json以及可选的前端 UI 组件。其中plugin.json必须包含名称、描述、图标 URL 和认证类型等字段。示例如下{ name: weather-plugin, description: Fetch real-time weather data by city name, icon: https://example.com/icon.png, auth_type: none, // 支持 none / api_key / oauth2 api_schema_url: ./openapi.yaml }核心架构分层Dify 插件系统采用四层架构设计各层职责明确接入层接收用户触发请求完成插件路由与参数注入协议层解析 OpenAPI Schema校验输入参数并生成标准化调用上下文执行层发起 HTTP 请求至插件后端服务支持重试、超时与错误映射响应层将原始 API 响应转换为 LLM 可理解的自然语言摘要或结构化数据片段本地开发调试流程使用 Dify CLI 工具可快速启动插件沙箱环境安装 CLInpm install -g difylabs/cli初始化插件项目dify plugin init my-weather-plugin启动本地服务dify plugin serve --port 8081在 Dify Web 控制台中选择「本地插件」并填写http://localhost:8081/plugin.json插件能力对比表能力项本地插件托管插件私有部署插件调试支持✅ 实时热重载❌ 需重新发布✅ 支持自建 registry认证方式仅支持无认证支持 API Key / OAuth2全认证类型支持第二章Chrome DevTools深度联动调试实战2.1 插件运行时上下文注入与DevTools Sources面板精准映射上下文注入核心机制Chrome 扩展通过content_scripts或chrome.scripting.executeScript注入脚本时需显式声明runAt: document_idle以确保 DOM 就绪且上下文可调试chrome.scripting.executeScript({ target: { tabId }, world: MAIN, // 关键使脚本在页面主世界运行与 Sources 面板源码一致 files: [injected.js] });world: MAIN是映射前提——若使用ISOLATED默认脚本将运行在隔离世界Sources 中无法定位其源码位置断点失效。Sources 面板映射关键配置配置项推荐值作用sourceMapstrue启用 sourcemap 支持实现压缩代码与原始 TS/JS 的双向跳转include[*.js, *.ts]确保 DevTools 加载所有相关源文件避免“No source found”提示2.2 利用Console API与Custom Event实现双向通信断点追踪核心机制通过console.trace()捕获调用栈结合CustomEvent在跨上下文如 iframe、Web Worker间广播断点位置实现双向可观测性。事件注册与触发示例const breakpointEvent new CustomEvent(debug:breakpoint, { detail: { id: bp-001, location: src/utils.js:42, timestamp: Date.now() } }); window.dispatchEvent(breakpointEvent); // 主线程触发该事件携带唯一 ID 与源码定位信息供监听器捕获并关联 console 输出detail.id用于去重与链路匹配location支持 IDE 跳转。关键优势对比方案跨上下文支持堆栈可追溯性console.log()❌✅仅当前上下文CustomEvent console.trace()✅✅事件触发时自动注入栈2.3 Network面板拦截与重放插件API请求模拟异常网络场景拦截与重放基础操作在 Chrome DevTools 的 Network 面板中右键任意请求可选择Block request URL或使用Replay XHR快速重发。重放时请求头、载荷与原始完全一致。模拟弱网与超时通过Throttling下拉菜单启用Slow 3G100ms RTT450kbps DL结合Network Conditions禁用缓存并勾选Offline模拟断连自定义响应篡改示例// 在 Overrides → Local Overrides 中映射 API 路径后编辑 mock 响应 { status: error, code: 503, message: Service Unavailable (simulated) }该 JSON 将在匹配/api/v1/sync请求时返回用于验证前端降级逻辑——如展示离线提示、启用本地缓存回退策略。常见异常响应对照表场景HTTP 状态码前端典型行为服务不可达0网络中断触发onerror跳转离线页网关超时504自动重试 ×2延时 1s2.4 Performance面板分析插件JS执行耗时瓶颈与内存泄漏定位识别长任务与强制同步布局在Performance录制中重点关注主线程上的“Long Task”50ms及黄色“Layout”块。强制同步布局常由读取 offsetTop 后立即修改 class 触发function badPattern() { const el document.getElementById(box); console.log(el.offsetTop); // 触发回流 el.className active; // 触发重排重绘 }该模式导致浏览器必须同步计算样式并布局阻塞渲染线程。应批量读写分离先读取所有布局信息再统一写入。内存泄漏典型模式检测全局变量意外引用 DOM 节点未清除的事件监听器尤其绑定到长期存活对象闭包中持有大型数据结构且作用域未释放Heap Snapshot对比关键指标指标健康阈值风险信号Detached DOM Trees010 节点且 retainers 指向全局JS Heap Size稳定波动 ≤15%连续3次GC后仍增长2.5 Application面板调试Service Worker生命周期与缓存策略验证生命周期关键阶段观察在 Chrome DevTools 的 **Application → Service Workers** 面板中可实时查看 installing、waiting、active 状态切换并点击「Skip Waiting」或「Update on reload」触发状态迁移。缓存策略验证代码// 注册时指定 scope 与 updateViaCache navigator.serviceWorker.register(/sw.js, { scope: /, updateViaCache: none // 强制每次检查新版本绕过 HTTP 缓存 });updateViaCache: none 确保浏览器忽略 sw.js 的 HTTP 缓存头使更新检测更可靠配合 Application 面板的「Update on reload」可精准复现 install → activate 流程。缓存存储内容核验缓存名称条目数典型用途static-v112CSS/JS/字体等静态资源dynamic-pages3HTML 路由页面含 fallback第三章本地热重载调试环境全栈搭建3.1 基于ViteReact的插件开发模板初始化与HMR配置调优快速初始化模板使用官方脚手架创建最小化插件工程npm create vitelatest my-plugin -- --template react该命令生成标准 React 项目结构剔除冗余依赖为插件化封装预留干净入口。HMR 精准热更新配置在vite.config.ts中启用模块级 HMRexport default defineConfig({ plugins: [react({ fastRefresh: true })], server: { hmr: { overlay: false, timeout: 3000 } } })fastRefresh启用 React 官方 Fast Refreshtimeout避免网络抖动导致 HMR 中断。关键参数对比参数默认值插件推荐值hmr.overlaytruefalse避免遮挡插件 UI 调试server.watch.depthundefined3适配多层 src/plugins 结构3.2 Dify本地后端Mock服务对接与动态插件注册机制实现Mock服务启动与接口契约对齐Dify后端通过mock-server.ts启动轻量HTTP服务自动加载/mocks/*.json定义的响应模板import { createMockServer } from dify/mock-core; const server createMockServer({ port: 5001, delay: 300, // 模拟网络延迟ms cors: true }); server.listen();该配置确保前端调用/v1/chat-messages等真实路径时返回符合OpenAPI Schema的结构化Mock数据避免因联调阻塞开发进度。插件动态注册核心流程插件以ESM模块形式注入注册器按优先级顺序解析并激活扫描plugins/**/index.ts入口文件校验plugin.manifest.json元信息完整性运行plugin.init()完成依赖绑定与路由挂载插件元信息规范字段类型说明idstring全局唯一标识符用于插件启停控制versionstring语义化版本触发热更新校验endpointsarray声明需代理的后端路径前缀3.3 WebSocket热更新通道构建从文件变更到插件实例实时刷新变更监听与事件分发使用 fsnotify 监控插件目录捕获.go或.yaml文件的写入与重命名事件watcher, _ : fsnotify.NewWatcher() watcher.Add(./plugins) for { select { case event : -watcher.Events: if event.Opfsnotify.Write fsnotify.Write || event.Opfsnotify.Rename fsnotify.Rename { broadcastUpdate(event.Name) // 触发WebSocket广播 } } }该逻辑确保仅对实质性变更响应避免临时文件如.swp干扰broadcastUpdate将路径映射为插件ID并推送到所有活跃连接。客户端热加载流程浏览器收到{type:reload,pluginId:auth-v2}消息卸载旧插件实例调用destroy()生命周期钩子动态import()新 JS 模块并初始化第四章全链路调试效能优化与工程化实践4.1 自定义Chrome扩展辅助调试插件状态快照与上下文导出核心能力设计通过 Chrome Extension API 捕获运行时关键状态支持一键导出当前 Tab 的扩展上下文快照包括 service worker 状态、content script 注入标记、storage 数据及 active tab 信息。快照导出实现chrome.runtime.sendMessage({ action: captureSnapshot }, (response) { const blob new Blob([JSON.stringify(response, null, 2)], { type: application/json }); chrome.downloads.download({ url: URL.createObjectURL(blob), filename: debug-snapshot-${Date.now()}.json }); });该逻辑向 background service worker 发起快照请求response包含storage.local.get()结果、chrome.runtime.getManifest()元数据及当前 Tab ID导出文件名含时间戳确保唯一性。导出字段对照表字段名来源API用途tabIdchrome.tabs.query({active: true})关联当前调试页面上下文storageDatachrome.storage.local.get()诊断持久化状态异常4.2 Source Map精准映射与TSX源码级断点调试配置指南Source Map核心配置项解析Webpack 中需启用 devtool: source-map 并确保 TSX 编译保留原始位置信息module.exports { devtool: source-map, // 生成独立 .map 文件支持浏览器精准定位 module: { rules: [{ test: /\.tsx?$/, use: { loader: ts-loader, options: { compilerOptions: { sourceMap: true } } // 必须开启 TS 层映射 } }] } };该配置使浏览器 DevTools 能将压缩后的 JS 行号反向映射至原始 TSX 文件实现行级断点。VS Code 调试器集成要点在.vscode/launch.json中设置sourceMaps: true确保outFiles指向构建产物目录如[./dist/**/*.js]TSX 文件路径必须与sourceRoot字段声明一致4.3 插件生命周期钩子埋点日志体系设计与DevTools Console智能过滤钩子埋点统一注入机制通过插件 SDK 在 onLoad、onReady、onUnload 等标准钩子中自动注入结构化日志调用export function injectLifecycleLogger(pluginId) { const originalOnLoad window[pluginId].onLoad; window[pluginId].onLoad function(...args) { console.log(%c[${pluginId}][LOAD], color:#2196F3;font-weight:bold, { timestamp: Date.now(), args }); return originalOnLoad?.apply(this, args); }; }该函数劫持原始钩子注入带插件标识、阶段标签与时间戳的可控日志console.log的 CSS 样式标记便于 DevTools 分组识别。Console 智能过滤策略支持正则匹配插件 ID如/^auth-.*$/按生命周期阶段标签过滤[LOAD]/[READY]联动 source map 显示真实插件文件路径4.4 CI/CD中集成调试能力自动化生成调试友好的生产构建包核心设计原则调试友好 ≠ 舍弃安全与性能。关键是在保留符号表、源码映射与可读性元数据的前提下严格剥离敏感调试逻辑如远程调试端口、内存转储触发器。构建配置示例Webpack SourceMapmodule.exports { mode: production, devtool: hidden-source-map, // 仅内嵌 source map URL不暴露原始源码 plugins: [ new CopyWebpackPlugin({ patterns: [{ from: dist/*.map, to: debug-artifacts/ }] // 单独归档 map 文件 }) ] };该配置生成带校验的 .map 文件并隔离存储避免污染生产部署包hidden-source-map 保证运行时无额外开销仅在错误上报或本地调试时按需加载。CI 流水线关键检查项构建产物中不含console.debug或debugger语句通过 ESLint CI 钩子拦截SourceMap 文件 SHA256 与主包哈希绑定并写入debug-manifest.json第五章插件性能压测与上线前最佳实践压测环境隔离与数据构造生产插件上线前必须在与线上同构的隔离环境K8s 1.26 Istio 1.21中执行压测。使用go-wrk模拟 500 并发、持续 5 分钟的请求流重点观测内存泄漏与 Goroutine 泄露func BenchmarkPluginHandler(b *testing.B) { b.ReportAllocs() for i : 0; i b.N; i { // 构造真实请求上下文含 JWT 解析与路由匹配开销 ctx : context.WithValue(context.Background(), trace_id, test-123) plugin.ServeHTTP(mockWriter{}, mockRequest{ctx: ctx}) } }关键指标基线阈值指标安全阈值告警阈值P99 响应延迟 120ms 200ms内存常驻增长 1MB/min 3MB/min灰度发布检查清单启用 Prometheus 自定义指标采集plugin_http_request_duration_seconds_bucket配置 Envoy 的局部限流策略每实例 QPS ≤ 300验证插件卸载后无残留 goroutine通过 pprof/goroutine?debug2 抓取快照比对真实案例API 网关插件内存溢出修复某 JWT 验证插件在压测中 12 分钟后 OOM定位发现 jwt.Parse() 后未调用 token.Valid() 导致解析缓存无限增长。修复后 P99 稳定在 87ms内存波动收敛至 ±0.3MB。

相关文章:

Dify插件调试效率提升300%:Chrome DevTools深度联动+本地热重载调试全链路揭秘

第一章:Dify插件开发入门与核心架构解析Dify 插件机制是其扩展能力的核心支柱,允许开发者以标准化方式接入外部服务、增强 LLM 应用的上下文感知与执行能力。插件基于 OpenAPI 3.0 规范定义接口契约,并通过 Dify 平台统一注册、鉴权与编排&am…...

从混乱到清晰:TOP课程Git学习资源链接优化全指南

从混乱到清晰:TOP课程Git学习资源链接优化全指南 【免费下载链接】curriculum The open curriculum for learning web development 项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum GitHub推荐项目精选(cu/curriculum)是…...

【Qt 应用开发】Qt 日志系统进阶:从 QDebug 到 QCritical 的实战配置与性能优化

1. Qt日志系统深度解析:从基础到实战 第一次接触Qt日志系统时,我也曾被各种输出宏搞得晕头转向。直到在项目中踩过几次坑后才明白,合理的日志配置能节省80%的调试时间。Qt提供了qDebug、qInfo、qWarning、qCritical四个级别的日志输出&#x…...

别再只会让电机转!用STM32和Proteus深度模拟28BYJ-48步进电机的加减速曲线与堵转检测

基于STM32的28BYJ-48步进电机高级控制:S形曲线与堵转检测实战 在嵌入式开发领域,步进电机控制常被视为入门级项目——接上驱动模块,写几行代码让电机转动似乎就大功告成。但当我们把场景切换到实际产品中,粗暴的启停控制和速度突变…...

别再死记硬背了!用生活中的例子帮你彻底搞懂HFSS边界条件(理想E/H、阻抗、集总RLC)

用厨房电器和家具秒懂HFSS边界条件:硬件工程师的生存指南 刚接触HFSS时,那些边界条件总让我想起第一次进高端厨房的体验——面对各种看不懂的按钮和术语,连最简单的煮鸡蛋都可能搞砸。就像电磁仿真中,选错一个边界条件&#xff0c…...

AvalancheGo网络通信:P2P协议和消息队列的实现原理

AvalancheGo网络通信:P2P协议和消息队列的实现原理 【免费下载链接】avalanchego Go implementation of an Avalanche node. 项目地址: https://gitcode.com/gh_mirrors/ava/avalanchego AvalancheGo是Avalanche节点的Go语言实现,其网络通信系统基…...

从设计到测试:聊聊DFT工程师如何在芯片里‘埋’下Scan-FF这根‘暗线’

从设计到测试:DFT工程师如何精准部署Scan-FF技术链 在芯片设计的世界里,可测试性设计(DFT)就像给复杂电路装上"透视镜",而Scan-FF技术则是这面镜子的核心光学组件。当一颗芯片包含数十亿晶体管时&#xff0c…...

告别电脑!用iOS快捷指令给你的手机浏览器装个“开发者模式”

用iOS快捷指令打造移动端网页调试神器:零代码实现元素修改与数据抓取 在咖啡馆等方案时突然发现网页文案有误?通勤路上需要紧急抓取竞品价格数据?传统移动端浏览器缺乏开发者工具总让人束手无策。其实你的iPhone里藏着一位"隐形开发者&q…...

从技术幕后到价值核心:金融思维如何重塑软件测试者的职业未来

一扇被悄然推开的门当代码的严谨逻辑与资本市场的瞬息万变相遇,会碰撞出怎样的火花?一个看似与技术圈层格格不入的趋势正在蔓延:那些站在技术金字塔尖的开发者,正将目光投向金融领域。这并非不务正业,而是一场深刻的价…...

手把手教你用Keil MDK5和STM32F103ZET6给LVGL v7.1.0安个家(附DMA加速技巧)

STM32F103ZET6实战:Keil MDK5环境下的LVGL v7.1.0移植与DMA加速全解析 当一块800480的LCD屏幕遇上仅有64KB RAM的STM32F103ZET6,图形界面开发似乎成了不可能的任务。这正是LVGL(Light and Versatile Graphics Library)展现魔力的…...

FPGA实战避坑:从一次复位信号不稳定说起,手把手教你实现异步复位同步释放

FPGA复位设计实战:从亚稳态陷阱到异步复位同步释放的完整解决方案 复位信号:FPGA设计的隐形守护者 在FPGA开发中,复位信号就像交响乐团的指挥棒,确保所有寄存器从已知状态开始协调工作。我曾在一个图像处理项目中遇到诡异现象&…...

AD9361官方例程里的Cache操作详解:为什么DMA传输后必须调用Xil_DCacheInvalidateRange?

AD9361高速数据流中的Cache一致性陷阱:从DMA传输异常看Zynq缓存机制 在基于Zynq SoC和AD9361的射频系统中,许多开发者都遇到过这样的诡异现象:PL端通过DMA将ADC采样数据准确写入DDR后,PS端CPU读取的却是一堆"过时"数据。…...

3步搭建你的AI化学助手:ChemCrow让复杂化学问题变简单

3步搭建你的AI化学助手:ChemCrow让复杂化学问题变简单 【免费下载链接】chemcrow-public Chemcrow 项目地址: https://gitcode.com/gh_mirrors/ch/chemcrow-public 还在为复杂的化学计算和分子分析烦恼吗?ChemCrow AI化学助手来了!这是…...

离散选择模型中的‘极值’秘密:为什么Gumbel分布是Logit模型的基石?

离散选择模型中的‘极值’秘密:为什么Gumbel分布是Logit模型的基石? 在交通规划中选择公交还是地铁?在市场营销中预测消费者会购买A品牌还是B品牌?这些看似简单的二选一问题背后,都隐藏着一个强大的统计学工具——离散…...

3种方法解锁赛博朋克2077存档修改:从新手到专家的完整指南

3种方法解锁赛博朋克2077存档修改:从新手到专家的完整指南 【免费下载链接】CyberpunkSaveEditor A tool to edit Cyberpunk 2077 sav.dat files 项目地址: https://gitcode.com/gh_mirrors/cy/CyberpunkSaveEditor 赛博朋克2077存档编辑器(Cyber…...

用Docker搞定Android 13源码环境:告别宿主机污染,实现一键编译

用Docker容器化Android 13源码编译环境:隔离与效率的终极实践 在Android系统开发领域,源码编译环境的搭建一直是个令人头疼的问题。传统方式直接在物理机或虚拟机上配置环境,不仅容易导致系统污染,还会因为依赖冲突而耗费大量调试…...

3大秘诀:如何用NHSE彻底改变你的动物森友会游戏体验

3大秘诀:如何用NHSE彻底改变你的动物森友会游戏体验 【免费下载链接】NHSE Animal Crossing: New Horizons save editor 项目地址: https://gitcode.com/gh_mirrors/nh/NHSE 你是否曾经在《集合啦!动物森友会》中花费数小时摇树、钓鱼、挖化石&am…...

Audio Slicer终极指南:5步掌握免费音频智能分段技术

Audio Slicer终极指南:5步掌握免费音频智能分段技术 【免费下载链接】audio-slicer A simple GUI application that slices audio with silence detection 项目地址: https://gitcode.com/gh_mirrors/aud/audio-slicer 你是否曾面对长达数小时的录音素材&…...

保姆级教程:在Qt6中用子线程处理多个QSerialPort,实现多设备同时通信

工业级多线程串口通信框架设计:Qt6高效管理多设备通信实战 在工业自动化、物联网网关等场景中,经常需要同时与多个串口设备(如传感器、PLC、模块等)进行稳定通信。传统单线程串口处理方式在面对多设备时往往力不从心,容…...

工业现场设备的监控系统(有完整资料)

资料查找方式:特纳斯电子(电子校园网):搜索下面编号即可编号:T1532310M设计简介:本设计是工业现场设备的监控系统,主要实现以下功能:通过温湿度传感器检测温湿度,湿度过高…...

QMCDecode:三步解锁QQ音乐加密格式,实现跨平台音乐自由

QMCDecode:三步解锁QQ音乐加密格式,实现跨平台音乐自由 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录&…...

5步掌握Whisper.cpp离线语音识别:从零到精通的实践手册

5步掌握Whisper.cpp离线语音识别:从零到精通的实践手册 【免费下载链接】whisper.cpp Port of OpenAIs Whisper model in C/C 项目地址: https://gitcode.com/GitHub_Trending/wh/whisper.cpp 在当今数据隐私日益重要的时代,云端语音识别服务面临…...

告别Ghost!用官方镜像给NVMe硬盘装Win11,驱动加载这一步很多人会错

从Ghost到原生安装:NVMe硬盘Win11系统部署全指南 当一块崭新的NVMe固态硬盘插入主板M.2插槽时,多数用户期待的是即插即用的流畅体验。然而在官方镜像安装界面中,那个令人困惑的"找不到驱动器"提示,往往成为技术小白与硬…...

视觉Transformer在姿态估计领域的范式革新:ViTPose技术深度解析

视觉Transformer在姿态估计领域的范式革新:ViTPose技术深度解析 【免费下载链接】ViTPose The official repo for [NeurIPS22] "ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation" and [TPAMI23] "ViTPose: Vision Transfor…...

避坑指南:辰华CHI软件宏命令(Macro Command)编写与调试的5个常见错误

辰华CHI宏命令实战避坑手册:5个高频错误解析与调试技巧 在电化学测试领域,辰华CHI软件的宏命令功能一直是科研人员的得力助手,但就像任何强大的工具一样,它也可能成为效率黑洞——当你在凌晨三点的实验室里,面对满屏红…...

告别复杂推导:用数学归纳法5步搞定Pinsker不等式的证明(思路拆解)

数学归纳法五步拆解Pinsker不等式:从基础引理到降维技巧的完整指南 第一次看到Pinsker不等式时,那个关于概率分布之间KL散度与平方距离的不等式关系让我既着迷又困惑。教科书上常见的证明往往依赖复杂的变分法或积分技巧,直到发现这个基于数学…...

OpCore Simplify:黑苹果EFI一键生成的终极指南

OpCore Simplify:黑苹果EFI一键生成的终极指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为复杂的OpenCore配置而烦恼吗&#xf…...

从气象云图到地形渲染:用Python Matplotlib的contourf函数实现数据可视化实战

从气象云图到地形渲染:用Python Matplotlib的contourf函数实现数据可视化实战 当气象学家需要展示台风路径上的温度分布,当地质工程师分析地震波传播的强度变化,或是当环境科学家研究污染物扩散范围时,他们面临的共同挑战是如何将…...

TouchGal 完全指南:5步掌握Galgame文化社区核心功能

TouchGal 完全指南:5步掌握Galgame文化社区核心功能 【免费下载链接】kun-touchgal-next TouchGAL是立足于分享快乐的一站式Galgame文化社区, 为Gal爱好者提供一片净土! 项目地址: https://gitcode.com/gh_mirrors/ku/kun-touchgal-next TouchGal是一个专为G…...

终极鼠标增强方案:Mac Mouse Fix让你的普通鼠标在macOS上超越苹果触控板

终极鼠标增强方案:Mac Mouse Fix让你的普通鼠标在macOS上超越苹果触控板 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix 还在为macO…...