// This is the legend code. Add this after your existing table code.
// Add custom CSS for the legend
const legendCSS = `
<style>
.legend-container {
text-align: right;
margin-top: 10px;
font-size: 0.9em;
color: #888;
}
.legend-item {
margin-left: 10px;
}
.legend-code {
font-family: monospace;
background-color: #444;
padding: 1px 3px;
border-radius: 3px;
color: #fff;
}
</style>
`;
// Render the custom CSS for the legend
dv.container.innerHTML += legendCSS;
// Create and render the legend
const legendHTML = `
<div class="legend-container">
<span class="legend-item"><span class="legend-code">H</span>ighlights</span>
<span class="legend-item">Action <span class="legend-code">I</span>tems</span>
<span class="legend-item"><span class="legend-code">A</span>ctions</span>
<span class="legend-item">(<span class="legend-code">V</span>alue <span class="legend-code">P</span>er <span class="legend-code">M</span>inute)</span>
</div>
`;
dv.container.innerHTML += legendHTML;
async function main() {
const folderPath = "Content Farm/Web";
// Filter out inbox and archive entries
let pages = dv.pages(`"${folderPath}"`).filter(p => {
if (p.file.name === "📇 Web Inbox") return false;
const filePath = p.file.path || p.file.folder;
return filePath && !filePath.includes("Archive");
});
// Highlight checker
function hasHighlights(content) {
if (typeof content === "string") return content.includes("==");
if (typeof content === "object") return JSON.stringify(content).includes("==");
return false;
}
// Extract VPM from "# rate_value" JSON or fallback
function extractVPM(content) {
const blockRegex = /#\s*rate_value\s*\n+({[\s\S]*?})/m;
const blockMatch = content.match(blockRegex);
if (blockMatch) {
try {
const obj = JSON.parse(blockMatch[1]);
if (obj.vpm !== undefined) return String(obj.vpm);
} catch (error) {
console.log("Failed to parse JSON from # rate_value block:", error);
}
}
const vpmRegex = /"vpm"\s*:\s*"?([\d.]+)"?/;
const vpmMatch = content.match(vpmRegex);
return vpmMatch ? vpmMatch[1] : "";
}
let tableData = [];
// Load each page's content
for (let page of pages) {
const content = await dv.io.load(page.file.path);
const hasTasks = page.file.tasks && page.file.tasks.length > 0;
const hasOpenTasks = hasTasks && page.file.tasks.some(task => !task.completed);
const hasConsumedChecked = page.file.tasks?.some(
task => task.text.toLowerCase().includes("consumed") && task.completed
);
if (!hasOpenTasks || hasConsumedChecked) continue;
const highlightsPresent = hasHighlights(content);
const hasActionItemTag = page.file.tags?.includes("#action-item");
const hasActionTag = page.file.tags?.includes("#action");
const vpmValue = extractVPM(content);
tableData.push({
link: page.file.link,
words: Math.round(page.file.size / 6),
flags: [
highlightsPresent ? "H" : "",
hasActionItemTag ? "I" : "",
hasActionTag ? "A" : ""
].filter(Boolean).join(" "),
vpm: vpmValue
});
}
// Sort by filename descending
tableData.sort((a, b) => {
const nameA = a.link.path.split("/").pop() || "";
const nameB = b.link.path.split("/").pop() || "";
return nameB.localeCompare(nameA);
});
// Style rows and flags
const customCSS = `
<style>
.dataview.table-view-table tr:nth-child(even) {
background-color: #333;
color: white;
}
.flag-code {
font-family: monospace;
background-color: #444;
padding: 2px 4px;
border-radius: 3px;
color: white;
}
</style>
`;
dv.container.innerHTML += customCSS;
// Render the table (without Creation Date)
dv.table(
["Page", "~ Word Count", "Flags", "VPM"],
tableData.map(row => [
row.link,
row.words,
row.flags ? `<span class="flag-code">${row.flags}</span>` : "",
row.vpm
])
);
}
main();