Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
说明具体问题和改动后的行为。

验证:列出实际运行的构建、回归测试和原生界面检查;说明尚需实机验收的部分。

发布范围:说明是否影响 Beta、正式或旧更新线路。涉及 Beta 发布时,填写 build、标签、PKG SHA256 和发布记录;正式线路变更需要维护者明确决定。
40 changes: 23 additions & 17 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# This workflow will build a Swift project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift

name: Swift
name: macOS checks

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: checks-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
build:
Expand All @@ -16,12 +17,17 @@ jobs:

steps:
- uses: actions/checkout@v5
- name: Build
run: swift build -v
- name: Run tests (if present)
run: |
if [ -d Tests ] && find Tests -name "*.swift" -print -quit | grep -q .; then
swift test -v
else
echo "No test files found. Skipping swift test."
fi
with:
fetch-depth: 0
- name: Check whitespace
run: git diff --check origin/main...HEAD
- name: Build release
run: swift build -c release
- name: Verify stable and Beta update contracts
run: bash packaging/pkg/test_updates.sh
- name: Verify native UI controllers
run: python3 packaging/pkg/test_ui.py
- name: Verify actual startup sequence
run: python3 packaging/pkg/test_startup.py
- name: Verify published Beta70 source baseline
run: python3 scripts/verify_release_source.py docs/releases/2.9.10-beta.70.json --ref v2.9.10-beta.70
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,20 @@
*.dmg
*.zip
/v*/

# Generated applications, packages and developer-machine state
*.app/
*.pkg
/build/
.swiftpm/
.wrangler/
.learnings/
__pycache__/
*.py[cod]
.claude/settings.local.json

# Private feedback correspondence stays on the developer's machine
/feedback-consent-emails.md

# Offline experiments: keep source available; exclude generated data
/analysis/thermal_ml/artifacts/
1 change: 0 additions & 1 deletion .wrangler/cache/cf.json

This file was deleted.

6 changes: 0 additions & 6 deletions .wrangler/cache/wrangler-account.json

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
33 changes: 25 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ iFanControl 是一个面向 Apple Silicon Mac 的原生菜单栏风扇控制工

## App 架构红线

- **UI意图保护**:主菜单“告诉开发者”下的邀请文本框是维护者明确保留的设计,用于鼓励反馈;点击进入独立窗口而不直接编辑是预期行为,不得当重复/假输入移除。
- **启动呈现边界**:等待面板必须先于硬件探测显示,按真实步骤更新,到菜单/控制循环初始化后收起;无假百分比/最低等待。更新回执仍按运行build校验,显示在面板收起之后。首次引导与无风扇提示仍保留;不要为缩短等待误改原硬件/遥测/安全逻辑。
- **状态时间语义**:recordedAt是最近一次状态记录写入时间,普通相同签名不会重写,force路径可强制记录;不能与“正常”并列呈现为过去才正常,不能为了UI时间刷新改控温/记录频率。68显示当前状态/配置模式,记录时间在明确标注的tooltip中。

- 菜单一次性构建 + 原地更新(`buildMenuOnce` + `updateDynamicMenuItems`),不要每 2 秒重建 NSMenu
- `BackgroundHardwareReader` 在后台线程执行硬件 I/O,NSLock 保护缓存
- `FanManager` 是 `@MainActor`,硬件调用**必须**在后台线程
Expand All @@ -24,17 +28,18 @@ iFanControl 是一个面向 Apple Silicon Mac 的原生菜单栏风扇控制工
- ZIP 包 app bundle 里**绝不能包含 config.json**(2026-04-27 教训:误带旧 config 导致误判无风扇)
- 开机自启动用 `SMAppService.mainApp.register()`,旧 LaunchAgent plist 首次启动时自动清理

## 构建与替换
## 构建与Git约定

遵循 `CONTRIBUTING.md`。main维持正式代码,codex/3.0-beta保存Beta开发基线;日常直接小步提交/推送开发分支,较大修改按需用任务分支和PR;禁止强推/删除长期分支,不要求构建/PR作为保存代码门槛。Beta标签与发布记录对应真实源码输入。构建workflow已停用、只保留手动触发,不得为了保存Git历史运行构建/测试/打包;用户明确要求本阶段只建立历史、追踪和回退。不能用Git推送触发部署。

App元数据来源为 `packaging/app/Info.plist`,不要修改本机生成的App来代替源码版本提交。

```bash
swift build -c release
cp .build/release/MacFanControl iFanControl.app/Contents/MacOS/iFanControl
cp iFanControl.app/Contents/MacOS/iFanControl /Applications/iFanControl.app/Contents/MacOS/iFanControl
codesign --force --sign - /Applications/iFanControl.app
open /Applications/iFanControl.app
python3 scripts/build_app.py
python3 packaging/pkg/build.py --app build/iFanControl.app --tool kentsmc
```

部署前先确认用户实际运行路径:`/Applications/iFanControl.app/`,不是开发目录(CODE_AUDIT.md#3)
产物位于build/与packaging/pkg/output/,不追踪二进制App/签名/Cloudflare缓存。本机历史文件保留;实验与反馈邮件不要混入公开提交。禁止自动替换用户实际安装的 `/Applications/iFanControl.app`;安装体验通过现有PKG路线验收。

## 发布纪律

Expand All @@ -44,6 +49,12 @@ open /Applications/iFanControl.app

不要只推 GitHub 代码就认为用户能收到更新。

### Beta 独立发布(2026-10-12 用户确认)

开发者手动本地安装 2.9.10/build62 作为种子版本,先只部署 Beta 线路。Beta 迭代只改 `update-manifest-beta.json`、独立含 build 的 PKG 与该 feed 的 no-store 规则;不要同步推进正式 manifest、`iFanControl-macOS.pkg`、`/download`、官网版本/时间线或正式 GitHub Release。正式/旧线路保持 2.9.9/build52,直到用户决定正式发布。

`output/release-candidate/channel-preview/` 包含待正式发布的 build62 文件,**不能整体部署为 Beta 测试线路**。首次 Beta 部署使用 `output/beta-bootstrap-62/site/`,以线上 Production 资产清单逐一校验后构建;后端 functions 保持原源码,保留与线上不同的本地文件,避免将无关工作顺带发布。后续 Beta build 必须大于 62;正式发布时 build 必须大于此前所有 Beta build。

## 官网部署

```bash
Expand All @@ -64,7 +75,13 @@ npx wrangler pages deploy ./ --project-name ifan-stats --no-bundle --skip-cachin

数据库结构、表设计等细节见 `ifan-stats/` 目录内代码。

## 多语言
## 3.0 UI 设计约束(2026-10-13)

用户要求先全面梳理再改善。简洁、优雅、高效涵盖进入、等待、反馈、失败恢复和真实完成;现状与方案见 `docs/development/3.0-ui-audit.md`。保留PKG的系统完成勾与打开说明,不用自定义conclusion覆盖它,不在root安装脚本中自动启动GUI。检查/下载/校验/打开Installer不能提前宣称安装成功。

菜单一次构建原地刷新、即时展示偏好、转速显式应用、曲线关闭保存/预设切换通知都有现有理由。UI重构应明确这些语义,不能把有意的差异当bug一并改掉。安全窗口95℃与代码/历史110℃的差异应按真实规则统一说明,不能改控温来迁就文案。第一轮64/65行为验收通过但视觉被用户否定;第二轮已应用原生设计skill并发布Beta66,原从65→66验收。66记录见 `docs/development/3.0-ui-polish.md`。Beta67曾进一步精简主菜单并修复提示白边/放大勾,信息栏设置维持66原样;新记录见 `docs/development/3.0-ui-menu-notice.md`。Beta68恢复反馈邀请框并说明状态记录时间,记录见 `docs/development/3.0-ui-feedback-intent.md`;原58个非Beta-feed资产及后端保持,正式build52线路继续保留;Beta69放大帮助侧栏导航并收紧选择区域、添加官网按钮,记录见 `docs/development/3.0-ui-help-sidebar.md`;保留59个非Beta-feed资产和后端,正式/旧/download仍52,当前Beta70增加真实启动阶段面板,菜单/控制循环初始化后收起,再显示更新回执/首次引导;保留60个旧非Beta-feed资产,正式/旧/download仍52,记录见 `docs/development/3.0-ui-startup.md`,下一build须大于70。

## 多语言约定

中 / 英存储在 `UserDefaults("ifancontrol.ui.language")`,切换后需重启。三个模块各自定义 `currentLanguage`(main.swift / SensorCatalog.swift / FanCurveWindow.swift)。

Expand Down
50 changes: 50 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Git历史与回退约定

本阶段只建立源码历史、远端备份和回退点。提交或推送不触发构建、测试、打包或部署;GitHub构建workflow已停用,源码中的触发方式也只保留手动入口。现有构建/测试工具保留供将来明确需要时使用,不是保存代码的前置条件。

## 分支与检查点

- `main`:正式版本代码,目前为2.9.9/build52。正式发布另行决定。
- `codex/3.0-beta`:当前开发代码。已保存Beta70完整基线,后续可以直接小步提交和推送。
- `codex/<任务名>`:较大实验或需要隔离的修改,可按需创建;PR也是可选的审阅入口。
- 两条长期分支只防止强推和删除,没有必须构建或PR的门槛。
- 发布检查点用注释标签,例如 `v2.9.10-beta.70`。v*标签禁止更新/删除,已发布检查点不覆盖。

Beta70源码基线提交为 `03e3d1d`;对应标签和 `docs/releases/2.9.10-beta.70.json` 保存已发布包的源码输入与哈希。它是发布后补录的基线,未伪造62~69提交,也不声称原包中嵌入了此Git提交。

## 日常保存

```sh
git status --short
git diff
# 明确选择本次文件,例如:
git add Sources/MacFanControl/main.swift
git diff --cached
git commit -m "fix: describe the concrete change"
git push
```

按能独立理解的改动提交,常用feat/fix/docs/chore前缀。不混入其他尚未完成的工作,不使用未经查看的整目录提交。构建、安装、替换用户App和云端发布与Git保存分开;本阶段不执行这些操作。

## 查看与回退

```sh
git log --oneline --decorate -12
git show v2.9.10-beta.70
```

已推送的错误修改用 `git revert <提交>` 生成反向提交,再推送,保留发生问题和修复的完整历史。它只改变源码,不会自动降级已安装的软件。

需要查看完整旧状态时,先提交或暂存当前改动,再从标签创建恢复分支:

```sh
git switch -c codex/recovery-beta70 v2.9.10-beta.70
```

不对共享分支使用reset --hard加force push,不删除现有历史,也不重写发布标签。回退前先确认需要撤销的范围;这里提供操作方法,不代表已执行产品回退。

## 文件范围

追踪Sources、源码元数据 `packaging/app/Info.plist`、图标/现有工具输入、脚本、研发文档和截图证据。生成App/PKG/签名、Cloudflare/Xcode缓存、用户配置、反馈邮件与实验数据不入库。

原本已追踪的App/缓存仅取消Git追踪,现有磁盘文件和旧Git历史保留。analysis、HTML试稿及旧公告经本机.git/info/exclude留在工作区;以后需要维护时单独审阅加入。研发截图保存在docs/development/assets;完整旧构建诊断仍在忽略的packaging/pkg/output。
37 changes: 35 additions & 2 deletions PROJECT_MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
关联时间戳记忆:`/Users/puremilk/Documents/mac fancontrol/macfan-control-v2/PROJECT_TIMELINE.md`
读取建议:先读本文件,再读时间戳记忆,避免遗漏最新变更。

更新时间:2026-10-05
更新时间:2026-10-13

## 1. 项目当前基线

Expand All @@ -20,7 +20,40 @@ iFanControl 是一个面向 Apple Silicon 带风扇机型的风扇控制工具
- 开机自启动(SMAppService 原生登录项 API,系统设置中可直接管理)
- 信息栏展示设置(完整/简洁/迷你三种模式,弹窗设置,存 `ifancontrol.ui.display_mode`,切换即时生效)

当前版本:`2.9.9` / build 52
当前正式版本:`2.9.9` / build 52

开发者种子版本:`2.9.10` / build 62(由 52 递增 10),用户手动本地安装。独立 Beta 线路已部署;正式渠道仍为 `2.9.9` / build52,未正式发布 build62。

当前Beta版本:`2.9.10 Beta` / build70,Production `6066e693-f572-417a-b5b9-cd784c371078`。维护者可从69在线升级;下一build须大于70。正式/旧线路和官网下载维持2.9.9/build52,最终核验见 output/beta-70/verification.json。

UI意图保留项:主菜单“告诉开发者”下的文本框外观有意用于鼓励反馈,虽不直接输入、与标题打开同一反馈窗口,也必须保留,不能按“重复/假输入”删掉。68恢复原文案/圆角/留白,点击区域使用透明原生按钮,先结束菜单追踪再打开独立反馈窗口。中英文实际控件点击已验证正文获得焦点。

顶部明确显示“控制正常 · 自动/手动”,记录时间放在提示文字中并命名“最近一次状态记录”。ControlStatusStore的recordedAt是状态记录写入时间;正常mode/detail签名未变化时不逐次重写,启动/交还系统等force路径例外。不能把它当实时采样/最后正常检查时间,也不能为让时间新鲜而改写原控温/记录规则。

70增加启动面板:在硬件识别前显示非激活原生窗口与独立线程等待指示器,依真实执行阶段更新文案,菜单/控制循环和后台任务准备好后收起。“已更新至…”与首次语言/无风扇/温度源提示延到收起后。初始化本身的探测、硬件顺序、控温及安全逻辑不变,没有假百分比或最低展示计时。

Release、原生控制器、真实updater回归通过;新增实际AppDelegate隔离回归覆盖普通/6秒同步等待/无风扇/解锁失败/未来回执/首次语言六种场景,断言等待面板先于探测、菜单/计时器完整初始化后收起、之后回执和引导。实际中文浅色、英文深色、阶段切换和完成勾已查看。测试硬件/命令/网络为替身,状态/配置/日志隔离;没有真实安装/控温、替换用户应用或发送反馈。源文件保护见 output/beta-70/protected-invariants.json。

70PKG SHA256 `b446cf03a71c2d3e3652c715f702d7ad14ce4f4c52cc4fe5f53a709090b5cf7a`。Beta单独发布保留60个非Beta-feed旧资产及后端/绑定,官网和稳定/旧manifest为52;以最终verification记录为准。当前说明见 `docs/development/3.0-ui-startup.md`。69帮助侧栏/官网、68反馈意图及更早UI交付记录作为历史保留。自动预下载/设备适配尚未混入,正式发布另行决定。真实安装后的启动等待体验由维护者通过69→70验收。

### Git机制(2026-10-13)

Beta70基线在 `codex/3.0-beta`,对应注释标签 `v2.9.10-beta.70`;main继续保留正式build52。长期分支只禁止强推/删除;没有构建/PR门槛。用户明确缩小范围为Git历史、追踪、回退,已停用自动构建,不再重跑CI;原在途任务在取消请求前已结束,目前无运行任务。v*版本标签通过仓库ruleset禁止更新/删除。规则见CONTRIBUTING.md,发布输入/包/部署证据见docs/releases/2.9.10-beta.70.json。

Info.plist提交来源为packaging/app/Info.plist,root icon.png和kentsmc作为固定输入;scripts/build_app.py生成build/iFanControl.app并记录提交/是否有改动/哈希。生成App、签名、PKG、Cloudflare状态停止追踪但磁盘文件不删除;历史Git不改写。实验源码/HTML/旧公告仍在本机exclude,反馈邮件全局ignore;原生证据图纳入docs/development/assets。

现有构建/回归脚本保留供以后明确需要时使用,本阶段不再运行。GitHub构建workflow已停用,源码触发方式仅手动;推送不会触发构建、测试、打包、部署或正式Release。反向提交git revert用于共享历史回退,标签恢复分支用于检查旧状态,不使用reset/force覆盖历史。70是发布后补录源码基线,未伪造62~69提交。

### 2.9.10 / build 62 筹备

- 简介大 App 图标同次开窗累计点击十次解锁开发者模式;未解锁时关窗清零,解锁状态跨启动/升级保留。
- 独立开发者页中单独勾选 Beta,默认仍为正式线路;退出开发者模式会关闭 Beta 并隐藏入口,仅改变后续更新路线,不自动降级。
- Beta 使用 `update-manifest-beta.json`,与正式版共用现有 PKG 提示、下载、SHA256、系统安装器和正常退出流程;检查/下载中切换路线会忽略旧结果。
- 正式和 Beta 共用递增 build;Beta 身份用独立 Info.plist/manifest 字段,保持系统数字版本字段及既有统计格式。
- 未改变硬件、控温、曲线、配置、遥测、菜单轮询和安装脚本。首次发布仅增加隐藏入口及更新通道支持,不混入 3.0 UI/硬件/预下载改动。
- 种子 PKG 与待正式发布预览:`packaging/pkg/output/release-candidate/`;不要整体部署预览为 Beta 测试。Beta 单独发布只改 Beta feed、独立 PKG 与其缓存规则,正式/旧线路、官网下载/时间线及正式 GitHub Release 保持 build52。
- 已上线 Beta 基线:2.9.10/build62 种子包独立副本,Production `cb249cea-6cb3-4703-84b2-477f13454a28`;53 个旧静态资产哈希、7 个函数源码及运行绑定未变。部署/核验记录在 `packaging/pkg/output/beta-bootstrap-62/`。
- 构建、通道/校验测试、真实 updater 隔离夹具通过,原生窗口验证十次点击/隐藏/退出及深浅色;实际在线 PKG 安装升级仍需发布前验证。

### build 52 说明

Expand Down
Loading