A pixel-art 2D scene that visualizes team members' real-time activity status. Events are sourced from HackMD Webhooks and GitHub (Webhooks + API Polling). The server maintains a state machine that computes each character's state at any point in time. The frontend renders an interactive timeline with playback controls (play, fast-forward, rewind, scrub).
Inspired by claw.info (PixelClaw style).
graph TD
subgraph Event Sources
HW[HackMD Webhook]
GW[GitHub Webhook]
GP[GitHub API Polling]
end
subgraph ASP.NET Core Server
WC[Webhook Controllers]
GPS[GitHubPollingService]
EP[EventProcessor]
SM[StateMachine]
DB[(SQLite via EF Core)]
API[REST API]
HUB[SignalR Hub]
end
subgraph Blazor WASM Client
TC[Timeline Control]
PS[Pixel 2D Scene]
SP[Status Panel]
end
HW --> WC
GW --> WC
GP --> GPS
WC --> EP
GPS --> EP
EP --> DB
EP --> SM
EP --> HUB
SM --> API
HUB -->|Real-time events| TC
API -->|State snapshots| TC
TC --> PS
TC --> SP
| Layer | Technology |
|---|---|
| Backend | ASP.NET Core 9, C# |
| Real-time | SignalR |
| Database | SQLite + EF Core |
| Frontend | Blazor WebAssembly |
| 2D Rendering | PixiJS (Canvas/WebGL) |
| Container | Docker (multi-stage build) |
hackmdinfo/
├── src/
│ ├── HackMdInfo.Server/ # ASP.NET Core Web API + SignalR
│ ├── HackMdInfo.Client/ # Blazor WASM frontend
│ └── HackMdInfo.Shared/ # Shared DTOs, enums, interfaces
├── tests/
│ ├── HackMdInfo.Server.Tests/ # Unit + integration tests
│ └── HackMdInfo.Shared.Tests/ # Shared model tests
├── docs/
│ ├── plan/architecture.md # Architecture design doc
│ └── ARC42/ # ARC42 documentation
├── .devcontainer/ # Dev container configuration
└── HackMdInfo.sln
- .NET 9 SDK
- Node.js LTS (for PixiJS)
- SQLite 3
Or simply use the Dev Container (recommended):
# Open in VS Code with Dev Containers extension
code .
# -> Reopen in Container# Restore dependencies
dotnet restore
# Run the server (hosts both API and Blazor client)
dotnet run --project src/HackMdInfo.Server
# Or with hot reload
dotnet watch run --project src/HackMdInfo.ServerOpen http://localhost:5000 in your browser.
# All tests
dotnet test
# With verbose output
dotnet test --verbosity normal| Method | Path | Description |
|---|---|---|
POST |
/api/webhooks/hackmd |
Receive HackMD webhook events |
POST |
/api/webhooks/github |
Receive GitHub webhook events |
GET |
/api/state?timestamp={iso} |
Get state snapshot at a specific time |
GET |
/api/events?from={iso}&to={iso} |
Get events within a time range |
GET |
/api/characters |
List all characters |
| Method | Description |
|---|---|
ReceiveEvent(TimelineEventDto) |
Real-time new event push |
ReceiveStateUpdate(CharacterStateDto) |
Real-time character state change |
| Event | EventType | Character Action | Idle Timeout |
|---|---|---|---|
| Note updated | hackmd.note.updated |
WritingNote | 5 min |
| Note created | hackmd.note.created |
CreatedNote | 3 min |
| Git push | github.push |
Pushing | 3 min |
| PR opened | github.pr.opened |
OpeningPR | 5 min |
# Simulate HackMD webhook
curl -X POST http://localhost:5000/api/webhooks/hackmd \
-H "Content-Type: application/json" \
-d '{"event":"note.updated","noteId":"abc123","userPath":"alice","title":"Lecture Notes"}'
# Simulate GitHub push webhook
curl -X POST http://localhost:5000/api/webhooks/github \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-d '{"pusher":{"name":"bob"},"repository":{"name":"my-project"},"ref":"refs/heads/main"}'# Build
docker build -t hackmdinfo .
# Run with persistent data
docker run -p 8080:8080 -v hackmdinfo-data:/app/data hackmdinfoMIT