mirror of
https://github.com/insile/OpenWords.git
synced 2026-07-22 05:48:39 +00:00
2026/3/19
This commit is contained in:
parent
39de9cfc20
commit
664c98c454
13 changed files with 277 additions and 198 deletions
|
|
@ -86,6 +86,10 @@
|
|||
|
||||

|
||||
|
||||
为当前活动页面选中的单词添加双链, 命令调用或者右键菜单
|
||||
|
||||

|
||||
|
||||
### 恢复默认设置
|
||||
|
||||
重置所有设置选项为默认值, 请谨慎使用
|
||||
|
|
|
|||
BIN
README/13.gif
Normal file
BIN
README/13.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "openwords",
|
||||
"name": "OpenWords",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"minAppVersion": "1.10.0",
|
||||
"description": "用于英语学习中背单词与单词管理的插件",
|
||||
"author": "insile",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "openwords",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openwords",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.11",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"obsidian": "latest",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "openwords",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"description": "用于英语学习中背单词与单词管理的插件",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
|
|
|
|||
195
src/main.ts
195
src/main.ts
|
|
@ -5,13 +5,14 @@ import { MainView, MAIN_VIEW, PageType } from './views/MainView';
|
|||
import { CardInfo } from './utils/Card';
|
||||
import { supermemo, SuperMemoGrade } from 'supermemo';
|
||||
import posTagger from 'wink-pos-tagger';
|
||||
import { asArray, asNumber, asDateString } from 'utils/Converters';
|
||||
|
||||
|
||||
// 插件主类
|
||||
export default class OpenWords extends Plugin {
|
||||
settings: OpenWordsSettings; // 插件设置
|
||||
settingsSnapshot: OpenWordsSettings; // 插件设置快照
|
||||
tagger: posTagger = new posTagger(); // 词性标注器实例
|
||||
tagger: posTagger = new posTagger(); // 词性标注器实例
|
||||
allCards: Map<string, CardInfo> = new Map(); // 所有单词
|
||||
masterCards: Map<string, CardInfo> = new Map(); // 掌握单词
|
||||
enabledCards: Map<string, CardInfo> = new Map(); // 启用单词
|
||||
|
|
@ -26,6 +27,7 @@ export default class OpenWords extends Plugin {
|
|||
// 加载插件设置参数, 设置选项卡, 左侧工具栏按钮, 注册主视图
|
||||
await this.loadSettings();
|
||||
this.addSettingTab(new OpenWordsSettingTab(this.app, this));
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case
|
||||
this.addRibbonIcon('slack', 'OpenWords', async () => { await this.activateView(); });
|
||||
this.registerView(MAIN_VIEW, (leaf) => new MainView(leaf, this));
|
||||
|
||||
|
|
@ -35,18 +37,43 @@ export default class OpenWords extends Plugin {
|
|||
this.addCommand({
|
||||
id: 'LearningTypeModal',
|
||||
name: '学习视图',
|
||||
callback: () => {
|
||||
this.activateView();
|
||||
callback: async () => {
|
||||
await this.activateView();
|
||||
}
|
||||
});
|
||||
// 添加双链命令
|
||||
this.addCommand({
|
||||
id: 'addDoubleBrackets',
|
||||
name: '添加双链',
|
||||
editorCallback: () => {
|
||||
this.addDoubleBrackets();
|
||||
name: '文档双链',
|
||||
editorCallback: async () => {
|
||||
await this.addDoubleBrackets();
|
||||
}
|
||||
});
|
||||
// 添加单词双链命令
|
||||
this.addCommand({
|
||||
id: 'addDoubleBracketsWord',
|
||||
name: '单词双链',
|
||||
editorCallback: async () => {
|
||||
await this.autoDoubleLinkWord();
|
||||
}
|
||||
});
|
||||
// 注册编辑器菜单
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("editor-menu", (menu, editor, view) => {
|
||||
const selection = editor.getSelection().trim();
|
||||
if (!selection) return;
|
||||
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(`链接 "${selection}" 到单词库`)
|
||||
.setIcon("link")
|
||||
.onClick(async () => {
|
||||
// 执行上面提到的搜索和替换逻辑
|
||||
await this.autoDoubleLinkWord();
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
// 注册文件监听器
|
||||
this.registerFileWatchers();
|
||||
// 扫描所有单词文件, 更新状态栏
|
||||
|
|
@ -75,13 +102,13 @@ export default class OpenWords extends Plugin {
|
|||
// 加载单词元数据
|
||||
async loadWordMetadata(file: TFile, single = true) {
|
||||
const allCards = this.allCards;
|
||||
const masterCards = this.masterCards;
|
||||
const masterCards = this.masterCards;
|
||||
const enabledCards = this.enabledCards;
|
||||
const newCards = this.newCards;
|
||||
const dueCards = this.dueCards;
|
||||
|
||||
// 先删除旧的单词元数据
|
||||
masterCards.delete(file.basename);
|
||||
masterCards.delete(file.basename);
|
||||
enabledCards.delete(file.basename);
|
||||
newCards.delete(file.basename);
|
||||
dueCards.delete(file.basename);
|
||||
|
|
@ -95,45 +122,31 @@ export default class OpenWords extends Plugin {
|
|||
allCards.delete(file.basename);
|
||||
if (single) { this.updateStatusBar(); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const tags: string[] = frontMatter.tags || [];
|
||||
// 解析 FrontMatter 中的单词属性
|
||||
const tags: string[] = asArray(frontMatter.tags);
|
||||
const isMastered = frontMatter["掌握"] === true;
|
||||
const isTagged = tags.some(tag => this.settings.enabledTags.includes(tag));
|
||||
|
||||
const card: CardInfo = {
|
||||
front: file.basename,
|
||||
path: file.path,
|
||||
dueDate: frontMatter["到期日"],
|
||||
interval: frontMatter["间隔"],
|
||||
efactor: frontMatter["易记因子"] * 0.01,
|
||||
repetition: frontMatter["重复次数"],
|
||||
dueDate: asDateString(frontMatter["到期日"]),
|
||||
interval: asNumber(frontMatter["间隔"], 0),
|
||||
efactor: asNumber(frontMatter["易记因子"], 250) * 0.01,
|
||||
repetition: asNumber(frontMatter["重复次数"], 0),
|
||||
isMastered: isMastered,
|
||||
};
|
||||
if (
|
||||
!card.front ||
|
||||
!card.path ||
|
||||
card.dueDate === undefined ||
|
||||
card.interval === undefined ||
|
||||
card.efactor === undefined ||
|
||||
card.repetition === undefined ||
|
||||
Number.isNaN(card.efactor) ||
|
||||
Number.isNaN(card.interval) ||
|
||||
Number.isNaN(card.repetition)
|
||||
) {
|
||||
allCards.delete(file.basename);
|
||||
if (single) { this.updateStatusBar(); }
|
||||
return;
|
||||
|
||||
// 更新所有单词
|
||||
allCards.set(card.front, card);
|
||||
|
||||
// 更新掌握单词
|
||||
if (isMastered) {
|
||||
masterCards.set(card.front, card);
|
||||
}
|
||||
|
||||
// 更新所有单词
|
||||
allCards.set(card.front, card);
|
||||
|
||||
// 更新掌握单词
|
||||
if (isMastered) {
|
||||
masterCards.set(card.front, card);
|
||||
}
|
||||
|
||||
// 更新启用单词, 新单词, 旧单词
|
||||
if (isTagged && !isMastered) {
|
||||
enabledCards.set(card.front, card);
|
||||
|
|
@ -151,19 +164,19 @@ export default class OpenWords extends Plugin {
|
|||
// 扫描所有单词文件
|
||||
async scanAllNotes() {
|
||||
this.allCards.clear();
|
||||
this.masterCards.clear();
|
||||
this.masterCards.clear();
|
||||
this.enabledCards.clear();
|
||||
this.newCards.clear();
|
||||
this.dueCards.clear();
|
||||
const normalized = this.settings.folderPath.endsWith("/")
|
||||
? this.settings.folderPath
|
||||
: this.settings.folderPath + "/";
|
||||
const normalized = this.settings.folderPath.endsWith("/")
|
||||
? this.settings.folderPath
|
||||
: this.settings.folderPath + "/";
|
||||
const files = this.app.vault.getMarkdownFiles();
|
||||
const filteredFiles = files.filter(file => file.path.startsWith(normalized));
|
||||
|
||||
if (filteredFiles.length === 0) {
|
||||
new Notice('指定的文件夹下没有 Markdown 文件!');
|
||||
this.updateStatusBar()
|
||||
this.updateStatusBar()
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -186,28 +199,38 @@ export default class OpenWords extends Plugin {
|
|||
const newDate = window.moment().add(result.interval, 'day').format('YYYY-MM-DD');
|
||||
const file = this.app.vault.getFileByPath(card.path);
|
||||
|
||||
if (!file) {
|
||||
if (!file) {
|
||||
new Notice(`文件 ${card.path} 不存在!`);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||
await this.app.fileManager.processFrontMatter(file, (frontMatter: Record<string, unknown>) => {
|
||||
frontMatter["到期日"] = newDate;
|
||||
frontMatter["间隔"] = result.interval;
|
||||
frontMatter["易记因子"] = Math.round(result.efactor * 100);
|
||||
frontMatter["重复次数"] = result.repetition;
|
||||
});
|
||||
|
||||
new Notice(`${card.front} \n易记因子: ${result.efactor.toFixed(2)} \n重复次数: ${result.repetition} \n间隔: ${result.interval} \n到期日: ${newDate}`);
|
||||
new Notice(`${card.front} \n易记因子: ${result.efactor.toFixed(2)} \n重复次数: ${result.repetition} \n间隔: ${result.interval} \n到期日: ${newDate}`);
|
||||
|
||||
// 等待元数据缓存更新(最多等待 1 秒)
|
||||
let attempts = 0;
|
||||
while (attempts < 5) {
|
||||
const updated = this.app.metadataCache.getFileCache(file);
|
||||
if (updated?.frontmatter?.["到期日"] === newDate) {
|
||||
break; // 缓存已更新
|
||||
while (attempts < 20) {
|
||||
if (mode === 'new') {
|
||||
if (grade >= 3 && this.dueCards.has(card.front)) {
|
||||
break; // 以移出新单词池
|
||||
} else if (grade < 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
else {
|
||||
if (grade < 3 && this.newCards.has(card.front)) {
|
||||
break; // 以移出旧单词池
|
||||
} else if (grade >= 3 && this.dueCards.get(card.front)?.efactor === result.efactor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,10 +241,10 @@ export default class OpenWords extends Plugin {
|
|||
new Notice("没有单词需要重置!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// // 注销监听器,避免重置过程中触发大量缓存更新事件
|
||||
// this.unregisterFileWatchers();
|
||||
|
||||
|
||||
const notice = new Notice('重置中...', 0); // 创建一个持续显示的 Notice
|
||||
let count = 0; // 计数器
|
||||
const cardList = Array.from(this.enabledCards.values()); // 将 Map 转换为数组
|
||||
|
|
@ -229,7 +252,7 @@ export default class OpenWords extends Plugin {
|
|||
const concurrency = 10; // 并发处理数量
|
||||
const todayDate = window.moment().format('YYYY-MM-DD'); // 提前计算日期,避免重复计算
|
||||
let updateFrequency = Math.max(1, Math.floor(total / 50)); // 减少更新频率到 2%
|
||||
|
||||
|
||||
// 创建并发处理任务
|
||||
const processTasks = async () => {
|
||||
for (let i = 0; i < cardList.length; i += concurrency) {
|
||||
|
|
@ -237,7 +260,7 @@ export default class OpenWords extends Plugin {
|
|||
await Promise.all(chunk.map(async (card) => {
|
||||
const file = this.app.vault.getFileByPath(card.path);
|
||||
if (file) {
|
||||
await this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||
await this.app.fileManager.processFrontMatter(file, (frontMatter: Record<string, unknown>) => {
|
||||
frontMatter["到期日"] = todayDate;
|
||||
frontMatter["间隔"] = 0;
|
||||
frontMatter["易记因子"] = 250;
|
||||
|
|
@ -257,7 +280,8 @@ export default class OpenWords extends Plugin {
|
|||
await processTasks();
|
||||
notice.setMessage(`重置完成!共 ${count} 个单词`);
|
||||
} catch (error) {
|
||||
new Notice(`重置出错: ${error}`);
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
new Notice(`重置出错: ${message}`);
|
||||
console.error('Reset card error:', error);
|
||||
} finally {
|
||||
// // 重新注册监听器
|
||||
|
|
@ -268,7 +292,7 @@ export default class OpenWords extends Plugin {
|
|||
setTimeout(() => notice.hide(), 2000); // 2 秒后自动隐藏 Notice
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 建立单词双链
|
||||
async addDoubleBrackets() {
|
||||
const unmasteredWords = new Set(Array.from(this.enabledCards.values())
|
||||
|
|
@ -307,7 +331,7 @@ export default class OpenWords extends Plugin {
|
|||
return word; // 否则返回原始单词
|
||||
}
|
||||
});
|
||||
const updatedContent = updatedWords.join('');
|
||||
const updatedContent = updatedWords.join('');
|
||||
|
||||
doc.setValue(updatedContent); // 用更新后的内容替换原文
|
||||
new Notice('已添加双链!');
|
||||
|
|
@ -317,7 +341,7 @@ export default class OpenWords extends Plugin {
|
|||
// 生成单词索引 .base 文件
|
||||
async generateIndex() {
|
||||
const notice = new Notice('索引中...', 0);
|
||||
|
||||
|
||||
// 从设置读取标签并转换为配置对象(将多级标签的 '/' 替换为 '.')
|
||||
const levelConfigs = this.settings.enabledTags.map(tag => {
|
||||
const name = tag.replace(/\//g, '.');
|
||||
|
|
@ -352,10 +376,11 @@ export default class OpenWords extends Plugin {
|
|||
const frontMatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
|
||||
if (!frontMatter) return;
|
||||
|
||||
const tags: string[] = frontMatter.tags || [];
|
||||
const tags: string[] = asArray(frontMatter.tags);
|
||||
for (const level of levelConfigs) {
|
||||
if (tags.includes(level.tag)) {
|
||||
levelWordCounts[level.tag] = (levelWordCounts[level.tag] ?? 0) + 1; }
|
||||
levelWordCounts[level.tag] = (levelWordCounts[level.tag] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -409,8 +434,8 @@ views:
|
|||
- 间隔
|
||||
- 到期日
|
||||
${Array.from({ length: 26 }, (_, i) => {
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
return ` - type: table
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
return ` - type: table
|
||||
name: 总计.${letter}
|
||||
filters:
|
||||
and:
|
||||
|
|
@ -422,7 +447,7 @@ ${Array.from({ length: 26 }, (_, i) => {
|
|||
- 重复次数
|
||||
- 间隔
|
||||
- 到期日`;
|
||||
}).join('\n')}
|
||||
}).join('\n')}
|
||||
`;
|
||||
await this.writeFile(allBasePath, allBaseContent);
|
||||
|
||||
|
|
@ -446,8 +471,8 @@ views:
|
|||
- 间隔
|
||||
- 到期日
|
||||
${Array.from({ length: 26 }, (_, i) => {
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
return ` - type: table
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
return ` - type: table
|
||||
name: ${level.name.split('.').pop()}.${letter}
|
||||
filters:
|
||||
and:
|
||||
|
|
@ -459,7 +484,7 @@ ${Array.from({ length: 26 }, (_, i) => {
|
|||
- 重复次数
|
||||
- 间隔
|
||||
- 到期日`;
|
||||
}).join('\n')}
|
||||
}).join('\n')}
|
||||
`;
|
||||
|
||||
await this.writeFile(basePath, baseContent);
|
||||
|
|
@ -598,12 +623,36 @@ ${tagFilters}
|
|||
await this.writeFile(basePath, baseContent);
|
||||
}
|
||||
|
||||
// 自动为选中单词添加双链
|
||||
async autoDoubleLinkWord() {
|
||||
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (!activeView) {
|
||||
new Notice("请在 Markdown 编辑器中使用此命令");
|
||||
return;
|
||||
}
|
||||
const editor = activeView.editor;
|
||||
const selection = editor.getSelection().trim();
|
||||
if (!selection) {
|
||||
new Notice("请先选中一个单词");
|
||||
return;
|
||||
}
|
||||
const word = this.tagger.tagSentence(selection.toLowerCase())[0];
|
||||
const lemma = word?.lemma ?? selection.toLowerCase();
|
||||
if (this.allCards.has(lemma)) {
|
||||
editor.replaceSelection(`[[${lemma}|${selection}]]`);
|
||||
new Notice(`已成功链接到: ${selection}`);
|
||||
} else {
|
||||
// 4. 如果文件不存在,可以提示或提供创建选项
|
||||
new Notice(`单词库中未找到 "${selection}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新状态栏
|
||||
updateStatusBar(){
|
||||
updateStatusBar() {
|
||||
this.app.workspace.getLeavesOfType(MAIN_VIEW).forEach(leaf => {
|
||||
const view = leaf.view;
|
||||
if (view instanceof MainView) {
|
||||
void view.updateStatusBar();
|
||||
view.updateStatusBar();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -619,9 +668,9 @@ ${tagFilters}
|
|||
: this.settings.folderPath + "/";
|
||||
|
||||
// 监听单词文件创建事件
|
||||
const createRef = this.app.vault.on("create", (file: TFile) => {
|
||||
const createRef = this.app.vault.on("create", async (file: TFile) => {
|
||||
if (file.path.endsWith(".md") && file.path.startsWith(this.currentFolderPath)) {
|
||||
this.loadWordMetadata(file);
|
||||
await this.loadWordMetadata(file);
|
||||
}
|
||||
});
|
||||
this.fileWatcherRefs.push(createRef);
|
||||
|
|
@ -640,9 +689,9 @@ ${tagFilters}
|
|||
this.fileWatcherRefs.push(deleteRef);
|
||||
|
||||
// 监听单词文件缓存修改事件
|
||||
const changedRef = this.app.metadataCache.on("changed", (file) => {
|
||||
const changedRef = this.app.metadataCache.on("changed", async (file) => {
|
||||
if (file.path.endsWith(".md") && file.path.startsWith(this.currentFolderPath)) {
|
||||
this.loadWordMetadata(file);
|
||||
await this.loadWordMetadata(file);
|
||||
}
|
||||
});
|
||||
this.fileWatcherRefs.push(changedRef);
|
||||
|
|
@ -662,13 +711,13 @@ ${tagFilters}
|
|||
this.registerFileWatchers();
|
||||
}
|
||||
|
||||
onunload() {
|
||||
onunload() {
|
||||
// 清理文件监听器
|
||||
this.unregisterFileWatchers();
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<OpenWordsSettings>);
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Notice, PluginSettingTab, Setting, normalizePath } from 'obsidian';
|
||||
import { App, Notice, PluginSettingTab, Setting, TextComponent, normalizePath } from 'obsidian';
|
||||
import { FolderSuggest, TagHistorySuggest } from '../utils/InputSuggest';
|
||||
import { DEFAULT_SETTINGS } from './SettingData';
|
||||
import OpenWords from '../main';
|
||||
|
|
@ -7,6 +7,7 @@ import OpenWords from '../main';
|
|||
// 插件设置选项卡
|
||||
export class OpenWordsSettingTab extends PluginSettingTab {
|
||||
plugin: OpenWords;
|
||||
tagTextInput: TextComponent | null = null;
|
||||
|
||||
constructor(app: App, plugin: OpenWords) {
|
||||
super(app, plugin);
|
||||
|
|
@ -14,7 +15,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
// 初始化设置快照
|
||||
|
|
@ -75,74 +76,74 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
}
|
||||
};
|
||||
|
||||
// 添加标签的处理函数
|
||||
const handleAddTag = (val: string) => {
|
||||
const value = val.trim();
|
||||
if (!value) {
|
||||
new Notice('请输入标签名称!');
|
||||
return;
|
||||
}
|
||||
|
||||
const { enabledTags, tagHistory } = this.plugin.settingsSnapshot;
|
||||
|
||||
if (enabledTags.includes(value)) {
|
||||
new Notice('该标签已存在!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
enabledTags.push(value);
|
||||
if (!tagHistory.includes(value)) {
|
||||
tagHistory.push(value);
|
||||
}
|
||||
|
||||
// 渲染 UI 并清空输入(清空逻辑在回调中处理)
|
||||
renderTags();
|
||||
return true; // 表示添加成功
|
||||
};
|
||||
|
||||
// 添加标签输入与历史建议
|
||||
new Setting(containerEl)
|
||||
.setName('添加标签')
|
||||
.setDesc('输入新的标签名称,例如 "级别/小学"')
|
||||
.addText(text => {
|
||||
text.setPlaceholder('输入标签名称');
|
||||
text.onChange(async (value) => {
|
||||
(text.inputEl as any).currentValue = value;
|
||||
});
|
||||
|
||||
// 使用该引用,不再需要 (text.inputEl as any).currentValue
|
||||
const inputEl = text.inputEl;
|
||||
|
||||
// 初始化建议器
|
||||
new TagHistorySuggest(
|
||||
this.app,
|
||||
text.inputEl,
|
||||
inputEl,
|
||||
this.plugin.settingsSnapshot.tagHistory || [],
|
||||
(selectedValue) => text.setValue(selectedValue),
|
||||
(selectedValue) => void text.setValue(selectedValue),
|
||||
async (valueToDelete) => {
|
||||
const index = this.plugin.settingsSnapshot.tagHistory.indexOf(valueToDelete);
|
||||
if (index > -1) {
|
||||
this.plugin.settingsSnapshot.tagHistory.splice(index, 1);
|
||||
}
|
||||
const history = this.plugin.settingsSnapshot.tagHistory;
|
||||
const index = history.indexOf(valueToDelete);
|
||||
if (index > -1) history.splice(index, 1);
|
||||
}
|
||||
);
|
||||
|
||||
text.inputEl.addEventListener('keydown', (e) => {
|
||||
// 监听回车键
|
||||
inputEl.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const value = (text.inputEl as any).value?.trim();
|
||||
if (value && !this.plugin.settingsSnapshot.enabledTags.includes(value)) {
|
||||
this.plugin.settingsSnapshot.enabledTags.push(value);
|
||||
if (!this.plugin.settingsSnapshot.tagHistory.includes(value)) {
|
||||
this.plugin.settingsSnapshot.tagHistory.push(value);
|
||||
}
|
||||
renderTags();
|
||||
text.setValue('');
|
||||
} else if (value && this.plugin.settingsSnapshot.enabledTags.includes(value)) {
|
||||
new Notice('该标签已存在!');
|
||||
if (handleAddTag(text.getValue())) {
|
||||
text.setValue(''); // 添加成功后清空
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 存储 text 引用供外部按钮使用(可选,但更优雅)
|
||||
this.tagTextInput = text;
|
||||
})
|
||||
.addButton(button => button
|
||||
.setButtonText('添加')
|
||||
.setCta() // 设置为强调色按钮
|
||||
.onClick(() => {
|
||||
const inputs = containerEl.querySelectorAll('input[type="text"]');
|
||||
let tagInput = '';
|
||||
for (let i = inputs.length - 1; i >= 0; i--) {
|
||||
const input = inputs[i] as HTMLInputElement;
|
||||
if (input.placeholder === '输入标签名称') {
|
||||
tagInput = input.value?.trim() || '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tagInput && !this.plugin.settingsSnapshot.enabledTags.includes(tagInput)) {
|
||||
this.plugin.settingsSnapshot.enabledTags.push(tagInput);
|
||||
if (!this.plugin.settingsSnapshot.tagHistory.includes(tagInput)) {
|
||||
this.plugin.settingsSnapshot.tagHistory.push(tagInput);
|
||||
}
|
||||
renderTags();
|
||||
for (let i = inputs.length - 1; i >= 0; i--) {
|
||||
const input = inputs[i] as HTMLInputElement;
|
||||
if (input.placeholder === '输入标签名称') {
|
||||
input.value = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (tagInput && this.plugin.settingsSnapshot.enabledTags.includes(tagInput)) {
|
||||
new Notice('该标签已存在!');
|
||||
} else {
|
||||
new Notice('请输入标签名称!');
|
||||
// 直接利用上文的 text 引用
|
||||
if (this.tagTextInput && handleAddTag(this.tagTextInput.getValue())) {
|
||||
this.tagTextInput.setValue('');
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -161,7 +162,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
button.setDisabled(true);
|
||||
button.setButtonText('处理中...');
|
||||
try {
|
||||
this.plugin.settings = {...this.plugin.settingsSnapshot};
|
||||
this.plugin.settings = { ...this.plugin.settingsSnapshot };
|
||||
this.plugin.settings.enabledTags = [...this.plugin.settingsSnapshot.enabledTags];
|
||||
this.plugin.settings.tagHistory = [...(this.plugin.settingsSnapshot.tagHistory || this.plugin.settingsSnapshot.enabledTags)];
|
||||
await this.plugin.saveSettings();
|
||||
|
|
@ -178,7 +179,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
button.setDisabled(false);
|
||||
button.setButtonText('扫描');
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('生成索引')
|
||||
|
|
@ -198,7 +199,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
button.setDisabled(false);
|
||||
button.setButtonText('生成');
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("随机调度概率")
|
||||
|
|
@ -245,7 +246,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
new Notice('已恢复默认设置');
|
||||
// 重新渲染设置页以反映默认值
|
||||
this.display();
|
||||
this.plugin.settings = {...this.plugin.settingsSnapshot};
|
||||
this.plugin.settings = { ...this.plugin.settingsSnapshot };
|
||||
this.plugin.settings.enabledTags = [...this.plugin.settingsSnapshot.enabledTags];
|
||||
this.plugin.settings.tagHistory = [...(this.plugin.settingsSnapshot.tagHistory || this.plugin.settingsSnapshot.enabledTags)];
|
||||
await this.plugin.saveSettings();
|
||||
|
|
@ -282,6 +283,6 @@ export class OpenWordsSettingTab extends PluginSettingTab {
|
|||
button.setDisabled(false);
|
||||
button.setButtonText('重置单词属性');
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
src/utils/Converters.ts
Normal file
13
src/utils/Converters.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export function asArray<T>(value: unknown, defaultValue: T[] = []): T[] {
|
||||
if (Array.isArray(value)) return value as T[];
|
||||
if (value !== null && value !== undefined) return [value as T];
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
export function asNumber(value: unknown, defaultValue = 0): number {
|
||||
return typeof value === 'number' ? value : defaultValue;
|
||||
}
|
||||
|
||||
export function asDateString(value: unknown, defaultValue = window.moment().format('YYYY-MM-DD')): string {
|
||||
return typeof value === 'string' ? value : defaultValue;
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@ import { Setting, Notice, MarkdownRenderer, TFile } from 'obsidian';
|
|||
import { SuperMemoGrade } from 'supermemo';
|
||||
import { CardInfo } from '../utils/Card';
|
||||
import { MainView } from './MainView';
|
||||
import { asArray, asDateString, asNumber } from 'utils/Converters';
|
||||
|
||||
export async function LearningPage(this: MainView) {
|
||||
|
||||
this.currentLearingCard = this.pickNextLCard();
|
||||
this.currentLearingCard = await this.pickNextLCard();
|
||||
if (!this.currentLearingCard) return;
|
||||
|
||||
this.viewContainer.empty();
|
||||
|
|
@ -15,9 +16,9 @@ export async function LearningPage(this: MainView) {
|
|||
const settingsContainer = learningContainer.createDiv({ cls: 'openwords-learning-settings' });
|
||||
|
||||
backBtn.textContent = '返回';
|
||||
backBtn.onclick = () => {
|
||||
backBtn.onclick = async () => {
|
||||
this.page = 'type';
|
||||
this.render();
|
||||
await this.render();
|
||||
};
|
||||
|
||||
// ESC 快捷键返回
|
||||
|
|
@ -37,7 +38,7 @@ export async function LearningPage(this: MainView) {
|
|||
}
|
||||
|
||||
// 选卡逻辑
|
||||
export function pickNextLCard(this: MainView) {
|
||||
export async function pickNextLCard(this: MainView) {
|
||||
const now = window.moment();
|
||||
let pool: CardInfo[];
|
||||
if (this.page === 'new') { // 新词排序:间隔降序,间隔相同则易记因子升序
|
||||
|
|
@ -45,7 +46,7 @@ export function pickNextLCard(this: MainView) {
|
|||
if (pool.length === 0) {
|
||||
new Notice("已完成所有新词!");
|
||||
this.page = 'type';
|
||||
void this.render();
|
||||
await this.render();
|
||||
return null;
|
||||
}
|
||||
pool.sort((a, b) => {
|
||||
|
|
@ -62,7 +63,7 @@ export function pickNextLCard(this: MainView) {
|
|||
if (pool.length === 0) {
|
||||
new Notice("没有需要复习的卡片!");
|
||||
this.page = 'type';
|
||||
void this.render();
|
||||
await this.render();
|
||||
return null;
|
||||
}
|
||||
pool.sort((a, b) => {
|
||||
|
|
@ -95,14 +96,15 @@ export function renderCard(this: MainView, cardContainer: HTMLDivElement) {
|
|||
const fileCache = this.plugin.app.metadataCache.getFileCache(file);
|
||||
const frontMatter = fileCache?.frontmatter;
|
||||
if (!frontMatter) { return; }
|
||||
const tags: string[] = (frontMatter.tags || []).map((tag: string) => {
|
||||
const parts = tag.split('/');
|
||||
return parts.length > 1 ? parts[1] : tag;
|
||||
});
|
||||
const dueDate: string = frontMatter["到期日"];
|
||||
const interval: string = frontMatter["间隔"];
|
||||
const efactor: string = frontMatter["易记因子"];
|
||||
const repetition: string = frontMatter["重复次数"];
|
||||
const tags: string[] = asArray(frontMatter.tags)
|
||||
.map((tag: string) => {
|
||||
const parts = tag.split('/');
|
||||
return parts.at(-1) ?? tag; // 如果 at(-1) 失败,退回到原标签
|
||||
});
|
||||
const dueDate: string = asDateString(frontMatter["到期日"]);
|
||||
const interval: number = asNumber(frontMatter["间隔"], 0);
|
||||
const efactor: number = asNumber(frontMatter["易记因子"], 0);
|
||||
const repetition: number = asNumber(frontMatter["重复次数"], 0);
|
||||
|
||||
const metaDiv = cardContainer.createDiv({ cls: 'openwords-card-meta' });
|
||||
const tagsDiv = metaDiv.createDiv({ cls: 'openwords-card-meta-tags' });
|
||||
|
|
@ -125,8 +127,8 @@ export function renderSettings(this: MainView, cardContainer: HTMLDivElement, se
|
|||
new Setting(settingsContainer)
|
||||
.setName(label)
|
||||
.addButton(btn => {
|
||||
btn.setButtonText(String(grade+1));
|
||||
btn.buttonEl.setAttribute('data-grade', String(grade+1));
|
||||
btn.setButtonText(String(grade + 1));
|
||||
btn.buttonEl.setAttribute('data-grade', String(grade + 1));
|
||||
btn.onClick(() => this.rateCard(grade, cardContainer));
|
||||
});
|
||||
}
|
||||
|
|
@ -137,7 +139,7 @@ export async function rateCard(this: MainView, grade: SuperMemoGrade, cardContai
|
|||
if (this.isRating || !this.currentLearingCard) return;
|
||||
this.isRating = true;
|
||||
await this.plugin.updateCard(this.currentLearingCard, grade, this.page);
|
||||
this.currentLearingCard = this.pickNextLCard();
|
||||
this.currentLearingCard = await this.pickNextLCard();
|
||||
this.renderCard(cardContainer);
|
||||
this.isRating = false;
|
||||
}
|
||||
|
|
@ -146,12 +148,12 @@ export async function rateCard(this: MainView, grade: SuperMemoGrade, cardContai
|
|||
export function registerRatingKeyEvents(this: MainView, learningContainer: HTMLDivElement, cardContainer: HTMLDivElement) {
|
||||
const handleKeydown = async (event: KeyboardEvent) => {
|
||||
if (event.key >= '1' && event.key <= '6') {
|
||||
if (this.isRating || !this.currentLearingCard) return;
|
||||
if (this.isRating || !this.currentLearingCard) return;
|
||||
this.isRating = true;
|
||||
this.currentRatingKey = event.key;
|
||||
const btn = learningContainer.querySelector(`button[data-grade="${event.key}"]`);
|
||||
if (btn) btn.classList.add('active');
|
||||
const grade = parseInt(event.key)-1 as SuperMemoGrade;
|
||||
const grade = parseInt(event.key) - 1 as SuperMemoGrade;
|
||||
await this.plugin.updateCard(this.currentLearingCard, grade, this.page);
|
||||
}
|
||||
};
|
||||
|
|
@ -165,7 +167,7 @@ export function registerRatingKeyEvents(this: MainView, learningContainer: HTMLD
|
|||
if (btn) btn.classList.remove('active');
|
||||
this.isRating = false;
|
||||
this.currentRatingKey = null;
|
||||
this.currentLearingCard = this.pickNextLCard();
|
||||
this.currentLearingCard = await this.pickNextLCard();
|
||||
this.renderCard(cardContainer);
|
||||
}
|
||||
};
|
||||
|
|
@ -177,7 +179,7 @@ export function registerRatingKeyEvents(this: MainView, learningContainer: HTMLD
|
|||
export function registerRenderKeyEvents(this: MainView, learningContainer: HTMLDivElement, cardContainer: HTMLDivElement) {
|
||||
let isShowingMarkdown = false;
|
||||
const showMarkdown = async () => {
|
||||
if (isShowingMarkdown || !this.currentLearingCard) return;
|
||||
if (isShowingMarkdown || !this.currentLearingCard) return;
|
||||
const file = this.plugin.app.vault.getFileByPath(this.currentLearingCard.path);
|
||||
if (file instanceof TFile) {
|
||||
const markdownContent = await this.plugin.app.vault.cachedRead(file);
|
||||
|
|
@ -200,10 +202,10 @@ export function registerRenderKeyEvents(this: MainView, learningContainer: HTMLD
|
|||
this.renderCard(cardContainer);
|
||||
isShowingMarkdown = false;
|
||||
};
|
||||
|
||||
|
||||
// 延迟1秒后注册鼠标事件
|
||||
setTimeout(() => {
|
||||
cardContainer.addEventListener('mouseenter', showMarkdown);
|
||||
cardContainer.addEventListener('mouseenter', () => { void showMarkdown(); });
|
||||
cardContainer.addEventListener('mouseleave', showWord);
|
||||
}, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export class MainView extends ItemView {
|
|||
}
|
||||
|
||||
getViewType() { return MAIN_VIEW; }
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case
|
||||
getDisplayText() { return "OpenWords"; }
|
||||
getIcon(): string { return "slack"; }
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ export class MainView extends ItemView {
|
|||
this.viewContainer = container.createDiv({ cls: 'openwords-view-container' });
|
||||
this.statusBarEl = container.createDiv({ cls: 'openwords-view-statusbar' });
|
||||
await this.render();
|
||||
await this.updateStatusBar();
|
||||
this.updateStatusBar();
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
|
|
@ -82,9 +83,9 @@ export class MainView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
async updateStatusBar() {
|
||||
updateStatusBar() {
|
||||
if (!this.statusBarEl) return;
|
||||
|
||||
|
||||
// 计算各分类的单词数
|
||||
const newCount = this.plugin.newCards.size;
|
||||
const dueTodayCount = Array.from(this.plugin.dueCards.values())
|
||||
|
|
@ -93,43 +94,54 @@ export class MainView extends ItemView {
|
|||
const masteredCount = this.plugin.masterCards.size;
|
||||
const disabledCount = this.plugin.allCards.size - this.plugin.enabledCards.size;
|
||||
const totalCount = this.plugin.allCards.size;
|
||||
|
||||
|
||||
// 清空状态栏
|
||||
this.statusBarEl.empty();
|
||||
|
||||
|
||||
// 创建第一行:待学习、待复习、今日到期、已掌握、未启用
|
||||
const row1 = this.statusBarEl.createDiv({ cls: 'openwords-statusbar-row' });
|
||||
|
||||
|
||||
this.createStatItem(row1, '待学习', newCount);
|
||||
this.createStatItem(row1, '待复习', reviewCount);
|
||||
this.createStatItem(row1, '今日到期', dueTodayCount);
|
||||
this.createStatItem(row1, '已掌握', masteredCount);
|
||||
this.createStatItem(row1, '未启用', disabledCount);
|
||||
|
||||
|
||||
// 创建第二行:总计
|
||||
const row2 = this.statusBarEl.createDiv({ cls: 'openwords-statusbar-row openwords-statusbar-total' });
|
||||
this.createStatItem(row2, '总计', totalCount, true);
|
||||
}
|
||||
|
||||
|
||||
private createStatItem(container: HTMLElement, label: string, count: number, isTotal: boolean = false) {
|
||||
const item = container.createDiv({ cls: isTotal ? 'openwords-stat-item total' : 'openwords-stat-item' });
|
||||
item.createSpan({ cls: 'openwords-stat-label', text: label });
|
||||
item.createSpan({ cls: 'openwords-stat-count', text: count.toString() });
|
||||
|
||||
|
||||
// 添加点击事件,打开对应的视图
|
||||
if (isTotal) {
|
||||
item.addEventListener('click', async () => {
|
||||
// 创建单词状态 .base 文件
|
||||
await this.plugin.createWordStatusBaseFile();
|
||||
const basePath = `${this.plugin.settings.indexPath}/英语单词状态.base`;
|
||||
const file = this.app.vault.getFileByPath(basePath);
|
||||
if (!file) {
|
||||
new Notice('单词状态文件不存在,请先生成索引');
|
||||
return;
|
||||
}
|
||||
// 打开 .base 文件
|
||||
const leaf = this.app.workspace.getLeaf(false);
|
||||
await leaf.openFile(file);
|
||||
item.addEventListener('click', () => {
|
||||
// 使用立即执行的异步函数,并标记为 void
|
||||
void (async () => {
|
||||
try {
|
||||
// 创建单词状态 .base 文件
|
||||
await this.plugin.createWordStatusBaseFile();
|
||||
|
||||
const basePath = `${this.plugin.settings.indexPath}/英语单词状态.base`;
|
||||
const file = this.app.vault.getFileByPath(basePath);
|
||||
|
||||
if (!file) {
|
||||
new Notice('单词状态文件不存在,请先生成索引');
|
||||
return;
|
||||
}
|
||||
|
||||
// 打开 .base 文件
|
||||
const leaf = this.app.workspace.getLeaf(false);
|
||||
await leaf.openFile(file);
|
||||
} catch (error) {
|
||||
new Notice(`打开文件失败: ${String(error)}`);
|
||||
console.error('Open base file error:', error);
|
||||
}
|
||||
})();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { MainView } from './MainView';
|
|||
|
||||
export async function SpellingPage(this: MainView) {
|
||||
|
||||
this.currentSpellingCard = this.pickNextSCard();
|
||||
this.currentSpellingCard = await this.pickNextSCard();
|
||||
if (!this.currentSpellingCard) return;
|
||||
|
||||
this.viewContainer.empty();
|
||||
|
|
@ -16,9 +16,9 @@ export async function SpellingPage(this: MainView) {
|
|||
const feedbackContainer = spellingContainer.createDiv({ cls: 'openwords-spelling-feedback' });
|
||||
|
||||
backBtn.textContent = '返回';
|
||||
backBtn.onclick = () => {
|
||||
backBtn.onclick = async () => {
|
||||
this.page = 'type';
|
||||
this.render();
|
||||
await this.render();
|
||||
};
|
||||
|
||||
// ESC 快捷键返回
|
||||
|
|
@ -51,7 +51,7 @@ export async function SpellingPage(this: MainView) {
|
|||
|
||||
}
|
||||
|
||||
export function pickNextSCard(this: MainView) {
|
||||
export async function pickNextSCard(this: MainView) {
|
||||
// 从易记因子最低的50%中随机选择一个单词
|
||||
this.hasPeeked = false;
|
||||
this.errorCount = 0;
|
||||
|
|
@ -60,7 +60,7 @@ export function pickNextSCard(this: MainView) {
|
|||
if (cards.length === 0) {
|
||||
new Notice("已完成所有单词!");
|
||||
this.page = 'type';
|
||||
void this.render();
|
||||
await this.render();
|
||||
return null;
|
||||
}
|
||||
const sorted = cards.slice().sort((a, b) => a.efactor - b.efactor);
|
||||
|
|
@ -115,7 +115,7 @@ export async function renderInput(
|
|||
if (inputField.value.length === 1 && i < word.length - 1) {
|
||||
inputFields[i + 1]?.focus();
|
||||
}
|
||||
this.checkSpelling(inputFields, word, feedbackContainer, wordMeaningContainer, inputContainer);
|
||||
void this.checkSpelling(inputFields, word, feedbackContainer, wordMeaningContainer, inputContainer);
|
||||
});
|
||||
|
||||
inputField.addEventListener('keydown', (event) => {
|
||||
|
|
@ -144,11 +144,10 @@ export async function checkSpelling(
|
|||
|
||||
if (!this.plugin.dueCards.has(word)) {
|
||||
new Notice("当前单词不属于本模式范围, 评分无效并跳过");
|
||||
setTimeout(async () => {
|
||||
this.currentSpellingCard = this.pickNextSCard();
|
||||
if (!this.currentSpellingCard) return;
|
||||
await this.renderInput(wordMeaningContainer, inputContainer, feedbackContainer);
|
||||
}, 500);
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
this.currentSpellingCard = await this.pickNextSCard();
|
||||
if (!this.currentSpellingCard) return;
|
||||
await this.renderInput(wordMeaningContainer, inputContainer, feedbackContainer);
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -167,18 +166,17 @@ export async function checkSpelling(
|
|||
// 同步到 frontmatter
|
||||
const file = this.plugin.app.vault.getFileByPath(this.currentSpellingCard.path);
|
||||
if (file instanceof TFile) {
|
||||
await this.plugin.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||
await this.plugin.app.fileManager.processFrontMatter(file, (frontMatter: Record<string, unknown>) => {
|
||||
frontMatter["易记因子"] = Math.round(efactor * 100);
|
||||
});
|
||||
}
|
||||
|
||||
new Notice(`${this.currentSpellingCard.front} \n易记因子: ${efactor.toFixed(2)} \n重复次数: ${this.currentSpellingCard.repetition} \n间隔: ${this.currentSpellingCard.interval} \n到期日: ${this.currentSpellingCard.dueDate}`);
|
||||
}
|
||||
setTimeout(async () => {
|
||||
this.currentSpellingCard = this.pickNextSCard();
|
||||
if (!this.currentSpellingCard) return;
|
||||
await this.renderInput(wordMeaningContainer, inputContainer, feedbackContainer);
|
||||
}, 500);
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
this.currentSpellingCard = await this.pickNextSCard();
|
||||
if (!this.currentSpellingCard) return;
|
||||
await this.renderInput(wordMeaningContainer, inputContainer, feedbackContainer);
|
||||
} else {
|
||||
feedbackContainer.setText('错误,请重试!');
|
||||
this.errorCount += 1;
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ export function TypePage(this: MainView) {
|
|||
.addButton(btn => {
|
||||
const button = btn
|
||||
.setButtonText("开始")
|
||||
.onClick(() => {
|
||||
.onClick(async () => {
|
||||
if (this.plugin.newCards.size > 0) {
|
||||
this.page = 'new';
|
||||
void this.render();
|
||||
await this.render();
|
||||
} else {
|
||||
new Notice("没有新词可学了!");
|
||||
}
|
||||
|
|
@ -32,10 +32,10 @@ export function TypePage(this: MainView) {
|
|||
.addButton(btn => {
|
||||
const button = btn
|
||||
.setButtonText("开始")
|
||||
.onClick(() => {
|
||||
.onClick(async () => {
|
||||
if (this.plugin.dueCards.size > 0) {
|
||||
this.page = 'old';
|
||||
void this.render();
|
||||
await this.render();
|
||||
} else {
|
||||
new Notice("没有需要复习的单词!");
|
||||
}
|
||||
|
|
@ -49,10 +49,10 @@ export function TypePage(this: MainView) {
|
|||
.addButton(btn => {
|
||||
const button = btn
|
||||
.setButtonText('开始')
|
||||
.onClick(() => {
|
||||
.onClick(async () => {
|
||||
if (this.plugin.dueCards.size > 0) {
|
||||
this.page = 'spelling';
|
||||
void this.render();
|
||||
await this.render();
|
||||
} else {
|
||||
new Notice("没有需要默写的单词!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"1.0.10": "1.10.0"
|
||||
"1.0.11": "1.10.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue