写了个QuickAdd的js脚本,直接在弹窗里修改笔记属性

如果Yaml区设置隐藏了,虽然侧边栏也可以修改属性,但是我不喜欢用侧栏 :grin:,所以找写了个 Quickadd 脚本

09-01 来更新一下:在弹出窗口底部以多彩胶囊的形式加入了最常用的属性名推荐,也可以在代码里手动添加希望显示的属性名,点击自动把属性名加入输入框,不用手打了 :sweat_smile:

PS: 推荐用 Command 插件把命令绑在编辑器菜单,修改属性更丝滑省力

代码如下,保存成js,在QuickAdd里创建一个Marco,User Scripts添加上就可以用了。

module.exports = async (params) => {
    const app = params.app;
    const file = app.workspace.getActiveFile();
    if (!file) {
        new Notice("❌ 没有打开的笔记");
        return;
    }

    const cache = app.metadataCache.getFileCache(file);
    const fm = cache?.frontmatter || {};

    // 获取 vault 里所有 frontmatter 的 key 频率
    const allFiles = app.vault.getMarkdownFiles();
    const freqMap = {};
    for (let f of allFiles) {
        const c = app.metadataCache.getFileCache(f);
        if (!c?.frontmatter) continue;
        for (let key in c.frontmatter) {
            if (key === "position") continue;
            freqMap[key] = (freqMap[key] || 0) + 1;
        }
    }

    // 排序取前10
    const topKeys = Object.entries(freqMap)
        .sort((a, b) => b[1] - a[1])
        .map(([k]) => k)
        .slice(0, 20);

    // 你的自定义推荐
    const customKeys = ["img", "chapter"];

    // 合并 & 去重 & 去掉当前已有属性
    const recommendKeys = [...new Set([...customKeys, ...topKeys])]
        .filter(k => !(k in fm));

    // 当前 frontmatter 拼成文本
    let text = "";
    for (let key in fm) {
        if (key === "position") continue;
        let value = fm[key];
        if (Array.isArray(value)) value = `[${value.join(", ")}]`;
        text += `${key}: ${value}\n`;
    }

    // 创建弹窗
    const container = document.createElement("div");
    container.style.position = "fixed";
    container.style.top = "50%";
    container.style.left = "50%";
    container.style.transform = "translate(-50%, -50%)";
    container.style.backgroundColor = "var(--background-primary)";
    container.style.padding = "12px";
    container.style.borderRadius = "8px";
    container.style.boxShadow = "0 0 20px rgba(0,0,0,0.3)";
    container.style.zIndex = 9999;
    container.style.width = "500px";
    container.style.maxHeight = "80%";
    container.style.overflowY = "auto";

    // 渲染推荐属性胶囊
    let chipsHTML = recommendKeys.map(k => `
        <span class="prop-chip" data-key="${k}" style="
            display:inline-block;
            margin:4px;
            padding:2px 8px;
            font-size:12px;
            border-radius:12px;
            cursor:pointer;
            background: hsl(${Math.floor(Math.random()*360)},70%,85%);
            color: var(--text-normal);
            white-space: nowrap;
        ">${k}</span>
    `).join("");

    container.innerHTML = `
        <div style="text-align:center;font-weight:bold;font-size:18px;margin-bottom:12px;">
            🏷 编辑笔记属性
        </div>

        <textarea id="fmText" style="
            width: 100%;
            height: 280px;
            padding: 8px;
            border-radius: 6px;
            border: 1px solid var(--interactive-normal);
            font-family: var(--font-monospace);
            font-size: 14px;
            resize: vertical;
        ">${text}</textarea>

        <div style="margin-top:10px; font-size:12px; color:var(--text-muted);">
            <div>${chipsHTML}</div>
        </div>

        <div style="margin-top:12px; text-align:right; display:flex; justify-content:flex-end; gap:8px;">
            <button id="cancelBtn">❌ 取消</button>
            <button id="saveBtn">✔ 保存</button>
        </div>
    `;

    document.body.appendChild(container);

    const textarea = container.querySelector("#fmText");

    // 点击推荐属性 -> 在 textarea 末尾追加
    container.querySelectorAll(".prop-chip").forEach(el => {
        el.addEventListener("click", () => {
            const key = el.dataset.key;
            textarea.value = textarea.value.trimEnd() + `\n${key}: `;
        });
    });

    const cleanup = () => {
        document.body.removeChild(container);
    };

    container.querySelector("#cancelBtn").addEventListener("click", cleanup);

    container.querySelector("#saveBtn").addEventListener("click", async () => {
        const result = textarea.value;

        await app.fileManager.processFrontMatter(file, (fm) => {
            for (let key in fm) delete fm[key];
            const lines = result.split("\n");
            for (let line of lines) {
                line = line.trim();
                if (!line) continue;
                const [key, ...rest] = line.split(":");
                if (!key) continue;
                let value = rest.join(":").trim();
                if (value.startsWith("[") && value.endsWith("]")) {
                    value = value.slice(1, -1).split(",").map(v => v.trim()).filter(v => v);
                }
                fm[key.trim()] = value;
            }
        });

        cleanup();
        new Notice("✅ 属性已更新");
    });
};
1 个赞