如果Yaml区设置隐藏了,虽然侧边栏也可以修改属性,但是我不喜欢用侧栏
,所以找写了个 Quickadd 脚本
09-01 来更新一下:在弹出窗口底部以多彩胶囊的形式加入了最常用的属性名推荐,也可以在代码里手动添加希望显示的属性名,点击自动把属性名加入输入框,不用手打了 ![]()
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("✅ 属性已更新");
});
};
