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.
This commit is contained in:
2026-03-15 16:20:36 +01:00
parent 4c3be17b77
commit 1ac9dde347
19 changed files with 6077 additions and 28 deletions

View File

@@ -3,4 +3,16 @@ name = "plugin-calc"
version = "0.1.0"
edition = "2024"
[lib]
name = "plugin_calc"
path = "src/lib.rs"
[[bin]]
name = "plugin-calc"
path = "src/main.rs"
[dependencies]
async-trait = { workspace = true }
evalexpr = "11"
k-launcher-kernel = { path = "../../k-launcher-kernel" }
tokio = { workspace = true }

View File

@@ -0,0 +1,83 @@
use std::sync::Arc;
use async_trait::async_trait;
use k_launcher_kernel::{Plugin, PluginName, ResultId, ResultTitle, Score, SearchResult};
pub struct CalcPlugin;
impl CalcPlugin {
pub fn new() -> Self {
Self
}
}
impl Default for CalcPlugin {
fn default() -> Self {
Self::new()
}
}
fn should_eval(query: &str) -> bool {
query
.chars()
.next()
.map(|c| c.is_ascii_digit() || c == '(' || c == '-')
.unwrap_or(false)
|| query.starts_with('=')
}
#[async_trait]
impl Plugin for CalcPlugin {
fn name(&self) -> PluginName {
"calc"
}
async fn search(&self, query: &str) -> Vec<SearchResult> {
if !should_eval(query) {
return vec![];
}
let expr = query.strip_prefix('=').unwrap_or(query);
match evalexpr::eval_number(expr) {
Ok(n) if n.is_finite() => {
let display = if n.fract() == 0.0 {
format!("= {}", n as i64)
} else {
format!("= {n}")
};
vec![SearchResult {
id: ResultId::new("calc-result"),
title: ResultTitle::new(display),
description: None,
icon: None,
score: Score::new(90),
on_execute: Arc::new(|| {}),
}]
}
_ => vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn calc_valid_expr() {
let p = CalcPlugin::new();
let results = p.search("2+2").await;
assert_eq!(results[0].title.as_str(), "= 4");
}
#[tokio::test]
async fn calc_non_numeric_returns_empty() {
let p = CalcPlugin::new();
assert!(p.search("firefox").await.is_empty());
}
#[tokio::test]
async fn calc_bad_expr_returns_empty() {
let p = CalcPlugin::new();
assert!(p.search("1/0").await.is_empty());
}
}

View File

@@ -1,3 +1 @@
fn main() {
println!("Hello, world!");
}
fn main() {}