<%* // Record start time for entire template runtime const startTime = performance.now();

// Import necessary modules const crypto = require(‘crypto’); const fs = require(‘fs’);

// Function to normalize the text block and track removals function normalizeText(block) { console.log([${new Date().toISOString()}] Normalizing text block);

const removals = {
    headers: 0,
    boldItalic: 0,
    unorderedLists: 0,
    orderedLists: 0,
    todos: 0,
    digestLink: 0,
    sealLink: 0
};

// Strip markdown prefixes (e.g., headers, bold, italic)
block = block.replace(/^#+\s+/gm, () => { removals.headers++; return ''; }); // Headers
block = block.replace(/[*_]{1,3}([^*_]+)[*_]{1,3}/g, (_, p1) => { removals.boldItalic++; return p1; }); // Bold and Italics

// Remove ordered lists, lists, and todos
block = block.replace(/^\s*[-*+]\s+/gm, () => { removals.unorderedLists++; return ''; }); // Unordered lists
block = block.replace(/^\s*\d+\.\s+/gm, () => { removals.orderedLists++; return ''; }); // Ordered lists
block = block.replace(/^\s*[-*+]\s+\[.\]\s+/gm, () => { removals.todos++; return ''; }); // Todos

// Remove Digest Link
block = block.replace(/\[\^ds\/[a-f0-9]{12}\/\d{4}-\d{2}-\d{2}\^\]/g, () => { removals.digestLink++; return ''; });

// Remove Seal Link
block = block.replace(/\s\^ds-\d{4}-\d{2}-\d{2}-[a-f0-9]{12}/g, () => { removals.sealLink++; return ''; });

return { normalizedText: block.trim(), removals };

}

// Function to compute a 12-character SHA-256 hash function computeHash(text) { const fullHash = crypto.createHash(‘sha256’).update(text).digest(‘hex’); return fullHash.slice(0, 12); }

// Function to upsert content to a file function upsertToFile(filePath, header, content) { let fileContent = ”; if (fs.existsSync(filePath)) { fileContent = fs.readFileSync(filePath, ‘utf8’); }

const regex = new RegExp(`${header}[\\s\\S]*?(?=###|$)`, 'g');
const replacement = `${header}\n${content}\n\n`;

if (regex.test(fileContent)) {
    fileContent = fileContent.replace(regex, replacement);
} else {
    fileContent += replacement;
}

fs.writeFileSync(filePath, fileContent);

}

// Function to verify if content has already been digested function verifyExistingDigest(content, existingDigest) { const { normalizedText } = normalizeText(content); const computedDigest = computeHash(normalizedText); return computedDigest === existingDigest; }

// Get the currently selected text in Obsidian console.log([${new Date().toISOString()}] Getting selected text); let selection = app.workspace.activeEditor.getSelection();

// Check if no text is selected if (!selection || selection.trim() === ”) { const noSelectionError = “No text selected. Please select the text you want to digest.”; new Notice(noSelectionError); console.error([${new Date().toISOString()}] ${noSelectionError}); throw new Error(noSelectionError); }

// Check for existing Digest Links and Seal Links const digestLinkMatches = […selection.matchAll(/[^ds/([a-f0-9]{12})/(\d{4}-\d{2}-\d{2})^]/g)]; const sealLinkMatches = […selection.matchAll(/\s(^ds-(\d{4}-\d{2}-\d{2})-([a-f0-9]{12}))/g)];

if (digestLinkMatches.length > 1) { const warningMessage = Warning: Multiple Digest Links found (${digestLinkMatches.length}). Only the last one will be considered.; console.warn([${new Date().toISOString()}] ${warningMessage}); new Notice(warningMessage); }

if (sealLinkMatches.length > 1) { const warningMessage = Warning: Multiple Seal Links found (${sealLinkMatches.length}). Only the last one will be considered.; console.warn([${new Date().toISOString()}] ${warningMessage}); new Notice(warningMessage); }

let existingDigestLink = digestLinkMatches.length > 0 ? digestLinkMatches[digestLinkMatches.length - 1][0] : ”; let existingDigest = digestLinkMatches.length > 0 ? digestLinkMatches[digestLinkMatches.length - 1][1] : ”; let existingDigestDate = digestLinkMatches.length > 0 ? digestLinkMatches[digestLinkMatches.length - 1][2] : ”; let existingSealLink = sealLinkMatches.length > 0 ? sealLinkMatches[sealLinkMatches.length - 1][1] : ”; let existingSealDate = sealLinkMatches.length > 0 ? sealLinkMatches[sealLinkMatches.length - 1][2] : ”; let existingSealHash = sealLinkMatches.length > 0 ? sealLinkMatches[sealLinkMatches.length - 1][3] : ”;

let selectionWithoutLinks = selection; if (existingDigestLink) { selectionWithoutLinks = selectionWithoutLinks.replace(existingDigestLink, ”).trim(); } if (existingSealLink) { selectionWithoutLinks = selectionWithoutLinks.replace(existingSealLink, ”).trim(); }

// Normalize the selected text const { normalizedText: normalizedSelection, removals } = normalizeText(selectionWithoutLinks); console.log([${new Date().toISOString()}] Text normalized); console.log([${new Date().toISOString()}] Normalized Content:\n${normalizedSelection});

// Compute the normal hash of the normalized selection const normalHash = computeHash(normalizedSelection); console.log([${new Date().toISOString()}] Normal Hash computed: ${normalHash});

let digestVerificationResult = ”; if (existingDigestLink) { if (verifyExistingDigest(selectionWithoutLinks, existingDigest)) { const unchangedMessage = “Content unchanged. No re-digestion needed.”; new Notice(unchangedMessage); console.log([${new Date().toISOString()}] ${unchangedMessage}); digestVerificationResult = Existing Digest verified successfully. Date: ${existingDigestDate}; return; } else { digestVerificationResult = “Existing Digest verification failed. Content will be re-digested.”; console.log([${new Date().toISOString()}] ${digestVerificationResult}); } }

// Get the current date in YYYY-MM-DD format const digestDate = new Date().toISOString().split(‘T’)[0]; console.log([${new Date().toISOString()}] Digest date: ${digestDate});

// Calculate word and character counts const wordCount = normalizedSelection.split(/\s+/).length; const charCount = selectionWithoutLinks.length; const lineCount = selectionWithoutLinks.split(‘\n’).length;

// Get additional metadata const currentFile = app.workspace.getActiveFile(); const filePath = currentFile ? currentFile.path : “Unknown”; const frontmatter = app.metadataCache.getFileCache(currentFile).frontmatter; const authors = frontmatter && frontmatter.authors ? frontmatter.authors.join(”, ”) : “Unknown”;

// Ensure the directory exists before writing const digestsContentPath = ${app.vault.adapter.basePath}/Directory/Digests/content/; fs.mkdirSync(digestsContentPath, { recursive: true });

// Append to content digest file const contentDigestPath = ${digestsContentPath}${normalHash}.md; const contentDigestHeader = ### #${normalHash}/${digestDate}; const contentDigestEntry = ${selectionWithoutLinks} #cbc \n${normalizedSelection} #nbc \nFile: ${filePath}\nAuthors: ${authors}; upsertToFile(contentDigestPath, contentDigestHeader, contentDigestEntry); console.log([${new Date().toISOString()}] Upserted to content digest file: ${contentDigestPath});

// Calculate hashtime const endTime = performance.now(); const hashtime = ((endTime - startTime) / 1000).toFixed(6);

// Create the removal summary const removalSummary = Object.entries(removals) .filter(([_, count]) count > 0) .map(([type, count]) ${type}: ${count}) .join(‘\n’);

// Create the notice and log message const message = `Date Digest v1.5: Normal Hash: {wordCount} Character Count: {lineCount} Normal Character Count: {charCount - normalizedSelection.length} Hashtime Seconds: {digestDate} Digest File: {filePath} Authors: {existingSealLink ? ‘Existing Seal Link preserved: ’ + existingSealLink : ”} ${digestVerificationResult}

Removals during normalization: ${removalSummary}`;

new Notice(message); console.log([${new Date().toISOString()}] Date Digest v1.5: ${message});

// Update the selection with the new digest information const newDigestLink = [^ds/${normalHash}/${digestDate}^]; const updatedSelection = ${selectionWithoutLinks} ${newDigestLink}${existingSealLink ? ' ' + existingSealLink : ''}; app.workspace.activeEditor.editor.replaceSelection(updatedSelection);

// Create the footnote const footnote = [^ds/${normalHash}/${digestDate}^]: ${new Date().toISOString()} WC:${wordCount}, CC:${charCount}, LC:${lineCount}, DF:[[${contentDigestPath.split('/').pop()}]], File: ${filePath}, Authors: ${authors};

// Upsert the footnote under the ”### References” header const fileContent = await app.vault.read(currentFile); let updatedContent = fileContent;

const referencesHeader = ”### References”; const footnoteRegex = new RegExp(\\[\\^ds\\/[a-f0-9]{12}\\/\\d{4}-\\d{2}-\\d{2}\\^\\]:.*, ‘g’);

if (fileContent.includes(referencesHeader)) { const parts = fileContent.split(referencesHeader); if (footnoteRegex.test(parts[1])) { parts[1] = parts[1].replace(footnoteRegex, footnote); } else { parts[1] = \n${footnote}${parts[1]}; } updatedContent = parts.join(referencesHeader); } else { updatedContent += \n\n${referencesHeader}\n${footnote}\n; }

await app.vault.modify(currentFile, updatedContent); console.log([${new Date().toISOString()}] Footnote added or updated under References); %>