Skip to content

feat: 支持 Windows、macOS 和 Linux 跨平台悬浮窗 - #1

Merged
jinghero merged 49 commits into
jinghero:mainfrom
Craun718:codex/refactor
Oct 16, 2026
Merged

jinghero merged 49 commits into
jinghero:mainfrom
Craun718:codex/refactor

Conversation

@Craun718

@Craun718 Craun718 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

此前程序依赖 Windows WPF,无法直接在 macOS 和 Linux 使用。本 PR 提取平台无关核心库,以 Avalonia 作为跨平台发布入口,并保留原有统计、托盘和报告导出功能。

主要变更

  • 提取日志扫描、设置持久化、报告导出和无界面快照到 CodexTpsFloat.Core,补充日志重写与单实例唤醒回归测试。
  • 新增 Avalonia 悬浮面板,支持时间窗口、模型筛选、今日分时、报告导出、自动刷新和登录启动;使用矢量按钮图标,调整分段选择样式并修复刷新周期恢复。
  • 适配 macOS/Linux 配置目录、登录启动和进程间单实例通信;处理无托盘环境、macOS 启动显示面板、隐藏 Dock 图标、面板拖动及透明菜单栏图标。
  • 新增跨平台构建脚本与 CI,发布 Windows x64、macOS ARM64/x64 和 Linux x64 便携包,包含快照冒烟检查和 SHA-256 校验文件。
  • 固定已修补的 Tmds.DBus.Protocol 版本,更新使用文档、隐私说明和第三方许可。

验证

  • 已通过:dotnet run --project tests/CoreTests.csproj -c Release --nologo,覆盖统计、增量日志、设置、导出、登录启动和单实例唤醒等回归场景。
  • 已通过:dotnet build src/App/CodexTpsFloat.App.csproj -c Release --nologo --no-restore(本地 macOS,0 错误)。
  • NuGet 漏洞数据源访问失败,产生 NU1900 警告;本次未完成在线漏洞审计。
  • Windows/Linux 运行及各平台便携发布验证待 GitHub Actions 执行。

Sourcery 总结

在保留现有监控和报告功能的同时,让 Codex TPS 浮动监视器能够作为便携式应用在 Windows、macOS 和 Linux 上运行。

新功能:

  • 新增适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 浮动面板,支持统计信息、系统托盘/菜单栏控件、筛选、刷新间隔、报告、开机自启动和单实例激活。
  • 为便携式安装提供无头快照生成和冒烟验证功能。

错误修复:

  • 在增量扫描期间可靠地检测日志文件是否被重写或替换,包括文件长度相同的替换情况,以及 Unix 文件系统的行为。
  • 保留并正确还原面板刷新设置,并处理无法使用系统托盘图标的环境。

改进:

  • 将日志扫描、设置、报告导出、快照生成、平台路径、开机自启动和单实例支持提取为可复用的核心库。
  • 支持平台专属的配置位置、登录启动机制、窗口行为和菜单栏展示。
  • 更新依赖项许可证,并固定已修补的 DBus 协议版本。

构建:

  • 新增 Windows 和 Unix 便携式构建脚本,目标平台包括 Windows x64、macOS ARM64/x64 和 Linux x64。

CI:

  • 新增核心测试和跨平台便携式构建的 CI 覆盖。
  • 扩展发布自动化流程,为所有支持的平台执行验证、构建、冒烟测试、校验和生成及制品发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私和平台专属打包相关文档。

测试:

  • 扩展回归测试覆盖范围,包括日志重写、设置持久化、报告导出、平台路径、登录启动和单实例唤醒行为。

杂项:

  • 添加跨平台 UI 和运行时依赖项的第三方声明及许可证文件。
Original summary in English

Sourcery 摘要

启用 Codex TPS 浮动监视器,使其成为可在 Windows、macOS 和 Linux 上运行的便携式应用,同时保留监控和报告功能。

新功能:

  • 添加适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 浮动监视器,支持统计信息、筛选、刷新控制、报告、系统托盘或菜单栏集成、登录时启动以及单实例激活。
  • 提供无头 JSON 快照,用于便携式应用验证和脚本处理。

错误修复:

  • 可靠检测日志文件被重写或替换的情况,包括替换前后文件长度相同以及 Unix 文件系统行为。
  • 正确保留和恢复刷新设置,并处理没有可用系统托盘图标的环境。

增强功能:

  • 将扫描、设置、报告、快照生成、平台路径、自动启动和单实例行为提取到可复用的核心库中。
  • 根据各受支持平台调整配置存储、启动行为、窗口呈现和菜单栏集成。
  • 锁定已修正的 DBus 协议依赖项,并添加第三方许可声明。

构建:

  • 添加适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的跨平台便携式构建脚本。

CI:

  • 添加回归测试和跨平台便携式构建验证,包括快照冒烟测试、校验和计算以及发布构件发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私和平台打包文档。

测试:

  • 扩展回归测试覆盖范围,包括日志重写、设置持久化、报告导出、平台路径、登录时启动和单实例唤醒行为。

杂项:

  • 添加跨平台 UI 和运行时依赖项的许可证文件。
Original summary in English

Sourcery 生成的摘要

启用 Codex TPS 悬浮监控器,使其能够作为便携式应用在 Windows、macOS 和 Linux 上运行,同时保留监控与报告功能。

新功能:

  • 新增适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 悬浮监控器,支持筛选、刷新控制、报告、系统托盘或菜单栏集成、登录时启动,以及单实例激活。
  • 提供无头 JSON 快照,用于便携式应用冒烟测试和脚本化使用。

错误修复:

  • 可靠检测被重写或替换的日志文件,包括文件长度未变化的替换情况,同时确保增量扫描行为正确。
  • 保留并恢复刷新设置,并在系统托盘集成不可用时确保面板仍可使用。

改进:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取为可复用的核心库。
  • 根据各受支持平台调整配置存储、窗口行为、启动集成和菜单栏呈现方式。
  • 固定已修复的 DBus 协议依赖版本,并为跨平台运行时组件添加许可声明。

构建:

  • 新增适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的便携式构建脚本。

CI:

  • 新增回归测试和跨平台 CI 构建,支持便携式构建产物验证、快照冒烟测试、校验和生成以及自动发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私和平台打包相关文档。

测试:

  • 扩展回归测试覆盖范围,包括日志重写、设置持久化、报告导出、平台路径、登录时启动和单实例唤醒行为。

杂项:

  • 为基于 Avalonia 的跨平台运行时添加第三方许可证文件。
Original summary in English

Sourcery 摘要

启用 Codex TPS 浮动监视器,使其成为可在 Windows、macOS 和 Linux 上运行的便携式应用,同时保留监控和报告功能。

新功能:

  • 新增基于 Avalonia 的浮动监视器,支持 Windows、macOS 和 Linux,并针对各平台提供适配的系统托盘或菜单栏行为、筛选、刷新控件、报告、登录时启动和单实例激活功能。
  • 提供无头 JSON 快照,以便验证便携式应用并用于脚本处理。

错误修复:

  • 在增量扫描期间可靠检测被重写或替换的日志文件,包括长度相同的替换文件以及 Unix 文件系统行为。
  • 保留刷新设置,并确保在系统托盘集成不可用时面板仍可使用。

增强功能:

  • 将扫描、设置、报告导出、快照、平台路径、自动启动和单实例行为提取为可复用的核心库。
  • 使用特定于平台的配置位置、启动集成、窗口行为和菜单栏呈现方式。

构建:

  • 为 Windows x64、macOS ARM64/x64 和 Linux x64 添加便携式构建脚本。

CI:

  • 添加跨平台回归测试、便携式构建、快照冒烟测试、校验和生成以及多平台发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、打包和第三方许可证文档。

测试:

  • 扩展回归测试覆盖范围,包括日志重写、设置持久化、报告导出、平台路径、登录时启动和单实例唤醒行为。

杂项:

  • 固定经过修补的 DBus 协议依赖版本,并添加跨平台 UI 和运行时组件的第三方许可证声明。
Original summary in English

Sourcery 生成的摘要

启用 Codex TPS Float,使其能够作为可移植的监控应用在 Windows、macOS 和 Linux 上运行,同时保留其统计和报告功能。

新功能:

  • 新增跨平台 Avalonia 浮动监控器,支持 Windows、macOS 和 Linux,并提供适配各平台的系统托盘或菜单栏集成、筛选、刷新控件、报告、登录时启动以及单实例激活功能。
  • 提供无界面 JSON 快照,便于验证可移植应用和编写脚本。

Bug 修复:

  • 在增量扫描期间可靠地检测日志文件是否被重写或替换,包括文件长度相同的替换情况;并在多次启动之间保留刷新设置。

增强:

  • 将扫描、设置、报告导出、快照、平台路径、自动启动和单实例行为提取到可复用的核心库中。
  • 根据各受支持平台调整配置存储、启动集成、窗口行为和菜单栏显示。
  • 固定使用经过修补的 DBus 协议依赖,并为跨平台运行时附带第三方许可证声明。

构建:

  • 新增可移植构建脚本,用于生成适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的应用。

CI:

  • 新增回归测试和跨平台 CI 构建,包括可移植制品验证、快照冒烟测试、校验和生成以及自动发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、平台打包和许可证相关文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动以及单实例唤醒行为。
Original summary in English

Sourcery 总结

使监控工具能够作为便携式应用在 Windows、macOS 和 Linux 上运行,同时保留现有的统计和报告功能。

新功能:

  • 新增跨平台 Avalonia 应用,支持 Windows、macOS 和 Linux,包含监控、筛选、刷新控件、报告、登录时启动、系统托盘/菜单栏集成以及单实例激活。
  • 提供无界面 JSON 快照,用于脚本处理和便携版程序包的冒烟验证。

错误修复:

  • 在增量扫描期间可靠地检测被重写或替换的日志文件,包括长度相同的替换文件;并在重启后保留刷新设置。

增强功能:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取到可复用的核心库中。
  • 针对各支持平台调整配置存储、启动行为、窗口显示方式以及系统托盘/菜单栏行为。
  • 固定修正后的 DBus 协议依赖项,并为跨平台运行时组件附带许可证。

构建:

  • 新增便携版构建脚本,支持 Windows x64、macOS ARM64/x64 和 Linux x64。

CI:

  • 新增回归测试以及跨平台 CI 和发布工作流,涵盖构建、快照冒烟测试、校验和、构件和发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、打包和第三方许可证文档。

测试:

  • 扩大回归测试覆盖范围,包括日志文件替换、设置持久化、报告导出、平台路径、登录时启动和单实例激活。
Original summary in English

Sourcery 生成的摘要

让监控工具能够作为便携式应用在 Windows、macOS 和 Linux 上运行,同时保留其统计和报告功能。

新功能:

  • 新增适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 浮动监控窗口,同时保留筛选、刷新控件、报告、登录时启动、系统托盘或菜单栏集成以及单实例激活功能。
  • 提供无界面的 JSON 快照,供脚本使用并用于验证便携式软件包。

错误修复:

  • 在增量扫描期间可靠检测已重写或替换的日志文件,包括长度相同的替换文件;并在系统托盘集成不可用时保留可用功能。

增强:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取为可复用的跨平台核心组件。
  • 根据各支持平台调整配置存储、启动集成、窗口行为和菜单栏呈现方式。
  • 更新依赖项,并为跨平台 UI 和运行时组件附上许可声明。

构建:

  • 新增适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的便携式构建脚本。

CI:

  • 新增回归测试以及跨平台 CI 和发布工作流,涵盖构建、快照冒烟测试、校验和、制品和发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、平台打包和第三方许可相关文档。

测试:

  • 扩大回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动和单实例激活。
Original summary in English

Sourcery 摘要

使 Codex TPS 浮动监视器能够作为便携式应用程序在 Windows、macOS 和 Linux 上运行,同时保留其监控与报告功能。

新功能:

  • 新增适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 浮动监视器,支持筛选、刷新控制、报告、登录时启动,以及适配各平台的系统托盘或菜单栏集成。
  • 提供无头 JSON 快照,便于脚本使用和便携式应用冒烟测试。

错误修复:

  • 在增量扫描期间可靠检测被重写或替换的日志文件,包括长度相同的替换文件和 Unix 文件系统场景。
  • 跨启动保留面板设置,并确保系统托盘集成不可用时应用仍可正常使用。

增强功能:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取为可复用的核心库。
  • 针对各个受支持的平台调整配置存储、窗口行为、启动集成和单实例激活方式。
  • 固定已修复的 DBus 协议依赖项,并附带跨平台运行时组件的许可证。

构建:

  • 新增适用于 Windows x64、macOS ARM64/x64 和 Linux x64 应用程序的便携式构建脚本。

CI:

  • 新增回归测试以及跨平台 CI 和发布工作流,涵盖构建、快照冒烟测试、校验和、构建产物和多平台发布。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、平台打包和许可相关文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动和单实例激活。

杂项:

  • 为新增的 UI 和运行时依赖项添加第三方许可声明及随附的许可证文本。
Original summary in English

Sourcery 摘要

启用监控工具,使其能够作为便携式应用在 Windows、macOS 和 Linux 上运行,同时保留统计和报告功能。

新功能:

  • 添加基于 Avalonia 的悬浮监控面板,支持 Windows、macOS 和 Linux,并提供筛选、刷新控件、报告、登录时启动、系统托盘或菜单栏集成以及单实例激活功能。
  • 提供无头模式的 JSON 快照,用于脚本处理和便携式程序包冒烟验证。

错误修复:

  • 在增量扫描期间可靠地检测日志是否被重写或替换,包括文件长度相同的替换情况以及 Unix 文件系统场景。
  • 在多次启动之间保留面板偏好设置,并确保在系统托盘集成不可用时应用仍可正常使用。

增强:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取到可复用的跨平台核心库中。
  • 根据各受支持平台调整配置存储、窗口行为、启动集成和菜单栏显示方式。
  • 固定已修复的 DBus 协议依赖版本,并为跨平台运行时组件附上许可证。

构建:

  • 添加适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的便携式构建脚本。

CI:

  • 添加回归测试和跨平台 CI 与发布工作流,涵盖构建、冒烟测试、校验和、构建产物及发布。

文档:

  • 更新使用说明、配置、快照、构建、发布、隐私、平台程序包和第三方许可证文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动和单实例激活。
Original summary in English

Sourcery 汇总

使监控工具能够作为便携式跨平台应用在 Windows、macOS 和 Linux 上运行,同时保留其统计和报告功能。

新功能:

  • 新增基于 Avalonia 的悬浮监控面板,支持 Windows、macOS 和 Linux,并提供适配各平台的系统托盘或菜单栏集成、筛选、刷新控制、报告、登录时启动和单实例激活功能。
  • 提供无界面 JSON 快照,供脚本使用并用于验证便携式软件包。

错误修复:

  • 在增量扫描期间可靠地检测日志文件是否被重写或替换,包括文件长度相同的替换情况和 Unix 文件系统场景。
  • 保存并恢复刷新设置;在系统托盘集成不可用时,确保面板仍可正常使用。

改进:

  • 将扫描、设置、报告导出、快照、平台路径、自动启动和单实例行为提取到可复用的跨平台核心库中。
  • 根据各受支持平台调整配置存储、启动集成、窗口行为和菜单栏展示方式。
  • 固定已修复的 DBus 协议依赖版本,并为新增的跨平台运行时组件附上许可证。

构建:

  • 新增便携式构建脚本,支持 Windows x64、macOS ARM64/x64 和 Linux x64 自包含软件包。

CI:

  • 新增回归测试以及跨平台 CI 和发布工作流,涵盖构建、便携式软件包冒烟测试、校验和、构建产物和发布。

文档:

  • 更新使用说明、配置、快照、构建、发布、隐私、平台软件包和第三方许可证文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动和单实例唤醒行为。
Original summary in English

Sourcery 摘要

让监控应用能够作为可移植的跨平台 Avalonia 应用在 Windows、macOS 和 Linux 上运行,同时保留统计和报告功能。

新功能:

  • 新增 Avalonia 浮动监控窗口,可在 Windows、macOS 和 Linux 上运行,并提供适配各平台的系统托盘或菜单栏集成、筛选、刷新控制、报告、开机自启动和单实例激活功能。
  • 提供无界面 JSON 快照,供脚本使用和可移植软件包冒烟测试。

错误修复:

  • 在增量扫描期间可靠检测日志被重写或替换的情况,包括文件长度不变的替换,以及 Unix 文件系统中的相关情况。
  • 保留面板刷新设置,并确保在系统托盘集成不可用时应用仍可使用。

改进:

  • 将扫描、设置、报告、快照、平台路径、开机自启动和单实例行为提取为可复用的跨平台核心组件。

构建:

  • 新增适用于 Windows x64、macOS ARM64/x64 和 Linux x64 的可移植构建脚本。

CI:

  • 新增跨平台回归测试、可移植构建、快照冒烟测试、校验和计算,以及多平台发布流程。

文档:

  • 更新使用说明、配置、快照、构建、发布、隐私、平台打包和第三方许可相关文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、开机自启动、单实例激活和 Avalonia UI 行为。

杂项:

  • 为新增的跨平台 UI 和运行时依赖添加第三方许可声明,并固定已修补的 DBus 协议依赖版本。
Original summary in English

Sourcery 总结

让监控应用能够作为便携式跨平台 Avalonia 应用在 Windows、macOS 和 Linux 上运行,同时保留现有的统计和报告功能。

新功能:

  • 添加适用于 Windows、macOS 和 Linux 的跨平台 Avalonia 浮动监控窗口,支持筛选、刷新控制、报告、登录时启动、系统托盘或菜单栏集成,以及单实例激活。
  • 提供无界面 JSON 快照,便于验证便携式应用和编写脚本。

错误修复:

  • 在增量扫描期间可靠地检测被重写或替换的日志,包括长度相同的替换情况和 Unix 文件系统场景。
  • 保留并恢复面板设置;在系统托盘集成不可用时,仍确保应用可正常使用。

改进:

  • 将扫描、设置、报告、快照、平台路径、自动启动和单实例行为提取为可复用的核心组件。
  • 根据各个支持的平台调整配置存储、窗口行为、启动集成和菜单栏呈现方式。
  • 锁定已修补的 DBus 协议依赖,并为新增的跨平台运行时组件附上许可证。

构建:

  • 添加便携式构建脚本,用于构建 Windows x64、macOS ARM64/x64 和 Linux x64 应用。

CI:

  • 添加跨平台回归测试、便携式构建、快照冒烟测试、校验和,以及自动化的多平台发布流程。

文档:

  • 更新使用、配置、快照、构建、发布、隐私、平台打包和第三方许可证文档。

测试:

  • 扩展回归测试覆盖范围,包括日志替换、设置持久化、报告导出、平台路径、登录时启动、单实例激活和 Avalonia UI 行为。
Original summary in English

Summary by Sourcery

Enable the monitoring application to run as a portable cross-platform Avalonia app on Windows, macOS, and Linux while retaining its existing statistics and reporting capabilities.

New Features:

  • Add a cross-platform Avalonia floating monitor for Windows, macOS, and Linux with filtering, refresh controls, reporting, login startup, tray or menu-bar integration, and single-instance activation.
  • Provide headless JSON snapshots for portable application validation and scripting.

Bug Fixes:

  • Detect rewritten or replaced logs reliably during incremental scanning, including same-length replacements and Unix filesystem cases.
  • Preserve and restore panel settings while keeping the application usable when tray integration is unavailable.

Enhancements:

  • Extract scanning, settings, reporting, snapshots, platform paths, autostart, and single-instance behavior into reusable core components.
  • Adapt configuration storage, window behavior, startup integration, and menu-bar presentation to each supported platform.
  • Lock the patched DBus protocol dependency and include licenses for new cross-platform runtime components.

Build:

  • Add portable build scripts for Windows x64, macOS ARM64/x64, and Linux x64 applications.

CI:

  • Add cross-platform regression testing, portable builds, snapshot smoke tests, checksums, and automated multi-platform release publishing.

Documentation:

  • Update usage, configuration, snapshot, build, release, privacy, platform packaging, and third-party license documentation.

Tests:

  • Expand regression coverage for log replacement, settings persistence, report export, platform paths, login startup, single-instance activation, and Avalonia UI behavior.

Use consistently sized PathIcon controls and refresh the dependency lock files for the macOS release build.
Reference version 0.21.3 explicitly and update the lock file to resolve GHSA-xrw6-gwf8-vvr9 and eliminate NU1903.
@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

审查者指南

本 PR 将原有 Windows WPF 监视器拆分为跨平台核心库,并以 Avalonia 提供 Windows、macOS 和 Linux 悬浮面板;同时加入平台适配的配置、自动启动和单实例通信,实现多架构便携构建、快照冒烟验证及自动发布,并扩展日志重写和核心回归测试覆盖。

单实例面板激活时序图

sequenceDiagram
    participant Launcher as Second launch
    participant Instance as SingleInstance
    participant Existing as Existing App
    participant Panel as MainWindow
    Launcher->>Instance: Create
    Instance-->>Launcher: IsFirstInstance false
    Launcher->>Instance: SignalExistingInstance
    Instance->>Existing: Wake request
    Existing->>Instance: ShowExistingPanel
    Instance->>Panel: ShowPanel
    Panel-->>Instance: Acknowledgment
    Instance-->>Launcher: Return success
Loading

跨平台便携版快照验证时序图

sequenceDiagram
    participant CI as GitHub Actions
    participant Build as Portable build script
    participant App as CodexTpsFloat
    participant Snapshot as SnapshotWriter
    participant Scanner as UsageScanner
    CI->>Build: publish runtime
    Build-->>CI: portable artifact
    CI->>App: --snapshot-json
    App->>Snapshot: Write
    Snapshot->>Scanner: Refresh
    Scanner-->>Snapshot: UsageSnapshot
    Snapshot-->>App: JSON file and exit code
    App-->>CI: Smoke-test result
Loading

增量日志重写检测流程图

flowchart TD
    Refresh[UsageScanner.Refresh] --> Cursor[Load file cursor]
    Cursor --> Match[TailMatches]
    Match -->|matches| Append[ReadCompleteLines]
    Match -->|mismatch or truncation| Reset[Clear cursors and usage]
    Reset --> Rebuild[Refresh from rewritten file]
    Append --> Snapshot[Updated UsageSnapshot]
Loading

文件变更

变更 详情 文件
将日志扫描、设置、报告、快照、平台路径、自动启动和单实例通信提取为可复用的跨平台核心层,同时修复增量扫描对日志重写/替换的识别。
  • 新增跨平台配置目录、原子设置持久化、报告导出、无界面快照和登录启动实现。
  • 通过文件尾部字节校验识别 Unix/Windows 上的截断、替换及等长重写,并清理过期统计状态。
  • 使用 Windows 命名同步对象及 Unix 锁文件/请求确认文件实现单实例唤醒。
  • 保留 WPF 对核心库的调用并迁移原有快照、设置和报告逻辑。
src/Core/CodexTpsFloat.Core.csproj
src/Core/Autostart.cs
src/Core/PanelSettings.cs
src/Core/PlatformPaths.cs
src/Core/ReportExporter.cs
src/Core/SettingsStore.cs
src/Core/SingleInstance.cs
src/Core/SnapshotWriter.cs
src/Core/UsageScanner.cs
src/CodexTpsFloat.csproj
src/UsageScanner.cs
src/MainWindow.xaml.cs
src/Program.cs
新增基于 Avalonia 的跨平台悬浮面板和应用启动入口,覆盖桌面交互、托盘/菜单栏、配置恢复和无头模式。
  • 实现统计面板、时间窗口与模型筛选、今日分时、报告导出、自动刷新、置顶、拖动和截图。
  • 按平台选择便携或用户配置目录,并适配 Windows 托盘、macOS 菜单栏/Dock 行为及无托盘 Linux 环境。
  • 新增跨平台命令行解析、单实例激活和快照模式入口;固定刷新周期与面板状态的恢复逻辑。
  • 添加 Avalonia、SkiaSharp、DBus 等 UI/运行时依赖及锁定文件。
src/App/App.axaml
src/App/App.axaml.cs
src/App/CodexTpsFloat.App.csproj
src/App/MainWindow.axaml
src/App/MainWindow.axaml.cs
src/App/Program.cs
src/App/app.manifest
src/App/packages.lock.json
Directory.Build.props
建立 Windows、macOS 和 Linux 的便携构建、CI 验证及多平台发布流程。
  • 新增 PowerShell/Bash 发布脚本,生成 Windows x64、macOS ARM64/x64 和 Linux x64 自包含单文件包。
  • CI 矩阵覆盖核心回归测试、Windows 旧版托盘生命周期测试和各平台构建产物上传。
  • 发布工作流增加版本校验、跨平台打包、SHA-256 校验文件、解压快照冒烟测试和统一 GitHub Release 发布。
.github/workflows/ci.yml
.github/workflows/release.yml
build.ps1
build.sh
更新跨平台使用、隐私、构建发布和第三方依赖许可文档。
  • 说明各平台下载包、启动方式、配置位置、签名限制和平台特有托盘/菜单栏行为。
  • 补充 Avalonia 及其运行时依赖的许可证声明和随包许可文件。
README.md
PRIVACY.md
THIRD-PARTY-NOTICES.md
licenses/ANGLE-BSD.txt
licenses/Avalonia-MIT.txt
licenses/Inter-OFL-1.1.txt
licenses/MicroCom.Runtime-MIT.txt
licenses/SkiaSharp-HarfBuzzSharp-MIT.txt
licenses/SkiaSharp-HarfBuzzSharp-THIRD-PARTY-NOTICES.txt
licenses/Tmds.DBus.Protocol-MIT.txt
扩展回归测试,验证核心行为和跨平台运行时集成。
  • 覆盖等长日志重写、设置归一化与原子保存、Markdown/JSON 报告、平台路径和登录启动条目。
  • 通过子进程测试单实例抢占、唤醒确认、超时清理及 Unix 锁错误处理。
tests/CoreTests.csproj
tests/Program.cs

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review。
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论创建 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以在审查评论下回复 @sourcery-ai issue,根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,随时重新生成标题。
  • 生成拉取请求摘要: 在拉取请求正文中你希望生成摘要的位置写入 @sourcery-ai summary,即可随时在指定位置生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,随时重新生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已处理完所有评论且不想再看到它们,此功能会很有用。
  • 撤销所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可撤销所有现有的 Sourcery 审查。如果你想从头开始进行新的审查,此功能尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义使用体验

访问你的控制面板以便:

  • 启用或禁用审查功能,例如由 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

本 PR 将原有 Windows WPF 监视器拆分为跨平台核心库,并以 Avalonia 提供 Windows、macOS 和 Linux 悬浮面板;同时加入平台适配的配置、自动启动和单实例通信,实现多架构便携构建、快照冒烟验证及自动发布,并扩展日志重写和核心回归测试覆盖。

Sequence diagram for single-instance panel activation

sequenceDiagram
    participant Launcher as Second launch
    participant Instance as SingleInstance
    participant Existing as Existing App
    participant Panel as MainWindow
    Launcher->>Instance: Create
    Instance-->>Launcher: IsFirstInstance false
    Launcher->>Instance: SignalExistingInstance
    Instance->>Existing: Wake request
    Existing->>Instance: ShowExistingPanel
    Instance->>Panel: ShowPanel
    Panel-->>Instance: Acknowledgment
    Instance-->>Launcher: Return success
Loading

Sequence diagram for cross-platform portable snapshot validation

sequenceDiagram
    participant CI as GitHub Actions
    participant Build as Portable build script
    participant App as CodexTpsFloat
    participant Snapshot as SnapshotWriter
    participant Scanner as UsageScanner
    CI->>Build: publish runtime
    Build-->>CI: portable artifact
    CI->>App: --snapshot-json
    App->>Snapshot: Write
    Snapshot->>Scanner: Refresh
    Scanner-->>Snapshot: UsageSnapshot
    Snapshot-->>App: JSON file and exit code
    App-->>CI: Smoke-test result
Loading

Flow diagram for incremental log rewrite detection

flowchart TD
    Refresh[UsageScanner.Refresh] --> Cursor[Load file cursor]
    Cursor --> Match[TailMatches]
    Match -->|matches| Append[ReadCompleteLines]
    Match -->|mismatch or truncation| Reset[Clear cursors and usage]
    Reset --> Rebuild[Refresh from rewritten file]
    Append --> Snapshot[Updated UsageSnapshot]
Loading

File-Level Changes

Change Details Files
将日志扫描、设置、报告、快照、平台路径、自动启动和单实例通信提取为可复用的跨平台核心层,同时修复增量扫描对日志重写/替换的识别。
  • 新增跨平台配置目录、原子设置持久化、报告导出、无界面快照和登录启动实现。
  • 通过文件尾部字节校验识别 Unix/Windows 上的截断、替换及等长重写,并清理过期统计状态。
  • 使用 Windows 命名同步对象及 Unix 锁文件/请求确认文件实现单实例唤醒。
  • 保留 WPF 对核心库的调用并迁移原有快照、设置和报告逻辑。
src/Core/CodexTpsFloat.Core.csproj
src/Core/Autostart.cs
src/Core/PanelSettings.cs
src/Core/PlatformPaths.cs
src/Core/ReportExporter.cs
src/Core/SettingsStore.cs
src/Core/SingleInstance.cs
src/Core/SnapshotWriter.cs
src/Core/UsageScanner.cs
src/CodexTpsFloat.csproj
src/UsageScanner.cs
src/MainWindow.xaml.cs
src/Program.cs
新增基于 Avalonia 的跨平台悬浮面板和应用启动入口,覆盖桌面交互、托盘/菜单栏、配置恢复和无头模式。
  • 实现统计面板、时间窗口与模型筛选、今日分时、报告导出、自动刷新、置顶、拖动和截图。
  • 按平台选择便携或用户配置目录,并适配 Windows 托盘、macOS 菜单栏/Dock 行为及无托盘 Linux 环境。
  • 新增跨平台命令行解析、单实例激活和快照模式入口;固定刷新周期与面板状态的恢复逻辑。
  • 添加 Avalonia、SkiaSharp、DBus 等 UI/运行时依赖及锁定文件。
src/App/App.axaml
src/App/App.axaml.cs
src/App/CodexTpsFloat.App.csproj
src/App/MainWindow.axaml
src/App/MainWindow.axaml.cs
src/App/Program.cs
src/App/app.manifest
src/App/packages.lock.json
Directory.Build.props
建立 Windows、macOS 和 Linux 的便携构建、CI 验证及多平台发布流程。
  • 新增 PowerShell/Bash 发布脚本,生成 Windows x64、macOS ARM64/x64 和 Linux x64 自包含单文件包。
  • CI 矩阵覆盖核心回归测试、Windows 旧版托盘生命周期测试和各平台构建产物上传。
  • 发布工作流增加版本校验、跨平台打包、SHA-256 校验文件、解压快照冒烟测试和统一 GitHub Release 发布。
.github/workflows/ci.yml
.github/workflows/release.yml
build.ps1
build.sh
更新跨平台使用、隐私、构建发布和第三方依赖许可文档。
  • 说明各平台下载包、启动方式、配置位置、签名限制和平台特有托盘/菜单栏行为。
  • 补充 Avalonia 及其运行时依赖的许可证声明和随包许可文件。
README.md
PRIVACY.md
THIRD-PARTY-NOTICES.md
licenses/ANGLE-BSD.txt
licenses/Avalonia-MIT.txt
licenses/Inter-OFL-1.1.txt
licenses/MicroCom.Runtime-MIT.txt
licenses/SkiaSharp-HarfBuzzSharp-MIT.txt
licenses/SkiaSharp-HarfBuzzSharp-THIRD-PARTY-NOTICES.txt
licenses/Tmds.DBus.Protocol-MIT.txt
扩展回归测试,验证核心行为和跨平台运行时集成。
  • 覆盖等长日志重写、设置归一化与原子保存、Markdown/JSON 报告、平台路径和登录启动条目。
  • 通过子进程测试单实例抢占、唤醒确认、超时清理及 Unix 锁错误处理。
tests/CoreTests.csproj
tests/Program.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你好——我发现了 14 个问题

给 AI 代理的提示
请处理此次代码审查中的评论:

## 各条评论

### 评论 1
<location path="src/Core/Autostart.cs" line_range="110" />
<code_context>
+                [Desktop Entry]
+                Type=Application
+                Name=Codex TPS
+                Exec="{executable}"
+                Terminal=false
+                X-GNOME-Autostart-enabled=true
</code_context>
<issue_to_address>
**登录时启动失败**

如果已安装的可执行文件路径包含桌面条目保留字符(例如引号、反斜杠、百分号或换行符),`LinuxAutostart.SetEnabled` 会将未转义的路径写入 `.desktop` 文件的 `Exec` 值,导致桌面解析器错误解析命令;如果路径中包含精心构造的引号和换行符,还可能在登录时执行注入的命令。

写入之前,请按照桌面条目 `Exec` 语法对可执行文件路径进行编码。
</issue_to_address>

### 评论 2
<location path="src/App/App.axaml.cs" line_range="41" />
<code_context>
+                await window.CaptureAndCloseAsync(Options.CapturePath);
+            // Avalonia can create a tray icon on macOS without macOS displaying it,
+            // so there is no reliable way to detect an unusable menu bar item.
+            else if (OperatingSystem.IsMacOS() || !window.HasTray)
+                window.ShowPanel();
+        };
</code_context>
<issue_to_address>
**面板无法访问**

如果 Linux 桌面没有可用的状态通知托盘宿主,但 Avalonia 仍创建了托盘对象,`HasTray` 会报告存在 `TrayIcon` 对象,因此 `App` 会跳过 `ShowPanel()`,尽管实际上没有显示托盘图标;用户没有可见的方式打开或退出应用。

托盘实际上不可用时,请提供可见的面板作为后备入口。

另请查看 `src/App/MainWindow.axaml.cs:41-42`、`src/App/MainWindow.axaml.cs:51`、`src/App/MainWindow.axaml.cs:62`。
</issue_to_address>

### 评论 3
<location path="src/Core/SingleInstance.cs" line_range="62" />
<code_context>
+        var mutex = new Mutex(true, scopedName, out bool first);
+        try
+        {
+            var wake = new EventWaitHandle(false, EventResetMode.AutoReset, scopedName + ".Show");
+            RegisteredWaitHandle? registration = null;
+            if (first && showExisting != null)
</code_context>
<issue_to_address>
**旧版面板仍保持隐藏**

用户在旧版本仍在运行时启动此 Windows 版本,`CreateWindows` 会共享旧互斥体名称,但发送信号到 `Local\CodexTpsFloat.Panel.v1.Show`,而旧版应用监听的是 `Local\CodexTpsFloat.Show.v1`;新进程随后作为次实例退出,旧面板也不会显示。

请保持唤醒事件名称与旧版 Windows 应用兼容,或以其他方式确保旧实例收到显示请求。
</issue_to_address>

### 评论 4
<location path="src/App/Program.cs" line_range="10" />
<code_context>
+
+public static class Program
+{
+    public static int Main(string[] args)
+    {
+        var options = CommandLineOptions.Parse(args);
</code_context>
<issue_to_address>
**Windows 选择器无法打开**

Windows 用户打开文件夹选择器或报告保存对话框时,`Program.Main` 会在默认的 MTA 线程上启动 Avalonia,因此基于 Windows COM 的选择器无法打开,用户也就无法选择 Codex 目录或导出报告。

请在 STA 线程上启动 Windows UI 线程。
</issue_to_address>

### 评论 5
<location path="src/App/Program.cs" line_range="20" />
<code_context>
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**捕获进程覆盖较新的设置**

当捕获任务与普通应用会话同时运行,且普通会话在捕获进程读取设置后更改了设置时,即使已有另一个实例,`--capture` 进程仍会运行,并加载自己的一份设置副本。如果普通应用在捕获进程关闭前保存了更改,捕获窗口的关闭处理程序会将过时的设置副本写回,覆盖用户较新的设置。

请勿从仅用于捕获的窗口持久化设置;或者通过某种机制串行化写入,并拒绝过时的设置副本。

另请查看 `src/App/MainWindow.axaml.cs:491`、`src/Core/SettingsStore.cs:39-40`。
</issue_to_address>

### 评论 6
<location path="src/App/Program.cs" line_range="19-20" />
<code_context>
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**唤醒请求丢失**

如果第二个进程在 `App.ShowExistingPanel` 尚未赋值前向第一个实例发送信号,`SingleInstance.Create` 可能会在回调仍为 null 时处理掉唤醒请求;第二个进程退出时并未打开第一个面板,因此面板仍保持隐藏。

请保留唤醒请求,直到 `ShowExistingPanel` 就绪后再分发。

另请查看 `src/App/App.axaml.cs:31-32`、`src/Core/SingleInstance.cs:79-83`。
</issue_to_address>

### 评论 7
<location path="src/App/Program.cs" line_range="16" />
<code_context>
+        if (options == null) return 2;
+
+        if (options.SnapshotPath != null)
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
</code_context>
<issue_to_address>
**快照错误绕过 CLI 错误处理**

如果无法创建或写入快照目标,`Program.Main` 会在 `try` 块之前调用 `SnapshotWriter.Write`,导致文件系统异常绕过文档规定的快照错误处理路径,而不是返回退出代码 1。

请通过与其他启动失败相同的错误处理路径处理快照写入异常。

另请查看 `src/Core/SnapshotWriter.cs:22`、`src/App/Program.cs:17`、`src/App/Program.cs:24`。
</issue_to_address>

### 评论 8
<location path="src/Core/SingleInstance.cs" line_range="96" />
<code_context>
+                    FileShare.None);
+                owns = true;
+            }
+            catch (IOException)
+            {
+            }
</code_context>
<issue_to_address>
**Unix 启动后退出且没有显示面板**

如果打开 Unix 锁文件时发生了与其他进程持有该文件无关的 I/O 错误,`CreateUnix` 会将所有 `IOException` 都视为另一个实例持有锁的证据。因此,`Program` 会向假定的所有者发送信号并成功退出,即使实际上没有所有者;用户既看不到面板,也收不到错误提示。

请区分正常的锁竞争和其他锁文件 I/O 错误,并报告后者。

另请查看 `src/Core/SingleInstance.cs:171`、`src/App/Program.cs:22-23`。
</issue_to_address>

### 评论 9
<location path="src/Core/SingleInstance.cs" line_range="172" />
<code_context>
+namespace CodexTpsFloat.App;
+
+public partial class App : Application
+{
+    public static CommandLineOptions Options { get; set; } = new(null, null, null);
+    public static SingleInstance? SingleInstance { get; set; }
</code_context>
<issue_to_address>
**第二次启动未能唤醒应用,且没有提示**

如果写入 Unix 唤醒文件或以原子方式替换该文件失败,`SignalExistingInstance` 会吞掉文件写入和替换错误,而 `Program.Main` 随即返回成功;第二次启动看起来成功了,但运行中的面板并未显示。

请返回或传播信号发送结果,并在无法传递唤醒请求时让第二次启动报告失败或使用备用方案。
</issue_to_address>

### 评论 10
<location path="src/App/Program.cs" line_range="18-20" />
<code_context>
+        if (options.SnapshotPath != null)
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**第二次启动请求悄无声息地丢失**

如果第二次启动在第一个实例初始化或关闭期间发出信号,或者 Unix 唤醒文件 I/O 失败,所有者可能会在 `App.ShowExistingPanel` 赋值前处理掉 Unix 唤醒请求、在初始化期间删除请求,或在关闭期间取消监听器;`SignalExistingInstance` 也会忽略 Unix 写入失败。随后 `Program` 会返回成功,却没有打开面板,用户的启动操作因此毫无效果。

请保留唤醒请求,直到 UI 回调就绪,并使用带确认、可重试的交接机制,确保没有实例处理请求时不能报告成功。

另请查看 `src/App/App.axaml.cs:32`、`src/Core/SingleInstance.cs:106`、`src/Core/SingleInstance.cs:171`、`src/Core/SingleInstance.cs:184`。
</issue_to_address>

### 评论 11
<location path="src/App/MainWindow.axaml.cs" line_range="277-280" />
<code_context>
+    // macOS three-finger drag and ordinary mouse drag both end up here, so the whole
+    // panel is a drag surface except for controls that need the press themselves.
+    static bool HasInteractiveAncestor(Visual source) =>
+        source.FindAncestorOfType<Button>() != null
+        || source.FindAncestorOfType<TextBox>() != null
+        || source.FindAncestorOfType<ComboBox>() != null
+        || source.FindAncestorOfType<DataGrid>() != null;
+
+    async void RefreshClick(object? sender, RoutedEventArgs e) => await RefreshAsync();
</code_context>
<issue_to_address>
**点击控件会开始拖动**

用户点击时长选项或设置复选框时,`HasInteractiveAncestor` 会排除按钮、文本框、组合框和数据网格,但没有排除单选按钮或复选框;`DragPanel` 会调用 `BeginMoveDrag`,因此这些控件无法可靠地响应操作。

请将单选按钮和复选框排除在面板拖动区域之外。
</issue_to_address>

### 评论 12
<location path="src/App/MainWindow.axaml.cs" line_range="485-489" />
<code_context>
+        if (closing) return;
+        closing = true;
+        timer.Stop();
+        if (double.IsFinite(Position.X) && double.IsFinite(Position.Y))
+        {
+            double scale = Screens.ScreenFromWindow(this)?.Scaling ?? 1;
+            settings.X = Position.X / scale;
+            settings.Y = Position.Y / scale;
+        }
+        SaveSettings();
</code_context>
<issue_to_address>
**隐藏状态下退出会重置面板位置**

用户在全新启动 Windows 或 Linux 后,若没有先显示面板就从托盘退出,`DisposeWindow` 只要发现 `Position` 是有限值就会持久化该位置,即使最初隐藏的窗口从未打开过也是如此;窗口默认位置 `(0, 0)` 会覆盖已保存的坐标,导致下次启动时面板出现在屏幕左上角。

只有在窗口至少打开并定位过一次之后,才持久化面板坐标。
</issue_to_address>

### 评论 13
<location path="src/App/MainWindow.axaml.cs" line_range="382" />
<code_context>
+
+    async void ChooseHomeClick(object? sender, RoutedEventArgs e)
+    {
+        if (refreshing) return;
+        var folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+        {
</code_context>
<issue_to_address>
**所选文件夹显示的是过时数据**

文件夹选择器打开后、返回前,如果定时器启动了刷新,选择器打开期间就可能开始一次刷新。随后 `ChooseHomeClick` 会替换 `scanner` 和 `requestClocks`,但它调用的 `RefreshAsync` 因 `refreshing` 仍为 true 而直接返回;正在执行的任务则会发布旧文件夹的快照,导致所选文件夹的数据不会显示,直到下一次定时器触发。

请为新选定的文件夹排队刷新,并丢弃扫描器或主目录与当前选择不匹配的结果。

另请查看 `src/App/MainWindow.axaml.cs:172`、`src/App/MainWindow.axaml.cs:397-400`。
</issue_to_address>

### 评论 14
<location path=".github/workflows/ci.yml" line_range="66" />
<code_context>
+
+      - name: Run hidden tray lifecycle tests (Windows only)
+        if: runner.os == 'Windows'
+        run: dotnet run --project tests/TrayLifecycleTests.csproj -c Release --nologo -- --never-show
+
+      - name: Build portable application
</code_context>
<issue_to_address>
**托盘测试覆盖的是旧版应用**

Windows CI 或发布验证成功时,工作流会运行 `tests/TrayLifecycleTests.csproj`;该测试引用 `src/CodexTpsFloat.csproj` 并测试 WPF `MainWindow`,而不是新的 Avalonia 发布入口。因此,即使实际发布应用的托盘行为存在问题,所宣传的托盘生命周期检查仍可能通过。

请添加能够启动并验证 Avalonia 应用的生命周期测试,或者将现有步骤标注为仅适用于旧版 WPF,并避免将其视为对发布应用的验证。

另请查看 `.github/workflows/release.yml:73`。
</issue_to_address>

Sourcery 评估

需要人工审查。 目前有 14 个问题需要先处理;新跨平台 UI 或打包中的缺陷通常属于常规运行时或发布问题,可以按常规方式修复。如果用户启用了登录时启动,应用会写入可能在代码回滚后仍然存在的操作系统自动启动条目;此影响范围有限,可通过关闭该设置或删除条目来移除。

阻塞性问题:src/Core/Autostart.cs:110、src/App/App.axaml.cs:41、src/Core/SingleInstance.cs:62、src/App/Program.cs:10、src/App/Program.cs:20,以及另外 9 项


Sourcery 对开源项目免费——如果您喜欢我们的审查,欢迎分享 ✨
Original comment in English

Hey - I've found 14 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/Core/Autostart.cs" line_range="110" />
<code_context>
+                [Desktop Entry]
+                Type=Application
+                Name=Codex TPS
+                Exec="{executable}"
+                Terminal=false
+                X-GNOME-Autostart-enabled=true
</code_context>
<issue_to_address>
**Login startup launches incorrectly**

When the installed executable path contains desktop-entry-reserved characters such as quotes, backslashes, percent signs, or newlines, `LinuxAutostart.SetEnabled` inserts the path unescaped into the `.desktop` `Exec` value, so the desktop parser misreads the command or, for crafted quotes and newlines, launches an injected command at login.

Encode the executable path according to the desktop-entry `Exec` grammar before writing it.
</issue_to_address>

### Comment 2
<location path="src/App/App.axaml.cs" line_range="41" />
<code_context>
+                await window.CaptureAndCloseAsync(Options.CapturePath);
+            // Avalonia can create a tray icon on macOS without macOS displaying it,
+            // so there is no reliable way to detect an unusable menu bar item.
+            else if (OperatingSystem.IsMacOS() || !window.HasTray)
+                window.ShowPanel();
+        };
</code_context>
<issue_to_address>
**Panel becomes inaccessible**

When a Linux desktop has no usable status-notifier host but Avalonia still creates the tray object, `HasTray` reports that a `TrayIcon` object exists, so `App` skips `ShowPanel()` even though no tray icon is displayed; the user has no visible way to open or exit the app.

Provide a visible panel fallback when the tray is not actually available.

Also at `src/App/MainWindow.axaml.cs:41-42`, `src/App/MainWindow.axaml.cs:51`, `src/App/MainWindow.axaml.cs:62`.
</issue_to_address>

### Comment 3
<location path="src/Core/SingleInstance.cs" line_range="62" />
<code_context>
+        var mutex = new Mutex(true, scopedName, out bool first);
+        try
+        {
+            var wake = new EventWaitHandle(false, EventResetMode.AutoReset, scopedName + ".Show");
+            RegisteredWaitHandle? registration = null;
+            if (first && showExisting != null)
</code_context>
<issue_to_address>
**Older panel stays hidden**

When a user launches this Windows version while the previous version is still running, `CreateWindows` shares the old mutex name but signals `Local\CodexTpsFloat.Panel.v1.Show`, while the older app listens on `Local\CodexTpsFloat.Show.v1`; the new process exits as a secondary instance and the old panel is not shown.

Keep the wake event name compatible with the previous Windows version or otherwise ensure the older instance receives the show request.
</issue_to_address>

### Comment 4
<location path="src/App/Program.cs" line_range="10" />
<code_context>
+
+public static class Program
+{
+    public static int Main(string[] args)
+    {
+        var options = CommandLineOptions.Parse(args);
</code_context>
<issue_to_address>
**Windows pickers fail to open**

When a Windows user opens the folder picker or report save dialog, `Program.Main` starts Avalonia on the default MTA thread, so the Windows COM-based pickers fail to open and users cannot choose a Codex directory or export a report.

Start the Windows UI thread in STA.
</issue_to_address>

### Comment 5
<location path="src/App/Program.cs" line_range="20" />
<code_context>
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**Capture overwrites newer settings**

When a capture run overlaps a normal app session and the normal session changes settings after capture loads them, a `--capture` process is allowed to run despite another instance and loads its own settings copy. If the regular app saves changed settings before capture closes, the capture window’s closing handler saves its stale copy over them, losing the user’s newer settings.

Do not persist settings from capture-only windows, or serialize writes with a mechanism that rejects stale settings copies.

Also at `src/App/MainWindow.axaml.cs:491`, `src/Core/SettingsStore.cs:39-40`.
</issue_to_address>

### Comment 6
<location path="src/App/Program.cs" line_range="19-20" />
<code_context>
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**Wake request is lost**

When a second process signals the first instance before `App.ShowExistingPanel` is assigned, `SingleInstance.Create` can consume the wake request while the callback is still null; the second process exits without opening the first panel, which remains hidden.

Retain wake requests until `ShowExistingPanel` is ready, then dispatch them.

Also at `src/App/App.axaml.cs:31-32`, `src/Core/SingleInstance.cs:79-83`.
</issue_to_address>

### Comment 7
<location path="src/App/Program.cs" line_range="16" />
<code_context>
+        if (options == null) return 2;
+
+        if (options.SnapshotPath != null)
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
</code_context>
<issue_to_address>
**Snapshot errors bypass CLI handling**

When the snapshot destination cannot be created or written, `Program.Main` calls `SnapshotWriter.Write` before its `try` block, so a filesystem exception escapes the documented snapshot error path instead of returning exit code 1.

Handle snapshot-writing exceptions through the same error path as other startup failures.

Also at `src/Core/SnapshotWriter.cs:22`, `src/App/Program.cs:17`, `src/App/Program.cs:24`.
</issue_to_address>

### Comment 8
<location path="src/Core/SingleInstance.cs" line_range="96" />
<code_context>
+                    FileShare.None);
+                owns = true;
+            }
+            catch (IOException)
+            {
+            }
</code_context>
<issue_to_address>
**Unix launch exits without a panel**

When opening the Unix lock file fails for an I/O reason unrelated to another process holding it, `CreateUnix` treats every `IOException` as proof that another instance owns the lock, so `Program` signals the presumed owner and exits successfully even when no owner exists; the user gets no panel or error.

Distinguish normal lock contention from other lock-file I/O failures and report the latter.

Also at `src/Core/SingleInstance.cs:171`, `src/App/Program.cs:22-23`.
</issue_to_address>

### Comment 9
<location path="src/Core/SingleInstance.cs" line_range="172" />
<code_context>
+namespace CodexTpsFloat.App;
+
+public partial class App : Application
+{
+    public static CommandLineOptions Options { get; set; } = new(null, null, null);
+    public static SingleInstance? SingleInstance { get; set; }
</code_context>
<issue_to_address>
**Second launch silently fails to wake**

When writing or atomically replacing the Unix wake file fails, `SignalExistingInstance` swallows file-write and replacement errors, while `Program.Main` returns success immediately afterward; the second launch appears successful but the running panel is not shown.

Return or propagate a signaling result and make the second launch report failure or use a fallback when the wake request cannot be delivered.
</issue_to_address>

### Comment 10
<location path="src/App/Program.cs" line_range="18-20" />
<code_context>
+        if (options.SnapshotPath != null)
+            return SnapshotWriter.Write(options.SnapshotPath, options.CodexHome);
+
+        using var singleInstance = SingleInstance.Create(
+            "CodexTpsFloat", () => App.ShowExistingPanel?.Invoke());
+        if (!singleInstance.IsFirstInstance && options.CapturePath == null)
+        {
+            singleInstance.SignalExistingInstance();
</code_context>
<issue_to_address>
**Second launch is silently lost**

When a second launch signals while the first instance is initializing or shutting down, or Unix wake-file I/O fails, the owner can consume a Unix wake request before `App.ShowExistingPanel` is assigned, delete a request while initializing, or cancel its listener during shutdown; `SignalExistingInstance` also suppresses Unix write failures. `Program` then returns success without opening a panel, so the user’s launch does nothing.

Keep wake requests pending until the UI callback is ready, and use an acknowledged/retryable handoff that cannot report success when no instance handled the request.

Also at `src/App/App.axaml.cs:32`, `src/Core/SingleInstance.cs:106`, `src/Core/SingleInstance.cs:171`, `src/Core/SingleInstance.cs:184`.
</issue_to_address>

### Comment 11
<location path="src/App/MainWindow.axaml.cs" line_range="277-280" />
<code_context>
+    // macOS three-finger drag and ordinary mouse drag both end up here, so the whole
+    // panel is a drag surface except for controls that need the press themselves.
+    static bool HasInteractiveAncestor(Visual source) =>
+        source.FindAncestorOfType<Button>() != null
+        || source.FindAncestorOfType<TextBox>() != null
+        || source.FindAncestorOfType<ComboBox>() != null
+        || source.FindAncestorOfType<DataGrid>() != null;
+
+    async void RefreshClick(object? sender, RoutedEventArgs e) => await RefreshAsync();
</code_context>
<issue_to_address>
**Controls start dragging**

When a user clicks a duration option or settings checkbox, `HasInteractiveAncestor` excludes buttons, text boxes, combo boxes, and data grids, but not radio buttons or checkboxes; `DragPanel` calls `BeginMoveDrag` instead of reliably activating those controls.

Exclude radio buttons and checkboxes from the panel drag surface.
</issue_to_address>

### Comment 12
<location path="src/App/MainWindow.axaml.cs" line_range="485-489" />
<code_context>
+        if (closing) return;
+        closing = true;
+        timer.Stop();
+        if (double.IsFinite(Position.X) && double.IsFinite(Position.Y))
+        {
+            double scale = Screens.ScreenFromWindow(this)?.Scaling ?? 1;
+            settings.X = Position.X / scale;
+            settings.Y = Position.Y / scale;
+        }
+        SaveSettings();
</code_context>
<issue_to_address>
**Hidden exit resets panel position**

When the user exits a fresh Windows or Linux launch from the tray without showing the panel first, `DisposeWindow` persists `Position` whenever it is finite, even if the initially hidden window was never opened; its default `(0, 0)` overwrites the saved coordinates, so the panel appears at the screen’s top-left on the next launch.

Persist the panel coordinates only after the window has been opened and positioned at least once.
</issue_to_address>

### Comment 13
<location path="src/App/MainWindow.axaml.cs" line_range="382" />
<code_context>
+
+    async void ChooseHomeClick(object? sender, RoutedEventArgs e)
+    {
+        if (refreshing) return;
+        var folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+        {
</code_context>
<issue_to_address>
**Selected folder shows stale data**

When the periodic timer starts a refresh after the folder picker opens but before it returns, the timer can start a refresh while the folder picker is open. `ChooseHomeClick` then replaces `scanner` and `requestClocks`, but its `RefreshAsync` call returns because `refreshing` is still true; the in-flight task publishes its old-folder snapshot, leaving the selected folder’s data unshown until a later timer tick.

Queue a refresh for the newly selected folder and discard results whose scanner/home no longer matches the current selection.

Also at `src/App/MainWindow.axaml.cs:172`, `src/App/MainWindow.axaml.cs:397-400`.
</issue_to_address>

### Comment 14
<location path=".github/workflows/ci.yml" line_range="66" />
<code_context>
+
+      - name: Run hidden tray lifecycle tests (Windows only)
+        if: runner.os == 'Windows'
+        run: dotnet run --project tests/TrayLifecycleTests.csproj -c Release --nologo -- --never-show
+
+      - name: Build portable application
</code_context>
<issue_to_address>
**Tray tests exercise the legacy app**

When windows CI or release validation succeeds, the workflow runs `tests/TrayLifecycleTests.csproj`, which references `src/CodexTpsFloat.csproj` and tests WPF `MainWindow`; it does not exercise the new Avalonia release entry point, so the advertised tray lifecycle check passes even if the shipped app’s tray behavior is broken.

Add lifecycle coverage that launches and verifies the Avalonia app, or label the existing step as legacy WPF-only and avoid treating it as validation of the release app.

Also at `.github/workflows/release.yml:73`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 14 findings to address first, and a bug in the new cross-platform UI or packaging would usually be an ordinary runtime or release issue that can be fixed normally. If a user enables login startup, the app writes an OS autostart entry that can outlive a code revert; it is bounded and can be removed by disabling the setting or deleting the entry.

Blocking findings: src/Core/Autostart.cs:110, src/App/App.axaml.cs:41, src/Core/SingleInstance.cs:62, src/App/Program.cs:10, src/App/Program.cs:20, and 9 more


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/Core/Autostart.cs Outdated
Comment thread src/App/App.axaml.cs
Comment thread src/Core/SingleInstance.cs Outdated
Comment thread src/App/Program.cs
Comment thread src/App/Program.cs Outdated
Comment thread src/App/Program.cs Outdated
Comment thread src/App/MainWindow.axaml.cs Outdated
Comment thread src/App/MainWindow.axaml.cs Outdated
Comment thread src/App/MainWindow.axaml.cs
Comment thread .github/workflows/ci.yml
@Craun718

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嘿——我发现了 2 个问题

面向 AI 智能体的提示
请处理此次代码审查中的评论:

## 单独评论

### 评论 1
<location path="src/Core/Autostart.cs" line_range="98-99" />
<code_context>
+    // Exec quoting is decoded after desktop-entry string escapes.
+    static string EncodeExecutable(string path)
+    {
+        if (path.Contains('=') || path.Contains('\0'))
+            throw new ArgumentException("登录启动路径不能包含等号或空字符。");
+        var quoted = new StringBuilder();
+        foreach (char c in path)
</code_context>
<issue_to_address>
**登录启动会拒绝有效路径**

当 Linux 可执行文件路径中包含等号时,`EncodeExecutable` 会拒绝 `=`,尽管它在 desktop-entry 的 `Exec` 值中是有效字符。因此,`SetEnabled` 会返回错误,用户无法通过该安装路径启用登录启动。

在对 desktop-entry 的 `Exec` 值进行编码时,允许可执行文件路径包含 `=`。
</issue_to_address>

### 评论 2
<location path="src/App/MainWindow.axaml.cs" line_range="428-431" />
<code_context>
+        var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
+        {
+            Title = "导出报告",
+            SuggestedFileName = $"Codex-TPS-{date:yyyy-MM-dd}.md",
+            DefaultExtension = "md",
+            ShowOverwritePrompt = true,
+            FileTypeChoices = new[]
+            {
+                new FilePickerFileType("中文报告") { Patterns = new[] { "*.md" } },
</code_context>
<issue_to_address>
**JSON 导出可能会写入 Markdown**

当用户选择 JSON 文件类型,但文件选择器仍保留建议的 `.md` 文件名时,`ReportExporter.Write` 会根据返回路径的扩展名选择格式,因此它会将 Markdown 写入 `.md` 文件,而不是生成用户要求的 JSON 报告。

在调用 `ReportExporter.Write` 之前,确保所选文件类型决定返回文件名的扩展名。

另请查看 `src/Core/ReportExporter.cs:35-36`。
</issue_to_address>

Sourcery 评估

需要人工审查。 请先处理 2 个发现的问题;此外,Avalonia 移植中的缺陷可能会导致面板无法启动,或在某个平台上无法正常工作;通常可以通过回退更改来修复此类问题。如果启用了登录启动,回退后可能仍会留下格式错误的 LaunchAgent 或 desktop entry,但影响范围有限,可以通过禁用登录启动或删除该条目来清理。

阻塞性发现:src/Core/Autostart.cs:99、src/App/MainWindow.axaml.cs:431


Sourcery 对开源项目免费——如果您喜欢我们的审查结果,欢迎分享 ✨
Original comment in English

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/Core/Autostart.cs" line_range="98-99" />
<code_context>
+    // Exec quoting is decoded after desktop-entry string escapes.
+    static string EncodeExecutable(string path)
+    {
+        if (path.Contains('=') || path.Contains('\0'))
+            throw new ArgumentException("登录启动路径不能包含等号或空字符。");
+        var quoted = new StringBuilder();
+        foreach (char c in path)
</code_context>
<issue_to_address>
**Login startup rejects valid paths**

When the Linux executable path contains an equals sign, `EncodeExecutable` rejects `=`, although it is valid in a desktop-entry `Exec` value. `SetEnabled` therefore returns an error and users cannot enable login startup from that installation path.

Allow `=` in the executable path when encoding the desktop-entry `Exec` value.
</issue_to_address>

### Comment 2
<location path="src/App/MainWindow.axaml.cs" line_range="428-431" />
<code_context>
+        var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
+        {
+            Title = "导出报告",
+            SuggestedFileName = $"Codex-TPS-{date:yyyy-MM-dd}.md",
+            DefaultExtension = "md",
+            ShowOverwritePrompt = true,
+            FileTypeChoices = new[]
+            {
+                new FilePickerFileType("中文报告") { Patterns = new[] { "*.md" } },
</code_context>
<issue_to_address>
**JSON export can write Markdown**

When the user chooses the JSON file type but the picker retains the suggested `.md` filename, `ReportExporter.Write` selects the format from the returned path extension, so it writes Markdown to a `.md` file instead of the requested JSON report.

Ensure the selected file type determines the returned filename extension before calling `ReportExporter.Write`.

Also at `src/Core/ReportExporter.cs:35-36`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and a defect in the Avalonia port could prevent the panel from starting or working correctly on a platform; that behavior is normally fixed by reverting. If login startup is enabled, a malformed LaunchAgent or desktop entry can remain after a revert, but it is bounded and can be removed by disabling login startup or deleting the entry.

Blocking findings: src/Core/Autostart.cs:99, src/App/MainWindow.axaml.cs:431


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/Core/Autostart.cs Outdated
Comment thread src/App/MainWindow.axaml.cs Outdated
@Craun718

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你好——我发现了 1 个问题

给 AI 代理的提示
请处理这次代码审查中的评论:

## 个别评论

### 评论 1
<location path="src/Core/ReportExporter.cs" line_range="16" />
<code_context>
+{
+    public static string BuildMarkdown(UsageSnapshot snapshot, DateTime date, string? model, IReadOnlyList<HourStats> hours)
+    {
+        var text = new StringBuilder($"# Codex TPS 分时统计\n\n日期:{date:yyyy-MM-dd};北京时间 UTC+8;模型:{model ?? "全部模型"}。\n\n快照:{snapshot.GeneratedAt:yyyy-MM-dd HH:mm:ss zzz}。\n\n| 时段 | 样本 | 中位数 | 加权 TPS |\n|---|---:|---:|---:|\n");
+        foreach (var h in hours)
+            text.AppendLine($"| {h.Hour} | {h.Samples} | {h.Median?.ToString("F2", CultureInfo.InvariantCulture) ?? "—"} | {h.Weighted?.ToString("F2", CultureInfo.InvariantCulture) ?? "—"} |");
</code_context>
<issue_to_address>
**不可信的模型文本会被转换为 Markdown**

如果攻击者能够在被扫描的日志中放入经过构造的模型值,而用户随后导出报告,并在会渲染注入内容的 Markdown 查看器中打开报告,那么 `BuildMarkdown` 会在未转义的情况下插入模型值。因此,精心构造的日志数据可能会在导出的报告中加入远程图片或具有欺骗性的链接等 Markdown 内容;会渲染或加载这些内容的查看器将处理攻击者控制的内容。

在将模型值插入报告之前,请先对其进行 Markdown 转义(或将其呈现为不可执行的纯文本)。
</issue_to_address>

Sourcery 评估

等待批准。 请先处理 1 个问题。

阻塞问题:src/Core/ReportExporter.cs:16


Sourcery 为开源项目免费提供服务——如果您喜欢我们的审查,请考虑分享 ✨
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/Core/ReportExporter.cs" line_range="16" />
<code_context>
+{
+    public static string BuildMarkdown(UsageSnapshot snapshot, DateTime date, string? model, IReadOnlyList<HourStats> hours)
+    {
+        var text = new StringBuilder($"# Codex TPS 分时统计\n\n日期:{date:yyyy-MM-dd};北京时间 UTC+8;模型:{model ?? "全部模型"}。\n\n快照:{snapshot.GeneratedAt:yyyy-MM-dd HH:mm:ss zzz}。\n\n| 时段 | 样本 | 中位数 | 加权 TPS |\n|---|---:|---:|---:|\n");
+        foreach (var h in hours)
+            text.AppendLine($"| {h.Hour} | {h.Samples} | {h.Median?.ToString("F2", CultureInfo.InvariantCulture) ?? "—"} | {h.Weighted?.ToString("F2", CultureInfo.InvariantCulture) ?? "—"} |");
</code_context>
<issue_to_address>
**Untrusted model text becomes Markdown**

When an attacker can place a crafted model value in a scanned log and the user exports and opens the report in a Markdown viewer that renders the injected content, `BuildMarkdown` interpolates the model value without escaping it, so crafted log data can add Markdown such as a remote image or deceptive link to an exported report; a viewer that renders or loads it processes attacker-controlled content.

Escape the model value for Markdown (or render it as inert text) before inserting it into the report.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: src/Core/ReportExporter.cs:16


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/Core/ReportExporter.cs Outdated
@Craun718

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嘿——我已经审查了你的更改,整体看起来很棒!

Sourcery 评估

需要人工审查。 如果特定平台的登录启动处理或设置持久化存在问题,用户配置文件中可能会留下启动项或错误的偏好设置;即使回滚,这些残留也可能仍然存在,但影响范围有限,可以将其删除或修正。大多数 UI 或平台集成缺陷则属于普通的运行时错误,回滚即可撤销其影响。


Sourcery 对开源项目免费——如果你喜欢我们的审查,请考虑分享 ✨
Original comment in English

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the platform-specific login-startup handling or settings persistence is wrong, it can leave a startup entry or incorrect preferences in the user’s profile; those artifacts may remain after reverting, but are bounded and can be removed or corrected. Most UI or platform integration defects would otherwise be ordinary runtime bugs that a revert would undo.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗨——我已经审阅了你的更改,整体看起来很棒!

Sourcery 评估

需要人工审核。 如果新的登录启动处理逻辑有误,启用它可能会留下 LaunchAgent 或桌面自动启动项;即使回滚此更改,该项仍会继续启动应用。影响范围有限,可通过禁用登录启动或删除该项来消除;除此之外,界面和报告方面的问题只会造成常规且可逆的运行时影响。


Sourcery 对开源项目免费——如果你喜欢我们的评审,欢迎分享 ✨
Original comment in English

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the new login-startup handling is wrong, enabling it can leave a LaunchAgent or desktop autostart entry that continues launching the app after this change is reverted. The effect is bounded and can be removed by disabling login startup or deleting the entry; the UI and reporting defects otherwise have ordinary, reversible runtime impact.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@jinghero
jinghero merged commit 2683aa1 into jinghero:main Oct 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants