Skip to content

Commit 47ab1fc

Browse files
committed
CLAUDE.md
1 parent 8182427 commit 47ab1fc

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
ScreenKey is a cross-platform desktop application built with **Tauri 2.4 + React 19 + Rust** that displays keyboard inputs on screen in real-time. The application uses platform-specific keyboard capture implementations: `evdev` for Linux (global `/dev/input` access) and `rdev` for macOS/Windows.
8+
9+
## Development Commands
10+
11+
All commands must be run from the `screenkey-app/` directory.
12+
13+
### Development
14+
```bash
15+
cd screenkey-app
16+
17+
# Development mode (requires sudo on Linux for keyboard capture)
18+
sudo npm run tauri:dev
19+
20+
# Frontend-only development (without Tauri)
21+
npm run dev
22+
23+
# TypeScript type checking
24+
npx tsc --noEmit
25+
```
26+
27+
### Building
28+
```bash
29+
cd screenkey-app
30+
31+
# Build frontend only
32+
npm run build
33+
34+
# Build complete Tauri application (frontend + backend)
35+
npm run tauri:build
36+
37+
# Build output locations:
38+
# - Linux: src-tauri/target/release/bundle/{appimage,deb}/
39+
# - macOS: src-tauri/target/release/bundle/macos/
40+
# - Windows: src-tauri/target/release/bundle/msi/
41+
```
42+
43+
### Rust Development
44+
```bash
45+
cd screenkey-app/src-tauri
46+
47+
# Format Rust code
48+
cargo fmt --all
49+
50+
# Linting with Clippy
51+
cargo clippy --all-targets -- -D warnings
52+
53+
# Check without building
54+
cargo check
55+
```
56+
57+
### Release Management
58+
```bash
59+
# Automated version bump and release (from project root)
60+
cd screenkey-app
61+
npm run release # Patch version (0.0.x)
62+
npm run release:minor # Minor version (0.x.0)
63+
npm run release:major # Major version (x.0.0)
64+
65+
# This script:
66+
# 1. Checks for uncommitted changes (fails if dirty)
67+
# 2. Bumps version in package.json, tauri.conf.json, Cargo.toml
68+
# 3. Updates CHANGELOG.md with new version and date
69+
# 4. Commits changes with "chore: bump version to X.X.X"
70+
# 5. Creates git tag (vX.X.X)
71+
# 6. Pushes to origin (triggers GitHub Actions release builds)
72+
```
73+
74+
## Architecture
75+
76+
### Frontend-Backend Communication (Tauri IPC)
77+
78+
**Rust → React:** Keyboard events are emitted from Rust backend via Tauri's event system:
79+
80+
```rust
81+
// src-tauri/src/main.rs
82+
app_handle.emit("key-press", KeyEvent {
83+
key: String,
84+
modifiers: Vec<String>
85+
})
86+
```
87+
88+
```typescript
89+
// src/App.tsx
90+
import { listen } from '@tauri-apps/api/event'
91+
92+
listen<KeyEvent>('key-press', (event) => {
93+
setKeys(prev => [...prev, { ...event.payload, timestamp: Date.now() }])
94+
})
95+
```
96+
97+
**React → Rust:** Window control operations use Tauri window API:
98+
99+
```typescript
100+
import { getCurrentWindow } from '@tauri-apps/api/window'
101+
const appWindow = getCurrentWindow()
102+
103+
await appWindow.minimize()
104+
await appWindow.close()
105+
await appWindow.startDragging()
106+
```
107+
108+
### Platform-Specific Keyboard Capture
109+
110+
The backend uses conditional compilation for different platforms:
111+
112+
**Linux (`src-tauri/src/main.rs:279-352`):**
113+
- Uses `evdev` crate to read from `/dev/input/event*` devices
114+
- Requires root privileges or user in `input` group
115+
- `find_keyboard_devices()` scans for devices with keyboard capabilities
116+
- Tracks modifier state globally in `AppState::modifiers` Mutex
117+
- Adaptive polling: 1ms when events detected, 10ms when idle (CPU optimization)
118+
119+
**macOS/Windows (`src-tauri/src/main.rs:355-402`):**
120+
- Uses `rdev` crate for cross-platform keyboard hooks
121+
- Requires Accessibility permissions (macOS) or Administrator (Windows)
122+
- Same modifier state tracking mechanism
123+
124+
### State Management
125+
126+
**Frontend State (React hooks):**
127+
- `keys: KeyEvent[]` - Keypress history with timestamps
128+
- `layoutDirection` - 'vertical' | 'horizontal' | 'wrapped'
129+
- `settings` - Persisted to localStorage, contains:
130+
- `displayDuration` - Auto-hide timer (0 = never hide)
131+
- `opacity` - Window opacity (0.1-1.0)
132+
- `fontSize` - Key display size (12-32px)
133+
- `theme` - Active theme name
134+
- `customTheme?` - Custom theme colors
135+
136+
**Backend State (Rust Mutex):**
137+
- `AppState::modifiers: Mutex<Vec<String>>` - Currently pressed modifier keys
138+
- Thread-safe access from keyboard capture thread
139+
140+
### Key Components
141+
142+
**`src/App.tsx` (401 lines):**
143+
- Event listener setup and cleanup
144+
- Settings panel rendering and persistence
145+
- Auto-hide timer (`useEffect` with `displayDuration`)
146+
- Theme management (6 presets + custom)
147+
- Window control handlers (drag, minimize, close)
148+
149+
**`src/components/KeyDisplay.tsx` (84 lines):**
150+
- Smart auto-scroll: only scrolls when user is at bottom (prevents forced scrolling during manual navigation)
151+
- Layout-aware scrolling (horizontal: `scrollLeft`, vertical/wrapped: `scrollTop`)
152+
- Per-key rendering with modifier support
153+
154+
**`src-tauri/src/main.rs` (409 lines):**
155+
- Platform detection via `#[cfg(target_os = "...")]`
156+
- Key mapping functions: `key_to_string()`, `rdev_key_to_string()`
157+
- Always-on-top enforcement: re-asserts every 2 seconds in background thread
158+
- Modifier tracking on press/release
159+
160+
## Important Implementation Details
161+
162+
### Always-on-Top Window
163+
The window uses periodic re-assertion to stay on top:
164+
```rust
165+
// main.rs:273-276
166+
std::thread::spawn(move || loop {
167+
std::thread::sleep(std::time::Duration::from_secs(2));
168+
let _ = window_clone.set_always_on_top(true);
169+
});
170+
```
171+
172+
### Smart Scroll Behavior
173+
Auto-scroll only triggers when user is near bottom/end (50px threshold):
174+
```typescript
175+
// KeyDisplay.tsx:34-48
176+
const threshold = 50
177+
const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold
178+
if (isAtBottom) {
179+
container.scrollTop = container.scrollHeight
180+
}
181+
```
182+
183+
### Drag-Handle Event Propagation
184+
Buttons in the header must stop propagation to prevent drag interference:
185+
```tsx
186+
<button onMouseDown={(e) => e.stopPropagation()}>
187+
```
188+
189+
### Settings Persistence
190+
Settings are automatically saved to localStorage on every change:
191+
```typescript
192+
useEffect(() => {
193+
localStorage.setItem('screenkey-settings', JSON.stringify(settings))
194+
}, [settings])
195+
```
196+
197+
## CI/CD Pipeline
198+
199+
**`.github/workflows/ci.yml`:**
200+
- Runs on push/PR to main/master
201+
- Steps: TypeScript type check → Frontend build → Rust fmt check → Clippy → Tauri build
202+
203+
**`.github/workflows/release.yml`:**
204+
- Triggered by version tags (`v*`) or manual dispatch
205+
- Builds for 4 targets: Linux x64, macOS Intel/ARM, Windows x64
206+
- Creates draft release, uploads binaries, auto-publishes
207+
208+
## System Dependencies (Linux)
209+
210+
Required for building on Ubuntu/Debian:
211+
```bash
212+
sudo apt-get install -y \
213+
libwebkit2gtk-4.1-dev \
214+
build-essential \
215+
curl wget file \
216+
libssl-dev \
217+
libgtk-3-dev \
218+
libayatana-appindicator3-dev \
219+
librsvg2-dev \
220+
libx11-dev
221+
```
222+
223+
## Running on Linux
224+
225+
The app requires elevated permissions for global keyboard capture:
226+
227+
```bash
228+
# Option 1: Run with sudo
229+
sudo npm run tauri:dev
230+
sudo ./src-tauri/target/release/screenkey-app
231+
232+
# Option 2: Add user to input group (requires logout)
233+
sudo usermod -a -G input $USER
234+
# Then run without sudo after logging out/in
235+
```
236+
237+
## File Structure Notes
238+
239+
- `screenkey-app/` - Main application directory (all npm commands run here)
240+
- `screenkey-app/src/` - React frontend (TypeScript + CSS)
241+
- `screenkey-app/src-tauri/` - Rust backend (Tauri application)
242+
- `screenkey-app/src-tauri/Cargo.toml` - Platform-specific dependencies with `[target.'cfg(...)']`
243+
- Root directory contains project-level files (CHANGELOG.md, RELEASE.md)
244+
245+
## Key Rust Dependencies
246+
247+
- `tauri = "2.4"` - Desktop app framework
248+
- `evdev = "0.12"` - Linux keyboard capture (Linux only)
249+
- `rdev = "0.5"` - Cross-platform keyboard hooks (macOS/Windows only)
250+
- `x11 = "2.21"` - X11 display server access (Linux only)
251+
- `serde` + `serde_json` - Serialization for IPC
252+
253+
## Vite Configuration
254+
255+
Development server runs on port **1420** (strict port mode enabled). Environment variables with `VITE_` or `TAURI_` prefix are exposed to frontend.

0 commit comments

Comments
 (0)