# Contributing to LocalMode First off — thank you. LocalMode is **local-first, privacy-first, offline-first AI for the browser**, and it gets better every time someone files a sharp bug report, tightens a type, writes a test, or ships a new component. This guide explains how to set up the repo, the conventions we hold the line on, and how to get a change merged. New here? Jump to [Your first contribution](#your-first-contribution). --- ## Table of contents - [Code of conduct](#code-of-conduct) - [Ways to contribute](#ways-to-contribute) - [Repository layout](#repository-layout) - [Prerequisites](#prerequisites) - [Getting started](#getting-started) - [The golden rule: read the existing code first](#the-golden-rule-read-the-existing-code-first) - [Development workflow](#development-workflow) - [Architecture rules (non-negotiable)](#architecture-rules-non-negotiable) - [Testing — the most important section](#testing--the-most-important-section) - [Code style](#code-style) - [Changesets & versioning](#changesets--versioning) - [Contributing to `apps/ui` (the UI registry & blocks)](#contributing-to-appsui-the-ui-registry--blocks) - [Contributing to docs](#contributing-to-docs) - [Submitting a pull request](#submitting-a-pull-request) - [Reporting bugs & requesting features](#reporting-bugs--requesting-features) - [Reporting a security issue](#reporting-a-security-issue) - [Your first contribution](#your-first-contribution) - [License](#license) --- ## Code of conduct Be kind, be constructive, assume good faith. We welcome contributors of every background and experience level. Harassment, personal attacks, and dismissive behavior aren't tolerated in issues, pull requests, or discussions. If something crosses that line, contact the maintainers privately (see [Reporting a security issue](#reporting-a-security-issue) for the private channel). --- ## Ways to contribute You don't need to write framework internals to help: - **Report a bug** with a minimal reproduction (see [Reporting bugs](#reporting-bugs--requesting-features)). - **Improve docs** — package `README.md`s, the docs site (`apps/docs`), JSDoc, or this file. - **Add tests** for an under-covered function or edge case. - **Fix a bug** — small, well-tested fixes are the easiest reviews. - **Add a UI component or block** to `apps/ui` (see the dedicated section below). - **Add a model** to a provider catalog, or a provider for an existing core interface. - **Triage** — reproduce open issues, add missing details, suggest labels. If a change is large or changes public API, **open an issue or a discussion first** so we can align on the approach before you invest the time. --- ## Repository layout LocalMode is a **pnpm monorepo**. ``` packages/ core/ Zero-dependency core — all functions, interfaces, VectorDB, agents, RAG, security react/ React hooks for every core function (+ pipeline step factories) ai-sdk/ Vercel AI SDK provider transformers/ HuggingFace Transformers.js provider (ONNX) webllm/ WebLLM provider (WebGPU) wllama/ GGUF provider via llama.cpp WASM litert/ Google LiteRT-LM provider mediapipe/ Google MediaPipe Tasks provider chrome-ai/ Chrome Built-in AI provider (Gemini Nano) langchain/ LangChain.js adapters devtools/ In-app AI observability (hooks) pdfjs/ PDF text extraction dexie/ idb/ localforage/ Storage adapters apps/ ui/ @localmode/ui — the shadcn-style registry + /blocks gallery (localmode.ai) docs/ Documentation site (localmode.dev) ``` --- ## Prerequisites | Tool | Version | Notes | | ---- | ------- | ----- | | **Node.js** | `>= 18` | `20 LTS` or newer recommended | | **pnpm** | `>= 10` | The required package manager — do **not** use npm or yarn to install | | **git** | any recent | | A modern browser (Chrome/Edge for WebGPU, or any modern browser for WASM) is needed to run the `apps/ui` demos and Playwright end-to-end tests. --- ## Getting started ```bash # 1. Fork the repo on GitHub, then clone your fork git clone https://github.com//LocalMode.git cd LocalMode # 2. Install every workspace's dependencies (pnpm links them together) pnpm install # 3. Build all packages once (providers peer-depend on core's build output) pnpm build # 4. Run the test suite to confirm a clean baseline pnpm test pnpm test:types ``` Working on a single package? Filter to it: ```bash pnpm --filter @localmode/core dev # tsup --watch for the core package pnpm --filter @localmode/core test # run just core's tests pnpm --filter ui dev # run the localmode.ai app locally pnpm --filter docs dev # run the localmode.dev docs site locally ``` --- ## The golden rule: read the existing code first **The implemented code is the source of truth. Before writing anything new, find the closest existing implementation and match it exactly** — its structure, naming, error handling, and JSDoc style. A change that looks like it was always part of the codebase is a change that's easy to review and easy to maintain. | You're touching… | Read first | | ---------------- | ---------- | | A core function | `packages/core/src/embeddings/embed.ts`, `classification/classify.ts` | | A core interface | `packages/core/src//types.ts` | | Error handling | `packages/core/src/errors/index.ts`, `errors/format.ts` | | A provider implementation | `packages/transformers/src/implementations/` | | A storage adapter | `packages/dexie/src/storage.ts`, `packages/idb/src/storage.ts` | | A UI primitive | `apps/ui/registry/localmode///` | | A block | `apps/ui/src/app/blocks//` | --- ## Development workflow 1. **Create a branch** off `main`: ```bash git checkout -b fix/embed-abort-signal # or feat/…, docs/…, test/…, chore/… ``` 2. **Make the change**, matching the surrounding code. 3. **Add or update tests** — see the testing section; this is not optional. 4. **Add a changeset** if you changed any published package (see below). 5. **Run the local checks** (the same gates a reviewer will expect green): ```bash pnpm lint # ESLint over packages/** pnpm test # Vitest — packages/**/*.test.ts and *.spec.ts pnpm test:types # tsc type-level contract tests (*.test-d.ts) pnpm typecheck # tsc --noEmit across every workspace pnpm build # ensure every package still builds ``` 6. **Commit** using [Conventional Commits](#commit-messages) and open a PR. ### Handy scripts (run from the repo root) | Command | What it does | | ------- | ------------ | | `pnpm build` | Build every package (`pnpm -r build`) | | `pnpm test` | Run the Vitest suite over `packages/**` | | `pnpm test:watch` | Vitest in watch mode | | `pnpm test:coverage` | Vitest with a v8 coverage report | | `pnpm test:types` | Type-level tests (`tsc --noEmit` on the typetest project) | | `pnpm typecheck` | `tsc --noEmit` across all workspaces | | `pnpm lint` / `pnpm lint:fix` | ESLint over `packages/**` (autofix with `:fix`) | | `pnpm format` | Prettier `--write` over the repo | | `pnpm changeset` | Record a version bump + changelog entry | | `pnpm check:peers` | Verify provider peer-dependency ranges | > Apps carry their own scripts (Next.js presets). Run app-specific checks with `pnpm --filter ui