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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:
uv run --no-sync pytest -q tests/test_launcher.py
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ClassFox-2.0.0-${{ matrix.platform }}-unsigned
name: ClassFox-2.0.1-${{ matrix.platform }}-unsigned
path: |
app-ui/src-tauri/target/release/bundle/dmg/*.dmg
app-ui/src-tauri/target/release/bundle/nsis/*.exe
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jobs:
python-version: '3.12'
- run: uv sync --project api-service --locked --only-group docs
- run: uv run --project api-service --no-sync zensical build --strict
- name: Assemble product homepage and documentation
run: |
cp -R website/. site/
test -f site/guide/index.html
test -f site/assets/classroom.png
test -f site/assets/site.js
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# 更新记录

## 2.0.1 — 2026-09-23

- 将应用标题栏、启动页与 macOS/Windows 安装图标中的默认 Tauri 标识替换为 ClassFox 狐狸标识。
- 重新设计 README,加入使用合成课堂数据采集的真实应用截图。
- 发布暖白极简的 ClassFox 2.0 产品主页,接入 `class.nexa-lang.com`;技术文档保留原有内容路径,入口为 `/guide/`。
- 课堂数据、模型配置和 API 行为保持 2.0.0 兼容,微信正式交付仍计划于 v2.5。

## 2.0.0 — 2026-09-23

### 课堂与桌面
Expand Down
126 changes: 86 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,121 @@
# 课狐 ClassFox 2.0
<div align="center">
<a href="https://class.nexa-lang.com/">
<img src="website/assets/fox.png" width="88" alt="ClassFox 狐狸标识" />
</a>
<h1>ClassFox · 课狐</h1>
<p><strong>把注意力,留在课堂。</strong></p>
<p>课堂原文 · 即时问答 · 课后笔记<br />一个本地优先、免费开源的桌面课堂助手。</p>
<p>
<a href="https://github.com/ouyangyipeng/ClassAssistant/releases/latest"><img src="https://img.shields.io/github/v/release/ouyangyipeng/ClassAssistant?style=flat-square&color=c84923&label=release" alt="最新版本" /></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-252d2d?style=flat-square" alt="MIT License" /></a>
<img src="https://img.shields.io/badge/macOS%20%7C%20Windows-desktop-69716b?style=flat-square" alt="支持 macOS 和 Windows" />
</p>
<p><a href="https://class.nexa-lang.com/"><strong>访问官网</strong></a> · <a href="https://github.com/ouyangyipeng/ClassAssistant/releases/latest"><strong>下载应用</strong></a> · <a href="https://class.nexa-lang.com/guide/">使用文档</a> · <a href="CHANGELOG.md">更新记录</a> · <a href="https://github.com/ouyangyipeng/ClassAssistant/issues">反馈问题</a></p>
<img src="website/assets/thought-to-notes.jpg" alt="从声音到笔记:橙色丝带连接纸张的概念插图" width="100%" />
<p><sub>听见一个瞬间,留下一段思路。Meet ClassFox 2.0.</sub></p>
</div>

<img src="docs/img/logo透明背景.png" alt="课狐 ClassFox" width="120" />
## 一堂课,一个工作区

课狐把课堂转录、关键词提醒、即时问答和课后笔记放在一个桌面工作区里。每堂课独立保存原文;可以下载本地语音与问答模型,也可以使用自己的服务商 API Key(BYOK)。微信小程序计划在 v2.5 正式交付,已有开发预览代码。
不用在录音、聊天和笔记之间反复切换。ClassFox 将原文留在左侧,把课堂助手放在手边。跟上老师的思路,需要时再问一句。

**v2.0 聚焦桌面体验、原文保留和本地模型。** 安装包、在线账户实测、微信真机和商店上架分别验收,具体边界见 [验证记录](docs/project/validation.md)。微信预览没有公开 AppID,不属于 v2.0 正式验收范围。
<a href="https://class.nexa-lang.com/#experience"><img src="website/assets/classroom.png" alt="ClassFox 实际课堂工作区:完整原文与课堂助手并排显示" width="100%" /></a>

[下载安装包](https://github.com/ouyangyipeng/ClassAssistant/releases) · [首次使用](docs/user-guide/quickstart.md) · [升级与数据迁移](docs/user-guide/migration.md) · [微信小程序](mini-program/README.md) · [更新记录](CHANGELOG.md)
<p align="center"><sub>实际应用界面 · 使用合成课堂与模型回答演示功能,不代表真实课堂或付费服务实测。</sub></p>

## 2.0 的主要变化
| 听过的,留得住 | 没跟上的,接着问 | 下课后,继续学 |
| :--- | :--- | :--- |
| 每堂课独立保存原文、资料与笔记。按时间回看、搜索和导出。 | 救场、进度与追问结合课堂内容和资料,逐段显示回答,随时取消。 | 长课堂按段整理为 Markdown 笔记,随时回到原文核对。 |

- **课堂原文保留**:SQLite 按课堂保存转录、资料和笔记。总结使用原文快照,成功后另存笔记;失败、取消和新开课堂不会覆盖旧原文。
- **本地模型安装**:应用内下载 SenseVoiceSmall INT8、Qwen3.5 4B Q4_K_M 与固定版本 llama.cpp,检查文件摘要及加载结果,支持取消和重试。
- **流式课堂助手**:救场、进度、追问和总结逐段显示,提供取消、首字耗时与总耗时;长课按段整理,明确报告超时和服务商错误。
- **桌面工作区**:完整课堂视图与置顶紧凑窗、原文搜索、PPTX/PDF/DOCX/TXT/Markdown 资料、历史笔记、原生保存对话框、浅色/深色外观。
- **跨平台进程管理**:Tauri 启动带身份验证的本机后端,只清理本实例持有的子进程。应用目录只读,用户数据放在系统用户目录。
- **v2.5 微信开发预览(尚未正式交付)**:手机录音、原文、提醒、问答、笔记和导出;Key 默认仅驻留内存。连接电脑时使用一次性扫码、双端核对和加密消息,手机只能访问自己的课堂。
- **原文始终保留。** SQLite 事务保存课堂;总结另存笔记,失败、取消与新开课堂不会覆盖旧原文。
- **模型在你选择的地方运行。** 应用内下载离线语音与问答模型,或分别配置自己的 ASR / LLM 服务。
- **资料也在课堂里。** 支持 PPTX、PDF、DOCX、TXT 和 Markdown,供课堂问答参考。
- **展开或收起,都顺手。** 完整工作区、置顶紧凑窗、关键词提醒,以及浅色 / 深色外观。
- **记录带得走。** 通过原生保存对话框导出原文和笔记,继续放进你的学习工作流。

## 选择使用方式
<details>
<summary><strong>看看课后笔记与紧凑窗口</strong></summary>
<br />
<img src="website/assets/notes.png" alt="ClassFox 的 Markdown 课堂笔记" width="100%" />
<p>笔记另存,原文不被替换。需要更安静的桌面时,收起为紧凑窗口:</p>
<img src="website/assets/compact.png" alt="ClassFox 实际置顶紧凑窗口" width="430" />
</details>

| 方式 | 所需资源 | 课堂内容去向 |
| --- | --- | --- |
| 桌面本地模式 | 下载语音和问答模型;足够磁盘、内存 | 在电脑上处理,原文保存在本机 |
| 桌面 BYOK | ASR 和 LLM 可分别配置自己的服务 | 相应音频或文字发送到选定服务商 |
| 手机独立 BYOK(v2.5 预览) | 微信项目可运行;预设服务商 Key;网络 | 音频和文字分别发送到所选 ASR、LLM 服务商 |
| 手机连接电脑(v2.5 预览) | 同一可互通局域网;电脑运行课狐并已安装本地模型 | 加密传给电脑处理;电脑和手机各保存手机课堂记录 |
## 从下一堂课开始

本地问答权重约 2.74 GB,语音模型约 240 MB,安装还需要临时空间。运行内存与响应速度取决于设备和上下文。模型不随桌面安装包内置,首次安装需要联网。手机首版不在手机上运行离线大模型。
| macOS · Apple Silicon | macOS · Intel | Windows · x64 |
| :---: | :---: | :---: |
| [**下载 DMG ↗**](https://github.com/ouyangyipeng/ClassAssistant/releases/download/v2.0.1/ClassFox_2.0.1_aarch64.dmg) | [**下载 DMG ↗**](https://github.com/ouyangyipeng/ClassAssistant/releases/download/v2.0.1/ClassFox_2.0.1_x64.dmg) | [**下载安装程序 ↗**](https://github.com/ouyangyipeng/ClassAssistant/releases/download/v2.0.1/ClassFox_2.0.1_x64-setup.exe) |
| M 系列芯片 | Intel 芯片 | 64 位系统 |

## 首次使用
[发布说明与 SHA-256 校验和](https://github.com/ouyangyipeng/ClassAssistant/releases/latest) · [首次使用指南](https://class.nexa-lang.com/user-guide/quickstart/)

1. 打开「设置 → 本地模型」完成模型安装,或在「问答服务」「语音与麦克风」配置 BYOK。
2. 本地模式可先点「准备问答模型」,把首次加载安排在课前。BYOK 可用固定短文本测试连接。
3. 检测麦克风,填写关键词。录音前确认已取得参与者的知情同意。
4. 开始一堂课,选择麦克风、浏览器语音、文本输入或分段音频上传。系统听写可直接输入文本框。
5. 需要时发起救场、进度或追问;结束后整理笔记并导出。
> **安装提示**:macOS 包使用 ad-hoc 签名,尚未完成 Developer ID 签名和公证;Windows 包未使用商业代码签名,可能出现系统安全或信誉提示。请从本项目发布页下载并核对校验和。模型在应用内另行下载,不随安装包内置。

浏览器语音依赖 WebView 和网络,不等于离线识别。模型回答和识别结果可能有误,需要结合原始课堂内容核实。
**01 · 选择模型** 在「设置 → 本地模型」安装离线模型,或配置自己的语音识别与问答服务。

## 从源码运行
**02 · 准备课堂** 测试连接、检测麦克风、填写关键词。本地问答可在课前预热。

需要 Python 3.12、[uv](https://docs.astral.sh/uv/getting-started/installation/)、Node.js 22+、pnpm 12.4.2、Rust 和 [Tauri 平台构建依赖](https://v2.tauri.app/start/prerequisites/)。本次验证使用 Python 3.12.13 / Node.js 24。
**03 · 开始记录** 录音、补充文字或上传音频;需要时问一句,下课后整理并导出。

录音前请取得参与者的知情同意。识别和模型回答可能出错,需要结合课堂原文核实。浏览器语音依赖 WebView 与网络,不等于离线识别。

## 本地优先,选择自由

| | 本地模型 | 自带 API Key · BYOK |
| :--- | :--- | :--- |
| **准备** | 在应用内下载 SenseVoice、Qwen 与推理运行时 | 分别配置 ASR、LLM 服务与自己的 Key |
| **处理位置** | 使用本地模型时,音频和文字在电脑上处理 | 相应音频或文字发送到所选服务商 |
| **使用成本** | 自己的磁盘、内存与算力 | 由所选服务商决定 |
| **开始前** | 下载、校验、课前预热 | 保存配置、测试连接 |

本地问答权重约 **2.74 GB**,语音模型约 **240 MB**,安装还需临时空间;速度与内存占用取决于设备和上下文。macOS 本地问答运行时要求 **13.3+**,较旧系统可选择 BYOK 或已有兼容服务。首次模型下载需要联网。

**微信小程序 → v2.5。** 仓库已保留手机独立 BYOK 与连接电脑的开发预览,但尚未完成 AppID、开发者工具、真机与上架验收,不属于 v2.0 的正式功能承诺。[了解预览范围](mini-program/README.md)

## 开放构建

需要 Python 3.12、[uv](https://docs.astral.sh/uv/getting-started/installation/)、Node.js 22+、pnpm 12.4.2、Rust 和 [Tauri 平台依赖](https://v2.tauri.app/start/prerequisites/)。本项目使用锁文件管理依赖。

```bash
git clone https://github.com/ouyangyipeng/ClassAssistant.git
cd ClassAssistant
uv run --no-project --python 3.12 scripts/dev.py
```

Windows 也可运行根目录 `dev.bat`。启动脚本使用锁定依赖和独立后端,不会按名称或端口强杀其他程序,也不会创建或覆盖 `.env`。
Windows 也可运行根目录 `dev.bat`。脚本使用独立后端,不会按名称或端口强杀其他程序,不创建或覆盖 `.env`。

<details>
<summary><strong>构建安装包、验证与官网开发</strong></summary>

```bash
# macOS 本机架构应用与 DMG;默认仅临时签名
# macOS:当前架构应用与 DMG,默认 ad-hoc 签名
uv run --no-project --python 3.12 scripts/build.py --bundles app,dmg

# Windows x64 安装程序(在 Windows 上运行)
# Windows x64:在 Windows 上构建
uv run --no-project --python 3.12 scripts/build.py --bundles nsis

# 产品主页静态预览
python3 -m http.server 8080 --directory website
```

构建、签名、回归和发布门禁见 [开发指南](docs/getting-started/development.md) 与 [打包说明](docs/getting-started/packaging.md)。微信源码有独立的 [构建与配置说明](mini-program/README.md)。
[开发指南](docs/getting-started/development.md) · [打包与签名](docs/getting-started/packaging.md) · [验证范围](docs/project/validation.md) · [网站与品牌维护](docs/getting-started/website.md)

## 数据和升级
</details>

桌面正式版数据目录为 macOS 的 `~/Library/Application Support/ClassFox` 或 Windows 的 `%LOCALAPPDATA%\ClassFox`。调试版使用 `ClassFox Development`。数据库包含课堂文本,备份应在正常退出应用后复制整个数据目录。
## 数据属于你

旧版 `data/`、`.env` 与外观偏好可在设置中分别导入。旧文件保留,重复导入相同文本不重复创建记录。旧版本已经压缩或覆盖掉的原文无法恢复。2.0 不分发共享试用 Key,不继续开放旧的无鉴权 API;第三方调用方请阅读 [接口迁移说明](docs/developer/api-reference.md)。
macOS 数据位于 `~/Library/Application Support/ClassFox`,Windows 位于 `%LOCALAPPDATA%\ClassFox`;调试版使用独立的 `ClassFox Development`。请在正常退出后备份整个数据目录。

## 贡献与许可
旧版数据、配置与外观偏好可在设置中显式导入,原文件保留。已被旧版覆盖或压缩掉的原文无法恢复。2.0 不分发共享试用 Key;第三方调用方需迁移到经过身份验证的 `/api/v2`。

欢迎提供可复现的问题、脱敏日志和改进建议,参见 [贡献指南](CONTRIBUTING.md)。macOS 分支和 WebSpeech 等历史贡献的处理记录见 [issues 与分支](docs/project/maintenance.md)。
[升级与迁移](docs/user-guide/migration.md) · [接口说明](docs/developer/api-reference.md) · [问题与历史分支](docs/project/maintenance.md)

项目代码使用 [MIT License](LICENSE)。语音模型、问答模型、运行时和第三方依赖各自遵循上游许可,见 [第三方说明](THIRD_PARTY_NOTICES.md)。
---

macOS 本地问答使用的 llama.cpp 官方运行时最低要求 **13.3**,安装前会检查;较旧系统仍可选择 BYOK 或已有兼容服务。
<div align="center">
<img src="website/assets/fox.png" width="42" alt="" />
<p><strong>为每一个想跟上思路的瞬间。</strong></p>
<p>欢迎一个可复现的问题、一条建议,或一次 Pull Request。<br /><a href="CONTRIBUTING.md">参与贡献</a> · <a href="LICENSE">MIT License</a> · <a href="THIRD_PARTY_NOTICES.md">第三方许可</a></p>
<p><sub>ClassFox contributors · Built in the open.</sub></p>
</div>
2 changes: 1 addition & 1 deletion api-service/classfox/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""ClassFox application services."""

__version__ = "2.0.0"
__version__ = "2.0.1"
2 changes: 1 addition & 1 deletion api-service/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "classfox-service"
version = "2.0.0"
version = "2.0.1"
description = "Local-first classroom transcription and assistance"
requires-python = ">=3.12,<3.14"
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion api-service/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
async def test_health_works_without_a_model_key_and_exposes_no_paths(client: AsyncClient) -> None:
response = await client.get("/api/health")
assert response.status_code == 200
assert response.json() == {"status": "healthy", "version": "2.0.0"}
assert response.json() == {"status": "healthy", "version": "2.0.1"}


async def test_credentials_required_for_settings_and_classroom_data(client: AsyncClient) -> None:
Expand Down
2 changes: 1 addition & 1 deletion api-service/tests/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def close(process: asyncio.subprocess.Process) -> None:
async def test_owned_launcher_auth_readiness_and_shutdown(tmp_path: Path, shutdown: str) -> None:
process, ready = await launch(tmp_path / "中文 data")
try:
assert ready["type"] == "ready" and ready["version"] == "2.0.0"
assert ready["type"] == "ready" and ready["version"] == "2.0.1"
port, token = ready["port"], ready["token"]
assert isinstance(port, int) and isinstance(token, str) and len(token) >= 32
async with httpx.AsyncClient(base_url=f"http://127.0.0.1:{port}", trust_env=False) as client:
Expand Down
2 changes: 1 addition & 1 deletion api-service/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "app-ui",
"private": true,
"version": "2.0.0",
"version": "2.0.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
6 changes: 6 additions & 0 deletions app-ui/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export default defineConfig({
screenshot: "only-on-failure",
},
webServer: [
{
command: `"${python}" -m http.server 18866 --bind 127.0.0.1 --directory website`,
cwd: root,
port: 18866,
reuseExistingServer: false,
},
{
command: `"${python}" tests/ui_server.py`,
cwd: path.join(root, "api-service"),
Expand Down
2 changes: 1 addition & 1 deletion app-ui/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app-ui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "app-ui"
version = "2.0.0"
version = "2.0.1"
description = "ClassFox classroom transcription and assistance"
authors = ["Ouyang Yipeng and ClassFox contributors"]
edition = "2021"
Expand Down
Binary file modified app-ui/src-tauri/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/128x128@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app-ui/src-tauri/icons/64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square107x107Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square142x142Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square150x150Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square284x284Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square30x30Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square310x310Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square44x44Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square71x71Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/Square89x89Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app-ui/src-tauri/icons/icon.icns
Binary file not shown.
Binary file modified app-ui/src-tauri/icons/icon.ico
Binary file not shown.
Binary file modified app-ui/src-tauri/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion app-ui/src-tauri/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ mod tests {
for input in [
"invalid",
r#"{"type":"error","message":"synthetic-private-detail"}"#,
r#"{"type":"ready","port":8765,"token":"bad\r\nheader","version":"2.0.0"}"#,
r#"{"type":"ready","port":8765,"token":"bad\r\nheader","version":"2.0.1"}"#,
] {
let result = validate_ready(input);
assert!(result.is_err());
Expand Down
2 changes: 1 addition & 1 deletion app-ui/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "课狐ClassFox",
"version": "2.0.0",
"version": "2.0.1",
"identifier": "com.classassistant.app",
"build": {
"beforeDevCommand": "pnpm dev",
Expand Down
Loading