☰
LobeHub Desktop 第三方许可证合规:font-list 声明与系统字体枚举实现解析
2026/10/11 1:41:42 网站建设 项目流程
  • 人工智能
  • AI 应用
  • 大模型
  • AI Agent
  • 多智能体
  • 工具调用
  • 前端
  • 后端

【免费下载链接】lobehub

🤯 LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.

项目地址:https://gitcode.com/GitHub_Trending/lo/lobehub
点击查看免费下载

LobeHub 桌面端(apps/desktop)在 THIRD_PARTY_NOTICES.md 中集中声明了其所捆绑第三方组件的许可证。本文以该声明文件为核心,完整给出其中 font-list 的 MIT 许可文本与合规要点,并结合主进程控制器、IPC 服务层、前端 Hook 与单元测试的真实代码,讲清 font-list 在 LobeHub Desktop「系统字体枚举」这条调用链上的实际工作方式,帮助读者理解桌面端应用如何同时满足开源合规与功能实现两个目标。

声明文件声明了什么:font-list 与 MIT License

桌面客户端(Electron 应用)在打包分发时会把大量 npm 依赖一并带上,因此保留每个第三方组件的许可证文本是常见的合规实践。当前 apps/desktop/THIRD_PARTY_NOTICES.md 声明了唯一一个第三方组件:

  • 组件名:font-list
  • 版本:2.1.0
  • 许可证:MIT License
  • 版权声明:Copyright (c) 2017 oldj

许可证全文如下(原文档中的完整文本,一字未删):

MIT License Copyright (c) 2017 oldj Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

需要注意两个合规细节:其一,声明固定版本2.1.0,而依赖声明中是 caret 范围(见下一节),说明 NOTICE 文件通常以实际纳入构建/打包的版本为准进行记录;其二,MIT 协议要求「上述版权声明与许可声明须包含在软件的全部副本或实质部分中」,这正是该 NOTICE 文件存在的直接原因——它把字体枚举库的版权与许可文本随产品一并携带。

依赖声明:package.json 中的 font-list

在桌面端应用清单 apps/desktop/package.json 中可以看到该依赖的声明:

"font-list": "^2.1.0"

^2.1.0允许安装 2.x 范围内的补丁与小版本升级,而 NOTICE 文件记录的2.1.0则是声明基线。二者配合,保证了「实际用什么版本、许可证文本以哪个版本为准」是可追溯的。

主进程实现:SystemController 中的字体枚举

font-list 在仓库中的唯一使用点位于桌面端主进程的系统控制器 SystemCtr.ts。它对外暴露两个 IPC 方法,内部复用同一个私有方法:

@IpcMethod() async getSystemMonospaceFonts(): Promise<SystemFont[]> { return this.loadSystemFonts(true); // 仅等宽字体 } @IpcMethod() async getSystemFonts(): Promise<SystemFont[]> { return this.loadSystemFonts(false); // 全部字体 }

返回值类型是一个简单的label/value二元组(label为展示名,value为可渲染的字体族名),与渲染进程侧 electronSystemService 导出的SystemFont接口保持一致。

loadSystemFonts(SystemCtr.ts)的核心逻辑可以拆成五步:

  1. 惰性加载:使用import('font-list')动态导入并调用getFonts2(),避免把字体枚举库塞进主进程启动关键路径;
  2. 过滤与去重:遍历getFonts2()返回的字体记录,label取font.name.trim(),value取font.familyName.trim(),任意为空的记录直接丢弃;monospaceOnly为真时跳过非等宽字体(font.monospace标记);
  3. 按族名归一化去重:以label.toLocaleLowerCase()作为键存入Map,同一字体族(如Menlo与menlo)只保留首次出现的记录;
  4. 排序:[...families.values()]后按label.localeCompare字典序排序,保证设置界面下拉列表顺序稳定;
  5. Promise 级缓存:结果以'monospace'/'all'为键缓存在实例字段systemFontsPromises中。注意缓存的是Promise 而非数组——同一键的并发请求共享同一次系统枚举;若 Promise 进入 reject 分支,则先systemFontsPromises.delete(cacheKey)再抛出,保证下次调用会重试而不是永远拿到一个失败缓存。

IPC 桥与渲染进程服务层

控制器通过@IpcMethod()装饰器注册处理器,配合类上的groupName = 'system',最终形成system.getSystemMonospaceFonts这类通道名——这在单元测试直接以通道名调用时可以印证(见 SystemCtr.test.ts 中的invokeIpc('system.getSystemMonospaceFonts'))。

渲染进程侧的服务类 ElectronSystemService 提供了同名薄封装:

async getSystemMonospaceFonts(): Promise<SystemFont[]> { return this.ipc.system.getSystemMonospaceFonts(); } async getSystemFonts(): Promise<SystemFont[]> { return this.ipc.system.getSystemFonts(); }

前端消费:设置页字体下拉

设置界面通过 Hook useSystemFontOptions 消费上述 IPC 能力。该 Hook 的行为值得展开:

  • 根据入参monospaceOnly决定调用getSystemMonospaceFonts()还是getSystemFonts(),并维护isLoading/hasLoadError两个状态;
  • 选项列表的拼装顺序为:首项永远是应用默认字体哨兵值APPLICATION_DEFAULT_FONT('__application_default__'),其后是values中当前已选中但系统枚举不到的字体(用unavailableLabel生成「不可用」标签,防止用户既有配置因字体被卸载而丢失),最后才是系统字体全集;
  • 通过useEffect中的active标志处理组件卸载后的竞态,避免 setState 到已卸载组件。

这条链路解释了为什么 font-list 这类底层库值得出现在第三方声明里:它直接支撑了桌面端「按本机真实字体设置外观/等宽字体」这一可感知功能。

测试如何验证去重、排序与缓存

单元测试 SystemCtr.test.ts 首先对 font-list 做了整体 mock:

vi.mock('font-list', () => ({ getFonts2: fontListGetFonts2Mock, }));

getSystemMonospaceFonts的用例(L238-L259)用一组刻意构造的输入覆盖了前述全部关键行为:

输入记录验证点
{ familyName: 'Inter', monospace: false, ... }非等宽字体被monospaceOnly过滤
{ familyName: ' Menlo ', name: ' Menlo ' }与{ familyName: 'menlo', ... }两侧 trim + 大小写归一化后合并为一条
{ familyName: '"Courier New"', ... }保留带引号的 familyName 原样作为value
{ familyName: '', name: '' }空值记录被丢弃
连续两次invokeIpc('system.getSystemMonospaceFonts')结果一致且fontListGetFonts2Mock仅被调用一次(Promise 缓存生效)

断言的最终结果是有序的[{ label: 'Courier New', value: '"Courier New"' }, { label: 'Menlo', value: 'Menlo' }],同时用toHaveBeenCalledOnce()锁死了「缓存住、不重复枚举系统字体」的行为契约。

小结:一份 NOTICE 背后的完整证据链

回到 THIRD_PARTY_NOTICES.md 本身,它的内容虽只有一条 font-list 2.1.0 的 MIT 声明,但在仓库中能找到一条完整可验证的对应链:依赖声明(^2.1.0)→ 主进程枚举与缓存实现 → IPC 服务层 → 设置页字体下拉 Hook → 去重/排序/缓存单测。这为桌面端开源合规提供了一个典型样板:许可证文本、依赖版本与实际用法三者一一对应,任何一端的变更(升级 font-list 版本、更换字体枚举实现)都应当同步检查另外两端是否仍然一致。

  • 人工智能
  • AI 应用
  • 大模型
  • AI Agent
  • 多智能体
  • 工具调用
  • 前端
  • 后端

【免费下载链接】lobehub

🤯 LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.

项目地址:https://gitcode.com/GitHub_Trending/lo/lobehub
点击查看免费下载

相关推荐

上一篇:《仓颉编程快速上手》基础Collection类型:仓颉Array、ArrayList、HashMap完整教程
下一篇:MicYou Linux部署指南:PipeWire虚拟麦克风安装与配置全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询