(A brief 1-2 sentence description of the location’s vibe or history.)
[!info]-
const sessions = dv.pages('"Campaign Information/Sessions"') .where(p => { // Handle both single links and lists of links in the 'arc' property const arc = p.arc; if (!arc) return false; if (Array.isArray(arc)) { return arc.some(link => link.path === dv.current().file.path); } return arc.path === dv.current().file.path; }) .sort(p => p.file.name, 'asc');for (let session of sessions) { const file = app.vault.getAbstractFileByPath(session.file.path); const content = await app.vault.read(file); const lines = content.split('\n'); let summaryLines = []; let foundSummary = false; for (let line of lines) { // Look for the start of your specific callout if (line.trim().toLowerCase().includes('[!success]+ saga20 summary')) { foundSummary = true; continue; } // While inside the callout, collect lines starting with '>' if (foundSummary) { if (line.trim().startsWith('>')) { // Clean the '>' off the front of the line summaryLines.push(line.trim().replace(/^>\s?/, "")); } else if (line.trim() === "" && summaryLines.length > 0) { // Keep going if it's just a blank line within the callout summaryLines.push(""); } else if (summaryLines.length > 0) { // Stop when we hit a line that doesn't start with '>' break; } } } if (summaryLines.length > 0) { dv.header(3, session.file.link); dv.paragraph(summaryLines.join('\n')); dv.el("hr", ""); // Adds a horizontal line between session recaps }}
let current = dv.current();let title = current.file?.name || current.name || "";// 0. Suppress artwork search on template notesif (title.toLowerCase().includes("template")) { dv.paragraph(`*(Location / Building Image Preview)*`);} else { // 1. Gather all potential names (Title, quoted nicknames, and Obsidian Aliases) let rawNames = [title]; // Extract quoted nickname if present in title (e.g. Holy Dominion "Estwatch" -> Estwatch) let matchNick = title.match(/"([^"]+)"/); if (matchNick && matchNick[1]) { rawNames.push(matchNick[1]); } // Pull aliases safely let aliases = current.file?.aliases || current.aliases || []; if (typeof aliases === "string") { rawNames.push(aliases); } else if (Array.isArray(aliases) || (aliases && typeof aliases[Symbol.iterator] === "function")) { for (let a of aliases) { if (a && typeof a === "string") rawNames.push(a); } } // 2. Build sanitized search terms let searchTerms = new Set(); for (let name of rawNames) { if (!name || typeof name !== "string") continue; let lower = name.toLowerCase().trim().replace(/["']/g, ""); let slug = lower.replace(/[\s-]+/g, "_").replace(/^_+|_+$ /g, ""); if (lower.length >= 2) searchTerms.add(lower); if (slug.length >= 2) searchTerms.add(slug); } // 3. Search target folders for matching image files (.png, .webp, .jpg, etc.) let imageFolders = ["Images/Locations", "Image/Locations", "Images/Buildings", "Image/Buildings"]; let fallbackImage = ""; let matches = app.vault.getFiles().filter(f => { let pathLower = f.path.toLowerCase(); let inTargetFolder = imageFolders.some(folder => pathLower.includes(folder.toLowerCase())); if (!inTargetFolder) return false; if (fallbackImage && pathLower.includes(fallbackImage.toLowerCase())) return false; let imgName = f.name.toLowerCase(); return Array.from(searchTerms).some(term => term && imgName.includes(term)); }); // 4. Select match (prioritize "settlement", "overview", "cover", or "map", otherwise take the first file) let match = matches.find(f => { let nameLower = f.name.toLowerCase(); return nameLower.includes("settlement") || nameLower.includes("overview") || nameLower.includes("cover") || nameLower.includes("map"); }) || (matches.length > 0 ? matches[0] : null); // 5. Render artwork, fallback image, or clean message if (match && match.path) { dv.paragraph(`![[${match.path}|cover]]`); } else { let fallbackFile = null; if (fallbackImage && fallbackImage.trim() !== "") { let cleanFB = fallbackImage.toLowerCase().trim(); fallbackFile = app.vault.getFiles().find(f => f.path.toLowerCase() === cleanFB || f.path.toLowerCase().includes(cleanFB) || f.name.toLowerCase().includes(cleanFB) ); } if (fallbackFile && fallbackFile.path) { dv.paragraph(`![[${fallbackFile.path}|cover]]`); } else { dv.paragraph(`*No map or illustration found for ${title}*`); } }}
Districts
Areas & Points of Interest
Astrakane
Astrakane
Overview
(A brief 1-2 sentence description of the location’s vibe or history.)
let current = dv.current();let title = current.file?.name || current.name || "";// 0. Suppress artwork search on template notesif (title.toLowerCase().includes("template")) { dv.paragraph(`*(Location / Building Image Preview)*`);} else { // 1. Gather all potential names (Title, quoted nicknames, and Obsidian Aliases) let rawNames = [title]; // Extract quoted nickname if present in title (e.g. Holy Dominion "Estwatch" -> Estwatch) let matchNick = title.match(/"([^"]+)"/); if (matchNick && matchNick[1]) { rawNames.push(matchNick[1]); } // Pull aliases safely let aliases = current.file?.aliases || current.aliases || []; if (typeof aliases === "string") { rawNames.push(aliases); } else if (Array.isArray(aliases) || (aliases && typeof aliases[Symbol.iterator] === "function")) { for (let a of aliases) { if (a && typeof a === "string") rawNames.push(a); } } // 2. Build sanitized search terms let searchTerms = new Set(); for (let name of rawNames) { if (!name || typeof name !== "string") continue; let lower = name.toLowerCase().trim().replace(/["']/g, ""); let slug = lower.replace(/[\s-]+/g, "_").replace(/^_+|_+$ /g, ""); if (lower.length >= 2) searchTerms.add(lower); if (slug.length >= 2) searchTerms.add(slug); } // 3. Search target folders for matching image files (.png, .webp, .jpg, etc.) let imageFolders = ["Images/Locations", "Image/Locations", "Images/Buildings", "Image/Buildings"]; let fallbackImage = ""; let matches = app.vault.getFiles().filter(f => { let pathLower = f.path.toLowerCase(); let inTargetFolder = imageFolders.some(folder => pathLower.includes(folder.toLowerCase())); if (!inTargetFolder) return false; if (fallbackImage && pathLower.includes(fallbackImage.toLowerCase())) return false; let imgName = f.name.toLowerCase(); return Array.from(searchTerms).some(term => term && imgName.includes(term)); }); // 4. Select match (prioritize "settlement", "overview", "cover", or "map", otherwise take the first file) let match = matches.find(f => { let nameLower = f.name.toLowerCase(); return nameLower.includes("settlement") || nameLower.includes("overview") || nameLower.includes("cover") || nameLower.includes("map"); }) || (matches.length > 0 ? matches[0] : null); // 5. Render artwork, fallback image, or clean message if (match && match.path) { dv.paragraph(`![[${match.path}|cover]]`); } else { let fallbackFile = null; if (fallbackImage && fallbackImage.trim() !== "") { let cleanFB = fallbackImage.toLowerCase().trim(); fallbackFile = app.vault.getFiles().find(f => f.path.toLowerCase() === cleanFB || f.path.toLowerCase().includes(cleanFB) || f.name.toLowerCase().includes(cleanFB) ); } if (fallbackFile && fallbackFile.path) { dv.paragraph(`![[${fallbackFile.path}|cover]]`); } else { dv.paragraph(`*No map or illustration found for ${title}*`); } }}