Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
be3ecd6
web: add Click UI provider and theme bridge
alex-clickhouse Aug 15, 2026
679d24e
web: fix cascade layer order so Click UI controls survive preflight
alex-clickhouse Aug 15, 2026
6133d6f
web: adopt the ClickHouse palette, type scale and status tokens
alex-clickhouse Aug 15, 2026
57018c1
web: add the UI primitive layer and move icons onto Click UI
alex-clickhouse Aug 15, 2026
2b3fd5b
web: fix three silent colour-cascade bugs in the variant tables
alex-clickhouse Aug 15, 2026
dfdbfa9
web: add solid status fills, and correct the guidance that caused the…
alex-clickhouse Aug 15, 2026
ecc4041
web: migrate the task board, tool blocks and app shell onto the primi…
alex-clickhouse Aug 15, 2026
ff4648c
web: migrate all 17 pages onto the primitives
alex-clickhouse Aug 15, 2026
51850d1
web: migrate the chat surface onto the primitives
alex-clickhouse Aug 15, 2026
d4abf71
web: fix session sidebar type size, star fill and dim identity hues
alex-clickhouse Aug 15, 2026
17bae2d
web: migrate the code that arrived with the rebase
alex-clickhouse Aug 25, 2026
9b0b9de
web: rebuild the text ramp so every step clears WCAG AA
alex-clickhouse Aug 25, 2026
3c803db
web: let a caller's className beat the primitive's own classes
alex-clickhouse Aug 25, 2026
e9090f8
web: stop Click UI's unused toast hotkey stealing focus
alex-clickhouse Aug 25, 2026
8956357
web: claim only the ARIA patterns these controls actually implement
alex-clickhouse Aug 25, 2026
2798406
Raise the Node floor to 22.12 and make the lockfile installable under…
alex-clickhouse Aug 25, 2026
1989804
web: unstrand the composer kebab on a phone, and keep its popup on sc…
alex-clickhouse Aug 25, 2026
6fb9778
web: put the composer back on the type scale
alex-clickhouse Aug 25, 2026
188f0de
web: fix light-mode elevation, emphasis weight and the green
alex-clickhouse Aug 25, 2026
a131db8
web: let the composer fill the pane instead of the reading column
alex-clickhouse Aug 25, 2026
7c2346a
web: keep the comments on the code that is there
alex-clickhouse Aug 26, 2026
8a84d19
web: put the icon set back on lucide
alex-clickhouse Aug 26, 2026
878c6d1
web: prevent narrow status rows from overflowing
alex-clickhouse Aug 26, 2026
8ca5af6
web: fix starred-row layout shift on hover in session sidebar
pufit Aug 28, 2026
6751424
web: take light-mode hue-yellow off the lime ramp so stars read gold
pufit Aug 28, 2026
b2eb573
Merge remote-tracking branch 'origin/main' into alex-clickhouse/click…
pufit Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,64 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v5
with:
# Not pinned to web/package.json's `engines` floor of 22.12.0 — the
# `frontend-floor` job below covers that. The *test* toolchain needs
# more than the app itself does (jsdom declares `^22.22.2 ||
# ^24.15.0 || >=26.0.0`), so running the suite at the floor would be
# running it outside its own supported range.
node-version: "22"
cache: npm
cache-dependency-path: web/package-lock.json

- name: Install dependencies
run: npm ci
# --strict-peer-deps turns an unsatisfiable peer range from a warning
# into a failure. The default `npm ci` installs such a dependency anyway
# and says nothing, but anyone whose npm has strict peers on cannot
# install the lockfile at all. This job catches that here instead.
# An `overrides` entry in web/package.json is how to settle one.
#
# Deliberately NOT --engine-strict. npm applies that to every package in
# the lockfile, including dev-only tooling whose Node floors are already
# higher than anything we would declare as Nerve's (jsdom 22.22.2,
# undici 22.19.0) — so it cannot express "the shipped dependencies fit
# the declared floor", which is the property worth checking.
run: npm ci --strict-peer-deps

- name: Build frontend (tsc -b && vite build)
run: npm run build

- name: Run test suite (Vitest)
run: npm test

frontend-floor:
# The Node floor is a support claim: install.sh refuses anything older,
# README and docs/setup.md tell people to install it, and web/package.json
# declares it in `engines`. This job is what keeps those four from drifting
# apart — it installs and builds on exactly the oldest Node we accept.
#
# Install and build only; see the note on `frontend-build` for why the test
# suite does not run down here.
name: Frontend install floor (Node 22.12.0)
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: web
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Node.js at the declared floor
uses: actions/setup-node@v5
with:
# Exact, and it has to stay in step with `engines.node` in
# web/package.json and MIN_NODE_VERSION in install.sh.
node-version: "22.12.0"
cache: npm
cache-dependency-path: web/package-lock.json

- name: Install dependencies
run: npm ci --strict-peer-deps

- name: Build frontend (tsc -b && vite build)
run: npm run build
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The installer handles everything — installs dependencies (Python, Node.js, uv)
<details>
<summary><strong>Manual install</strong></summary>

**Prerequisites:** [Git](https://git-scm.com/), [Python 3.13+](https://www.python.org/), [uv](https://docs.astral.sh/uv/), [Node.js 18+](https://nodejs.org/)
**Prerequisites:** [Git](https://git-scm.com/), [Python 3.13+](https://www.python.org/), [uv](https://docs.astral.sh/uv/), [Node.js 22.12+](https://nodejs.org/)

```bash
git clone https://github.com/ClickHouse/nerve.git && cd nerve
Expand Down Expand Up @@ -262,7 +262,7 @@ See [docs/config.md](docs/config.md) for all options.

- [Python](https://www.python.org/) 3.13+
- [uv](https://docs.astral.sh/uv/) (recommended package manager)
- [Node.js](https://nodejs.org/) 18+ (for web UI build)
- [Node.js](https://nodejs.org/) 22.12+ (for web UI build)
- [Claude Code CLI](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) (bundled with `claude-agent-sdk`)
- Anthropic API key **or** Claude subscription via [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) proxy
- Optional: OpenAI API key (for vector-based memory search — without it, LLM-based recall is used), Telegram bot token, [gog](https://github.com/googleworkspace/cli) CLI, [gh](https://cli.github.com/) CLI
Expand Down
2 changes: 1 addition & 1 deletion docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The `nerve init` wizard walks you through deployment, mode selection, API keys,

### Server deployment
- Python 3.13+
- Node.js 18+ (for web UI build)
- Node.js 22.12+ (for web UI build)
- Anthropic API key **or** Claude subscription (via CLIProxyAPI proxy)

### Docker deployment
Expand Down
34 changes: 27 additions & 7 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ CONFIG_DIR="${NERVE_CONFIG_DIR:-$INSTALL_DIR}"
# failed at `uv pip install -e .` with an opaque dependency conflict.
MIN_PYTHON_MINOR=13
PREFERRED_PYTHON_MINOR=13
# Vite 7 (see web/package.json) requires Node 20.19+ or 22.12+.
MIN_NODE_VERSION="20.19.0"
# 22.12, not Vite 7's 20.19 floor: @clickhouse/click-ui declares
# `engines.node >=22.12.0`, and web/package.json declares the same.
# Provisioning a 20.19 here would install a Node the dependency graph refuses
# under a strict (`--engine-strict`) install, which is a support claim we
# cannot back.
MIN_NODE_VERSION="22.12.0"
NON_INTERACTIVE="${NERVE_NON_INTERACTIVE:-0}"
AUTO_YES="${NERVE_YES:-0}"
[ "$NON_INTERACTIVE" = "1" ] && AUTO_YES=1
Expand Down Expand Up @@ -331,7 +335,7 @@ ensure_python() {
success "Python $installed_py_ver installed via system packages"
}

# --- Dependency: Node.js 18+ ---
# --- Dependency: Node.js (floor in MIN_NODE_VERSION) ---

ensure_node() {
if command_exists node; then
Expand All @@ -341,7 +345,7 @@ ensure_node() {
success "Node.js v$ver"
return
fi
warn "Node.js v$ver is too old (need v${MIN_NODE_VERSION}+ or v22.12+)"
warn "Node.js v$ver is too old (need v${MIN_NODE_VERSION}+)"
fi

info "Node.js v${MIN_NODE_VERSION}+ is not installed"
Expand Down Expand Up @@ -372,7 +376,10 @@ ensure_node() {
run_as_root pacman -S --noconfirm nodejs npm
;;
zypper)
run_as_root zypper install -y nodejs20
# nodejs22, not nodejs20: openSUSE's nodejs20 tops out below the
# MIN_NODE_VERSION floor, so it would install and then fail the
# check below.
run_as_root zypper install -y nodejs22
;;
brew)
brew install node
Expand All @@ -389,7 +396,7 @@ ensure_node() {
;;
*)
error "Don't know how to install Node.js on $DISTRO."
error "Install Node.js ${MIN_NODE_MAJOR}+ manually and re-run."
error "Install Node.js v${MIN_NODE_VERSION}+ manually and re-run."
exit 1
;;
esac
Expand All @@ -399,7 +406,20 @@ ensure_node() {
exit 1
fi

success "Node.js $(node --version) installed"
# A distro's "node" package is whatever that distro froze, and nodesource's
# setup_lts.x follows whatever upstream calls LTS today. Neither is promised
# to clear our floor, and a too-old Node here surfaces much later as an
# opaque syntax error inside a dependency. Check what actually landed.
local new_ver
new_ver="$(node --version | tr -d 'v')"
if ! version_ge "$new_ver" "$MIN_NODE_VERSION"; then
error "Installed Node.js v${new_ver}, but v${MIN_NODE_VERSION}+ is required."
error "The package for $DISTRO is behind. Install a newer Node.js and re-run:"
error " https://nodejs.org/en/download/"
exit 1
fi

success "Node.js v${new_ver} installed"
}

# --- Clone or Update Repository ---
Expand Down
2 changes: 1 addition & 1 deletion nerve/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1913,7 +1913,7 @@ def _build_web_ui(self) -> None:
if not shutil.which("node"):
click.secho(
" ⚠ Node.js not found — web UI not built.\n"
" Install Node.js 18+ and run: cd web && npm ci && npm run build",
" Install Node.js 22.12+ and run: cd web && npm ci && npm run build",
fg="yellow",
)
return
Expand Down
23 changes: 21 additions & 2 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,31 @@
there, 404 otherwise. Not a bundled asset: it is per-instance config. -->
<link rel="icon" href="/favicon.ico" />
<title>Nerve</title>
<!-- Pre-paint theme stamp. Must mirror themeStore's getInitialPreference()
and resolve() exactly, or the first frame disagrees with the app.

Both attributes are needed and they are not interchangeable:
data-theme Nerve's own preference. Absent means "follow the OS",
which a media query in index.css handles.
data-cui-theme Click UI's tokens. It has no "system" value and it
defaults to LIGHT when the attribute is missing —
while Nerve defaults to dark. Leaving it unset is a
white flash on every cold load. -->
<script>
(function() {
(function () {
var p = localStorage.getItem('nerve-theme');
if (p === 'light' || p === 'dark') {
if (p !== 'light' && p !== 'dark' && p !== 'system') p = 'dark';
if (p !== 'system') {
document.documentElement.setAttribute('data-theme', p);
}
var resolved =
p === 'system'
? (window.matchMedia &&
window.matchMedia('(prefers-color-scheme: light)').matches
? 'light'
: 'dark')
: p;
document.documentElement.setAttribute('data-cui-theme', resolved);
})();
</script>
</head>
Expand Down
Loading
Loading