Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ proxy for Claude/npm, create `~/.config/kadr/claude-env.json`:
{ "env": { "HTTPS_PROXY": "http://127.0.0.1:1080", "NO_PROXY": "127.0.0.1,localhost" } }
```

## NixOS

On NixOS (tested on Niri/Wayland), a plain `npm install`/`npm run dev` can
fail or render a blank window because of a few platform quirks (building
`node-pty`, no prebuilt Electron binary, Wayland/Ozone). A ready-made
`flake.nix` devShell handles this:

```bash
nix develop
npm install
npm run dev
```

See [docs/nixos.md](docs/nixos.md) for details and a non-flake fallback.

## How the AI integration works

Kadr starts a local HTTP bridge into the renderer and hands Claude an MCP
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ Claude/npm нужен прокси — создайте `~/.config/kadr/claude-e
{ "env": { "HTTPS_PROXY": "http://127.0.0.1:1080", "NO_PROXY": "127.0.0.1,localhost" } }
```

## NixOS

На NixOS (проверено на Niri/Wayland) `npm install`/`npm run dev` из коробки
может упасть или показать пустой экран из-за нескольких платформенных
особенностей (сборка `node-pty`, отсутствие prebuilt-бинарника Electron,
Wayland/Ozone). Есть `flake.nix` с готовым `devShell`:

```bash
nix develop
npm install
npm run dev
```

Подробности и вариант без флейков — в [docs/nixos.md](docs/nixos.md).

## Как устроена ИИ-интеграция

Kadr поднимает локальный мост в рендерер и отдаёт Claude MCP-сервер с
Expand Down
110 changes: 110 additions & 0 deletions docs/nixos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Running Kadr on NixOS / Запуск Kadr на NixOS

<details open>
<summary><b>English</b></summary>

## Quick start (with flake)

```bash
nix develop
npm install
npm run dev
```

The devShell provides `gcc`/`make`/`python3` (needed to build the native
`node-pty` dependency), `electron`, and `ffmpeg`, and automatically:

- forces X11/XWayland via `ELECTRON_OZONE_PLATFORM_HINT=x11` (native
Wayland/Ozone can crash or render a blank window depending on your
compositor),
- links `node_modules/electron/dist/electron` to the nixpkgs Electron
binary, since the npm `electron` package's binary download doesn't work
on NixOS's non-FHS filesystem.

## Without the flake

If you don't want to use flakes, run once per shell session:

```bash
nix-shell -p gcc gnumake python3 --run "npm install"

export ELECTRON_OZONE_PLATFORM_HINT=x11
export XDG_SESSION_TYPE=x11
unset WAYLAND_DISPLAY

nix-shell -p electron --run '
mkdir -p node_modules/electron/dist
printf "electron" > node_modules/electron/path.txt
ln -sf $(which electron) node_modules/electron/dist/electron
npm run dev
'
```

## Known-good config

These fixes live in the app code (not NixOS-specific), but are worth
knowing about if you hit similar symptoms elsewhere:

- `electron.vite.config.ts` pins the renderer dev server to
`host: '127.0.0.1'` to avoid an intermittent `ERR_NETWORK_CHANGED` on
systems with IPv6 disabled or misconfigured.
- `electron/preload.ts` assigns `window.kadr` unconditionally (guarded by
`Object.keys(api)`) so Vite's bundler can't tree-shake the whole API
object away when `contextIsolation: false` is a compile-time constant.

</details>

<details>
<summary><b>Русский</b></summary>

## Быстрый старт (с флейком)

```bash
nix develop
npm install
npm run dev
```

DevShell предоставляет `gcc`/`make`/`python3` (нужны для сборки нативной
зависимости `node-pty`), `electron` и `ffmpeg`, а также автоматически:

- принудительно включает X11/XWayland через `ELECTRON_OZONE_PLATFORM_HINT=x11`
(нативный Wayland/Ozone может падать или рисовать пустое окно в
зависимости от вашего компоузера),
- линкует `node_modules/electron/dist/electron` на бинарник Electron из
nixpkgs, так как загрузка бинарника npm-пакетом `electron` не работает
на не-FHS файловой системе NixOS.

## Без флейка

Если не хотите использовать флейки, выполняйте один раз на сессию шелла:

```bash
nix-shell -p gcc gnumake python3 --run "npm install"

export ELECTRON_OZONE_PLATFORM_HINT=x11
export XDG_SESSION_TYPE=x11
unset WAYLAND_DISPLAY

nix-shell -p electron --run '
mkdir -p node_modules/electron/dist
printf "electron" > node_modules/electron/path.txt
ln -sf $(which electron) node_modules/electron/dist/electron
npm run dev
'
```

## Известные фиксы в конфиге

Эти фиксы находятся в коде приложения (не специфичны для NixOS), но
полезно о них знать при похожих симптомах в других окружениях:

- `electron.vite.config.ts` закрепляет dev-сервер рендерера на
`host: '127.0.0.1'`, что убирает периодический `ERR_NETWORK_CHANGED` на
системах с отключённым или неправильно настроенным IPv6.
- `electron/preload.ts` присваивает `window.kadr` безусловно (под защитой
`Object.keys(api)`), чтобы бандлер Vite не мог вытряхнуть весь объект
API tree-shaking'ом, когда `contextIsolation: false` известна на этапе
компиляции.

</details>
4 changes: 4 additions & 0 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export default defineConfig({
},
renderer: {
root: '.',
server: {
host: '127.0.0.1',
port: 5173
},
build: {
outDir: 'out/renderer',
rollupOptions: { input: resolve(__dirname, 'index.html') }
Expand Down
13 changes: 8 additions & 5 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ function createWindow() {
app.exit(1)
}
})
if (process.env.ELECTRON_RENDERER_URL) {
win.loadURL(process.env.ELECTRON_RENDERER_URL)
} else {
win.loadFile(join(__dirname, '../renderer/index.html'))
}
setTimeout(() => {
if (process.env.ELECTRON_RENDERER_URL) {
const url = process.env.ELECTRON_RENDERER_URL.replace('localhost', '127.0.0.1');
win.loadURL(url)
} else {
win.loadFile(join(__dirname, '../renderer/index.html'))
}
}, 1500);
}

/**
Expand Down
7 changes: 6 additions & 1 deletion electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,13 @@ const api: KadrApi = {
}
}

// Side-effect guard: prevent Vite from tree-shaking the api object
// because the renderer accesses these methods at runtime via window.kadr
void Object.keys(api)

// contextIsolation is off (see main.ts: export frames pass by reference),
// so the api object lands on the shared window directly; the bridge branch
// keeps working if isolation is ever re-enabled.
Object.defineProperty(globalThis, 'kadr', { value: api, writable: true, configurable: true })
if (process.contextIsolated) contextBridge.exposeInMainWorld('kadr', api)
else (globalThis as unknown as { kadr: KadrApi }).kadr = api
console.log('[preload] kadr api keys:', Object.keys(api))
53 changes: 53 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
description = "Kadr dev environment (NixOS)";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShells.default = pkgs.mkShell {
buildInputs = [
pkgs.nodejs_22
pkgs.electron
pkgs.ffmpeg
pkgs.gcc
pkgs.gnumake
pkgs.python3
pkgs.pkg-config
];

shellHook = ''
# --- Force X11/XWayland: native Wayland/Ozone crashes or renders
# a blank window under some compositors (observed on Niri). ---
export ELECTRON_OZONE_PLATFORM_HINT=x11
export XDG_SESSION_TYPE=x11
unset WAYLAND_DISPLAY
unset NIRI_SOCKET

export KADR_FFMPEG="${pkgs.ffmpeg}/bin/ffmpeg"
export KADR_FFPROBE="${pkgs.ffmpeg}/bin/ffprobe"

# --- npm's "electron" package expects to download a prebuilt
# binary into node_modules/electron/dist/, which fails on
# NixOS's non-FHS filesystem. Point it at the nixpkgs build
# instead, the same way `electron` upstream supports via
# path.txt + dist/<name>. ---
if [ -d node_modules/electron ] && [ ! -f node_modules/electron/dist/electron ]; then
mkdir -p node_modules/electron/dist
printf "electron" > node_modules/electron/path.txt
ln -sf "${pkgs.electron}/bin/electron" node_modules/electron/dist/electron
echo "[flake] linked node_modules/electron/dist/electron -> nixpkgs electron"
fi

echo "[flake] Kadr devShell ready. Run: npm install && npm run dev"
'';
};
});
}