🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI
广告☁️ 云服务器特惠阿里云首购 8 折 · 腾讯云合作特惠
DeepSeek Harness Hub
← 返回列表

tequed232/duofen-kebiao

DeepSeek Harnessspec-screened扫描:无法判定在 GitHub 查看 ↗
需源码安装

【AI 警告】多分课表 - Material 3 Expressive 课表与多模态记录 Web + Android

暂不能直接安装(需源码编译或环境不满足):缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。 · 最近上游提交 2026/9/24 · 已提供中文文档

【AI 警告】多分课表 - Material 3 Expressive 课表与多模态记录 (Web + Android)

综合分
36.3
GitHub 分
36.3
用户评分
—
★ Stars
4
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add tequed232/duofen-kebiao
缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装,改用 GitHub 源安装
信任档位:已验证本站已于 3 天前真实安装成功(L4 · 真实安装)
是什么
dsh 原生插件 · tool
装得上吗
本站已真实安装成功(L4 · 真实安装,非静态推断)
安全吗
本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
还在维护吗
活跃:最近一次提交在 2 天前

档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →

🟢实装验证通过· 2026/9/23
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/24(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查需源码安装

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包m3-expressive-voice-notes(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/20 20:43:19

用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

由 DeepSeek 最新模型翻译生成
多分课表

Material 3 Expressive 风格的课表应用 —— 四日课表 + 教材识别 + 上课提醒(灵动岛 / 流体云)+ 课表导入。目标形态为竖屏手机 412 × 892dp(1080p 级设备),浏览器内直接运行,dist/ 为可直接部署的 production build。

仓库同时包含一份 Kotlin/Compose 的 Android 实现(app/,Gradle 工程)。所有界面以 web/ 为唯一设计基准,本 README 描述 Web 实现。

在线体验:tequed232.github.io/duofen-kebiao
发布页:github.com/tequed232/duofen-kebiao/releases

功能

| 路由 | 屏幕 | 主要能力 |
| --- | --- | --- |
| schedule | 课表 · 多分课表(首页) | 内嵌课表 + 四日表格(上午/中午/下午/晚上时间轴、左右拖动跟手切换日期窗口)、点击课程查看老师/时间/地点、点击地点启动地图导航、课表数据导入面板、右下角「回到今天」 |
| scheduleFilter | 筛选 | 老师 / 课程 / 地点 / 时间 四个标签检索,结果按 课程·老师·地点 三列排列,点击回到课表并高亮 5 秒 |
| textbookList | 教材 | 已导入教材清单、封面选图识别(走「教材识别接口」)、手动填写与移除 |
| settings | 设置 | 外观 / 性能 / 安全区 / 地图与学校名 / 索引与开源入口等列表项 + 叠放其上的开关与 Expressive 滑块、已保存消息条(撤销) |
| apiEdit | 接口配置 | 全应用唯一的通用接口配置(地址 + 密钥,只存本机),目前供教材封面识别使用;可选手选一张图试跑 |
| about | 关于 | 应用信息、Material 3 设计说明、致谢 · 名片墙、素材与隐私说明 |
| licenses | 开源相关 | 按用途分类的依赖清单(scripts/collect-licenses.mjs 生成) |
| blank | 屏幕 7 | 按草图保留的空屏幕 |

课表(自主嵌入)

- 内置课表由教务系统导出的 学生课表.doc(RTF 表格)解析而来,结果固化在 Web 的 web/src/data/schedule.ts —— 应用启动即自带课表;也可以在应用内用「课表数据」面板导入自己的导出文件覆盖它(导入结果存本机 IndexedDB)。
- 4×4 容器 + 左右翻页:容器是四行(上午 / 中午 / 下午 / 晚上)× 四列的表格,一周按 7 天 计算,左右拖动跟手翻页(第 1 页 周一–周四,第 2 页 周五–周日 + 下周一),底部有页码圆点与左右翻页按钮;点击某天选中该日,下方列出当天课程。
- 识别课表月份:解析出的周次会换算成具体日期,顶部显示「2026年9月 · 9月17日 周四」与课表覆盖 2026年8月 – 2027年1月;点按该按钮弹出月份/日期选择器(列出学期内每个月的教学周区间,也可按具体日期跳转),选择后自动定位到对应教学周与星期。课程详情里还会列出该课的具体上课日期(共 N 次课、跨哪几个月)。
- 周次:按学期开始日期(可改)计算当前教学周,表格只显示该周实际开设的课程;◀ ▶ 切换周次。
- 向下滑收起课表:在课表上向下滑(或页面向下滚动)会把课表折叠成一行摘要(周次 · 星期 · 日期 · 当天课程数),腾出空间显示当天课程清单;点摘要行或向上滑即可展开。
- 课程详情:默认只显示课程名,点击后展开授课老师、节次时间、周次、上课日期与地点;点击地点即调用地图。
- 筛选屏:老师/课程/地点/时间 四标签,点击结果回到课表并高亮 5 秒(若该课程不在当前周,自动切到它开课的那一周并提示)。
- 导入:课表数据 面板支持 .doc/.rtf、.html 表格、.csv/.txt 文本导入或恢复内置课表,结果存于 IndexedDB。

上课提醒(灵动岛 / 流体云)

- 每 30 秒检查一次「下一节课」,提前量可调(设置 → 上课提醒,默认 10 分钟);同一节课只提醒一次(按 课程+日期+节次 去重,重启不重复打扰),下课后自动撤销通知。
- 通知带两个动作:导航去(直接拉起地图到教室)与 我到了(回到应用看这节课要带的教材)。
- 设置 → 实时通知(流体云)里有「发送实况测试」,用来确认状态栏实况是否出现。

Android(Android 16 / 天玑 9400 / ColorOS 流体云)

- app/ 只是 WebView 宿主(MainActivity.kt + LiveUpdates.kt),界面全部来自 web/,两端同一份构建。
- 流体云:上课提醒与「发送实况测试」会发布 Android 16 Live Updates(Notification.ProgressStyle + setRequestPromotedOngoing(true)),ColorOS 16 会渲染成流体云卡片;完成后显示结果并在数秒后自动收起。该 API 仅存在于 Android 16,代码用反射调用,因此同一份 APK 在旧系统上退化为普通进行中通知,在 Android 16 上则进入流体云。
- 已加入 POST_NOTIFICATIONS 运行时申请、enableOnBackInvokedCallback(Android 16 预测式返回)、windowSoftInputMode=adjustResize;随语音与相机功能删除,清单里已不再申请录音权限与相机 / 麦克风特性。
- 本机 SDK 平台为 android-35,因此 APK 目前是 targetSdk 35(在 Android 16 / ColorOS 16 上正常运行);安装 platforms;android-36 后把 app/build.gradle.kts 的 compileSdk/targetSdk 改成 36 并升级 AGP ≥ 8.9 即可得到 targetSdk 36 构建。

数据与存储

- 课表、教材与设置全部写入 IndexedDB(m3-expressive-notes),刷新后保留;IndexedDB 不可用时自动降级到 localStorage。
- 没有示例/假数据:没有导入课表就用内置课表、没有教材就显示空状态;没有配置识别接口时明确提示而不是编造结果。
- 教材封面识别用的接口地址与密钥只存本机,随设置一起导出/恢复;不配置也能用,只是识别按钮会提示未配置。

技术栈

- React 19 + TypeScript + Vite 7:web/ 单页应用,vite.config.ts 以 root=web、outDir=dist 构建纯静态产物
- Material Web(@material/web 2.5):filled/outlined/tonal/text button、outlined text field、switch、slider、navigation bar/tab、card、dialog、menu、list-item、fab、divider、icon、circular-progress、ripple 等标准组件;库里没有的(顶部应用栏、消息条、轮播、全屏查看器、可展开面板)才自行实现
- Material Symbols Rounded:scripts/subset-icons.mjs 用 fontkit 把 5.2 MB 变量字体按 PUA 码位裁剪到 77 KB(保留 FILL/GRAD/opsz/wght 轴),scripts/check-icons.mjs 在构建时保证没有图标名漏出子集
- @material/material-color-utilities:SchemeExpressive(高对比度)生成全套 --md-sys-color- 角色,代码里不写死颜色
- Roboto:本地字体(@fontsource/roboto 400/500/700),离线可用
- Playwright + Chromium:412×892 真机视口自动化验收与逐屏截图
- Kotlin / Gradle / WebView:app/ Android 宿主,只做原生权限、SAF 文件选择、外部跳转、返回键与 Live Updates
- Anubis + Cloudflare:可选的反爬层(自建 VPS)与 CDN / Pages(见 deploy/)

目录结构

仓库只做四件事:网页应用(web/)、Android 宿主(app/)、自动化与发布(scripts/ + .github/)、文档与合规(docs/ + 根目录的几份 Markdown)。
构建产物(dist/、build/)与签名文件一律不入库。

顶层

| 目录 | 作用 | 放进这里的规则 |
| --- | --- | --- |
| web/ | 网页应用本体(唯一设计基准),Vite root | 只有网页端代码;界面、样式、数据解析都进这里 |
| app/ | Android 宿主:只做 WebView 外壳与原生能力 | 不写业务界面;界面一律来自 web/ |
| scripts/ | 构建、校验、发布、数据导入脚本 | 一个脚本只做一件事,名字以 check- / verify- 开头的是验收类 |
| docs/ | 文档与授权凭据 | 说明、台账、评估、规范都放这里 |
| deploy/ | 部署配置(反爬 / CDN) | 只放配置与对应说明,不放构建产物 |
| .github/ | CI 与仓库自动化 | 工作流按用途一个文件一件事 |
| legacy/ | 上一版单文件页面,仅供备查 | 不再改动 |
| preview/ | 宽屏设计稿(非应用运行时资源) | 只放设计稿,不参与构建 |
| artwork/ | 图标原图(1024×1024) | 重新生成全密度图标用,不参与运行时 |
| gradle/ | Gradle Wrapper | 仅 Wrapper,勿手工改 |

根目录文件

| 文件 | 功能 |
| --- | --- |
| package.json / package-lock.json | npm 依赖与脚本入口(build / verify / check:) |
| vite.config.ts | 构建配置:root=web、outDir=dist、相对 base(可托管在任意子路径) |
| tsconfig.json | TypeScript 配置 |
| build.gradle.kts / settings.gradle.kts / gradle.properties | Android 构建(产物落 app/build/outputs/apk/release/) |
| gradlew / gradlew.bat | Gradle 启动器 |
| keystore.properties.example | 正式签名配置模板(真实文件 keystore.properties 不入库) |
| README.md / RELEASE_NOTES.md / CONTRIBUTORS.md | 主文档 / 发布说明 / 贡献者名单 |
| .gitignore | 忽略构建产物、签名文件与安装包 |

web/ —— 网页应用

| 路径 | 功能 |
| --- | --- |
| web/index.html | Vite 入口 HTML |
| web/public/ | 原样拷贝进 dist/ 的静态文件:manifest.webmanifest、_headers、PWA 图标、permissions/ 授权截图(关于页点开可看) |
| web/src/assets/ | 参与打包的图片:avatars/ 各人公开头像、app-icon-192.png |
| web/src/main.tsx bootstrap.tsx | 入口:Material Web 注册、主题与性能档引导 |
| web/src/App.tsx | 412×892 舞台、屏幕栈、消息条、启动页 |
| web/src/components/ | 可复用界面件:md.tsx(Material Web 封装)、layout.tsx(应用栏 / 分节头 / 空态)、overlays.tsx(对话框 / 面板)、schedule.tsx(课表格)、splash.tsx(启动页)、m3shape.tsx(M3 形状)、brands.tsx(平台剪影图标) |
| web/src/screens/ | 八个屏幕,一屏一文件 |
| web/src/nav/navigation.tsx | 屏幕栈与转场(含预测式返回) |
| web/src/state/AppState.tsx | 全局状态:设置、课表、教材、消息条 |
| web/src/lib/ | 逻辑层(见下表) |
| web/src/theme/ | 设计令牌与样式(见下表) |
| web/src/data/ | 内置数据:默认课表、教材词表、许可清单 |
| web/src/types/jsx.d.ts |  自定义元素的 JSX 类型声明 |

八个屏幕(web/src/screens/):

| 文件 | 屏幕 |
| --- | --- |
| ScheduleScreen.tsx | 课表(首页) |
| ScheduleFilterScreen.tsx | 筛选 |
| SettingsScreen.tsx | 设置 |
| TextbooksScreen.tsx | 教材窗口(导入 / 识别 / 管理) |
| AboutScreen.tsx | 关于:设计说明、致谢名片墙、开源相关入口 |
| LicensesScreen.tsx | 开源相关(许可清单) |
| ApiEditScreen.tsx | 接口配置 |
| BlankScreen.tsx | 空白屏(转场用) |

逻辑层(web/src/lib/):

| 文件 | 功能 |
| --- | --- |
| schedule.ts scheduleHtml.ts scheduleCache.ts | 课表解析(文本 / HTML 导入)、本地快照与回档 |
| textbooks.ts | 教材识别与词表匹配 |
| db.ts | IndexedDB 读写(数据只在本机) |
| api.ts | 外部接口调用与端点准入(非 https 只放行本机 / 局域网;密钥不外发、报错不回显) |
| imaging.ts | 图片处理与识别结果整理 |
| lens.ts useLens.ts | Liquid Glass 透镜:位移贴图生成与滤镜挂载 |
| classReminder.ts native.ts | 上课提醒、与 Android 宿主通信(流体云 / 通知 / 返回键) |
| meta.ts | 应用元信息与贡献者名单唯一数据源 CREDITS(README / 关于页同源) |
| cdn.ts perf.ts perf-telemetry.ts utils.ts types.ts | CDN 资源地址、性能档与埋点、通用工具与类型 |

主题(web/src/theme/):tokens.css(色彩 / 字体 / 间距角色)、palette.ts(动态配色 SchemeExpressive)、motion.ts(M3 Expressive 弹簧转场)、components.css(组件样式)、schedule.css(课表专有样式)、base.css(基础与开屏)、icon-font.css + icon-codepoints.ts(图标字体与码位)。

app/ —— Android 宿主

| 文件 | 功能 |
| --- | --- |
| app/src/main/java/com/app/m3expressive/MainActivity.kt | WebView 宿主:原生权限、SAF 文件选择、外部跳转、返回键 |
| …/LiveUpdates.kt | Android 16 Live Updates / ColorOS 流体云进度通知 |
| …/NativeDock.kt | 原生底边栏(液态玻璃条,位于 WebView 之下) |
| app/src/main/AndroidManifest.xml | 权限与组件声明 |
| app/src/main/res/mipmap-/ | 自适应图标(5 档密度 + 圆形) |
| app/src/main/res/drawable/ values/ | 通知图标、配色与主题 |

scripts/ —— 自动化

| 文件 | 功能 |
| --- | --- |
| subset-icons.mjs check-icons.mjs | 按需裁剪 Material Symbols 子集;构建时校验无图标漏出 |
| verify.mjs verify-device.ps1 visual-parity.mjs | Playwright 真机视口全流程验收与截图、真机核对、视觉比对 |
| check-imports.mjs | 防「用了没导入」导致白屏 |
| check-secrets.mjs check-web-security.mjs | 密钥 / 凭据扫描;注入面、密钥进 URL、明文端点守卫 |
| check-repo-hygiene.mjs | 安装包、压缩包、超 2 MB 文件不得被跟踪 |
| check-apk-parity.mjs | APK 内嵌资源与 dist/ 逐文件哈希比对 |
| check-version.mjs | 版本号一致性:meta.ts 的 APP_VERSION 为唯一来源,挡住 package.json 与 RELEASE_NOTES.md 漂移 |
| check-about-credits.mjs | 关于页名片墙版式(每行一张、头像在左、按钮齐全、无溢出) |
| github-release.mjs | 发布:规范命名上传、归档到私有仓库、只保留最近 5 条 |
| collect-licenses.mjs | 汇总依赖许可 → 设置页「开源相关」与 README 清单 |
| rtf-dump.mjs | 把教务导出的 RTF 课表转成纯文本,便于检查表格结构 |
| serve-dist.mjs | 本地起静态服务器预览 dist/(默认 4174,check-about-credits.mjs 也对着它跑) |

docs/ —— 文档与凭据

| 文件 | 内容 |
| --- | --- |
| PROJECT-STATE.md | 项目现状与坑位记录(接手先看这份) |
| commit-convention.md | 提交信息规范(类型(范围): 中文描述) |
| writing-style.md | 协作与表达约定 |
| asset-permissions.md | 素材授权台账:任何第三方素材进仓库前必须在此留记录 |
| permissions/ | 授权凭据(聊天原文截图) |
| icon-source.jpg | 图标原图(896×896,留档) |
| security-review.md | 本地安全审查报告与整改 |
| v2-refactor.md | v2 重构记录 |
| coolapk-glass.md | 酷安液态玻璃实现分析(自研参考) |
| dynamic-island.md live-activity-plan.md | 灵动岛 / 实况通知的评估与路线 |
| promo-brief.md | 宣传片创作说明 |
| context-links.md | 外部资料与链接 |

新增文件放哪里

| 你要加的东西 | 放这里 |
| --- | --- |
| 新界面 | web/src/screens/ + web/src/nav/navigation.tsx 注册 |
| 可复用界面件 | web/src/components/ |
| 课表 / 教材 / 接口相关逻辑 | web/src/lib/ 对应文件 |
| 需要被打包的图片 | web/src/assets/ |
| 需要原样发布到站点的文件 | web/public/ |
| 颜色 / 动效 / 样式 | web/src/theme/ |
| 校验脚本 | scripts/,命名 check-.mjs 并接入 package.json 与 CI |
| 说明文档 | docs/ |
| 第三方素材 | 先在 docs/asset-permissions.md 登记授权,再放进 web/public/ 或 app/src/main/res/ |

本地开发

npm install
npm run dev        # 开发模式 (http://127.0.0.1:5173)
npm run build      # 生成 Material Symbols 子集 -> 校验图标 -> vite build
npm run preview    # 预览 production build (http://127.0.0.1:4173)
npm run verify     # 用 Chromium 真机视口跑一遍全流程并截图(screenshots/)

交付物:dist/(62 个文件、约 1.76 MB)—— 纯静态站点,任意静态服务器直接托管即可。

dist/
index.html                       入口(相对路径引用,可放在任意子目录)
assets/index-.js  425 KB        React + Material Web + 业务代码
assets/bootstrap-.js 292 KB     Material Web 组件
assets/index-.css  75 KB        设计令牌 + 组件样式
assets/material-symbols-rounded-subset-.woff2  77 KB   Material Symbols Rounded(71 个图标子集)
assets/roboto--normal-.woff2                  Roboto 400/500/700(本地字体,离线可用)

构建与发布

网页(GitHub Pages)

源码留在 main,构建产物不入库。推送到 main 后由 .github/workflows/pages.yml 重新构建,并把 dist/ 推送到 gh-pages 分支发布(gh-pages 上放 .nojekyll 关闭 Jekyll 处理);PR 只跑构建校验,不发布。上一版单文件页面保留在仓库的 legacy/index.html,仅供备查。

首次启用需要把 Pages 的发布源指向 gh-pages 分支(仓库管理员执行一次):

gh api -X PUT repos/tequed232/duofen-kebiao/pages -f source.branch=gh-pages -f source.path=/

本地复核发布物直接看 dist/ 即可,不要再往仓库根目录拷。

Release(APK + Web 构建)

成品命名与保留规则(2026-09 起):

| 规则 | 内容 |
| --- | --- |
| 命名 | 一个版本只发两份,文件名统一:duofen-kebiao-.apk、duofen-kebiao--web.zip(不再用 enhance- 这套旧名) |
| 别名 | 不再发布 enhance.apk 这类「始终最新」别名;要最新版就用下面的 latest 直链 |
| 保留 | 本仓库 Releases 只保留最近 5 条;更早的版本先归档到私有仓库 duofen-kebiao-releases 再从本仓库移除附件(release 条目与 tag 保留) |
| 归档 | 归档是 private 仓库,保存所有历史版本的成品;源码始终在主仓库对应 tag |

取出本机已保存的 GitHub 凭据(Git Credential Manager),不会打印 token
$out = "protocol=httpsnhost=github.comn" | git credential fill
$env:GITHUB_TOKEN = ($out | Select-String '^password=').Line.Substring(9)

发版:上传两份成品,随后自动执行「归档 + 只留最近 5 条」
node --use-system-ca scripts/github-release.mjs --tag v3.0.2 --target main
--name "多分课表 v3.0.2 —— 安全审查整改 + 致谢口径" --notes RELEASE_NOTES.md
--asset "duofen-kebiao-3.0.2.apk=app/build/outputs/apk/release/app-release.apk"
--asset "duofen-kebiao-3.0.2-web.zip=build/release/duofen-kebiao-3.0.2-web.zip"

只整理历史、不建新版本
node --use-system-ca scripts/github-release.mjs --tag v3.0.2 --prune-only
--asset 的名字可以省略:省略时按 tag 自动命名(.apk → duofen-kebiao-.apk,.zip → duofen-kebiao--web.zip)。

--use-system-ca 是必要的:本机 Node 默认信任链校验不到中间证书(UNABLE_TO_VERIFY_LEAF_SIGNATURE)。
APK 取的是 Gradle 的实际输出路径 app/build/outputs/apk/release/app-release.apk;web zip 需自己打包后放进 build/release/。

Release 签名

release 构建默认回退 debug 签名,只够自用。要换成正式签名,把 keystore.properties.example 复制成仓库根的 keystore.properties 并填好四项(storeFile 相对仓库根解析),构建时会自动启用;该文件已在 .gitignore 中忽略。

仓库卫生

安装包与压缩包不进仓库(APK / web zip 只是 Release 附件,Pages 上不再托管 APK):

- .gitignore 忽略 .apk / .aab / .zip 等;
- npm run check:hygiene(scripts/check-repo-hygiene.mjs)与 .github/workflows/repo-hygiene.yml 在每次推送与 PR 上检查,被跟踪的安装包/压缩包会让 CI 变红(防 git add -f 与 PR 里塞二进制);
- 最新版直链:https://github.com/tequed232/duofen-kebiao/releases/latest(历史版本见私有归档仓库;旧链接 tequed232.github.io/duofen-kebiao/enhance.apk 已失效)。

线上部署同样用 scripts/verify.mjs 回归:

$env:OUT_DIR='screenshots-live'; node scripts/verify.mjs https://tequed232.github.io/duofen-kebiao/

设计实现要点

- 动态配色 — web/src/theme/palette.ts。能从环境拿到用户强调色时(?seed=、window.__MD_SYS_SEED__、已存强调色、CSS accent-color),用 @material/material-color-utilities 的 SchemeExpressive(高对比度)生成全套角色;拿不到就用题目给定的 Green 备用配色逐值写入。所有 UI 只引用 --md-sys-color- 角色,代码里没有写死颜色。
- 动效 — web/src/theme/motion.ts 解析求解 M3 Expressive 的物理弹簧(spatial 0.9 / effects 1.0 阻尼比,stiffness 1400·700·300 / 3800·1600·800),生成 CSS linear() 缓动与时长并写入自定义属性,用于页面转场、面板展开、消息条与涟漪反馈。
- 页面栈导航 — web/src/nav/navigation.tsx:slide(右侧滑入)/ fade(淡入)/ zoom(缩放弹簧)三种转场,栈同步到 history.state,系统返回手势与返回键和页面内返回按钮行为一致(反向播放进入动画)。
- 图标 — Material Symbols Rounded;子集裁剪与校验见上文「技术栈」。

自动化验证

npm run verify(Playwright + Chromium,412×892 视口)走的真实流程:

课表首页渲染(四日表格 4 列 × 4 段 × 7 节次)→ 点开教材窗口 / 关闭 → 设置页
→ 教材识别接口页(填地址与密钥、保存)→ 返回设置
→ 课表:点击课程详情 → 教材封面匹配与标记 → 拖动切换日期窗口 → 下滑收起 / 点摘要展开
→ 筛选屏检索 → 结果高亮定位 → 课表数据导入面板 → 导入后课表出现新课 → 恢复演示课表

语音 / 相机 / 记录相关步骤已随功能删除一并移除(见 test(verify) 提交)。
逐步结果(含逐屏截图路径)都会写进 screenshots/report.json,0 报错才算通过。

结果:全部步骤通过、0 个 console 错误、0 个 page error(逐步结果见 screenshots/report.json);截图见 screenshots/(report.json 内含配色、尺寸与令牌核对数据)。线上部署(GitHub Pages)用同一套脚本跑过一遍,截图与报告在 screenshots-live/。

反爬防火墙(Anubis)与持续监控

站点前面可以挂一层 Anubis(TecharoHQ)—— 用工作量证明挡住 AI 抓取器与脚本爬虫,真人首访只有几百毫秒的静默计算。配置与监控都在仓库里:

- deploy/anubis/docker-compose.yml — Anubis + Caddy(TLS/限速),Anubis 固定版本并暴露 /metrics
- deploy/anubis/botPolicies.yaml — 放行搜索引擎、拒绝 AI 抓取器与常见脚本 UA、按权重分档挑战
- deploy/anubis/Caddyfile — 域名 TLS、限速、安全响应头,reverse_proxy 到 Anubis
- deploy/anubis/VERSION — 锁定的上游版本
- .github/workflows/anubis-watch.yml — 持续监控:每天比对上游 release(有新版本自动开 issue)、校验策略文件、并在配置了 ANUBIS_BASE_URL secret 时探活线上实例
GitHub Pages 不能直接跑 Anubis(它需要自己的服务器作为反向代理)。做法是:域名解析到 VPS,Caddy 终止 TLS 后交给 Anubis,Anubis 回源到 https://tequed232.github.io/duofen-kebiao。详细的部署步骤、Prometheus 抓取配置与告警建议见 deploy/anubis/README.md。

相关文档

- docs/live-activity-plan.md —— 灵动岛/实况通知的现状评估与两条路线(本地调度 vs Cloudflare 远程触发)
- docs/writing-style.md —— 协作与表达约定(中文高语义 / 允许一词多义 / 参照国内开源项目 / AI 思考展示的口径)
- docs/promo-brief.md —— 宣传片创作说明(交给视频制作方 / MiniMax:创作要点、分镜、提示词、授权与禁止项)
- docs/commit-convention.md —— 提交信息规范
- docs/asset-permissions.md —— 素材授权台账
- docs/PROJECT-STATE.md —— 项目现状与坑位记录

唯一设计基准:Web

所有功能(课表、筛选、教材、设置、关于、开源相关…)都以 web/ 为唯一设计基准。

APK 不含任何自有界面:app/ 里只有两个 Kotlin 文件 ——

| 文件 | 作用 |
| --- | --- |
| MainActivity.kt | WebView 宿主:加载 APK 内嵌的同一个 Web 构建;只做原生权限、SAF 文件选择、外部跳转、返回键 |
| LiveUpdates.kt | Android 16 / ColorOS 流体云进度通知(网页通过 window.DuofenNative.liveUpdate() 调用) |

构建链路保证不会漂移:

npm run build          # 产出 dist/(网页)
npm run apk            # preBuild 自动把 dist/ 同步进 app/src/main/assets/www 后编译
npm run apk:parity     # 逐文件比对 APK 内嵌资源与 dist/(文件名 + sha256),不一致即失败
node scripts/visual-parity.mjs   # 同一虚拟设备分别截 APK 内嵌资源与线上网页,逐屏对照

版本号也只有一个来源:web/src/lib/meta.ts 的 APP_VERSION(APK 的 versionName/versionCode 由它推导)。以前那套自绘的 Compose 课表页 / RTF 解析器 / 教材数据已经删除,避免出现第二套会漂移的实现。

素材授权

任何第三方素材进入本仓库之前,都必须先在 docs/asset-permissions.md 留下书面授权记录;没有记录的素材一律不得进入代码或构建产物。当前插画作者 miratsu(Bilibili 空间 18112887)已通过审核,但尚未明确 CC 授权框架,暂不引入,状态与私信模板见该文档;.github/workflows/permission-reminder.yml 会每周提醒跟进,状态更新后自动停止。

构建产物里的图片只有一类:「致谢 · 名片墙」上各人的公开头像(GitHub / B 站,已登记在 docs/asset-permissions.md)。课表页头图暂缺(主美会另出,到位后按台账流程登记再启用)。早期版本用过的插画已于 v1.0.9 全部下架,安装图标与界面装饰改由 Material Symbols Rounded 字形 + Material 3 Expressive 形状语汇自行绘制,配色一律取 --md-sys-color- 角色。

贡献者

提交信息统一按 docs/commit-convention.md 写:类型(范围): 中文描述。

应用「关于 → 致谢 · 名片墙」与 CONTRIBUTORS.md 是同一份名单(数据源:web/src/lib/meta.ts 的 CREDITS),名片墙为 1×3(作者本人整行)+ 其余三列排布(最后一行不满时最后一张跨列补满):

| 名片 | 角色 | 链接 |
| --- | --- | --- |
| 罗xx | 作者 · 项目发起 · 界面动效 · 数据与部署 | GitHub · Bilibili · 抖音 |
| 饼干233 | 翻译 · 同学 | GitHub · Bilibili |
| 维舟(来自MAA-Meow) | 该项目顾问 | GitHub |
| 米达达 | 表情包引用 | Bilibili |
| Hanbing | 主美画师 · 同学 | Bilibili |
| 椿湫 | 导师 | GitHub |
表情包原图不进入构建产物。名片墙上每个入口都带对应平台的剪影图标(GitHub / Bilibili / 抖音 / X),路径内联在 web/src/components/brands.tsx,来自 Remix Icon(Apache-2.0)。

作品许可(名片上的 CC 标记,点开可看授权原文):

- 寒冰(Hanbing):CC BY —— 署名使用,不允许任何形式的 AI 修改;凭据 docs/permissions/hanbing-cc-by.jpg。
- 米达达(miratsu):CC BY-NC —— 署名 + 禁止商用(作品不打包进构建产物,只署名与链接)。

Liquid Glass 视觉与引用

底边栏与应用标识采用 Liquid Glass 质感(半透明玻璃药丸 + 冰彩渐变 + 顶部反光),由项目以 SVG/CSS 自行绘制,不嵌入任何图片素材;实现思路参考以下开源库,特此致谢:

- rdev/liquid-glass-react(6.2k stars)— Apple 风格 Liquid Glass 的 React 实现(SVG 位移折射 + 鼠标高光)
- AndrewPrifer/liquid-dom(2.5k stars)— 面向 Web 的实时 DOM 玻璃透镜折射
- shuding/liquid-glass(1.2k stars)— 可复制的 Liquid Glass 着色器(SVG + Canvas)

设置 → 底边栏风格(互斥)可在 Liquid Glass 与 Material 3 原生导航栏 之间切换。

声明

Tequed232 拥有本项目的最终解释权。

开源相关(Open source)

本项目共引入 23 个开源依赖,按用途分门别类列出(与设置里「关于 → 开源相关」一致)。
感谢每一位作者与维护者。

自动生成:修改依赖后运行 node scripts/collect-licenses.mjs 重新整理。

界面与组件

_界面框架与 Material 3 组件_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| @material/web | 2.5.0 | Apache-2.0 | 未提供版权方信息 |
| react | 19.3.0 | MIT | 未提供版权方信息 |
| react-dom | 19.3.0 | MIT | 未提供版权方信息 |

设计系统 · 图标 · 字体

_配色算法、图标字体与字体裁剪_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| @fontsource/roboto | 5.3.0 | OFL-1.1 | Google Inc. |
| @material/material-color-utilities | 0.4.0 | Apache-2.0 | Material Eng |
| fontkit | 2.0.4 | MIT | Devon Govett |
| material-symbols | 0.47.4 | Apache-2.0 | 未提供版权方信息 |
| Roboto | variable | Apache License 2.0 | Google Fonts |
| subset-font | 2.7.0 | BSD-3-Clause | Andreas Lind |

构建与开发工具

_打包、类型与样式处理_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| @vitejs/plugin-react | 5.2.0 | MIT | Evan You |
| typescript | 5.9.3 | Apache-2.0 | Microsoft Corp. |
| vite | 7.3.6 | MIT | Evan You |

测试与验证

_自动化验收与逐屏视觉校验_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| playwright | 1.63.0 | Apache-2.0 | Microsoft Corporation |

Android 运行时

_APK(WebView 宿主)用到的库_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| androidx.activity:activity-ktx | 1.10.0 | Apache License 2.0 | The Android Open Source Project |
| androidx.core:core-ktx | 1.15.0 | Apache License 2.0 | The Android Open Source Project |
| androidx.webkit:webkit | 1.12.1 | Apache License 2.0 | The Android Open Source Project |
| Kotlin Standard Library | 2.0.x | Apache License 2.0 | JetBrains |

部署与安全

_反爬、CDN 与持续监控_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| Anubis | v1.27.0(deploy/anubis 锁定) | MIT License | TecharoHQ |
| Cloudflare CDN / Pages | — | 商业服务(配置见 deploy/cloudflare) | Cloudflare, Inc. |

其他

_未归类的依赖_

| 名称 | 版本 | 许可 | 版权 / 开发者 |
| --- | --- | --- | --- |
| @techstark/opencv-js | 5.0.0-release.1 | Apache-2.0 | Wilson |
| @types/react | 19.3.0 | MIT | Asana |
| @types/react-dom | 19.3.0 | MIT | Asana |
| tesseract.js | 7.0.0 | Apache-2.0 | 未提供版权方信息 |

参考实现(未引入代码)

| 名称 | 版本 | 许可 | 版权 / 开发者 | 说明 |
| --- | --- | --- | --- | --- |
| AndroidLiquidGlass | — | Apache License 2.0 | Kyant0 | 酷安底边栏液体玻璃的实现(本项目仅参考其思路:背景采样 + 色调 + 边缘渐隐 + 触摸光斑) |
| Shapes | — | Apache License 2.0 | Kyant | 酷安使用的形状库(胶囊/圆角) |
| free_reflection | 2.0.0 | 未提供许可信息 | weishu | 酷安用于反射调用隐藏 API |
| liquid-glass-react | 1.1.1 | MIT License | rdev | Apple 风格 Liquid Glass 的 React 实现(已装依赖,当前底边栏为自绘) |
| liquid-dom | — | MIT License | Andrew Prifer | Web 端玻璃透镜折射参考 |
| shuding/liquid-glass | — | MIT License | Shu Ding | SVG + Canvas 玻璃着色器参考 |

相关文档

- docs/live-activity-plan.md —— 灵动岛/实况通知的现状评估与两条路线(本地调度 vs Cloudflare 远程触发)
- docs/writing-style.md —— 协作与表达约定(中文高语义 / 允许一词多义 / 参照国内开源项目 / AI 思考展示的口径)
- docs/promo-brief.md —— 宣传片创作说明(交给视频制作方 / MiniMax:创作要点、分镜、提示词、授权与禁止项)
- docs/commit-convention.md —— 提交信息规范
- docs/asset-permissions.md —— 素材授权台账
- docs/PROJECT-STATE.md —— 项目现状与坑位记录

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入社群

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

DPharness QQ 群二维码,QQ 扫码进群
QQ 扫码进群
DPharness 飞书群二维码,飞书扫码进群
飞书扫码进群