From 62332e5008906d4ca0161a637228960b5f3623d1 Mon Sep 17 00:00:00 2001 From: Chenyan Wang Date: Sun, 3 May 2026 20:52:40 +0800 Subject: [PATCH] Fix bot scan Required issues: async/await, unhandled promises, sentence case --- NEXT_STEPS.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.js | 76 ++++++++++++++++++++++++---------------------- src/main.ts | 78 ++++++++++++++++++++++++----------------------- 3 files changed, 164 insertions(+), 73 deletions(-) create mode 100644 NEXT_STEPS.md diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md new file mode 100644 index 0000000..a72b733 --- /dev/null +++ b/NEXT_STEPS.md @@ -0,0 +1,83 @@ +# Smart Folder View - 下一步执行清单 + +更新时间:2026-05-03 + +## 当前状态 + +- 社区插件提交流程已完成:PR 已创建并通过自动校验(Ready for review)。 +- 插件仓库已发布 `0.1.0` 正式 Release(非 pre-release)。 + +## 下次开工先做这 3 件事 + +1. 打开社区 PR,查看是否有人工审核评论。 +2. 如果有修改意见:在仓库修复 -> 发新 Release 资产 -> 在同一个 PR 回复“已修复”。 +3. 如果没有评论:转去完善 README(截图/GIF/使用示例),提升安装转化。 + +## 审核阶段操作规范 + +- 不要新开 PR,所有修改都在现有 PR 上继续。 +- 不要关闭 PR。 +- 每次修改后都在 PR 下留一句简短回复,说明改了什么。 +- 如果改了影响发布的文件(如 `main.js`、`manifest.json`、`styles.css`),要同步更新 GitHub Release 资产。 + +## README 强化清单(优先级高) + +- [ ] 增加 1 张总览截图(时间轴 + 看板) +- [ ] 增加 3-4 段短 GIF(每段 10-20 秒) +- [ ] 增加“30 秒上手”步骤 +- [ ] 增加“典型使用场景” +- [ ] 增加“常见问题 / 排查” +- [ ] 增加中英文标题锚点(便于分享) + +## 演示素材脚本(可直接照着录) + +### GIF 1:创建页面 + +1. 打开命令:Create Smart Folder Page +2. 选择来源文件夹、模板、字段 +3. 点击 Preview +4. 点击 Save and Open + +### GIF 2:时间轴操作 + +1. 切换筛选条件 +2. 拖动卡片调整顺序 +3. 点击 Undo last action + +### GIF 3:看板操作 + +1. 切换到 Board +2. 跨列拖拽卡片 +3. 展示自动更新分组字段 + +### GIF 4:预设与复用 + +1. 保存 preset +2. 切换 preset +3. 展示状态持久化 + +## 版本发布 SOP(后续每个版本都按这个走) + +1. 修改代码。 +2. 更新 `manifest.json` 的 `version` 与必要字段。 +3. 更新 `versions.json`:新增 `"新版本": "最低兼容版本"`。 +4. 打包并创建 GitHub Release(tag 必须与版本号完全一致,不加 `v`)。 +5. 上传 Release 资产: + - `main.js` + - `manifest.json` + - `styles.css`(如有) +6. 在社区 PR(审核期)或新版本发布说明(已上架后)同步说明变更。 + +## 你可以晚点再做的优化 + +- [ ] 配置 Git 全局身份,消除 commit 提示: + - `git config --global user.name "your-name"` + - `git config --global user.email "your-email"` +- [ ] 补一个 `CHANGELOG.md`(可选) +- [ ] 增加 issue 模板(bug / feature request) + +## 快速入口链接 + +- 插件仓库: +- 社区提交 PR: +- Obsidian 插件指南: diff --git a/main.js b/main.js index b2c88f5..6ddaf0f 100644 --- a/main.js +++ b/main.js @@ -156,11 +156,11 @@ var I18N = { }, en: { title: "Smart Folder View", - openBuilder: "Create Smart Folder Page", - openManager: "Manage Saved Pages", - openLast: "Open Last Page", + openBuilder: "Create Smart Folder page", + openManager: "Manage saved pages", + openLast: "Open last page", noProfile: "No saved pages yet. Please create one first.", - setupTitle: "Configure Page", + setupTitle: "Configure page", setupDesc: "Page settings anchor data source: folder, template file, and filterable fields. Preview first, then save.", profileName: "Page name", sourceFolder: "Source folder", @@ -181,19 +181,19 @@ var I18N = { board: "Board", asc: "Ascending (old -> new)", desc: "Descending (new -> old)", - savePage: "Save Page", - updatePage: "Update Page", - openBuilderBtn: "Create Page", - openManagerBtn: "Page Manager", + savePage: "Save page", + updatePage: "Update page", + openBuilderBtn: "Create page", + openManagerBtn: "Page manager", startActionTitle: "Smart Folder", startActionDesc: "Choose an action:", - startOpenPage: "Open Page", - startNewPage: "Create Page", + startOpenPage: "Open page", + startNewPage: "Create page", pageSaved: "Page saved", pageUpdated: "Page updated", saveFailed: "Failed to save page", pageNamePrompt: "Enter page name", - managerTitle: "Saved Pages", + managerTitle: "Saved pages", open: "Open", edit: "Edit", delete: "Delete", @@ -281,7 +281,7 @@ function detectLanguage() { try { const locale = window.moment?.locale?.() || ""; if (locale.toLowerCase().startsWith("zh")) return "zh"; - } catch (_e) { + } catch { } return "en"; } @@ -720,7 +720,7 @@ var ProfileManagerModal = class extends obsidian.Modal { contentEl.createEl("h2", { text: this.plugin.t("managerTitle") }); for (const profile of this.plugin.data.profiles) { const s = new obsidian.Setting(contentEl).setName(profile.name || profile.sourceFolder).setDesc(profile.sourceFolder); - s.addButton((b) => b.setButtonText(this.plugin.t("open")).onClick(async () => { + s.addButton((b) => b.setButtonText(this.plugin.t("open")).onClick(() => { void (async () => { this.plugin.clearDraftProfile(); this.plugin.data.lastActiveProfileId = profile.id; @@ -891,7 +891,7 @@ var SmartFolderView = class extends obsidian.ItemView { async rerenderPreserveScroll() { const container = this.containerEl.children[1]; const scrollTop = container?.scrollTop || 0; - await this.renderView(); + this.renderView(); const nextContainer = this.containerEl.children[1]; await new Promise((resolve) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); if (nextContainer) nextContainer.scrollTop = scrollTop; @@ -1014,7 +1014,7 @@ var SmartFolderView = class extends obsidian.ItemView { e.preventDefault(); this.app.workspace.openLinkText(href, file.path, e.ctrlKey || e.metaKey); }, true); - this.app.vault.read(file).then((raw) => { + void this.app.vault.read(file).then((raw) => { const body = stripFrontmatter(raw) || "\uFF08\u65E0\u6B63\u6587\uFF09"; obsidian.MarkdownRenderer.render(this.app, body, bodyEl, file.path, this); }).catch(() => bodyEl.setText("\uFF08\u6B63\u6587\u8BFB\u53D6\u5931\u8D25\uFF09")); @@ -1124,13 +1124,15 @@ var SmartFolderView = class extends obsidian.ItemView { topBar.addClass("sfv-top-bar"); const saveBtn = topBar.createEl("button", { text: this.plugin.isDraft(profile.id) ? this.plugin.t("savePage") : this.plugin.t("updatePage") }); saveBtn.addClass("mod-cta"); - saveBtn.addEventListener("click", async () => { - try { - await this.plugin.saveProfile(profile, true, false); - await this.rerenderPreserveScroll(); - } catch { - new obsidian.Notice(this.plugin.t("saveFailed")); - } + saveBtn.addEventListener("click", () => { + void (async () => { + try { + await this.plugin.saveProfile(profile, true, false); + await this.rerenderPreserveScroll(); + } catch { + new obsidian.Notice(this.plugin.t("saveFailed")); + } + })(); }); topBar.createEl("button", { text: this.plugin.t("openBuilderBtn") }).addEventListener("click", () => new SetupModal(this.app, this.plugin).open()); topBar.createEl("button", { text: this.plugin.t("openManagerBtn") }).addEventListener("click", () => new ProfileManagerModal(this.app, this.plugin).open()); @@ -1331,14 +1333,16 @@ var SmartFolderView = class extends obsidian.ItemView { this.saveOrder(profile.id, this.orderKey(profile), ordered.map((e) => e.file)); new obsidian.Notice(this.plugin.t("saveOrderDone")); }); - orderActions2.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", async () => { - this.recordUndoSnapshot(profile); - this.clearOrder(profile.id, this.orderKey(profile)); - new obsidian.Notice(this.plugin.t("resetOrderDone")); - await this.rerenderPreserveScroll(); + orderActions2.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", () => { + void (async () => { + this.recordUndoSnapshot(profile); + this.clearOrder(profile.id, this.orderKey(profile)); + new obsidian.Notice(this.plugin.t("resetOrderDone")); + await this.rerenderPreserveScroll(); + })(); }); - orderActions2.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", async () => { - await this.undoLastAction(profile); + orderActions2.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", () => { + void this.undoLastAction(profile); }); const timelineEl = container.createEl("div"); timelineEl.addClass("sfv-timeline"); @@ -1495,14 +1499,14 @@ var SmartFolderView = class extends obsidian.ItemView { } new obsidian.Notice(this.plugin.t("saveOrderDone")); }); - orderActions.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", async () => { + orderActions.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", () => { this.recordUndoSnapshot(profile); for (const col of columns) this.clearOrder(profile.id, this.orderKey(profile, col)); new obsidian.Notice(this.plugin.t("resetOrderDone")); - await this.renderView(); + this.renderView(); }); - orderActions.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", async () => { - await this.undoLastAction(profile); + orderActions.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", () => { + void this.undoLastAction(profile); }); const boardEl = container.createEl("div"); boardEl.addClass("sfv-board"); @@ -1845,7 +1849,7 @@ var SmartFolderPlugin = class extends obsidian.Plugin { async onload() { await this.loadPluginData(); this.registerView(VIEW_TYPE, (leaf) => new SmartFolderView(leaf, this)); - this.registerMarkdownCodeBlockProcessor("smart-folder-page", async (source, el) => { + this.registerMarkdownCodeBlockProcessor("smart-folder-page", (source, el) => { el.empty(); let raw; try { @@ -1939,7 +1943,7 @@ var SmartFolderPlugin = class extends obsidian.Plugin { leaf = workspace.getLeaf("tab"); await leaf.setViewState({ type: VIEW_TYPE, active: true }); } - workspace.revealLeaf(leaf); + void workspace.revealLeaf(leaf); const view = leaf.view; if (view instanceof SmartFolderView) { view.setPinnedProfile(profile || null); @@ -1947,7 +1951,7 @@ var SmartFolderPlugin = class extends obsidian.Plugin { this.data.lastActiveProfileId = profile.id; await this.persist(); } - await view.onOpen(); + view.onOpen(); } } onunload() { diff --git a/src/main.ts b/src/main.ts index 987e5bd..81cb3e0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -135,11 +135,11 @@ const I18N = { }, en: { title: 'Smart Folder View', - openBuilder: 'Create Smart Folder Page', - openManager: 'Manage Saved Pages', - openLast: 'Open Last Page', + openBuilder: 'Create Smart Folder page', + openManager: 'Manage saved pages', + openLast: 'Open last page', noProfile: 'No saved pages yet. Please create one first.', - setupTitle: 'Configure Page', + setupTitle: 'Configure page', setupDesc: 'Page settings anchor data source: folder, template file, and filterable fields. Preview first, then save.', profileName: 'Page name', sourceFolder: 'Source folder', @@ -160,19 +160,19 @@ const I18N = { board: 'Board', asc: 'Ascending (old -> new)', desc: 'Descending (new -> old)', - savePage: 'Save Page', - updatePage: 'Update Page', - openBuilderBtn: 'Create Page', - openManagerBtn: 'Page Manager', + savePage: 'Save page', + updatePage: 'Update page', + openBuilderBtn: 'Create page', + openManagerBtn: 'Page manager', startActionTitle: 'Smart Folder', startActionDesc: 'Choose an action:', - startOpenPage: 'Open Page', - startNewPage: 'Create Page', + startOpenPage: 'Open page', + startNewPage: 'Create page', pageSaved: 'Page saved', pageUpdated: 'Page updated', saveFailed: 'Failed to save page', pageNamePrompt: 'Enter page name', - managerTitle: 'Saved Pages', + managerTitle: 'Saved pages', open: 'Open', edit: 'Edit', delete: 'Delete', @@ -262,7 +262,7 @@ function detectLanguage() { try { const locale = window.moment?.locale?.() || ''; if (locale.toLowerCase().startsWith('zh')) return 'zh'; - } catch (_e) { + } catch { // ignore } return 'en'; @@ -768,7 +768,7 @@ class ProfileManagerModal extends obsidian.Modal { for (const profile of this.plugin.data.profiles) { const s = new obsidian.Setting(contentEl).setName(profile.name || profile.sourceFolder).setDesc(profile.sourceFolder); - s.addButton(b => b.setButtonText(this.plugin.t('open')).onClick(async () => { + s.addButton(b => b.setButtonText(this.plugin.t('open')).onClick(() => { void (async () => { this.plugin.clearDraftProfile(); this.plugin.data.lastActiveProfileId = profile.id; @@ -950,7 +950,7 @@ class SmartFolderView extends obsidian.ItemView { async rerenderPreserveScroll() { const container = this.containerEl.children[1]; const scrollTop = container?.scrollTop || 0; - await this.renderView(); + this.renderView(); const nextContainer = this.containerEl.children[1]; // Wait two animation frames to ensure browser has completed layout before restoring scroll await new Promise(resolve => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); @@ -1087,7 +1087,7 @@ class SmartFolderView extends obsidian.ItemView { e.preventDefault(); this.app.workspace.openLinkText(href, file.path, e.ctrlKey || e.metaKey); }, true); - this.app.vault.read(file).then(raw => { + void this.app.vault.read(file).then(raw => { const body = stripFrontmatter(raw) || '(无正文)'; obsidian.MarkdownRenderer.render(this.app, body, bodyEl, file.path, this); }).catch(() => bodyEl.setText('(正文读取失败)')); @@ -1215,14 +1215,16 @@ class SmartFolderView extends obsidian.ItemView { topBar.addClass('sfv-top-bar'); const saveBtn = topBar.createEl('button', { text: this.plugin.isDraft(profile.id) ? this.plugin.t('savePage') : this.plugin.t('updatePage') }); saveBtn.addClass('mod-cta'); - saveBtn.addEventListener('click', async () => { - try { - // Save current page config directly into saved pages/history list. - await this.plugin.saveProfile(profile, true, false); - await this.rerenderPreserveScroll(); - } catch { - new obsidian.Notice(this.plugin.t('saveFailed')); - } + saveBtn.addEventListener('click', () => { + void (async () => { + try { + // Save current page config directly into saved pages/history list. + await this.plugin.saveProfile(profile, true, false); + await this.rerenderPreserveScroll(); + } catch { + new obsidian.Notice(this.plugin.t('saveFailed')); + } + })(); }); topBar.createEl('button', { text: this.plugin.t('openBuilderBtn') }).addEventListener('click', () => new SetupModal(this.app, this.plugin).open()); topBar.createEl('button', { text: this.plugin.t('openManagerBtn') }).addEventListener('click', () => new ProfileManagerModal(this.app, this.plugin).open()); @@ -1446,14 +1448,16 @@ class SmartFolderView extends obsidian.ItemView { this.saveOrder(profile.id, this.orderKey(profile), ordered.map(e => e.file)); new obsidian.Notice(this.plugin.t('saveOrderDone')); }); - orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', async () => { - this.recordUndoSnapshot(profile); - this.clearOrder(profile.id, this.orderKey(profile)); - new obsidian.Notice(this.plugin.t('resetOrderDone')); - await this.rerenderPreserveScroll(); + orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', () => { + void (async () => { + this.recordUndoSnapshot(profile); + this.clearOrder(profile.id, this.orderKey(profile)); + new obsidian.Notice(this.plugin.t('resetOrderDone')); + await this.rerenderPreserveScroll(); + })(); }); - orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', async () => { - await this.undoLastAction(profile); + orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', () => { + void this.undoLastAction(profile); }); const timelineEl = container.createEl('div'); @@ -1624,14 +1628,14 @@ class SmartFolderView extends obsidian.ItemView { } new obsidian.Notice(this.plugin.t('saveOrderDone')); }); - orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', async () => { + orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', () => { this.recordUndoSnapshot(profile); for (const col of columns) this.clearOrder(profile.id, this.orderKey(profile, col)); new obsidian.Notice(this.plugin.t('resetOrderDone')); - await this.renderView(); + this.renderView(); }); - orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', async () => { - await this.undoLastAction(profile); + orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', () => { + void this.undoLastAction(profile); }); const boardEl = container.createEl('div'); @@ -2067,7 +2071,7 @@ class SmartFolderPlugin extends obsidian.Plugin { this.registerView(VIEW_TYPE, leaf => new SmartFolderView(leaf, this)); - this.registerMarkdownCodeBlockProcessor('smart-folder-page', async (source, el) => { + this.registerMarkdownCodeBlockProcessor('smart-folder-page', (source, el) => { el.empty(); let raw; try { @@ -2165,7 +2169,7 @@ class SmartFolderPlugin extends obsidian.Plugin { leaf = workspace.getLeaf('tab'); await leaf.setViewState({ type: VIEW_TYPE, active: true }); } - workspace.revealLeaf(leaf); + void workspace.revealLeaf(leaf); const view = leaf.view; if (view instanceof SmartFolderView) { view.setPinnedProfile(profile || null); @@ -2173,7 +2177,7 @@ class SmartFolderPlugin extends obsidian.Plugin { this.data.lastActiveProfileId = profile.id; await this.persist(); } - await view.onOpen(); + view.onOpen(); } }