A non-linear tree-based chat interface for any OpenAI-compatible LLM provider.
Conversations are displayed as a tree. Branch at any node, merge paths back together, and explore multiple ideas in parallel without losing context.
| Feature | Description |
|---|---|
| 🌳 Tree-based conversations | Branch from any node and explore multiple directions independently while preserving context |
| 🔀 Branch merging | Merge 2+ nodes in one click, with per-branch context strategies (raw / milestone / condensed) |
| 🗂️ Canvas artifacts | Place text/image artifact nodes on the canvas and inject them into merged prompts |
| 🖼️ Multimodal support | Upload images and automatically detect vision capability (OpenRouter/OpenAI compatible) |
| 📎 File attachments | Parse PDF, Word, Excel, TXT, and more on the client and inject content into prompts |
| ✏️ Edit propagation | Edit any node and automatically regenerate all downstream child nodes |
| 🔍 Focus mode | Double-click a node to enter fullscreen mode with up/down navigation |
| 📖 Linear view | Side panel shows the active path in classic chat bubbles with streaming and collapsible thinking blocks |
| 📍 Milestone navigation | Visual timeline marks branch/merge points for quick jumping |
| 🔌 Provider-agnostic API | Works with OpenRouter, OpenAI, Ollama, LM Studio, and any OpenAI-compatible endpoint |
| 💾 Local-only storage | All data stays in browser localStorage; nothing is uploaded to a server |
| 🔗 Stateless sharing | Entire conversation tree is gzip-compressed and encoded into the URL |
| 🌐 Bilingual UI | Switch between Chinese and English in Settings |
# 1) Clone the repository
git clone https://github.com/yourusername/ChatTree.git
cd ChatTree
# 2) Install dependencies
npm install
# 3) Start development server
npm run devOpen http://localhost:3000, then fill in your API key in Settings.
Create .env at the repository root:
OPENAI_API_KEY=sk-your-key-hereThe key is used through a Next.js API route proxy and is not exposed to the client.
- Open ⚙️ Settings in the sidebar
- Fill in API Key and API URL (leave URL empty to use OpenAI default)
- Click 🔄 Sync to fetch model list
- Enable 🙈 to persist the key in browser storage
⚠️ In direct client mode, your key is sent directly to the provider.
| Service | API URL |
|---|---|
| Ollama | http://localhost:11434/v1 |
| LM Studio | http://localhost:1234/v1 |
| Any OpenAI-compatible service | your endpoint + /v1 |
| Technology | Version | Purpose |
|---|---|---|
| Next.js | 16 | App framework (App Router + Turbopack) |
| React | 19 | UI framework |
| ReactFlow | 11 | Tree/canvas rendering |
| MUI | 7 | Component library |
| pako | 2 | gzip compression for share links |
| react-dnd | 16 | Drag-and-drop ordering |
| markdown-to-jsx | 9 | Markdown rendering (code highlight, LaTeX, Mermaid) |
| KaTeX (CDN) | latest | Math formula rendering |
chattree/
├── src/
│ ├── app/
│ │ ├── api/chat/route.js # Next.js API route (server-side key proxy)
│ │ ├── privacy/page.js # Privacy policy page
│ │ ├── layout.js # Global layout (fonts, metadata)
│ │ └── page.js # Entry page mounting <TreeChat />
│ │
│ ├── components/ # React components (see doc/components.md)
│ │ ├── TreeChat.js # Main app container and state hub
│ │ ├── ChatNode.js # Conversation tree node card
│ │ ├── ArtifactNode.js # Canvas artifact node
│ │ ├── LinearChatView.js # Linear chat view + milestone sidebar
│ │ ├── InputPanel.js # Message input panel (including merge config)
│ │ ├── InfoPanel.js # Left sidebar (conversation list)
│ │ ├── SettingsModal.js # Settings modal
│ │ ├── ModelSelector.js # Model picker (search/favorites)
│ │ ├── MergeEdge.js # Custom ReactFlow merge edge
│ │ ├── FocusModeOverlay.js # Fullscreen focus overlay
│ │ ├── MarkdownContent.js # Markdown rendering (code/LaTeX/Mermaid/SVG)
│ │ └── ... # Other helper components
│ │
│ ├── hooks/ # Custom React hooks (see doc/hooks.md)
│ │ ├── useChatApi.js # Streaming LLM requests (SSE parsing)
│ │ ├── useChatManagement.js # Conversation list CRUD
│ │ ├── useNodeOperations.js # Node CRUD, merge, cascade regeneration
│ │ ├── useFocusMode.js # Focus mode keyboard navigation
│ │ ├── useGroupedChats.js # Grouped conversation canvas
│ │ └── useModels.js # Model list fetch + cache
│ │
│ └── utils/ # Pure utility functions
│ ├── constants.js # Global constants/defaults
│ ├── storage.js # localStorage access (with quota fallback)
│ ├── treeUtils.js # Tree traversal + prompt context building
│ ├── sharing.js # URL share encode/decode (gzip + base64)
│ ├── fileParser.js # Client-side file parsing
│ └── visionModels.js # Vision capability detection
│
├── doc/ # Standalone documentation
│ ├── components.md # Component architecture details
│ ├── hooks.md # Hook reference
│ ├── storage.md # Storage strategy and fallback
│ ├── merge-context.md # Merge context strategy
│ └── sc.png # UI screenshot
│
├── public/ # Static assets
├── next.config.mjs
└── package.json
# Development (Turbopack hot reload)
npm run dev
# Production build
npm run build
# Run production build locally
npm run start
# Lint
npm run lint
# Deploy to GitHub Pages
npm run deployFor more details, see the doc/ directory:
ChatTree is designed with a client-first, privacy-preserving architecture:
- API key isolation and local storage: API keys are stored in a dedicated localStorage key, separate from chat data. If persistence is disabled in Settings, the key is cleared when the page is closed.
- Sandboxed HTML preview: HTML preview uses a sandboxed iframe without
allow-same-origin(sandbox="allow-scripts"), so preview content cannot access parent window localStorage. - Safe SVG rendering: SVG content is rendered via URL-encoded
<img>loading rather thandangerouslySetInnerHTML, preventing script execution. - Strict Mermaid security: Mermaid is configured with strict security mode (
securityLevel: "strict") to block script/HTML injection. - Stateless sharing: Shared trees are encoded locally (gzip + URL-safe base64) with no backend storage requirement.
