This commit is contained in:
2025-10-22 22:13:54 +02:00
parent 9e44354ef0
commit b63d46a78e
331 changed files with 352530 additions and 3 deletions

View File

@@ -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);
}
```
---