Start using a workspace so I can be a good organized boy
This commit is contained in:
parent
e77828b9ab
commit
3177c4f3f6
271
Cargo.lock
generated
271
Cargo.lock
generated
@ -44,12 +44,6 @@ version = "2.9.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@ -126,39 +120,6 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flume"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"nanorand",
|
|
||||||
"spin",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fragile"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -166,7 +127,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -203,12 +163,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-sink"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -221,13 +175,9 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
@ -298,10 +248,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -582,16 +530,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.77"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "khronos_api"
|
name = "khronos_api"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
@ -614,16 +552,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.27"
|
||||||
@ -665,15 +593,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nanorand"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@ -683,12 +602,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "option-ext"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -775,51 +688,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "relm4"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6bae902de22fd92e62641f047975abf228573425b9b8de175e8ab5b6cda10379"
|
|
||||||
dependencies = [
|
|
||||||
"flume",
|
|
||||||
"fragile",
|
|
||||||
"futures",
|
|
||||||
"gtk4",
|
|
||||||
"once_cell",
|
|
||||||
"relm4-css",
|
|
||||||
"relm4-macros",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "relm4-components"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3155d84425d33cdc62929ed2c79bfcfdcb2fff6340c5f7041ed24b56577c9d57"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"relm4",
|
|
||||||
"tracker",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "relm4-css"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37dbe7a114855a22618f0e13595ce6b3f165478c13c2dfc4f4f99614da105797"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "relm4-macros"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "175fce497fc6f11dde7ea56daa30ff7ad29a534bbc209d59d766659c880ba5f1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
@ -844,12 +712,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
@ -897,15 +759,6 @@ version = "1.15.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.9.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.106"
|
version = "2.0.106"
|
||||||
@ -1016,57 +869,6 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-attributes",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-attributes"
|
|
||||||
version = "0.1.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracker"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ce5c98457ff700aaeefcd4a4a492096e78a2af1dd8523c66e94a3adb0fdbd415"
|
|
||||||
dependencies = [
|
|
||||||
"tracker-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracker-macros"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc19eb2373ccf3d1999967c26c3d44534ff71ae5d8b9dacf78f4b13132229e48"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -1096,76 +898,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "waycast-core"
|
||||||
version = "0.2.100"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
[[package]]
|
||||||
|
name = "waycast-gtk"
|
||||||
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"gio",
|
||||||
"once_cell",
|
"glib",
|
||||||
"wasm-bindgen-macro",
|
"gtk4",
|
||||||
|
"gtk4-layer-shell",
|
||||||
|
"waycast-core",
|
||||||
|
"waycast-plugins",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "waycast-plugins"
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"log",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-backend",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "waycast"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
"gtk4",
|
|
||||||
"gtk4-layer-shell",
|
|
||||||
"relm4",
|
|
||||||
"relm4-components",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracker",
|
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"waycast-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
23
Cargo.toml
23
Cargo.toml
@ -1,16 +1,7 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "waycast"
|
members = [
|
||||||
version = "0.1.0"
|
"waycast-core",
|
||||||
edition = "2024"
|
"waycast-plugins",
|
||||||
|
"waycast-gtk"
|
||||||
[dependencies]
|
]
|
||||||
directories = "6.0.0"
|
resolver = "2"
|
||||||
gio = "0.21.1"
|
|
||||||
glib = "0.21.1"
|
|
||||||
gtk = { version = "0.10.0", package = "gtk4" }
|
|
||||||
gtk4-layer-shell = "0.6.1"
|
|
||||||
relm4 = "0.10.0"
|
|
||||||
relm4-components = "0.10.0"
|
|
||||||
tokio = { version = "1.0", features = ["rt", "time", "macros"] }
|
|
||||||
tracker = "0.2.2"
|
|
||||||
walkdir = "2.5.0"
|
|
||||||
|
159
Makefile
Normal file
159
Makefile
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
# Waycast - Makefile for development convenience
|
||||||
|
.PHONY: help build run test clean install dev release check fmt lint fix deps docker
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
help: ## Show this help message
|
||||||
|
@echo "Waycast Development Commands:"
|
||||||
|
@echo ""
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
||||||
|
@echo ""
|
||||||
|
@echo "Examples:"
|
||||||
|
@echo " make dev # Start development"
|
||||||
|
@echo " make build-all # Build everything"
|
||||||
|
@echo " make install # Install to system"
|
||||||
|
|
||||||
|
# Development
|
||||||
|
run: ## Run waycast GUI
|
||||||
|
cargo run -p waycast-gtk
|
||||||
|
|
||||||
|
run-daemon: ## Run waycast daemon (when implemented)
|
||||||
|
@echo "Daemon not yet implemented"
|
||||||
|
# cargo run -p waycast-daemon
|
||||||
|
|
||||||
|
# Building
|
||||||
|
build: ## Build waycast GUI (debug)
|
||||||
|
cargo build -p waycast-gtk
|
||||||
|
|
||||||
|
build-all: ## Build all crates (debug)
|
||||||
|
cargo build --workspace
|
||||||
|
|
||||||
|
build-core: ## Build core library only
|
||||||
|
cargo build -p waycast-core
|
||||||
|
|
||||||
|
build-plugins: ## Build plugins only
|
||||||
|
cargo build -p waycast-plugins
|
||||||
|
|
||||||
|
# Release builds
|
||||||
|
release: ## Build waycast GUI (optimized)
|
||||||
|
cargo build -p waycast-gtk --release
|
||||||
|
|
||||||
|
release-all: ## Build all crates (optimized)
|
||||||
|
cargo build --workspace --release
|
||||||
|
|
||||||
|
# Testing & Quality
|
||||||
|
test: ## Run all tests
|
||||||
|
cargo test --workspace
|
||||||
|
|
||||||
|
test-core: ## Run core tests only
|
||||||
|
cargo test -p waycast-core
|
||||||
|
|
||||||
|
check: ## Quick compile check
|
||||||
|
cargo check --workspace
|
||||||
|
|
||||||
|
fmt: ## Format all code
|
||||||
|
cargo fmt --all
|
||||||
|
|
||||||
|
lint: ## Run clippy lints
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
fix: ## Auto-fix linting issues
|
||||||
|
cargo clippy --workspace --all-targets --all-features --fix --allow-dirty --allow-staged
|
||||||
|
cargo fmt --all
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
deps: ## Update dependencies
|
||||||
|
cargo update
|
||||||
|
|
||||||
|
deps-audit: ## Check for security vulnerabilities
|
||||||
|
cargo audit
|
||||||
|
|
||||||
|
deps-unused: ## Check for unused dependencies (requires cargo-machete)
|
||||||
|
cargo machete
|
||||||
|
|
||||||
|
# Installation & Packaging
|
||||||
|
install: release ## Install waycast to system
|
||||||
|
cargo install --path waycast-gtk --force
|
||||||
|
|
||||||
|
install-deps: ## Install required system dependencies (Debian/Ubuntu)
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y build-essential libgtk-4-dev libadwaita-1-dev pkg-config
|
||||||
|
|
||||||
|
uninstall: ## Remove waycast from system
|
||||||
|
cargo uninstall waycast
|
||||||
|
|
||||||
|
# Development Environment
|
||||||
|
setup: install-deps ## Set up development environment
|
||||||
|
rustup update
|
||||||
|
rustup component add rustfmt clippy
|
||||||
|
cargo install cargo-watch cargo-audit cargo-machete
|
||||||
|
@echo "Development environment ready!"
|
||||||
|
|
||||||
|
# Cleaning
|
||||||
|
clean: ## Clean build artifacts
|
||||||
|
cargo clean
|
||||||
|
|
||||||
|
clean-all: clean ## Deep clean (including cache)
|
||||||
|
rm -rf target/
|
||||||
|
rm -rf ~/.cargo/registry/cache/
|
||||||
|
|
||||||
|
# Future: Packaging & Distribution
|
||||||
|
deb: release ## Build Debian package (future)
|
||||||
|
@echo "Debian packaging not yet implemented"
|
||||||
|
# Use cargo-deb when ready
|
||||||
|
|
||||||
|
rpm: release ## Build RPM package (future)
|
||||||
|
@echo "RPM packaging not yet implemented"
|
||||||
|
# Use cargo-rpm when ready
|
||||||
|
|
||||||
|
flatpak: release ## Build Flatpak (future)
|
||||||
|
@echo "Flatpak packaging not yet implemented"
|
||||||
|
|
||||||
|
# Future: System Integration
|
||||||
|
enable-daemon: ## Enable waycast daemon service (future)
|
||||||
|
@echo "Daemon service not yet implemented"
|
||||||
|
# systemctl --user enable waycast-daemon
|
||||||
|
|
||||||
|
disable-daemon: ## Disable waycast daemon service (future)
|
||||||
|
@echo "Daemon service not yet implemented"
|
||||||
|
# systemctl --user disable waycast-daemon
|
||||||
|
|
||||||
|
# Docker (for CI/testing)
|
||||||
|
docker-build: ## Build in Docker container
|
||||||
|
docker build -t waycast-build .
|
||||||
|
|
||||||
|
docker-test: ## Test in Docker container
|
||||||
|
docker run --rm waycast-build make test
|
||||||
|
|
||||||
|
# Performance & Profiling
|
||||||
|
bench: ## Run benchmarks
|
||||||
|
cargo bench --workspace
|
||||||
|
|
||||||
|
profile: ## Profile the application (requires cargo-flamegraph)
|
||||||
|
cargo flamegraph --bin waycast
|
||||||
|
|
||||||
|
size: ## Check binary size
|
||||||
|
@echo "Binary sizes:"
|
||||||
|
@ls -lh target/release/waycast 2>/dev/null || echo "Run 'make release' first"
|
||||||
|
|
||||||
|
# Development workflow shortcuts
|
||||||
|
quick: fmt check ## Quick development check (format + compile)
|
||||||
|
|
||||||
|
full: clean fmt lint test build-all ## Full development check
|
||||||
|
|
||||||
|
ci: fmt lint test build-all ## CI pipeline simulation
|
||||||
|
|
||||||
|
# Git hooks
|
||||||
|
hooks: ## Install git hooks
|
||||||
|
@echo "#!/bin/sh" > .git/hooks/pre-commit
|
||||||
|
@echo "make fmt lint" >> .git/hooks/pre-commit
|
||||||
|
@chmod +x .git/hooks/pre-commit
|
||||||
|
@echo "Git hooks installed!"
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs: ## Build and open documentation
|
||||||
|
cargo doc --workspace --open
|
||||||
|
|
||||||
|
# Development tools installation
|
||||||
|
tools: ## Install useful development tools
|
||||||
|
cargo install cargo-watch cargo-audit cargo-machete cargo-flamegraph cargo-deb cargo-outdated
|
||||||
|
@echo "Development tools installed!"
|
71
README.md
71
README.md
@ -1,3 +1,70 @@
|
|||||||
# WayCast
|
# Waycast
|
||||||
|
|
||||||
I already ordered the programmer socks
|
A launcher for Wayland that doesn't suck. Think Raycast but for Linux.
|
||||||
|
|
||||||
|
I already ordered the programmer socks.
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
Waycast is an application launcher built for Wayland desktops. It's fast, extensible, and designed to get out of your way while helping you find what you need.
|
||||||
|
|
||||||
|
**Current features:**
|
||||||
|
- Search and launch desktop applications
|
||||||
|
- Search files in your home directories (Documents, Pictures, Music, Videos)
|
||||||
|
- Fuzzy search that actually works
|
||||||
|
- Fast startup with background file indexing
|
||||||
|
- GTK4 with proper layer shell integration
|
||||||
|
|
||||||
|
**Planned features:**
|
||||||
|
- Background daemon for instant launches
|
||||||
|
- Plugin system for extensions
|
||||||
|
- Calculator, clipboard history, system controls
|
||||||
|
- Terminal UI for SSH sessions
|
||||||
|
- Web search integration
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
This is a Cargo workspace with three main crates:
|
||||||
|
|
||||||
|
- **waycast-core** - The launcher engine (traits, logic, no UI)
|
||||||
|
- **waycast-plugins** - Plugin implementations (desktop apps, file search)
|
||||||
|
- **waycast-gtk** - GTK4 UI and main binary
|
||||||
|
|
||||||
|
### Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make help # See all available commands
|
||||||
|
make quick # Format code + compile check
|
||||||
|
make test # Run tests (that I don't have yet)
|
||||||
|
make build-all # Build everything
|
||||||
|
make install # Install to system
|
||||||
|
```
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
waycast/
|
||||||
|
├── waycast-core/ # Core launcher logic
|
||||||
|
├── waycast-plugins/ # Plugin implementations
|
||||||
|
└── waycast-gtk/ # GTK UI (main app)
|
||||||
|
```
|
||||||
|
|
||||||
|
The core is deliberately minimal and UI-agnostic. Plugins depend on core. UI depends on both core and plugins. Nothing depends on the UI.
|
||||||
|
|
||||||
|
## Why Another Launcher?
|
||||||
|
|
||||||
|
Linux desktop launchers are either too basic (dmenu, wofi) or too bloated (some KDE thing with 47 configuration tabs). Raycast nailed the UX on macOS, but there's no good equivalent for Linux.
|
||||||
|
|
||||||
|
Waycast aims to be:
|
||||||
|
- **Fast** - Sub-100ms search responses, instant startup
|
||||||
|
- **Clean** - Good defaults, minimal configuration needed
|
||||||
|
- **Extensible** - Plugin system for custom functionality
|
||||||
|
- **Native** - Proper Wayland integration, not an Electron app
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
TBA
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
TBA
|
30
src/lib.rs
30
src/lib.rs
@ -1,30 +0,0 @@
|
|||||||
pub mod launcher;
|
|
||||||
pub mod plugins;
|
|
||||||
pub mod ui;
|
|
||||||
pub mod util;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum LaunchError {
|
|
||||||
CouldNotLaunch(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LauncherListItem {
|
|
||||||
fn id(&self) -> String;
|
|
||||||
fn title(&self) -> String;
|
|
||||||
fn description(&self) -> Option<String>;
|
|
||||||
fn execute(&self) -> Result<(), LaunchError>;
|
|
||||||
fn icon(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LauncherPlugin {
|
|
||||||
fn init(&self);
|
|
||||||
fn name(&self) -> String;
|
|
||||||
fn priority(&self) -> i32;
|
|
||||||
fn description(&self) -> Option<String>;
|
|
||||||
// Prefix to isolate results to only use this plugin
|
|
||||||
fn prefix(&self) -> Option<String>;
|
|
||||||
// Only search/use this plugin if the prefix was typed
|
|
||||||
fn by_prefix_only(&self) -> bool;
|
|
||||||
// Actual item searching functions
|
|
||||||
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>>;
|
|
||||||
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>>;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
pub mod gtk;
|
|
||||||
|
|
||||||
// Re-export commonly used items
|
|
||||||
pub use gtk::GtkLauncherUI;
|
|
7
waycast-core/Cargo.toml
Normal file
7
waycast-core/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "waycast-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# No dependencies for core traits and launcher logic
|
@ -1,7 +1,33 @@
|
|||||||
use crate::{LauncherListItem, LauncherPlugin};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LaunchError {
|
||||||
|
CouldNotLaunch(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LauncherListItem {
|
||||||
|
fn id(&self) -> String;
|
||||||
|
fn title(&self) -> String;
|
||||||
|
fn description(&self) -> Option<String>;
|
||||||
|
fn execute(&self) -> Result<(), LaunchError>;
|
||||||
|
fn icon(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LauncherPlugin {
|
||||||
|
fn init(&self);
|
||||||
|
fn name(&self) -> String;
|
||||||
|
fn priority(&self) -> i32;
|
||||||
|
fn description(&self) -> Option<String>;
|
||||||
|
// Prefix to isolate results to only use this plugin
|
||||||
|
fn prefix(&self) -> Option<String>;
|
||||||
|
// Only search/use this plugin if the prefix was typed
|
||||||
|
fn by_prefix_only(&self) -> bool;
|
||||||
|
// Actual item searching functions
|
||||||
|
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>>;
|
||||||
|
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>>;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WaycastLauncher {
|
pub struct WaycastLauncher {
|
||||||
plugins: Vec<Arc<dyn LauncherPlugin>>,
|
plugins: Vec<Arc<dyn LauncherPlugin>>,
|
||||||
plugins_show_always: Vec<Arc<dyn LauncherPlugin>>,
|
plugins_show_always: Vec<Arc<dyn LauncherPlugin>>,
|
||||||
@ -20,9 +46,7 @@ impl WaycastLauncher {
|
|||||||
current_results_by_id: HashMap::new(),
|
current_results_by_id: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl WaycastLauncher {
|
|
||||||
pub fn add_plugin(mut self, plugin: Box<dyn LauncherPlugin>) -> Self {
|
pub fn add_plugin(mut self, plugin: Box<dyn LauncherPlugin>) -> Self {
|
||||||
let p: Arc<dyn LauncherPlugin> = plugin.into();
|
let p: Arc<dyn LauncherPlugin> = plugin.into();
|
||||||
if !p.by_prefix_only() {
|
if !p.by_prefix_only() {
|
||||||
@ -87,23 +111,25 @@ impl WaycastLauncher {
|
|||||||
&self.current_results
|
&self.current_results
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_item(&self, index: usize) -> Result<(), crate::LaunchError> {
|
pub fn execute_item(&self, index: usize) -> Result<(), LaunchError> {
|
||||||
if let Some(item) = self.current_results.get(index) {
|
if let Some(item) = self.current_results.get(index) {
|
||||||
item.execute()
|
item.execute()
|
||||||
} else {
|
} else {
|
||||||
Err(crate::LaunchError::CouldNotLaunch("Invalid index".into()))
|
Err(LaunchError::CouldNotLaunch("Invalid index".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_item_by_id(&self, id: &str) -> Result<(), crate::LaunchError> {
|
pub fn execute_item_by_id(&self, id: &str) -> Result<(), LaunchError> {
|
||||||
if let Some(&index) = self.current_results_by_id.get(id) {
|
if let Some(&index) = self.current_results_by_id.get(id) {
|
||||||
if let Some(item) = self.current_results.get(index) {
|
if let Some(item) = self.current_results.get(index) {
|
||||||
item.execute()
|
item.execute()
|
||||||
} else {
|
} else {
|
||||||
Err(crate::LaunchError::CouldNotLaunch("Item index out of bounds".into()))
|
Err(LaunchError::CouldNotLaunch(
|
||||||
|
"Item index out of bounds".into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(crate::LaunchError::CouldNotLaunch("Item not found".into()))
|
Err(LaunchError::CouldNotLaunch("Item not found".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
waycast-gtk/Cargo.toml
Normal file
16
waycast-gtk/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "waycast-gtk"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "waycast"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
waycast-core = { path = "../waycast-core" }
|
||||||
|
waycast-plugins = { path = "../waycast-plugins" }
|
||||||
|
gio = "0.21.1"
|
||||||
|
glib = "0.21.1"
|
||||||
|
gtk = { version = "0.10.0", package = "gtk4" }
|
||||||
|
gtk4-layer-shell = "0.6.1"
|
@ -1,8 +1,11 @@
|
|||||||
use gtk::Application;
|
mod ui;
|
||||||
|
mod util;
|
||||||
|
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use waycast::launcher::WaycastLauncher;
|
use gtk::Application;
|
||||||
use waycast::plugins;
|
use ui::gtk::GtkLauncherUI;
|
||||||
use waycast::ui::gtk::GtkLauncherUI;
|
use waycast_core::WaycastLauncher;
|
||||||
|
use waycast_plugins;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = Application::builder()
|
let app = Application::builder()
|
||||||
@ -10,7 +13,7 @@ fn main() {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
app.connect_activate(|app| {
|
app.connect_activate(|app| {
|
||||||
let mut file_search_plugin = plugins::file_search::new();
|
let mut file_search_plugin = waycast_plugins::file_search::new();
|
||||||
|
|
||||||
match file_search_plugin.add_search_path("/home/javi/working-files/DJ Music/") {
|
match file_search_plugin.add_search_path("/home/javi/working-files/DJ Music/") {
|
||||||
Err(e) => eprintln!("{}", e),
|
Err(e) => eprintln!("{}", e),
|
||||||
@ -19,7 +22,7 @@ fn main() {
|
|||||||
|
|
||||||
// Create the core launcher
|
// Create the core launcher
|
||||||
let launcher = WaycastLauncher::new()
|
let launcher = WaycastLauncher::new()
|
||||||
.add_plugin(Box::new(plugins::drun::new()))
|
.add_plugin(Box::new(waycast_plugins::drun::new()))
|
||||||
.add_plugin(Box::new(file_search_plugin))
|
.add_plugin(Box::new(file_search_plugin))
|
||||||
.init();
|
.init();
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
use crate::launcher::WaycastLauncher;
|
|
||||||
use gio::ListStore;
|
|
||||||
use gio::prelude::ApplicationExt;
|
use gio::prelude::ApplicationExt;
|
||||||
|
use gio::ListStore;
|
||||||
use gtk::gdk::Texture;
|
use gtk::gdk::Texture;
|
||||||
use gtk::gdk_pixbuf::Pixbuf;
|
use gtk::gdk_pixbuf::Pixbuf;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
@ -14,6 +13,7 @@ use layerShell::LayerShell;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use waycast_core::WaycastLauncher;
|
||||||
|
|
||||||
// GObject wrapper to store LauncherListItem in GTK's model system
|
// GObject wrapper to store LauncherListItem in GTK's model system
|
||||||
mod imp {
|
mod imp {
|
||||||
@ -321,8 +321,12 @@ impl GtkLauncherUI {
|
|||||||
let mut launcher_ref = launcher.borrow_mut();
|
let mut launcher_ref = launcher.borrow_mut();
|
||||||
let results = launcher_ref.get_default_results();
|
let results = launcher_ref.get_default_results();
|
||||||
for entry in results.iter() {
|
for entry in results.iter() {
|
||||||
let item_obj =
|
let item_obj = LauncherItemObject::new(
|
||||||
LauncherItemObject::new(entry.title(), entry.description(), entry.icon(), entry.id());
|
entry.title(),
|
||||||
|
entry.description(),
|
||||||
|
entry.icon(),
|
||||||
|
entry.id(),
|
||||||
|
);
|
||||||
list_store.append(&item_obj);
|
list_store.append(&item_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,9 +336,7 @@ impl GtkLauncherUI {
|
|||||||
}
|
}
|
||||||
drop(launcher_ref); // Release the borrow
|
drop(launcher_ref); // Release the borrow
|
||||||
|
|
||||||
Self {
|
Self { window }
|
||||||
window,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
waycast-gtk/src/ui/mod.rs
Normal file
1
waycast-gtk/src/ui/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod gtk;
|
12
waycast-plugins/Cargo.toml
Normal file
12
waycast-plugins/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "waycast-plugins"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
waycast-core = { path = "../waycast-core" }
|
||||||
|
directories = "6.0.0"
|
||||||
|
gio = "0.21.1"
|
||||||
|
glib = "0.21.1"
|
||||||
|
tokio = { version = "1.0", features = ["rt", "rt-multi-thread", "time", "macros", "sync"] }
|
||||||
|
walkdir = "2.5.0"
|
@ -1,6 +1,5 @@
|
|||||||
use crate::LauncherPlugin;
|
use gio::{prelude::*, AppInfo, DesktopAppInfo, Icon};
|
||||||
use crate::{LaunchError, LauncherListItem};
|
use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin};
|
||||||
use gio::{AppInfo, DesktopAppInfo, Icon, prelude::*};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DesktopEntry {
|
pub struct DesktopEntry {
|
@ -1,5 +1,5 @@
|
|||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use gio::prelude::{AppInfoExt, FileExt};
|
use gio::prelude::FileExt;
|
||||||
use glib::object::Cast;
|
use glib::object::Cast;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@ -8,7 +8,7 @@ use std::time::Duration;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
use crate::{LaunchError, LauncherListItem, LauncherPlugin};
|
use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct FileEntry {
|
struct FileEntry {
|
||||||
@ -81,15 +81,6 @@ impl LauncherListItem for FileEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: There should be a method add_search_path to add new paths
|
|
||||||
// when the plugin is getting initialized. After all paths are
|
|
||||||
// added I should get the lowest common denominator. So for example
|
|
||||||
// if we have:
|
|
||||||
// $HOME/Documents
|
|
||||||
// $HOME/Documents/wallpapers
|
|
||||||
// Then we should just keep $HOME/Documents since wallpapers
|
|
||||||
// will be included in it anyways
|
|
||||||
|
|
||||||
pub fn default_search_list() -> Vec<PathBuf> {
|
pub fn default_search_list() -> Vec<PathBuf> {
|
||||||
if let Some(ud) = UserDirs::new() {
|
if let Some(ud) = UserDirs::new() {
|
||||||
let mut paths: Vec<PathBuf> = Vec::new();
|
let mut paths: Vec<PathBuf> = Vec::new();
|
||||||
@ -124,6 +115,7 @@ pub fn new() -> FileSearchPlugin {
|
|||||||
files: Arc::new(Mutex::new(Vec::new())),
|
files: Arc::new(Mutex::new(Vec::new())),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileSearchPlugin {
|
pub struct FileSearchPlugin {
|
||||||
search_paths: Vec<PathBuf>,
|
search_paths: Vec<PathBuf>,
|
||||||
skip_dirs: Vec<String>,
|
skip_dirs: Vec<String>,
|
Loading…
x
Reference in New Issue
Block a user