backlog-md/src
Jeff Emmett 47b8bdfd93 Add dependency visualization dashboard with D3.js
- Add BoardTabs component for switching between Kanban and Dependencies views
- Add DependencyView (Mermaid-based) and DependencyViewD3 (D3.js force-directed graph)
- Add DependencyFilters for status, priority, and completed task filtering
- Add graph utilities for building dependency graphs and generating visualizations
- Features: drag nodes, pan/zoom, hover highlighting, click-to-edit, sequence badges
- Color coding: green (done), blue (in progress), gray (to do), red border (high priority)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 12:12:05 +01:00
..
aggregator feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
commands Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
completions Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
constants Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
core feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
file-system Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
formatters Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
git Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
guidelines Fix symlink for Docker: replace with actual file 2025-12-04 02:23:26 -08:00
markdown feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
mcp feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
server fix: route ordering for /api/tasks/reorder endpoint 2025-12-04 04:45:36 -08:00
test Add dependency visualization dashboard with D3.js 2026-01-08 12:12:05 +01:00
types feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
ui Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
utils feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
web Add dependency visualization dashboard with D3.js 2026-01-08 12:12:05 +01:00
agent-instructions.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
board.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
cli.ts feat: add estimatedHours field and fix aggregator project duplication 2025-12-25 21:27:59 -05:00
index.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
readme.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00

readme.ts

import { join } from "node:path";
import { exportKanbanBoardToFile } from "./board.ts";
import type { Task } from "./types/index.ts";

const BOARD_START = "<!-- BOARD_START -->";
const BOARD_END = "<!-- BOARD_END -->";

export async function updateReadmeWithBoard(tasks: Task[], statuses: string[], projectName: string, version?: string) {
	const readmePath = join(process.cwd(), "README.md");
	let readmeContent = "";
	try {
		readmeContent = await Bun.file(readmePath).text();
	} catch {
		// If README.md doesn't exist, create it.
	}

	// Use the same high-quality board generation as file export
	// Create a temporary file to get the properly formatted board
	const tempPath = join(process.cwd(), ".temp-board.md");
	await exportKanbanBoardToFile(tasks, statuses, tempPath, projectName);
	const fullBoardContent = await Bun.file(tempPath).text();

	// Extract timestamp from the board content
	const timestampMatch = fullBoardContent.match(/Generated on: ([^\n]+)/);
	const timestamp = timestampMatch ? timestampMatch[1] : new Date().toISOString().replace("T", " ").substring(0, 19);

	// Extract just the board table (skip all metadata headers)
	const lines = fullBoardContent.split("\n");
	const tableStartIndex = lines.findIndex(
		(line) =>
			line.includes("|") &&
			(line.includes("To Do") || line.includes("In Progress") || line.includes("Done") || line.includes("---")),
	);
	const boardTable = lines.slice(tableStartIndex).join("\n").trim();

	// Clean up temp file
	try {
		await Bun.file(tempPath).write("");
		await Bun.$`rm -f ${tempPath}`;
	} catch {
		// Ignore cleanup errors
	}

	// Create the board section with a nice title
	const versionText = version ? ` (${version})` : "";
	const statusTitle = `## 📊 ${projectName} Project Status${versionText}`;
	const subtitle = "This board was automatically generated by [Backlog.md](https://backlog.md)";
	const boardSection = `${statusTitle}\n\n${subtitle}\n\nGenerated on: ${timestamp}\n\n${boardTable}`;

	const startMarkerIndex = readmeContent.indexOf(BOARD_START);
	const endMarkerIndex = readmeContent.indexOf(BOARD_END);
	const licenseIndex = readmeContent.indexOf("## License");

	if (startMarkerIndex !== -1 && endMarkerIndex !== -1) {
		const preContent = readmeContent.substring(0, startMarkerIndex + BOARD_START.length);
		const postContent = readmeContent.substring(endMarkerIndex);
		readmeContent = `${preContent}\n\n${boardSection}\n\n${postContent}`;
	} else if (licenseIndex !== -1) {
		const preContent = readmeContent.substring(0, licenseIndex);
		const postContent = readmeContent.substring(licenseIndex);
		readmeContent = `${preContent}${BOARD_START}\n\n${boardSection}\n\n${BOARD_END}\n\n${postContent}`;
	} else {
		// If markers are not found, append the board at the end of the file.
		readmeContent += `\n\n${BOARD_START}\n\n${boardSection}\n\n${BOARD_END}`;
	}

	await Bun.write(readmePath, readmeContent);
}