Compare commits
1 Commits
0.5.1
..
1d6b049a39
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d6b049a39 |
+12
-14
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
欢迎支持此项目!
|
欢迎支持此项目!
|
||||||
|
|
||||||
目前, 项目仓库主服务器为<a href="https://git.pluv27.top/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">作者的 Gitea 实例</a>, 它负责管理同步, 保证可用性并同时接受来自多个社区的协作, 并在 <a href="https://github.com/pluvium27/HeurAMS" target="_blank" rel="noopener noreferrer">GitHub</a>, <a href="https://invent.kde.org/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">KDE Invent</a> 和 <a href="https://gitee.com/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">Gitee</a> 设置了镜像同步.
|
目前, 项目仓库主服务器为<a href="https://git.pluv27.top/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">作者的 Gitea 实例</a>, 以保证可用性并同时接受来自多个社区的协作, 并在 <a href="https://github.com/pluvium27/HeurAMS" target="_blank" rel="noopener noreferrer">GitHub</a> 和 <a href="https://invent.kde.org/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">KDE Invent</a> 设置了镜像同步.
|
||||||
|
|
||||||
这丝毫不影响项目接受来自 <a href="https://github.com/pluvium27/HeurAMS" target="_blank" rel="noopener noreferrer">GitHub</a>, <a href="https://invent.kde.org/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">KDE Invent</a> 和 <a href="https://gitee.com/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">Gitee</a> 的 PR, 在 GitHub, KDE Invent 和 Gitee 所接受的 PR 会保留贡献者标识并按原样同步回所有平台, 欢迎在任意平台为项目做出贡献.
|
这丝毫不影响项目接受来自 <a href="https://github.com/pluvium27/HeurAMS" target="_blank" rel="noopener noreferrer">GitHub</a> 和 <a href="https://invent.kde.org/pluv/HeurAMS" target="_blank" rel="noopener noreferrer">KDE Invent</a> 的 PR, 在 GitHub 与 KDE Invent 所接受的 PR 会保留贡献者标识并按原样同步回所有平台, 欢迎在任意平台为项目做出贡献.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> 我们已经开始着手于基于 KDE 用户界面框架 `Kirigami` 的现代跨平台前端开发, 称作 "KiriMemo", 包名是 "org.kde.kirimemo", 但其并非 KDE 项目\
|
> 我们已经开始着手于基于 KDE 用户界面框架 `Kirigami` 的现代跨平台前端开发, 称作 "KiriMemo", 包名是 "org.kde.kirimemo", 但其并非 KDE 项目\
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
|
|
||||||
分支划分:
|
分支划分:
|
||||||
|
|
||||||
- `dev` 分支(仓库默认分支): 主线开发分支, 自身仅用于非重构的问题修复和整合功能分支, 拉取请求在该分支合并
|
- `main` 分支: 稳定版本, 仅当稳定版本释出或修补版本时将 `dev` 合并到 `main` 上
|
||||||
- `master` 分支: 主线稳定版本, 仅当稳定版本释出或修补版本时将 `dev` 合并到 `master` 上
|
- `dev` 分支: 主线开发版本, 自身仅用于非重构的问题修复和整合功能分支
|
||||||
- 功能与重构分支: 从 `dev` 分支创建, 命名格式为 `feature/描述` 或 `fix/描述` 或 `refactor/描述` 或 `next/版本号`
|
- 功能与重构分支: 从 `dev` 分支创建, 命名格式为 `feature/描述` 或 `fix/描述` 或 `refactor/v版本号`
|
||||||
- 功能与重构分支应先合并至 `dev`, 再合并至 `master`
|
- 功能与重构分支应先合并至 `dev`, 再合并至 `main`
|
||||||
|
|
||||||
代码格式化:
|
代码格式化:
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
mdformat --number .
|
mdformat --number .
|
||||||
```
|
```
|
||||||
- 对于 Textual CSS, 可以使用 `prettier` 格式化
|
- 对于 Textual CSS, 可以使用 `prettier` 格式化
|
||||||
- 格式化不是必需的, 可以整合入一次 `style` 提交, 但 `master` 和 `dev` 分支上的代码应尽量整洁, 以便合并时审查
|
- 格式化不是必需的, 可以整合入一次 `style` 提交, 但 `main` 和 `dev` 分支上的代码应尽量整洁, 以便合并时审查
|
||||||
|
|
||||||
提交消息:
|
提交消息:
|
||||||
|
|
||||||
@@ -54,16 +54,12 @@
|
|||||||
- 为了一致性和可追溯性, 项目自 v0.4.0 重构后重新初始化仓库起就禁止使用 Fast-forward 合并
|
- 为了一致性和可追溯性, 项目自 v0.4.0 重构后重新初始化仓库起就禁止使用 Fast-forward 合并
|
||||||
- 可以设置 `git config merge.ff false`
|
- 可以设置 `git config merge.ff false`
|
||||||
|
|
||||||
提交署名方式:
|
|
||||||
由于 KDE Invent 设施的奇怪 git hook, commit 的 Author 字段需要看起来像个真名(例如 Wang Zhiyu 不能写为 wangzhiyu, 否则 KDE Invent 的 hook 会拒绝 push),
|
|
||||||
所以请确保您的 git 配置使用了类似正式姓名的格式 (例如 git config user.name "Li Hua", 也即中间需要空格), 不一定要真实姓名, 邮箱无要求, 也可以将单名重复使用两次 (例如 Thura Thura) 以通过检测.
|
|
||||||
|
|
||||||
## 设置开发环境
|
## 设置开发环境
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.pluv27.top/pluv/HeurAMS # 默认分支为 dev, 所以不必切换分支
|
git clone https://git.pluv27.top/pluv/HeurAMS
|
||||||
|
|
||||||
cd HeurAMS
|
cd HeurAMS
|
||||||
|
git checkout dev
|
||||||
|
|
||||||
# 如果决定使用 uv (推荐)
|
# 如果决定使用 uv (推荐)
|
||||||
|
|
||||||
@@ -71,7 +67,9 @@ python3 -m pip install uv
|
|||||||
|
|
||||||
uv sync --all-extras # 同步开发运行环境
|
uv sync --all-extras # 同步开发运行环境
|
||||||
|
|
||||||
uv run heurams
|
uv run heurams # 验证包安装
|
||||||
|
|
||||||
|
uv run heurams-tui # 启动 TUI
|
||||||
|
|
||||||
# 如果决定使用原生 python 环境 (不推荐, 但我们保留了这种方式以便在不便支持 uv 与硬链接的环境和文件系统(例如 termux)上运行)
|
# 如果决定使用原生 python 环境 (不推荐, 但我们保留了这种方式以便在不便支持 uv 与硬链接的环境和文件系统(例如 termux)上运行)
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
## 黑乎乎的这个界面我怎么用?
|
## 黑乎乎的这个界面我怎么用?
|
||||||
|
|
||||||
首先, 如果您只是想要一个亮色模式, 可以直接按下 `d` 键或点击 "d 主题" 按钮, 这会让您的界面变得白乎乎的(
|
|
||||||
|
|
||||||
得益于微软几十年对用户进行的"命令行即落后"教育, 以及 `conhost.exe` 和 `cmd.exe` 的糟糕体验, 您对终端用户界面感到不适应是完全正常的.
|
得益于微软几十年对用户进行的"命令行即落后"教育, 以及 `conhost.exe` 和 `cmd.exe` 的糟糕体验, 您对终端用户界面感到不适应是完全正常的.
|
||||||
|
|
||||||
但实际上, 虽然看起来像老式电脑屏幕, Textual 和终端标准其实比您想象得要现代一些.
|
但实际上, 虽然看起来像老式电脑屏幕, Textual 和终端标准其实比您想象得要现代一些.
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
## 特性
|
|
||||||
|
|
||||||
### 间隔重复调度器
|
|
||||||
|
|
||||||
> 许多出版物都广泛讨论了不同重复间隔对学习效果的影响. 特别是, 间隔效应被认为是一种普遍现象. 间隔效应是指, 如果重复的间隔是分散/稀疏的, 而不是集中重复, 那么学习任务的表现会更好. 因此, 有观点提出, 学习中使用的最佳重复间隔是**最长的, 但不会导致遗忘的间隔**.
|
|
||||||
|
|
||||||
- 软件开箱即用, 无需多加配置即可使用默认的 `SM-2` 算法进行学习
|
|
||||||
- 算法模块是 "潜进" 内核 (heurams.kernel) 中的一等公民, 内核天然支持插拔各型算法
|
|
||||||
- 无需安装繁杂的插件即可分单元集完成算法快速切换与调优, 研究者可以方便地修改算法模块以便捷地进行研究与测试
|
|
||||||
- 默认使用 `SM-2` 简单间隔重复算法, 此算法亦用作 `Anki` 闪卡记忆软件的默认闪卡调度器
|
|
||||||
- 还内置了 `NSP-0` 筛选用非间隔重复算法以便快速筛选记忆内容, `FSRS` 先进间隔重复算法作为效率更高的调度器, 与 `SM-15M (移植自 sm.js 项目)` 复杂间隔重复算法(逆向工程)
|
|
||||||
- 算法模块可以标记记忆项目, 也可以动态规划每个记忆单元的记忆间隔时间表, 动态跟踪记忆反馈数据, 以优化长期记忆保留率与稳定性
|
|
||||||
- 得益于项目的模块化架构与单元集结构设计, 同一个单元集可以与任意种算法共存并互通, 这对研究者及想探索/实验高效率方法的用户极其友好
|
|
||||||
|
|
||||||
### 多模态学习进程
|
|
||||||
|
|
||||||
与 Anki 的 SQLite `.apkg` 包不同, 我们坚持使用人类可读的文件夹组织单元集, 这带来了若干好处, 包括:
|
|
||||||
|
|
||||||
- 人类可读: 您可以用任意工具, 乃至一个记事本自由修改记忆载荷数据而无需打开软件
|
|
||||||
- 元数据配置: 配置自由度极高, 可以任意组合, 重造, 乃至创造新内容
|
|
||||||
- 测验, 算法与知识互相隔离: 一条知识不再是单一的闪卡, 不仅可以用若干不同的算法规划, 还可以用多种并行的谜题类型测验, 极大地提升学习效果和丰富度. 作为学习者, 您无需担忧概念复杂--仅需从云端下载单元集即可开箱即用上述特性!
|
|
||||||
- 多模态学习
|
|
||||||
- 软件自身集成了文本转语音 (TTS) , 音频与语言模型 (LLM) 模块, 这些功能乃至功能本身都是可插拔, 可扩展, 可切换驱动的, 这为内容创建了极大的丰富度
|
|
||||||
- 软件内置多种谜题类型, 包括选择题 (MCQ), 填空题 (Cloze) 与识别题 (Recognition), 您可在同一单元应用多种, 或是选择性启用
|
|
||||||
- 软件天然支持动态内容生成, 支持宏驱动的模板系统, 根据上下文乃至语言模型动态生成知识点的解析
|
|
||||||
- 在间隔重复研究尚被 SuperMemo 系列独占的时代, Wozniak 就早已表示 "如果不能理解知识, 就无需记忆它". 今天, 我们依然相信理解是记忆的基石
|
|
||||||
- 云同步与分享优化:
|
|
||||||
- 由于记忆数据和单元集文件都是文本文件, 故可进行快速的增量同步而无需完整地上传所有文件, 并且设计天然支持版本控制
|
|
||||||
- 如果您想分享单文件, 软件也支持导出为压缩包或合并成单文本文件以通过纯文本文件形式在 pastebin 等平台分享
|
|
||||||
- 性能提升: 得益于现代且支持分块的文件组织结构, 潜进能在保持高自由度的同时仅使用 python 就能达到敏捷且低占用的用户体验
|
|
||||||
- AI 辅助友好: 想象您有一些 `.apkg` 牌组或一大段教材内容, 您可以方便且高效率地使用 AI 工具以创建可在 HeurAMS 使用的单元集
|
|
||||||
|
|
||||||
### 内置实用用户界面
|
|
||||||
|
|
||||||
尽管不是唯一前端, 但响应式 Textual 框架构建的内置终端用户界面在多种场景下仍具有独特优势:
|
|
||||||
|
|
||||||
- 跨平台, 并支持触屏/鼠标/键盘多操作模式
|
|
||||||
- 与几乎所有现代终端模拟器相容
|
|
||||||
- 对于<a href="https://www.arewesixelyet.com/" target="_blank" rel="noopener noreferrer">支持 sixel 协议的终端模拟器</a>, 可高清显示图像内容
|
|
||||||
- 对于不支持 sixel 协议的终端模拟器, 也支持图片低清的兼容显示模式
|
|
||||||
- 可通过 textual-web 作为服务部署, 并在任意浏览器使用
|
|
||||||
- 简洁直观, 键盘友好, 全功能且高效率的用户界面设计
|
|
||||||
- 易于嵌入: 可在 getty/kmscon 中运行而无需任何桌面图形服务
|
|
||||||
- 资源占用小, 运行流畅, 不拖泥带水
|
|
||||||
- 便于测试与调试程序库
|
|
||||||
|
|
||||||
查看[屏幕截图](SCREENSHOTS.md).
|
|
||||||
|
|
||||||
|
|
||||||
## 包依赖组说明
|
|
||||||
|
|
||||||
由于部分依赖只被少数功能需要, 所以我们把可选依赖分得比较细, 前面提供的命令会安装部分可选依赖, 以下是依赖组列表:
|
|
||||||
|
|
||||||
| 依赖组 | 包含模块 | 说明 |
|
|
||||||
|--------|----------|------|
|
|
||||||
| 构建系统 | hatchling | 构建时安装 |
|
|
||||||
| 最小化安装 | tabulate, toml, transitions, click | 核心驱动程序库, 始终必需 |
|
|
||||||
| interface | textual | 基本用户界面依赖 |
|
|
||||||
| algo-fsrs | fsrs | FSRS 算法模块 |
|
|
||||||
| tts-edgetts | edge-tts | 微软文本转语音 |
|
|
||||||
| llm | llms-py | API 调用 |
|
|
||||||
| audio-playsound | playsound3 | 通用音频模块 |
|
|
||||||
| dev | zmq, pytest, pytest-cov | 开发调试与测试工具 |
|
|
||||||
| basic | [tts-edgetts], [llm], [algo-fsrs] | 适用于用户体验的较轻依赖组(推荐) |
|
|
||||||
| all | 以上所有依赖 | 完整安装组 |
|
|
||||||
|
|
||||||
## 关于此仓库
|
|
||||||
|
|
||||||
此仓库为 "潜进" 的核心程序库在 python 语言下的实现\
|
|
||||||
包含数据模型与框架, 并内置了基于 textual 框架的前端实现 (interface 子模块)\
|
|
||||||
除了通过内置前端进行学习外, 开发者也能在 python 环境中导入 `heurams` 库或使用 `RPC` 与 `heurams` 程序库实例通讯, 使用框架构建其他辅助记忆功能前端或其他应用程序
|
|
||||||
|
|
||||||
潜进项目的所有仓库如下:
|
|
||||||
|
|
||||||
| 项目名称 | 状态 | 说明 | 包名 | 技术栈 | 目标平台 |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| HeurAMS | 开发中<br/>原型可用 | 提供通用核心程序库与基本用户界面 | `heurams` | Python | 标准 Python 环境 |
|
|
||||||
| KiriMemo | 开发中<br/>原型可用 | 基于 KDE 技术的现代跨平台前端 | `org.kde.kirimemo` | C++, Qt6, Kirigami, PyOtherSide | 桌面与移动设备 |
|
|
||||||
| ArkMemo | 开发中 | 基于 ArkUI 的现代跨平台前端 | `top.pluv27.arkmemo` | ArkTS, ArkUI | 移动设备 |
|
|
||||||
| HeurStudio | 计划中 | AI 辅助的单体单元集高级创建与编辑工具 | `top.pluv27.heurstudio` | C++, Qt6, Kirigami, PyOtherSide | 桌面 |
|
|
||||||
| HeurSync | 开发中 | 用户数据同步服务器<br/>集成 Web 前端与排行榜 | `heursync` | Go, SQL | 网页与服务器 |
|
|
||||||
| HeurRepo | 开发中 | 单元集文档源服务器<br/>与单元集分享平台 | `heurrepo` | Go, SQL | 网页与服务器 |
|
|
||||||
|
|
||||||
尽管现在后三样有点画大饼的意思, 但是我们的路线是明了的
|
|
||||||
@@ -2,46 +2,90 @@
|
|||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
"潜进" (HeurAMS: Heuristic Auxiliary Memorizing Scheduler, 启发式记忆辅助调度器) 是一种基于启发式算法与认知科学理论的辅助记忆调度器, 旨在帮助用户更高效地进行记忆工作与学习规划,
|
"潜进" (HeurAMS: Heuristic Auxiliary Memorizing Scheduler, 启发式记忆辅助调度器) 是一种基于启发式算法与认知科学理论的辅助记忆调度器, 旨在帮助用户更高效地进行记忆工作与学习规划,\
|
||||||
也是一种开放, 优雅, 易于扩展的间隔重复调度器实验平台, 旨在帮助研究者更高效地进行前沿记忆算法的调查实验与研究.
|
也是一种开放, 优雅, 易于扩展的间隔重复调度器实验平台, 旨在帮助研究者更高效地进行前沿记忆算法的研究.
|
||||||
|
|
||||||
[详细介绍](INTRODUCTION.md) [屏幕截图](SCREENSHOTS.md)
|
## 关于此仓库
|
||||||
|
|
||||||
<p align="left">
|
此仓库为 "潜进" 的核心程序库在 python 语言下的实现\
|
||||||
<a href="https://github.com/pluvium27/HeurAMS" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/GitHub-fafafa?style=for-the-badge&logo=github&logoColor=181717" alt="GitHub" /></a>
|
包含数据模型与框架, 并内置了基于 textual 框架的前端实现 (interface 子模块)\
|
||||||
<a href="https://invent.kde.org/pluv/HeurAMS" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/KDE_Invent-1D99F3?style=for-the-badge&logo=kde&logoColor=white" alt="KDE Invent" /></a>
|
除了通过内置前端进行学习外, 开发者也能在 python 环境中导入 `heurams` 库或使用 `RPC` 与 `heurams` 程序库实例通讯, 使用框架构建其他辅助记忆功能前端或其他应用程序
|
||||||
<a href="https://gitee.com/pluv/HeurAMS" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Gitee-C71D23?style=for-the-badge&logo=gitee&logoColor=white" alt="Gitee" /></a>
|
|
||||||
<a href="https://git.pluv27.top/pluv/HeurAMS" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/git.pluv27.top-609926?style=for-the-badge&logo=gitea&logoColor=white" alt="git.pluv27.top" /></a>
|
> [!NOTE]
|
||||||
</p>
|
> 我们已经着手于基于 KDE 用户界面框架 `Kirigami` 的现代跨平台前端开发, 称作 "KiriMemo", 包名是 "org.kde.kirimemo", 但其并非 KDE 项目\
|
||||||
|
> 它通过 `PyOtherSide` 直接复用 python 内核, 为 Windows, Linux, macOS, Android, iOS 和 Plasma Mobile 提供现代用户界面\
|
||||||
|
> 如果您善于开发 C++, QML, Qt 与 KDE 框架, 欢迎加入到 KiriMemo 项目的开发
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
### 间隔重复调度器
|
||||||
|
|
||||||
|
> 许多出版物都广泛讨论了不同重复间隔对学习效果的影响. 特别是, 间隔效应被认为是一种普遍现象. 间隔效应是指, 如果重复的间隔是分散/稀疏的, 而不是集中重复, 那么学习任务的表现会更好. 因此, 有观点提出, 学习中使用的最佳重复间隔是**最长的, 但不会导致遗忘的间隔**.
|
||||||
|
|
||||||
|
- 软件开箱即用, 无需多加配置即可使用默认的 `SM-2` 算法进行学习
|
||||||
|
- 此外, 算法模块是 "潜进" 内核 (heurams.kernel) 中的一等公民, 内核天然支持插拔各型算法
|
||||||
|
- 无需安装繁杂的插件即可分单元集完成算法快速切换与调优, 研究者可以方便地修改算法模块以便捷地进行研究与测试
|
||||||
|
- 默认使用 `SM-2` 简单间隔重复算法, 此算法亦用作 `Anki` 闪卡记忆软件的默认闪卡调度器
|
||||||
|
- 内置 `NSP-0` 筛选用非间隔重复算法以便快速筛选记忆内容, `FSRS` 先进间隔重复算法作为效率更高的调度器, 与 `SM-15M (移植自 sm.js 项目)` 复杂间隔重复算法(逆向工程)
|
||||||
|
- 算法模块可以标记记忆项目, 也可以动态规划每个记忆单元的记忆间隔时间表, 动态跟踪记忆反馈数据, 以优化长期记忆保留率与稳定性
|
||||||
|
- 得益于项目的模块化架构与单元集结构设计, 一个项目甚至可以与任意种算法共存并互通, 这对研究者及想探索/实验高效率方法的用户极其友好
|
||||||
|
|
||||||
|
### 多模态学习进程
|
||||||
|
|
||||||
|
与 Anki 的 SQLite `.apkg` 包不同, 我们坚持使用人类可读的文件夹组织单元集, 这带来了若干好处, 包括:
|
||||||
|
|
||||||
|
- 人类可读: 您可以用任意工具, 乃至一个记事本自由修改记忆载荷数据而无需打开软件
|
||||||
|
- 元数据配置: 配置自由度极高, 可以任意组合, 重造, 乃至创造新内容
|
||||||
|
- 测验, 算法与知识互相隔离: 一条知识不再是单一的闪卡, 不仅可以用若干不同的算法规划, 还可以用多种并行的谜题类型测验, 极大地提升学习效果和丰富度. 作为学习者, 您无需担忧概念复杂--仅需从云端下载单元集即可开箱即用上述特性!
|
||||||
|
- 多模态学习
|
||||||
|
- 软件自身集成了文本转语音 (TTS) , 音频与语言模型 (LLM) 模块, 这些功能乃至功能本身都是可插拔, 可扩展, 可切换驱动的, 这为内容创建了极大的丰富度
|
||||||
|
- 软件内置多种谜题类型, 包括选择题 (MCQ), 填空题 (Cloze) 与识别题 (Recognition), 您可在同一单元应用多种, 或是选择性启用
|
||||||
|
- 软件天然支持动态内容生成, 支持宏驱动的模板系统, 根据上下文乃至语言模型动态生成知识点的解析
|
||||||
|
- 在间隔重复研究尚被 SuperMemo 系列独占的时代, Wozniak 就早已表示 "如果不能理解知识, 就无需记忆它". 今天, 我们依然相信理解是记忆的基石
|
||||||
|
- 云同步与分享优化:
|
||||||
|
- 由于记忆数据和单元集文件都是文本文件, 故可进行快速的增量同步而无需完整地上传所有文件, 并且设计天然支持版本控制
|
||||||
|
- 如果您想分享单文件, 软件也支持导出为压缩包或合并成单文本文件以通过纯文本文件形式在 pastebin 等平台分享
|
||||||
|
- 性能提升: 得益于现代且支持分块的文件组织结构, 潜进能在保持高自由度的同时仅使用 python 就能达到敏捷且低占用的用户体验
|
||||||
|
- AI 辅助友好: 想象您有一些 .apkg 牌组或一大段教材内容, 您可以方便且高效率地使用 AI 工具创建可在 HeurAMS 使用的单元集
|
||||||
|
|
||||||
|
### 内置实用用户界面
|
||||||
|
|
||||||
|
尽管不是唯一前端, 但响应式 Textual 框架构建的内置终端用户界面在多种场景下仍具有独特优势:
|
||||||
|
|
||||||
|
- 跨平台, 并支持触屏/鼠标/键盘多操作模式
|
||||||
|
- 与几乎所有现代终端模拟器相容
|
||||||
|
- 对于<a href="https://www.arewesixelyet.com/" target="_blank" rel="noopener noreferrer">支持 sixel 协议的终端模拟器</a>, 可高清显示图像内容
|
||||||
|
- 对于不支持 sixel 协议的终端模拟器, 也支持图片低清的兼容显示模式
|
||||||
|
- 可通过 textual-web 作为服务部署, 并在任意浏览器使用
|
||||||
|
- 简洁直观, 键盘友好, 全功能且高效率的用户界面设计
|
||||||
|
- 易于嵌入: 可在 getty/kmscon 中运行而无需任何桌面图形服务
|
||||||
|
- 资源占用小, 运行流畅, 不拖泥带水
|
||||||
|
- 便于测试与调试程序库
|
||||||
|
|
||||||
|
[查看屏幕截图](SCREENSHOTS.md)
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
### 从包管理器安装
|
### 从包管理器安装
|
||||||
|
|
||||||
潜进 (包名是 `heurams`) 处于早期开发考虑, 尚未上架 PyPI, 但您可以用 pip 支持的 git 协议安装稳定版和开发版本, 这要求您的电脑上安装了 python 环境 (建议 3.12.13 及之后版本).
|
潜进 (包名是 `heurams`) 处于早期开发考虑, 尚未上架 PyPI, 但您可以用我们的基础设施安装稳定版和开发版本.
|
||||||
|
|
||||||
#### 面向用户的安装
|
#### 稳定版本
|
||||||
|
|
||||||
从稳定的 `master` 分支安装, 并安装适用于用户体验的可选依赖(推荐):
|
安装适用于用户体验的可选依赖(推荐):
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install --upgrade 'heurams[basic] @ https://git.pluv27.top/pluv/HeurAMS/archive/master.zip'
|
python -m pip install heurams[basic] -i https://pypi.pluv27.top/root/stable/+simple/
|
||||||
```
|
|
||||||
|
|
||||||
从较前沿, 大致稳定的 `dev` 分支安装, 并安装适用于用户体验的可选依赖(如果您追求较前沿的改进):
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install --force-reinstall --no-deps 'heurams[basic] @ https://git.pluv27.top/pluv/HeurAMS/archive/dev.zip'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
安装适用于一般计算机的通用音频模块(基于 playsound3):\
|
安装适用于一般计算机的通用音频模块(基于 playsound3):\
|
||||||
(此项不适用于 termux 环境, termux 的音频支持是内建的)
|
(此项不适用于 termux 环境, termux 的音频支持是内建的)
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install --upgrade 'heurams[audio-playsound] @ https://git.pluv27.top/pluv/HeurAMS/archive/master.zip'
|
python -m pip install heurams[audio-playsound] -i https://pypi.pluv27.top/root/stable/+simple/
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 面向开发者的安装
|
#### 开发版本
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> 对于部分 Linux 发行版和 Android Termux 用户:\
|
> 对于部分 Linux 发行版和 Android Termux 用户:\
|
||||||
@@ -49,15 +93,27 @@ pip install --upgrade 'heurams[audio-playsound] @ https://git.pluv27.top/pluv/He
|
|||||||
> 例如在 termux 上先运行 `pkg install cmake clang libzmq`.\
|
> 例如在 termux 上先运行 `pkg install cmake clang libzmq`.\
|
||||||
> 项目功能本身不依赖它, 但需要该依赖用于启动可选的调试服务器.
|
> 项目功能本身不依赖它, 但需要该依赖用于启动可选的调试服务器.
|
||||||
|
|
||||||
从 `dev` 分支进行基于 git 的可编辑安装, 并安装全部可选依赖(推荐):
|
安装全部可选依赖(推荐):
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install --force-reinstall --no-deps 'heurams[all] @ https://git.pluv27.top/pluv/HeurAMS/archive/dev.zip'
|
python -m pip install heurams[all] -i https://pypi.pluv27.top/root/dev/+simple/
|
||||||
```
|
```
|
||||||
|
|
||||||
> 您也可以从 `refactor/...` 等特定分支安装
|
#### 依赖组说明
|
||||||
|
|
||||||
[依赖组说明](INTRODUCTION.md#包依赖组说明)
|
由于部分依赖只被少数功能需要, 所以我们把可选依赖分得比较细, 前面提供的命令会安装部分可选依赖, 以下是依赖组列表:
|
||||||
|
|
||||||
|
| 依赖组 | 包含模块 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| 最小化安装 | tabulate, toml, transitions | 核心驱动程序库, 始终必需 |
|
||||||
|
| interface | textual, psutil | 基本用户界面依赖 |
|
||||||
|
| algo-fsrs | fsrs | FSRS 算法模块 |
|
||||||
|
| tts-edgetts | edge-tts | 微软文本转语音 |
|
||||||
|
| llm | llms-py | API 调用 |
|
||||||
|
| audio-playsound | playsound3 | 通用音频模块 |
|
||||||
|
| dev | zmq, pytest, pytest-cov | 开发调试与测试工具 |
|
||||||
|
| basic | [tts-edgetts], [llm], [algo-fsrs] | 适用于用户体验的较轻依赖组(推荐) |
|
||||||
|
| all | 以上所有依赖 | 完整安装组 |
|
||||||
|
|
||||||
### 从源码安装
|
### 从源码安装
|
||||||
|
|
||||||
@@ -80,17 +136,15 @@ pip install --force-reinstall --no-deps 'heurams[all] @ https://git.pluv27.top/p
|
|||||||
|
|
||||||
## 项目标识
|
## 项目标识
|
||||||
|
|
||||||
HeurAMS 项目标识如下, 文件(位图和矢量图)位于 `./src/heurams/assets/art/` 目录.
|
HeurAMS 项目标识如下, 矢量图文件位于 `./src/heurams/assets/art/` 目录.
|
||||||
|
|
||||||
<img src="src/heurams/assets/art/banner128-light.png" height="96px" title="位图横幅(不透明)">
|
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 5px;">
|
<div style="display: flex; flex-wrap: wrap; gap: 5px;">
|
||||||
<img src="src/heurams/assets/art/logo.svg" height="96px" title="矢量图标">
|
<img src="src/heurams/assets/art/logo.svg" height="96px">
|
||||||
<img src="src/heurams/assets/art/logo-mono-light.svg" height="96px" title="单色明亮矢量图标">
|
<img src="src/heurams/assets/art/logo-mono-light.svg" height="96px">
|
||||||
<img src="src/heurams/assets/art/logo-mono-dark.svg" height="96px" title="单色暗色矢量图标">
|
<img src="src/heurams/assets/art/logo-mono-dark.svg" height="96px">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
颜色分别是: `#1660A5 (海蓝色)` `#545F70 (蓝灰色)` `#FFFFFF (单色明亮图标白色)` `#1A1A1A (单色暗色图标深黑色)` `#2f2f35 (文字颜色)`.
|
颜色分别是: `#1660A5 (海蓝色)` `#545F70 (蓝灰色)` `#1A1A1A (暗色单色图标深黑色)` `#FFFFFF (明亮单色图标白色)`.
|
||||||
|
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
@@ -106,7 +160,7 @@ HeurAMS 项目标识如下, 文件(位图和矢量图)位于 `./src/heurams/asse
|
|||||||
|
|
||||||
#### SM.js (slaypni)
|
#### SM.js (slaypni)
|
||||||
|
|
||||||
- 上游版本: commit `6e3bb4a` (2015年2月4日上游已停止维护)
|
- 上游版本: commit `6e3bb4afaf484426deb4a9fa3bcffe42ac066b45` (2015年2月4日上游已停止维护)
|
||||||
- 引用方式: 将 coffeescript 重写为 python 并间接引用, 数学原理一致; 并对重写后代码进行逻辑, 性能与标准化 API 改进
|
- 引用方式: 将 coffeescript 重写为 python 并间接引用, 数学原理一致; 并对重写后代码进行逻辑, 性能与标准化 API 改进
|
||||||
- 位置: `src/heurams/kernel/algorithms/sm15m*.py`
|
- 位置: `src/heurams/kernel/algorithms/sm15m*.py`
|
||||||
- 原项目: [SM.js](https://github.com/slaypni/SM-15)
|
- 原项目: [SM.js](https://github.com/slaypni/SM-15)
|
||||||
|
|||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "heurams"
|
name = "heurams"
|
||||||
version = "0.5.1"
|
version = "0.5.0"
|
||||||
authors = [{ name = "Wang Zhiyu", email = "pluvium27@outlook.com" }]
|
authors = [{ name = "pluvium27", email = "pluvium27@outlook.com" }]
|
||||||
description = "Heuristic Auxiliary Memory Scheduler"
|
description = "Heuristic Auxiliary Memory Scheduler"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
@@ -13,7 +13,6 @@ license = "AGPL-3.0-or-later"
|
|||||||
license-files = ["LICENSE"]
|
license-files = ["LICENSE"]
|
||||||
|
|
||||||
dependencies = [ # 这些依赖只能驱动 kernel 程序库
|
dependencies = [ # 这些依赖只能驱动 kernel 程序库
|
||||||
"click>=8.3.3",
|
|
||||||
"tabulate>=0.10.0",
|
"tabulate>=0.10.0",
|
||||||
"toml>=0.10.2",
|
"toml>=0.10.2",
|
||||||
"transitions>=0.9.3",
|
"transitions>=0.9.3",
|
||||||
@@ -61,6 +60,7 @@ default = true
|
|||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
heurams = "heurams.__main__:main"
|
heurams = "heurams.__main__:main"
|
||||||
|
heurams-tui = "heurams.interface.__main__:main"
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
@@ -72,5 +72,5 @@ markers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["uv_build>=0.7.19"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "uv_build"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#print("欢迎使用 HeurAMS 及其组件!")
|
print("欢迎使用 HeurAMS 及其组件!")
|
||||||
|
|
||||||
# 补充日志记录
|
# 补充日志记录
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|||||||
+16
-57
@@ -1,63 +1,22 @@
|
|||||||
import platform
|
import heurams.services.version as ver
|
||||||
|
|
||||||
import click
|
|
||||||
from heurams.services.version import ver, stage, codename, codename_cn
|
|
||||||
|
|
||||||
|
|
||||||
@click.group(
|
|
||||||
invoke_without_command=True,
|
|
||||||
help=(
|
|
||||||
f"HeurAMS {ver} - 启发式辅助记忆调度器"
|
|
||||||
),
|
|
||||||
context_settings={"help_option_names": ["-h", "--help"]},
|
|
||||||
)
|
|
||||||
@click.version_option(
|
|
||||||
ver, "-v", "--version",
|
|
||||||
prog_name="HeurAMS",
|
|
||||||
message=f"%(prog)s %(version)s {stage} ({codename}/{codename_cn}), {platform.system()}",
|
|
||||||
)
|
|
||||||
@click.pass_context
|
|
||||||
def cli(ctx):
|
|
||||||
if ctx.invoked_subcommand is None:
|
|
||||||
click.echo(cli.get_help(ctx))
|
|
||||||
ctx.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
def tui():
|
|
||||||
"""启动内置基本用户界面 (TUI)"""
|
|
||||||
import heurams.interface.__main__ as tui_module
|
|
||||||
|
|
||||||
tui_module.main()
|
|
||||||
|
|
||||||
|
|
||||||
def _print_version():
|
|
||||||
click.echo(
|
|
||||||
f"HeurAMS {ver} ({codename}/{codename_cn}), 阶段: {stage}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
def version():
|
|
||||||
"""输出版本信息"""
|
|
||||||
_print_version()
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(name="ver", hidden=True)
|
|
||||||
def ver_cmd():
|
|
||||||
"""输出版本信息"""
|
|
||||||
_print_version()
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(name="help")
|
|
||||||
@click.pass_context
|
|
||||||
def help_cmd(ctx):
|
|
||||||
"""显示此帮助信息"""
|
|
||||||
click.echo(cli.get_help(ctx.parent))
|
|
||||||
|
|
||||||
|
|
||||||
|
# __main__.py
|
||||||
def main():
|
def main():
|
||||||
cli()
|
prompt = f"""HeurAMS {ver.ver} 已经被成功地安装在系统中.
|
||||||
|
HeurAMS 被设计为一个带有辅助记忆调度器功能的软件包, 无法直接被执行, 但可被其他 Python 程序调用.
|
||||||
|
若您想启动内置的基本用户界面:
|
||||||
|
请运行 python -m heurams.interface,
|
||||||
|
或者 python -m heurams.interface.__main__
|
||||||
|
python 代指您使用的解释器, 在某些发行版中可能是 python3, 而 python 命令被指向了 python2.
|
||||||
|
尽管项目保留了 requirements.txt, 我们仍不推荐使用系统 python 和原始 venv 进行开发.
|
||||||
|
项目的推荐开发环境工具是 uv.
|
||||||
|
如果你的环境已经安装了 uv:
|
||||||
|
先运行 uv sync --all-extras 同步环境, 此命令只需要执行一遍, uv 会自动处理依赖.
|
||||||
|
然后通过运行 uv run heurams-tui 启动内置基本用户界面.
|
||||||
|
此时您的解释器在项目目录里的 .venv/bin 中, 使用 IDE 开发前, 务必切换解释器!
|
||||||
|
注意: 一个常见的误区是, 执行 interface 下的 __main__.py 运行基本用户界面, 这会导致 Python 上下文环境异常, 请不要这样做."""
|
||||||
|
print(prompt)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ class HeurAMSApp(App):
|
|||||||
) -> None: # 用来给没使用/禁用的快捷键占位, 因为 Binding 删除不了
|
) -> None: # 用来给没使用/禁用的快捷键占位, 因为 Binding 删除不了
|
||||||
pass
|
pass
|
||||||
|
|
||||||
'''
|
# 移除烦人的 "rich traceback"
|
||||||
# 移除烦人的 "rich traceback", 但可能导致未定义行为出现, 所以注释掉
|
# Textual 官方不会管这破事, 写 Rich 写入脑了导致的
|
||||||
|
# 不知道哪来的自信改标准库的 traceback
|
||||||
# https://github.com/Textualize/textual/discussions/6255
|
# https://github.com/Textualize/textual/discussions/6255
|
||||||
# NOTE: 进行 textual 版本升级时, 确保查看过上游代码, 尤其是 App 的 _exception
|
# NOTE: 进行 textual 版本升级时, 确保查看过上游代码, 尤其是 App 的 _exception
|
||||||
# 如果行为变了就把下面的删了 (虽然有 fallback)
|
# 如果行为变了就把下面的删了 (虽然有 fallback)
|
||||||
@@ -88,4 +89,3 @@ class HeurAMSApp(App):
|
|||||||
self._close_messages_no_wait()
|
self._close_messages_no_wait()
|
||||||
raise self._exception
|
raise self._exception
|
||||||
super().panic(*args) # ditto
|
super().panic(*args) # ditto
|
||||||
'''
|
|
||||||
@@ -2,13 +2,13 @@ from heurams.interface import *
|
|||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
import threading
|
import threading
|
||||||
|
import zmq
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def start_debug_server(app):
|
def start_debug_server(app):
|
||||||
import zmq
|
|
||||||
logger = get_logger("zmq_debug")
|
logger = get_logger("zmq_debug")
|
||||||
context = zmq.Context()
|
context = zmq.Context()
|
||||||
socket = context.socket(zmq.REP)
|
socket = context.socket(zmq.REP)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
import playsound3
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
@@ -13,7 +15,6 @@ logger = get_logger(__name__)
|
|||||||
def play_by_path(path: pathlib.Path):
|
def play_by_path(path: pathlib.Path):
|
||||||
logger.debug("playsound_audio.play_by_path: 开始播放 %s", path)
|
logger.debug("playsound_audio.play_by_path: 开始播放 %s", path)
|
||||||
try:
|
try:
|
||||||
import playsound3
|
|
||||||
playsound3.playsound(str(path))
|
playsound3.playsound(str(path))
|
||||||
logger.debug("播放完成: %s", path)
|
logger.debug("播放完成: %s", path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ logger = get_logger(__name__)
|
|||||||
|
|
||||||
# from .protocol import PlayFunctionProtocol
|
# from .protocol import PlayFunctionProtocol
|
||||||
|
|
||||||
|
|
||||||
def play_by_path(path: pathlib.Path):
|
def play_by_path(path: pathlib.Path):
|
||||||
logger.debug("termux_audio.play_by_path: 开始播放 %s", path)
|
logger.debug("termux_audio.play_by_path: 开始播放 %s", path)
|
||||||
try:
|
try:
|
||||||
os.system(f"play-audio {path.resolve()}")
|
os.system(f"play-audio {path}")
|
||||||
logger.debug("播放命令已执行: %s", path)
|
logger.debug("播放命令已执行: %s", path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("播放失败: %s, 错误: %s", path, e)
|
logger.error("播放失败: %s, 错误: %s", path, e)
|
||||||
|
raise
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
ver = "0.5.1"
|
ver = "0.5.0"
|
||||||
stage = "stable"
|
stage = "stable"
|
||||||
codename = "fulcrum"
|
codename = "fulcrum"
|
||||||
codename_cn = "支点"
|
codename_cn = "支点"
|
||||||
|
|||||||
@@ -184,18 +184,6 @@ wheels = [
|
|||||||
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "click"
|
|
||||||
version = "8.3.3"
|
|
||||||
source = { registry = "https://mirrors.ustc.edu.cn/pypi/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://mirrors.ustc.edu.cn/pypi/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -407,10 +395,9 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heurams"
|
name = "heurams"
|
||||||
version = "0.5.1"
|
version = "0.5.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
|
||||||
{ name = "tabulate" },
|
{ name = "tabulate" },
|
||||||
{ name = "toml" },
|
{ name = "toml" },
|
||||||
{ name = "transitions" },
|
{ name = "transitions" },
|
||||||
@@ -455,7 +442,6 @@ tts-edgetts = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "click", specifier = ">=8.3.3" },
|
|
||||||
{ name = "edge-tts", marker = "extra == 'tts-edgetts'", specifier = ">=7.2.8" },
|
{ name = "edge-tts", marker = "extra == 'tts-edgetts'", specifier = ">=7.2.8" },
|
||||||
{ name = "fsrs", marker = "extra == 'algo-fsrs'", specifier = ">=6.3.1" },
|
{ name = "fsrs", marker = "extra == 'algo-fsrs'", specifier = ">=6.3.1" },
|
||||||
{ name = "heurams", extras = ["algo-fsrs"], marker = "extra == 'all'" },
|
{ name = "heurams", extras = ["algo-fsrs"], marker = "extra == 'all'" },
|
||||||
|
|||||||
Reference in New Issue
Block a user