Skip to content

Commit 411211e

Browse files
committed
feat: update and release. Dockerize: multi-stage build, compose, publish); add db-backed model registry and cached OpenRouter catalog/search (enable/disable/default, strict chat model enforcement, better upstream error surfacing, DB-connect retries) and serve the built client from Express; revamp client UX with models page, per-node model menus + send/retry-all, settings modal (system prompt), theme toggle, markdown rendering/icons, sidebar history + editable summaries, plus refreshed styles/fonts/metadata and updated ignores.
1 parent 39d64b4 commit 411211e

39 files changed

+4724
-624
lines changed

.dockerignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.git
2+
.DS_Store
3+
4+
**/node_modules
5+
**/dist
6+
**/.env
7+
**/*.tsbuildinfo
8+
9+
client/.vite
10+
client/.turbo
11+
server/.turbo
12+
13+
npm-debug.log*
14+
yarn-debug.log*
15+
yarn-error.log*
16+
pnpm-debug.log*
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Publish Docker image
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
tags: ["v*"]
7+
workflow_dispatch: {}
8+
9+
permissions:
10+
contents: read
11+
packages: write
12+
13+
jobs:
14+
build-and-push:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Docker Buildx
21+
uses: docker/setup-buildx-action@v3
22+
23+
- name: Log in to GHCR
24+
uses: docker/login-action@v3
25+
with:
26+
registry: ghcr.io
27+
username: ${{ github.actor }}
28+
password: ${{ secrets.GITHUB_TOKEN }}
29+
30+
- name: Docker metadata
31+
id: meta
32+
uses: docker/metadata-action@v5
33+
with:
34+
images: ghcr.io/${{ github.repository }}
35+
tags: |
36+
type=raw,value=latest,enable={{is_default_branch}}
37+
type=sha
38+
type=ref,event=tag
39+
40+
- name: Build and push
41+
uses: docker/build-push-action@v6
42+
with:
43+
context: .
44+
push: true
45+
tags: ${{ steps.meta.outputs.tags }}
46+
labels: ${{ steps.meta.outputs.labels }}
47+
cache-from: type=gha
48+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pnpm-debug.log*
2929
.turbo
3030
.parcel-cache
3131
.cache
32+
**/.npm-cache
33+
**/.vite
34+
.direnv
35+
.envrc
36+
*.swp
3237

3338

3439
agent-notes/

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Repository Guidelines
2+
3+
TreeChat is a minimal branching chat UI with a React/TypeScript client and an Express/TypeScript server (streaming model responses, backed by Postgres).
4+
5+
## Essentials
6+
7+
- Use `npm` in `client/` and `server/` (each has its own `package-lock.json`; prefer `npm ci`).
8+
- Build/dev commands: `docs/BUILD.md`
9+
- TypeScript + code style notes: `docs/TYPESCRIPT.md`
10+
- Testing approach (including bugfix workflow): `docs/TESTING.md`
11+
- Env/DB configuration: `docs/CONFIG.md`
12+
- Commit/PR expectations: `docs/CONTRIBUTING.md`
13+
14+
## Bug Reports
15+
16+
When I report a bug, don't start by trying to fix it. Instead, start by writing a test that reproduces the bug. Then, have subagents try to fix the bug and prove it with a passing test.

Dockerfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# syntax=docker/dockerfile:1
2+
3+
ARG NODE_VERSION=20-alpine
4+
5+
FROM node:${NODE_VERSION} AS client-deps
6+
WORKDIR /app/client
7+
COPY client/package.json client/package-lock.json ./
8+
RUN npm ci
9+
10+
FROM client-deps AS client-build
11+
COPY client/ ./
12+
RUN npm run build
13+
14+
FROM node:${NODE_VERSION} AS server-deps
15+
WORKDIR /app/server
16+
COPY server/package.json server/package-lock.json ./
17+
RUN npm ci
18+
19+
FROM server-deps AS server-build
20+
COPY server/ ./
21+
RUN npm run build
22+
23+
FROM node:${NODE_VERSION} AS runtime
24+
ENV NODE_ENV=production
25+
WORKDIR /app/server
26+
27+
COPY server/package.json server/package-lock.json ./
28+
RUN npm ci --omit=dev && npm cache clean --force
29+
30+
COPY --from=server-build /app/server/dist ./dist
31+
COPY --from=client-build /app/client/dist ./public
32+
33+
ENV PORT=8787
34+
ENV SERVE_CLIENT=1
35+
EXPOSE 8787
36+
37+
CMD ["node", "dist/index.js"]

README.md

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,65 @@ This is a minimal React + TypeScript app that renders full branching conversatio
1111
- client: Vite + React + TypeScript UI that renders a conversation tree.
1212
- server: Express + OpenAI SDK proxy endpoint streaming assistant deltas.
1313

14-
## Quick start
14+
## Quickstart (Docker)
15+
16+
Prereqs: Docker Desktop (includes `docker compose`).
17+
18+
### Option A: One command (pull a prebuilt image)
19+
20+
This is the easiest UX for users: pull a prebuilt image + Postgres via Docker Compose.
21+
22+
```
23+
curl -fsSL https://gist.githubusercontent.com/yxzwayne/8beab5c1dd1d1329ca03107061307723/raw/treechat-docker-compose.pull.yml \
24+
| docker compose -f - up -d --pull always
25+
```
26+
27+
Then open http://localhost:8787
28+
29+
Defaults: the container starts with `USE_MOCK=1` (no API key required).
30+
31+
If port 8787 is busy, pick another host port:
32+
33+
```
34+
curl -fsSL https://gist.githubusercontent.com/yxzwayne/8beab5c1dd1d1329ca03107061307723/raw/treechat-docker-compose.pull.yml \
35+
| HOST_PORT=8788 docker compose -f - up -d --pull always
36+
```
37+
38+
To run with real model calls, pass `USE_MOCK=0` and an API key to `docker compose`:
39+
40+
```
41+
curl -fsSL https://gist.githubusercontent.com/yxzwayne/8beab5c1dd1d1329ca03107061307723/raw/treechat-docker-compose.pull.yml \
42+
| USE_MOCK=0 OPENROUTER_API_KEY=... docker compose -f - up -d --pull always
43+
```
44+
45+
### Option B: Local (build from source)
46+
47+
From this repo:
48+
49+
- `docker compose up --build`
50+
- open http://localhost:8787
51+
52+
See `docs/DOCKER.md`.
53+
54+
## Developer setup (no Docker)
1555

1656
1) Install deps
17-
- client: `cd client && npm i`
18-
- server: `cd server && npm i`
57+
- client: `cd client && npm ci`
58+
- server: `cd server && npm ci`
1959

2060
2) Configure env
2161
- In `server/.env` set `OPENROUTER_API_KEY=your_key`
22-
- Optional: USE_MOCK=1 to use a local mocked streaming model
62+
- Optional: set `USE_MOCK=1` to run without network
63+
64+
3) Run (two terminals)
65+
- server: `cd server && npm run dev` (default port 8787)
66+
- client: `cd client && npm run dev` (Vite dev server on 5173; proxied to the server)
2367

24-
3) Run
25-
- server: `npm run dev` (default port 8787)
26-
- client: `npm run dev` (Vite dev server on 5173; proxied to http://localhost:8787)
68+
## Using the app
2769

28-
4) Use
29-
- Type in the chat box while a leaf node is selected. The assistant response streams in as a new child under that leaf.
30-
- Click Retry on a user message to generate a sibling assistant answer; all branches remain visible.
31-
- Click Edit on a user message to create a branched edit and continue from there.
70+
- Type while a leaf node is selected. The assistant response streams in as a new child under that leaf.
71+
- Click Retry on a user message to generate a sibling assistant answer; all branches remain visible.
72+
- Click Edit on a user message to create a branched edit and continue from there.
3273

3374
# Developer Notes
3475

client/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="color-scheme" content="dark light" />
7+
<meta name="theme-color" content="#100F0F" />
8+
<link rel="preconnect" href="https://fonts.googleapis.com" />
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10+
<link
11+
href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap"
12+
rel="stylesheet"
13+
/>
14+
<link
15+
href="https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,100..900;1,100..900&display=swap"
16+
rel="stylesheet"
17+
/>
18+
<link
19+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"
20+
rel="stylesheet"
21+
/>
622
<link rel="icon" type="image/png" sizes="64x64" href="/assets/logos/oak64.png" />
723
<link rel="icon" type="image/svg+xml" href="/assets/logos/oak64.svg" />
824
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />

0 commit comments

Comments
 (0)