cd08c50ca6
Co-authored-by: Y.anbe <yoshitaka.anbe@aot.it.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
121 lines
7.9 KiB
Markdown
121 lines
7.9 KiB
Markdown
---
|
|
title: "Web Development"
|
|
description: "Develop Kandev's Vite/React routes, domain data flow, settings, workbench, state, and responsive UI."
|
|
---
|
|
|
|
# Web Development
|
|
|
|
`apps/web/` is a React 19 single-page application built by Vite. In production, the Go backend serves its output and remains the API, WebSocket, and authorization boundary.
|
|
|
|
## Quick path
|
|
|
|
1. Run `make dev` and use the printed URLs.
|
|
2. Trace the route through `src/spa-routes.tsx` or `src/settings-routes.tsx`.
|
|
3. Put requests in the owning domain client/hook and live updates in the WebSocket handler.
|
|
4. Test loading, error, reconnect, keyboard, desktop, and mobile states before review.
|
|
|
|
## Run the application
|
|
|
|
```bash
|
|
make dev
|
|
```
|
|
|
|
Use the combined development supervisor for feature work. It selects available ports, starts Go and Vite, and makes Go proxy the Vite UI. `make dev-web` starts Vite alone on `localhost:37429`; it does not provide a backend. Do not hardcode the auto-selected combined-development ports.
|
|
|
|
## Trace a route
|
|
|
|
Startup flows through:
|
|
|
|
```text
|
|
src/main.tsx
|
|
-> src/boot-payload.ts
|
|
-> components/state-provider.tsx
|
|
-> src/app-shell.tsx
|
|
-> src/spa-routes.tsx
|
|
```
|
|
|
|
The client first uses `window.__KANDEV_BOOT_PAYLOAD__`, or fetches `/api/v1/app-state`, then hydrates state before rendering route content. Routing is Kandev's own SPA router in `lib/routing/`, not React Router or Next.js. Files such as `app/tasks/[id]/page.tsx` are components; their paths do not create routes.
|
|
|
|
Top-level matching lives in `src/spa-routes.tsx`. Settings use `src/settings-routes.tsx`; task detail enters through `src/task-detail-route.tsx`. Add a route to the explicit table and its tests rather than relying on the filesystem.
|
|
|
|
## Trace domain data
|
|
|
|
- `lib/api/domains/` owns typed HTTP clients.
|
|
- `hooks/domains/` owns feature query/mutation behavior.
|
|
- `lib/state/store.ts`, `lib/state/slices/`, and `lib/state/hydration/` own shared Zustand state.
|
|
- `lib/ws/client.ts`, `lib/ws/router.ts`, and `lib/ws/handlers/` apply live updates.
|
|
- `app/` holds page-level feature components.
|
|
- `components/` holds feature and reusable composition.
|
|
- `apps/packages/ui/`, `types/`, and `theme/` hold cross-workspace packages.
|
|
|
|
Use `@kandev/ui` for shared primitives. Add a state slice only when data is genuinely shared or event-driven; route-local server data does not automatically need global state.
|
|
|
|
The backend remains authoritative. A mutation should use the owning domain client/hook, and state must tolerate duplicates, reconnects, late events, and a route changing during a response. Do not reimplement workflow transitions, executor lifecycle, provider permissions, or Git rules in a component.
|
|
|
|
When a wire field changes, update the Go DTO/event, TypeScript type, HTTP client, hydration, WebSocket handler, and compatibility tests that consume it. Prefer additive changes when released clients can overlap.
|
|
|
|
## Add or change settings
|
|
|
|
Settings pages live under `app/settings/` and `components/settings/`. Route matching is in `src/settings-routes.tsx`. Navigation is assembled under `components/app-sidebar/sections/settings/` and, for general settings, `components/settings/general-nav.ts`.
|
|
|
|
A setting normally needs:
|
|
|
|
1. a backend-owned type, validation, scope, and persistence/secret strategy;
|
|
2. a typed client in `lib/api/domains/` and domain hook;
|
|
3. route and navigation registration;
|
|
4. page/components for loading, empty, invalid, saved, error, and missing-dependency states;
|
|
5. state/hydration only if other screens consume it;
|
|
6. route/unit tests and a Playwright user journey.
|
|
|
|
Preserve workspace, instance, or global scope in every request and link. Make restart requirements, unavailable dependencies, and experimental status visible.
|
|
|
|
## Change the task workbench
|
|
|
|
`/t/:id` resolves through `src/task-detail-route.tsx`, `app/tasks/[id]/kanban-task-shell.tsx`, and `components/task/task-page-content.tsx`. The advanced desktop layout uses `components/task/dockview-desktop-layout.tsx`, `lib/state/dockview-store.ts`, and `lib/state/layout-manager/`. Mobile task layout lives separately under `components/task/mobile/`.
|
|
|
|
Chat, plan, changes, files, terminal, editor/diff, preview, commit, and pull-request panels share task, session, repository, and executor state. Test a changed action with:
|
|
|
|
- no repository and multiple repositories;
|
|
- preparing, running, stopped, failed, and completed sessions;
|
|
- stale or delayed HTTP/WebSocket data;
|
|
- a restored dock layout and a narrow mobile route;
|
|
- absent optional providers or executor capabilities.
|
|
|
|
## Localize user-facing copy
|
|
|
|
Every new user-facing string must go through i18next: `t("namespace:key")` or `<Trans>`, never an inline literal. That covers visible copy, `placeholder`, `aria-label`, `title`, `alt`, toasts, validation and empty-state text, and dialog copy. Externalization of the existing UI is complete, so a hardcoded literal is now a regression rather than unfinished migration.
|
|
|
|
Catalogs live in `apps/web/src/locales/<locale>/<namespace>.json`. English is the source locale and the only hand-authored catalog; it is bundled into the entry chunk, while each other locale is a lazily fetched chunk loaded before that locale activates. Shipped locales are English, European Portuguese, Simplified Chinese, Traditional Chinese (Taiwan), Traditional Chinese (Hong Kong), and Japanese, plus a generated `pseudo` catalog that only development and E2E builds contain. A missing key falls back to English at runtime.
|
|
|
|
Three mistakes cause silent bugs: never translate a string compared with `===`, never call `t()` at module scope, and never pass an English plural ending as a value; use `count` with `_one`/`_other` keys.
|
|
|
|
```bash
|
|
pnpm run i18n:check # gate: plurals, module-scope t(), Trans indices
|
|
pnpm run i18n:ratchet # gate: no new hardcoded literals in changed lines
|
|
pnpm run i18n:parity # advisory report: per-locale missing and extra keys
|
|
pnpm run i18n:sweep <path> # advisory report: copy the jsx-only lint rule cannot see
|
|
```
|
|
|
|
The first two run in pre-commit and CI. The last two are reports that always exit zero and are wired into neither, because real-locale translation is maintained out of band and must not fail an English-only change. A clean lint does not prove a file is done: the rule only inspects JSX literals and skips anything assigned to a SCREAMING_CASE identifier, so review config tables by eye and use the pseudo-locale (**Settings > General > Appearance** in development builds) as the completeness check. Shared `@kandev/ui` primitives cannot import the app's i18n runtime, so their accessibility labels are localized at the call site. Full guide: [`docs/i18n.md`](../i18n.md).
|
|
|
|
## Security, accessibility, and responsiveness
|
|
|
|
Frontend checks improve the experience; they are not authorization. The backend must enforce identity, workspace/task scope, and destructive operations. Treat provider text, repository content, Markdown/HTML, URLs, and agent output as untrusted. Keep raw HTML rendering paired with the existing sanitizer and never interpolate shell commands or credentials in the browser.
|
|
|
|
Use semantic controls and labels, visible focus, complete keyboard interaction, named icon buttons, status text beyond color, and reduced-motion behavior. Verify narrow viewports without overflow or desktop-only hover assumptions.
|
|
|
|
## Test and review
|
|
|
|
```bash
|
|
make test-web
|
|
make typecheck-web
|
|
make lint-web
|
|
make test-e2e
|
|
```
|
|
|
|
Vitest tests are colocated with source. Playwright fixtures and specs are under `apps/web/e2e/`. Repository policy requires every `apps/web/` change to add or update a Playwright scenario; seed isolated test data and never use a developer's normal database or workspace.
|
|
|
|
Before review, confirm loading/empty/error/reconnect states, keyboard names, desktop and mobile layouts, Go/TypeScript wire compatibility, focused unit tests, truthful Playwright coverage, and updated public screenshots or terminology.
|
|
|
|
Related: [Architecture](architecture.md), [Testing](testing.md), and [Developer tools](developer-tools.md).
|