From 808c3fc9c7b7a4fd588f22f16b330448c1bdf49b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 13:14:48 +0000 Subject: [PATCH] docs(claude): describe the SvelteKit frontend The frontend was rewritten in SvelteKit (Svelte 5 + TypeScript, Vite) and merged to main, but CLAUDE.md still described it as vanilla JS/CSS with Biome and a jsconfig tsc check. Update the guidance to match: - Architecture: /frontend is the SvelteKit SPA; /static is the retained legacy vanilla frontend. - New "Frontend Build & Dev Commands" (npm / fe-* recipes) and a "Frontend Architecture (frontend/src/)" map (routes, lib/api, stores, composables, i18n, icons, styles, static vendored assets). - Conventions rewritten for Svelte 5 runes + TypeScript (no `any`), API via lib/api/endpoints, scoped component CSS with var(--*) tokens, dark mode via data-color-scheme. - Pre-commit is now `npm run check` (svelte-check + ESLint + Stylelint + Prettier) + Vitest, replacing Biome/jsconfig. - "What Claude must NOT do" updated (no `any`, don't hand-edit package-lock.json, prefer vendoring heavy deps, SvelteKit is the framework). --- CLAUDE.md | 109 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index dce83b8a..a687c221 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co This project is split into two parts: - `/src` — OxiCloud Backend server in **Rust** -- `/static` — Oxicloud Frontend in **vanilla CSS & vanilla JavaScript** +- `/frontend` — OxiCloud Frontend: a **SvelteKit (Svelte 5) + TypeScript** single-page app built with Vite + +> The original vanilla-JS/CSS frontend still lives in `/static` and is retained +> during the migration, but new frontend work goes in `/frontend`. Vite builds +> the SvelteKit app to `static-dist/`, which the Rust web layer serves in +> release. # Backend part @@ -159,63 +164,81 @@ Canonical examples to mirror: `authz.denied` in `application/ports/authorization # Frontend part +The frontend is a **SvelteKit** single-page app (Svelte 5 + TypeScript, Vite, +`adapter-static`) under `frontend/`. Vite builds it to `static-dist/`, which the +Rust web layer serves in release (unmatched client routes fall back to the SPA +shell); `PROFILE=dev` serves the unbuilt source. The legacy vanilla frontend in +`static/` is retained for now but is **not** where new work goes. + +## Frontend Build & Dev Commands + +Run from `frontend/` (or via the `fe-*` justfile recipes from the repo root): + +```bash +npm ci # install deps (just fe-install) +npm run dev # Vite dev server + HMR (just fe-dev) — backend must run on :8086 +npm run build # build the SPA → static-dist/ (just fe-build) +npm run check # svelte-check + ESLint + Stylelint + Prettier (just fe-check) +npm run test:unit # Vitest (just fe-test) +npm run format # prettier --write . +``` + +`just dev` runs the backend and the Vite dev server together. CI uses **Node 24**; Node 22+ works locally. + +## Frontend Architecture (`frontend/src/`) + +- `routes/` — SvelteKit pages (`+page.svelte`, `+layout.svelte`), one folder per route (`files/[...path]`, `photos`, `shared`, `trash`, `admin`, `s/[token]`, …). +- `lib/components/` — reusable Svelte components (`AppShell`, `PhotoLightbox`, `ShareDialog`, `Modal`, …). +- `lib/api/` — HTTP layer: `client.ts` (`apiFetch`/`apiJson`), `csrf.ts` (`getCsrfHeaders`), `types.ts` (API DTO types — map the backend here), and `endpoints/*.ts` (one module per area: files, folders, photos, people, grants, …). +- `lib/stores/` — global reactive state as `*.svelte.ts` rune stores (`session`, `ui`, `theme`, `dialogs`). +- `lib/composables/` — reusable rune logic (`useSelection`, `useOwnerCache`). +- `lib/i18n/` — bespoke reactive i18n; `t(key, [params], fallback)` reads `frontend/static/locales/*.json` (16 locales) with `{{param}}` interpolation and an English fallback. +- `lib/icons/` — `Icon.svelte` + a generated Font Awesome `registry.ts`. +- `lib/utils/`, `lib/vendor/` — shared helpers and minimal typings/loaders for vendored libs. +- `lib/styles/` — global CSS (`app.css`, `base/`, `ported/`). +- `static/` — served at the web root: `locales/`, `vendors/` (maplibre-gl, pmtiles, hash-wasm), `workers/` (deltaWorker), optional `basemaps/`. + ## Code conventions -### Javascript +### Svelte / TypeScript -- ES Modules (import/export), no CommonJS -- No frameworks — vanilla JS only -- Naming: `camelCase` for variables/functions, `PascalCase` for classes -- No `var` — use `const`/`let` only -- **JSDoc required** on all public functions — `jsconfig.json` enables `checkJs` globally (equivalent to `@ts-check` on every file) -- Always us static/js/core/types.js to mapp OxCcloud API structure -- Type parameters, return types, and complex types via `@typedef`: - -```js -/** - * @typedef {Object} User - * @property {number} id - * @property {string} name - */ - -/** - * @param {User} user - * @param {string} [role="viewer"] - * @returns {Promise} - */ -async function updateUser(user, role = 'viewer') { … } -``` +- **Svelte 5 runes** — `$state`, `$derived`, `$props`, `$effect`, `$bindable`. No legacy `export let` for new components. +- **TypeScript everywhere** (`lang="ts"` in components). **No `any`** — `typescript-eslint` recommended is enforced; prefer precise types, `unknown` + narrowing, or a minimal declared interface for an untyped global (see `lib/vendor/maplibre.ts`). +- ES Modules; `camelCase` for variables/functions, `PascalCase` for components/classes; `const`/`let`, never `var`. +- API DTO shapes live in `lib/api/types.ts`; call the backend through `lib/api/endpoints/*` — don't bare-`fetch` `/api` from components. ### Code duplication -Never duplicate logic across JS modules. If the same behaviour is needed in more than one place, extract it into a shared utility function and import it. Preferred homes: -- DOM/UI helpers → `static/js/utils/` or an existing utility module -- API call wrappers → the relevant API module (e.g. `api/files.js`) -- Event or state patterns shared across components → a dedicated shared module +Never duplicate logic across modules/components. Extract shared behaviour: +- DOM/UI helpers → `lib/utils/` +- API wrappers → the relevant `lib/api/endpoints/*` module +- Cross-component state/logic → a `lib/stores/*.svelte.ts` store or a `lib/composables/*` +- Shared markup → a component (e.g. `PhotoLightbox` is shared by the photos grid, People and Places) ### CSS -- BEM methodology for class names (`.block__element--modifier`) -- CSS custom properties in `:root` for colors and spacing -- **All colors must use `var(--*)` — no raw hex, rgb, or named colors anywhere except in `:root` declarations** -- Mobile-first: media queries expand, they don't restrict -- One CSS file per logical component in `/static/css/` -- [data-theme="dark"] is permitted only in /static/css/themes/dark.css + +- BEM methodology for class names (`.block__element--modifier`). +- Component styles live in the component's scoped `