- Turborepo monorepo:
apps/desktop(Tauri v2 + SolidStart),apps/web(Next.js),apps/cli(Rust CLI).packages/*shared libs (e.g.,database,ui,ui-solid,utils,web-*).crates/*Rust media/recording/rendering/camera crates.scripts/*,infra/, andpackages/local-docker/for tooling and local services.
- Install:
pnpm install; setup:pnpm env-setupthenpnpm cap-setup. - Dev:
pnpm dev(web+desktop). Desktop only:pnpm dev:desktop. Web only:pnpm dev:weborcd apps/web && pnpm dev. - Build:
pnpm build(Turbo). Desktop release:pnpm tauri:build. - DB:
pnpm db:generate→pnpm db:push→pnpm db:studio. - Docker:
pnpm docker:up | docker:stop | docker:clean. - Quality:
pnpm lint,pnpm format,pnpm typecheck. Rust:cargo build -p <crate>,cargo test -p <crate>.
- TypeScript: 2‑space indent; Biome formats/lints (
pnpm format). - Rust:
rustfmt+ workspace clippy lints. - Naming: files kebab‑case (
user-menu.tsx); components PascalCase; Rust modules snake_case, crates kebab‑case. - Runtime: Node 20, pnpm 10.x, Rust 1.88+, Docker for MySQL/MinIO.
- NO COMMENTS: Never add comments to code (
//,/* */,///,//!,#, etc.). Code must be self-explanatory through naming, types, and structure. This applies to all languages (TypeScript, Rust, JavaScript, etc.).
All Rust code must respect these workspace-level lints defined in Cargo.toml:
Rust compiler lints:
unused_must_use = "deny"— Always handleResult/Optionor types marked#[must_use]; never ignore them.
Clippy lints (all denied):
dbg_macro— Never usedbg!()in code; use proper logging instead.let_underscore_future— Never writelet _ = async_fn()which silently drops futures; await or explicitly handle them.unchecked_duration_subtraction— Usesaturating_subinstead of-forDurationto avoid panics.collapsible_if— Merge nestedifstatements: useif a && b { }instead ofif a { if b { } }.clone_on_copy— Don't call.clone()onCopytypes; just copy them directly.redundant_closure— Use function references directly:iter.map(foo)instead ofiter.map(|x| foo(x)).ptr_arg— Accept&[T]or&strinstead of&Vec<T>or&Stringin function parameters.len_zero— Use.is_empty()instead of.len() == 0or.len() > 0.let_unit_value— Don't assign()to a variable: writefoo();instead oflet _ = foo();when return is unit.unnecessary_lazy_evaluations— Use.unwrap_or(val)instead of.unwrap_or_else(|| val)for cheap values.needless_range_loop— Usefor item in &collectioninstead offor i in 0..collection.len()when index isn't needed.manual_clamp— Use.clamp(min, max)instead of manualifchains or.min().max()patterns.
- TS/JS: Vitest where present (e.g., desktop). Name tests
*.test.ts(x)near sources. - Rust:
cargo testper crate; tests insrcortests. - Prefer unit tests for logic and light smoke tests for flows; no strict coverage yet.
- Conventional style:
feat:,fix:,chore:,improve:,refactor:,docs:(e.g.,fix: hide watermark for pro users). - PRs: clear description, linked issues, screenshots/GIFs for UI, env/migration notes. Keep scope tight and update docs when behavior changes.
- Do not start extra servers; use
pnpm dev:weborpnpm dev:desktopas needed. - Never edit auto‑generated files:
**/tauri.ts,**/queries.ts,apps/desktop/src-tauri/gen/**. - Prefer existing scripts and Turbo filters over ad‑hoc commands; clear
.turboonly when necessary. - Database flow: always
db:generate→db:pushbefore relying on new schema. - Keep secrets out of VCS; configure via
.envfrompnpm env-setup. - macOS note: desktop permissions (screen/mic) apply to the terminal running
pnpm dev:desktop. - CRITICAL: NO CODE COMMENTS: Never add any form of comments (
//,/* */,///,//!,#, etc.) to generated or edited code. Code must be self-explanatory.
- Next.js API routes in
apps/web/app/api/*are built with@effect/platform'sHttpApibuilder; copy the existing class/group/endpoint pattern instead of ad-hoc handlers. - Acquire backend services (e.g.,
Videos,S3Buckets) insideEffect.genblocks and wire them throughLayer.provide/HttpApiBuilder.group, translating domain errors toHttpApiErrorvariants. - Convert the effectful API to a Next.js handler with
apiToHandler(ApiLive)from@/lib/serverand export the returnedhandler—avoid callingrunPromiseinside route files. - On the server, run effects through
EffectRuntime.runPromisefrom@/lib/server, typically afterprovideOptionalAuth, so cookies and per-request context are attached automatically. - On the client, use
useEffectQuery/useEffectMutationfrom@/lib/EffectRuntime; they already bind the managed runtime and tracing so you shouldn't callEffectRuntime.run*directly in components.
- Always format code before completing work: run
pnpm formatfor TypeScript/JavaScript andcargo fmtfor Rust. - Run these commands regularly during development and always at the end of a coding session to ensure consistent formatting.