> ## Documentation Index
> Fetch the complete documentation index at: https://songbird.studiocollective.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# daw.club — Gitea Frontend

# daw\.club — Gitea Frontend

Custom React frontend for browsing and managing Songbird projects hosted on the [Gitea](https://about.gitea.com/) instance at [git.daw.club](https://git.daw.club).

**Live**: [https://daw.club](https://daw.club) (Cloudflare Pages)
**API backend**: [https://git.daw.club](https://git.daw.club) (Gitea 1.22.6 on GCE)

## Stack

* **Vite** + **React 19** + **TypeScript**
* **Tailwind CSS v4** with `@tailwindcss/vite` plugin
* **Design tokens**: Zinc (dark) + Emerald (accent) palette, Inter font — matches Songbird's design system
* **shadcn/ui-style** components (Button, Card, Input) using `class-variance-authority`
* **Lucide React** for icons
* **React Router v7** for client-side routing

## Pages

| Route           | Page         | Description                                                  |
| --------------- | ------------ | ------------------------------------------------------------ |
| `/`             | Explore      | Search and browse projects in a card grid                    |
| `/login`        | Login        | Token-based auth against Gitea API                           |
| `/register`     | Register     | Redirects to Gitea's native sign-up page                     |
| `/:owner/:name` | Repo Detail  | File browser, edit name/description, clone URL, ZIP download |
| `/:owner`       | User Profile | List of a user's repositories                                |

## Quick Start

```bash theme={null}
cd server/gitea
npm install
npm run dev
```

The dev server starts at `http://localhost:5173`. By default it talks to `https://git.daw.club`.

### Environment Variables

Copy `.env.example` to `.env` to configure:

```bash theme={null}
# Gitea instance URL (without trailing slash)
VITE_GITEA_URL=https://git.daw.club
```

### Build

```bash theme={null}
npm run build     # TypeScript check + Vite production build -> dist/
npm run preview   # Preview the production build locally
npm run lint      # ESLint
```

Production build outputs to `dist/` (\~285 KB JS, \~90 KB gzipped).

## Deploy to Cloudflare Pages

The frontend is deployed as a static site on [Cloudflare Pages](https://pages.cloudflare.com/).

### Prerequisites

* [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/): `npm install -g wrangler`
* Cloudflare API token with **Cloudflare Pages: Edit** + **Zone DNS: Edit** permissions
* Set environment variables:
  ```bash theme={null}
  export CLOUDFLARE_API_TOKEN="<your-token>"
  export CLOUDFLARE_ACCOUNT_ID="8a0ff27d0ad9b0aa3e9ade38ad6be106"
  ```

### Deploy

```bash theme={null}
cd server/gitea
npm run build
wrangler pages deploy dist --project-name daw-club --branch main
```

The site is available at:

* **Custom domain**: [https://daw.club](https://daw.club)
* **Pages.dev fallback**: [https://daw-club.pages.dev](https://daw-club.pages.dev)

### First-time setup (already done)

If setting up from scratch:

```bash theme={null}
# Create the Pages project
wrangler pages project create daw-club --production-branch main

# Add custom domain via Cloudflare API
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/daw-club/domains" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "daw.club"}'

# Create CNAME DNS record: daw.club -> daw-club.pages.dev
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"CNAME","name":"@","content":"daw-club.pages.dev","proxied":true,"ttl":1}'
```

## API Layer

The API client lives in `src/lib/api.ts`. It wraps the [Gitea REST API](https://gitea.com/api/swagger) and handles:

* **Auth**: Token creation via HTTP Basic Auth (`POST /users/:username/tokens`), stored in `localStorage`
* **Repos**: Search, get, update, delete
* **Contents**: File/directory listing, archive download (fetch-based, no token in URL)
* **Users**: Get user profile, list user repos

### Auth flow

1. User enters username/password on `/login`
2. Frontend creates an API token via Gitea's basic auth endpoint
3. Token stored in `localStorage` as `gitea_token`
4. All subsequent API calls include `Authorization: token <sha1>` header
5. Registration redirects to Gitea's native sign-up page at `git.daw.club/user/sign_up`

### CORS

Gitea must have CORS enabled in `/etc/gitea/app.ini` on the server:

```ini theme={null}
[cors]
ENABLED = true
ALLOW_DOMAIN = *
ALLOW_SUBDOMAIN = true
METHODS = GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
```

## Project Structure

```
server/gitea/
├── src/
│   ├── App.tsx              # Router + Layout wrapper
│   ├── main.tsx             # Entry point
│   ├── index.css            # Tailwind v4 theme (HSL color tokens)
│   ├── components/
│   │   ├── Layout.tsx       # Header, footer, search bar
│   │   ├── Button.tsx       # CVA button variants
│   │   ├── Card.tsx         # Card component
│   │   └── Input.tsx        # Input component
│   ├── pages/
│   │   ├── Explore.tsx      # Project search + card grid
│   │   ├── Login.tsx        # Login form
│   │   ├── Register.tsx     # Registration redirect
│   │   ├── RepoDetail.tsx   # File browser + repo management
│   │   └── UserProfile.tsx  # User's repo list
│   └── lib/
│       ├── api.ts           # Gitea REST API client
│       └── utils.ts         # cn() utility (clsx + tailwind-merge)
├── index.html               # Entry HTML (Inter font, dark body fallback)
├── vite.config.ts           # Vite + React + Tailwind v4
├── .env.example             # Environment variable template
└── dist/                    # Production build output
```

## Design Notes

* **Dark theme**: Uses `color-scheme: dark` on `:root` plus `!important` on `html/body/#root` background to override Tailwind v4's base layer cascade. Inline `style` on `<body>` in `index.html` as an additional fallback.
* **Inter font**: Loaded from Google Fonts CDN. Falls back to `system-ui` if unavailable.
* **Auth state**: Lives in module-level variables + `localStorage`, not React state. UI reactivity depends on navigation re-renders after login/logout.
* **No automated tests**: All behavior is verified via manual E2E testing against the live Gitea instance.
