Added
This commit is contained in:
353
HomePage.md
353
HomePage.md
@@ -319,3 +319,356 @@ dv.paragraph(md);
|
||||
![[Wohnorte]]
|
||||
|
||||
---
|
||||
# Stats
|
||||
```dataviewjs
|
||||
// === Config ===
|
||||
const splitOnSlash = false; // true -> merge hierarchical tags (#project/obsidian -> project)
|
||||
const topN = 50; // show top N tags (null for all)
|
||||
|
||||
// === Catppuccin Mocha Palette ===
|
||||
const catppuccin = {
|
||||
rosewater: '#f5e0dc',
|
||||
flamingo: '#f2cdcd',
|
||||
pink: '#f5c2e7',
|
||||
mauve: '#cba6f7',
|
||||
red: '#f38ba8',
|
||||
maroon: '#eba0ac',
|
||||
peach: '#fab387',
|
||||
yellow: '#f9e2af',
|
||||
green: '#a6e3a1',
|
||||
teal: '#94e2d5',
|
||||
sky: '#89dceb',
|
||||
sapphire: '#74c7ec',
|
||||
blue: '#89b4fa',
|
||||
lavender: '#b4befe',
|
||||
text: '#cdd6f4',
|
||||
subtext1: '#bac2de',
|
||||
base: '#1e1e2e'
|
||||
};
|
||||
|
||||
const catppuccinColors = [
|
||||
catppuccin.rosewater,
|
||||
catppuccin.flamingo,
|
||||
catppuccin.pink,
|
||||
catppuccin.mauve,
|
||||
catppuccin.red,
|
||||
catppuccin.maroon,
|
||||
catppuccin.peach,
|
||||
catppuccin.yellow,
|
||||
catppuccin.green,
|
||||
catppuccin.teal,
|
||||
catppuccin.sky,
|
||||
catppuccin.sapphire,
|
||||
catppuccin.blue,
|
||||
catppuccin.lavender
|
||||
];
|
||||
|
||||
// === Helpers ===
|
||||
function flattenTags(input, out = []) {
|
||||
if (!input) return out;
|
||||
if (Array.isArray(input)) {
|
||||
for (const v of input) flattenTags(v, out);
|
||||
} else if (typeof input === 'string') {
|
||||
out.push(input);
|
||||
} else if (typeof input === 'object') {
|
||||
if (typeof input.tag === 'string') out.push(input.tag);
|
||||
else if (typeof input.path === 'string') out.push(input.path);
|
||||
else {
|
||||
const s = String(input);
|
||||
if (s && s !== '[object Object]') out.push(s);
|
||||
}
|
||||
} else {
|
||||
out.push(String(input));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convert a string to Title Case (preserving slashes/hyphens)
|
||||
function toTitleCase(str) {
|
||||
return str.replace(/[\w]+/g, w =>
|
||||
w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// === Collect tags from all pages ===
|
||||
const pages = dv.pages();
|
||||
let allTags = [];
|
||||
for (const p of pages) {
|
||||
if (p.file?.tags) flattenTags(p.file.tags, allTags);
|
||||
if (p.tags) flattenTags(p.tags, allTags);
|
||||
if (p.tags_list) flattenTags(p.tags_list, allTags);
|
||||
}
|
||||
|
||||
// === Normalize and count ===
|
||||
const counts = {};
|
||||
for (let tag of allTags) {
|
||||
if (!tag) continue;
|
||||
tag = String(tag).replace(/^#/, '').trim().toLowerCase();
|
||||
if (!tag) continue;
|
||||
if (splitOnSlash && tag.includes('/')) tag = tag.split('/')[0];
|
||||
counts[tag] = (counts[tag] || 0) + 1;
|
||||
}
|
||||
|
||||
// === Handle no tags ===
|
||||
const tagEntries = Object.entries(counts);
|
||||
if (tagEntries.length === 0) {
|
||||
dv.el("p", "⚠️ No tags found in your vault.");
|
||||
} else {
|
||||
// === Sort + limit ===
|
||||
tagEntries.sort((a, b) => b[1] - a[1]);
|
||||
const limited = (topN && tagEntries.length > topN)
|
||||
? tagEntries.slice(0, topN)
|
||||
: tagEntries;
|
||||
|
||||
// Convert to title case for display
|
||||
const labels = limited.map(e => toTitleCase(e[0].replace(/[-_]/g, ' ')));
|
||||
const dataValues = limited.map(e => e[1]);
|
||||
|
||||
// === Color setup ===
|
||||
const bgColors = [];
|
||||
const borderColors = [];
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
const color = catppuccinColors[i % catppuccinColors.length];
|
||||
bgColors.push(color + "55");
|
||||
borderColors.push(color);
|
||||
}
|
||||
|
||||
// === Chart.js config ===
|
||||
const chartData = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Tag Usage',
|
||||
data: dataValues,
|
||||
backgroundColor: bgColors,
|
||||
borderColor: borderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
title: {
|
||||
display: true,
|
||||
text: `Tag Usage (Top ${labels.length})`,
|
||||
color: catppuccin.text,
|
||||
font: { size: 16 }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { color: catppuccin.subtext1, autoSkip: false, maxRotation: 60, minRotation: 30 },
|
||||
title: { display: true, text: 'Tags', color: catppuccin.text },
|
||||
grid: { color: catppuccin.base }
|
||||
},
|
||||
y: {
|
||||
ticks: { color: catppuccin.subtext1 },
|
||||
title: { display: true, text: 'Count', color: catppuccin.text },
|
||||
beginAtZero: true,
|
||||
grid: { color: catppuccin.base }
|
||||
}
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
};
|
||||
|
||||
// === Render ===
|
||||
const wrapper = this.container.createEl('div');
|
||||
wrapper.style.minHeight = '320px';
|
||||
wrapper.style.maxHeight = '60vh';
|
||||
wrapper.style.overflow = 'auto';
|
||||
wrapper.style.borderRadius = '8px';
|
||||
wrapper.style.padding = '8px';
|
||||
|
||||
window.renderChart(chartData, wrapper);
|
||||
}
|
||||
```
|
||||
```dataviewjs
|
||||
// === Config ===
|
||||
const splitOnSlash = false; // true -> merge hierarchical tags (#project/obsidian -> project)
|
||||
const topN = 20; // show top N tags (null for all)
|
||||
|
||||
// === Catppuccin Mocha Palette ===
|
||||
const catppuccin = {
|
||||
rosewater: '#f5e0dc',
|
||||
flamingo: '#f2cdcd',
|
||||
pink: '#f5c2e7',
|
||||
mauve: '#cba6f7',
|
||||
red: '#f38ba8',
|
||||
maroon: '#eba0ac',
|
||||
peach: '#fab387',
|
||||
yellow: '#f9e2af',
|
||||
green: '#a6e3a1',
|
||||
teal: '#94e2d5',
|
||||
sky: '#89dceb',
|
||||
sapphire: '#74c7ec',
|
||||
blue: '#89b4fa',
|
||||
lavender: '#b4befe',
|
||||
text: '#cdd6f4',
|
||||
subtext1: '#bac2de',
|
||||
base: '#1e1e2e'
|
||||
};
|
||||
|
||||
const catppuccinColors = [
|
||||
catppuccin.rosewater,
|
||||
catppuccin.flamingo,
|
||||
catppuccin.pink,
|
||||
catppuccin.mauve,
|
||||
catppuccin.red,
|
||||
catppuccin.maroon,
|
||||
catppuccin.peach,
|
||||
catppuccin.yellow,
|
||||
catppuccin.green,
|
||||
catppuccin.teal,
|
||||
catppuccin.sky,
|
||||
catppuccin.sapphire,
|
||||
catppuccin.blue,
|
||||
catppuccin.lavender
|
||||
];
|
||||
|
||||
// === Helpers ===
|
||||
function flattenTags(input, out = []) {
|
||||
if (!input) return out;
|
||||
if (Array.isArray(input)) {
|
||||
for (const v of input) flattenTags(v, out);
|
||||
} else if (typeof input === 'string') {
|
||||
out.push(input);
|
||||
} else if (typeof input === 'object') {
|
||||
if (typeof input.tag === 'string') out.push(input.tag);
|
||||
else if (typeof input.path === 'string') out.push(input.path);
|
||||
else {
|
||||
const s = String(input);
|
||||
if (s && s !== '[object Object]') out.push(s);
|
||||
}
|
||||
} else {
|
||||
out.push(String(input));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function toTitleCase(str) {
|
||||
return str.replace(/[\w]+/g, w =>
|
||||
w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// === Collect tags ===
|
||||
const pages = dv.pages();
|
||||
let allTags = [];
|
||||
for (const p of pages) {
|
||||
if (p.file?.tags) flattenTags(p.file.tags, allTags);
|
||||
if (p.tags) flattenTags(p.tags, allTags);
|
||||
if (p.tags_list) flattenTags(p.tags_list, allTags);
|
||||
}
|
||||
|
||||
// === Normalize + count ===
|
||||
const counts = {};
|
||||
for (let tag of allTags) {
|
||||
if (!tag) continue;
|
||||
tag = String(tag).replace(/^#/, '').trim().toLowerCase();
|
||||
if (!tag) continue;
|
||||
if (splitOnSlash && tag.includes('/')) tag = tag.split('/')[0];
|
||||
counts[tag] = (counts[tag] || 0) + 1;
|
||||
}
|
||||
|
||||
const tagEntries = Object.entries(counts);
|
||||
if (tagEntries.length === 0) {
|
||||
dv.el("p", "⚠️ No tags found in your vault.");
|
||||
} else {
|
||||
// === Sort + limit ===
|
||||
tagEntries.sort((a, b) => b[1] - a[1]);
|
||||
const limited = (topN && tagEntries.length > topN)
|
||||
? tagEntries.slice(0, topN)
|
||||
: tagEntries;
|
||||
|
||||
const labels = limited.map(e => toTitleCase(e[0].replace(/[-_]/g, ' ')));
|
||||
const dataValues = limited.map(e => e[1]);
|
||||
const total = dataValues.reduce((a, b) => a + b, 0);
|
||||
|
||||
// === Colors ===
|
||||
const bgColors = [], borderColors = [];
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
const color = catppuccinColors[i % catppuccinColors.length];
|
||||
bgColors.push(color + "aa");
|
||||
borderColors.push(color);
|
||||
}
|
||||
|
||||
// === Create container ===
|
||||
const wrapper = this.container.createEl('div');
|
||||
wrapper.style.minHeight = '400px';
|
||||
wrapper.style.maxHeight = '70vh';
|
||||
wrapper.style.position = 'relative';
|
||||
wrapper.style.borderRadius = '8px';
|
||||
wrapper.style.padding = '8px';
|
||||
wrapper.style.overflow = 'auto';
|
||||
|
||||
// === Center label ===
|
||||
const centerLabel = document.createElement('div');
|
||||
centerLabel.style.position = 'absolute';
|
||||
centerLabel.style.top = '50%';
|
||||
centerLabel.style.left = '44%';
|
||||
centerLabel.style.transform = 'translate(-50%, -50%)';
|
||||
centerLabel.style.color = catppuccin.text;
|
||||
centerLabel.style.fontSize = '14px';
|
||||
centerLabel.style.fontWeight = '600';
|
||||
centerLabel.style.textAlign = 'center';
|
||||
centerLabel.innerText = 'Tag Usage';
|
||||
wrapper.appendChild(centerLabel);
|
||||
|
||||
// === Chart.js config ===
|
||||
const chartData = {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: dataValues,
|
||||
backgroundColor: bgColors,
|
||||
borderColor: borderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
cutout: '60%',
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'right',
|
||||
labels: { color: catppuccin.subtext1, font: { size: 12 } }
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: `Tag Distribution (Top ${labels.length})`,
|
||||
color: catppuccin.text,
|
||||
font: { size: 16 }
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: ctx => {
|
||||
const val = ctx.raw;
|
||||
const pct = ((val / total) * 100).toFixed(1);
|
||||
return `${ctx.label}: ${val} (${pct}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onHover: (evt, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const el = elements[0];
|
||||
const label = labels[el.index];
|
||||
const value = dataValues[el.index];
|
||||
const pct = ((value / total) * 100).toFixed(1);
|
||||
centerLabel.innerText = `${label}\n${pct}%`;
|
||||
} else {
|
||||
centerLabel.innerText = 'Tag Usage';
|
||||
}
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
};
|
||||
|
||||
window.renderChart(chartData, wrapper);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user