From 967f558908173f29bce370bcd58e2cbdb80e263e Mon Sep 17 00:00:00 2001 From: yan Date: Fri, 1 May 2026 21:18:21 +0800 Subject: [PATCH] release: 1.5.6 --- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- scripts/release.sh | 6 +- src/ConceptCompletionModal.ts | 2 +- src/QuestionGraphManager.ts | 3 +- src/SettingsTab.ts | 4 +- src/main.ts | 273 ++++++++++++++++------------------ versions.json | 3 +- 9 files changed, 147 insertions(+), 152 deletions(-) diff --git a/manifest.json b/manifest.json index 2c53cbd..4ab2ce0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "istart-note-ai", "name": "IStart-Note-AI", - "version": "1.5.5", + "version": "1.5.6", "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", diff --git a/package-lock.json b/package-lock.json index bcce5af..6efa95a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "istart-note-ai", - "version": "1.5.5", + "version": "1.5.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "istart-note-ai", - "version": "1.5.5", + "version": "1.5.6", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index f9c7b94..123aa1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "istart-note-ai", - "version": "1.5.5", + "version": "1.5.6", "description": "IStart-Note-AI: DeepSeek-powered knowledge graph plugin for Obsidian", "main": "main.js", "scripts": { diff --git a/scripts/release.sh b/scripts/release.sh index 5923ace..8e0b07a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -116,8 +116,10 @@ update_versions() { # Build # --------------------------------------------------------------------------- build_plugin() { - info "Installing dependencies..." - (cd "$PROJECT_DIR" && npm install --silent) + if [[ ! -d "$PROJECT_DIR/node_modules" ]]; then + info "Installing dependencies..." + (cd "$PROJECT_DIR" && npm install) + fi info "Building plugin (production)..." (cd "$PROJECT_DIR" && npm run build) diff --git a/src/ConceptCompletionModal.ts b/src/ConceptCompletionModal.ts index cde546d..2541637 100644 --- a/src/ConceptCompletionModal.ts +++ b/src/ConceptCompletionModal.ts @@ -65,7 +65,7 @@ export class PreviewModal extends Modal { const previewEl = contentEl.createDiv({ attr: { style: "max-height: 60vh; overflow-y: auto; border: 1px solid var(--background-modifier-border); border-radius: 4px; padding: 12px; margin-bottom: 16px;" } }); - MarkdownRenderer.render(this.app, this.previewMd, previewEl, "", this.component); + void MarkdownRenderer.render(this.app, this.previewMd, previewEl, "", this.component); new Setting(contentEl) .addButton((btn) => diff --git a/src/QuestionGraphManager.ts b/src/QuestionGraphManager.ts index ffa75ae..75cd1c8 100644 --- a/src/QuestionGraphManager.ts +++ b/src/QuestionGraphManager.ts @@ -93,7 +93,8 @@ export class QuestionGraphManager { await this.app.vault.create(indexPath, body); } - const indexFile = this.app.vault.getAbstractFileByPath(indexPath) as TFile; + const indexFile = this.app.vault.getAbstractFileByPath(indexPath); + if (!indexFile || !(indexFile instanceof TFile)) return; let content = await this.app.vault.read(indexFile); // 避免重复插入 diff --git a/src/SettingsTab.ts b/src/SettingsTab.ts index 5ebfdf7..71cc112 100644 --- a/src/SettingsTab.ts +++ b/src/SettingsTab.ts @@ -137,9 +137,9 @@ export class DeepSeekSettingsTab extends PluginSettingTab { text .setPlaceholder("your-app-secret") .setValue(this.plugin.settings.baiduSync.appSecret) - .onChange(async (v) => { + .onChange((v) => { this.plugin.settings.baiduSync.appSecret = v.trim(); - await this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); text.inputEl.type = "password"; return text; diff --git a/src/main.ts b/src/main.ts index 376b8c5..498d3f4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,41 +26,34 @@ export default class DeepSeekPlugin extends Plugin { async onload() { await this.loadSettings(); - // 侧边栏图标:提问 this.addRibbonIcon("brain", "DeepSeek ask", () => { this.openQuestionModal(); }); - // 侧边栏图标:百度云同步 this.addRibbonIcon("cloud", "Baidu cloud sync status", () => { void this.activateSyncView(); }); - // 注册同步状态视图 this.registerView(SYNC_VIEW_TYPE, (leaf) => new BaiduSyncView(leaf, this)); - // 命令:提问(不设默认快捷键) this.addCommand({ id: "ask-deepseek", name: "Ask DeepSeek and generate a knowledge note", callback: () => this.openQuestionModal(), }); - // 命令:补全当前概念页 this.addCommand({ id: "complete-current-concept", name: "Complete current concept page", callback: () => { void this.completeCurrentConcept(); }, }); - // 命令:扫描空概念页 this.addCommand({ id: "scan-empty-concepts", name: "Scan empty concept pages", callback: () => { void this.scanAndBatchComplete(); }, }); - // 命令:框选提问(不设默认快捷键) this.addCommand({ id: "context-qa", name: "Ask based on selection", @@ -75,7 +68,6 @@ export default class DeepSeekPlugin extends Plugin { }, }); - // 命令:补充当前章节 this.addCommand({ id: "append-current-section", name: "Append content to current section", @@ -94,36 +86,32 @@ export default class DeepSeekPlugin extends Plugin { }, }); - // 命令:问题图谱索引 this.addCommand({ id: "open-question-index", name: "Open question index", callback: () => { void this.openQuestionIndex(); }, }); - // 命令:百度云同步 this.addCommand({ id: "baidu-sync", name: "Baidu Netdisk sync / backup", callback: () => this.openBaiduSyncModal(), }); - // 命令:打开同步状态面板 this.addCommand({ id: "baidu-sync-view", name: "Open Baidu cloud sync status panel", callback: () => { void this.activateSyncView(); }, }); - // 命令:百度云授权 this.addCommand({ id: "baidu-auth", name: "Baidu Netdisk re-authorize", callback: () => this.openBaiduAuthModal(), }); - // 设置页 this.addSettingTab(new DeepSeekSettingsTab(this.app, this)); + this.registerEvent( this.app.workspace.on("file-menu", (menu, file) => { if (!(file instanceof TFile) || file.extension !== "md") return; @@ -132,30 +120,30 @@ export default class DeepSeekPlugin extends Plugin { item .setTitle("IStart-Note-AI: Complete this concept page") .setIcon("brain") - .onClick(async () => { - const manager = new ConceptPageManager(this.app, this.settings); - const info = await manager.analyzeFile(file); - if (!info) { - new Notice("该文件不是概念页"); - return; - } - new DepthSelectModal(this.app, info.conceptName, (depth) => { - void this.runConceptCompletion(info.file, info.conceptName, depth, { - sourceQuestion: info.sourceQuestion, - sourceAnswer: info.sourceAnswer, - }); - }).open(); + .onClick(() => { + void (async () => { + const manager = new ConceptPageManager(this.app, this.settings); + const info = await manager.analyzeFile(file); + if (!info) { + new Notice("该文件不是概念页"); + return; + } + new DepthSelectModal(this.app, info.conceptName, (depth) => { + void this.runConceptCompletion(info.file, info.conceptName, depth, { + sourceQuestion: info.sourceQuestion, + sourceAnswer: info.sourceAnswer, + }); + }).open(); + })(); }); }); }) ); - // 编辑器内右键菜单 this.registerEvent( this.app.workspace.on("editor-menu", (menu, editor) => { const selection = editor.getSelection().trim(); - // 框选提问入口(有选中内容时显示) if (selection) { menu.addItem((item) => { item @@ -168,7 +156,6 @@ export default class DeepSeekPlugin extends Plugin { }); } - // 章节补充入口(光标在 ## 章节内时显示) const cursor = editor.getCursor(); const fullContent = editor.getValue(); const appender = new SectionAppender(this.app, this.settings); @@ -186,7 +173,6 @@ export default class DeepSeekPlugin extends Plugin { }); } - // 概念补全入口 const linkMatch = selection.match(/^\[\[(.+?)(?:\|.+?)?\]\]$/) || selection.match(/^(.+)$/); const conceptName = linkMatch?.[1]; @@ -196,30 +182,33 @@ export default class DeepSeekPlugin extends Plugin { item .setTitle(`IStart-Note-AI: Complete concept "${conceptName}"`) .setIcon("brain") - .onClick(async () => { - const manager = new ConceptPageManager(this.app, this.settings); - const conceptsPath = this.settings.conceptsPath || "Knowledge/Concepts"; - const filePath = `${conceptsPath}/${conceptName}.md`; - let file = this.app.vault.getAbstractFileByPath(filePath); + .onClick(() => { + void (async () => { + const manager = new ConceptPageManager(this.app, this.settings); + const conceptsPath = this.settings.conceptsPath || "Knowledge/Concepts"; + const filePath = `${conceptsPath}/${conceptName}.md`; + let conceptFile = this.app.vault.getAbstractFileByPath(filePath); - if (!file || !(file instanceof TFile)) { - const writer = new VaultWriter(this.app, this.settings); - await writer.ensureConceptNote(conceptName); - file = this.app.vault.getAbstractFileByPath(filePath); - } + if (!conceptFile || !(conceptFile instanceof TFile)) { + const writer = new VaultWriter(this.app, this.settings); + await writer.ensureConceptNote(conceptName); + conceptFile = this.app.vault.getAbstractFileByPath(filePath); + } - if (!file || !(file instanceof TFile)) { - new Notice(`无法找到或创建概念页:${conceptName}`); - return; - } + if (!conceptFile || !(conceptFile instanceof TFile)) { + new Notice(`无法找到或创建概念页:${conceptName}`); + return; + } - const info = await manager.analyzeFile(file); - new DepthSelectModal(this.app, conceptName, (depth) => { - void this.runConceptCompletion(file as TFile, conceptName, depth, { - sourceQuestion: info?.sourceQuestion, - sourceAnswer: info?.sourceAnswer, - }); - }).open(); + const targetFile = conceptFile; + const info = await manager.analyzeFile(targetFile); + new DepthSelectModal(this.app, conceptName, (depth) => { + void this.runConceptCompletion(targetFile, conceptName, depth, { + sourceQuestion: info?.sourceQuestion, + sourceAnswer: info?.sourceAnswer, + }); + }).open(); + })(); }); }); @@ -246,7 +235,6 @@ export default class DeepSeekPlugin extends Plugin { const client = new ContextQAClient(this.settings); const graphManager = new QuestionGraphManager(this.app, this.settings); - // 获取周围段落作为补充上下文(取文件前 500 字) let surroundingContext: string | undefined; if (sourceNotePath) { const sourceFile = this.app.vault.getAbstractFileByPath(sourceNotePath); @@ -263,38 +251,38 @@ export default class DeepSeekPlugin extends Plugin { notice.hide(); - // 分类 const classifier = new QuestionClassifier(this.settings); const classifyNotice = new Notice("🔍 分析问题关系...", 0); const classification = await classifier.classify(question, history); classifyNotice.hide(); - new QuestionClassifyModal(this.app, question, classification, async (confirmed) => { - const writeNotice = new Notice("✍️ 写入笔记...", 0); - try { - const writer = new VaultWriter(this.app, this.settings); - const file = await writer.writeContextQANote( - { question, context, sourceNote: sourceNotePath, surroundingContext }, - response - ); + new QuestionClassifyModal(this.app, question, classification, (confirmed) => { + void (async () => { + const writeNotice = new Notice("✍️ 写入笔记...", 0); + try { + const writer = new VaultWriter(this.app, this.settings); + const file = await writer.writeContextQANote( + { question, context, sourceNote: sourceNotePath, surroundingContext }, + response + ); - await graphManager.attachClassification(file, question, confirmed, response.concepts); - await graphManager.appendRecommendations(file, confirmed); - await graphManager.updateQuestionIndex(question, confirmed, file.path); + await graphManager.attachClassification(file, question, confirmed, response.concepts); + await graphManager.appendRecommendations(file, confirmed); + await graphManager.updateQuestionIndex(question, confirmed, file.path); - writeNotice.hide(); - new Notice(`✅ 上下文笔记已生成:${file.name}`); + writeNotice.hide(); + new Notice(`✅ 上下文笔记已生成:${file.name}`); - const leaf = this.app.workspace.getLeaf(false); - await leaf.openFile(file); + const leaf = this.app.workspace.getLeaf(false); + await leaf.openFile(file); - // 自动备份 - void this.triggerAutoBackup(file.path); - } catch (err) { - writeNotice.hide(); - new Notice(`❌ 写入失败:${(err as Error).message}`); - console.error("[IStart-Note-AI]", err); - } + void this.triggerAutoBackup(file.path); + } catch (err) { + writeNotice.hide(); + new Notice(`❌ 写入失败:${(err as Error).message}`); + console.error("[IStart-Note-AI]", err); + } + })(); }).open(); } catch (err) { notice.hide(); @@ -328,35 +316,35 @@ export default class DeepSeekPlugin extends Plugin { const classification = await classifier.classify(question, history); classifyNotice.hide(); - new QuestionClassifyModal(this.app, question, classification, async (confirmed) => { - const writeNotice = new Notice("✍️ 写入笔记...", 0); - try { - const writer = new VaultWriter(this.app, this.settings); - const file = await writer.writeQANote(question, response); + new QuestionClassifyModal(this.app, question, classification, (confirmed) => { + void (async () => { + const writeNotice = new Notice("✍️ 写入笔记...", 0); + try { + const writer = new VaultWriter(this.app, this.settings); + const file = await writer.writeQANote(question, response); - await graphManager.attachClassification(file, question, confirmed, response.concepts); - await graphManager.appendRecommendations(file, confirmed); - await graphManager.updateQuestionIndex(question, confirmed, file.path); + await graphManager.attachClassification(file, question, confirmed, response.concepts); + await graphManager.appendRecommendations(file, confirmed); + await graphManager.updateQuestionIndex(question, confirmed, file.path); - writeNotice.hide(); - new Notice(`✅ 笔记已生成:${file.name}`); + writeNotice.hide(); + new Notice(`✅ 笔记已生成:${file.name}`); - const leaf = this.app.workspace.getLeaf(false); - await leaf.openFile(file); + const leaf = this.app.workspace.getLeaf(false); + await leaf.openFile(file); - if (this.settings.autoOpenGraph) { - // commands API is not in the public type definitions - const appWithCommands = this.app as unknown as { commands: { executeCommandById: (id: string) => void } }; - appWithCommands.commands.executeCommandById("graph:open"); + if (this.settings.autoOpenGraph) { + const appWithCommands = this.app as unknown as { commands: { executeCommandById: (id: string) => void } }; + appWithCommands.commands.executeCommandById("graph:open"); + } + + void this.triggerAutoBackup(file.path); + } catch (err) { + writeNotice.hide(); + new Notice(`❌ 写入失败:${(err as Error).message}`); + console.error("[DeepSeek Plugin]", err); } - - // 自动备份 - void this.triggerAutoBackup(file.path); - } catch (err) { - writeNotice.hide(); - new Notice(`❌ 写入失败:${(err as Error).message}`); - console.error("[DeepSeek Plugin]", err); - } + })(); }).open(); } catch (err) { notice.hide(); @@ -370,7 +358,7 @@ export default class DeepSeekPlugin extends Plugin { const indexPath = normalizePath(`${indexFolder}/问题索引.md`); let file = this.app.vault.getAbstractFileByPath(indexPath); if (!file || !(file instanceof TFile)) { - await this.app.vault.createFolder(indexFolder).catch(() => {}); + try { await this.app.vault.createFolder(indexFolder); } catch { /* exists */ } file = await this.app.vault.create(indexPath, "# 问题索引\n\n## 核心问题\n\n## 深化问题\n\n## 扩展问题\n"); } if (file instanceof TFile) { @@ -416,11 +404,13 @@ export default class DeepSeekPlugin extends Plugin { this.app, conceptName, previewMd, - async () => { - await manager.writeCompletion(file, result, depth); - new Notice(`✅ 概念页已补全:${conceptName}`); - const leaf = this.app.workspace.getLeaf(false); - await leaf.openFile(file); + () => { + void (async () => { + await manager.writeCompletion(file, result, depth); + new Notice(`✅ 概念页已补全:${conceptName}`); + const leaf = this.app.workspace.getLeaf(false); + await leaf.openFile(file); + })(); }, () => { void this.runConceptCompletion(file, conceptName, depth, context); @@ -433,7 +423,7 @@ export default class DeepSeekPlugin extends Plugin { } } - private async runSectionAppend(file: TFile, sectionName: string, content: string) { + private runSectionAppend(file: TFile, sectionName: string, content: string) { const appender = new SectionAppender(this.app, this.settings); const section = appender.extractSection(content, sectionName); const existingItems = section?.existing @@ -441,7 +431,6 @@ export default class DeepSeekPlugin extends Plugin { .filter((l) => l.trim().startsWith("-")) .length ?? 0; - // 获取概念名(文件名或 frontmatter.name) const meta = this.app.metadataCache.getFileCache(file); const conceptName = (meta?.frontmatter?.name as string) || file.basename; @@ -467,9 +456,11 @@ export default class DeepSeekPlugin extends Plugin { this.app, sectionName, result.items, - async () => { - await appender.appendToSection(file, sectionName, result.items); - new Notice(`✅ 已追加 ${result.items.length} 条内容到"${sectionName}"`); + () => { + void (async () => { + await appender.appendToSection(file, sectionName, result.items); + new Notice(`✅ 已追加 ${result.items.length} 条内容到"${sectionName}"`); + })(); }, () => { void this.generateAndPreviewSection(file, conceptName, sectionName, existingContent, count); @@ -489,29 +480,31 @@ export default class DeepSeekPlugin extends Plugin { const items = empties.map((e) => ({ name: e.conceptName, path: e.file.path })); - new BatchScanModal(this.app, items, async (selectedPaths, depth) => { - let done = 0; - for (const path of selectedPaths) { - const abstract = this.app.vault.getAbstractFileByPath(path); - if (!abstract || !(abstract instanceof TFile)) continue; - const info = await manager.analyzeFile(abstract); - if (!info) continue; + new BatchScanModal(this.app, items, (selectedPaths, depth) => { + void (async () => { + let done = 0; + for (const path of selectedPaths) { + const abstract = this.app.vault.getAbstractFileByPath(path); + if (!abstract || !(abstract instanceof TFile)) continue; + const info = await manager.analyzeFile(abstract); + if (!info) continue; - const n = new Notice(`⏳ 补全中 (${++done}/${selectedPaths.length}):${info.conceptName}`, 0); - try { - const completer = new ConceptCompleter(this.settings); - const result = await completer.complete(info.conceptName, depth, { - sourceQuestion: info.sourceQuestion, - sourceAnswer: info.sourceAnswer, - }); - await manager.writeCompletion(abstract, result, depth); - n.hide(); - } catch (err) { - n.hide(); - new Notice(`❌ ${info.conceptName} 补全失败:${(err as Error).message}`); + const n = new Notice(`⏳ 补全中 (${++done}/${selectedPaths.length}):${info.conceptName}`, 0); + try { + const completer = new ConceptCompleter(this.settings); + const result = await completer.complete(info.conceptName, depth, { + sourceQuestion: info.sourceQuestion, + sourceAnswer: info.sourceAnswer, + }); + await manager.writeCompletion(abstract, result, depth); + n.hide(); + } catch (err) { + n.hide(); + new Notice(`❌ ${info.conceptName} 补全失败:${(err as Error).message}`); + } } - } - new Notice(`✅ 批量补全完成,共 ${done} 个概念页`); + new Notice(`✅ 批量补全完成,共 ${done} 个概念页`); + })(); }).open(); } @@ -542,15 +535,16 @@ export default class DeepSeekPlugin extends Plugin { new Notice("请先在设置中启用百度云同步"); return; } - new BaiduAuthModal(this.app, this.settings.baiduSync, async (accessToken, refreshToken, expiresAt) => { - this.settings.baiduSync.accessToken = accessToken; - this.settings.baiduSync.refreshToken = refreshToken; - this.settings.baiduSync.tokenExpiresAt = expiresAt; - await this.saveSettings(); + new BaiduAuthModal(this.app, this.settings.baiduSync, (accessToken, refreshToken, expiresAt) => { + void (async () => { + this.settings.baiduSync.accessToken = accessToken; + this.settings.baiduSync.refreshToken = refreshToken; + this.settings.baiduSync.tokenExpiresAt = expiresAt; + await this.saveSettings(); + })(); }).open(); } - /** 自动备份触发(生成笔记后调用) */ private async triggerAutoBackup(filePath: string) { const cfg = this.settings.baiduSync; if (!cfg.enabled || !cfg.autoBackup || !cfg.accessToken) return; @@ -563,7 +557,7 @@ export default class DeepSeekPlugin extends Plugin { const folder = filePath.includes("/") ? filePath.substring(0, filePath.lastIndexOf("/")) : ""; await service.backup(folder); } catch { - // 自动备份失败静默处理,不打扰用户 + // 自动备份失败静默处理 } } @@ -571,7 +565,6 @@ export default class DeepSeekPlugin extends Plugin { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); this.settings.baiduSync = Object.assign({}, DEFAULT_BAIDU_SYNC_CONFIG, this.settings.baiduSync); - // 启动时静默拉取远端配置(如果已授权) if (this.settings.baiduSync.enabled && this.settings.baiduSync.accessToken) { void this.pullConfig(true); } @@ -581,7 +574,6 @@ export default class DeepSeekPlugin extends Plugin { await this.saveData(this.settings); } - /** 推送当前配置(不含凭证)到百度云 */ async pushConfig() { const cfg = this.settings.baiduSync; if (!cfg.enabled || !cfg.accessToken) { @@ -595,7 +587,6 @@ export default class DeepSeekPlugin extends Plugin { new Notice(ok ? "✅ 配置已推送到百度云" : "❌ 配置推送失败"); } - /** 从百度云拉取配置并应用 */ async pullConfig(silent = false) { const cfg = this.settings.baiduSync; if (!cfg.enabled || !cfg.accessToken) return; diff --git a/versions.json b/versions.json index d301648..54e7439 100644 --- a/versions.json +++ b/versions.json @@ -5,5 +5,6 @@ "1.5.2": "1.4.0", "1.5.3": "1.4.0", "1.5.4": "1.4.0", - "1.5.5": "1.4.0" + "1.5.5": "1.4.0", + "1.5.6": "1.4.0" }