由于Obsidian没有文件历史记录功能,就只做了文件创建时间和修改时间的检测,需要安装Dataview插件 做好了效果就像这样,可以点击箭头切换年份,鼠标悬停的时候也会显示修改和创建的笔记的数量
由于Obsidian对于过长的行数不会一次性加载全部,如果全部都用js代码的话就会导致点进笔记的时候不能加载出来,得滑动到下面才能加载
- 所以这里我们采用自定义CSS的方式来减少笔记的行数
dataviewjs
function renderHeatmap(targetYear) {
const container = dv.container;
container.innerHTML = "";
const createdMap = new Map();
const modifiedMap = new Map();
for (let page of dv.pages()) {
const c = page.file.ctime;
const m = page.file.mtime;
if (c.year === targetYear) {
const d = c.toISODate();
createdMap.set(d, (createdMap.get(d) || 0) + 1);
}
if (m.year === targetYear) {
const d = m.toISODate();
modifiedMap.set(d, (modifiedMap.get(d) || 0) + 1);
}
}
let start = dv.date(`${targetYear}-01-01`);
while (start.weekday !== 7) start = start.minus({ days: 1 });
let end = dv.date(`${targetYear}-12-31`);
while (end.weekday !== 6) end = end.plus({ days: 1 });
const dates = [];
let current = start;
while (current <= end) {
dates.push(current);
current = current.plus({ days: 1 });
}
const weeks = [];
for (let i = 0; i < dates.length; i += 7) {
weeks.push(dates.slice(i, i + 7));
}
// ==== 动态列数匹配样式 ====
const style = document.createElement("style");
style.textContent = `
.grid {
display: grid;
grid-template-columns: repeat(${weeks.length}, 10px);
gap: 1px;
}
.month-labels {
display: grid;
grid-template-columns: 12px repeat(${weeks.length}, 11px);
}
`;
document.head.appendChild(style);
function getLevel(n) {
if (!n) return "level-0";
if (n === 1) return "level-1";
if (n <= 3) return "level-2";
if (n <= 6) return "level-3";
return "level-4";
}
const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const monthStartCols = {};
weeks.forEach((week, weekIdx) => {
for (let day of week) {
if (day.day === 1 && monthStartCols[day.month - 1] === undefined) {
monthStartCols[day.month - 1] = weekIdx;
}
}
});
let monthHTML = `<div class="month-labels"><div></div>`;
for (let i = 0; i < 12; i++) {
if (monthStartCols[i] !== undefined) {
const left = 11 + monthStartCols[i] * 11;
monthHTML += `<div class="month-label" style="left: ${left}px;">${monthNames[i]}</div>`;
}
}
monthHTML += `</div>`;
const weekdayHTML = `
<div class="weekday-labels">
<span>S</span>
<span>M</span>
<span>T</span>
<span>W</span>
<span>T</span>
<span>F</span>
<span>S</span>
</div>
`;
let gridHTML = `<div class="grid">`;
for (let week of weeks) {
gridHTML += `<div class="week">`;
for (let d of week) {
const dateStr = d.toISODate();
const cCount = createdMap.get(dateStr) || 0;
const mCount = modifiedMap.get(dateStr) || 0;
let levelClass = "";
if (cCount > 0) {
levelClass = getLevel(cCount);
} else if (mCount > 0) {
levelClass = `mod-${getLevel(mCount)}`;
} else {
levelClass = "level-0";
}
const tooltipText = [
`${dateStr}`,
cCount > 0 ? `Created: ${cCount} notes` : "",
mCount > 0 ? `Modified: ${mCount} notes` : ""
].filter(Boolean).join("<br>");
gridHTML += `
<div class="day ${levelClass} tooltip-wrapper">
${tooltipText ? `<div class="tooltip-text">${tooltipText}</div>` : ""}
</div>`;
}
gridHTML += `</div>`;
}
gridHTML += `</div>`;
container.innerHTML += `
<div class="github-calendar-wrapper">
<div class="github-calendar">
<div class="calendar-header">
<button onclick="__render(${targetYear - 1})">⬅️</button>
<div class="year-label" onclick="__render(${new Date().getFullYear()})">${targetYear}</div>
<button onclick="__render(${targetYear + 1})">➡️</button>
</div>
${monthHTML}
<div class="grid-area">
${weekdayHTML}
${gridHTML}
</div>
</div>
</div>
`;
}
window.__render = renderHeatmap;
renderHeatmap(dv.date("today").year);
heatmapcalendar.css
文件资源管理器中导航至Vault\.obsidian\snippets
,将css文件放在里面
然后在Obsidian中设置-外观-自定义CSS代码
启用相关css代码