From c246e68edd5bcf2e09963f63964ae6e5acc75d44 Mon Sep 17 00:00:00 2001
From: Evandro Fonseca Junior <1+evandro@noreply.git.evandro.dev.br>
Date: Sat, 24 Oct 2026 04:13:01 +0000
Subject: [PATCH] docs(readme): padroniza README (hero, badges, quickstart,
arquitetura)
---
README.md | 261 +++++++++++++++++++++++++-----------------------------
1 file changed, 121 insertions(+), 140 deletions(-)
diff --git a/README.md b/README.md
index c33907e..6917817 100644
--- a/README.md
+++ b/README.md
@@ -1,177 +1,158 @@
-# LocalMind
+
-**Chat client for local LLMs served by LM Studio. React Native + Expo app (Android, iOS and web) where every message stays on your own network: no cloud, no API key, no telemetry.**
+
LocalMind
-
-
+
Chat client for local LLMs served by LM Studio, on Android, iOS and web. Every message stays on your network: no cloud.
+
+
Quickstart ·
+
Architecture ·
+
Issues
+
+

-
-
+
+
-## About
+
-Running a model locally in LM Studio is easy; having a decent chat UI for it on the phone is not. LocalMind is that client: it talks straight to the LM Studio local server (the OpenAI-compatible `/v1` endpoints) over your LAN or `localhost`, streams the answer token by token, keeps chat sessions on the device and renders Markdown with code blocks.
+
-It is an alpha project. There is no backend, no account and no server to deploy: the app is only the client, and the model runs on your machine.
+## 📌 The problem
-## How it works
+Running a model locally in LM Studio is easy; having a decent chat UI for it on your phone is not. Hosted chat apps send every message to a cloud service, which defeats the point of running the model yourself.
-```
-Expo app (Android / iOS / web)
- app/chat, app/settings ......... screens (expo-router, file based)
- hooks/useLMStudio .............. sendMessage / regenerate / stop, stream callbacks
- services/lmStudioApi ........... HTTP + SSE client
- store/useAppStore .............. Zustand + AsyncStorage (sessions, settings, theme)
- |
- | GET /v1/models
- | POST /v1/chat/completions (stream: true -> SSE)
- v
- LM Studio local server (default http://localhost:1234)
-```
+LocalMind is only the client. There is no backend, no account and no server to deploy: the model runs on your machine and the app talks to it over your LAN or `localhost`.
-- The connection settings (host, port, optional API key) live in the Zustand store and are persisted with `AsyncStorage`.
-- `services/lmStudioApi.ts` builds the base URL as `http(s)://:/v1` and streams the response: `XMLHttpRequest` progress events on native platforms, `fetch` + `ReadableStream` on web.
-- Each request carries a fixed system prompt (`constants/SystemPrompt.ts`); `temperature` is `0.7` and `max_tokens` is `2048`.
-- Every finished exchange is saved as a chat session (title taken from the first user message), so History can reload it later.
+## ✨ What it does
-## Stack
+- Connects to the LM Studio local server through its OpenAI-compatible `/v1` API.
+- Streams answers token by token and lets you stop generation while it streams.
+- Renders Markdown with code blocks, with a copy button for code.
+- Edits, deletes and regenerates messages in the conversation.
+- Lists the models loaded in LM Studio in a header selector.
+- Saves every finished exchange as a chat session on the device; History reloads or deletes it.
+- Tests the server connection from Settings and shows a Connected / Disconnected badge.
+- Switches between light, dark and system themes.
-| Layer | Choice |
+## 🚀 Quickstart
+
+| Tool | Minimum version |
|---|---|
-| Runtime | Expo SDK 54, React Native 0.81.5, React 19.1 |
-| Routing | expo-router 6 (typed routes experiment on, React Compiler experiment on) |
-| State | Zustand 5 with `persist` + `@react-native-async-storage/async-storage` |
-| Animation / gestures | react-native-reanimated 4, react-native-gesture-handler |
-| Icons | lucide-react-native |
-| Markdown | `@ronradtke/react-native-markdown-display` |
-| Streaming | XMLHttpRequest (native) / fetch ReadableStream (web) |
-| Tooling | TypeScript 5.9 (`strict`), ESLint 9 flat config (`eslint-config-expo`) |
-| Model server | LM Studio local server, OpenAI-compatible `/v1` API |
+| Node.js | `>= 20.19.4` (required by React Native 0.81.5; not declared in `package.json`) |
+| npm | any recent version (`package-lock.json` is committed; `pnpm-lock.yaml` too) |
+| [LM Studio](https://lmstudio.ai) | with at least one model downloaded; local server on port `1234` by default |
+| Run target | [Expo Go](https://expo.dev/go) on a device, an Android/iOS emulator, or a browser |
-## Requirements
-
-- Node.js `>= 20.19.4` (required by React Native 0.81.5; verified here on Node 24.20.0)
-- npm (or pnpm) to install dependencies
-- [LM Studio](https://lmstudio.ai) with a model downloaded, the local server started and **CORS enabled** (Local Server tab, default port `1234`)
-- To run the app: [Expo Go](https://expo.dev/go) on a physical device, an Android/iOS emulator, or a browser for the web target
-
-## Quick start
-
-```bash
-git clone https://github.com/evandrodevbr/LocalMind.git
-cd LocalMind
-npm install
-npx expo start
-```
-
-Then pick a target from the Expo CLI: `w` for web, `a` for Android, `i` for iOS, or scan the QR code with Expo Go.
-
-Start the model side first:
+### 1. Start the model server (LM Studio)
1. Open LM Studio, download a model and go to the **Local Server** tab (the `<->` icon).
-2. Enable **CORS** and keep the port at `1234` (or note whatever you change it to).
+2. Load the model, enable **CORS** and keep the port at `1234` (or note the one you choose).
3. Click **Start Server**.
-Finally, in the app:
-
-1. Open **Settings** (gear icon in the chat header).
-2. Fill **Host IP / Address** and **Port Number**. On a physical device this must be your machine's LAN address (`192.168.x.x`), not `localhost`.
-3. Tap **Test Connection**. The badge turns to *Connected* and the model list becomes available in the chat header selector.
-
-## Usage
-
-| Screen | What it does |
-|---|---|
-| Chat (`/chat`) | Conversation list with Markdown rendering, streaming answers, copy code blocks, edit / delete / regenerate a message, stop generation while it streams, model selector in the header, "new chat" button. |
-| History (`/chat/history`) | Saved sessions, most recent first, with message count and date; tap to reload, trash icon to delete. |
-| Settings (`/settings`) | Host, port and optional API key, connection test and status badge, light/dark toggle, app info. |
-
-## API
-
-The app is a client of the LM Studio OpenAI-compatible API. Endpoints actually used:
-
-| Call | Where | Notes |
-|---|---|---|
-| `GET /v1/models` | `testConnection()`, `getModels()` | The connection test aborts after 5 s; the model list feeds the header selector. |
-| `POST /v1/chat/completions` | `chatCompletion()`, `chatCompletionStream()` | Streaming (`stream: true`) is the path used by the chat; SSE lines with `choices[0].delta.content` are appended to the last message. |
-
-- Base URL: `http://:/v1` (`http://` is prepended when the host has no scheme).
-- An `Authorization: Bearer ` header is sent only when an API key is filled in.
-- `GET /v1/models` responds with the OpenAI shape `{ "data": [ { "id": ... } ] }`.
-
-## Web target
-
-The same codebase exports a static web build:
+### 2. Run the app
```bash
-npx expo export --platform web # writes dist/
-npx serve dist # or any static server that maps /chat -> chat/index.html
+git clone https://git.evandro.dev.br/evandro/LocalMind.git
+cd LocalMind
+npm install
+npm run start # Expo CLI: press w (web), a (Android), i (iOS) or scan the QR with Expo Go
+# or go straight to a target:
+npm run android
+npm run ios
+npm run web
```
-The export generates `/`, `/chat`, `/chat/history`, `/settings`, `/_sitemap` and `/+not-found`. Use a static server that resolves extension-less URLs (`npx serve`, nginx `try_files $uri $uri.html $uri/ =404`); point unknown paths at `+not-found.html` for the app's 404 screen.
+### 3. Point the app at LM Studio
-## Project structure
+1. Open **Settings** (gear icon in the chat header).
+2. Fill **Host IP / Address** and **Port Number**. On a physical device the host must be your machine's LAN address (`192.168.x.x`), not `localhost`.
+3. Tap **Test Connection**. The badge turns to *Connected* and the loaded models appear in the header selector.
-```
-app/
-├── _layout.tsx root stack, theme provider, splash screen
-├── index.tsx redirects to /chat
-├── +not-found.tsx 404 screen
-├── chat/
-│ ├── _layout.tsx chat header (model selector, history, new chat, settings)
-│ ├── index.tsx conversation screen, prompt suggestions, edit/regenerate
-│ └── history.tsx saved sessions
-└── settings/index.tsx server connection, appearance, about
-components/
-├── chat/ ChatInput, MessageBubble, MessageActions, ModelSelector, TypingIndicator
-└── ui/ Button, GlassCard, ConnectionBadge, ScreenWrapper, icons
-constants/ Colors, theme, SystemPrompt (system prompt sent to the model)
-hooks/ useLMStudio (chat flow), useThemeColor, useColorScheme
-services/ lmStudioApi (HTTP/SSE), types
-store/ useAppStore (Zustand + AsyncStorage)
-docs/ LM Studio API notes and design reference
-.agent/ AI development tooling (agents, skills, rules), not app code
-```
+Until you do this the chat shows *Server offline*: the app does not connect on launch.
-## Verification
+### Network configuration
-There is no automated test suite in this repository. What exists and was actually run on Linux (Node 24.20.0, npm 11.19.0):
+The app has no `.env.example` and reads no environment variables. The server address is a runtime setting stored on the device.
-| Command | Result |
+| Setting | Required | Description |
+|---|---|---|
+| Host IP / Address | Yes | Default `localhost` (`DEFAULT_SERVER_CONFIG` in `services/types.ts`). `http://` is prepended when the value does not start with `http`. |
+| Port Number | Yes | Default `1234`, the LM Studio default. |
+| API Key (optional) | No | Empty by default. Sent as `Authorization: Bearer ` only when filled in. |
+
+The resulting base URL is `http(s)://:/v1`. Defaults for a local LM Studio: `http://localhost:1234/v1`.
+
+Other configuration lives in code, not in the UI:
+
+| Where | What |
|---|---|
-| `npm install` | exit 0 |
-| `npx tsc --noEmit` | exit 0, no type errors |
-| `npx expo lint` | exit 0, 0 errors and 3 warnings (`no-require-imports` twice and one `exhaustive-deps` in `app/chat/index.tsx`) |
-| `npx expo export --platform web` | exit 0, 6 static routes in `dist/` |
-| `npx expo start --web` | dev server serves the app on `http://localhost:8083`, no browser errors |
+| `services/lmStudioApi.ts` | `temperature: 0.7`, `max_tokens: 2048`; the connection test aborts after 5 s |
+| `constants/SystemPrompt.ts` | Fixed system prompt sent with every request |
+| `app.json` | App name `LocalAiApp`, scheme `localaiapp`, `newArchEnabled`, `web.output: static`, `typedRoutes` and `reactCompiler` experiments |
+| `store/useAppStore.ts` | Persisted under the `localaiapp-storage` key: theme, server config, chat sessions, current messages |
-End-to-end run of the exported web build in headless Chromium, against a local mock of the LM Studio `/v1` API:
+## 🧱 Architecture
-- `GET /v1/models` and `POST /v1/chat/completions` (SSE) reached the app: Test Connection returned *Connection successful*, and the header listed the mock model;
-- a message sent from the chat was answered by the mock and rendered incrementally: `Ola! Sou um modelo local rodando via mock do LM Studio. Teste E2E concluido com sucesso.`;
-- the session was persisted (`localStorage` key `localaiapp-storage`) and showed up in History with `2 msgs`;
-- zero uncaught page errors after the `import.meta` fix described below.
+```mermaid
+flowchart LR
+ Screens["Screens: app/chat, app/settings"] --> Hook["hooks/useLMStudio"]
+ Hook --> Api["services/lmStudioApi"]
+ Hook --> Store["store/useAppStore"]
+ Store --> Storage[("AsyncStorage")]
+ Api -->|"GET /v1/models, POST /v1/chat/completions"| LM["LM Studio local server"]
+```
-Not verified in this environment: Android and iOS builds (no emulator or device available), and therefore the native streaming path via `XMLHttpRequest`.
+- Screens are file-based routes (`expo-router`). `useLMStudio` exposes send, regenerate, stop and model fetching.
+- `lmStudioApi` builds the base URL and streams the response: `XMLHttpRequest` progress events on native, `fetch` with `ReadableStream` on web.
+- Streaming uses `stream: true`; SSE lines with `choices[0].delta.content` are appended to the last message.
+- `GET /v1/models` feeds the model selector and the connection test.
+- The Zustand store is persisted with `AsyncStorage`; a finished exchange becomes a session titled from the first user message.
-## Current state and limitations
+## 🗂️ Structure
-- Alpha. It was built against models served by LM Studio and has no fallback to any cloud API.
-- No auto-connect on launch: the app only checks the server from Settings or when the model selector is refreshed. Until then the chat shows *Server offline*.
-- `activeModelId` and `availableModels` are not persisted, so after restarting the app you have to select a model again.
-- `constants/Models.ts` (a Qwen model catalog) is not referenced anywhere in the app.
-- `react-native-sse` is declared in `package.json` but no code imports it.
+```
+app/ expo-router screens: chat/, chat/history, settings/, +not-found
+components/ chat/ (ChatInput, MessageBubble, ModelSelector...) and ui/
+constants/ Colors, theme, SystemPrompt, Models (unused)
+hooks/ useLMStudio, useThemeColor, useColorScheme
+services/ lmStudioApi (HTTP + SSE), types (ServerConfig, defaults)
+store/ useAppStore (Zustand + AsyncStorage)
+docs/ LM Studio API notes, design system reference
+.agent/ AI development tooling, not app code
+```
+
+## 🧪 Scripts
+
+| Command | What it does |
+|---|---|
+| `npm run start` | `expo start`: Expo dev server, pick a target from the CLI |
+| `npm run android` | `expo start --android` |
+| `npm run ios` | `expo start --ios` |
+| `npm run web` | `expo start --web` |
+| `npm run lint` | `expo lint` (ESLint 9 flat config, `eslint-config-expo`) |
+| `npm run reset-project` | `node ./scripts/reset-project.js`: Expo template script that moves or deletes `app/`, `components/`, `hooks/`, `constants/` and `scripts/`. **Do not run it on this project.** |
+
+Not scripts, but used by this project: `npx tsc --noEmit` (strict TypeScript check) and `npx expo export --platform web` (static build into `dist/`; serve it with a static server that resolves extension-less URLs, such as `npx serve dist`).
+
+## 📚 Documentation
+
+- [`docs/Documentação de Implementaçoes Futuras.md`](docs/Documenta%C3%A7%C3%A3o%20de%20Implementa%C3%A7oes%20Futuras.md): notes on the LM Studio API (`/api/v0`, `/v1`, SDKs, CLI) and ideas, in Portuguese.
+- [`docs/evandro-design-system.html`](docs/evandro-design-system.html): visual reference of the design system used in the UI.
+
+## 🛣️ Limitations and next steps
+
+- Alpha. Built against models served by LM Studio; no fallback to any cloud API.
+- No auto-connect on launch: the server is only checked from Settings or when the model selector refreshes.
+- `activeModelId` and `availableModels` are not persisted, so you select a model again after a restart.
+- `constants/Models.ts` (a Qwen model catalog) and the `react-native-sse` dependency are not used by any code.
+- `app.json` and the storage key still carry the previous project name (`LocalAiApp`, `localaiapp-storage`).
- Both `package-lock.json` and `pnpm-lock.yaml` are committed.
-- `app.json` still carries the previous project name (`LocalAiApp` / slug `localaiapp`) and the persisted storage key is `localaiapp-storage`.
-- No CI, no tests, no license file.
+- No tests and no CI. The previous README reported `npx tsc --noEmit` clean and `npx expo lint` with 0 errors and 3 warnings; Android and iOS builds, and the native `XMLHttpRequest` streaming path, were never verified.
-## Documentation
+## 🤝 Contributing
-| Document | Content |
-|---|---|
-| [`docs/Documentação de Implementaçoes Futuras.md`](docs/Documenta%C3%A7%C3%A3o%20de%20Implementa%C3%A7oes%20Futuras.md) | Notes on the LM Studio API (`/api/v0`, `/v1`, SDKs, CLI) and roadmap ideas (in Portuguese) |
-| [`docs/evandro-design-system.html`](docs/evandro-design-system.html) | Visual reference of the design system used in the UI |
+There is no test suite. Before opening a PR, make sure `npm run lint` and `npx tsc --noEmit` pass, and open issues on the [issue tracker](https://git.evandro.dev.br/evandro/LocalMind/issues).
-## License
+## 📄 License
-No license file is present in this repository. Without an explicit license the code is "all rights reserved" by default, so adding one (MIT, for example) is a pending decision of the project owner.
+No license defined — all rights reserved.