This commit is contained in:
Chris Wanstrath 2025-07-28 10:41:45 -07:00
parent 6ac231ce36
commit ed7e1349b5
2 changed files with 62 additions and 13 deletions

View File

@ -0,0 +1,38 @@
interface ProjectData {
name: string
mtime: Date
status: "recent" | "active" | "inactive"
}
interface ProjectsProps {
projects: ProjectData[]
}
export function Projects({ projects }: ProjectsProps) {
const statusText = (status: "recent" | "active" | "inactive") => {
switch (status) {
case "recent": return "Modified in the last 2 weeks"
case "active": return "Modified in the last month"
case "inactive": return "Modified over a month ago"
}
}
return (
<div>
<h1>Projects</h1>
<ul>
{projects.map(project => <li key={project.name}>
<abbr style={{ fontSize: "12px", borderBottom: "none", cursor: "default" }} title={statusText(project.status)}>
{project.status === "recent" ? "🟢" : project.status === "active" ? "🟡" : "🔴"}
</abbr>&nbsp;
<a
href={`/p/${project.name}`}
title={`Last modified ${project.mtime.toLocaleString()}`}
>
{project.name}
</a>
</li>)}
</ul>
</div>
)
}

View File

@ -1,12 +1,13 @@
import { Hono } from "hono"
import { serveStatic } from "hono/bun"
import { render } from "preact-render-to-string"
import { marked } from "marked"
import { $ } from "bun"
import { readdirSync } from "fs"
import { mkdir, readdir } from 'node:fs/promises'
import { basename, join } from 'path'
import { Layout } from "./components/Layout"
import { Project } from "./components/Project"
import { Projects } from "./components/Projects"
const PROJECTS_DIR = ".."
const WORKSHOP_REPO = "https://github.com/probablycorey/the-workshop"
@ -28,7 +29,7 @@ app.use("*", async (c, next) => {
const start = Date.now()
await next()
const end = Date.now()
console.log(`${c.req.method} ${c.req.url} - ${c.res.status} (${end - start}ms)`)
console.log(`${c.res.status} ${c.req.method} ${c.req.url} (${end - start}ms)`)
})
// ----------------------------------------------------------------------------
@ -36,8 +37,7 @@ app.use("*", async (c, next) => {
// ----------------------------------------------------------------------------
api.get('/projects', async c => {
const names = await projects()
return c.json(names)
return c.json(await projectsWithDates())
})
// ----------------------------------------------------------------------------
@ -45,15 +45,8 @@ api.get('/projects', async c => {
// ----------------------------------------------------------------------------
app.get("/", async (c) => {
const names = await projects()
return c.html(tsx(
<div>
<h1>Projects</h1>
<ul>
{names.map(name => <li key={name}><a href={`/p/${name}`}>{name}</a></li>)}
</ul>
</div>
))
const projects = await projectsWithDates()
return c.html(tsx(<Projects projects={projects} />))
})
app.get("/p/:name", async (c) => {
@ -140,6 +133,24 @@ async function projects(): Promise<string[]> {
return subdirs
}
async function projectsWithDates(): Promise<{ name: string, mtime: Date, status: "recent" | "active" | "inactive" }[]> {
const names = await projects()
const projectsWithDates = await Promise.all(names.map(async name => {
const lastModified = await mtime(name)
const daysSince = (Date.now() - lastModified.getTime()) / (24 * 60 * 60 * 1000)
const status: "recent" | "active" | "inactive" = daysSince <= 14 ? "recent" : daysSince <= 30 ? "active" : "inactive"
return { name, mtime: lastModified, status }
}))
const sorted = projectsWithDates.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
return sorted
}
async function mtime(project: string): Promise<Date> {
const proc = await $`cd ${PROJECTS_DIR} && git log -1 --format=%ct -- ${project}`.quiet()
if (proc.exitCode !== 0) return new Date(0)
return new Date(parseInt(proc.stdout.toString().trim()) * 1000)
}
function tsx(node: any) {
return "<!DOCTYPE html>" + render(<Layout>{node}</Layout>)
}