From d5f7967fd0e457cf6e16f9a41f3b674b1b05a83f Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Wed, 15 Jul 2026 12:09:16 +0600 Subject: [PATCH 001/103] ci: suppress Node 20 deprecation warning in docker publish --- .github/workflows/docker-publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a181b27..ff47d5d 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -9,6 +9,7 @@ on: env: REGISTRY: docker.io IMAGE_NAME: opencodehub/opencodehub + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true" jobs: build-and-push: From cafe93a38fb92e05f6706815ef1953054822be3c Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Wed, 15 Jul 2026 12:14:24 +0600 Subject: [PATCH 002/103] fix: optimize Dockerfile - use --production for deps, slim base image - Use bun install --production to exclude dev dependencies - Use oven/bun:1-slim instead of oven/bun:1 (smaller base) - Use --no-install-recommends for apt packages - Clean up apt cache in same layer - Rename REPOS_PATH to GIT_REPOS_PATH for consistency --- Dockerfile | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index dae0d48..7ae6c54 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -r COPY package.json bun.lock ./ COPY cli/package.json ./cli/package.json RUN for i in 1 2 3; do \ - bun install --frozen-lockfile && break || \ + bun install --frozen-lockfile --production && break || \ (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ done @@ -24,19 +24,20 @@ ENV SKIP_REDIS_CHECK=1 RUN bun run build # Production image -FROM oven/bun:1 AS runner +FROM oven/bun:1-slim AS runner WORKDIR /app # Install git, ssh, and bash (needed for git operations and entrypoint) -RUN apt-get update && apt-get install -y git openssh-client bash && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y --no-install-recommends git openssh-client bash && \ + rm -rf /var/lib/apt/lists/* # Create data directories -RUN mkdir -p /data/repos /data/storage /data/cache /data/ssh && \ +RUN mkdir -p /data/repositories /data/storage /data/cache /data/ssh && \ chown -R bun:bun /data -# Copy built application +# Copy only what's needed for runtime COPY --from=builder --chown=bun:bun /app/dist ./dist -COPY --from=builder --chown=bun:bun /app/node_modules ./node_modules +COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules COPY --from=builder --chown=bun:bun /app/package.json ./ # Copy drizzle config and schema for migrations @@ -51,7 +52,7 @@ COPY --chown=bun:bun docker-entrypoint.sh ./ ENV HOST=0.0.0.0 ENV PORT=4321 ENV DATA_DIR=/data -ENV REPOS_PATH=/data/repos +ENV GIT_REPOS_PATH=/data/repositories ENV STORAGE_PATH=/data/storage ENV CACHE_PATH=/data/cache ENV SSH_PATH=/data/ssh From 588337abefe304ed2b2d8ec64462e6ac3277721c Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Thu, 16 Jul 2026 13:01:20 +0600 Subject: [PATCH 003/103] docs: rich professional README with video placeholder and complete feature overview --- README.md | 262 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 193 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 8e20815..578ad0d 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,83 @@ -# OpenCodeHub -

- OpenCodeHub + OpenCodeHub

+

The self-hosted Git platform that doesn't compromise.

+

- CLI version - License - CI + CI + License + CLI version + Docs + Docker

-Self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI code review. Built for teams that want speed and control. - --- -## Quick Start +OpenCodeHub is a self-hosted Git platform with **stacked PRs**, **merge queue**, **CI/CD pipelines**, and **AI code review**. One platform for everything your team needs — no vendor lock-in, no per-seat pricing. -```bash -git clone https://github.com/swadhinbiswas/OpencodeHub.git -cd OpenCodeHub -cp .env.example .env -docker compose up -d -docker compose exec app bun run scripts/seed-admin.ts -``` - -Open `http://localhost:4321` and create your admin account. - -**Requirements**: Docker, or Node.js 20+ with PostgreSQL. +**[Documentation](https://docs.opencodehub.space)** · **[Deploy in 5 minutes](#deploy)** · **[CLI Reference](https://docs.opencodehub.space/reference/cli-commands/)** --- -## What It Does - -**Git Hosting** — HTTP smart protocol + SSH push/pull. Forks, mirroring, LFS, wiki. +## Demo -**Stacked PRs** — Break large changes into dependent branches. Each PR builds on the previous. Review in order, merge in order. + +[![OpenCodeHub Demo](https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg)](https://youtu.be/VIDEO_ID) -**Merge Queue** — Stack-aware merge ordering with speculative CI builds. `main` never breaks. +*Watch the full walkthrough — deployment, stacked PRs, AI review, and merge queue in action.* -**CI/CD** — GitHub Actions-compatible workflows. Docker-based runners. Artifacts, secrets, matrix builds. +--- -**AI Code Review** — GPT-4, Claude, Gemini, Groq, Ollama, and 5+ more providers. Catches bugs, security issues, and style problems automatically. +## Why OpenCodeHub? -**Issues & Projects** — Labels, milestones, custom fields, kanban boards. +| Problem | Solution | +|---------|----------| +| PRs too large to review | **Stacked PRs** — break changes into small, dependent branches | +| Merge conflicts on main | **Merge Queue** — stack-aware ordering with speculative CI builds | +| Slow review cycles | **AI Code Review** — catches bugs before humans even look | +| Split across 5+ services | **All-in-one** — Git, PRs, CI/CD, issues, wiki in one place | +| Data leaves your servers | **Self-hosted** — your code stays on your hardware | -**175+ API Endpoints** — REST + GraphQL. Full programmatic access to everything. +--- -**CLI** — `och` command line tool for stack workflows, reviews, and repository management. +## Features + +### Core Platform +- **Git Hosting** — HTTP smart protocol + SSH push/pull, forks, mirroring, LFS +- **Pull Requests** — Inline comments, approvals, suggested changes, draft PRs +- **Issues & Projects** — Labels, milestones, custom fields, kanban boards +- **Wiki** — Repository wiki with revision history +- **Organizations** — Teams, collaborators, role-based access control + +### Delivery Workflows +- **Stacked PRs** — Graphite-style stacked branches with web + CLI support +- **Merge Queue** — Stack-aware queue with speculative builds and priority lanes +- **CI/CD Pipelines** — GitHub Actions-compatible engine with Docker-based runners +- **Webhooks** — Outbound webhooks with event filtering and HMAC signing +- **Automations** — Rule-based workflow automation for PRs and deployments + +### AI & Quality +- **AI Code Review** — 10+ providers: GPT-4, Claude, Gemini, Groq, Ollama, OpenRouter +- **Secret Scanning** — Detect secrets in commits before they reach production +- **Branch Protection** — Required reviews, status checks, push restrictions +- **Developer Metrics** — PR velocity, review efficiency, time-to-merge tracking + +### Security +- **Authentication** — JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP, SSO/SAML +- **Authorization** — RBAC with roles, team permissions, collaborator levels +- **Rate Limiting** — Redis-backed per-endpoint rate limiting +- **Audit Logging** — Track all administrative actions + +### Extensibility +- **REST API** — 175+ endpoints covering all platform features +- **GraphQL** — Full GraphQL endpoint for flexible queries +- **CLI** — `och` command line tool with 20+ command groups +- **Storage** — Local, S3, MinIO, R2, or any S3-compatible backend --- @@ -66,73 +93,170 @@ docker compose up -d docker compose exec app bun run scripts/seed-admin.ts ``` +Open **http://localhost:4321** and create your admin account. + ### Render (Free) -See [Render Deployment Guide](docs/RENDER-DEPLOYMENT.md). Uses free PostgreSQL + Upstash Redis. +Deploy to Render with free PostgreSQL + Upstash Redis: -### Other Options +```bash +# 1. Create free Redis at upstash.com (Singapore region) +# 2. Push to GitHub +# 3. Render → New → Blueprint → Select your repo +# 4. Set REDIS_URL and SITE_URL +# 5. Deploy +``` + +See the [Render Deployment Guide](docs/RENDER-DEPLOYMENT.md) for step-by-step instructions. -| Platform | Guide | -|----------|-------| -| Docker Compose | [Deployment Guide](docs/administration/deployment.md) | -| NAS (Synology/TrueNAS) | [NAS Guide](docs/administration/deploy-nas.md) | -| Kubernetes | [K8s Guide](docs/administration/kubernetes.md) | -| Cloudflare Tunnel | [Cloudflare Guide](docs/administration/deploy-cloudflare.md) | -| Free Tier Options | [Free Deployment](docs/FREE-DEPLOYMENT.md) | +### More Deployment Options + +| Platform | Guide | Cost | +|----------|-------|------| +| Docker Compose | [Deployment Guide](docs/administration/deployment.md) | Free | +| Render (Asia) | [Render Guide](docs/RENDER-DEPLOYMENT.md) | Free | +| Oracle Cloud | [Free Deployment](docs/FREE-DEPLOYMENT.md) | Free forever | +| NAS (Synology/TrueNAS) | [NAS Guide](docs/administration/deploy-nas.md) | Free | +| Kubernetes | [K8s Guide](docs/administration/kubernetes.md) | Free | +| Cloudflare Tunnel | [Cloudflare Guide](docs/administration/deploy-cloudflare.md) | Free | + +--- + +## CLI + +```bash +# Install +npm install -g opencodehub-cli + +# Login +och auth login --url http://localhost:4321 + +# Stacked PR workflow +och stack create feature/auth-step-1 +och stack submit +och stack sync + +# Merge queue +och queue list +och queue add + +# Interactive cockpit +och focus +``` + +[Full CLI Reference](https://docs.opencodehub.space/reference/cli-commands/) + +--- + +## API + +```bash +# Create a repository +curl -X POST http://localhost:4321/api/repos \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"my-project","visibility":"public"}' + +# List pull requests +curl http://localhost:4321/api/repos/owner/repo/pulls \ + -H "Authorization: Bearer YOUR_TOKEN" + +# GraphQL +curl -X POST http://localhost:4321/api/graphql \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"query": "{ repositories { name owner { username } } }"}' +``` + +[Full API Reference](https://docs.opencodehub.space/api/rest-api/) --- ## Tech Stack -| Layer | Tech | -|-------|------| -| Framework | Astro 4.x SSR + React 18 | -| Database | PostgreSQL, SQLite, Turso (Drizzle ORM) | -| Auth | JWT, OAuth, 2FA/TOTP, SSO/SAML | +| Layer | Technology | +|-------|-----------| +| Framework | Astro 4.x (SSR) + React 18 | +| UI | Tailwind CSS + Radix UI | +| Database | PostgreSQL / SQLite / Turso (Drizzle ORM) | +| Auth | JWT + OAuth + 2FA/TOTP + SSO/SAML | +| Git | Native git CLI + simple-git + isomorphic-git | +| SSH | ssh2 library | | CI/CD | Docker-based runners, GitHub Actions syntax | -| Storage | Local, S3, MinIO, R2, or any S3-compatible | -| CLI | Commander.js (`npm i -g opencodehub-cli`) | +| Storage | Local filesystem or S3-compatible (AWS, MinIO, R2) | +| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter | +| Queue | BullMQ + Redis | +| CLI | Commander.js + Inquirer | --- -## Documentation +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CLIENTS │ +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ OPENCODEHUB PLATFORM │ +│ Web UI (Astro+React) │ REST API (175+ routes) │ GraphQL │ +│ Git Server (HTTP) │ SSH Server (ssh2) │ │ +│ Pipeline Runner (Docker) │ │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- -Full docs at [docs.opencodehub.space](https://docs.opencodehub.space/): +## Documentation -- [Installation](https://docs.opencodehub.space/getting-started/installation/) -- [Configuration](https://docs.opencodehub.space/administration/configuration/) -- [Stacked PRs](https://docs.opencodehub.space/features/stacked-prs/) -- [AI Code Review](https://docs.opencodehub.space/features/ai-review/) -- [CLI Reference](https://docs.opencodehub.space/reference/cli-commands/) -- [API Reference](https://docs.opencodehub.space/api/rest-api/) +| Topic | Link | +|-------|------| +| Installation | [docs.opencodehub.space/getting-started/installation](https://docs.opencodehub.space/getting-started/installation/) | +| Configuration | [docs.opencodehub.space/administration/configuration](https://docs.opencodehub.space/administration/configuration/) | +| Stacked PRs | [docs.opencodehub.space/features/stacked-prs](https://docs.opencodehub.space/features/stacked-prs/) | +| AI Code Review | [docs.opencodehub.space/features/ai-review](https://docs.opencodehub.space/features/ai-review/) | +| CLI Reference | [docs.opencodehub.space/reference/cli-commands](https://docs.opencodehub.space/reference/cli-commands/) | +| API Reference | [docs.opencodehub.space/api/rest-api](https://docs.opencodehub.space/api/rest-api/) | +| Deployment | [docs.opencodehub.space/administration/deployment](https://docs.opencodehub.space/administration/deployment/) | --- -## CLI +## Contributing ```bash -npm install -g opencodehub-cli +# Clone and setup +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpenCodeHub +cp .env.example .env +npm install +npm run db:push +bun run scripts/seed-admin.ts -och auth login --url http://localhost:4321 -och stack create feature/auth -och stack submit -och focus +# Start development +npm run dev + +# Run tests +npm run test ``` ---- +See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards. -## Contributing +--- -1. Fork the repo -2. Create a branch -3. `npm install && npm run db:push && npm run dev` -4. Run tests: `npm run test` -5. Open a PR +## Community -See [CONTRIBUTING.md](CONTRIBUTING.md) for details. +- **GitHub**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) +- **Issues**: [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) +- **Discussions**: [GitHub Discussions](https://github.com/swadhinbiswas/OpencodeHub/discussions) --- ## License -MIT +[MIT](LICENSE) — Use it however you want. From 6b35cc53545d06edcc8bd8240770a9520970d81c Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Thu, 16 Jul 2026 13:15:20 +0600 Subject: [PATCH 004/103] =?UTF-8?q?=F0=9F=A4=AFfix:=20remove=20duplicate?= =?UTF-8?q?=20HTML=20structure=20from=20api-docs=20page=20causing=20build?= =?UTF-8?q?=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/api-docs.astro | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/pages/api-docs.astro b/src/pages/api-docs.astro index 2bab145..7d9adef 100644 --- a/src/pages/api-docs.astro +++ b/src/pages/api-docs.astro @@ -2,21 +2,8 @@ import BaseLayout from "@/layouts/BaseLayout.astro"; --- - - - - OpenCodeHub API Reference - - - - - - - - - + +
+
+ + From cc17f31e4531c4fe7263a2273a091ab029682ddb Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Thu, 16 Jul 2026 13:18:48 +0600 Subject: [PATCH 005/103] docs: add Docker Hub description with logo and setup instructions --- .github/workflows/docker-publish.yml | 4 +- DOCKERHUB.md | 81 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 DOCKERHUB.md diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index ff47d5d..978e168 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -85,5 +85,5 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} repository: ${{ env.IMAGE_NAME }} - readme-filepath: ./README.md - short-description: "A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review." + readme-filepath: ./DOCKERHUB.md + short-description: "Self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI code review" diff --git a/DOCKERHUB.md b/DOCKERHUB.md new file mode 100644 index 0000000..2a1dccf --- /dev/null +++ b/DOCKERHUB.md @@ -0,0 +1,81 @@ +# OpenCodeHub + +![OpenCodeHub](https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/public/logo-light.png) + +**The self-hosted Git platform that doesn't compromise.** + +OpenCodeHub is a self-hosted Git platform with stacked PRs, merge queue, CI/CD pipelines, and AI code review. One platform for everything your team needs — no vendor lock-in, no per-seat pricing. + +--- + +## Features + +- **Stacked PRs** — Graphite-style stacked branches for incremental review +- **Merge Queue** — Stack-aware merge ordering with speculative CI builds +- **CI/CD Pipelines** — GitHub Actions-compatible workflows with Docker runners +- **AI Code Review** — 10+ providers: GPT-4, Claude, Gemini, Groq, Ollama +- **Git Hosting** — HTTP smart protocol + SSH push/pull, forks, mirroring, LFS +- **Issues & Projects** — Labels, milestones, custom fields, kanban boards +- **175+ API Endpoints** — REST + GraphQL for full programmatic access +- **CLI** — `och` command line tool for stack workflows and repository management + +--- + +## Quick Start + +```bash +# Pull and run +docker run -d \ + --name opencodehub \ + -p 4321:4321 \ + -v opencodehub-data:/data \ + -e JWT_SECRET=$(openssl rand -hex 32) \ + -e SESSION_SECRET=$(openssl rand -hex 32) \ + -e DATABASE_URL=postgresql://user:pass@db:5432/opencodehub \ + opencodehub/opencodehub:latest +``` + +Or with Docker Compose: + +```bash +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpenCodeHub +cp .env.example .env +docker compose up -d +docker compose exec app bun run scripts/seed-admin.ts +``` + +Open **http://localhost:4321** and create your admin account. + +--- + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `DATABASE_URL` | Yes | PostgreSQL connection string | +| `JWT_SECRET` | Yes | Random 32+ char string for JWT signing | +| `SESSION_SECRET` | Yes | Random 32+ char string for session encryption | +| `REDIS_URL` | Yes | Redis connection string | +| `SITE_URL` | Yes | Your public URL (e.g., `https://git.example.com`) | +| `STORAGE_TYPE` | No | `local` (default) or `s3` | + +--- + +## Documentation + +Full documentation: **[docs.opencodehub.space](https://docs.opencodehub.space)** + +- [Installation Guide](https://docs.opencodehub.space/getting-started/installation/) +- [Configuration Reference](https://docs.opencodehub.space/administration/configuration/) +- [Deployment Options](https://docs.opencodehub.space/administration/deployment/) +- [CLI Reference](https://docs.opencodehub.space/reference/cli-commands/) +- [API Reference](https://docs.opencodehub.space/api/rest-api/) + +--- + +## Links + +- **GitHub**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) +- **License**: MIT From 36b85b15fb34c3b75805d4b5e9e4a0abd5373371 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Thu, 16 Jul 2026 13:28:03 +0600 Subject: [PATCH 006/103] =?UTF-8?q?fix:=F0=9F=A5=9F=20replace=20@apply=20w?= =?UTF-8?q?ith=20plain=20CSS=20in=20layout=20style=20blocks=20-=20AdminLay?= =?UTF-8?q?out:=20replace=20@apply=20with=20explicit=20CSS=20properties=20?= =?UTF-8?q?-=20admin/index:=20remove=20:global()=20wrapper=20from=20style?= =?UTF-8?q?=20block=20-=20BaseLayout:=20replace=20@apply=20with=20plain=20?= =?UTF-8?q?CSS=20for=20container?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/AdminLayout.astro | 5 +++-- src/layouts/BaseLayout.astro | 21 ++++++++++++++++++++- src/pages/admin/index.astro | 10 ++++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/layouts/AdminLayout.astro b/src/layouts/AdminLayout.astro index 30ca6b9..6745932 100644 --- a/src/layouts/AdminLayout.astro +++ b/src/layouts/AdminLayout.astro @@ -18,9 +18,10 @@ const { title } = Astro.props; diff --git a/src/pages/admin/index.astro b/src/pages/admin/index.astro index 234ec04..deebafe 100644 --- a/src/pages/admin/index.astro +++ b/src/pages/admin/index.astro @@ -12,20 +12,4 @@ if (!user?.isAdmin) return Astro.redirect("/"); - - diff --git a/src/styles/globals.css b/src/styles/globals.css index f6404c4..f749d08 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -664,4 +664,21 @@ img { /* Better overflow handling */ .overflow-x-auto { -webkit-overflow-scrolling: touch; +} + +/* Admin HUD styles */ +.glass-panel { + background: rgba(13, 17, 23, 0.3); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); +} + +.custom-scrollbar::-webkit-scrollbar { + width: 4px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.1); + border-radius: 2px; } \ No newline at end of file From 20833f561d4f170eec3fcafdad2ac48d59d26794 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Thu, 16 Jul 2026 18:52:35 +0600 Subject: [PATCH 008/103] fix: remove all CSS style blocks from pages causing Docker build failure - notification-preferences: remove checkbox style block - blob/[branch]/[...path]: remove shiki/scrollbar style block - blame/[branch]/[path]: remove shiki/scrollbar style block - ide/index: move inline styles to body attribute - globals.css: add shiki/scrollbar styles centrally --- .../[repo]/blame/[branch]/[...path].astro | 29 ---------------- .../[repo]/blob/[branch]/[...path].astro | 34 ------------------- src/pages/[owner]/[repo]/ide/index.astro | 7 +--- .../settings/notification-preferences.astro | 7 ---- src/styles/globals.css | 32 +++++++++++++++++ 5 files changed, 33 insertions(+), 76 deletions(-) diff --git a/src/pages/[owner]/[repo]/blame/[branch]/[...path].astro b/src/pages/[owner]/[repo]/blame/[branch]/[...path].astro index 8c51522..e0e6473 100644 --- a/src/pages/[owner]/[repo]/blame/[branch]/[...path].astro +++ b/src/pages/[owner]/[repo]/blame/[branch]/[...path].astro @@ -250,32 +250,3 @@ const repo = { - - diff --git a/src/pages/[owner]/[repo]/blob/[branch]/[...path].astro b/src/pages/[owner]/[repo]/blob/[branch]/[...path].astro index 63d0d6a..b6087f4 100644 --- a/src/pages/[owner]/[repo]/blob/[branch]/[...path].astro +++ b/src/pages/[owner]/[repo]/blob/[branch]/[...path].astro @@ -381,40 +381,6 @@ function formatSize(bytes: number) { - - - - diff --git a/src/styles/globals.css b/src/styles/globals.css index f749d08..16c76df 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -666,6 +666,38 @@ img { -webkit-overflow-scrolling: touch; } +/* Code block scrollbar */ +.blob-scrollbar::-webkit-scrollbar { + height: 10px; + width: 10px; +} +.blob-scrollbar::-webkit-scrollbar-track { + background: transparent; +} +.blob-scrollbar::-webkit-scrollbar-thumb { + background: #30363d; + border-radius: 5px; + border: 2px solid #0d1117; +} +.blob-scrollbar::-webkit-scrollbar-thumb:hover { + background: #424a53; +} + +/* Shiki code highlighting overrides */ +.shiki { + background-color: transparent !important; + margin: 0 !important; + padding: 0 !important; +} +.shiki code { + display: flex; + flex-direction: column; +} +.shiki .line { + height: 1.5rem; + display: block; +} + /* Admin HUD styles */ .glass-panel { background: rgba(13, 17, 23, 0.3); From b6ccd26e7d44261bdb05b93eabbcf8716333a449 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 01:29:47 +0600 Subject: [PATCH 009/103] fix: remove highlight.js CSS import causing Docker build failure --- src/pages/[owner]/[repo]/index.astro | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/[owner]/[repo]/index.astro b/src/pages/[owner]/[repo]/index.astro index d3a1532..3e6ebd6 100644 --- a/src/pages/[owner]/[repo]/index.astro +++ b/src/pages/[owner]/[repo]/index.astro @@ -30,7 +30,6 @@ import RepoHeader from "@/components/repo/RepoHeader.astro"; import FileExplorer from "@/components/repo/FileExplorer.astro"; import { RepoControls } from "@/components/repo/RepoControls"; import { Card } from "@/components/ui/card"; -import "highlight.js/styles/github-dark.css"; const { owner: ownerName, repo: repoName } = Astro.params; const db = getDatabase(); From 121ebd96b5abfeab56c7e928e6a94f8a54fa6397 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 02:23:27 +0600 Subject: [PATCH 010/103] fix: remove last style block from BaseLayout, move fonts/container to globals.css - Remove diff --git a/src/styles/globals.css b/src/styles/globals.css index 16c76df..891942a 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -713,4 +713,23 @@ img { .custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 2px; +} + +/* Base layout styles */ +body { + font-family: "Inter", system-ui, sans-serif; +} + +code, +pre, +.font-mono { + font-family: "JetBrains Mono", monospace; +} + +.text-balance { + text-wrap: balance; +} + +html { + scroll-behavior: smooth; } \ No newline at end of file From 38dfc53a2042308936e1d4b2994ad072ffc2f658 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 11:46:51 +0600 Subject: [PATCH 011/103] fix: use postcss transformer to resolve vite:css build failure Vite 8.x defaults to LightningCSS for CSS transformation, which conflicts with Tailwind CSS v3 directives (@tailwind, @apply, @layer). This caused 'Attempted to assign to readonly property' error during Docker build on src/styles/globals.css. Explicitly set css.transformer to 'postcss' in the Vite config to ensure compatibility with Tailwind CSS v3. --- astro.config.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index a97c5c5..3d50adc 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -21,6 +21,9 @@ export default defineConfig({ host: true, }, vite: { + css: { + transformer: 'postcss', + }, server: { watch: { ignored: ['**/data/**', '**/repos/**', '**/storage/**', '**/.tmp/**', '**/postgres/**', '**/redis/**'], From 21ee8c15c13da4feb3b2ca0dcb8cc8448b862faa Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 11:54:12 +0600 Subject: [PATCH 012/103] fix: pin postcss to 8.4.x to fix Docker build failure The Docker build uses Astro 4.x which depends on Vite 5.4.21. PostCSS 8.5.x introduced frozen/readonly AST node properties that Vite 5.4.21's compileCSS function tries to mutate, causing: 'Attempted to assign to readonly property' on globals.css Pin PostCSS to ~8.4.49 (both in devDependencies and overrides) to ensure compatibility until Astro upgrades to Vite 6+. --- bun.lock | 5 +++-- package.json | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index d3c9f44..415f9ca 100644 --- a/bun.lock +++ b/bun.lock @@ -140,7 +140,7 @@ "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "^8.5.10", + "postcss": "~8.4.49", "tailwindcss": "^3.4.4", "typescript": "^5.9.3", "vitest": "^4.1.5", @@ -189,6 +189,7 @@ "lodash-es": "^4.17.23", "picomatch": "^4.0.4", "picomatch@2": "^2.3.2", + "postcss": "~8.4.49", "preact": "^10.29.0", "protobufjs": "^7.6.0", "qs": "^6.15.0", @@ -2442,7 +2443,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.15", "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "postcss": ["postcss@8.4.49", "https://registry.npmmirror.com/postcss/-/postcss-8.4.49.tgz", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="], "postcss-import": ["postcss-import@15.1.0", "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], diff --git a/package.json b/package.json index e97ba04..61a3ad1 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "^8.5.10", + "postcss": "~8.4.49", "tailwindcss": "^3.4.4", "typescript": "^5.9.3", "vitest": "^4.1.5" @@ -205,6 +205,7 @@ "preact": "^10.29.0", "brace-expansion": "^2.1.1", "diff": "^5.2.0", - "cross-spawn": "^7.0.6" + "cross-spawn": "^7.0.6", + "postcss": "~8.4.49" } } From 64926c1b49087fe9169f6baa8cf790066f534eff Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 20:48:21 +0600 Subject: [PATCH 013/103] fix: resolve docker build error and overhaul UI with premium github-like design - Fixed Vite 5 / PostCSS 8.5.x frozen AST bug by removing @astrojs/tailwind integration and pinning postcss. - Moved CSS tools to dependencies and updated Dockerfile for multi-stage dependency installs. - Cleaned and modernized globals.css. - Merged duplicate class definitions (.glass-panel, .custom-scrollbar). - Overhauled light theme to reflect modern GitHub aesthetics. - Added missing Shadcn sidebar and chart tokens for all themes. - Updated scrollbar implementation to use standard `scrollbar-width` and `scrollbar-color` properties. --- Dockerfile | 13 +- astro.config.mjs | 4 - bun.lock | 25 ++- package.json | 9 +- src/styles/globals.css | 405 +++++++++++++++++++++-------------------- 5 files changed, 233 insertions(+), 223 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7ae6c54..f6f70a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,16 +6,23 @@ LABEL org.opencontainers.image.title="OpenCodeHub" \ org.opencontainers.image.source="https://github.com/swadhinbiswas/OpencodeHub" \ org.opencontainers.image.licenses="MIT" -# Install dependencies +# Install ALL dependencies (needed for build including CSS tooling) FROM base AS deps RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* COPY package.json bun.lock ./ COPY cli/package.json ./cli/package.json RUN for i in 1 2 3; do \ - bun install --frozen-lockfile --production && break || \ + bun install --frozen-lockfile && break || \ (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ done +# Install production dependencies only (for runtime image) +FROM base AS prod-deps +RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* +COPY package.json bun.lock ./ +COPY cli/package.json ./cli/package.json +RUN bun install --frozen-lockfile --production + # Build the application FROM base AS builder COPY --from=deps /app/node_modules ./node_modules @@ -37,7 +44,7 @@ RUN mkdir -p /data/repositories /data/storage /data/cache /data/ssh && \ # Copy only what's needed for runtime COPY --from=builder --chown=bun:bun /app/dist ./dist -COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules +COPY --from=prod-deps --chown=bun:bun /app/node_modules ./node_modules COPY --from=builder --chown=bun:bun /app/package.json ./ # Copy drizzle config and schema for migrations diff --git a/astro.config.mjs b/astro.config.mjs index 3d50adc..23a30e2 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,6 +1,5 @@ import node from "@astrojs/node"; import react from "@astrojs/react"; -import tailwind from "@astrojs/tailwind"; import { defineConfig } from "astro/config"; import icon from "astro-icon"; @@ -8,9 +7,6 @@ export default defineConfig({ integrations: [ react(), icon(), - tailwind({ - applyBaseStyles: false, - }), ], output: "server", adapter: node({ diff --git a/bun.lock b/bun.lock index 415f9ca..6d0c103 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,6 @@ "@astrojs/check": "^0.9.6", "@astrojs/node": "^8.3.0", "@astrojs/react": "^3.6.0", - "@astrojs/tailwind": "^5.1.0", "@astrojs/vercel": "^7.7.0", "@aws-sdk/client-s3": "^3.948.0", "@aws-sdk/lib-storage": "^3.972.0", @@ -56,6 +55,7 @@ "arctic": "^3.7.0", "astro": "^4.11.0", "astro-icon": "^1.1.5", + "autoprefixer": "^10.4.19", "bcryptjs": "^2.4.3", "better-sqlite3": "^11.1.2", "bullmq": "^5.65.1", @@ -93,6 +93,7 @@ "pg": "^8.16.3", "pino": "^10.1.0", "pino-loki": "^3.0.0", + "postcss": "~8.4.49", "postgres": "^3.4.7", "prom-client": "^15.1.3", "qrcode": "^1.5.4", @@ -111,6 +112,7 @@ "sonner": "^2.0.7", "ssh2": "^1.15.0", "tailwind-merge": "^2.6.0", + "tailwindcss": "^3.4.4", "tailwindcss-animate": "^1.0.7", "three": "^0.182.0", "unified": "^11.0.5", @@ -137,11 +139,8 @@ "@types/ssh2": "^1.15.0", "@types/three": "^0.182.0", "@vitest/coverage-v8": "^4.1.5", - "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "~8.4.49", - "tailwindcss": "^3.4.4", "typescript": "^5.9.3", "vitest": "^4.1.5", }, @@ -231,8 +230,6 @@ "@astrojs/react": ["@astrojs/react@3.6.3", "https://registry.npmmirror.com/@astrojs/react/-/react-3.6.3.tgz", { "dependencies": { "@vitejs/plugin-react": "^4.3.3", "ultrahtml": "^1.5.3", "vite": "^5.4.10" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21", "@types/react-dom": "^17.0.17 || ^18.0.6", "react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-5ihLQDH5Runddug5AZYlnp/Q5T81QxhwnWJXA9rchBAdh11c6UhBbv9Kdk7b2PkXoEU70CGWBP9hSh0VCR58eA=="], - "@astrojs/tailwind": ["@astrojs/tailwind@5.1.5", "https://registry.npmmirror.com/@astrojs/tailwind/-/tailwind-5.1.5.tgz", { "dependencies": { "autoprefixer": "^10.4.20", "postcss": "^8.5.1", "postcss-load-config": "^4.0.2" }, "peerDependencies": { "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", "tailwindcss": "^3.0.24" } }, "sha512-1diguZEau7FZ9vIjzE4BwavGdhD3+JkdS8zmibl1ene+EHgIU5hI0NMgRYG3yea+Niaf7cyMwjeWeLvzq/maxg=="], - "@astrojs/telemetry": ["@astrojs/telemetry@3.1.0", "https://registry.npmmirror.com/@astrojs/telemetry/-/telemetry-3.1.0.tgz", { "dependencies": { "ci-info": "^4.0.0", "debug": "^4.3.4", "dlv": "^1.1.3", "dset": "^3.1.3", "is-docker": "^3.0.0", "is-wsl": "^3.0.0", "which-pm-runs": "^1.1.0" } }, "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA=="], "@astrojs/vercel": ["@astrojs/vercel@7.8.2", "https://registry.npmmirror.com/@astrojs/vercel/-/vercel-7.8.2.tgz", { "dependencies": { "@astrojs/internal-helpers": "^0.4.1", "@vercel/analytics": "^1.3.1", "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.4", "esbuild": "^0.21.5", "fast-glob": "^3.3.2", "web-vitals": "^3.5.2" }, "peerDependencies": { "astro": "^4.2.0" } }, "sha512-U2JsfN0LzFMX5CPrcb+5bAAyqyYURP6Dk/mUIXX87r5x4baQy+juU+ntvf926YA0tR7u6jPRXHymE2axQ/l3NQ=="], @@ -1451,7 +1448,7 @@ "cheerio-select": ["cheerio-select@2.1.0", "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], - "chokidar": ["chokidar@3.6.0", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + "chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@1.1.4", "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], @@ -2539,7 +2536,7 @@ "readable-stream": ["readable-stream@4.7.0", "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - "readdirp": ["readdirp@3.6.0", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "real-require": ["real-require@0.2.0", "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], @@ -3037,8 +3034,6 @@ "@asamuzakjp/dom-selector/lru-cache": ["lru-cache@11.5.1", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], - "@astrojs/check/chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "@astrojs/react/vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], "@astrojs/vercel/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], @@ -3153,8 +3148,6 @@ "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "cli-progress/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "cli-table3/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -3285,6 +3278,8 @@ "svgo/css-tree": ["css-tree@2.3.1", "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], + "tailwindcss/chokidar": ["chokidar@3.6.0", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + "tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], "tar/chownr": ["chownr@3.0.0", "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], @@ -3313,8 +3308,6 @@ "yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "@astrojs/check/chokidar/readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - "@astrojs/react/vite/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], "@astrojs/vercel/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -3549,6 +3542,10 @@ "svgo/css-tree/mdn-data": ["mdn-data@2.0.30", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], + "tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], diff --git a/package.json b/package.json index 61a3ad1..f3ca8f2 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "@astrojs/check": "^0.9.6", "@astrojs/node": "^8.3.0", "@astrojs/react": "^3.6.0", - "@astrojs/tailwind": "^5.1.0", "@astrojs/vercel": "^7.7.0", "@aws-sdk/client-s3": "^3.948.0", "@aws-sdk/lib-storage": "^3.972.0", @@ -156,7 +155,10 @@ "unist-util-visit": "^5.1.0", "yaml": "^2.8.0", "zod": "^3.23.8", - "zustand": "^4.5.4" + "zustand": "^4.5.4", + "autoprefixer": "^10.4.19", + "postcss": "~8.4.49", + "tailwindcss": "^3.4.4" }, "devDependencies": { "@axe-core/playwright": "^4.10.0", @@ -176,11 +178,8 @@ "@types/ssh2": "^1.15.0", "@types/three": "^0.182.0", "@vitest/coverage-v8": "^4.1.5", - "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "~8.4.49", - "tailwindcss": "^3.4.4", "typescript": "^5.9.3", "vitest": "^4.1.5" }, diff --git a/src/styles/globals.css b/src/styles/globals.css index 891942a..b6f22e0 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -4,26 +4,44 @@ @layer base { :root { + /* Premium Design System - OpenCodeHub Light Theme */ --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; + --foreground: 210 11% 15%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; + --card-foreground: 210 11% 15%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; + --popover-foreground: 210 11% 15%; + --primary: 212 100% 48%; + --primary-foreground: 0 0% 100%; + --secondary: 210 14% 95%; + --secondary-foreground: 210 11% 15%; + --muted: 210 20% 98%; + --muted-foreground: 210 11% 40%; + --accent: 210 20% 96%; + --accent-foreground: 210 11% 15%; + --destructive: 354 70% 54%; + --destructive-foreground: 0 0% 100%; + --border: 210 18% 87%; + --input: 210 18% 87%; + --ring: 212 100% 48%; --radius: 0.5rem; + + --success: 142 70% 45%; + --hero-gradient: radial-gradient(ellipse at top center, hsl(210 20% 98%) 0%, hsl(0 0% 100%) 60%); + --glass-blur: 20px; + --glass-bg: rgba(255, 255, 255, 0.7); + + /* Sidebar Tokens */ + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + + /* Chart Tokens */ --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; @@ -56,6 +74,16 @@ --hero-gradient: radial-gradient(ellipse at top center, hsl(229 40% 18%) 0%, hsl(227 45% 5%) 60%); --glass-blur: 20px; --glass-bg: rgba(18, 23, 42, 0.6); + + /* Sidebar Tokens */ + --sidebar-background: 229 40% 12%; + --sidebar-foreground: 228 33% 92%; + --sidebar-primary: 233 100% 74%; + --sidebar-primary-foreground: 227 45% 5%; + --sidebar-accent: 229 40% 16%; + --sidebar-accent-foreground: 228 33% 92%; + --sidebar-border: 0 0% 100% / 0.06; + --sidebar-ring: 233 100% 74%; } /* GitHub Dark Theme */ @@ -81,6 +109,16 @@ --ring: 212 92% 63%; --hero-gradient: radial-gradient(ellipse at top center, hsl(215 22% 14%) 0%, hsl(216 28% 7%) 60%); --glass-bg: rgba(13, 17, 23, 0.6); + + /* Sidebar Tokens */ + --sidebar-background: 215 22% 10%; + --sidebar-foreground: 210 17% 82%; + --sidebar-primary: 212 92% 63%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 215 22% 14%; + --sidebar-accent-foreground: 210 17% 82%; + --sidebar-border: 215 14% 34%; + --sidebar-ring: 212 92% 63%; } /* Dracula Theme */ @@ -106,6 +144,16 @@ --ring: 326 100% 74%; --hero-gradient: radial-gradient(ellipse at top center, hsl(231 15% 25%) 0%, hsl(231 15% 18%) 60%); --glass-bg: rgba(40, 42, 54, 0.6); + + /* Sidebar Tokens */ + --sidebar-background: 231 15% 20%; + --sidebar-foreground: 60 30% 96%; + --sidebar-primary: 326 100% 74%; + --sidebar-primary-foreground: 231 15% 18%; + --sidebar-accent: 231 15% 25%; + --sidebar-accent-foreground: 60 30% 96%; + --sidebar-border: 231 15% 25%; + --sidebar-ring: 326 100% 74%; } /* Rose Pine Theme */ @@ -131,6 +179,16 @@ --ring: 343 35% 65%; --hero-gradient: radial-gradient(ellipse at top center, hsl(247 23% 15%) 0%, hsl(249 22% 12%) 60%); --glass-bg: rgba(25, 23, 36, 0.6); + + /* Sidebar Tokens */ + --sidebar-background: 247 23% 15%; + --sidebar-foreground: 248 19% 89%; + --sidebar-primary: 343 35% 65%; + --sidebar-primary-foreground: 249 22% 12%; + --sidebar-accent: 248 25% 18%; + --sidebar-accent-foreground: 248 19% 89%; + --sidebar-border: 248 25% 18%; + --sidebar-ring: 343 35% 65%; } /* Tokyo Night Theme */ @@ -156,6 +214,16 @@ --ring: 263 70% 74%; --hero-gradient: radial-gradient(ellipse at top center, hsl(235 18% 20%) 0%, hsl(235 18% 14%) 60%); --glass-bg: rgba(26, 27, 38, 0.6); + + /* Sidebar Tokens */ + --sidebar-background: 235 18% 16%; + --sidebar-foreground: 227 70% 87%; + --sidebar-primary: 263 70% 74%; + --sidebar-primary-foreground: 235 18% 14%; + --sidebar-accent: 235 18% 20%; + --sidebar-accent-foreground: 227 70% 87%; + --sidebar-border: 235 18% 20%; + --sidebar-ring: 263 70% 74%; } } @@ -165,18 +233,38 @@ } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground transition-colors duration-300; + font-family: "Inter", system-ui, sans-serif; + } + + code, + pre, + .font-mono { + font-family: "JetBrains Mono", monospace; + } + + html { + scroll-behavior: smooth; } } -/* Custom scrollbar */ +/* ========================================================================== + Base Elements & Globals + ========================================================================== */ + +/* Modern Custom scrollbar */ +* { + scrollbar-width: thin; + scrollbar-color: hsl(var(--border)) transparent; +} + ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { - background: hsl(var(--muted)); + background: transparent; border-radius: 4px; } @@ -213,11 +301,11 @@ } .diff-add-line { - @apply bg-green-50 dark:bg-green-900/20 border-l-4 border-green-500; + @apply bg-green-50/50 dark:bg-green-900/20 border-l-4 border-green-500; } .diff-remove-line { - @apply bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500; + @apply bg-red-50/50 dark:bg-red-900/20 border-l-4 border-red-500; } .diff-context-line { @@ -226,11 +314,11 @@ /* File tree styles */ .file-tree-item { - @apply flex items-center gap-2 px-2 py-1 rounded cursor-pointer hover:bg-accent transition-colors; + @apply flex items-center gap-2 px-2 py-1 rounded cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors; } .file-tree-item.selected { - @apply bg-accent text-accent-foreground; + @apply bg-accent text-accent-foreground font-medium; } /* Commit graph styles */ @@ -260,17 +348,13 @@ @apply text-blue-600 dark:text-blue-400; } -/* Animation keyframes */ -@keyframes pulse-slow { - - 0%, - 100% { - opacity: 1; - } +/* ========================================================================== + Animations + ========================================================================== */ - 50% { - opacity: 0.5; - } +@keyframes pulse-slow { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } } .animate-pulse-slow { @@ -278,20 +362,18 @@ } @keyframes spin-slow { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } } .animate-spin-slow { animation: spin-slow 3s linear infinite; } -/* Markdown preview styles */ +/* ========================================================================== + Markdown Elements + ========================================================================== */ + .markdown-body { @apply text-foreground; } @@ -321,19 +403,19 @@ } .markdown-body code { - @apply bg-muted px-1.5 py-0.5 rounded text-sm font-mono; + @apply bg-muted px-1.5 py-0.5 rounded text-sm font-mono border border-border; } .markdown-body pre { - @apply bg-muted p-4 rounded-lg overflow-x-auto mb-4; + @apply bg-muted p-4 rounded-lg overflow-x-auto mb-4 border border-border; } .markdown-body pre code { - @apply bg-transparent p-0; + @apply bg-transparent p-0 border-none; } .markdown-body blockquote { - @apply border-l-4 border-muted-foreground pl-4 italic my-4; + @apply border-l-4 border-muted-foreground pl-4 italic my-4 text-muted-foreground; } .markdown-body table { @@ -350,14 +432,17 @@ } .markdown-body a { - @apply text-primary hover:underline; + @apply text-primary hover:underline font-medium; } .markdown-body img { - @apply max-w-full rounded-lg; + @apply max-w-full rounded-lg border border-border; } -/* Loading skeleton */ +/* ========================================================================== + Utilities + ========================================================================== */ + .skeleton { @apply animate-pulse bg-muted rounded; } @@ -367,16 +452,20 @@ @apply focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background; } -/* ====== Premium Landing Page Animations ====== */ +.text-balance { + text-wrap: balance; +} + +/* ========================================================================== + Premium Landing Page Animations + ========================================================================== */ -/* Text reveal animation - staggered word appearance */ @keyframes text-reveal { 0% { opacity: 0; transform: translateY(20px); filter: blur(4px); } - 100% { opacity: 1; transform: translateY(0); @@ -389,28 +478,20 @@ opacity: 0; } -/* Shimmer effect for timeline */ @keyframes shimmer { - 0% { - transform: translateX(-100%); - } - - 100% { - transform: translateX(200%); - } + 0% { transform: translateX(-100%); } + 100% { transform: translateX(200%); } } .animate-shimmer { animation: shimmer 3s infinite linear; } -/* Slide up + fade */ @keyframes slide-fade-up { 0% { opacity: 0; transform: translateY(30px); } - 100% { opacity: 1; transform: translateY(0); @@ -421,15 +502,11 @@ animation: slide-fade-up 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards; } -/* Soft pulse for dots */ @keyframes soft-pulse { - - 0%, - 100% { + 0%, 100% { opacity: 0.6; transform: scale(1); } - 50% { opacity: 1; transform: scale(1.3); @@ -440,62 +517,74 @@ animation: soft-pulse 2s ease-in-out infinite; } -/* Glass card hover effect */ +/* CTA Button Styles */ +.btn-primary-glow { + background: linear-gradient(135deg, hsl(var(--primary)) 0%, color-mix(in srgb, hsl(var(--primary)) 80%, white) 100%); + color: hsl(var(--primary-foreground)); + transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1); + box-shadow: 0 4px 20px color-mix(in srgb, hsl(var(--primary)) 30%, transparent); +} + +.btn-primary-glow:hover { + transform: translateY(-2px); + box-shadow: 0 8px 30px color-mix(in srgb, hsl(var(--primary)) 50%, transparent); +} + +.hero-gradient { + background: var(--hero-gradient); +} + +/* ========================================================================== + Glass & Dashboard HUD + ========================================================================== */ + .glass-card { background: var(--glass-bg); backdrop-filter: blur(var(--glass-blur)); - border: 1px solid rgba(255, 255, 255, 0.06); + border: 1px solid color-mix(in srgb, currentColor 10%, transparent); border-radius: 16px; transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1); } .glass-card:hover { - background: rgba(18, 23, 42, 0.8); - border-color: rgba(255, 255, 255, 0.12); + background: color-mix(in srgb, var(--glass-bg) 90%, black); + border-color: color-mix(in srgb, currentColor 20%, transparent); transform: translateY(-2px); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); } -/* CTA Button Styles */ -.btn-primary-glow { - background: linear-gradient(135deg, hsl(233 100% 74%) 0%, hsl(230 100% 82%) 100%); - color: hsl(227 45% 5%); - transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1); - box-shadow: 0 4px 20px rgba(124, 140, 255, 0.3); +/* Glass panel for admin dashboard / HUD */ +.glass-panel { + background: color-mix(in srgb, var(--glass-bg) 80%, transparent); + backdrop-filter: blur(var(--glass-blur)); + border: 1px solid color-mix(in srgb, currentColor 10%, transparent); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); } -.btn-primary-glow:hover { - transform: translateY(-2px); - box-shadow: 0 8px 30px rgba(124, 140, 255, 0.5); +/* Custom thin scrollbar for panels */ +.custom-scrollbar::-webkit-scrollbar { + width: 4px; } - -/* Hero gradient background */ -.hero-gradient { - background: var(--hero-gradient); +.custom-scrollbar::-webkit-scrollbar-track { + background: transparent; } - -/* Motion preference support */ -@media (prefers-reduced-motion: reduce) { - - .animate-text-reveal, - .animate-shimmer, - .animate-slide-up, - .animate-soft-pulse { - animation: none; - opacity: 1; - transform: none; - } +.custom-scrollbar::-webkit-scrollbar-thumb { + background: color-mix(in srgb, currentColor 15%, transparent); + border-radius: 2px; +} +.custom-scrollbar::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, currentColor 25%, transparent); } -/* ====== Aceternity UI Styles ====== */ +/* ========================================================================== + Aceternity UI Styles + ========================================================================== */ -/* Spotlight animation */ @keyframes spotlight { 0% { opacity: 0; transform: translate(-72%, -62%) scale(0.5); } - 100% { opacity: 1; transform: translate(-50%, -40%) scale(1); @@ -506,7 +595,6 @@ animation: spotlight 2s ease 0.75s 1 forwards; } -/* Dot backgrounds for Hero Highlight */ .bg-dot-thick-neutral-800 { background-image: radial-gradient(circle, rgba(255, 255, 255, 0.05) 1px, transparent 1px); background-size: 16px 16px; @@ -517,73 +605,37 @@ background-size: 16px 16px; } -/* Grid background */ .bg-grid-white\/\[0\.02\] { background-image: linear-gradient(to right, rgba(255, 255, 255, 0.02) 1px, transparent 1px), linear-gradient(to bottom, rgba(255, 255, 255, 0.02) 1px, transparent 1px); background-size: 40px 40px; } -/* Gradient text */ .bg-clip-text { -webkit-background-clip: text; background-clip: text; } -/* Glass panel for admin dashboard */ -.glass-panel { - background: rgba(18, 23, 42, 0.6); - backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.06); -} - -/* Custom scrollbar for panels */ -.custom-scrollbar::-webkit-scrollbar { - width: 6px; -} - -.custom-scrollbar::-webkit-scrollbar-track { - background: transparent; -} - -.custom-scrollbar::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.1); - border-radius: 3px; -} - -.custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.2); -} - -/* Smooth transitions for all interactive elements */ @layer utilities { .transition-smooth { transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1); } } -/* Gradient text animation */ @keyframes gradient-x { - 0% { - background-position: 0% 50%; - } - - 50% { - background-position: 100% 50%; - } - - 100% { - background-position: 0% 50%; - } + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } } .animate-gradient-x { animation: gradient-x 3s ease infinite; } -/* ====== Responsive Utilities ====== */ +/* ========================================================================== + Responsive & Accessibility Utilities + ========================================================================== */ -/* Hide scrollbar on mobile */ .no-scrollbar::-webkit-scrollbar { display: none; } @@ -592,20 +644,16 @@ scrollbar-width: none; } -/* Better mobile spacing */ @media (max-width: 640px) { .container { padding-left: 1rem; padding-right: 1rem; } - - /* Stack grid items on mobile */ .grid-cols-2 { grid-template-columns: repeat(1, minmax(0, 1fr)); } } -/* Tablet adjustments */ @media (min-width: 640px) and (max-width: 1024px) { .container { padding-left: 1.5rem; @@ -613,33 +661,25 @@ } } -/* Touch-friendly interactive elements */ @media (hover: none) and (pointer: coarse) { button, a, input, textarea, select { min-height: 44px; min-width: 44px; } - - /* Remove hover effects on touch devices */ .hover\:-translate-y-1:hover, .hover\:scale-105:hover { transform: none; } } -/* Print styles */ @media print { - .no-print { - display: none !important; - } - + .no-print { display: none !important; } body { background: white !important; color: black !important; } } -/* Reduced motion */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; @@ -647,21 +687,28 @@ transition-duration: 0.01ms !important; scroll-behavior: auto !important; } + + .animate-text-reveal, + .animate-shimmer, + .animate-slide-up, + .animate-soft-pulse { + animation: none; + opacity: 1; + transform: none; + } } -/* Focus visible for keyboard navigation */ :focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; + border-radius: calc(var(--radius) - 2px); } -/* Prevent layout shift from lazy images */ img { max-width: 100%; height: auto; } -/* Better overflow handling */ .overflow-x-auto { -webkit-overflow-scrolling: touch; } @@ -675,12 +722,12 @@ img { background: transparent; } .blob-scrollbar::-webkit-scrollbar-thumb { - background: #30363d; + background: color-mix(in srgb, currentColor 15%, transparent); border-radius: 5px; - border: 2px solid #0d1117; + border: 2px solid var(--background); } .blob-scrollbar::-webkit-scrollbar-thumb:hover { - background: #424a53; + background: color-mix(in srgb, currentColor 25%, transparent); } /* Shiki code highlighting overrides */ @@ -696,40 +743,4 @@ img { .shiki .line { height: 1.5rem; display: block; -} - -/* Admin HUD styles */ -.glass-panel { - background: rgba(13, 17, 23, 0.3); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.05); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); -} - -.custom-scrollbar::-webkit-scrollbar { - width: 4px; -} - -.custom-scrollbar::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.1); - border-radius: 2px; -} - -/* Base layout styles */ -body { - font-family: "Inter", system-ui, sans-serif; -} - -code, -pre, -.font-mono { - font-family: "JetBrains Mono", monospace; -} - -.text-balance { - text-wrap: balance; -} - -html { - scroll-behavior: smooth; } \ No newline at end of file From 7de42672a3342a2de590c43cbc087f535f75c481 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 21:04:59 +0600 Subject: [PATCH 014/103] fix: resolve CSRF validation error when creating a repository - Added getCsrfToken to new.astro to generate the token and cookie on the server. - Passed the token into a hidden input field. - Extracted the token and included it in the X-CSRF-Token header in the client-side fetch request. --- src/pages/new.astro | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/new.astro b/src/pages/new.astro index 69b00d5..a052c9a 100644 --- a/src/pages/new.astro +++ b/src/pages/new.astro @@ -11,11 +11,15 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { getCsrfToken } from "@/middleware/csrf"; const user = Astro.locals.user; if (!user) { return Astro.redirect("/login"); } + +const { token, cookie } = getCsrfToken(Astro.request); +Astro.response.headers.set("Set-Cookie", cookie); --- @@ -59,6 +63,7 @@ if (!user) {
+ Repository details @@ -326,10 +331,13 @@ if (!user) { includeAllBranches: formData.get("includeAllBranches") === "on", }; + const csrfToken = (document.getElementById("csrf-token") as HTMLInputElement)?.value; + const response = await fetch("/api/repos", { method: "POST", headers: { "Content-Type": "application/json", + "X-CSRF-Token": csrfToken || "", }, body: JSON.stringify(data), }); From 79e3b374d85fa2dd4632f5a9f55321ea9a090d80 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 17 Jul 2026 21:37:09 +0600 Subject: [PATCH 015/103] fix: implement global CSRF fetch interceptor - Extracted CSRF token generation to BaseLayout and AdminLayout. - Injected token via tag. - Added global window.fetch interceptor to automatically append the 'x-csrf-token' header to all state-changing requests (POST, PUT, PATCH, DELETE). - Reverted manual CSRF handling in new.astro as it's now handled globally for all API requests (repo creation, profile updates, etc). --- src/layouts/AdminLayout.astro | 31 +++++++++++++++++++++++++++++++ src/layouts/BaseLayout.astro | 34 +++++++++++++++++++++++++++++++++- src/pages/new.astro | 8 -------- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/layouts/AdminLayout.astro b/src/layouts/AdminLayout.astro index 8448c34..700ced3 100644 --- a/src/layouts/AdminLayout.astro +++ b/src/layouts/AdminLayout.astro @@ -1,12 +1,16 @@ --- import "@/styles/globals.css"; import { ViewTransitions } from "astro:transitions"; +import { getCsrfToken } from "@/middleware/csrf"; interface Props { title: string; } const { title } = Astro.props; + +const { token, cookie } = getCsrfToken(Astro.request); +Astro.response.headers.set("Set-Cookie", cookie); --- @@ -15,6 +19,33 @@ const { title } = Astro.props; {title} + + + diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 4f1d801..06efa95 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -2,6 +2,7 @@ import "@/styles/globals.css"; import Header from "@/components/layout/Header.astro"; import { Toaster } from "@/components/ui/sonner"; +import { getCsrfToken } from "@/middleware/csrf"; interface Props { title?: string; @@ -10,8 +11,11 @@ interface Props { const { title = "OpenCodeHub - Self-hosted Git Platform", - description = "A powerful, self-hosted Git platform with CI/CD, code review, and collaboration features", + description = "A powerful, self-hosted Git Platform with CI/CD, code review, and collaboration features", } = Astro.props; + +const { token, cookie } = getCsrfToken(Astro.request); +Astro.response.headers.set("Set-Cookie", cookie); --- @@ -23,6 +27,34 @@ const { {title} + + + From f94dfa6c0c0516217cd41e9fdfc1cef6645b34ca Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 024/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20Milestone?= =?UTF-8?q?sList.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/milestones/MilestonesList.tsx | 39 ++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/components/milestones/MilestonesList.tsx b/src/components/milestones/MilestonesList.tsx index b60e420..c615fa3 100644 --- a/src/components/milestones/MilestonesList.tsx +++ b/src/components/milestones/MilestonesList.tsx @@ -16,6 +16,7 @@ import { Archive } from "lucide-react"; import { useState } from "react"; +import { toast } from "sonner"; interface MilestoneItem { id: string; @@ -188,15 +189,47 @@ export default function MilestonesList({ milestones, repoOwner, repoName }: Prop className="absolute right-0 top-full mt-1 w-40 rounded-lg border border-border bg-accent shadow-xl z-10" >
- - - From 66c2465c1206a694301d58264c67f22d62dcc53b Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 025/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20NewMilest?= =?UTF-8?q?oneForm.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/milestones/NewMilestoneForm.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/milestones/NewMilestoneForm.tsx b/src/components/milestones/NewMilestoneForm.tsx index 1e84dfd..ee00d56 100644 --- a/src/components/milestones/NewMilestoneForm.tsx +++ b/src/components/milestones/NewMilestoneForm.tsx @@ -9,6 +9,7 @@ import { X } from "lucide-react"; import { useState } from "react"; +import { toast } from "sonner"; interface Props { repoOwner: string; @@ -34,18 +35,18 @@ export default function NewMilestoneForm({ repoOwner, repoName }: Props) { body: JSON.stringify({ title: title.trim(), description: description.trim() || null, - dueOn: dueDate || null, + dueDate: dueDate || null, }), }); if (res.ok) { window.location.href = `/${repoOwner}/${repoName}/milestones`; } else { - alert("Failed to create milestone"); + toast.error("Failed to create milestone"); } } catch (error) { console.error("Error creating milestone:", error); - alert("Failed to create milestone"); + toast.error("Failed to create milestone"); } finally { setIsSubmitting(false); } From c1d0d89814d66b4a8522f8e9514107109ceb52cc Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 026/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20CustomPRS?= =?UTF-8?q?tate.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/pulls/CustomPRState.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pulls/CustomPRState.tsx b/src/components/pulls/CustomPRState.tsx index 6c1cb7f..89dd119 100644 --- a/src/components/pulls/CustomPRState.tsx +++ b/src/components/pulls/CustomPRState.tsx @@ -61,7 +61,7 @@ export function CustomPRState({ initialStateId, customStates, repoOwner, repoNam const res = await fetch(`/api/repos/${repoOwner}/${repoName}/pulls/${prNumber}/state`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ state: selectedState }), + body: JSON.stringify(selectedState.startsWith('custom:') ? { stateId: selectedState.replace('custom:', '') } : { state: selectedState.replace('builtin:', '') }), }); const data = await res.json(); if (!res.ok) { From 36ee24fc15d9ffd7dffe7e53acebf6b621a9b4c1 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 027/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20InlineDif?= =?UTF-8?q?fReview.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/pulls/InlineDiffReview.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/components/pulls/InlineDiffReview.tsx b/src/components/pulls/InlineDiffReview.tsx index bd15402..818dec2 100644 --- a/src/components/pulls/InlineDiffReview.tsx +++ b/src/components/pulls/InlineDiffReview.tsx @@ -55,7 +55,7 @@ export function InlineDiffReview({ const data = await res.json(); const approvalMap: Record = {}; for (const approval of data.approvals || []) { - if (approval.approvedById === currentUser.id) { + if (approval.approvers?.includes(currentUser.username) || approval.approvedById === currentUser.id) { approvalMap[approval.path] = true; } } @@ -90,18 +90,9 @@ export function InlineDiffReview({ }); setFileApprovals(prev => ({ ...prev, [filePath]: true })); } else { - // Delete approval by path (this would need an endpoint to delete by path, - // or we can fetch the approval ID and delete it. - // The file-approvals.ts POST might handle upsert, but we should hit DELETE if unapproving. - // Let's assume there's an API, or we just rely on POST for now to overwrite. - const res = await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/file-approvals`); - const data = await res.json(); - const approval = data.approvals?.find((a: any) => a.path === filePath && a.approvedById === currentUser.id); - if (approval) { - await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/file-approvals/${approval.id}`, { - method: "DELETE" - }); - } + await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/file-approvals?path=${encodeURIComponent(filePath)}`, { + method: "DELETE" + }); setFileApprovals(prev => ({ ...prev, [filePath]: false })); } } catch { From d48f7d3e92512b184173c663f9d6a9dc645033c3 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 028/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20PullReque?= =?UTF-8?q?stsList.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/pulls/PullRequestsList.tsx | 110 +++++++++++----------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/components/pulls/PullRequestsList.tsx b/src/components/pulls/PullRequestsList.tsx index eb644e7..d3ebbf8 100644 --- a/src/components/pulls/PullRequestsList.tsx +++ b/src/components/pulls/PullRequestsList.tsx @@ -17,6 +17,7 @@ import { Loader2 } from "lucide-react"; import { useState } from "react"; +import { toast } from "sonner"; interface PullRequest { id: string; @@ -151,8 +152,10 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, const nodes = payload?.data?.graph?.nodes?.length || 0; const edges = payload?.data?.graph?.edges?.length || 0; setWorkflowMsg(`Dependency graph updated (${nodes} PRs, ${edges} edges).`); + toast.success(`Found ${nodes} PRs with ${edges} dependencies`); } catch (error: any) { setWorkflowMsg(error?.message || "Failed to analyze dependencies"); + toast.error(error?.message || "Failed to analyze dependencies"); } finally { setIsAnalyzing(false); } @@ -216,8 +219,11 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, throw new Error(payload?.error?.message || "Failed to apply stack order"); } setWorkflowMsg(`Created stack ${payload?.data?.stackId}.`); + toast.success("Stack created successfully"); + window.location.reload(); } catch (error: any) { setWorkflowMsg(error?.message || "Failed to apply stack order"); + toast.error(error?.message || "Failed to apply stack order"); } finally { setIsApplying(false); } @@ -249,10 +255,13 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, const failed = payload?.data?.failed?.length || 0; const skipped = payload?.data?.skipped?.length || 0; setWorkflowMsg(`Bulk merge complete: merged ${merged}, failed ${failed}, skipped ${skipped}.`); + if (merged > 0) toast.success(`Merged ${merged} PR(s)`); + if (failed > 0) toast.error(`Failed to merge ${failed} PR(s)`); setSelectedPrIds([]); - window.location.reload(); + setTimeout(() => window.location.reload(), 1000); } catch (error: any) { setWorkflowMsg(error?.message || "Failed to bulk merge selected PRs"); + toast.error(error?.message || "Failed to bulk merge"); } finally { setIsBulkMerging(false); } @@ -260,34 +269,31 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, return (
- {/* Enhanced Header with Glow */} + {/* Header */} - {/* Search Input with Glass Effect */} -
-
-
- - setSearchQuery(e.target.value)} - className="w-full rounded-xl border border-border bg-secondary backdrop-blur-sm pl-10 pr-4 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-green-500/50 focus:ring-2 focus:ring-green-500/20 transition-all" - /> -
+ {/* Search Input */} +
+ + setSearchQuery(e.target.value)} + className="w-full rounded-lg border border-border bg-card pl-10 pr-4 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary/50 focus:ring-1 focus:ring-primary/20 transition-all" + />
{/* Action Buttons */} -
+
Labels @@ -296,16 +302,16 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, href={`/${repoOwner}/${repoName}/milestones`} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} - className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-border bg-secondary text-sm text-muted-foreground hover:bg-secondary/80 hover:border-border/80 transition-all" + className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border bg-card text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-all" > Milestones New Pull Request @@ -313,14 +319,14 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount,
- {/* PRs Container with Glass Effect */} + {/* PRs Container */} -
+ {/* Dependency Workflow */} +

Dependency Workflow

@@ -333,7 +339,7 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, type="button" onClick={analyzeDependencies} disabled={isAnalyzing} - className="inline-flex items-center gap-1 rounded-lg border border-white/15 bg-white/[0.04] px-3 py-1.5 text-xs text-gray-200 hover:bg-white/[0.08] disabled:opacity-60" + className="inline-flex items-center gap-1.5 rounded-lg border border-border bg-card px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50 transition-all" > {isAnalyzing ? : } Analyze Dependencies @@ -341,7 +347,7 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, @@ -349,7 +355,7 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, type="button" onClick={suggestStackOrder} disabled={isSuggesting} - className="inline-flex items-center gap-1 rounded-lg border border-white/15 bg-white/[0.04] px-3 py-1.5 text-xs text-gray-200 hover:bg-white/[0.08] disabled:opacity-60" + className="inline-flex items-center gap-1.5 rounded-lg border border-border bg-card px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50 transition-all" > {isSuggesting ? : } Suggest Order ({selectedPrIds.length}) @@ -358,7 +364,7 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, type="button" onClick={applySuggestedOrder} disabled={isApplying || !suggestion || suggestion.cycles.length > 0} - className="inline-flex items-center gap-1 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 px-3 py-1.5 text-xs font-medium text-foreground disabled:opacity-60" + className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50 transition-all" > {isApplying ? : } Apply As Stack @@ -366,7 +372,7 @@ export default function PullRequestsList({ pullRequests, openCount, closedCount, + className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" + > + {branches.length > 0 ? ( + branches.map(b => ( + + )) + ) : ( + + )} + +

+ The default branch is the base branch for pull requests and code comparisons. +

@@ -241,6 +302,24 @@ export default function RepoSettings({ repo }: RepoSettingsProps) {
+ {/* Sync Default Branch */} + + + Default Branch + + Current default branch: {repo.defaultBranch} + + + +

+ If your default branch doesn't match the actual branch in git, use the sync button to fix it. +

+ +
+
+ Danger Zone From 10e2e75fe8654f6f4056fef82f8715c143555f3d Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 030/103] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20WorkflowS?= =?UTF-8?q?ettings.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repo/settings/WorkflowSettings.tsx | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/components/repo/settings/WorkflowSettings.tsx b/src/components/repo/settings/WorkflowSettings.tsx index 94fb69c..0cb2841 100644 --- a/src/components/repo/settings/WorkflowSettings.tsx +++ b/src/components/repo/settings/WorkflowSettings.tsx @@ -4,7 +4,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { toast } from "sonner"; -import { Loader2, Plus, GripVertical, Trash2 } from "lucide-react"; +import { Loader2, Plus, GripVertical, Trash2, Check, X } from "lucide-react"; import type { PRStateDefinition } from "@/db/schema/pr-states"; @@ -18,6 +18,10 @@ export default function WorkflowSettings({ repositoryId, owner, repo }: Workflow const [states, setStates] = useState([]); const [loading, setLoading] = useState(true); const [newStateName, setNewStateName] = useState(""); + const [newStateDesc, setNewStateDesc] = useState(""); + const [allowMerge, setAllowMerge] = useState(false); + const [requireCodeOwner, setRequireCodeOwner] = useState(false); + const [reviewerUsernames, setReviewerUsernames] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); // Fetch existing states @@ -50,7 +54,11 @@ export default function WorkflowSettings({ repositoryId, owner, repo }: Workflow body: JSON.stringify({ name: newStateName.toLowerCase().replace(/\s+/g, "_"), displayName: newStateName, - color: "#6B7280" // Default color for now, can add picker later + color: "#6B7280", // Default color for now, can add picker later + description: newStateDesc || undefined, + allowMerge, + requireCodeOwner, + reviewers: reviewerUsernames ? reviewerUsernames.split(",").map(u => ({ username: u.trim() })).filter(u => u.username) : undefined }) }); @@ -62,6 +70,10 @@ export default function WorkflowSettings({ repositoryId, owner, repo }: Workflow const newState = await res.json(); setStates([...states, newState]); setNewStateName(""); + setNewStateDesc(""); + setReviewerUsernames(""); + setAllowMerge(false); + setRequireCodeOwner(false); toast.success("State created"); } catch (e) { console.error(e); @@ -114,7 +126,15 @@ export default function WorkflowSettings({ repositoryId, owner, repo }: Workflow

{state.displayName}

-

{state.name}

+

{state.name} {state.description ? `- ${state.description}` : ""}

+
+ + {state.allowMerge ? : } Merge + + + {state.requireCodeOwner ? : } CodeOwner + +
{state.isDefault && Default}
@@ -152,6 +172,36 @@ export default function WorkflowSettings({ repositoryId, owner, repo }: Workflow disabled />
+
+ + setNewStateDesc(e.target.value)} + /> +
+
+ + setReviewerUsernames(e.target.value)} + /> +
+
+ +

If unchecked, pull requests in this state cannot be merged.

+ +

Require approvals from CODEOWNERS before this state allows merging.

+
-
-
- {(commit.sha || sha)?.substring(0, 7)} + +
+ {commitInfo.parentShas && commitInfo.parentShas.length > 0 && ( +
+ {commitInfo.parentShas.length} parent{commitInfo.parentShas.length > 1 ? 's' : ''} + {commitInfo.parentShas.map((parentSha: string) => ( + {parentSha.substring(0, 7)} + ))} +
+ )} +
+ commit + {(commitInfo.sha || sha)?.substring(0, 7)}
-
- {commit.stats?.additions || 0} additions, {commit.stats?.deletions || 0} deletions +
+
+ + +
+
+ {fileStats.length} file{fileStats.length !== 1 ? 's' : ''} changed +
+
+ +{totalAdditions} + -{totalDeletions} +
+ {fileStats.slice(0, 20).map((f: any) => ( +
+ ))}
- -
-

Changed files

- - {commit.files && commit.files.length > 0 ? ( - commit.files.map((file: any) => ( -
-
- {file.path} - - +{file.additions || 0} -{file.deletions || 0} - -
- {file.diff && ( -
-
{file.diff}
+ +
+ +
)} From 2fbd4438055a85402308b0b060f1f020d04f50b0 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 039/103] =?UTF-8?q?=E2=9C=A8=20feat:=20update=20new.astro?= =?UTF-8?q?=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/[owner]/[repo]/issues/new.astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[owner]/[repo]/issues/new.astro b/src/pages/[owner]/[repo]/issues/new.astro index 740df4b..eb90a46 100644 --- a/src/pages/[owner]/[repo]/issues/new.astro +++ b/src/pages/[owner]/[repo]/issues/new.astro @@ -273,7 +273,7 @@ const customFields = await db.query.customFieldDefinitions.findMany({ } // Redirect to the new issue - window.location.href = `/${owner}/${repo}/issues/${result.data.number}`; + window.location.href = `/${owner}/${repo}/issues/${result.number}`; } catch (error) { console.error("Error creating issue:", error); alert(error instanceof Error ? error.message : "An error occurred"); From a940dcaadcbb71347f57ca1b3420113eb898868c Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 040/103] =?UTF-8?q?=E2=9C=A8=20feat:=20update=20[number].a?= =?UTF-8?q?stro=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/[owner]/[repo]/pulls/[number].astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[owner]/[repo]/pulls/[number].astro b/src/pages/[owner]/[repo]/pulls/[number].astro index 06a6a21..2b2596b 100644 --- a/src/pages/[owner]/[repo]/pulls/[number].astro +++ b/src/pages/[owner]/[repo]/pulls/[number].astro @@ -1795,7 +1795,7 @@ try { } loadAutoMergeStatus(); loadPrChecks(); - loadCodeOwnerEnforcement(); + // loadCodeOwnerEnforcement handled by CodeOwnersEnforcement component loadMergeReadiness(); mergeBtn?.addEventListener("click", async () => { From c9745f57c8b2354e537b2c4de093fdc9c62e6851 Mon Sep 17 00:00:00 2001 From: swadhinbiswas Date: Fri, 24 Jul 2026 22:30:16 +0600 Subject: [PATCH 041/103] =?UTF-8?q?=E2=9C=A8=20feat:=20update=20deploy-key?= =?UTF-8?q?s.astro=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[owner]/[repo]/settings/deploy-keys.astro | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/pages/[owner]/[repo]/settings/deploy-keys.astro b/src/pages/[owner]/[repo]/settings/deploy-keys.astro index ff38196..da1d1a5 100644 --- a/src/pages/[owner]/[repo]/settings/deploy-keys.astro +++ b/src/pages/[owner]/[repo]/settings/deploy-keys.astro @@ -225,6 +225,32 @@ try {
+ +
+

About Deploy Keys

+
+

Deploy keys are SSH keys that grant access to a single repository. They're used for:

+
    +
  • CI/CD pipelines to pull code during builds
  • +
  • Deployment servers to pull code for deployment
  • +
  • Automation scripts that need repository access
  • +
+
+

Key differences from SSH keys:

+
    +
  • Deploy keys are tied to a single repository
  • +
  • SSH keys (in Settings) give access to all your repositories
  • +
  • Deploy keys can be read-only or read/write
  • +
+
+
+

To add a deploy key:

+
ssh-keygen -t ed25519 -C "deploy@production-server"
+

Then copy the public key and paste it in the form above.

+
+
+
+