Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Contributing to DiffKit

Thanks for your interest in contributing to DiffKit! This guide will help you get started.

## Development Setup

Follow the [Getting Started](README.md#getting-started) section in the README to set up your local environment.

## Workflow

1. **Fork the repo** and create your branch from `main`
2. **Make your changes** — keep commits focused and atomic
3. **Run checks** before pushing:
```bash
pnpm check-types # Type checking
pnpm lint # Linting
pnpm format # Formatting
```
4. **Open a pull request** against `main`

## Code Style

This project uses [Biome](https://biomejs.dev/) for linting and formatting. Run `pnpm format` to auto-format your code. Pre-commit hooks via Husky and lint-staged will also catch issues before they're committed.

A few conventions to keep in mind:

- Prefer editing existing files over creating new ones
- Keep components in the appropriate package (`apps/dashboard` for app-specific, `packages/ui` for shared)

## Project Architecture

DiffKit is a **pnpm monorepo** managed with **Turborepo**:

- **`apps/dashboard`** — The main web app built with TanStack Start, deployed to Cloudflare Workers
- **`packages/ui`** — Shared UI components (Radix UI primitives + Tailwind CSS)
- **`packages/icons`** — Icon wrapper package
- **`packages/typescript-config`** — Shared TypeScript configurations

### Key Technologies

- **TanStack Router** — File-based routing in `apps/dashboard/src/routes/`
- **TanStack Query** — Server state management and caching
- **Drizzle ORM** — Database schema and migrations in `apps/dashboard/src/db/` and `apps/dashboard/drizzle/`
- **Better Auth** — Authentication with GitHub OAuth
- **Cloudflare D1** — SQLite database at the edge

### Adding a New Route

Routes live in `apps/dashboard/src/routes/`. TanStack Router uses file-based routing — create a new file and the route is automatically registered.

Protected routes go under `_protected/` which enforces authentication.

### Adding a UI Component

Shared components go in `packages/ui/src/components/`. App-specific components go in `apps/dashboard/src/components/`.

## Pull Requests

- Keep PRs focused — one feature or fix per PR
- Write a clear title and description explaining what changed and why
- Include screenshots for UI changes
- Make sure all checks pass before requesting review

## Reporting Bugs

Open an [issue](https://github.com/stylessh/diffkit/issues) with:

- A clear description of the bug
- Steps to reproduce
- Expected vs actual behavior
- Screenshots if applicable

## Suggesting Features

Open an [issue](https://github.com/stylessh/diffkit/issues) with the `enhancement` label describing:

- The problem you're trying to solve
- Your proposed solution
- Any alternatives you've considered

## License

By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025-present DiffKit Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
173 changes: 173 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# DiffKit

A fast, design-first GitHub dashboard for developers who want to stay on top of their pull requests, issues, and code reviews — without the noise.

## Features

- **Pull Requests** — View, filter, and manage your open PRs across repos
- **Issues** — Track assigned issues with labels, milestones, and status
- **Code Reviews** — See pending review requests in one place
- **PR Diff Viewer** — Review pull request changes with inline comments
- **Dark Mode** — Full dark mode support out of the box
- **Fast** — Deployed on Cloudflare Workers at the edge

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Framework | TanStack Start (React 19) |
| Routing | TanStack Router (file-based) |
| Data | TanStack Query + Octokit |
| Database | Cloudflare D1 (SQLite) via Drizzle ORM |
| Auth | Better Auth with GitHub OAuth |
| Styling | Tailwind CSS 4 + Radix UI |
| Icons | Lucide React |
| Build | Vite 7 + Turborepo |
| Runtime | Cloudflare Workers |
| Linting | Biome |

## Getting Started

### Prerequisites

- [Node.js](https://nodejs.org/) (v20+)
- [pnpm](https://pnpm.io/) (v10+)
- A [GitHub OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)

### Setup

1. **Clone the repo**

```bash
git clone https://github.com/stylessh/diffkit.git
cd diffkit
```

2. **Install dependencies**

```bash
pnpm install
```

3. **Configure environment variables**

Create a `.dev.vars` file in `apps/dashboard/`:

```
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
BETTER_AUTH_SECRET=a_random_32_character_string
BETTER_AUTH_URL=http://localhost:3000
```

> To get GitHub OAuth credentials, create a new OAuth App in [GitHub Developer Settings](https://github.com/settings/developers) with the callback URL set to `http://localhost:3000/api/auth/callback/github`.

4. **Run database migrations**

```bash
pnpm --filter dashboard migrate
```

5. **Start the dev server**

```bash
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) in your browser.


## Scripts

| Command | Description |
|---------|------------|
| `pnpm dev` | Start all dev servers |
| `pnpm build` | Build all packages and apps |
| `pnpm lint` | Lint the codebase |
| `pnpm check` | Run Biome checks |
| `pnpm check-types` | Type-check all packages |
| `pnpm format` | Format code with Biome |

## Roadmap

### Dashboard

- [x] Overview with PR, issue, and review counts
- [ ] Activity feed (recent events across repos)
- [ ] Customizable dashboard widgets

### Pull Requests

- [x] List PRs by role (authored, assigned, review requested, mentioned, involved)
- [x] PR detail view with metadata, body, and comments
- [x] PR diff viewer with syntax highlighting
- [x] Inline review comments on specific lines
- [x] Submit reviews (approve, request changes, comment)
- [x] Update branch with base
- [ ] Create new pull requests
- [ ] Merge pull requests (merge, squash, rebase)
- [ ] Close / reopen pull requests
- [ ] Edit PR title, body, and metadata
- [ ] Add / remove reviewers
- [ ] Add / remove labels
- [ ] Link issues to pull requests

### Issues

- [x] List issues by role (assigned, authored, mentioned)
- [x] Issue detail view with metadata, body, and comments
- [ ] Create new issues
- [ ] Close / reopen issues
- [ ] Comment on issues
- [ ] Edit issue title, body, and metadata
- [ ] Assign / unassign users
- [ ] Add / remove labels
- [ ] Set milestones

### Code Reviews

- [x] Pending review requests view
- [x] File tree navigator with status badges
- [x] Side-by-side diff view
- [x] Multi-line comment selection
- [ ] Resolve / unresolve review threads
- [ ] Suggest changes (code suggestions in comments)
- [ ] Review comment reactions

### Notifications

- [ ] Notification inbox
- [ ] Mark as read / unread
- [ ] Filter by type (PR, issue, review, CI)
- [ ] Desktop notifications

### Repositories

- [ ] Repository list and search
- [ ] Repository file browser
- [ ] Branch and tag management
- [ ] README preview

### Search

- [ ] Global search across PRs, issues, and repos
- [ ] Saved searches and filters
- [ ] Advanced query syntax

### General

- [x] GitHub OAuth authentication
- [x] Dark mode with system preference
- [x] Response caching with ETags
- [ ] Keyboard shortcuts
- [ ] Command palette
- [ ] User settings and preferences
- [ ] Mobile-responsive layout

## Contributing

We welcome contributions! Please read the [Contributing Guide](CONTRIBUTING.md) before submitting a pull request.

## License

[MIT](LICENSE)
4 changes: 2 additions & 2 deletions apps/dashboard/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"components": "@/components",
"hooks": "@/hooks",
"lib": "@/lib",
"utils": "@quickhub/ui/lib/utils",
"ui": "@quickhub/ui/components"
"utils": "@diffkit/ui/lib/utils",
"ui": "@diffkit/ui/components"
}
}
12 changes: 6 additions & 6 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@quickhub/dashboard",
"name": "@diffkit/dashboard",
"private": true,
"type": "module",
"imports": {
Expand All @@ -16,15 +16,15 @@
"check": "biome check",
"check-types": "tsc --noEmit",
"migrate": "pnpm run migrate:local",
"migrate:local": "node ../../scripts/run-d1-migrations.mjs quickhub-db --local",
"migrate:remote": "node ../../scripts/run-d1-migrations.mjs quickhub-db --remote",
"migrate:local": "node ../../scripts/run-d1-migrations.mjs DB --local",
"migrate:remote": "node ../../scripts/run-d1-migrations.mjs DB --remote",
"deploy": "pnpm run build && wrangler deploy"
},
"dependencies": {
"@cloudflare/vite-plugin": "^1.26.0",
"@pierre/diffs": "^1.1.12",
"@quickhub/icons": "workspace:*",
"@quickhub/ui": "workspace:*",
"@diffkit/icons": "workspace:*",
"@diffkit/ui": "workspace:*",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "latest",
"@tanstack/react-query": "latest",
Expand All @@ -46,7 +46,7 @@
"devDependencies": {
"@biomejs/biome": "2.4.5",
"@cloudflare/workers-types": "^4.20260405.1",
"@quickhub/typescript-config": "workspace:*",
"@diffkit/typescript-config": "workspace:*",
"@tanstack/devtools-vite": "latest",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
Expand Down
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/issues/issue-row.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommentIcon, IssuesIcon } from "@quickhub/icons";
import { cn } from "@quickhub/ui/lib/utils";
import { CommentIcon, IssuesIcon } from "@diffkit/icons";
import { cn } from "@diffkit/ui/lib/utils";
import { Link } from "@tanstack/react-router";
import { formatRelativeTime } from "#/components/pulls/pull-request-row";
import type { IssueSummary } from "#/lib/github.types";
Expand Down
8 changes: 4 additions & 4 deletions apps/dashboard/src/components/layouts/dashboard-topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
ReviewsIcon,
SunIcon,
SystemIcon,
} from "@quickhub/icons";
import { Avatar, AvatarFallback } from "@quickhub/ui/components/avatar";
import { Button } from "@quickhub/ui/components/button";
} from "@diffkit/icons";
import { Avatar, AvatarFallback } from "@diffkit/ui/components/avatar";
import { Button } from "@diffkit/ui/components/button";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -20,7 +20,7 @@ import {
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@quickhub/ui/components/dropdown-menu";
} from "@diffkit/ui/components/dropdown-menu";
import { Link, useRouter } from "@tanstack/react-router";
import { useTheme } from "next-themes";
import { useCallback, useEffect, useRef, useState } from "react";
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/layouts/error-screen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button } from "@quickhub/ui/components/button";
import { Button } from "@diffkit/ui/components/button";
import { type ErrorComponentProps, useRouter } from "@tanstack/react-router";
import { AlertCircleIcon, RefreshCwIcon } from "lucide-react";

Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/src/components/pulls/pull-request-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
GitPullRequestDraftIcon,
GitPullRequestIcon,
ViewIcon,
} from "@quickhub/icons";
import { Markdown } from "@quickhub/ui/components/markdown";
import { cn } from "@quickhub/ui/lib/utils";
} from "@diffkit/icons";
import { Markdown } from "@diffkit/ui/components/markdown";
import { cn } from "@diffkit/ui/lib/utils";
import { useQuery } from "@tanstack/react-query";
import { Link, useRouter } from "@tanstack/react-router";
import { useState } from "react";
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/lib/query-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useEffect, useRef } from "react";
import { githubCachePolicy } from "./github-cache-policy";
import { readStoredTabs, type Tab, useTabs } from "./tab-store";

const GITHUB_QUERY_CACHE_STORAGE_KEY = "quickhub:github-query-cache:v1";
const GITHUB_QUERY_CACHE_STORAGE_KEY = "diffkit:github-query-cache:v1";
const GITHUB_QUERY_CACHE_MAX_AGE_MS = githubCachePolicy.viewer.gcTimeMs;

type PersistedGitHubQueryCache = {
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/lib/tab-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface Tab {
deletions?: number;
}

export const TABS_STORAGE_KEY = "quickhub:tabs";
export const TABS_STORAGE_KEY = "diffkit:tabs";

export function readStoredTabs(): Tab[] {
if (typeof window === "undefined") return [];
Expand Down
Loading