2026/2/21

This commit is contained in:
项阳 2026-02-21 14:17:17 +08:00
parent aca53a6204
commit 156af15db9
9 changed files with 354 additions and 231 deletions

View file

@ -1,8 +1,8 @@
{
"id": "openwords",
"name": "OpenWords",
"version": "1.0.7",
"minAppVersion": "1.8.10",
"version": "1.0.8",
"minAppVersion": "1.10.0",
"description": "用于英语学习中背单词与单词管理的插件",
"author": "insile",
"authorUrl": "https://github.com/insile",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "openwords",
"version": "1.0.7",
"version": "1.0.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openwords",
"version": "1.0.7",
"version": "1.0.8",
"license": "MIT",
"dependencies": {
"obsidian": "latest",

View file

@ -1,6 +1,6 @@
{
"name": "openwords",
"version": "1.0.7",
"version": "1.0.8",
"description": "用于英语学习中背单词与单词管理的插件",
"main": "main.js",
"type": "module",

View file

@ -1,4 +1,4 @@
import {EventRef, MarkdownView, Notice, Plugin, TFile, TFolder, Vault} from 'obsidian';
import { EventRef, MarkdownView, Notice, Plugin, TFile, TFolder, Vault } from 'obsidian';
import { OpenWordsSettings, DEFAULT_SETTINGS } from "./settings/SettingData";
import { OpenWordsSettingTab } from './settings/SettingTab';
import { MainView, MAIN_VIEW, PageType } from './views/MainView';
@ -125,7 +125,6 @@ export default class OpenWords extends Plugin {
if (single) { this.updateStatusBar(); }
return;
}
const isMasteredOld = allCards.get(card.front)?.isMastered;
// 更新所有单词
allCards.set(card.front, card);
@ -145,13 +144,6 @@ export default class OpenWords extends Plugin {
}
}
// 如果掌握状态发生变化,更新复选框
if (isMastered !== isMasteredOld && isMasteredOld !== undefined) {
const newCheckbox = isMastered ? 'x' : ' ';
const oldCheckbox = isMasteredOld ? 'x' : ' ';
await this.syncMetadataToCheckbox(file, newCheckbox, oldCheckbox);
}
// 如果是单个文件更新状态栏
if (single) { this.updateStatusBar(); }
}
@ -178,8 +170,6 @@ export default class OpenWords extends Plugin {
await Promise.all(filteredFiles.map(async (file) => {
await this.loadWordMetadata(file, false);
}));
// new Notice(`扫描完成!共 ${this.allCards.size} 个单词`); // 完成后更新消息
}
// 更新单词属性
@ -188,7 +178,7 @@ export default class OpenWords extends Plugin {
(mode === 'new' && !this.newCards.has(card.front)) ||
(mode === 'old' && !this.dueCards.has(card.front))
) {
new Notice(`${card.front} 不属于本模式范围 \n评分无效并跳过`);
new Notice(`${card.front} \n不属于本模式范围 \n评分无效并跳过`);
return;
}
@ -212,12 +202,12 @@ export default class OpenWords extends Plugin {
// 等待元数据缓存更新(最多等待 1 秒)
let attempts = 0;
while (attempts < 10) {
while (attempts < 5) {
const updated = this.app.metadataCache.getFileCache(file);
if (updated?.frontmatter?.["易记因子"] === Math.round(result.efactor * 100)) {
if (updated?.frontmatter?.["到期日"] === newDate) {
break; // 缓存已更新
}
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise(resolve => setTimeout(resolve, 200));
attempts++;
}
}
@ -229,8 +219,8 @@ export default class OpenWords extends Plugin {
return;
}
// 注销监听器,避免重置过程中触发大量缓存更新事件
this.unregisterFileWatchers();
// // 注销监听器,避免重置过程中触发大量缓存更新事件
// this.unregisterFileWatchers();
const notice = new Notice('重置中...', 0); // 创建一个持续显示的 Notice
let count = 0; // 计数器
@ -270,11 +260,11 @@ export default class OpenWords extends Plugin {
new Notice(`重置出错: ${error}`);
console.error('Reset card error:', error);
} finally {
// 重新注册监听器
this.registerFileWatchers();
// 重新扫描所有单词文件,确保数据同步
await this.scanAllNotes();
this.updateStatusBar();
// // 重新注册监听器
// this.registerFileWatchers();
// // 重新扫描所有单词文件,确保数据同步
// await this.scanAllNotes();
// this.updateStatusBar();
setTimeout(() => notice.hide(), 2000); // 2 秒后自动隐藏 Notice
}
}
@ -324,183 +314,163 @@ export default class OpenWords extends Plugin {
}
}
// 生成单词索引
// 生成单词索引 .base 文件
async generateIndex() {
const notice = new Notice('索引中...', 0); // 创建一个持续显示的 Notice
const levels = [
"级别/小学", "级别/中考", "级别/高考四级", "级别/考研",
"级别/六级", "级别/雅思", "级别/托福", "级别/GRE"
];
const wordsDir = this.settings.folderPath; // 单词文件夹路径
const indexDir = this.settings.indexPath; // 索引文件夹路径
const notice = new Notice('索引中...', 0);
// 从设置读取标签并转换为配置对象(将多级标签的 '/' 替换为 '.'
const levelConfigs = this.settings.enabledTags.map(tag => {
const name = tag.replace(/\//g, '.');
return { tag, name };
});
// 创建索引文件夹 (如果不存在)
const wordsDir = this.settings.folderPath;
const indexDir = this.settings.indexPath;
// 创建索引文件夹
const existingIndexFolder = this.app.vault.getFolderByPath(indexDir);
if (!existingIndexFolder) {
await this.app.vault.createFolder(indexDir);
}
// 遍历所有单词,记录每个单词的级别
const wordRecords: Record<string, { [letter: string]: string[] }> = {};
for (const level of levels) {
wordRecords[level] = {};
// 统计每个级别的单词数
const levelWordCounts: Record<string, number> = {};
for (const level of levelConfigs) {
levelWordCounts[level.tag] = 0;
}
const folder = this.app.vault.getAbstractFileByPath(wordsDir);
if (!(folder instanceof TFolder)) return; // 确认目录存在
const folder = this.app.vault.getAbstractFileByPath(wordsDir);
if (!(folder instanceof TFolder) || wordsDir === "/") {
notice.setMessage('指定的单词文件夹不存在!');
setTimeout(() => notice.hide(), 2000);
return
};
Vault.recurseChildren(folder, (file) => {
if (!(file instanceof TFile) || file.extension !== "md") return;
Vault.recurseChildren(folder, (file) => {
if (!(file instanceof TFile) || file.extension !== "md") return;
const frontMatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
if (!frontMatter) return; // 如果没有 FrontMatter跳过
const tags: string[] = frontMatter.tags || [];
const isMastered = frontMatter["掌握"] === true;
for (const level of levels) {
if (tags.includes(level)) {
const letter = file.basename[0].toUpperCase(); // 获取单词首字母
if (!wordRecords[level][letter]) {
wordRecords[level][letter] = [];
}
if (isMastered)
wordRecords[level][letter].push(`- [x] [[${file.basename}]]`);
else {
wordRecords[level][letter].push(`- [ ] [[${file.basename}]]`);
}
const frontMatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
if (!frontMatter) return;
const tags: string[] = frontMatter.tags || [];
for (const level of levelConfigs) {
if (tags.includes(level.tag)) {
levelWordCounts[level.tag]++;
}
}
})
});
const totalWordsList: Record<string, number> = {}; // 记录每个级别的总单词数
// 遍历级别,生成索引
for (const level of levels) {
const levelName = level.split('/').pop(); // 获取级别名称
const levelFolderPath = `${indexDir}/${levelName}`;
const existingLevelFolder = this.app.vault.getFolderByPath(levelFolderPath);
notice.setMessage(`索引中... ${levelName}`); // 更新 Notice 的消息
// 总单词数
const totalCount = this.allCards.size;
// 如果级别文件夹不存在,则创建
if (!existingLevelFolder) {
await this.app.vault.createFolder(levelFolderPath);
}
// 生成主索引文件
const mainIndexPath = `${indexDir}/英语单词索引.md`;
notice.setMessage('索引中... 生成主索引');
let totalWords = 0; // 统计总单词数
const letterStats: { letter: string; count: number }[] = []; // 记录每个字母的单词数量
for (let i = 65; i <= 90; i++) {
const letter = String.fromCharCode(i);
const words = wordRecords[level][letter] || []; // 如果没有单词,返回空数组
words.sort((a, b) => {
const extractName = (line: string) => {
const match = line.match(/\[\[(.+?)\]\]/);
return match ? match[1] : line;
};
return extractName(a).localeCompare(extractName(b));
}); // 按字母顺序排序
totalWords += words.length; // 累加总单词数
letterStats.push({ letter, count: words.length }); // 添加统计信息
// 创建或覆盖字母索引文件
const letterFilePath = `${levelFolderPath}/${levelName}.${letter}.md`;
const content = words.join('\n');
await this.writeFile(letterFilePath, `##### ${levelName}.${letter} ${words.length}\n${content}`); // 写入索引文件
}
totalWordsList[level] = totalWords; // 记录每个级别的总单词数
// 生成总索引文件
const totalIndexFilePath = `${levelFolderPath}/单词索引.${levelName}.md`;
const totalIndexContent = [
`##### 单词索引.${levelName} ${totalWords}`,
`| 首字母 | 数量 |`,
`| :-------: | --: |`,
...letterStats.map(stat => `| [${stat.letter}](${levelName}.${stat.letter}) | ${stat.count} |`)
].join('\n');
await this.writeFile(totalIndexFilePath, totalIndexContent); // 写入索引文件
}
// 生成全索引
const fullIndexFolderPath = `${indexDir}/全索引`;
notice.setMessage(`索引中... 全索引`); // 更新 Notice 的消息
const existingFullIndexFolder = this.app.vault.getFolderByPath(fullIndexFolderPath);
if (!existingFullIndexFolder) {
await this.app.vault.createFolder(fullIndexFolderPath);
}
// 先收集所有字母到一个新的对象
const allWordsByLetter: Record<string, string[]> = {}; // { A: [...], B: [...], ... }
for (const levelRecords of Object.values(wordRecords)) {
for (const [letter, words] of Object.entries(levelRecords)) {
if (!allWordsByLetter[letter]) {
allWordsByLetter[letter] = [];
}
allWordsByLetter[letter].push(...words);
}
}
let totalWords = 0;
const letterStats: { letter: string; count: number }[] = [];
// 构建表格
const titleRow = '| [[英语单词索引.总计.base#总计\\|总计]] | ' + levelConfigs.map(l => `[[英语单词索引.${l.name}.base#${l.name.split('.').pop()}\\|${l.name.split('.').pop()}]]`).join(' | ') + ' |';
const separator = '| :---: | ' + levelConfigs.map(() => ':---:').join(' | ') + ' |';
const countRow = '| ' + totalCount + ' | ' + levelConfigs.map(l => levelWordCounts[l.tag] || 0).join(' | ') + ' |';
// 字母行
const letterRows: string[] = [];
for (let i = 65; i <= 90; i++) {
const letter = String.fromCharCode(i);
let words = allWordsByLetter[letter] || []; // 如果没有单词,返回空数组
words = Array.from(new Set(words)); // 去重
totalWords += words.length; // 累加总单词数
letterStats.push({ letter, count: words.length });
words.sort((a, b) => {
const extractName = (line: string) => {
const match = line.match(/\[\[(.+?)\]\]/);
return match ? match[1] : line;
};
return extractName(a).localeCompare(extractName(b));
});
// 创建或覆盖字母索引文件
const letterFilePath = `${fullIndexFolderPath}/全索引.${letter}.md`;
const content = words.join('\n');
await this.writeFile(letterFilePath, `##### 全索引.${letter} ${words.length}\n${content}`); // 写入索引文件
const cells = [`[[英语单词索引.总计.base#总计.${letter}\\|${letter}]]`];
for (const level of levelConfigs) {
cells.push(`[[英语单词索引.${level.name}.base#${level.name.split('.').pop()}.${letter}\\|${letter}]]`);
}
letterRows.push('| ' + cells.join(' | ') + ' |');
}
// 生成总索引文件
const totalIndexFilePath = `${fullIndexFolderPath}/单词索引.全索引.md`;
const totalIndexContent = [
`##### 单词索引.全索引 ${totalWords}`,
`| 首字母 | 数量 |`,
`| :-------: | --: |`,
...letterStats.map(stat => `| [${stat.letter}](全索引.${stat.letter}) | ${stat.count} |`)
// 合并表格
const mainContent = [
titleRow,
separator,
countRow,
...letterRows
].join('\n');
await this.writeFile(totalIndexFilePath, totalIndexContent); // 写入索引文件
await this.writeFile(mainIndexPath, '##### 英语单词索引\n\n' + mainContent);
// 生成总计 .base 配置文件(总计列)
notice.setMessage('索引中... 生成总计索引');
const allBasePath = `${indexDir}/英语单词索引.总计.base`;
const allBaseContent = `
filters:
and:
- file.folder.startsWith("${wordsDir}")
views:
- type: table
name: 总计
order:
-
- file.name
-
-
-
-
${Array.from({ length: 26 }, (_, i) => {
const letter = String.fromCharCode(65 + i);
return ` - type: table
name: 总计.${letter}
filters:
and:
- file.basename.startsWith("${letter.toLowerCase()}")
order:
-
- file.name
-
-
-
- `;
}).join('\n')}
`;
await this.writeFile(allBasePath, allBaseContent);
const finalIndexPath = `${indexDir}/英语单词.md`;
// 定义表头
const header = [
`##### 英语单词`,
``,
`| | [n.](单词索引.名词) | [v.](单词索引.动词) | [adj.](单词索引.形容词) | [adv.](单词索引.副词) | [prep.](单词索引.介词) | [pron.](单词索引.代词) | [det.](单词索引.限定词) | [conj.](单词索引.连词) |`,
`| :--------------: | :------------: | :------------: | :---------------: | :--------------: | :---------------: | :---------------: | :---------------: | :---------------: |`,
`| [全索引](单词索引.全索引) | [小学](单词索引.小学) | [中考](单词索引.中考) | [高四](单词索引.高考四级) | [考研](单词索引.考研) | [六级](单词索引.六级) | [雅思](单词索引.雅思) | [托福](单词索引.托福) | [GRE](单词索引.GRE) |`,
`| ${this.allCards.size} | ${totalWordsList["级别/小学"]} | ${totalWordsList["级别/中考"]} | ${totalWordsList["级别/高考四级"]} | ${totalWordsList["级别/考研"]} | ${totalWordsList["级别/六级"]} | ${totalWordsList["级别/雅思"]} | ${totalWordsList["级别/托福"]} | ${totalWordsList["级别/GRE"]} |`
];
// 生成每个级别的 .base 配置文件
for (const level of levelConfigs) {
notice.setMessage(`索引中... ${level.name}`);
const basePath = `${indexDir}/英语单词索引.${level.name}.base`;
// 定义字母索引行
const alphabetRows = [];
for (let i = 65; i <= 90; i++) {
const letter = String.fromCharCode(i);
alphabetRows.push(
`| [${letter}](全索引.${letter}) | [${letter}](小学.${letter}) | [${letter}](中考.${letter}) | [${letter}](高考四级.${letter}) | [${letter}](考研.${letter}) | [${letter}](六级.${letter}) | [${letter}](雅思.${letter}) | [${letter}](托福.${letter}) | [${letter}](GRE.${letter}) |`
);
// 构建 DataviewJS 配置
const baseContent = `
filters:
and:
- file.folder.startsWith("${wordsDir}")
- file.tags.contains("${level.tag}")
views:
- type: table
name: ${level.name.split('.').pop()}
order:
-
- file.name
-
-
-
-
${Array.from({ length: 26 }, (_, i) => {
const letter = String.fromCharCode(65 + i);
return ` - type: table
name: ${level.name.split('.').pop()}.${letter}
filters:
and:
- file.basename.startsWith("${letter.toLowerCase()}")
order:
-
- file.name
-
-
-
- `;
}).join('\n')}
`;
await this.writeFile(basePath, baseContent);
}
// 合并表头和字母索引行
const finalContent = [...header, ...alphabetRows].join('\n')+'\n';
await this.writeFile(finalIndexPath, finalContent); // 写入索引文件
notice.setMessage("索引生成完成!");
setTimeout(() => notice.hide(), 2000); // 2 秒后自动隐藏 Notice
notice.setMessage('索引生成完成!');
setTimeout(() => notice.hide(), 2000);
}
// 覆盖写入文件
@ -513,25 +483,123 @@ export default class OpenWords extends Plugin {
}
}
// 监听单词属性变化并同步到复选框状态
async syncMetadataToCheckbox(file: TFile, newCheckbox: string, oldCheckbox: string) {
const resolvedLinks = this.app.metadataCache.resolvedLinks;
const backlinks: Record<string, number> = {};
for (const [sourcePath, links] of Object.entries(resolvedLinks)) {
if (links[file.path]) {
backlinks[sourcePath] = links[file.path];
}
}
for (const [sourcePath, ] of Object.entries(backlinks)) {
if (sourcePath.startsWith(this.settings.indexPath)) {
const sourceFile = this.app.vault.getFileByPath(sourcePath)
if (sourceFile instanceof TFile) {
const newWord = `- [${newCheckbox}] [[${file.basename}]]`;
const oldWord = `- [${oldCheckbox}] [[${file.basename}]]`;
await this.app.vault.process(sourceFile, (data) => data.replace(oldWord, newWord));
}
}
// 创建英语单词状态 .base 文件
async createWordStatusBaseFile() {
const wordsDir = this.settings.folderPath;
const indexDir = this.settings.indexPath;
const enabledTagsList = this.settings.enabledTags;
// 创建索引文件夹
const existingIndexFolder = this.app.vault.getFolderByPath(indexDir);
if (!existingIndexFolder) {
await this.app.vault.createFolder(indexDir);
}
// 构建 enabledTags 的 or 条件
const tagFilters = enabledTagsList.map(tag => ` - file.tags.contains("${tag}")`).join('\n');
// 构建 .base 文件内容
const baseContent = `filters:
and:
- file.folder.startsWith("${wordsDir}")
views:
- type: table
name: 总计
order:
-
- file.name
-
-
-
-
- type: table
name: 待学习
filters:
and:
- or:
${tagFilters}
- and:
- == false
- and:
- == 0
order:
-
- file.name
-
-
-
-
- type: table
name: 待复习
filters:
and:
- or:
${tagFilters}
- and:
- == false
- and:
- != 0
- and:
- > today()
order:
-
- file.name
-
-
-
-
- type: table
name: 今日到期
filters:
and:
- or:
${tagFilters}
- and:
- == false
- and:
- != 0
- and:
- <= today()
order:
-
- file.name
-
-
-
-
- type: table
name: 已掌握
filters:
and:
- and:
- == true
order:
-
- file.name
-
-
-
-
- type: table
name: 未启用
filters:
or:
- not:
${tagFilters}
- and:
- == true
order:
-
- file.name
-
-
-
-
`;
// 写入或更新 .base 文件
const basePath = `${this.settings.indexPath}/英语单词状态.base`;
await this.writeFile(basePath, baseContent);
}
// 更新状态栏

View file

@ -169,6 +169,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
await this.plugin.reinitializeFileWatchers();
await this.plugin.scanAllNotes();
new Notice(`扫描完成!共 ${this.plugin.allCards.size} 个单词`);
await this.plugin.createWordStatusBaseFile();
this.plugin.updateStatusBar();
} catch (error) {
console.error('缓存过程中发生错误:', error);
@ -181,7 +182,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName('生成索引')
.setDesc('按级别生成所有单词的首字母索引文件')
.setDesc('按标签生成所有单词的首字母索引文件')
.addButton(button => button
.setButtonText('索引')
.onClick(async () => {
@ -252,6 +253,7 @@ export class OpenWordsSettingTab extends PluginSettingTab {
await this.plugin.reinitializeFileWatchers();
await this.plugin.scanAllNotes();
new Notice(`扫描完成!共 ${this.plugin.allCards.size} 个单词`);
await this.plugin.createWordStatusBaseFile();
this.plugin.updateStatusBar();
} catch (error) {
console.error('恢复默认设置失败:', error);

View file

@ -193,8 +193,12 @@ export function registerRenderKeyEvents(this: MainView, learningContainer: HTMLD
this.renderCard(cardContainer);
isShowingMarkdown = false;
};
cardContainer.addEventListener('mouseenter', showMarkdown);
cardContainer.addEventListener('mouseleave', showWord);
// 延迟1秒后注册鼠标事件
setTimeout(() => {
cardContainer.addEventListener('mouseenter', showMarkdown);
cardContainer.addEventListener('mouseleave', showWord);
}, 1000);
const handleKeyDown = async (event: KeyboardEvent) => {
if (event.key === 'Tab') {

View file

@ -1,4 +1,4 @@
import { ItemView, WorkspaceLeaf, Component } from 'obsidian';
import { ItemView, WorkspaceLeaf, Component, Notice } from 'obsidian';
import { TypePage } from './TypePage';
import { SpellingPage, pickNextSCard, renderInput, checkSpelling } from './SpellingPage';
import { LearningPage, pickNextLCard, renderCard, renderSettings, rateCard, registerRatingKeyEvents, registerRenderKeyEvents } from './LearningPage';
@ -97,10 +97,10 @@ export class MainView extends ItemView {
// 清空状态栏
this.statusBarEl.empty();
// 创建第一行:新单词、待复习、今日到期、已掌握、未启用
// 创建第一行:待学习、待复习、今日到期、已掌握、未启用
const row1 = this.statusBarEl.createDiv({ cls: 'openwords-statusbar-row' });
this.createStatItem(row1, '新单词', newCount);
this.createStatItem(row1, '待学习', newCount);
this.createStatItem(row1, '待复习', reviewCount);
this.createStatItem(row1, '今日到期', dueTodayCount);
this.createStatItem(row1, '已掌握', masteredCount);
@ -115,5 +115,22 @@ export class MainView extends ItemView {
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);
});
};
}
}

View file

@ -8,42 +8,74 @@ export function TypePage(this: MainView) {
typeContainer.createDiv({ cls: 'openwords-type-title', text: 'OpenWords' });
const buttonContainer = typeContainer.createDiv({ cls: 'openwords-type-button-grid' });
const buttons: HTMLElement[] = [];
new Setting(buttonContainer)
.setName(`学习新词`)
.addButton(btn => btn
.setButtonText("开始")
.onClick(() => {
if (this.plugin.newCards.size > 0) {
this.page = 'new';
void this.render();
} else {
new Notice("没有新词可学了!");
}
}));
.addButton(btn => {
const button = btn
.setButtonText("开始")
.onClick(() => {
if (this.plugin.newCards.size > 0) {
this.page = 'new';
void this.render();
} else {
new Notice("没有新词可学了!");
}
});
buttons.push(button.buttonEl);
return button;
});
new Setting(buttonContainer)
.setName(`复习旧词`)
.addButton(btn => btn
.setButtonText("开始")
.onClick(() => {
if (this.plugin.dueCards.size > 0) {
this.page = 'old';
void this.render();
} else {
new Notice("没有需要复习的单词!");
}
}));
.addButton(btn => {
const button = btn
.setButtonText("开始")
.onClick(() => {
if (this.plugin.dueCards.size > 0) {
this.page = 'old';
void this.render();
} else {
new Notice("没有需要复习的单词!");
}
});
buttons.push(button.buttonEl);
return button;
});
new Setting(buttonContainer)
.setName('默写单词')
.addButton(btn => btn
.setButtonText('开始')
.onClick(() => {
if (this.plugin.dueCards.size > 0) {
this.page = 'spelling';
void this.render();
} else {
new Notice("没有需要默写的单词!");
}
}));
.addButton(btn => {
const button = btn
.setButtonText('开始')
.onClick(() => {
if (this.plugin.dueCards.size > 0) {
this.page = 'spelling';
void this.render();
} else {
new Notice("没有需要默写的单词!");
}
});
buttons.push(button.buttonEl);
return button;
});
// 快捷键监听
typeContainer.setAttribute('tabindex', '0');
typeContainer.focus();
typeContainer.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'q' && buttons[0]) {
e.preventDefault();
buttons[0].click();
} else if (e.key === 'w' && buttons[1]) {
e.preventDefault();
buttons[1].click();
} else if (e.key === 'e' && buttons[2]) {
e.preventDefault();
buttons[2].click();
}
});
}

View file

@ -1,3 +1,3 @@
{
"1.0.7": "1.8.10"
"1.0.8": "1.10.0"
}