v0.2.0
Some checks failed
CI / test (push) Failing after 5m16s
CI / fmt (push) Has been cancelled
CI / clippy (push) Has been cancelled
Release / build (push) Failing after 5m31s

clean architecture refactor, performance, resilience, DX/UX

architecture:
- 13 crates with proper domain/application/infrastructure layers
- domain crate: newtypes, ports (Plugin, AppLauncher), constants
- kernel: pure orchestrator
- shared UI state machine (k-launcher-ui-core)
- merged plugin-api into domain as ports module
- granular file structure (no monolithic lib.rs)
- all tests extracted to tests/ directories

features:
- frecency boost in search results
- empty query shows top frecent apps
- append-only frecency log with configurable compaction
- config-driven styling (all colors, sizes, debounce)
- configurable terminal emulator, external plugin timeout
- log rotation with max_log_files
- loading indicator, descriptive placeholder text
- graceful shutdown via iced::exit() + Plugin::shutdown()
- --version flag, panic hook, signal handling (SIGINT/SIGTERM)
- SpawnInTerminal in external plugin protocol

performance:
- ~1500 -> ~50 heap allocs per keystroke
- reused Matcher, Pattern, char buffer across entries
- Arc<str> for shared result fields
- pre-filter before fuzzy matching
- partial sort for top frecent IDs
- cached lowercase names in entries

resilience:
- parking_lot (no mutex poisoning)
- thiserror hierarchy (PluginError, ConfigError, AppError)
- all silent error swallowing replaced with tracing::warn
- config parse errors logged

quality:
- named constants (no magic strings/numbers)
- named types (no anonymous tuples)
- Rgba newtype with validation
- domain newtype validation (debug_assert non-empty)
- man page, LICENSE (MIT), PKGBUILD, example config
- plugin development guide updated
- make check (fmt + clippy + test), make dev (RUST_LOG=debug)

style: format code for better readability in tests and function signatures

fix: update build_entries function signature to ignore frecency parameter

fix(review): bugs, arch violations, design smells

P1 bugs:
- unix_launcher: shell_split respects quoted args (was split_whitespace)
- plugin-host: 5s timeout on external plugin search
- ui: handle engine init panic, wire error state
- ui-egui: read window config instead of always using defaults
- plugin-url: use OpenPath action instead of SpawnProcess+xdg-open

Architecture:
- remove WindowConfig (mirror of WindowCfg); use WindowCfg directly
- remove on_select closure from SearchResult (domain leakage)
- remove LaunchAction::Custom; add Plugin::on_selected + SearchEngine::on_selected
- apps: record frecency via on_selected instead of embedded closure

Design smells:
- frecency: extract decay_factor helper, write outside mutex
- apps: remove cfg(test) cache_path hack; add new_for_test ctor
- apps: stable ResultId using name+exec to prevent collision
- files: stable ResultId using full path instead of index
- plugin-host: remove k-launcher-os-bridge dep (WindowConfig gone)

Update iced dependency in Cargo.toml to disable default features and add additional ones

feat(app): enhance engine initialization with EngineHandle and update run function signature

feat: production hardening (panic isolation, file logging, apps cache)

- Kernel::search wraps each plugin in catch_unwind; panics are logged and return []
- init_logging() adds daily rolling file at ~/.local/share/k-launcher/logs/
- AppsPlugin caches entries to ~/.cache/k-launcher/apps.bin via bincode; stale-while-revalidate on subsequent launches
- 57 tests pass

refactor: remove client module and associated show command logic

fix(app): format code for clarity in update function

chore: update .gitignore and enhance README with compositor setup instructions

chore(docs): remove unused screenshot file

feature/prod-ready (#1)

Reviewed-on: #1

fix(calc): remove ambiguous log alias, use ln/log2/log10 explicitly

fix(calc): fix log/ln naming, cache math context, strengthen sin(pi) test

feat(calc): add math functions (sqrt, sin, cos, etc.) and pi/e constants

refactor(calc): rename preprocess, extend underscore test assertions

feat(calc): strip underscore digit separators

feat: update dependencies for improved compatibility and performance

feat: add plugin-url for URL handling and open in browser functionality

feat: add support for external plugins and enhance plugin management

feat: add Makefile for build, run, and installation commands

feat: add required features for k-launcher-egui and update dependencies

feat: update README and add documentation for installation, configuration, usage, and plugin development

feat: enhance configuration management and UI styling, remove unused theme module

feat: add k-launcher-config crate for configuration management and integrate with existing components

feat: add k-launcher-ui-egui crate for enhanced UI

- Introduced a new crate `k-launcher-ui-egui` to provide a graphical user interface using eframe and egui.
- Updated the workspace configuration in `Cargo.toml` to include the new crate.
- Implemented the main application logic in `src/app.rs`, handling search functionality and user interactions.
- Created a library entry point in `src/lib.rs` to expose the `run` function for launching the UI.
- Modified the `k-launcher` crate to include a new binary target for the egui-based launcher.
- Added a new main file `src/main_egui.rs` to initialize and run the egui UI with the existing kernel and launcher components.

feat: implement OS bridge and enhance app launcher functionality

feat: add FilesPlugin for file searching and integrate into KLauncher

feat: implement frecency tracking for app usage and enhance search functionality

feat: add CmdPlugin for executing terminal commands and update workspace configuration

refactor: update dependencies and improve keyboard event handling in KLauncherApp

refactor: simplify theme usage and enhance AppsPlugin structure

feat: restructure k-launcher workspace and add core functionality

- Updated Cargo.toml to include a new k-launcher crate and reorganized workspace members.
- Introduced a README.md file detailing the project philosophy, architecture, and technical specifications.
- Implemented a new Kernel struct in k-launcher-kernel for managing plugins and search functionality.
- Created a Plugin trait for plugins to implement, allowing for asynchronous search operations.
- Developed k-launcher-ui with an Iced-based UI for user interaction, including search input and result display.
- Added AppsPlugin and CalcPlugin to handle application launching and basic calculations, respectively.
- Established a theme module for UI styling, focusing on an Aero aesthetic.
- Removed unnecessary main.rs files from plugin crates, streamlining the project structure.

Initialize k-launcher project structure with multiple crates and basic configurations
This commit is contained in:
2026-07-24 13:42:14 +02:00
parent 2e773cdeaf
commit 051d19d878
95 changed files with 4129 additions and 2591 deletions

View File

@@ -2,50 +2,88 @@
Config file: `~/.config/k-launcher/config.toml`
The file is optional — all fields have defaults and missing sections fall back to defaults automatically. Create it manually if you want to customize behavior.
The file is optional — all fields have defaults and missing sections fall back to defaults automatically. If the file exists but has a parse error, a warning is logged and defaults are used.
## Full Annotated Example
See [config.example.toml](../config.example.toml) for a ready-to-copy template with all options.
```toml
[window]
width = 600.0 # window width in logical pixels
height = 400.0 # window height in logical pixels
decorations = false # show window title bar / frame
transparent = true # allow background transparency
resizable = false # allow manual resizing
## Sections
[appearance]
# RGBA: r/g/b are 0255 as floats, a is 0.01.0
background_rgba = [20.0, 20.0, 30.0, 0.9] # main background
border_rgba = [229.0, 125.0, 33.0, 1.0] # accent/border color
border_width = 1.0 # border thickness in pixels
border_radius = 8.0 # corner radius of the window
search_font_size = 18.0 # font size of the search input
title_size = 15.0 # font size of result titles
desc_size = 12.0 # font size of result descriptions
row_radius = 4.0 # corner radius of result rows
placeholder = "Search..." # search input placeholder text
### [window]
[search]
max_results = 8 # maximum results shown at once
| Field | Type | Default | Description |
|---|---|---|---|
| `width` | float | `600.0` | Window width in pixels |
| `height` | float | `400.0` | Window height in pixels |
| `decorations` | bool | `false` | Show window title bar |
| `transparent` | bool | `true` | Enable background transparency |
| `resizable` | bool | `false` | Allow manual resizing |
[plugins]
calc = true # math expression evaluator
cmd = true # shell command runner (> prefix)
files = true # filesystem browser (/ or ~/ prefix)
apps = true # XDG application launcher
### [appearance]
# External (dynamic) plugins — repeat block for each plugin
[[plugins.external]]
name = "my-plugin" # display name / identifier
path = "/path/to/my-plugin" # path to executable
args = [] # optional extra arguments
```
| Field | Type | Default | Description |
|---|---|---|---|
| `background_rgba` | [R,G,B,A] | `[20, 20, 30, 0.9]` | Main background color |
| `border_rgba` | [R,G,B,A] | `[229, 125, 33, 1.0]` | Border/accent color |
| `border_width` | float | `1.0` | Border thickness |
| `border_radius` | float | `8.0` | Window corner radius |
| `search_font_size` | float | `18.0` | Search input font size |
| `title_size` | float | `15.0` | Result title font size |
| `desc_size` | float | `12.0` | Result description font size |
| `row_radius` | float | `4.0` | Result row corner radius |
| `placeholder` | string | `"Search apps, ..."` | Search input placeholder |
| `selected_row_rgba` | [R,G,B,A] | `[229, 125, 33, 1.0]` | Selected result background |
| `unselected_row_rgba` | [R,G,B,A] | `[255, 255, 255, 0.07]` | Unselected result background |
| `description_rgba` | [R,G,B,A] | `[210, 215, 230, 1.0]` | Description text color |
| `no_results_rgba` | [R,G,B,A] | `[180, 180, 200, 0.5]` | "No results" text color |
| `error_rgba` | [R,G,B,A] | `[255, 80, 80, 1.0]` | Error text color |
| `icon_size` | float | `24.0` | App icon size in pixels |
## RGBA Format
#### RGBA format
Colors use `[r, g, b, a]` arrays where:
- `r`, `g`, `b` — red, green, blue channels as floats **0.0255.0**
- `a` — alpha (opacity) as a float **0.01.0**
Colors use `[R, G, B, A]` arrays where R/G/B are 0255 (as floats) and A is 0.01.0 (opacity). Values are clamped to valid ranges.
Example — semi-transparent white: `[255.0, 255.0, 255.0, 0.5]`
### [search]
| Field | Type | Default | Description |
|---|---|---|---|
| `max_results` | integer | `8` | Maximum results shown |
| `debounce_ms` | integer | `50` | Milliseconds to wait after last keystroke before searching |
| `frecency_compact_threshold` | integer | `50` | Frecency log entries before compacting to snapshot |
### [plugins]
| Field | Type | Default | Description |
|---|---|---|---|
| `calc` | bool | `true` | Calculator plugin |
| `cmd` | bool | `true` | Shell command plugin |
| `files` | bool | `true` | File browser plugin |
| `apps` | bool | `true` | Application search plugin |
### [[plugins.external]]
Repeatable block for external plugins.
| Field | Type | Default | Description |
|---|---|---|---|
| `name` | string | required | Plugin display name |
| `path` | string | required | Path to plugin executable |
| `args` | string[] | `[]` | Arguments to pass |
| `timeout_secs` | integer | `5` | Search timeout per query |
### [logging]
| Field | Type | Default | Description |
|---|---|---|---|
| `max_log_files` | integer | `7` | Daily log files to keep |
Logs are stored in `~/.local/share/k-launcher/logs/`.
### [terminal]
| Field | Type | Default | Description |
|---|---|---|---|
| `cmd` | string | auto-detect | Terminal emulator for `>` commands |
If unset, detected from `$TERM_CMD`, `$TERMINAL`, or PATH (foot, kitty, alacritty, wezterm, konsole, xterm).
Example: `cmd = "kitty -e"`

View File

@@ -1,39 +1,61 @@
# Installation
## Prerequisites
## Arch Linux (AUR)
```bash
yay -S k-launcher
```
## Build from Source
### Prerequisites
- **Rust** stable toolchain — install via [rustup](https://rustup.rs)
- **git**
- A **Wayland** or **X11** compositor (Linux)
## Build from Source
### Build and install
```bash
git clone https://github.com/GKaszewski/k-launcher
cd k-launcher
cargo build --release
make install
```
Binary location: `target/release/k-launcher`
### Optional: install to PATH
```bash
cp target/release/k-launcher ~/.local/bin/
```
This builds a release binary and copies it to `~/.local/bin/k-launcher`.
Ensure `~/.local/bin` is in your `$PATH`.
## Autostart
### Manual build
```bash
cargo build --release
cp target/release/k-launcher ~/.local/bin/
```
## Compositor Keybind
### Hyprland
Add to `~/.config/hypr/hyprland.conf`:
```
exec-once = k-launcher
windowrule = float, ^(k-launcher)$
windowrule = center, ^(k-launcher)$
bind = SUPER, Space, exec, k-launcher
```
### Sway
Add to `~/.config/sway/config`:
```
for_window [app_id="k-launcher"] floating enable, move position center
bindsym Mod4+space exec k-launcher
```
## Autostart (optional)
### systemd user service
Create `~/.config/systemd/user/k-launcher.service`:
@@ -55,3 +77,9 @@ Then enable it:
```bash
systemctl --user enable --now k-launcher
```
## Verify
```bash
k-launcher --version
```

View File

@@ -35,6 +35,7 @@ The process is kept alive between queries — do **not** exit after each respons
| `"type"` | Extra fields | Behavior |
|----------|-------------|---------|
| `SpawnProcess` | `"cmd"` | Launch process directly |
| `SpawnInTerminal` | `"cmd"` | Run command in terminal emulator |
| `CopyToClipboard` | `"text"` | Copy text to clipboard |
| `OpenPath` | `"path"` | Open file/dir with xdg-open |
@@ -53,7 +54,8 @@ In `~/.config/k-launcher/config.toml`:
[[plugins.external]]
name = "my-plugin"
path = "/usr/lib/k-launcher/plugins/my-plugin"
args = [] # optional
args = [] # optional
timeout_secs = 5 # optional, default 5
```
Multiple `[[plugins.external]]` blocks are supported.
@@ -96,7 +98,7 @@ for line in sys.stdin:
## Built-in Plugins (compiled-in)
Built-in plugins implement the `Plugin` trait from `k-launcher-kernel` as Rust crates compiled into the binary.
Built-in plugins implement the `Plugin` trait from `k-launcher-domain` as Rust crates compiled into the binary.
### 1. Create a new crate in the workspace
@@ -120,7 +122,7 @@ members = [
```toml
[dependencies]
k-launcher-kernel = { path = "../../k-launcher-kernel" }
k-launcher-domain = { workspace = true }
async-trait = "0.1"
```
@@ -129,8 +131,10 @@ async-trait = "0.1"
`crates/plugins/plugin-hello/src/lib.rs`:
```rust
use std::sync::Arc;
use async_trait::async_trait;
use k_launcher_kernel::{LaunchAction, Plugin, ResultId, ResultTitle, Score, SearchResult};
use k_launcher_domain::{LaunchAction, Plugin, ResultId, ResultTitle, Score, SearchResult};
pub struct HelloPlugin;
@@ -154,11 +158,10 @@ impl Plugin for HelloPlugin {
vec![SearchResult {
id: ResultId::new("hello:world"),
title: ResultTitle::new("Hello, World!"),
description: Some("A greeting from the hello plugin".to_string()),
description: Some(Arc::from("A greeting from the hello plugin")),
icon: None,
score: Score::new(80),
action: LaunchAction::CopyToClipboard("Hello, World!".to_string()),
on_select: None,
}]
}
}
@@ -192,11 +195,10 @@ plugin-hello = { path = "../plugins/plugin-hello" }
|-------|------|-------------|
| `id` | `ResultId` | Unique stable ID (e.g. `"apps:firefox"`) |
| `title` | `ResultTitle` | Primary display text |
| `description` | `Option<String>` | Secondary line shown below title |
| `icon` | `Option<String>` | Icon name or path (currently unused in renderer) |
| `description` | `Option<Arc<str>>` | Secondary line shown below title |
| `icon` | `Option<Arc<str>>` | Icon name or path (currently unused in renderer) |
| `score` | `Score(u32)` | Sort priority — higher wins |
| `action` | `LaunchAction` | What happens on `Enter` |
| `on_select` | `Option<Arc<dyn Fn()>>` | Optional side-effect on selection (e.g. frecency bump) |
### `LaunchAction` Variants
@@ -206,7 +208,6 @@ plugin-hello = { path = "../plugins/plugin-hello" }
| `SpawnInTerminal(String)` | Run command inside a terminal emulator |
| `OpenPath(String)` | Open a file or directory with `xdg-open` |
| `CopyToClipboard(String)` | Copy text to clipboard |
| `Custom(Arc<dyn Fn()>)` | Arbitrary closure |
### Scoring Guidance