diff --git a/webui2/README.md b/webui2/README.md index 7cc6f4d6649deacd88c521a84a4442b295dd5b62..e31e8aee416588090de11364bd0b9cfa86e0e232 100644 --- a/webui2/README.md +++ b/webui2/README.md @@ -1,6 +1,6 @@ # webui2 -New web interface for git-bug. Built with Vite + React + TypeScript + Tailwind + shadcn/ui. +New web interface for git-bug. Built with Vite 8 + React 19 + TypeScript 6 + Tailwind v4 + shadcn/ui + TanStack Router + Apollo Client 4. ## Quickstart @@ -15,22 +15,24 @@ pnpm install pnpm dev ``` -Open http://localhost:5173. Vite proxies `/graphql`, `/api`, and `/auth` to the Go server on port 3000. +Open http://localhost:5173. Vite proxies `/graphql`, `/gitfile`, `/gitraw`, `/upload`, and `/auth` to the Go server on port 3000. Node 22 is required. If you use asdf, `.tool-versions` pins the right version automatically. ## Routes -| Path | Page | -| ----------------------- | -------------------------------------------------------- | -| `/` | Repo picker — auto-redirects when there is only one repo | -| `/_` | Default repo (issues + code browser) | -| `/_/issues` | Issue list with search and label filtering | -| `/_/issues/new` | New issue form | -| `/_/issues/:id` | Issue detail and timeline | -| `/_/user/:id` | User profile | -| `/_/commit/:hash` | Commit detail with collapsible file diffs | -| `/auth/select-identity` | OAuth identity adoption (first-time login) | +| Path | Page | +| ------------------------------ | ---------------------------------------------- | +| `/` | Repo picker — auto-redirects for single repo | +| `/$repo/tree/$ref/...path` | Code browser — directory listing | +| `/$repo/blob/$ref/...path` | Code browser — file viewer | +| `/$repo/commits/$ref` | Commit history | +| `/$repo/commit/$hash` | Commit detail with collapsible file diffs | +| `/$repo/issues` | Issue list with search, filters, pagination | +| `/$repo/issues/new` | New issue form | +| `/$repo/issues/$id` | Issue detail and timeline | +| `/$repo/user/$id` | User profile with their issues | +| `/auth/select-identity` | OAuth identity adoption (first-time login) | `_` is the URL segment for the default (unnamed) repository. Named repositories use their registered name. @@ -38,59 +40,102 @@ Node 22 is required. If you use asdf, `.tool-versions` pins the right version au ``` src/ -├── pages/ # One file per route +├── routes/ # File-based routing (TanStack Router) +│ ├── __root.tsx # Root layout (Shell + error boundary) +│ ├── index.tsx # Repo picker (/) +│ ├── $repo.tsx # Repo layout — normalizes slug, preloads refs +│ ├── $repo/ +│ │ ├── index.tsx # Redirect to tree/{defaultRef} +│ │ ├── _code.tsx # Code browser layout (breadcrumb, ref selector) +│ │ ├── _code/ # tree/$ref/$, blob/$ref/$, commits/$ref +│ │ ├── _issues.tsx # Issues layout — preloads labels + identities +│ │ ├── _issues/ # issues/, issues/new, issues/$id, user/$id +│ │ └── commit/ # commit/$hash +│ └── auth/ # select-identity ├── components/ -│ ├── bugs/ # Issue components (BugRow, Timeline, ...) -│ ├── code/ # Code browser (FileTree, FileViewer, ...) -│ ├── content/ # Markdown renderer -│ ├── layout/ # Header + Shell -│ └── ui/ # shadcn/ui — never edit manually -│ # Update with: npx shadcn update -├── graphql/ # .graphql source files — edit these, then run codegen -├── __generated__/ # Generated typed hooks — do not edit -└── lib/ # apollo.ts, auth.tsx, theme.tsx, gitApi.ts, repo.tsx, utils.ts +│ ├── bugs/ # Issue components (BugRow, Timeline, ...) +│ ├── code/ # Code browser (FileTree, FileViewer, ...) +│ ├── content/ # Markdown renderer with repo-aware links +│ ├── layout/ # Header + Shell +│ └── ui/ # shadcn/ui + ButtonLink, BackLink +├── graphql/ # .graphql source files — edit these, then run codegen +├── __generated__/ # Generated typed hooks — do not edit +├── assets/ # Logo SVG +├── lib/ # apollo.ts, auth.tsx, theme.tsx, utils.ts +├── routeTree.gen.ts # Auto-generated route tree — do not edit +└── App.tsx # Router instance + context ``` -## Data flow +## Routing -**Bug tracking** uses GraphQL (`/graphql`). Queries and mutations are defined in `src/graphql/*.graphql` and codegen produces typed React hooks into `src/__generated__/graphql.ts`. After changing any `.graphql` file run: +Routes use [TanStack Router](https://tanstack.com/router) with file-based routing and automatic code splitting. The `@tanstack/router-plugin` Vite plugin generates `routeTree.gen.ts` from the `src/routes/` directory. + +Pathless layout routes (`_code.tsx`, `_issues.tsx`) group child routes that share data loading or layout without adding URL segments. + +The router context provides: +- `preloadQuery` — Apollo `createQueryPreloader` for data loading in route loaders +- `ref` — normalized repo slug (null for default repo), set by `$repo.tsx` `beforeLoad` +- `labelsRef`, `identitiesRef` — preloaded shared queries, set by `_issues.tsx` `beforeLoad` + +Custom link components: +- `ButtonLink` — `createLink()`-wrapped anchor with button styling and preload-on-intent +- `BackLink` — uses `router.history.back()` when possible, falls back to a typed Link + +## Data loading + +Data is loaded in route loaders using Apollo's `preloadQuery` + `useReadQuery` pattern: + +```ts +export const Route = createFileRoute("/$repo/issues/$id")({ + loader: async ({ context: { preloadQuery, ref }, params: { id } }) => { + const bugDetailRef = preloadQuery(BugDetailDocument, { + variables: { ref, prefix: id }, + }); + return { bugDetailRef: await preloadQuery.toPromise(bugDetailRef) }; + }, +}); +``` + +The router waits for `toPromise()` before transitioning, then the component reads data with `useReadQuery()`. Cascading queries (e.g. last commits after tree loads) stay as component-level `useQuery`. + +Search params that affect data loading use `loaderDeps` so the loader re-runs when they change (e.g. issue filters, pagination cursors). + +After changing any `.graphql` file, regenerate typed hooks: ```bash pnpm codegen ``` -**Code browser** uses REST endpoints at `/api/repos/{owner}/{repo}/git/*` implemented in `api/http/git_browse_handler.go`. `_` is used for both owner and repo (local single-user setup). The TypeScript client is `src/lib/gitApi.ts`. +## Tooling + +| Tool | Purpose | +| --- | --- | +| [oxlint](https://oxc.rs) | Linter with type-aware rules (replaces ESLint) | +| [oxfmt](https://oxc.rs) | Formatter with import + Tailwind class sorting | +| [valibot](https://valibot.dev) | Runtime validation for search params and fetch responses | +| [@tsconfig/bases](https://github.com/tsconfig/bases) | Shared tsconfig presets (vite-react + strictest) | -| Endpoint | Description | -| ------------------------------------- | --------------------------------------- | -| `GET /git/refs` | List branches and tags | -| `GET /git/trees/{ref}?path=` | Directory listing with last-commit info | -| `GET /git/blobs/{ref}?path=` | File content | -| `GET /git/raw/{ref}/{path}` | Raw file download | -| `GET /git/commits?ref=&limit=&after=` | Paginated commit log | -| `GET /git/commits/{sha}` | Commit metadata + changed file list | -| `GET /git/commits/{sha}/diff?path=` | Per-file structured diff (lazy-loaded) | +```bash +pnpm lint # oxlint (type-aware, 0 warnings target) +pnpm lint:fix # oxlint with auto-fix +pnpm fmt # oxfmt format +pnpm fmt:check # oxfmt check only +pnpm check # lint + format check +``` ## Auth Three modes, configured at server start: - **`local`** — single user derived from git config; all writes enabled, no login UI. -- **`oauth`** — multi-user via external providers; all API endpoints require a valid session; unauthenticated requests get 401. +- **`external`** — multi-user via OAuth providers; unauthenticated requests get 401. - **`readonly`** — no identity; all write actions hidden in the UI. -`AuthContext` (`src/lib/auth.tsx`) fetches `serverConfig` + `userIdentity` on load and exposes `{ user, mode, oauthProviders }` to the whole tree. +`AuthProvider` (`src/lib/auth.tsx`) fetches server config + user identity on load and exposes `{ user, mode, loginProviders }` to the component tree. ## Build for production -The Go binary embeds the compiled frontend via `//go:embed all:dist` in `webui2/handler.go`. The Makefile `build-webui2` target runs the Vite build before compiling Go: - -```bash -# From repo root -make build -``` - -Or manually: +The Go binary embeds the compiled frontend via `//go:embed all:dist` in `webui2/handler.go`: ```bash pnpm build # outputs to webui2/dist/ @@ -99,4 +144,4 @@ cd .. && go build . # embeds dist/ into the binary ## Theming -`ThemeProvider` (`src/lib/theme.tsx`) toggles the `dark` class on ``. CSS variables for both modes are defined in `src/index.css`. shadcn/ui components pick them up automatically. +`ThemeProvider` (`src/lib/theme.tsx`) toggles the `dark` class on ``. CSS variables for both modes are defined in `src/index.css` using Tailwind v4's `@theme inline` block. Components pick them up automatically.