Islandflow docs index
A browsable index of files under docs/ with filtering and grouped navigation.
No files match that filter.
import { promises as fs } from "node:fs"; import path from "node:path"; const docsDir = path.resolve(process.cwd(), "docs"); const outputFile = path.join(docsDir, "index.html"); const dateFormatter = new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeStyle: "short", }); function escapeHtml(value) { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function formatBytes(bytes) { if (bytes < 1024) { return `${bytes} B`; } const units = ["KB", "MB", "GB"]; let size = bytes / 1024; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex += 1; } return `${size.toFixed(size >= 10 ? 0 : 1)} ${units[unitIndex]}`; } function docsHref(relativePath) { const encoded = relativePath .split("/") .map((part) => encodeURIComponent(part)) .join("/"); return `./${encoded}`; } async function collectDocsFiles(rootDir, currentDir = rootDir, acc = []) { const entries = await fs.readdir(currentDir, { withFileTypes: true }); const sortedEntries = entries.sort((a, b) => a.name.localeCompare(b.name)); for (const entry of sortedEntries) { if (entry.name.startsWith(".")) { continue; } const absolutePath = path.join(currentDir, entry.name); const relativePath = path.relative(rootDir, absolutePath).replaceAll(path.sep, "/"); if (relativePath === "index.html") { continue; } if (entry.isDirectory()) { await collectDocsFiles(rootDir, absolutePath, acc); continue; } if (entry.isFile()) { const stats = await fs.stat(absolutePath); acc.push({ relativePath, category: relativePath.includes("/") ? relativePath.split("/")[0] : "root", sizeBytes: stats.size, modifiedAt: stats.mtime, }); } } return acc; } function groupByCategory(items) { const groups = new Map(); for (const item of items) { if (!groups.has(item.category)) { groups.set(item.category, []); } groups.get(item.category).push(item); } return groups; } function sortedCategories(groups) { const preferredOrder = ["turns", "daily-git", "general", "plans", "root"]; const groupNames = [...groups.keys()]; return groupNames.sort((a, b) => { const aIndex = preferredOrder.indexOf(a); const bIndex = preferredOrder.indexOf(b); if (aIndex !== -1 || bIndex !== -1) { if (aIndex === -1) return 1; if (bIndex === -1) return -1; return aIndex - bIndex; } return a.localeCompare(b); }); } function renderDocument(items) { const sortedItems = [...items].sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime()); const groups = groupByCategory(sortedItems); const categories = sortedCategories(groups); const totalCount = sortedItems.length; const categoryChips = categories .map((category) => { const count = groups.get(category).length; return `${escapeHtml( category )} ${count}`; }) .join("\n"); const groupsMarkup = categories .map((category) => { const entries = groups.get(category); const entryMarkup = entries .map((entry) => { const extension = path.extname(entry.relativePath).replace(".", "") || "file"; const searchable = `${entry.relativePath} ${category}`.toLowerCase(); return `
A browsable index of files under docs/ with filtering and grouped navigation.
No files match that filter.