mirror of
https://github.com/yan-istart/IStart-Note-AI-Plugin.git
synced 2026-07-22 06:51:37 +00:00
release: 1.7.4
This commit is contained in:
parent
cc6f12e147
commit
79aa7df7a9
8 changed files with 830 additions and 1213 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "istart-note-ai",
|
||||
"name": "IStart-Note-AI",
|
||||
"version": "1.7.3",
|
||||
"version": "1.7.4",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "Generate structured knowledge notes from questions and selected text using DeepSeek AI, with automatic concept pages, bidirectional links, and a question graph.",
|
||||
"author": "Yan",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "istart-note-ai",
|
||||
"version": "1.7.3",
|
||||
"version": "1.7.4",
|
||||
"description": "IStart-Note-AI: DeepSeek-powered knowledge graph plugin for Obsidian",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
215
src/actions/definitions.ts
Normal file
215
src/actions/definitions.ts
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
import { Notice } from "obsidian";
|
||||
import { ActionDef } from "./types";
|
||||
|
||||
/**
|
||||
* 所有插件动作的定义。
|
||||
* 新增功能只需在此数组中添加一条。
|
||||
*/
|
||||
export const ALL_ACTIONS: ActionDef[] = [
|
||||
// ── 通用 ──────────────────────────────────────────────────
|
||||
{
|
||||
id: "ask-deepseek",
|
||||
label: "提问",
|
||||
icon: "message-circle",
|
||||
description: "向 AI 提问并生成知识笔记",
|
||||
group: "general",
|
||||
when: { always: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => ctx.plugin.openQuestionModal(),
|
||||
},
|
||||
{
|
||||
id: "new-reading-project",
|
||||
label: "新建阅读项目",
|
||||
icon: "book-open",
|
||||
description: "输入书名,生成阅读地图",
|
||||
group: "general",
|
||||
when: { always: true },
|
||||
showIn: ["panel"],
|
||||
run: (ctx) => ctx.plugin.openNewReadingProject(),
|
||||
},
|
||||
{
|
||||
id: "baidu-sync",
|
||||
label: "百度云同步",
|
||||
icon: "cloud",
|
||||
group: "general",
|
||||
when: { always: true },
|
||||
showIn: ["panel"],
|
||||
run: (ctx) => ctx.plugin.openBaiduSyncModal(),
|
||||
},
|
||||
|
||||
// ── 选中文字 ──────────────────────────────────────────────
|
||||
{
|
||||
id: "context-qa",
|
||||
label: "基于选中内容提问",
|
||||
icon: "help-circle",
|
||||
group: "selection",
|
||||
when: { hasSelection: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
const path = ctx.activeFile?.path ?? "";
|
||||
ctx.plugin.openContextQAModal(ctx.selection, path);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "generate-diagram",
|
||||
label: "生成图表 / 公式",
|
||||
icon: "bar-chart-2",
|
||||
group: "selection",
|
||||
when: { hasSelection: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor) return;
|
||||
ctx.plugin.openDiagramGenerator(ctx.selection, ctx.fileContent.slice(0, 800), ctx.editor);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "expand-selection",
|
||||
label: "扩写选中内容",
|
||||
icon: "expand",
|
||||
group: "selection",
|
||||
when: { hasSelection: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor) return;
|
||||
void ctx.plugin.runExpand(ctx.selection, ctx.fileContent.slice(0, 1500), ctx.editor);
|
||||
},
|
||||
},
|
||||
|
||||
// ── 编辑 ──────────────────────────────────────────────────
|
||||
{
|
||||
id: "smart-complete",
|
||||
label: "智能补全",
|
||||
icon: "sparkles",
|
||||
description: "自动判断:补全/扩写/续写",
|
||||
group: "edit",
|
||||
when: { always: true },
|
||||
showIn: ["panel", "editor-menu", "file-menu"],
|
||||
run: (ctx) => {
|
||||
if (ctx.editor) {
|
||||
void ctx.plugin.runSmartComplete(ctx.editor);
|
||||
} else if (ctx.targetFile) {
|
||||
// file-menu: 先打开文件再执行
|
||||
void (async () => {
|
||||
const leaf = ctx.app.workspace.getLeaf(false);
|
||||
await leaf.openFile(ctx.targetFile!);
|
||||
setTimeout(() => {
|
||||
const ed = ctx.app.workspace.activeEditor?.editor;
|
||||
if (ed) void ctx.plugin.runSmartComplete(ed);
|
||||
}, 200);
|
||||
})();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "continue-writing",
|
||||
label: "续写",
|
||||
icon: "pencil",
|
||||
description: "从光标位置继续写",
|
||||
group: "edit",
|
||||
when: { noSelection: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor) return;
|
||||
const cursor = ctx.editor.getCursor();
|
||||
const before = ctx.editor.getRange({ line: 0, ch: 0 }, cursor);
|
||||
void ctx.plugin.runContinue(before, ctx.editor);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "append-section",
|
||||
label: "补充当前章节",
|
||||
icon: "plus-circle",
|
||||
group: "edit",
|
||||
when: { inSection: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor || !ctx.activeFile || !ctx.sectionName) return;
|
||||
void ctx.plugin.runSectionAppend(ctx.activeFile, ctx.sectionName, ctx.fileContent);
|
||||
},
|
||||
},
|
||||
|
||||
// ── 概念页 ────────────────────────────────────────────────
|
||||
{
|
||||
id: "complete-concept",
|
||||
label: "补全概念页",
|
||||
icon: "brain",
|
||||
group: "concept",
|
||||
when: { fileType: ["concept"], filePath: "Concepts/" },
|
||||
showIn: ["panel", "editor-menu", "file-menu"],
|
||||
run: (ctx) => { void ctx.plugin.completeCurrentConcept(); },
|
||||
},
|
||||
{
|
||||
id: "scan-empty-concepts",
|
||||
label: "扫描空概念页",
|
||||
icon: "search",
|
||||
group: "concept",
|
||||
when: { fileType: ["concept"], filePath: "Concepts/" },
|
||||
showIn: ["panel"],
|
||||
run: (ctx) => { void ctx.plugin.scanAndBatchComplete(); },
|
||||
},
|
||||
|
||||
// ── 阅读 ──────────────────────────────────────────────────
|
||||
{
|
||||
id: "resume-reading",
|
||||
label: "补全阅读项目",
|
||||
icon: "refresh-cw",
|
||||
description: "补全缺失章节的预设问题",
|
||||
group: "reading",
|
||||
when: { fileType: ["reading-project"] },
|
||||
showIn: ["panel", "editor-menu", "file-menu"],
|
||||
run: (ctx) => { void ctx.plugin.resumeReadingProject(); },
|
||||
},
|
||||
{
|
||||
id: "chapter-summary",
|
||||
label: "生成章节总结",
|
||||
icon: "file-text",
|
||||
group: "reading",
|
||||
when: { fileType: ["reading-note"] },
|
||||
showIn: ["panel", "editor-menu", "file-menu"],
|
||||
run: (ctx) => {
|
||||
const editor = ctx.editor ?? ctx.app.workspace.activeEditor?.editor;
|
||||
if (editor) void ctx.plugin.runChapterSummary(editor);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "feynman-test",
|
||||
label: "费曼检验",
|
||||
icon: "help-circle",
|
||||
description: "检验理解程度",
|
||||
group: "reading",
|
||||
when: { fileType: ["reading-note"] },
|
||||
showIn: ["panel", "editor-menu", "file-menu"],
|
||||
run: (ctx) => {
|
||||
const editor = ctx.editor ?? ctx.app.workspace.activeEditor?.editor;
|
||||
if (editor) void ctx.plugin.runFeynmanTest(editor);
|
||||
},
|
||||
},
|
||||
|
||||
// ── 文档工具 ──────────────────────────────────────────────
|
||||
{
|
||||
id: "analyze-document",
|
||||
label: "分析文档缺失",
|
||||
icon: "search",
|
||||
description: "AI 分析并建议补充内容",
|
||||
group: "document",
|
||||
when: { always: true },
|
||||
showIn: ["panel", "editor-menu"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor) return;
|
||||
void ctx.plugin.runDocumentAnalysis(ctx.fileContent, ctx.editor);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "smart-diagram",
|
||||
label: "智能生成图表",
|
||||
icon: "bar-chart-2",
|
||||
description: "AI 自动判断最合适的图表类型",
|
||||
group: "document",
|
||||
when: { hasSelection: true },
|
||||
showIn: ["panel"],
|
||||
run: (ctx) => {
|
||||
if (!ctx.editor) return;
|
||||
void ctx.plugin.runDiagramGeneration(ctx.selection, "auto", ctx.fileContent.slice(0, 800), ctx.editor);
|
||||
},
|
||||
},
|
||||
];
|
||||
195
src/actions/registry.ts
Normal file
195
src/actions/registry.ts
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import { Notice, TFile } from "obsidian";
|
||||
import type DeepSeekPlugin from "../main";
|
||||
import { ActionDef, ActionContext, ActionEntry, ActionGroup, GROUP_TITLES, GROUP_ORDER } from "./types";
|
||||
import { CommandPanelModal } from "../features/command-panel/CommandPanelModal";
|
||||
import type { PanelGroup, PanelAction } from "../features/command-panel/CommandPanelModal";
|
||||
import { SectionAppender } from "../ai/SectionAppender";
|
||||
|
||||
/**
|
||||
* 注册所有 actions 到插件的各个入口(命令、右键菜单、面板)
|
||||
*/
|
||||
export function registerAllActions(plugin: DeepSeekPlugin, actions: ActionDef[]) {
|
||||
// 1. 为每个 action 注册命令
|
||||
// 需要编辑器的 action 用 editorCallback(移动端工具栏可用)
|
||||
for (const action of actions) {
|
||||
const needsEditor = action.showIn.includes("editor-menu") || action.when.hasSelection || action.when.inSection || action.when.noSelection;
|
||||
|
||||
if (needsEditor) {
|
||||
plugin.addCommand({
|
||||
id: action.id,
|
||||
name: action.label,
|
||||
editorCallback: (editor) => {
|
||||
const ctx = buildContext(plugin, null);
|
||||
ctx.editor = editor;
|
||||
ctx.selection = editor.getSelection().trim();
|
||||
ctx.fileContent = editor.getValue();
|
||||
const cursor = editor.getCursor();
|
||||
const appender = new SectionAppender(plugin.app, plugin.settings);
|
||||
ctx.sectionName = appender.getSectionAtCursor(ctx.fileContent, cursor.line);
|
||||
if (evaluateWhen(action.when, ctx)) {
|
||||
action.run(ctx);
|
||||
} else {
|
||||
new Notice(`当前上下文不支持此操作`);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
plugin.addCommand({
|
||||
id: action.id,
|
||||
name: action.label,
|
||||
callback: () => {
|
||||
const ctx = buildContext(plugin, null);
|
||||
if (evaluateWhen(action.when, ctx)) {
|
||||
action.run(ctx);
|
||||
} else {
|
||||
new Notice(`当前上下文不支持此操作`);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 注册 editor-menu
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("editor-menu", (menu, editor) => {
|
||||
const ctx = buildContext(plugin, null);
|
||||
ctx.editor = editor;
|
||||
ctx.selection = editor.getSelection().trim();
|
||||
ctx.fileContent = editor.getValue();
|
||||
|
||||
// 重新计算 sectionName
|
||||
const cursor = editor.getCursor();
|
||||
const appender = new SectionAppender(plugin.app, plugin.settings);
|
||||
ctx.sectionName = appender.getSectionAtCursor(ctx.fileContent, cursor.line);
|
||||
|
||||
const visible = actions.filter(
|
||||
(a) => a.showIn.includes("editor-menu") && evaluateWhen(a.when, ctx)
|
||||
);
|
||||
|
||||
for (const action of visible) {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(`IStart-Note-AI: ${action.label}`)
|
||||
.setIcon(action.icon)
|
||||
.onClick(() => action.run(ctx));
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 3. 注册 file-menu
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("file-menu", (menu, file) => {
|
||||
if (!(file instanceof TFile) || file.extension !== "md") return;
|
||||
|
||||
const fileMeta = plugin.app.metadataCache.getFileCache(file);
|
||||
const fileType = fileMeta?.frontmatter?.type as string | undefined;
|
||||
|
||||
const ctx = buildContext(plugin, file);
|
||||
ctx.fileType = fileType;
|
||||
ctx.filePath = file.path;
|
||||
ctx.targetFile = file;
|
||||
|
||||
const visible = actions.filter(
|
||||
(a) => a.showIn.includes("file-menu") && evaluateWhen(a.when, ctx)
|
||||
);
|
||||
|
||||
for (const action of visible) {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(`IStart-Note-AI: ${action.label}`)
|
||||
.setIcon(action.icon)
|
||||
.onClick(() => action.run(ctx));
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 4. 注册面板打开命令
|
||||
plugin.addCommand({
|
||||
id: "open-panel",
|
||||
name: "Open command panel",
|
||||
callback: () => openPanel(plugin, actions),
|
||||
});
|
||||
|
||||
// 5. Ribbon icon 打开面板
|
||||
plugin.addRibbonIcon("brain", "IStart-Note-AI", () => {
|
||||
openPanel(plugin, actions);
|
||||
});
|
||||
}
|
||||
|
||||
/** 打开统一面板 */
|
||||
function openPanel(plugin: DeepSeekPlugin, actions: ActionDef[]) {
|
||||
const editor = plugin.app.workspace.activeEditor?.editor ?? null;
|
||||
const ctx = buildContext(plugin, null);
|
||||
if (editor) {
|
||||
ctx.editor = editor;
|
||||
ctx.selection = editor.getSelection().trim();
|
||||
ctx.fileContent = editor.getValue();
|
||||
const cursor = editor.getCursor();
|
||||
const appender = new SectionAppender(plugin.app, plugin.settings);
|
||||
ctx.sectionName = appender.getSectionAtCursor(ctx.fileContent, cursor.line);
|
||||
}
|
||||
|
||||
// 按 group 分组,过滤可见 actions
|
||||
const groups: PanelGroup[] = [];
|
||||
|
||||
for (const groupId of GROUP_ORDER) {
|
||||
const groupActions = actions.filter(
|
||||
(a) => a.group === groupId && a.showIn.includes("panel") && evaluateWhen(a.when, ctx)
|
||||
);
|
||||
if (groupActions.length === 0) continue;
|
||||
|
||||
const panelActions: PanelAction[] = groupActions.map((a) => ({
|
||||
id: a.id,
|
||||
icon: a.icon,
|
||||
label: a.label,
|
||||
description: a.description,
|
||||
callback: () => a.run(ctx),
|
||||
}));
|
||||
|
||||
groups.push({ title: GROUP_TITLES[groupId], actions: panelActions });
|
||||
}
|
||||
|
||||
new CommandPanelModal(plugin.app, groups).open();
|
||||
}
|
||||
|
||||
/** 构建当前上下文 */
|
||||
function buildContext(plugin: DeepSeekPlugin, targetFile: TFile | null): ActionContext {
|
||||
const activeFile = plugin.app.workspace.getActiveFile();
|
||||
const file = targetFile ?? activeFile;
|
||||
const fileMeta = file ? plugin.app.metadataCache.getFileCache(file) : null;
|
||||
|
||||
return {
|
||||
plugin,
|
||||
app: plugin.app,
|
||||
editor: null,
|
||||
activeFile,
|
||||
selection: "",
|
||||
fileContent: "",
|
||||
fileType: fileMeta?.frontmatter?.type as string | undefined,
|
||||
filePath: file?.path ?? "",
|
||||
sectionName: null,
|
||||
targetFile,
|
||||
};
|
||||
}
|
||||
|
||||
/** 评估可见性条件 */
|
||||
function evaluateWhen(when: ActionDef["when"], ctx: ActionContext): boolean {
|
||||
if (when.always) return true;
|
||||
|
||||
if (when.hasSelection && !ctx.selection) return false;
|
||||
if (when.noSelection && ctx.selection) return false;
|
||||
|
||||
if (when.fileType) {
|
||||
const match = when.fileType.some((t) => ctx.fileType === t) ||
|
||||
(when.filePath && ctx.filePath.includes(when.filePath));
|
||||
if (!match) return false;
|
||||
} else if (when.filePath) {
|
||||
if (!ctx.filePath.includes(when.filePath)) return false;
|
||||
}
|
||||
|
||||
if (when.inSection && !ctx.sectionName) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
61
src/actions/types.ts
Normal file
61
src/actions/types.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import type { App, Editor, TFile } from "obsidian";
|
||||
import type DeepSeekPlugin from "../main";
|
||||
|
||||
/** 动作执行时的上下文 */
|
||||
export interface ActionContext {
|
||||
plugin: DeepSeekPlugin;
|
||||
app: App;
|
||||
editor: Editor | null;
|
||||
activeFile: TFile | null;
|
||||
selection: string; // 选中文字(trim 后)
|
||||
fileContent: string; // 当前文件全文
|
||||
fileType: string | undefined; // frontmatter.type
|
||||
filePath: string; // 当前文件路径
|
||||
sectionName: string | null; // 光标所在 section 名
|
||||
// file-menu 专用:右键的目标文件(可能不是当前打开的文件)
|
||||
targetFile: TFile | null;
|
||||
}
|
||||
|
||||
/** 可见性条件 */
|
||||
export interface ActionWhen {
|
||||
always?: boolean; // 始终可见
|
||||
hasSelection?: boolean; // 需要有选中文字
|
||||
noSelection?: boolean; // 需要没有选中文字
|
||||
fileType?: string[]; // frontmatter type 匹配其一
|
||||
filePath?: string; // 文件路径包含此字符串
|
||||
inSection?: boolean; // 光标在某个 ## section 内
|
||||
}
|
||||
|
||||
/** 动作出现的入口 */
|
||||
export type ActionEntry = "panel" | "editor-menu" | "file-menu";
|
||||
|
||||
/** 面板分组 */
|
||||
export type ActionGroup = "general" | "selection" | "edit" | "concept" | "reading" | "sync" | "document";
|
||||
|
||||
/** 动作定义 */
|
||||
export interface ActionDef {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
description?: string;
|
||||
group: ActionGroup;
|
||||
when: ActionWhen;
|
||||
showIn: ActionEntry[];
|
||||
run: (ctx: ActionContext) => void;
|
||||
}
|
||||
|
||||
/** 分组标题映射 */
|
||||
export const GROUP_TITLES: Record<ActionGroup, string> = {
|
||||
general: "通用",
|
||||
selection: "选中文字",
|
||||
edit: "编辑",
|
||||
concept: "概念页",
|
||||
reading: "阅读",
|
||||
sync: "同步",
|
||||
document: "文档工具",
|
||||
};
|
||||
|
||||
/** 分组排序 */
|
||||
export const GROUP_ORDER: ActionGroup[] = [
|
||||
"general", "selection", "edit", "concept", "reading", "sync", "document",
|
||||
];
|
||||
|
|
@ -64,6 +64,7 @@ export class BaiduSyncView extends ItemView {
|
|||
this.makeBtn(btnRow, "⬆ 强制备份", "default", () => { void this.forceBackup(); });
|
||||
this.makeBtn(btnRow, "⬇ 强制更新", "default", () => { void this.forceUpdate(); });
|
||||
this.makeBtn(btnRow, "⇄ 双向同步", "cta", () => { void this.runSync(); });
|
||||
this.makeBtn(btnRow, "⚠️ 强制覆盖", "default", () => { void this.forceOverwrite(); });
|
||||
|
||||
// 上次扫描时间
|
||||
if (this.lastScanTime) {
|
||||
|
|
@ -251,6 +252,54 @@ export class BaiduSyncView extends ItemView {
|
|||
await this.scan();
|
||||
}
|
||||
|
||||
async forceOverwrite() {
|
||||
const cfg = this.plugin.settings.baiduSync;
|
||||
const service = new BaiduSyncService(this.app, cfg);
|
||||
const tokenOk = await service.ensureValidToken();
|
||||
if (!tokenOk) { new Notice("Token 已过期,请重新授权"); return; }
|
||||
|
||||
// 二次确认
|
||||
const confirmed = await new Promise<boolean>((resolve) => {
|
||||
const modal = new (class extends (require("obsidian") as { Modal: new (app: import("obsidian").App) => import("obsidian").Modal }).Modal {
|
||||
onOpen() {
|
||||
this.titleEl.setText("⚠️ 确认强制覆盖");
|
||||
this.contentEl.createEl("p", {
|
||||
text: "此操作将删除本地所有同步文件,然后从云端完整恢复。不可撤销!",
|
||||
cls: "istart-sync-modal-warning",
|
||||
});
|
||||
const { Setting } = require("obsidian") as typeof import("obsidian");
|
||||
new Setting(this.contentEl)
|
||||
.addButton((btn: import("obsidian").ButtonComponent) => btn.setButtonText("确认覆盖").setWarning().onClick(() => { this.close(); resolve(true); }))
|
||||
.addButton((btn: import("obsidian").ButtonComponent) => btn.setButtonText("取消").onClick(() => { this.close(); resolve(false); }));
|
||||
}
|
||||
onClose() { this.contentEl.empty(); resolve(false); }
|
||||
})(this.app);
|
||||
modal.open();
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
const notice = new Notice("⏳ 强制覆盖:清理本地...", 0);
|
||||
|
||||
// 删除本地文件
|
||||
const files = this.app.vault.getFiles().filter(
|
||||
(f) => !f.path.split("/").some((p) => p.startsWith("."))
|
||||
);
|
||||
for (const f of files) {
|
||||
try { await this.app.vault.delete(f); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// 从云端恢复
|
||||
notice.setMessage("⏳ 强制覆盖:从云端恢复...");
|
||||
const result = await service.restore("", true, (c, t, file) => {
|
||||
notice.setMessage(`⏳ 恢复中 (${c}/${t}):${file.split("/").pop()}`);
|
||||
});
|
||||
notice.hide();
|
||||
|
||||
new Notice(`✅ 强制覆盖完成:恢复 ${result.downloaded} 个文件`);
|
||||
await this.scan();
|
||||
}
|
||||
|
||||
private async uploadOne(path: string) {
|
||||
const cfg = this.plugin.settings.baiduSync;
|
||||
const abstract = this.app.vault.getAbstractFileByPath(path);
|
||||
|
|
|
|||
1516
src/main.ts
1516
src/main.ts
File diff suppressed because it is too large
Load diff
|
|
@ -13,5 +13,6 @@
|
|||
"1.7.0": "1.4.0",
|
||||
"1.7.1": "1.4.0",
|
||||
"1.7.2": "1.4.0",
|
||||
"1.7.3": "1.4.0"
|
||||
"1.7.3": "1.4.0",
|
||||
"1.7.4": "1.4.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue