refactor: remove client module and associated show command logic
Some checks failed
CI / test (push) Failing after 5m7s
CI / clippy (push) Failing after 4m59s
CI / fmt (push) Successful in 28s

This commit is contained in:
2026-03-15 23:49:31 +01:00
parent 12f1f541ae
commit 58d0739cea
3 changed files with 110 additions and 21 deletions

110
ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,110 @@
# k-launcher Architecture
## Philosophy
- **TDD:** Red-Green-Refactor is mandatory. No functional code without a failing test first.
- **Clean Architecture:** Strict layer separation — Domain, Application, Infrastructure, Main.
- **Newtype Pattern:** All domain primitives wrapped (e.g. `struct Score(f64)`).
- **Small Traits / ISP:** Many focused traits over one "God" trait.
- **No Cyclic Dependencies:** Use IoC (define traits in higher-level modules, implement in lower-level).
---
## Workspace Structure
| Crate | Layer | Responsibility |
|---|---|---|
| `k-launcher-kernel` | Domain + Application | Newtypes (`ResultId`, `ResultTitle`, `Score`), `Plugin` trait, `SearchEngine` trait, `AppLauncher` port, `Kernel` use case |
| `k-launcher-config` | Infrastructure | TOML config loading; `Config`, `WindowCfg`, `AppearanceCfg`, `PluginsCfg` structs |
| `k-launcher-os-bridge` | Infrastructure | `UnixAppLauncher` (process spawning), `WindowConfig` adapter |
| `k-launcher-plugin-host` | Infrastructure | `ExternalPlugin` — JSON-newline IPC protocol for out-of-process plugins |
| `k-launcher-ui` | Infrastructure | iced 0.14 Elm-like UI (`KLauncherApp`, debounced async search, keyboard nav) |
| `k-launcher-ui-egui` | Infrastructure | Alternative egui UI (feature-gated) |
| `plugins/plugin-apps` | Infrastructure | XDG `.desktop` parser, frecency scoring, nucleo fuzzy matching |
| `plugins/plugin-calc` | Infrastructure | `evalexpr`-based calculator |
| `plugins/plugin-cmd` | Infrastructure | Shell command runner |
| `plugins/plugin-files` | Infrastructure | File path search |
| `plugins/plugin-url` | Infrastructure | URL opener |
| `k-launcher` | Main/Entry | DI wiring, CLI arg parsing (`show` command), `run_ui()` composition root |
---
## Dependency Graph
```
k-launcher (main)
├── k-launcher-kernel (Domain/Application)
├── k-launcher-config (Infrastructure — pure data, no kernel dep)
├── k-launcher-os-bridge (Infrastructure)
├── k-launcher-plugin-host (Infrastructure)
├── k-launcher-ui (Infrastructure)
└── plugins/* (Infrastructure)
└── k-launcher-kernel
```
All arrows point inward toward the kernel. The kernel has no external dependencies.
---
## Core Abstractions (kernel)
```rust
// Plugin trait — implemented by every plugin
async fn search(&self, query: &str) -> Vec<SearchResult>;
// SearchEngine trait — implemented by Kernel
async fn search(&self, query: &str) -> Vec<SearchResult>;
// AppLauncher port — implemented by UnixAppLauncher in os-bridge
fn execute(&self, action: &LaunchAction);
// DesktopEntrySource trait (plugin-apps) — swappable .desktop file source
```
---
## Plugin System
Two kinds of plugins:
1. **In-process** — implement `Plugin` in Rust, linked at compile time.
- `plugin-calc`, `plugin-apps`, `plugin-cmd`, `plugin-files`, `plugin-url`
2. **External / out-of-process**`ExternalPlugin` in `k-launcher-plugin-host` communicates via JSON newline protocol over stdin/stdout.
- Query: `{"query": "..."}`
- Response: `[{"id": "...", "title": "...", "score": 1.0, "description": "...", "icon": "...", "action": "..."}]`
Plugins are enabled/disabled via `~/.config/k-launcher/config.toml`.
---
## Kernel (Application Use Case)
`Kernel::search` fans out to all registered plugins concurrently via `join_all`, merges results, sorts by `Score` descending, truncates to `max_results`.
---
## UI Architecture (iced 0.14 — Elm model)
- **State:** `KLauncherApp` holds engine ref, launcher ref, query string, results, selected index, appearance config.
- **Messages:** `QueryChanged`, `ResultsReady`, `KeyPressed`
- **Update:**
- `QueryChanged` → spawns debounced async task (50 ms) → `ResultsReady`
- Epoch guard prevents stale results from out-of-order responses
- **View:** search bar + scrollable result list with icon support (SVG/raster)
- **Subscription:** keyboard events — `Esc` = quit, `Enter` = launch, arrows = navigate
- **Window:** transparent, undecorated, centered (Wayland-compatible)
---
## Frecency (plugin-apps)
`FrecencyStore` records app launches by ID. On empty query, returns top-5 frecent apps instead of search results.
---
## Configuration
`~/.config/k-launcher/config.toml` — sections: `[window]`, `[appearance]`, `[search]`, `[plugins]`.
All fields have sane defaults; a missing file yields defaults without error.

View File

@@ -1,10 +0,0 @@
use std::io::Write;
pub fn send_show() -> Result<(), Box<dyn std::error::Error>> {
let runtime_dir =
std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/run/user/1000".to_string());
let socket_path = format!("{runtime_dir}/k-launcher.sock");
let mut stream = std::os::unix::net::UnixStream::connect(&socket_path)?;
stream.write_all(b"show\n")?;
Ok(())
}

View File

@@ -1,5 +1,3 @@
mod client;
use std::sync::Arc;
use k_launcher_kernel::Kernel;
@@ -15,15 +13,6 @@ use plugin_files::FilesPlugin;
fn main() {
tracing_subscriber::fmt::init();
let args: Vec<String> = std::env::args().collect();
if args.get(1).map(|s| s.as_str()) == Some("show") {
if let Err(e) = client::send_show() {
eprintln!("error: failed to send show command: {e}");
std::process::exit(1);
}
return;
}
if let Err(e) = run_ui() {
eprintln!("error: UI: {e}");
std::process::exit(1);