├── .envrc ├── lla ├── .gitignore ├── src │ ├── utils │ │ └── mod.rs │ ├── commands │ │ ├── mod.rs │ │ └── plugin_utils.rs │ ├── lister │ │ ├── mod.rs │ │ └── basic.rs │ ├── filter │ │ ├── mod.rs │ │ ├── extension.rs │ │ ├── regex_filter.rs │ │ ├── glob_filter.rs │ │ ├── case_insensitive.rs │ │ ├── pattern.rs │ │ └── composite.rs │ ├── formatter │ │ ├── mod.rs │ │ ├── default.rs │ │ ├── column_config.rs │ │ ├── fuzzy.rs │ │ ├── csv.rs │ │ ├── grid.rs │ │ └── recursive.rs │ ├── main.rs │ ├── sorter │ │ ├── size.rs │ │ ├── date.rs │ │ ├── alphabetical.rs │ │ └── mod.rs │ └── error.rs ├── README.md └── Cargo.toml ├── plugins ├── sizeviz │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── Cargo.lock ├── categorizer │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── Cargo.lock ├── dirs_meta │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── Cargo.lock ├── file_hash │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── file_meta │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── file_tagger │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── git_status │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── Cargo.lock ├── code_complexity │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── Cargo.lock ├── keyword_search │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── last_git_commit │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── code_snippet_extractor │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── duplicate_file_detector │ ├── .gitignore │ ├── Cargo.toml │ └── README.md ├── file_organizer │ ├── src │ │ └── strategies │ │ │ ├── mod.rs │ │ │ ├── size.rs │ │ │ ├── type.rs │ │ │ ├── date.rs │ │ │ └── extension.rs │ └── Cargo.toml ├── file_remover │ ├── Cargo.toml │ └── README.md ├── flush_dns │ ├── Cargo.toml │ └── README.md ├── file_mover │ ├── Cargo.toml │ └── README.md ├── file_copier │ ├── Cargo.toml │ └── README.md ├── kill_process │ └── Cargo.toml ├── jwt │ ├── Cargo.toml │ └── README.md ├── google_meet │ ├── Cargo.toml │ └── README.md ├── npm │ ├── Cargo.toml │ └── README.md ├── youtube │ ├── Cargo.toml │ └── README.md └── google_search │ ├── Cargo.toml │ └── README.md ├── lla_plugin_interface ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs └── src │ └── plugin.proto ├── .gitignore ├── lla_plugin_utils ├── src │ ├── ui │ │ ├── mod.rs │ │ ├── selector.rs │ │ └── text.rs │ ├── syntax.rs │ ├── actions.rs │ └── format.rs ├── Cargo.toml └── README.md ├── shell.nix ├── default.nix ├── rust-toolchain.toml ├── scripts ├── generate_proto.sh ├── generate_plugins.sh └── build_plugins.sh ├── nfpm.yaml ├── LICENSE ├── flake.nix ├── Cargo.toml ├── .github ├── workflows │ ├── update-proto.yml │ ├── generate-completions.yml │ ├── README.md │ └── release-themes.yml └── scripts │ └── release_helpers.sh ├── flake.lock ├── themes ├── moonlight.toml ├── zen.toml ├── circuit_dreams.toml ├── gruvbox_dark.toml ├── nord.toml └── vesper.toml ├── install.sh └── plugins.md /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /lla/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /plugins/sizeviz/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/categorizer/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/dirs_meta/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/file_hash/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/file_meta/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/file_tagger/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/git_status/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /lla_plugin_interface/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /plugins/code_complexity/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/keyword_search/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/last_git_commit/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/code_snippet_extractor/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /plugins/duplicate_file_detector/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store -------------------------------------------------------------------------------- /lla/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cache; 2 | pub mod color; 3 | pub mod icons; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target/ 3 | 4 | .cache/ 5 | 6 | completions/* 7 | !completions/*.bash 8 | !completions/*.fish 9 | !completions/_lla 10 | !completions/*.ps1 11 | !completions/*.elv -------------------------------------------------------------------------------- /lla/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod args; 2 | pub mod command_handler; 3 | pub mod diff; 4 | pub mod file_utils; 5 | pub mod init_wizard; 6 | pub mod jump; 7 | pub mod plugin_utils; 8 | pub mod search; 9 | -------------------------------------------------------------------------------- /lla_plugin_interface/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "lla_plugin_interface" 7 | version = "0.1.1" 8 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod components; 2 | pub mod live_suggest; 3 | pub mod selector; 4 | pub mod text; 5 | 6 | pub use live_suggest::interactive_suggest; 7 | pub use text::{format_size, TextBlock, TextStyle}; 8 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | fetchTarball { 3 | url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; 4 | sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } 5 | ) { 6 | src = ./.; 7 | }).shellNix 8 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | fetchTarball { 3 | url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; 4 | sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } 5 | ) { 6 | src = ./.; 7 | }).defaultNix 8 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = ["rustfmt", "clippy"] 4 | targets = [ 5 | "x86_64-unknown-linux-gnu", 6 | "x86_64-apple-darwin", 7 | "aarch64-unknown-linux-gnu", 8 | "aarch64-apple-darwin", 9 | "x86_64-pc-windows-msvc", 10 | ] 11 | -------------------------------------------------------------------------------- /scripts/generate_proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd "$(dirname "$0")/.." 5 | 6 | # Build with the regenerate-protobuf feature 7 | echo "Building with regenerate-protobuf feature..." 8 | cargo build --features regenerate-protobuf 9 | 10 | echo "Successfully generated protobuf bindings" 11 | -------------------------------------------------------------------------------- /lla/src/lister/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use std::path::PathBuf; 3 | 4 | pub trait FileLister { 5 | fn list_files( 6 | &self, 7 | directory: &str, 8 | recursive: bool, 9 | depth: Option, 10 | ) -> Result>; 11 | } 12 | 13 | pub mod archive; 14 | mod basic; 15 | mod fuzzy; 16 | mod recursive; 17 | 18 | pub use basic::BasicLister; 19 | pub use fuzzy::FuzzyLister; 20 | pub use recursive::RecursiveLister; 21 | -------------------------------------------------------------------------------- /plugins/sizeviz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sizeviz" 3 | description = "File size visualizer plugin for lla" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | prost = "0.12" 12 | bytes = "1.5" 13 | lazy_static = "1.4" 14 | parking_lot = "0.12" 15 | serde = { version = "1.0", features = ["derive"] } 16 | 17 | [lib] 18 | crate-type = ["cdylib"] 19 | -------------------------------------------------------------------------------- /plugins/git_status/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "git_status" 3 | description = "Shows Git repository status information" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | colored = "2.0.0" 11 | prost = "0.12" 12 | bytes = "1.5" 13 | lazy_static = "1.4" 14 | parking_lot = "0.12" 15 | serde = { version = "1.0", features = ["derive"] } 16 | 17 | [lib] 18 | crate-type = ["cdylib"] 19 | -------------------------------------------------------------------------------- /plugins/file_tagger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_tagger" 3 | description = "Add and manage tags for files" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | colored = "2.0.0" 11 | prost = "0.12" 12 | bytes = "1.5" 13 | lazy_static = "1.4" 14 | parking_lot = "0.12" 15 | serde = { version = "1.0", features = ["derive"] } 16 | dirs = "5.0" 17 | 18 | [lib] 19 | crate-type = ["cdylib"] 20 | -------------------------------------------------------------------------------- /plugins/file_organizer/src/strategies/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod date; 2 | pub mod extension; 3 | pub mod size; 4 | pub mod r#type; 5 | 6 | use std::path::{Path, PathBuf}; 7 | 8 | pub trait OrganizationStrategy { 9 | fn organize(&self, dir: &Path, dry_run: bool) -> Result, String>; 10 | fn execute_moves(&self, moves: Vec<(PathBuf, PathBuf)>) -> Result<(), String>; 11 | } 12 | 13 | pub use date::DateStrategy; 14 | pub use extension::ExtensionStrategy; 15 | pub use r#type::TypeStrategy; 16 | pub use size::SizeStrategy; 17 | -------------------------------------------------------------------------------- /plugins/file_meta/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_meta" 3 | description = "Displays the file metadata of each file" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | chrono = "0.4.38" 11 | colored = "2.0" 12 | prost = "0.12" 13 | bytes = "1.5" 14 | serde = { version = "1.0", features = ["derive"] } 15 | lazy_static = "1.4" 16 | parking_lot = "0.12" 17 | 18 | [lib] 19 | crate-type = ["cdylib"] 20 | -------------------------------------------------------------------------------- /lla_plugin_interface/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lla_plugin_interface" 3 | description = "Interface for lla plugins" 4 | version.workspace = true 5 | edition.workspace = true 6 | authors.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | keywords = ["cli", "ls", "blazing-fast"] 10 | categories = ["command-line-utilities"] 11 | 12 | [dependencies] 13 | serde.workspace = true 14 | prost.workspace = true 15 | 16 | [build-dependencies] 17 | prost-build.workspace = true 18 | 19 | [features] 20 | regenerate-protobuf = [] 21 | -------------------------------------------------------------------------------- /plugins/file_hash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_hash" 3 | description = "Displays the hash of each file" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | sha1 = "0.10.1" 9 | sha2 = "0.10.2" 10 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 11 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 12 | colored = "2.0.0" 13 | prost = "0.12" 14 | bytes = "1.5" 15 | lazy_static = "1.4" 16 | parking_lot = "0.12" 17 | serde = { version = "1.0", features = ["derive"] } 18 | 19 | [lib] 20 | crate-type = ["cdylib"] 21 | -------------------------------------------------------------------------------- /plugins/file_remover/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_remover" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "A plugin for lla that provides interactive file and directory removal" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 12 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | dialoguer = "0.11" 16 | colored = "2.0" 17 | lazy_static = "1.4" 18 | parking_lot = "0.12" 19 | -------------------------------------------------------------------------------- /plugins/file_organizer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_organizer" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "A plugin for lla that organizes files using various strategies" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 12 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 13 | colored = "2.0" 14 | lazy_static = "1.4" 15 | parking_lot = "0.12" 16 | serde = { version = "1.0", features = ["derive"] } 17 | toml = "0.8" 18 | dirs = "5.0" 19 | chrono = "0.4" 20 | -------------------------------------------------------------------------------- /plugins/categorizer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "categorizer" 3 | description = "Categorizes files based on their extensions and metadata" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | colored = "2.0" 11 | prost = "0.12" 12 | bytes = "1.5" 13 | serde = { version = "1.0", features = ["derive"] } 14 | lazy_static = "1.4" 15 | parking_lot = "0.12" 16 | toml = "0.8" 17 | dirs = "5.0" 18 | 19 | [lib] 20 | crate-type = ["cdylib"] 21 | -------------------------------------------------------------------------------- /plugins/code_complexity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "code_complexity" 3 | description = "Analyzes code complexity and provides metrics" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | colored = "2.0" 11 | prost = "0.12" 12 | bytes = "1.5" 13 | serde = { version = "1.0", features = ["derive"] } 14 | lazy_static = "1.4" 15 | parking_lot = "0.12" 16 | toml = "0.8" 17 | dirs = "5.0" 18 | 19 | [lib] 20 | crate-type = ["cdylib"] 21 | -------------------------------------------------------------------------------- /plugins/last_git_commit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "last_git_commit" 3 | description = "A plugin for the lla that provides the last git commit hash" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | prost = "0.12" 12 | bytes = "1.5" 13 | lazy_static = "1.4" 14 | parking_lot = "0.12" 15 | serde = { version = "1.0", features = ["derive"] } 16 | serde_json = "1.0" 17 | 18 | [lib] 19 | crate-type = ["cdylib"] 20 | -------------------------------------------------------------------------------- /plugins/duplicate_file_detector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "duplicate_file_detector" 3 | description = "Detects duplicate files by comparing their content hashes" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | sha2 = "0.10.2" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | colored = "2.0.0" 12 | prost = "0.12" 13 | bytes = "1.5" 14 | lazy_static = "1.4" 15 | parking_lot = "0.12" 16 | serde = { version = "1.0", features = ["derive"] } 17 | 18 | [lib] 19 | crate-type = ["cdylib"] 20 | -------------------------------------------------------------------------------- /plugins/flush_dns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flush_dns" 3 | description = "Flush DNS cache on macOS, Linux, and Windows" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | chrono = "0.4" 19 | 20 | [lib] 21 | crate-type = ["cdylib"] 22 | -------------------------------------------------------------------------------- /plugins/file_mover/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_mover" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "A plugin for lla that provides clipboard functionality for moving files and directories" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 12 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | dialoguer = "0.11" 16 | colored = "2.0" 17 | lazy_static = "1.4" 18 | parking_lot = "0.12" 19 | dirs = "5.0" 20 | -------------------------------------------------------------------------------- /plugins/file_copier/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file_copier" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "A plugin for lla that provides clipboard functionality for copying files and directories" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 12 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | dialoguer = "0.11" 16 | colored = "2.0" 17 | lazy_static = "1.4" 18 | parking_lot = "0.12" 19 | dirs = "5.0" 20 | -------------------------------------------------------------------------------- /plugins/dirs_meta/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dirs_meta" 3 | description = "Analyzes directories and shows metadata" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = { workspace = true } 9 | walkdir = { workspace = true } 10 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 11 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 12 | rayon = { workspace = true } 13 | lazy_static = "1.4" 14 | parking_lot = { workspace = true } 15 | prost = { workspace = true } 16 | bytes = "1.5" 17 | serde = { workspace = true, features = ["derive"] } 18 | 19 | [lib] 20 | crate-type = ["cdylib"] 21 | -------------------------------------------------------------------------------- /plugins/kill_process/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kill_process" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "A plugin for lla that allows killing processes with an interactive interface" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 12 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | dialoguer = "0.11" 16 | colored = "2.0" 17 | lazy_static = "1.4" 18 | parking_lot = "0.12" 19 | sysinfo = "0.33" 20 | fuzzy-matcher = "0.3.7" 21 | console = "0.15.8" 22 | -------------------------------------------------------------------------------- /plugins/jwt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jwt" 3 | description = "JWT decoder and analyzer with search and validation capabilities" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | chrono = "0.4" 19 | serde_json = "1.0" 20 | arboard = "3.3.0" 21 | base64 = "0.22" 22 | regex = "1.11" 23 | 24 | [lib] 25 | crate-type = ["cdylib"] 26 | -------------------------------------------------------------------------------- /plugins/google_meet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "google_meet" 3 | description = "Google Meet plugin for creating meeting rooms and managing links" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | arboard = "3.3.0" 19 | chrono = "0.4" 20 | uuid = { version = "1.0", features = ["v4"] } 21 | 22 | [lib] 23 | crate-type = ["cdylib"] 24 | -------------------------------------------------------------------------------- /lla/src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use std::path::PathBuf; 3 | 4 | pub trait FileFilter: Send + Sync { 5 | fn filter_files(&self, files: &[PathBuf]) -> Result>; 6 | } 7 | 8 | mod case_insensitive; 9 | mod composite; 10 | mod extension; 11 | mod glob_filter; 12 | mod pattern; 13 | mod range; 14 | mod regex_filter; 15 | 16 | pub use case_insensitive::CaseInsensitiveFilter; 17 | pub use composite::{CompositeFilter, FilterOperation}; 18 | pub use extension::ExtensionFilter; 19 | pub use glob_filter::GlobFilter; 20 | pub use pattern::PatternFilter; 21 | pub use range::{parse_size_range, parse_time_range, NumericRange, TimeRange}; 22 | pub use regex_filter::RegexFilter; 23 | -------------------------------------------------------------------------------- /plugins/npm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "npm" 3 | description = "NPM package search with bundlephobia integration and favorites management" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | chrono = "0.4" 19 | reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } 20 | serde_json = "1.0" 21 | arboard = "3.3.0" 22 | 23 | [lib] 24 | crate-type = ["cdylib"] 25 | -------------------------------------------------------------------------------- /plugins/keyword_search/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "keyword_search" 3 | description = "Searches file contents for user-specified keywords" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | regex = "1.11.1" 15 | prost = "0.12" 16 | bytes = "1.5" 17 | lazy_static = "1.4" 18 | parking_lot = "0.12" 19 | dialoguer = "0.11.0" 20 | console = "0.15.7" 21 | syntect = "5.1" 22 | chrono = "0.4" 23 | arboard = "3.3.0" 24 | itertools = "0.12.0" 25 | strip-ansi-escapes = "0.2.0" 26 | 27 | [lib] 28 | crate-type = ["cdylib"] 29 | -------------------------------------------------------------------------------- /lla/src/filter/extension.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::Result; 3 | use std::path::PathBuf; 4 | 5 | pub struct ExtensionFilter { 6 | extension: String, 7 | } 8 | 9 | impl ExtensionFilter { 10 | pub fn new(extension: String) -> Self { 11 | ExtensionFilter { extension } 12 | } 13 | } 14 | 15 | impl FileFilter for ExtensionFilter { 16 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 17 | Ok(files 18 | .iter() 19 | .filter(|file| { 20 | file.extension() 21 | .and_then(|ext| ext.to_str()) 22 | .map(|ext| ext == self.extension) 23 | .unwrap_or(false) 24 | }) 25 | .cloned() 26 | .collect()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/code_snippet_extractor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "code_snippet_extractor" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "Extract and manage code snippets with tagging support" 6 | 7 | [dependencies] 8 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 9 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 10 | colored = "2.0" 11 | toml = "0.8" 12 | serde = { version = "1.0", features = ["derive"] } 13 | dirs = "5.0" 14 | ring = "0.17" 15 | base64 = "0.21" 16 | uuid = { version = "1.4", features = ["v4"] } 17 | chrono = "0.4" 18 | syntect = "5.1" 19 | lazy_static = "1.4" 20 | parking_lot = "0.12" 21 | dialoguer = "0.11.0" 22 | fuzzy-matcher = "0.3.7" 23 | arboard = "3.3.0" 24 | serde_json = "1.0" 25 | console = "0.15.7" 26 | 27 | [lib] 28 | crate-type = ["cdylib"] 29 | -------------------------------------------------------------------------------- /plugins/youtube/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "youtube" 3 | description = "YouTube search plugin with autosuggestions and history management" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | arboard = "3.3.0" 19 | chrono = "0.4" 20 | url = "2.5" 21 | reqwest = { version = "0.11", default-features = false, features = [ 22 | "blocking", 23 | "json", 24 | "rustls-tls", 25 | ] } 26 | serde_json = "1.0" 27 | indicatif = "0.17" 28 | 29 | [lib] 30 | crate-type = ["cdylib"] 31 | -------------------------------------------------------------------------------- /plugins/google_search/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "google_search" 3 | description = "Google search with autosuggestions, search history management, and clipboard fallback" 4 | version = "0.5.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | colored = "2.0.0" 9 | lla_plugin_interface = { path = "../../lla_plugin_interface" } 10 | lla_plugin_utils = { path = "../../lla_plugin_utils" } 11 | dirs = "5.0.1" 12 | serde = { version = "1.0.200", features = ["derive"] } 13 | toml = "0.8.8" 14 | prost = "0.12" 15 | bytes = "1.5" 16 | dialoguer = "0.11.0" 17 | console = "0.15.7" 18 | arboard = "3.3.0" 19 | chrono = "0.4" 20 | url = "2.5" 21 | reqwest = { version = "0.11", default-features = false, features = [ 22 | "blocking", 23 | "json", 24 | "rustls-tls", 25 | ] } 26 | serde_json = "1.0" 27 | indicatif = "0.17" 28 | 29 | [lib] 30 | crate-type = ["cdylib"] 31 | -------------------------------------------------------------------------------- /plugins/last_git_commit/README.md: -------------------------------------------------------------------------------- 1 | # lla Last Git Commit Plugin 2 | 3 | Git history plugin for `lla` providing real-time commit tracking with rich formatting. 4 | 5 | ## Features 6 | 7 | - Short hash, author, and timestamp display 8 | - Path-specific history 9 | - Color-coded information 10 | - Multiple display formats 11 | - Smart caching and quick lookups 12 | 13 | ## Configuration 14 | 15 | Config file: `~/.config/lla/last_git_commit/config.toml` 16 | 17 | ```toml 18 | [colors] 19 | hash = "bright_yellow" 20 | author = "bright_cyan" 21 | time = "bright_green" 22 | info = "bright_blue" 23 | name = "bright_yellow" 24 | ``` 25 | 26 | ## Display Formats 27 | 28 | Default: 29 | 30 | ``` 31 | document.txt 32 | Commit: a1b2c3d 2 days ago 33 | ``` 34 | 35 | Long: 36 | 37 | ``` 38 | document.txt 39 | Commit: a1b2c3d 40 | Author: John Doe 41 | Time: 2 days ago 42 | ``` 43 | -------------------------------------------------------------------------------- /lla/src/filter/regex_filter.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::Result; 3 | use regex::Regex; 4 | use std::path::PathBuf; 5 | 6 | pub struct RegexFilter { 7 | regex: Regex, 8 | } 9 | 10 | impl RegexFilter { 11 | pub fn new(pattern: String) -> Self { 12 | RegexFilter { 13 | regex: Regex::new(&pattern).unwrap_or_else(|_| Regex::new(".*").unwrap()), 14 | } 15 | } 16 | } 17 | 18 | impl FileFilter for RegexFilter { 19 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 20 | Ok(files 21 | .iter() 22 | .filter(|file| { 23 | file.file_name() 24 | .and_then(|name| name.to_str()) 25 | .map(|name| self.regex.is_match(name)) 26 | .unwrap_or(false) 27 | }) 28 | .cloned() 29 | .collect()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lla/src/filter/glob_filter.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::Result; 3 | use glob::Pattern; 4 | use std::path::PathBuf; 5 | 6 | pub struct GlobFilter { 7 | pattern: Pattern, 8 | } 9 | 10 | impl GlobFilter { 11 | pub fn new(pattern: String) -> Self { 12 | GlobFilter { 13 | pattern: Pattern::new(&pattern).unwrap_or_else(|_| Pattern::new("*").unwrap()), 14 | } 15 | } 16 | } 17 | 18 | impl FileFilter for GlobFilter { 19 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 20 | Ok(files 21 | .iter() 22 | .filter(|file| { 23 | file.file_name() 24 | .and_then(|name| name.to_str()) 25 | .map(|name| self.pattern.matches(name)) 26 | .unwrap_or(false) 27 | }) 28 | .cloned() 29 | .collect()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lla_plugin_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lla_plugin_utils" 3 | version.workspace = true 4 | edition.workspace = true 5 | description.workspace = true 6 | authors.workspace = true 7 | license.workspace = true 8 | 9 | [dependencies] 10 | lla_plugin_interface = { path = "../lla_plugin_interface", version = "0.5.1" } 11 | serde = { workspace = true } 12 | colored = { workspace = true } 13 | toml = { workspace = true } 14 | dirs = { workspace = true } 15 | prost = { workspace = true } 16 | bytes = "1.5.0" 17 | chrono = { workspace = true } 18 | users = { workspace = true } 19 | indicatif = { workspace = true } 20 | console = "0.15.8" 21 | dialoguer = "0.11.0" 22 | syntect = { version = "5.1.0", optional = true } 23 | lazy_static = { version = "1.4", optional = true } 24 | 25 | [features] 26 | default = ["config", "ui", "format", "syntax", "interactive"] 27 | config = [] 28 | ui = [] 29 | format = [] 30 | syntax = ["syntect", "lazy_static"] 31 | interactive = [] 32 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/ui/selector.rs: -------------------------------------------------------------------------------- 1 | use super::components::LlaDialoguerTheme; 2 | use dialoguer::{MultiSelect, Select}; 3 | 4 | pub fn select_single( 5 | prompt: &str, 6 | items: &[T], 7 | default: Option, 8 | ) -> Result { 9 | let theme = LlaDialoguerTheme::default(); 10 | let mut selector = Select::with_theme(&theme).with_prompt(prompt).items(items); 11 | 12 | if let Some(default_idx) = default { 13 | selector = selector.default(default_idx); 14 | } 15 | 16 | selector 17 | .interact() 18 | .map_err(|e| format!("Failed to show selector: {}", e)) 19 | } 20 | 21 | pub fn select_multiple(prompt: &str, items: &[T]) -> Result, String> { 22 | let theme = LlaDialoguerTheme::default(); 23 | MultiSelect::with_theme(&theme) 24 | .with_prompt(prompt) 25 | .items(items) 26 | .interact() 27 | .map_err(|e| format!("Failed to show selector: {}", e)) 28 | } 29 | -------------------------------------------------------------------------------- /lla/src/formatter/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use crate::plugin::PluginManager; 3 | use lla_plugin_interface::proto::DecoratedEntry; 4 | 5 | pub trait FileFormatter { 6 | fn format_files( 7 | &self, 8 | files: &[DecoratedEntry], 9 | plugin_manager: &mut PluginManager, 10 | depth: Option, 11 | ) -> Result; 12 | } 13 | 14 | pub mod column_config; 15 | pub mod csv; 16 | mod default; 17 | mod fuzzy; 18 | mod git; 19 | mod grid; 20 | pub mod json; 21 | mod long; 22 | mod recursive; 23 | pub mod serializable; 24 | mod sizemap; 25 | mod table; 26 | mod timeline; 27 | mod tree; 28 | 29 | pub use default::DefaultFormatter; 30 | pub use fuzzy::FuzzyFormatter; 31 | pub use git::GitFormatter; 32 | pub use grid::GridFormatter; 33 | pub use long::LongFormatter; 34 | pub use recursive::RecursiveFormatter; 35 | pub use sizemap::SizeMapFormatter; 36 | pub use table::TableFormatter; 37 | pub use timeline::TimelineFormatter; 38 | pub use tree::TreeFormatter; 39 | -------------------------------------------------------------------------------- /plugins/git_status/README.md: -------------------------------------------------------------------------------- 1 | # lla Git Status Plugin 2 | 3 | Git integration plugin for `lla` providing real-time repository status with rich formatting. 4 | 5 | ## Features 6 | 7 | - Status tracking (staged, modified, untracked, conflicts) 8 | - Repository info (branch, commits, working tree) 9 | - Color-coded display 10 | - Performance optimized 11 | 12 | ## Configuration 13 | 14 | Config location: `~/.config/lla/git_status/config.toml` 15 | 16 | ```toml 17 | [colors] 18 | clean = "bright_green" 19 | modified = "bright_yellow" 20 | staged = "bright_green" 21 | untracked = "bright_blue" 22 | conflict = "bright_red" 23 | branch = "bright_cyan" 24 | commit = "bright_yellow" 25 | info = "bright_blue" 26 | name = "bright_yellow" 27 | ``` 28 | 29 | ## Display Examples 30 | 31 | Basic: 32 | 33 | ``` 34 | document.txt 35 | Git: modified, 2 staged 36 | ``` 37 | 38 | Detailed: 39 | 40 | ``` 41 | project/ 42 | Branch: main 43 | Commit: a1b2c3d Initial commit 44 | Status: 2 staged, 1 modified, 3 untracked 45 | ``` 46 | -------------------------------------------------------------------------------- /lla/src/lister/basic.rs: -------------------------------------------------------------------------------- 1 | use super::FileLister; 2 | use crate::error::Result; 3 | use std::fs; 4 | use std::path::PathBuf; 5 | 6 | pub struct BasicLister; 7 | 8 | impl FileLister for BasicLister { 9 | fn list_files( 10 | &self, 11 | directory: &str, 12 | _recursive: bool, 13 | _depth: Option, 14 | ) -> Result> { 15 | let mut files = Vec::with_capacity(16); 16 | 17 | let entries = fs::read_dir(directory)?; 18 | // No config available here; exclusion is applied later in list_and_decorate_files 19 | for entry in entries.flatten() { 20 | let p = entry.path(); 21 | // Skip current and parent dir entries if the underlying FS yields them 22 | if p.file_name() 23 | .and_then(|n| n.to_str()) 24 | .map(|n| n == "." || n == "..") 25 | .unwrap_or(false) 26 | { 27 | continue; 28 | } 29 | files.push(p); 30 | } 31 | 32 | Ok(files) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /nfpm.yaml: -------------------------------------------------------------------------------- 1 | name: lla 2 | arch: "${ARCH}" 3 | platform: linux 4 | version: "${VERSION}" 5 | section: utils 6 | priority: optional 7 | maintainer: "Achaq " 8 | description: | 9 | Blazing fast and highly customizable ls replacement with superpowers. 10 | homepage: "https://github.com/chaqchase/lla" 11 | license: "MIT" 12 | provides: 13 | - lla 14 | contents: 15 | - src: "${BINARY_PATH}" 16 | dst: "/usr/bin/lla" 17 | file_info: 18 | mode: 0755 19 | - src: "LICENSE" 20 | dst: "/usr/share/licenses/lla/LICENSE" 21 | file_info: 22 | mode: 0644 23 | - src: "completions/lla.bash" 24 | dst: "/usr/share/bash-completion/completions/lla" 25 | file_info: 26 | mode: 0644 27 | - src: "completions/_lla" 28 | dst: "/usr/share/zsh/site-functions/_lla" 29 | file_info: 30 | mode: 0644 31 | - src: "completions/lla.fish" 32 | dst: "/usr/share/fish/vendor_completions.d/lla.fish" 33 | file_info: 34 | mode: 0644 35 | - src: "completions/lla.elv" 36 | dst: "/usr/share/elvish/lib/completions/lla.elv" 37 | file_info: 38 | mode: 0644 39 | -------------------------------------------------------------------------------- /plugins/sizeviz/README.md: -------------------------------------------------------------------------------- 1 | # lla Size Visualizer Plugin 2 | 3 | File size visualization plugin for `lla` providing real-time size analysis with rich formatting. 4 | 5 | ## Features 6 | 7 | - Human-readable size formatting 8 | - Visual progress bars with Unicode blocks 9 | - Size-based color coding 10 | - Multiple display formats 11 | - Smart caching and quick analysis 12 | 13 | ## Configuration 14 | 15 | Config file: `~/.config/lla/sizeviz/config.toml` 16 | 17 | ```toml 18 | [colors] 19 | tiny = "bright_green" # ≤ 1KB 20 | small = "bright_cyan" # 1KB - 1MB 21 | medium = "bright_yellow" # 1MB - 10MB 22 | large = "bright_red" # 10MB - 100MB 23 | huge = "bright_magenta" # > 100MB 24 | info = "bright_blue" 25 | size = "bright_yellow" 26 | percentage = "bright_magenta" 27 | ``` 28 | 29 | ## Display Formats 30 | 31 | Default: 32 | 33 | ``` 34 | document.pdf 35 | █████░░░░░ 2.5 MB 36 | ``` 37 | 38 | Long: 39 | 40 | ``` 41 | ┌─ Size ──────────────────────────────────── 42 | │ ████████████░░░░░░░░░░ 25.5 MB 43 | │ 2.5% of reference (1GB) 44 | └────────────────────────────────────────── 45 | ``` 46 | -------------------------------------------------------------------------------- /lla_plugin_utils/README.md: -------------------------------------------------------------------------------- 1 | # lla_plugin_utils 2 | 3 | Utility library for building lla plugins. 4 | 5 | ## Core Components 6 | 7 | ### UI Components 8 | 9 | - `BoxComponent`: Customizable bordered text boxes 10 | - `HelpFormatter`: Command help text formatting 11 | - `KeyValue`: Key-value pair display 12 | - `List`: List display with borders 13 | - `Spinner`: Progress indicator 14 | - `TextBlock`: Styled text with colors 15 | - `InteractiveSelector`: CLI selection menus 16 | 17 | ### Plugin Infrastructure 18 | 19 | - `BasePlugin`: Base plugin implementation 20 | - `ConfigManager`: Plugin configuration handling 21 | - `ActionRegistry`: Plugin action registration and handling 22 | - `ProtobufHandler`: Protocol buffer message handling 23 | 24 | ### Code Utilities 25 | 26 | - `CodeHighlighter`: Syntax highlighting for code snippets 27 | - `format`: File metadata formatting utilities 28 | - `syntax`: Code syntax highlighting support 29 | 30 | ## Features 31 | 32 | - Configurable via TOML 33 | - Interactive CLI components 34 | - Syntax highlighting (optional) 35 | - Protobuf message handling 36 | - Action registration system 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mohamed Achaq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /plugins/file_hash/README.md: -------------------------------------------------------------------------------- 1 | # lla File Hash Plugin 2 | 3 | A high-performance file hashing plugin for `lla` that calculates secure cryptographic hashes (SHA-1 and SHA-256). 4 | 5 | ## Features 6 | 7 | - SHA-1 and SHA-256 hash calculation 8 | - Efficient buffered reading 9 | - Progress indication 10 | - Rich display formatting 11 | 12 | ## Configuration 13 | 14 | Located at `~/.config/lla/file_hash/config.toml`: 15 | 16 | ```toml 17 | [colors] 18 | sha1 = "bright_green" # SHA-1 hash color 19 | sha256 = "bright_yellow" # SHA-256 hash color 20 | success = "bright_green" # Success messages 21 | info = "bright_blue" # Information messages 22 | name = "bright_yellow" # Name highlighting 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```bash 28 | # View help information 29 | lla plugin --name file_hash --action help 30 | ``` 31 | 32 | ## Display Formats 33 | 34 | ### Default Format 35 | 36 | ``` 37 | document.pdf 38 | SHA1: a1b2c3d4 39 | SHA256: e5f6g7h8 40 | ``` 41 | 42 | ### Long Format 43 | 44 | ``` 45 | document.pdf 46 | SHA1: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 47 | SHA256: u1v2w3x4y5z6a7b8c9d0e1f2g3h4i5j6k7l8m9n0p1q2r3s4t5u6v7w8x9y0 48 | ``` 49 | -------------------------------------------------------------------------------- /plugins/dirs_meta/README.md: -------------------------------------------------------------------------------- 1 | # lla Directory Metadata Plugin 2 | 3 | Real-time directory statistics with intelligent caching. 4 | 5 | ## Features 6 | 7 | - **Analysis**: Parallel scanning, caching, configurable depth 8 | - **Statistics**: File counts, sizes, subdirectories, modification times 9 | - **Performance**: Multi-threaded, cache-optimized 10 | 11 | ## Configuration 12 | 13 | `~/.config/lla/dirs_meta/config.toml`: 14 | 15 | ```toml 16 | cache_size = 1000 # Max cached directories 17 | max_scan_depth = 100 # Max scan depth 18 | parallel_threshold = 1000 # Min entries for parallel 19 | 20 | [colors] 21 | files = "bright_cyan" 22 | dirs = "bright_green" 23 | size = "bright_yellow" 24 | time = "bright_magenta" 25 | ``` 26 | 27 | ## Usage 28 | 29 | ```bash 30 | # Show stats 31 | lla plugin --name dirs_meta --action stats --args "/path/to/directory" 32 | 33 | # Clear cache 34 | lla plugin --name dirs_meta --action clear-cache 35 | ``` 36 | 37 | ## Display Formats 38 | 39 | Default: `Documents (15 files, 2.5 GB)` 40 | 41 | Long: 42 | 43 | ``` 44 | Documents 45 | Files: 15 46 | Directories: 3 47 | Total Size: 2.5 GB 48 | Modified: 5 mins ago 49 | ``` 50 | 51 | Units: B/KB/MB/GB, seconds/minutes/hours/days ago 52 | -------------------------------------------------------------------------------- /plugins/file_meta/README.md: -------------------------------------------------------------------------------- 1 | # lla File Metadata Plugin 2 | 3 | A file metadata plugin for `lla` that provides comprehensive file information with rich formatting. 4 | 5 | ## Features 6 | 7 | - Timestamp tracking (access, modify, create) 8 | - Ownership and permission details 9 | - Size statistics 10 | - Color-coded information display 11 | 12 | ## Configuration 13 | 14 | Located at `~/.config/lla/file_meta/config.toml`: 15 | 16 | ```toml 17 | [colors] 18 | accessed = "bright_blue" # Access time color 19 | modified = "bright_green" # Modification time color 20 | created = "bright_yellow" # Creation time color 21 | ownership = "bright_magenta" # UID/GID information color 22 | size = "bright_cyan" # File size color 23 | permissions = "bright_red" # Permissions color 24 | success = "bright_green" # Success messages 25 | info = "bright_blue" # Information messages 26 | name = "bright_yellow" # Name highlighting 27 | ``` 28 | 29 | ## Usage 30 | 31 | ```bash 32 | # View help information 33 | lla plugin --name file_meta --action help 34 | ``` 35 | 36 | ## Display Format 37 | 38 | ``` 39 | document.pdf 40 | Accessed: 2024-03-15 14:30:22 41 | Modified: 2024-03-15 14:30:20 42 | Created: 2024-03-15 14:30:18 43 | UID/GID: 1000/1000 44 | Size: 1.0 MB 45 | Permissions: 644 46 | ``` 47 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | naersk.url = "github:nix-community/naersk/master"; 4 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 5 | utils.url = "github:numtide/flake-utils"; 6 | }; 7 | 8 | outputs = { self, nixpkgs, utils, naersk }: 9 | utils.lib.eachDefaultSystem (system: 10 | let 11 | pkgs = import nixpkgs { inherit system; }; 12 | naersk-lib = pkgs.callPackage naersk { }; 13 | in 14 | { 15 | defaultPackage = naersk-lib.buildPackage { 16 | src = ./.; 17 | }; 18 | 19 | devShell = with pkgs; mkShell { 20 | buildInputs = [ 21 | cargo 22 | rustc 23 | rustfmt 24 | pre-commit 25 | rustPackages.clippy 26 | protobuf 27 | ]; 28 | RUST_SRC_PATH = rustPlatform.rustLibSrc; 29 | 30 | shellHook = '' 31 | echo "Development shell activated" 32 | echo "Note: protobuf is available for regenerating bindings" 33 | echo "To regenerate protobuf files, run: cargo build --features regenerate-protobuf" 34 | ''; 35 | }; 36 | 37 | packages.withProtobuf = naersk-lib.buildPackage { 38 | src = ./.; 39 | buildInputs = with pkgs; [ protobuf ]; 40 | cargoBuildFeatures = [ "regenerate-protobuf" ]; 41 | }; 42 | } 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /lla/src/main.rs: -------------------------------------------------------------------------------- 1 | mod commands; 2 | mod config; 3 | mod error; 4 | mod filter; 5 | mod formatter; 6 | mod installer; 7 | mod lister; 8 | mod plugin; 9 | mod sorter; 10 | mod theme; 11 | mod utils; 12 | 13 | use commands::args::{Args, Command}; 14 | use commands::command_handler::handle_command; 15 | use config::Config; 16 | use error::Result; 17 | use plugin::PluginManager; 18 | use utils::color::set_theme; 19 | 20 | fn main() -> Result<()> { 21 | let (mut config, config_error) = load_config()?; 22 | 23 | set_theme(config.get_theme()); 24 | 25 | let args = Args::parse(&config)?; 26 | theme::set_no_color(args.no_color); 27 | 28 | if let Some(Command::Clean) = args.command { 29 | println!("🔄 Starting plugin cleaning..."); 30 | let mut plugin_manager = PluginManager::new(config.clone()); 31 | return plugin_manager.clean_plugins(); 32 | } 33 | 34 | let mut plugin_manager = initialize_plugin_manager(&args, &config)?; 35 | handle_command(&args, &mut config, &mut plugin_manager, config_error) 36 | } 37 | 38 | fn load_config() -> Result<(Config, Option)> { 39 | let (layers, config_error) = config::load_config_layers(None)?; 40 | Ok((layers.effective, config_error)) 41 | } 42 | 43 | fn initialize_plugin_manager(args: &Args, config: &Config) -> Result { 44 | let mut plugin_manager = PluginManager::new(config.clone()); 45 | plugin_manager.discover_plugins(&args.plugins_dir)?; 46 | Ok(plugin_manager) 47 | } 48 | -------------------------------------------------------------------------------- /lla/src/filter/case_insensitive.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::Result; 3 | use std::path::PathBuf; 4 | 5 | pub struct CaseInsensitiveFilter { 6 | inner: Box, 7 | } 8 | 9 | impl CaseInsensitiveFilter { 10 | pub fn new(inner: Box) -> Self { 11 | CaseInsensitiveFilter { inner } 12 | } 13 | 14 | fn to_lowercase_path(path: &PathBuf) -> PathBuf { 15 | let parent = path.parent().unwrap_or_else(|| path.as_ref()); 16 | let filename = path 17 | .file_name() 18 | .and_then(|name| name.to_str()) 19 | .map(|name| name.to_lowercase()) 20 | .unwrap_or_default(); 21 | parent.join(filename) 22 | } 23 | } 24 | 25 | impl FileFilter for CaseInsensitiveFilter { 26 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 27 | let lowercase_files: Vec = files.iter().map(Self::to_lowercase_path).collect(); 28 | 29 | let filtered = self.inner.filter_files(&lowercase_files)?; 30 | let filtered_lowercase: Vec = 31 | filtered.iter().map(Self::to_lowercase_path).collect(); 32 | 33 | Ok(files 34 | .iter() 35 | .enumerate() 36 | .filter(|(i, _)| { 37 | let lowercase_path = Self::to_lowercase_path(&lowercase_files[*i]); 38 | filtered_lowercase.contains(&lowercase_path) 39 | }) 40 | .map(|(_, path)| path.clone()) 41 | .collect()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["lla", "lla_plugin_interface", "lla_plugin_utils", "plugins/*"] 4 | 5 | [workspace.package] 6 | description = "Blazing Fast and highly customizable ls Replacement with Superpowers" 7 | authors = ["Achaq "] 8 | version = "0.5.1" 9 | categories = ["utilities", "file-system", "cli", "file-management"] 10 | edition = "2021" 11 | license = "MIT" 12 | keywords = ["ls", "file-system", "cli", "file-management"] 13 | repository = "https://github.com/chaqchase/lla" 14 | 15 | [workspace.dependencies] 16 | clap = "3.2.25" 17 | clap_complete = "3.2.5" 18 | serde = { version = "1.0.207", features = ["derive"] } 19 | toml = "0.8.19" 20 | dirs = "5.0.1" 21 | colored = "2.0" 22 | rayon = "1.5" 23 | chrono = "0.4" 24 | chrono-humanize = "0.2" 25 | libloading = "0.8.5" 26 | serde_json = "1.0" 27 | walkdir = "2.5" 28 | tempfile = "3.2" 29 | users = "0.11" 30 | lla_plugin_interface = { path = "lla_plugin_interface" } 31 | dashmap = "5.5.3" 32 | parking_lot = "0.12" 33 | once_cell = "1.18" 34 | unicode-width = "0.1.11" 35 | strip-ansi-escapes = "0.1.1" 36 | terminal_size = "0.4.1" 37 | dialoguer = "0.11.0" 38 | atty = "0.2.14" 39 | indicatif = "0.17.9" 40 | console = "0.15.8" 41 | regex = "1.5" 42 | glob = "0.3" 43 | # Protobuf dependencies 44 | prost = "0.12" 45 | prost-build = "0.12" 46 | ureq = { version = "2", features = ["json", "gzip"] } 47 | sha2 = "0.10" 48 | 49 | [profile.release] 50 | lto = true 51 | codegen-units = 1 52 | strip = true 53 | 54 | [profile.dev.build-override] 55 | opt-level = 3 56 | -------------------------------------------------------------------------------- /plugins/categorizer/README.md: -------------------------------------------------------------------------------- 1 | # lla Categorizer Plugin 2 | 3 | File categorization plugin for `lla` that organizes files based on extensions, with hierarchical categorization support. 4 | 5 | ## Features 6 | 7 | - Automatic file categorization by extension with colored labels 8 | - Hierarchical categories and subcategories 9 | - Size-based rules and statistics tracking 10 | - Configurable categories, colors, and rules 11 | 12 | ## Default Categories 13 | 14 | ### Documents 15 | 16 | - Color: bright_blue 17 | - Extensions: txt, md, doc, docx, pdf, rtf, odt 18 | - Subcategories: 19 | - Text: txt, md 20 | - Office: doc, docx, xls, xlsx, ppt, pptx 21 | 22 | ### Code 23 | 24 | - Color: bright_cyan 25 | - Extensions: rs, py, js, ts, java, c, cpp, h, hpp, go, rb, php 26 | - Subcategories: 27 | - Systems: rs, c, cpp, h, hpp 28 | - Web: js, ts, html, css, php 29 | - Scripts: py, rb, sh, bash 30 | 31 | ## Usage 32 | 33 | ```bash 34 | # Add category 35 | lla plugin --name categorizer --action add-category --args "Images" "yellow" "jpg,png,gif" 36 | 37 | # Add subcategory 38 | lla plugin --name categorizer --action add-subcategory --args "Images" "Raster" "jpg,png,gif" 39 | 40 | # List categories 41 | lla plugin --name categorizer --action list-categories 42 | ``` 43 | 44 | ## Configuration 45 | 46 | Config file: `~/.config/lla/plugins/categorizer/config.toml` 47 | 48 | - Category definitions and mappings 49 | - Size rules and subcategories 50 | - UI color schemes 51 | 52 | ## Display Formats 53 | 54 | - **default**: `[Documents]` 55 | - **long**: `[Documents] (Text)` 56 | -------------------------------------------------------------------------------- /lla/src/formatter/default.rs: -------------------------------------------------------------------------------- 1 | use super::FileFormatter; 2 | use crate::error::Result; 3 | use crate::plugin::PluginManager; 4 | use crate::utils::color::{colorize_file_name, colorize_file_name_with_icon}; 5 | use crate::utils::icons::format_with_icon; 6 | use lla_plugin_interface::proto::DecoratedEntry; 7 | use std::path::Path; 8 | pub struct DefaultFormatter { 9 | pub show_icons: bool, 10 | } 11 | 12 | impl DefaultFormatter { 13 | pub fn new(show_icons: bool) -> Self { 14 | Self { show_icons } 15 | } 16 | } 17 | impl FileFormatter for DefaultFormatter { 18 | fn format_files( 19 | &self, 20 | files: &[DecoratedEntry], 21 | plugin_manager: &mut PluginManager, 22 | _depth: Option, 23 | ) -> Result { 24 | Ok(files 25 | .iter() 26 | .map(|file| { 27 | let path = Path::new(&file.path); 28 | let colored_name = colorize_file_name(path).to_string(); 29 | let name_with_icon = colorize_file_name_with_icon( 30 | path, 31 | format_with_icon(path, colored_name, self.show_icons), 32 | ) 33 | .to_string(); 34 | let plugin_fields = plugin_manager.format_fields(file, "default").join(" "); 35 | if plugin_fields.is_empty() { 36 | name_with_icon 37 | } else { 38 | format!("{} {}", name_with_icon, plugin_fields) 39 | } 40 | }) 41 | .collect::>() 42 | .join("\n")) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plugins/file_tagger/README.md: -------------------------------------------------------------------------------- 1 | # lla File Tagger Plugin 2 | 3 | A file tagging plugin for `lla` that provides persistent tag management. 4 | 5 | ## Features 6 | 7 | - Add, remove, and list file tags 8 | - Persistent storage with efficient lookup 9 | - Color-coded tag display 10 | - Interactive commands 11 | - List all tags across files 12 | - Query files by tag 13 | 14 | ## Configuration 15 | 16 | Config file: `~/.config/lla/file_tagger/config.toml` 17 | 18 | ```toml 19 | [colors] 20 | tag = "bright_cyan" # Tag text 21 | tag_label = "bright_green" # Tag label 22 | success = "bright_green" # Success messages 23 | info = "bright_blue" # Info messages 24 | name = "bright_yellow" # Name highlighting 25 | ``` 26 | 27 | ## Storage 28 | 29 | Persistent tag data is stored at: `~/.config/lla/file_tags.txt` 30 | 31 | ## Usage 32 | 33 | ```bash 34 | # Add tag 35 | lla plugin --name file_tagger --action add-tag --args "/path/to/file" "important" 36 | 37 | # Remove tag 38 | lla plugin --name file_tagger --action remove-tag --args "/path/to/file" "important" 39 | 40 | # List tags 41 | lla plugin --name file_tagger --action list-tags --args "/path/to/file" 42 | 43 | # List all tags 44 | lla plugin --name file_tagger --action all-tags 45 | 46 | # List files by tag 47 | lla plugin --name file_tagger --action files-by-tag --args "important" 48 | 49 | # Help 50 | lla plugin --name file_tagger --action help 51 | ``` 52 | 53 | ### Display Examples 54 | 55 | Default format: 56 | 57 | ``` 58 | document.pdf 59 | Tags: [important] [work] [urgent] 60 | ``` 61 | 62 | Long format: 63 | 64 | ``` 65 | document.pdf 66 | Tag: important 67 | Tag: work 68 | Tag: urgent 69 | ``` 70 | -------------------------------------------------------------------------------- /lla/README.md: -------------------------------------------------------------------------------- 1 | # `lla` - Blazing Fast `ls` Replacement with Superpowers 2 | 3 |

4 |

5 | Logo 6 |
lla 7 |

8 | 9 | lla is a modern `ls` replacement that transforms how developers interact with their filesystem. Built with Rust's performance capabilities and designed with user experience in mind, lla combines the familiarity of `ls` with powerful features like specialized views, Git integration, and a robust plugin system with an extensible list of plugins to add more functionality. 10 | 11 | ![default](https://github.com/user-attachments/assets/ba5fa273-c2c4-4143-b199-ab5bff1bb608) 12 | 13 | ## Features 14 | 15 | - Multiple Views: Default clean view, long format, tree structure, table layout, grid display 16 | - Git Integration: Built-in status visualization and repository insights 17 | - Advanced Organization: Timeline view, storage analysis, recursive exploration 18 | - Smart Search: complex filtering patterns (OR, AND, NOT, XOR), regex support 19 | - Customization: Plugin system, theme manager, custom shortcuts, configurable display 20 | - High Performance: Built with Rust, modern UI, clean listings 21 | - Smart Sorting: Multiple criteria, directory-first option, natural sorting 22 | - Flexible Config: Easy initialization, plugin management, configuration tools 23 | - Rich Plugin Ecosystem: File ops and metadata enhancements, code analysis, git tools, and more 24 | 25 | ## Installation 26 | 27 | ```bash 28 | # Using Cargo 29 | cargo install lla 30 | ``` 31 | 32 | For more installation options, documentation, and detailed usage instructions, visit: 33 | [lla Github repository](https://github.com/chaqchase/lla) 34 | -------------------------------------------------------------------------------- /lla_plugin_interface/build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | 3 | fn main() -> Result<()> { 4 | #[cfg(feature = "regenerate-protobuf")] 5 | { 6 | use std::path::PathBuf; 7 | use std::process::Command; 8 | let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); 9 | println!("cargo:rerun-if-changed=src/plugin.proto"); 10 | 11 | if std::env::var("PROTOC").is_ok() 12 | || Command::new("protoc").output().is_ok() 13 | || Command::new("protoc-gen-rust").output().is_ok() 14 | || Command::new("protoc-gen-rust-macos").output().is_ok() 15 | || Command::new("protoc-gen-rust-linux").output().is_ok() 16 | { 17 | if let Err(e) = prost_build::Config::new() 18 | .out_dir(&out_dir) 19 | .compile_protos(&["src/plugin.proto"], &["src/"]) 20 | { 21 | eprintln!("Warning: Failed to compile protos: {}", e); 22 | eprintln!("Using pre-generated files from src/generated/"); 23 | return Ok(()); 24 | } 25 | 26 | if let Err(e) = std::fs::create_dir_all("src/generated") { 27 | eprintln!("Warning: Failed to create generated directory: {}", e); 28 | return Ok(()); 29 | } 30 | 31 | if let Err(e) = std::fs::copy(out_dir.join("lla_plugin.rs"), "src/generated/mod.rs") { 32 | eprintln!("Warning: Failed to copy generated file: {}", e); 33 | eprintln!("Using pre-generated files from src/generated/"); 34 | return Ok(()); 35 | } 36 | } else { 37 | eprintln!("Note: protoc not found, using pre-generated files from src/generated/"); 38 | } 39 | } 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /plugins/duplicate_file_detector/README.md: -------------------------------------------------------------------------------- 1 | # lla Duplicate File Detector Plugin 2 | 3 | A plugin for `lla` that identifies identical files using secure hash comparison. 4 | 5 | ## Features 6 | 7 | - SHA-256 content hashing with intelligent caching 8 | - Original file and duplicate chain tracking 9 | - Color-coded status display 10 | - Performance optimized with chunk-based processing 11 | 12 | ## Configuration 13 | 14 | Located at `~/.config/lla/duplicate_file_detector/config.toml`: 15 | 16 | ```toml 17 | [colors] 18 | duplicate = "bright_red" # Duplicate file indicator 19 | has_duplicates = "bright_yellow"# Original file with duplicates 20 | path = "bright_cyan" # File path display 21 | success = "bright_green" # Success messages 22 | info = "bright_blue" # Information messages 23 | name = "bright_yellow" # Name highlighting 24 | ``` 25 | 26 | ## Usage 27 | 28 | ```bash 29 | # Clear the detection cache 30 | lla plugin --name duplicate_file_detector --action clear-cache 31 | 32 | # View help information 33 | lla plugin --name duplicate_file_detector --action help 34 | ``` 35 | 36 | ## Display Formats 37 | 38 | ### Default Format 39 | 40 | ``` 41 | file.txt 42 | Status: DUPLICATE of /path/to/original.txt 43 | 44 | other.txt 45 | Status: HAS DUPLICATES: /path/to/copy1.txt, /path/to/copy2.txt 46 | ``` 47 | 48 | ### Long Format 49 | 50 | ``` 51 | file.txt 52 | Status: DUPLICATE 53 | Original File: /path/to/original.txt 54 | 55 | other.txt 56 | Status: HAS DUPLICATES 57 | Duplicate Copies: /path/to/copy1.txt 58 | /path/to/copy2.txt 59 | ``` 60 | 61 | ## Technical Details 62 | 63 | - Uses SHA-256 hashing with 8KB chunk-based reading 64 | - Implements efficient caching with automatic invalidation 65 | - Thread-safe operations 66 | - Color-coded status indicators for duplicates and originals 67 | -------------------------------------------------------------------------------- /lla/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lla" 3 | version.workspace = true 4 | edition.workspace = true 5 | description = "Blazing Fast and highly customizable ls Replacement with Superpowers" 6 | authors.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | keywords = ["cli", "ls", "blazing-fast"] 10 | categories = ["command-line-utilities"] 11 | exclude = ["docs"] 12 | 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | clap.workspace = true 18 | clap_complete.workspace = true 19 | serde.workspace = true 20 | toml.workspace = true 21 | dirs.workspace = true 22 | colored.workspace = true 23 | rayon.workspace = true 24 | chrono.workspace = true 25 | chrono-humanize = "0.2" 26 | libloading.workspace = true 27 | serde_json.workspace = true 28 | walkdir.workspace = true 29 | tempfile.workspace = true 30 | users.workspace = true 31 | parking_lot.workspace = true 32 | lla_plugin_interface = { version = "0.5.1", path = "../lla_plugin_interface" } 33 | lla_plugin_utils = { version = "0.5.1", path = "../lla_plugin_utils" } 34 | once_cell.workspace = true 35 | dashmap.workspace = true 36 | unicode-width.workspace = true 37 | strip-ansi-escapes.workspace = true 38 | terminal_size.workspace = true 39 | dialoguer.workspace = true 40 | atty.workspace = true 41 | indicatif.workspace = true 42 | console.workspace = true 43 | prost = "0.12" 44 | regex.workspace = true 45 | glob.workspace = true 46 | ignore = "0.4" 47 | crossterm = "0.27" 48 | crossbeam-channel = "0.5.14" 49 | unicode-normalization = "0.1.22" 50 | num_cpus = "1.16" 51 | fuzzy-matcher = "0.3.7" 52 | csv = "1" 53 | pathdiff = "0.2" 54 | zip = { version = "1", default-features = false, features = ["deflate"] } 55 | tar = "0.4" 56 | flate2 = "1" 57 | libc = "0.2" 58 | ureq.workspace = true 59 | sha2.workspace = true 60 | similar = "2" 61 | -------------------------------------------------------------------------------- /lla/src/sorter/size.rs: -------------------------------------------------------------------------------- 1 | use super::{FileSorter, SortOptions}; 2 | use crate::error::Result; 3 | use lla_plugin_interface::proto::DecoratedEntry; 4 | use rayon::prelude::*; 5 | use std::path::PathBuf; 6 | 7 | pub struct SizeSorter; 8 | 9 | impl FileSorter for SizeSorter { 10 | fn sort_files_with_metadata( 11 | &self, 12 | entries: &mut [(PathBuf, &DecoratedEntry)], 13 | options: SortOptions, 14 | ) -> Result<()> { 15 | entries.par_sort_unstable_by(|(_path_a, entry_a), (_path_b, entry_b)| { 16 | if options.dirs_first { 17 | let a_is_dir = entry_a.metadata.as_ref().map_or(false, |m| m.is_dir); 18 | let b_is_dir = entry_b.metadata.as_ref().map_or(false, |m| m.is_dir); 19 | 20 | match (a_is_dir, b_is_dir) { 21 | (true, false) => { 22 | return if options.reverse { 23 | std::cmp::Ordering::Greater 24 | } else { 25 | std::cmp::Ordering::Less 26 | } 27 | } 28 | (false, true) => { 29 | return if options.reverse { 30 | std::cmp::Ordering::Less 31 | } else { 32 | std::cmp::Ordering::Greater 33 | } 34 | } 35 | _ => {} 36 | } 37 | } 38 | 39 | let size_a = entry_a.metadata.as_ref().map_or(0, |m| m.size); 40 | let size_b = entry_b.metadata.as_ref().map_or(0, |m| m.size); 41 | let size_order = size_a.cmp(&size_b); 42 | 43 | if options.reverse { 44 | size_order 45 | } else { 46 | size_order.reverse() 47 | } 48 | }); 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lla/src/sorter/date.rs: -------------------------------------------------------------------------------- 1 | use super::{FileSorter, SortOptions}; 2 | use crate::error::Result; 3 | use lla_plugin_interface::proto::DecoratedEntry; 4 | use rayon::prelude::*; 5 | use std::path::PathBuf; 6 | 7 | pub struct DateSorter; 8 | 9 | impl FileSorter for DateSorter { 10 | fn sort_files_with_metadata( 11 | &self, 12 | entries: &mut [(PathBuf, &DecoratedEntry)], 13 | options: SortOptions, 14 | ) -> Result<()> { 15 | entries.par_sort_unstable_by(|(_path_a, entry_a), (_path_b, entry_b)| { 16 | if options.dirs_first { 17 | let a_is_dir = entry_a.metadata.as_ref().map_or(false, |m| m.is_dir); 18 | let b_is_dir = entry_b.metadata.as_ref().map_or(false, |m| m.is_dir); 19 | 20 | match (a_is_dir, b_is_dir) { 21 | (true, false) => { 22 | return if options.reverse { 23 | std::cmp::Ordering::Greater 24 | } else { 25 | std::cmp::Ordering::Less 26 | } 27 | } 28 | (false, true) => { 29 | return if options.reverse { 30 | std::cmp::Ordering::Less 31 | } else { 32 | std::cmp::Ordering::Greater 33 | } 34 | } 35 | _ => {} 36 | } 37 | } 38 | 39 | let time_a = entry_a.metadata.as_ref().map_or(0, |m| m.modified); 40 | let time_b = entry_b.metadata.as_ref().map_or(0, |m| m.modified); 41 | let date_order = time_a.cmp(&time_b); 42 | 43 | if options.reverse { 44 | date_order.reverse() 45 | } else { 46 | date_order 47 | } 48 | }); 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /scripts/generate_plugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")/.." 4 | 5 | output_file="plugins.md" 6 | 7 | cat > "$output_file" << EOL 8 | # LLA Plugins 9 | 10 | This document lists all available plugins for LLA and provides installation instructions. 11 | 12 | ## Installation 13 | 14 | You can install all plugins at once using: 15 | 16 | \`\`\`bash 17 | lla install --git https://github.com/triyanox/lla 18 | \`\`\` 19 | 20 | Or you can install individual plugins as described below. 21 | 22 | ## Available Plugins 23 | 24 | EOL 25 | 26 | for plugin_dir in plugins/*/; do 27 | if [ -f "${plugin_dir}Cargo.toml" ]; then 28 | name=$(grep '^name' "${plugin_dir}Cargo.toml" | sed 's/name = "\(.*\)"/\1/') 29 | version=$(grep '^version' "${plugin_dir}Cargo.toml" | sed 's/version = "\(.*\)"/\1/') 30 | description=$(grep '^description' "${plugin_dir}Cargo.toml" | sed 's/description = "\(.*\)"/\1/') 31 | 32 | if [ -z "$description" ]; then 33 | description="No description provided." 34 | fi 35 | 36 | readme_path="${plugin_dir}README.md" 37 | doc_link="" 38 | if [ -f "$readme_path" ]; then 39 | doc_link="[Documentation](${plugin_dir}README.md)" 40 | fi 41 | 42 | cat >> "$output_file" << EOL 43 | ### ${name} 44 | 45 | **Description:** ${description} 46 | 47 | **Version:** ${version} 48 | 49 | **Documentation:** ${doc_link} 50 | 51 | **Installation Options:** 52 | 53 | 1. Using LLA install command: 54 | \`\`\`bash 55 | lla install --dir path/to/lla/${plugin_dir} 56 | \`\`\` 57 | 58 | 2. Manual installation: 59 | \`\`\`bash 60 | git clone https://github.com/triyanox/lla 61 | cd lla/${plugin_dir} 62 | cargo build --release 63 | \`\`\` 64 | 65 | Then, copy the generated \`.so\`, \`.dll\`, or \`.dylib\` file from the \`target/release\` directory to your LLA plugins directory. 66 | 67 | EOL 68 | fi 69 | done 70 | 71 | echo "Generated $output_file" -------------------------------------------------------------------------------- /plugins/npm/README.md: -------------------------------------------------------------------------------- 1 | # lla NPM Plugin 2 | 3 | NPM package search plugin for `lla` with bundlephobia integration and favorites management. 4 | 5 | ## Features 6 | 7 | - **Package Search**: Search npm registry for package information 8 | - **Bundle Size**: View bundlephobia.com size metrics (minified & gzipped) 9 | - **Favorites**: Manage favorite packages 10 | - **Clipboard Integration**: Copy install commands 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Search for npm packages 16 | lla plugin --name npm --action search 17 | 18 | # View favorite packages 19 | lla plugin --name npm --action favorites 20 | 21 | # Configure preferences (package manager) 22 | lla plugin --name npm --action preferences 23 | 24 | # Show help 25 | lla plugin --name npm --action help 26 | ``` 27 | 28 | ## Configuration 29 | 30 | Config location: `~/.config/lla/plugins/npm/config.toml` 31 | 32 | ```toml 33 | favorites = [] # List of favorite packages 34 | registry = "https://registry.npmjs.org" # NPM registry URL 35 | package_manager = "npm" # Package manager (npm, yarn, pnpm, bun) 36 | 37 | [colors] 38 | success = "bright_green" 39 | info = "bright_cyan" 40 | warning = "bright_yellow" 41 | error = "bright_red" 42 | package = "bright_blue" 43 | version = "bright_magenta" 44 | ``` 45 | 46 | ## Display Examples 47 | 48 | Package Details: 49 | 50 | ``` 51 | ──────────────────────────────────────────────────────────── 52 | 📦 react v18.2.0 53 | ──────────────────────────────────────────────────────────── 54 | Description: React is a JavaScript library for building UIs 55 | Author: Meta 56 | License: MIT 57 | Homepage: https://reactjs.org/ 58 | 59 | Bundle Size: 60 | Minified: 42.5 KB 61 | Gzipped: 13.2 KB 62 | ──────────────────────────────────────────────────────────── 63 | ``` 64 | 65 | Favorites List: 66 | 67 | ``` 68 | ⚡ Choose action 69 | > 📦 View package details 70 | 📋 Copy install commands 71 | 🗑️ Remove from favorites 72 | ← Back 73 | ``` 74 | -------------------------------------------------------------------------------- /.github/workflows/update-proto.yml: -------------------------------------------------------------------------------- 1 | name: Update Proto Bindings 2 | 3 | on: 4 | push: 5 | paths: 6 | - "lla_plugin_interface/src/plugin.proto" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | update-proto: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Setup Protoc 16 | uses: arduino/setup-protoc@v3 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | - name: Install Rust toolchain 21 | uses: dtolnay/rust-toolchain@master 22 | with: 23 | toolchain: 1.71.0 24 | 25 | - name: Cache cargo dependencies 26 | uses: actions/cache@v3 27 | with: 28 | path: | 29 | ~/.cargo/registry 30 | ~/.cargo/git 31 | target 32 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-cargo- 35 | 36 | - name: Generate Proto Bindings 37 | run: | 38 | cd lla_plugin_interface 39 | cargo build --features regenerate-protobuf 40 | cd .. 41 | 42 | - name: Check for changes 43 | id: git-check 44 | run: | 45 | git diff --quiet || echo "changes=true" >> $GITHUB_OUTPUT 46 | 47 | - name: Create Pull Request 48 | if: steps.git-check.outputs.changes == 'true' 49 | uses: peter-evans/create-pull-request@v5 50 | with: 51 | commit-message: "chore: update generated protobuf bindings" 52 | title: "Update Generated Protobuf Bindings" 53 | body: | 54 | Auto-generated PR to update protobuf bindings. 55 | 56 | This PR was automatically created because changes were detected in the protobuf definitions. 57 | The bindings have been regenerated to match the latest proto file. 58 | branch: "update-proto-bindings" 59 | base: "main" 60 | delete-branch: true 61 | -------------------------------------------------------------------------------- /plugins/keyword_search/README.md: -------------------------------------------------------------------------------- 1 | # lla Keyword Search Plugin 2 | 3 | High-performance keyword search plugin for `lla` with interactive search and rich display features. 4 | 5 | ## Features 6 | 7 | - **Smart Search**: Multi-keyword, case-sensitive, regex support 8 | - **Interactive**: File selection, filtering, action menu 9 | - **Rich Display**: Syntax highlighting, context visualization 10 | - **Analysis**: Match statistics and pattern detection 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Search in current directory 16 | lla plugin --name keyword_search --action search 17 | 18 | # Available actions after finding matches: 19 | 1. View detailed matches 20 | 2. Copy to clipboard 21 | 3. Save to file 22 | 4. Show statistics 23 | 5. Filter matches 24 | 6. Advanced analysis 25 | ``` 26 | 27 | ## Configuration 28 | 29 | Config location: `~/.config/lla/keyword_search/config.toml` 30 | 31 | ```toml 32 | keywords = [] # Keywords to search for 33 | case_sensitive = false # Case sensitivity 34 | use_regex = false # Regular expression support 35 | context_lines = 2 # Number of context lines 36 | max_matches = 5 # Maximum matches per file 37 | 38 | [colors] 39 | keyword = "bright_red" 40 | line_number = "bright_yellow" 41 | context = "bright_black" 42 | file = "bright_blue" 43 | success = "bright_green" 44 | info = "bright_cyan" 45 | ``` 46 | 47 | ## Display Examples 48 | 49 | Match View: 50 | 51 | ``` 52 | ───────────────────────────────── 53 | 📂 src/main.rs 54 | ───────────────────────────────── 55 | 123 │ function process() { 56 | 124 │ let data = analyze(); 57 | ►125 │ // TODO: Implement error handling 58 | 126 │ return data; 59 | 127 │ } 60 | ───────────────────────────────── 61 | ``` 62 | 63 | Statistics View: 64 | 65 | ``` 66 | 📊 Match Statistics: 67 | ───────────────────────────────── 68 | • Total matches: 5 69 | • Unique keywords: 2 70 | • Average context: 2.5 lines 71 | • File: src/main.rs 72 | ───────────────────────────────── 73 | ``` 74 | -------------------------------------------------------------------------------- /plugins/file_remover/README.md: -------------------------------------------------------------------------------- 1 | # lla File Remover Plugin 2 | 3 | A plugin for `lla` that provides an interactive interface for safely removing files and directories. 4 | 5 | ## Features 6 | 7 | - **Interactive Selection**: Multi-select interface for choosing files to remove 8 | - **Path Flexibility**: Support for both current and specified directories 9 | - **Safe Operations**: Confirmation prompts and error handling for safe removal 10 | - **Directory Support**: Recursive removal of directories 11 | - **User Interface**: Colored output and interactive menus 12 | 13 | ## Configuration 14 | 15 | ```toml 16 | [colors] 17 | success = "bright_green" 18 | info = "bright_blue" 19 | error = "bright_red" 20 | path = "bright_yellow" 21 | ``` 22 | 23 | ## Usage 24 | 25 | ### Basic Operations 26 | 27 | ```bash 28 | # Remove files/directories from current directory 29 | lla plugin --name file_remover --action remove 30 | 31 | # Remove files/directories from specified directory 32 | lla plugin --name file_remover --action remove --args /path/to/directory 33 | 34 | # Show help information 35 | lla plugin --name file_remover --action help 36 | ``` 37 | 38 | ## Common Workflows 39 | 40 | ### 1. Removing Files from Current Directory 41 | 42 | ```bash 43 | # In target directory 44 | cd /path/to/directory 45 | lla plugin --name file_remover --action remove 46 | # Select files to remove using space, confirm with enter 47 | ``` 48 | 49 | ### 2. Removing Files from Specific Directory 50 | 51 | ```bash 52 | # Remove files from a specific directory without changing location 53 | lla plugin --name file_remover --action remove --args /path/to/directory 54 | # Select files to remove using space, confirm with enter 55 | ``` 56 | 57 | ## Display Format 58 | 59 | ``` 60 | ───────────────────────────────────── 61 | File Remover 62 | ───────────────────────────────────── 63 | Select items to remove: 64 | → file1.txt 65 | → directory1 66 | → file2.rs 67 | ───────────────────────────────────── 68 | Use Space to select, Enter to confirm 69 | ───────────────────────────────────── 70 | ``` 71 | -------------------------------------------------------------------------------- /lla_plugin_interface/src/plugin.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package lla_plugin; 4 | 5 | message EntryMetadata { 6 | uint64 size = 1; 7 | uint64 modified = 2; 8 | uint64 accessed = 3; 9 | uint64 created = 4; 10 | bool is_dir = 5; 11 | bool is_file = 6; 12 | bool is_symlink = 7; 13 | uint32 permissions = 8; 14 | uint32 uid = 9; 15 | uint32 gid = 10; 16 | } 17 | 18 | message DecoratedEntry { 19 | string path = 1; 20 | EntryMetadata metadata = 2; 21 | map custom_fields = 3; 22 | } 23 | 24 | message PluginMessage { 25 | oneof message { 26 | bool get_name = 1; 27 | bool get_version = 2; 28 | bool get_description = 3; 29 | bool get_supported_formats = 4; 30 | DecoratedEntry decorate = 5; 31 | FormatFieldRequest format_field = 6; 32 | ActionRequest action = 7; 33 | bool list_actions = 8; 34 | string name_response = 101; 35 | string version_response = 102; 36 | string description_response = 103; 37 | SupportedFormatsResponse formats_response = 104; 38 | DecoratedEntry decorated_response = 105; 39 | FormattedFieldResponse field_response = 106; 40 | ActionResponse action_response = 107; 41 | string error_response = 108; 42 | ListActionsResponse list_actions_response = 109; 43 | } 44 | } 45 | 46 | message FormatFieldRequest { 47 | DecoratedEntry entry = 1; 48 | string format = 2; 49 | } 50 | 51 | message ActionRequest { 52 | string action = 1; 53 | repeated string args = 2; 54 | } 55 | 56 | message SupportedFormatsResponse { 57 | repeated string formats = 1; 58 | } 59 | 60 | message FormattedFieldResponse { 61 | optional string field = 1; 62 | } 63 | 64 | message ActionResponse { 65 | bool success = 1; 66 | optional string error = 2; 67 | } 68 | 69 | message ActionInfo { 70 | string name = 1; 71 | string usage = 2; 72 | string description = 3; 73 | repeated string examples = 4; 74 | } 75 | 76 | message ListActionsResponse { 77 | repeated ActionInfo actions = 1; 78 | } -------------------------------------------------------------------------------- /.github/scripts/release_helpers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | if [[ -n "${GITHUB_TOKEN:-}" && -z "${GH_TOKEN:-}" ]]; then 6 | export GH_TOKEN="$GITHUB_TOKEN" 7 | fi 8 | 9 | require_cmd() { 10 | local missing=() 11 | for cmd in "$@"; do 12 | if ! command -v "$cmd" >/dev/null 2>&1; then 13 | missing+=("$cmd") 14 | fi 15 | done 16 | 17 | if [[ ${#missing[@]} -gt 0 ]]; then 18 | echo "::error::Missing required commands: ${missing[*]}" >&2 19 | exit 1 20 | fi 21 | } 22 | 23 | log_note() { 24 | echo "::notice::$*" 25 | } 26 | 27 | ensure_release_exists() { 28 | local tag="$1" 29 | local release_name="$2" 30 | local notes_file="${3:-}" 31 | 32 | require_cmd gh 33 | 34 | if gh release view "$tag" >/dev/null 2>&1; then 35 | log_note "Release $tag already exists" 36 | if [[ -n "$notes_file" && -f "$notes_file" ]]; then 37 | gh release edit "$tag" --title "$release_name" --notes-file "$notes_file" 38 | fi 39 | else 40 | log_note "Creating release $tag" 41 | if [[ -n "$notes_file" && -f "$notes_file" ]]; then 42 | gh release create "$tag" --title "$release_name" --notes-file "$notes_file" 43 | else 44 | gh release create "$tag" --title "$release_name" --notes "Release $tag" 45 | fi 46 | fi 47 | } 48 | 49 | asset_exists() { 50 | local tag="$1" 51 | local asset_name="$2" 52 | 53 | require_cmd gh jq 54 | 55 | gh release view "$tag" --json assets --jq '.assets[].name' 2>/dev/null | grep -Fxq "$asset_name" 56 | } 57 | 58 | dispatch_next_stage() { 59 | local event_type="$1" 60 | local tag="$2" 61 | local version="$3" 62 | 63 | if [[ -z "$event_type" ]]; then 64 | log_note "No dispatch event provided, skipping" 65 | return 0 66 | fi 67 | 68 | require_cmd gh jq 69 | 70 | local payload 71 | payload=$(jq -nc --arg tag "$tag" --arg version "$version" '{tag:$tag,version:$version}') 72 | 73 | log_note "Dispatching $event_type for $tag" 74 | 75 | gh api "repos/${GITHUB_REPOSITORY}/dispatches" \ 76 | -f "event_type=${event_type}" \ 77 | -f "client_payload=${payload}" 78 | } 79 | 80 | -------------------------------------------------------------------------------- /plugins/flush_dns/README.md: -------------------------------------------------------------------------------- 1 | # lla Flush DNS Plugin 2 | 3 | DNS cache flushing plugin for `lla` with history tracking and cross-platform support. 4 | 5 | ## Features 6 | 7 | - **Cross-Platform**: Works on macOS, Linux, and Windows 8 | - **History Tracking**: Track all flush operations with timestamps 9 | - **Confirmation Prompts**: Optional confirmation before flushing 10 | - **Statistics**: View flush history and success rates 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Flush DNS cache 16 | lla plugin --name flush_dns --action flush 17 | 18 | # View flush history 19 | lla plugin --name flush_dns --action history 20 | 21 | # Clear history 22 | lla plugin --name flush_dns --action clear-history 23 | 24 | # Configure preferences 25 | lla plugin --name flush_dns --action preferences 26 | 27 | # Show help 28 | lla plugin --name flush_dns --action help 29 | ``` 30 | 31 | ## Configuration 32 | 33 | Config location: `~/.config/lla/plugins/flush_dns/config.toml` 34 | 35 | ```toml 36 | confirm_before_flush = true # Show confirmation prompt 37 | show_verbose_output = true # Display command output 38 | max_history_size = 50 # Maximum history entries 39 | 40 | [colors] 41 | success = "bright_green" 42 | info = "bright_cyan" 43 | warning = "bright_yellow" 44 | error = "bright_red" 45 | ``` 46 | 47 | ## Display Examples 48 | 49 | Flushing DNS: 50 | 51 | ``` 52 | ℹ Info: Flushing DNS cache on macOS... 53 | ✓ Success: DNS cache flushed successfully! 54 | ``` 55 | 56 | History View: 57 | 58 | ``` 59 | 📜 DNS Flush History: 60 | ────────────────────────────────────────────────────────────────────── 61 | ✓ 2025-10-20 16:30:45 [macOS] Success 62 | ✓ 2025-10-20 15:20:30 [macOS] Success 63 | ✗ 2025-10-20 14:15:22 [macOS] Failed 64 | ────────────────────────────────────────────────────────────────────── 65 | 66 | 📊 Statistics: 67 | • Total flushes: 25 68 | • Successful: 24 69 | • Failed: 1 70 | ``` 71 | 72 | ## Platform Commands 73 | 74 | - **macOS**: `sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder` 75 | - **Linux**: `sudo systemd-resolve --flush-caches` or `sudo systemctl restart nscd` 76 | - **Windows**: `ipconfig /flushdns` 77 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/syntax.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "syntax")] 2 | use lazy_static::lazy_static; 3 | #[cfg(feature = "syntax")] 4 | use syntect::{ 5 | easy::HighlightLines, 6 | highlighting::{Style, ThemeSet}, 7 | parsing::SyntaxSet, 8 | util::{as_24_bit_terminal_escaped, LinesWithEndings}, 9 | }; 10 | 11 | #[cfg(feature = "syntax")] 12 | lazy_static! { 13 | static ref SYNTAX_SET: SyntaxSet = SyntaxSet::load_defaults_newlines(); 14 | static ref THEME_SET: ThemeSet = ThemeSet::load_defaults(); 15 | } 16 | 17 | pub struct CodeHighlighter; 18 | 19 | impl CodeHighlighter { 20 | #[cfg(feature = "syntax")] 21 | pub fn highlight(code: &str, language: &str) -> String { 22 | let syntax = SYNTAX_SET 23 | .find_syntax_by_token(language) 24 | .unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text()); 25 | let mut h = HighlightLines::new(syntax, &THEME_SET.themes["base16-ocean.dark"]); 26 | 27 | let mut highlighted = String::new(); 28 | for line in LinesWithEndings::from(code) { 29 | let ranges: Vec<(Style, &str)> = 30 | h.highlight_line(line, &SYNTAX_SET).unwrap_or_default(); 31 | let escaped = as_24_bit_terminal_escaped(&ranges[..], false); 32 | highlighted.push_str(&escaped); 33 | } 34 | highlighted 35 | } 36 | 37 | #[cfg(not(feature = "syntax"))] 38 | pub fn highlight(code: &str, _language: &str) -> String { 39 | code.to_string() 40 | } 41 | 42 | pub fn highlight_with_line_numbers(code: &str, language: &str, start_line: usize) -> String { 43 | let highlighted = Self::highlight(code, language); 44 | let mut result = String::new(); 45 | for (i, line) in highlighted.lines().enumerate() { 46 | result.push_str(&format!("{:4} │ {}\n", i + start_line, line)); 47 | } 48 | result 49 | } 50 | } 51 | 52 | #[cfg(feature = "syntax")] 53 | pub fn get_available_themes() -> Vec { 54 | THEME_SET.themes.keys().cloned().collect() 55 | } 56 | 57 | #[cfg(not(feature = "syntax"))] 58 | pub fn get_available_themes() -> Vec { 59 | vec![] 60 | } 61 | -------------------------------------------------------------------------------- /plugins/google_meet/README.md: -------------------------------------------------------------------------------- 1 | # lla Google Meet Plugin 2 | 3 | Google Meet plugin for `lla` that creates meeting rooms and manages links with browser profile support. 4 | 5 | ## Features 6 | 7 | - **Quick Meeting Creation**: Instantly create Google Meet rooms 8 | - **Clipboard Integration**: Auto-copy meeting links to clipboard 9 | - **Browser Profiles**: Create meetings with specific browser profiles 10 | - **History Management**: Track and reuse meeting links 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Create a new meeting room 16 | lla plugin --name google_meet --action create 17 | 18 | # Create meeting with browser profile 19 | lla plugin --name google_meet --action create-with-profile 20 | 21 | # Manage meeting history 22 | lla plugin --name google_meet --action history 23 | 24 | # Manage browser profiles 25 | lla plugin --name google_meet --action profiles 26 | 27 | # Configure preferences 28 | lla plugin --name google_meet --action preferences 29 | 30 | # Show help 31 | lla plugin --name google_meet --action help 32 | ``` 33 | 34 | ## Configuration 35 | 36 | Config location: `~/.config/lla/plugins/google_meet/config.toml` 37 | 38 | ```toml 39 | auto_copy_link = true # Auto-copy links to clipboard 40 | max_history_size = 50 # Maximum history entries 41 | 42 | [[browser_profiles]] 43 | name = "Work" 44 | profile_path = "Profile 1" 45 | 46 | [[browser_profiles]] 47 | name = "Personal" 48 | profile_path = "Default" 49 | 50 | [colors] 51 | success = "bright_green" 52 | info = "bright_cyan" 53 | warning = "bright_yellow" 54 | error = "bright_red" 55 | link = "bright_blue" 56 | ``` 57 | 58 | ## Display Examples 59 | 60 | Creating Meeting: 61 | 62 | ``` 63 | ℹ Info: Creating Google Meet room... 64 | 🔗 Link: https://meet.google.com/abc-defg-hij 65 | ✓ Success: Link copied to clipboard! 66 | ✓ Success: Meeting room created successfully! 67 | ``` 68 | 69 | History Management: 70 | 71 | ``` 72 | ⚡ Choose action 73 | > 📋 Copy selected link 74 | 🌐 Open selected link 75 | 🗑️ Clear all history 76 | 77 | 🔗 https://meet.google.com/abc-defg-hij (2025-10-20 15:30:45) 78 | 🔗 https://meet.google.com/xyz-uvwx-stu (2025-10-20 14:20:30) 79 | ``` 80 | -------------------------------------------------------------------------------- /.github/workflows/generate-completions.yml: -------------------------------------------------------------------------------- 1 | name: Generate Shell Completions 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - "lla/src/commands/args.rs" 8 | branches: 9 | - main 10 | 11 | jobs: 12 | generate-completions: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | pull-requests: write 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup Protoc 21 | uses: arduino/setup-protoc@v3 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - name: Install Rust toolchain 26 | uses: dtolnay/rust-toolchain@master 27 | with: 28 | toolchain: 1.71.0 29 | 30 | - name: Rust Cache 31 | uses: Swatinem/rust-cache@v2 32 | 33 | - name: Build lla 34 | run: cargo build 35 | 36 | - name: Create completions directory 37 | run: mkdir -p completions 38 | 39 | - name: Generate completions 40 | run: | 41 | # Generate completions for each supported shell 42 | cargo run -- completion bash --output completions/lla.bash 43 | cargo run -- completion fish --output completions/lla.fish 44 | cargo run -- completion zsh --output completions/_lla 45 | cargo run -- completion powershell --output completions/lla.ps1 46 | cargo run -- completion elvish --output completions/lla.elv 47 | 48 | - name: Create Pull Request 49 | uses: peter-evans/create-pull-request@v5 50 | with: 51 | commit-message: "chore: update shell completions" 52 | title: "chore: update shell completions" 53 | body: | 54 | This PR updates the shell completions for all supported shells: 55 | - Bash 56 | - Fish 57 | - Zsh 58 | - PowerShell 59 | - Elvish 60 | 61 | These completions were automatically generated from the latest CLI arguments definition. 62 | branch: update-shell-completions 63 | delete-branch: true 64 | labels: | 65 | automation 66 | shell-completions 67 | -------------------------------------------------------------------------------- /lla/src/sorter/alphabetical.rs: -------------------------------------------------------------------------------- 1 | use super::{natural_cmp, FileSorter, SortOptions}; 2 | use crate::error::Result; 3 | use lla_plugin_interface::proto::DecoratedEntry; 4 | use rayon::prelude::*; 5 | use std::path::PathBuf; 6 | 7 | pub struct AlphabeticalSorter; 8 | 9 | impl FileSorter for AlphabeticalSorter { 10 | fn sort_files_with_metadata( 11 | &self, 12 | entries: &mut [(PathBuf, &DecoratedEntry)], 13 | options: SortOptions, 14 | ) -> Result<()> { 15 | entries.par_sort_unstable_by(|(path_a, entry_a), (path_b, entry_b)| { 16 | if options.dirs_first { 17 | let a_is_dir = entry_a.metadata.as_ref().map_or(false, |m| m.is_dir); 18 | let b_is_dir = entry_b.metadata.as_ref().map_or(false, |m| m.is_dir); 19 | 20 | match (a_is_dir, b_is_dir) { 21 | (true, false) => { 22 | return if options.reverse { 23 | std::cmp::Ordering::Greater 24 | } else { 25 | std::cmp::Ordering::Less 26 | } 27 | } 28 | (false, true) => { 29 | return if options.reverse { 30 | std::cmp::Ordering::Less 31 | } else { 32 | std::cmp::Ordering::Greater 33 | } 34 | } 35 | _ => {} 36 | } 37 | } 38 | 39 | let a_name = path_a.file_name().unwrap_or_default().to_string_lossy(); 40 | let b_name = path_b.file_name().unwrap_or_default().to_string_lossy(); 41 | 42 | let name_order = if options.natural { 43 | natural_cmp(&a_name, &b_name) 44 | } else if options.case_sensitive { 45 | a_name.cmp(&b_name) 46 | } else { 47 | a_name.to_lowercase().cmp(&b_name.to_lowercase()) 48 | }; 49 | 50 | if options.reverse { 51 | name_order.reverse() 52 | } else { 53 | name_order 54 | } 55 | }); 56 | 57 | Ok(()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lla/src/filter/pattern.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::Result; 3 | use std::path::PathBuf; 4 | 5 | pub struct PatternFilter { 6 | patterns: Vec, 7 | match_all: bool, 8 | } 9 | 10 | impl PatternFilter { 11 | pub fn new(pattern: String) -> Self { 12 | let patterns: Vec = if pattern.contains(',') { 13 | pattern 14 | .split(',') 15 | .map(|s| s.trim().to_string()) 16 | .filter(|s| !s.is_empty()) 17 | .collect() 18 | } else { 19 | vec![pattern] 20 | }; 21 | 22 | let match_all = patterns.iter().any(|p| p.starts_with('+')); 23 | let patterns = patterns 24 | .into_iter() 25 | .map(|p| { 26 | if p.starts_with('+') { 27 | p[1..].to_string() 28 | } else { 29 | p 30 | } 31 | }) 32 | .collect(); 33 | 34 | PatternFilter { 35 | patterns, 36 | match_all, 37 | } 38 | } 39 | 40 | fn matches_pattern(&self, path: &PathBuf) -> bool { 41 | if let Some(name) = path.file_name().and_then(|name| name.to_str()) { 42 | if self.match_all { 43 | return self.patterns.iter().all(|pattern| name.contains(pattern)); 44 | } else if self.patterns.iter().any(|pattern| name.contains(pattern)) { 45 | return true; 46 | } 47 | } 48 | 49 | if let Some(path_str) = path.to_str() { 50 | if self.match_all { 51 | self.patterns 52 | .iter() 53 | .all(|pattern| path_str.contains(pattern)) 54 | } else { 55 | self.patterns 56 | .iter() 57 | .any(|pattern| path_str.contains(pattern)) 58 | } 59 | } else { 60 | false 61 | } 62 | } 63 | } 64 | 65 | impl FileFilter for PatternFilter { 66 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 67 | Ok(files 68 | .iter() 69 | .filter(|file| self.matches_pattern(file)) 70 | .cloned() 71 | .collect()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lla/src/sorter/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use std::path::PathBuf; 3 | 4 | #[derive(Clone, Copy, Default)] 5 | pub struct SortOptions { 6 | pub reverse: bool, 7 | pub dirs_first: bool, 8 | pub case_sensitive: bool, 9 | pub natural: bool, 10 | } 11 | 12 | pub trait FileSorter: Send + Sync { 13 | fn sort_files_with_metadata( 14 | &self, 15 | entries: &mut [(PathBuf, &DecoratedEntry)], 16 | options: SortOptions, 17 | ) -> Result<()>; 18 | } 19 | 20 | mod alphabetical; 21 | mod date; 22 | mod size; 23 | 24 | pub use alphabetical::AlphabeticalSorter; 25 | pub use date::DateSorter; 26 | use lla_plugin_interface::proto::DecoratedEntry; 27 | pub use size::SizeSorter; 28 | 29 | pub(crate) fn natural_cmp(a: &str, b: &str) -> std::cmp::Ordering { 30 | let mut a_chars = a.chars().peekable(); 31 | let mut b_chars = b.chars().peekable(); 32 | 33 | while a_chars.peek().is_some() && b_chars.peek().is_some() { 34 | if a_chars.peek().unwrap().is_ascii_digit() && b_chars.peek().unwrap().is_ascii_digit() { 35 | let mut a_num = 0u64; 36 | let mut b_num = 0u64; 37 | 38 | while a_chars.peek().map_or(false, |c| c.is_ascii_digit()) { 39 | if let Some(digit) = a_chars.next() { 40 | a_num = a_num * 10 + digit.to_digit(10).unwrap() as u64; 41 | } 42 | } 43 | 44 | while b_chars.peek().map_or(false, |c| c.is_ascii_digit()) { 45 | if let Some(digit) = b_chars.next() { 46 | b_num = b_num * 10 + digit.to_digit(10).unwrap() as u64; 47 | } 48 | } 49 | 50 | match a_num.cmp(&b_num) { 51 | std::cmp::Ordering::Equal => continue, 52 | other => return other, 53 | } 54 | } else { 55 | match a_chars.next().unwrap().cmp(&b_chars.next().unwrap()) { 56 | std::cmp::Ordering::Equal => continue, 57 | other => return other, 58 | } 59 | } 60 | } 61 | 62 | match (a_chars.peek(), b_chars.peek()) { 63 | (None, None) => std::cmp::Ordering::Equal, 64 | (None, Some(_)) => std::cmp::Ordering::Less, 65 | (Some(_), None) => std::cmp::Ordering::Greater, 66 | _ => unreachable!(), 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lla/src/commands/plugin_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::config::Config; 2 | use crate::error::Result; 3 | use crate::plugin::PluginManager; 4 | use colored::*; 5 | use dialoguer::MultiSelect; 6 | use lla_plugin_utils::ui::components::LlaDialoguerTheme; 7 | use std::collections::HashSet; 8 | 9 | pub fn list_plugins(plugin_manager: &mut PluginManager) -> Result<()> { 10 | let plugins: Vec<(String, String, String)> = 11 | plugin_manager.list_plugins().into_iter().collect(); 12 | 13 | let plugin_names: Vec = plugins 14 | .iter() 15 | .map(|(name, version, desc)| { 16 | format!( 17 | "{} {} - {}", 18 | name.cyan(), 19 | format!("v{}", version).yellow(), 20 | desc 21 | ) 22 | }) 23 | .collect(); 24 | 25 | let theme = LlaDialoguerTheme::default(); 26 | let prompt = format!( 27 | "{}\n{}\n\nSelect plugins", 28 | "Plugin Manager".cyan().bold(), 29 | "Space to toggle, Enter to confirm".bright_black() 30 | ); 31 | 32 | let selections = MultiSelect::with_theme(&theme) 33 | .with_prompt(prompt) 34 | .items(&plugin_names) 35 | .defaults( 36 | &plugins 37 | .iter() 38 | .map(|(name, _, _)| plugin_manager.enabled_plugins.contains(name)) 39 | .collect::>(), 40 | ) 41 | .interact()?; 42 | 43 | let mut updated_plugins = HashSet::new(); 44 | 45 | for idx in selections { 46 | let (name, _, _) = &plugins[idx]; 47 | updated_plugins.insert(name.to_string()); 48 | } 49 | 50 | for (name, _, _) in &plugins { 51 | if updated_plugins.contains(name) { 52 | plugin_manager.enable_plugin(name)?; 53 | } else { 54 | plugin_manager.disable_plugin(name)?; 55 | } 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | pub fn handle_plugin_action( 62 | config: &mut Config, 63 | plugin_name: &str, 64 | action: &str, 65 | args: &[String], 66 | ) -> Result<()> { 67 | // Resolve plugin alias if exists 68 | let resolved_plugin = config.resolve_plugin_alias(plugin_name); 69 | let mut plugin_manager = PluginManager::new(config.clone()); 70 | plugin_manager.discover_plugins(&config.plugins_dir)?; 71 | plugin_manager.perform_plugin_action(&resolved_plugin, action, args) 72 | } 73 | -------------------------------------------------------------------------------- /plugins/youtube/README.md: -------------------------------------------------------------------------------- 1 | # lla YouTube Plugin 2 | 3 | YouTube search plugin for `lla` with live autosuggestions, history management, and clipboard support. 4 | 5 | ## Features 6 | 7 | - **Live Autocomplete**: Real-time search suggestions from YouTube API as you type 8 | - **Loading States**: Visual spinner while fetching suggestions 9 | - **Smart Search**: Multiple input options (live suggestions, history, clipboard) 10 | - **Search Selected Text**: Instantly search using text from clipboard 11 | - **History Management**: Persistent search history with statistics 12 | 13 | ## Usage 14 | 15 | ```bash 16 | # Perform a YouTube search 17 | lla plugin --name youtube --action search 18 | 19 | # Search with selected/clipboard text 20 | lla plugin --name youtube --action search-selected 21 | 22 | # Manage search history 23 | lla plugin --name youtube --action history 24 | 25 | # Configure preferences 26 | lla plugin --name youtube --action preferences 27 | 28 | # Show help 29 | lla plugin --name youtube --action help 30 | ``` 31 | 32 | ## Configuration 33 | 34 | Config location: `~/.config/lla/plugins/youtube/config.toml` 35 | 36 | ```toml 37 | remember_search_history = true # Enable/disable history persistence 38 | use_clipboard_fallback = true # Enable/disable clipboard fallback 39 | max_history_size = 100 # Maximum history entries 40 | 41 | [colors] 42 | success = "bright_green" 43 | info = "bright_cyan" 44 | warning = "bright_yellow" 45 | error = "bright_red" 46 | prompt = "bright_blue" 47 | link = "bright_magenta" 48 | ``` 49 | 50 | ## Display Examples 51 | 52 | Live Autocomplete: 53 | 54 | ``` 55 | 💡 Enter a search query to see live YouTube suggestions 56 | Search query: rust tutorial 57 | 58 | 🔄 Fetching suggestions from YouTube... 59 | ⠋ Loading suggestions... 60 | 61 | ✨ 10 suggestions found: 62 | Select a search query 63 | > 🎬 rust tutorial (your input) 64 | 💡 rust tutorial for beginners 65 | 💡 rust tutorial 2025 66 | 💡 rust tutorial game 67 | 💡 rust tutorial programming 68 | ... 69 | ``` 70 | 71 | History Statistics: 72 | 73 | ``` 74 | 📊 Search History Statistics: 75 | ────────────────────────────────────────────────── 76 | • Total searches: 42 77 | • Unique queries: 28 78 | • Oldest search: 2025-10-15 09:30:00 79 | • Most recent: 2025-10-20 16:15:30 80 | 81 | 🔥 Top 5 searches: 82 | • rust tutorial (8x) 83 | • golang explained (5x) 84 | • docker basics (3x) 85 | ``` 86 | -------------------------------------------------------------------------------- /plugins/code_complexity/README.md: -------------------------------------------------------------------------------- 1 | # lla Code Complexity Plugin 2 | 3 | A code analysis plugin for `lla` that performs real-time complexity analysis of source code. 4 | 5 | ## Features 6 | 7 | - **Multi-Metric Analysis** 8 | - Cyclomatic & Cognitive Complexity 9 | - Maintainability Index (0-100) 10 | - Function/Class Analysis 11 | - Control Flow & Volume Metrics 12 | - **Smart Thresholds**: Configurable with color-coding 13 | - **Real-Time Statistics**: Continuous metric tracking 14 | - **Detailed Reports**: File and language-level insights 15 | 16 | ## Default Configuration 17 | 18 | ### Complexity Thresholds 19 | 20 | - Low: < 10.0 21 | - Medium: < 20.0 22 | - High: < 30.0 23 | - Very High: ≥ 40.0 24 | 25 | ### Language Support (Default: Rust) 26 | 27 | - Function: `fn` 28 | - Class: `struct`, `impl`, `trait` 29 | - Branch: `if`, `match`, `else` 30 | - Loop: `for`, `while`, `loop` 31 | - Comments: `//`, `/*` 32 | - Max Line Length: 100 33 | - Max Function Length: 50 lines 34 | 35 | ## Usage 36 | 37 | ```bash 38 | # Set complexity thresholds 39 | lla plugin --name code_complexity --action set-thresholds --args 10 20 30 40 40 | 41 | # Show report 42 | lla plugin --name code_complexity --action show-report 43 | ``` 44 | 45 | ## Display Formats 46 | 47 | ### Default 48 | 49 | ``` 50 | [Complexity: 12 (MI: 85.3)] 51 | ``` 52 | 53 | ### Long 54 | 55 | ``` 56 | [Complexity: 12 (MI: 85.3)] 57 | ├── Lines: 150 58 | ├── Functions: 5 59 | ├── Classes: 2 60 | ├── Branches: 8 61 | ├── Loops: 4 62 | ├── Comments: 20 63 | └── Long functions: 64 | ├── process_data (55 lines) 65 | └── analyze_results (60 lines) 66 | ``` 67 | 68 | ## Configuration 69 | 70 | Config file: `~/.config/lla/code_complexity/config.toml` 71 | 72 | ### Language Settings 73 | 74 | ```toml 75 | [languages.Rust] 76 | extensions = ["rs"] 77 | function_patterns = ["fn "] 78 | class_patterns = ["struct ", "impl ", "trait "] 79 | branch_patterns = ["if ", "match ", "else"] 80 | loop_patterns = ["for ", "while ", "loop"] 81 | comment_patterns = ["//", "/*"] 82 | max_line_length = 100 83 | max_function_lines = 50 84 | ``` 85 | 86 | ### Thresholds 87 | 88 | ```toml 89 | [thresholds] 90 | low = 10.0 91 | medium = 20.0 92 | high = 30.0 93 | very_high = 40.0 94 | ``` 95 | 96 | ### Colors 97 | 98 | ```toml 99 | [colors] 100 | low = "bright_green" 101 | medium = "bright_yellow" 102 | high = "bright_red" 103 | very_high = "red" 104 | ``` 105 | -------------------------------------------------------------------------------- /plugins/google_search/README.md: -------------------------------------------------------------------------------- 1 | # lla Google Search Plugin 2 | 3 | Google search plugin for `lla` with live autosuggestions, history management, and clipboard fallback. 4 | 5 | ## Features 6 | 7 | - **Live Autocomplete**: Real-time search suggestions from Google API with loading states 8 | - **Smart Search**: Multiple input options (live suggestions, history, clipboard) 9 | - **History Management**: Persistent search history with statistics and analytics 10 | - **Interactive Interface**: Rich TUI with visual feedback 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Perform a Google search (live suggestions) 16 | lla plugin --name google_search --action search 17 | 18 | # Search with selected/clipboard text (prefills input; live suggestions) 19 | lla plugin --name google_search --action search-selected 20 | 21 | # Manage search history 22 | lla plugin --name google_search --action history 23 | 24 | # Configure preferences 25 | lla plugin --name google_search --action preferences 26 | 27 | # Show help 28 | lla plugin --name google_search --action help 29 | ``` 30 | 31 | ## Configuration 32 | 33 | Config location: `~/.config/lla/plugins/google_search/config.toml` 34 | 35 | ```toml 36 | remember_search_history = true # Enable/disable history persistence 37 | use_clipboard_fallback = true # Enable/disable clipboard fallback 38 | max_history_size = 100 # Maximum history entries 39 | 40 | [colors] 41 | success = "bright_green" 42 | info = "bright_cyan" 43 | warning = "bright_yellow" 44 | error = "bright_red" 45 | prompt = "bright_blue" 46 | ``` 47 | 48 | ## Display Examples 49 | 50 | Live Autocomplete (use ↑/↓ to select, Enter to confirm): 51 | 52 | ``` 53 | 💡 Enter a search query to see live Google suggestions 54 | Search query: rust programming 55 | 56 | 🔄 Fetching suggestions from Google... 57 | ⠋ Loading suggestions... 58 | 59 | ✨ 10 suggestions found: 60 | Select a search query 61 | > 🔍 rust programming (your input) 62 | 💡 rust programming tutorial 63 | 💡 rust programming language 64 | 💡 rust programming for beginners 65 | 💡 rust programming projects 66 | ... 67 | ``` 68 | 69 | History Statistics: 70 | 71 | ``` 72 | 📊 Search History Statistics: 73 | ────────────────────────────────────────────────── 74 | • Total searches: 25 75 | • Unique queries: 18 76 | • Oldest search: 2025-10-15 09:30:00 77 | • Most recent: 2025-10-20 14:30:45 78 | 79 | 🔥 Top 5 searches: 80 | • rust programming tutorial (5x) 81 | • golang best practices (3x) 82 | • python async await (2x) 83 | ``` 84 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/actions.rs: -------------------------------------------------------------------------------- 1 | use lla_plugin_interface::ActionInfo; 2 | use std::collections::HashMap; 3 | 4 | pub struct Action { 5 | pub handler: Box Result<(), String> + Send + Sync>, 6 | pub help: ActionHelp, 7 | } 8 | 9 | pub struct ActionHelp { 10 | pub usage: String, 11 | pub description: String, 12 | pub examples: Vec, 13 | } 14 | 15 | pub struct ActionRegistry { 16 | actions: HashMap, 17 | } 18 | 19 | impl ActionRegistry { 20 | pub fn new() -> Self { 21 | Self { 22 | actions: HashMap::new(), 23 | } 24 | } 25 | 26 | pub fn register(&mut self, name: &str, help: ActionHelp, handler: F) 27 | where 28 | F: Fn(&[String]) -> Result<(), String> + Send + Sync + 'static, 29 | { 30 | self.actions.insert( 31 | name.to_string(), 32 | Action { 33 | handler: Box::new(handler), 34 | help, 35 | }, 36 | ); 37 | } 38 | 39 | pub fn handle(&self, action: &str, args: &[String]) -> Result<(), String> { 40 | match self.actions.get(action) { 41 | Some(action) => (action.handler)(args), 42 | None => Err(format!("Unknown action: {}", action)), 43 | } 44 | } 45 | 46 | pub fn get_help(&self) -> Vec<(&str, &ActionHelp)> { 47 | self.actions 48 | .iter() 49 | .map(|(name, action)| (name.as_str(), &action.help)) 50 | .collect() 51 | } 52 | 53 | pub fn list_actions(&self) -> Vec { 54 | self.actions 55 | .iter() 56 | .map(|(name, action)| ActionInfo { 57 | name: name.clone(), 58 | usage: action.help.usage.clone(), 59 | description: action.help.description.clone(), 60 | examples: action.help.examples.clone(), 61 | }) 62 | .collect() 63 | } 64 | } 65 | 66 | impl Default for ActionRegistry { 67 | fn default() -> Self { 68 | Self::new() 69 | } 70 | } 71 | 72 | #[macro_export] 73 | macro_rules! define_action { 74 | ($registry:expr, $name:expr, $usage:expr, $description:expr, $examples:expr, $handler:expr) => { 75 | $registry.register( 76 | $name, 77 | $crate::actions::ActionHelp { 78 | usage: $usage.to_string(), 79 | description: $description.to_string(), 80 | examples: $examples.iter().map(|s| s.to_string()).collect(), 81 | }, 82 | $handler, 83 | ); 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "naersk": { 4 | "inputs": { 5 | "nixpkgs": "nixpkgs" 6 | }, 7 | "locked": { 8 | "lastModified": 1733346208, 9 | "narHash": "sha256-a4WZp1xQkrnA4BbnKrzJNr+dYoQr5Xneh2syJoddFyE=", 10 | "owner": "nix-community", 11 | "repo": "naersk", 12 | "rev": "378614f37a6bee5a3f2ef4f825a73d948d3ae921", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "nix-community", 17 | "ref": "master", 18 | "repo": "naersk", 19 | "type": "github" 20 | } 21 | }, 22 | "nixpkgs": { 23 | "locked": { 24 | "lastModified": 0, 25 | "narHash": "sha256-9tUadhnZQbWIiYVXH8ncfGXGvkNq3Hag4RCBEMUk7MI=", 26 | "path": "/nix/store/wbqb2mw5kmh9bf04aqbd84c3j6a280h8-source", 27 | "type": "path" 28 | }, 29 | "original": { 30 | "id": "nixpkgs", 31 | "type": "indirect" 32 | } 33 | }, 34 | "nixpkgs_2": { 35 | "locked": { 36 | "lastModified": 1733376361, 37 | "narHash": "sha256-aLJxoTDDSqB+/3orsulE6/qdlX6MzDLIITLZqdgMpqo=", 38 | "owner": "NixOS", 39 | "repo": "nixpkgs", 40 | "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", 41 | "type": "github" 42 | }, 43 | "original": { 44 | "owner": "NixOS", 45 | "ref": "nixpkgs-unstable", 46 | "repo": "nixpkgs", 47 | "type": "github" 48 | } 49 | }, 50 | "root": { 51 | "inputs": { 52 | "naersk": "naersk", 53 | "nixpkgs": "nixpkgs_2", 54 | "utils": "utils" 55 | } 56 | }, 57 | "systems": { 58 | "locked": { 59 | "lastModified": 1681028828, 60 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 61 | "owner": "nix-systems", 62 | "repo": "default", 63 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 64 | "type": "github" 65 | }, 66 | "original": { 67 | "owner": "nix-systems", 68 | "repo": "default", 69 | "type": "github" 70 | } 71 | }, 72 | "utils": { 73 | "inputs": { 74 | "systems": "systems" 75 | }, 76 | "locked": { 77 | "lastModified": 1731533236, 78 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 79 | "owner": "numtide", 80 | "repo": "flake-utils", 81 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 82 | "type": "github" 83 | }, 84 | "original": { 85 | "owner": "numtide", 86 | "repo": "flake-utils", 87 | "type": "github" 88 | } 89 | } 90 | }, 91 | "root": "root", 92 | "version": 7 93 | } 94 | -------------------------------------------------------------------------------- /lla/src/formatter/column_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, PartialEq, Eq)] 2 | pub enum ColumnKey { 3 | Permissions, 4 | Size, 5 | Modified, 6 | Created, 7 | Accessed, 8 | User, 9 | Group, 10 | Name, 11 | Path, 12 | Plugins, 13 | CustomField(String), 14 | } 15 | 16 | pub fn parse_columns(values: &[String]) -> Vec { 17 | let mut columns: Vec = values 18 | .iter() 19 | .map(|value| ColumnKey::from_config(value)) 20 | .collect(); 21 | 22 | if columns.is_empty() { 23 | columns.push(ColumnKey::Name); 24 | } 25 | columns 26 | } 27 | 28 | impl ColumnKey { 29 | pub fn from_config(raw: &str) -> ColumnKey { 30 | let trimmed = raw.trim(); 31 | if trimmed.is_empty() { 32 | return ColumnKey::Name; 33 | } 34 | if let Some(field) = trimmed.strip_prefix("field:") { 35 | return ColumnKey::CustomField(field.trim().to_string()); 36 | } 37 | match trimmed.to_lowercase().as_str() { 38 | "permissions" | "perms" => ColumnKey::Permissions, 39 | "size" => ColumnKey::Size, 40 | "modified" | "modified_at" | "date" => ColumnKey::Modified, 41 | "created" => ColumnKey::Created, 42 | "accessed" | "access" => ColumnKey::Accessed, 43 | "user" | "owner" => ColumnKey::User, 44 | "group" => ColumnKey::Group, 45 | "name" => ColumnKey::Name, 46 | "path" => ColumnKey::Path, 47 | "plugins" | "plugin" => ColumnKey::Plugins, 48 | _ => ColumnKey::CustomField(trimmed.to_string()), 49 | } 50 | } 51 | 52 | pub fn align_right(&self) -> bool { 53 | matches!(self, ColumnKey::Size) 54 | } 55 | 56 | pub fn header_label(&self) -> String { 57 | match self { 58 | ColumnKey::Permissions => "Permissions".to_string(), 59 | ColumnKey::Size => "Size".to_string(), 60 | ColumnKey::Modified => "Modified".to_string(), 61 | ColumnKey::Created => "Created".to_string(), 62 | ColumnKey::Accessed => "Accessed".to_string(), 63 | ColumnKey::User => "User".to_string(), 64 | ColumnKey::Group => "Group".to_string(), 65 | ColumnKey::Name => "Name".to_string(), 66 | ColumnKey::Path => "Path".to_string(), 67 | ColumnKey::Plugins => "Plugins".to_string(), 68 | ColumnKey::CustomField(field) => field.clone(), 69 | } 70 | } 71 | 72 | pub fn is_group(&self) -> bool { 73 | matches!(self, ColumnKey::Group) 74 | } 75 | 76 | pub fn is_plugins(&self) -> bool { 77 | matches!(self, ColumnKey::Plugins) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /plugins/code_snippet_extractor/README.md: -------------------------------------------------------------------------------- 1 | # lla Code Snippet Extractor Plugin 2 | 3 | A plugin for `lla` that extracts, organizes, and manages code snippets with metadata and search capabilities. 4 | 5 | ## Features 6 | 7 | - **Smart Extraction**: Automatic language detection, contextual extraction 8 | - **Organization**: Categories, tags, metadata tracking 9 | - **Search**: Fuzzy search, multi-select operations 10 | - **Interface**: Syntax highlighting, interactive CLI menus 11 | - **Import/Export**: JSON-based snippet management 12 | 13 | ## Configuration 14 | 15 | Config file: `~/.config/lla/code_snippets/config.toml` 16 | 17 | ```toml 18 | [colors] 19 | success = "bright_green" 20 | info = "bright_blue" 21 | error = "bright_red" 22 | name = "bright_yellow" 23 | language = "bright_cyan" 24 | tag = "bright_magenta" 25 | 26 | [syntax_themes] 27 | default = "Solarized (dark)" 28 | ``` 29 | 30 | ## Usage 31 | 32 | ### Basic Operations 33 | 34 | ```bash 35 | # Extract snippet with context 36 | lla plugin --name code_snippet_extractor --action extract --args "file.rs" "function_name" 10 20 3 37 | 38 | # List snippets 39 | lla plugin --name code_snippet_extractor --action list 40 | 41 | # View snippet 42 | lla plugin --name code_snippet_extractor --action get --args "snippet_id" 43 | ``` 44 | 45 | ### Organization 46 | 47 | ```bash 48 | # Add/remove tags 49 | lla plugin --name code_snippet_extractor --action add-tags --args "snippet_id" "tag1" "tag2" 50 | lla plugin --name code_snippet_extractor --action remove-tags --args "snippet_id" "tag1" 51 | 52 | # Category management 53 | lla plugin --name code_snippet_extractor --action set-category --args "snippet_id" "category_name" 54 | ``` 55 | 56 | ### Import/Export 57 | 58 | ```bash 59 | # Export/Import snippets 60 | lla plugin --name code_snippet_extractor --action export --args "snippets.json" 61 | lla plugin --name code_snippet_extractor --action import --args "snippets.json" 62 | ``` 63 | 64 | ## Display Format 65 | 66 | ``` 67 | ───────────────────────────────────── 68 | Example Function 69 | ID: abc123 • Language: rust • Version: v1 70 | ───────────────────────────────────── 71 | 📂 Source: src/example.rs 72 | 🏷️ Tags: #rust #function #example 73 | 📁 Category: Algorithms 74 | 🕒 Created: 2024-01-20 10:30:00 UTC 75 | ───────────────────────────────────── 76 | ◀ Context (3 lines) 77 | 1 │ // Helper functions 78 | ▶ Code (5 lines) 79 | 4 │ fn parse_input(input: &str) -> Option { 80 | 5 │ input.trim().parse().ok() 81 | 6 │ } 82 | ▼ Context (2 lines) 83 | 10 │ // Example usage 84 | ───────────────────────────────────── 85 | ``` 86 | 87 | ## Language Support 88 | 89 | Supports common languages: Rust, Python, JavaScript, TypeScript, Go, C/C++, Java, Ruby, PHP, Shell, HTML, CSS, Markdown, JSON, YAML, XML, SQL 90 | -------------------------------------------------------------------------------- /lla/src/formatter/fuzzy.rs: -------------------------------------------------------------------------------- 1 | use super::FileFormatter; 2 | use crate::error::Result; 3 | use crate::plugin::PluginManager; 4 | use crate::utils::color::*; 5 | use crate::utils::icons::format_with_icon; 6 | use colored::*; 7 | use lla_plugin_interface::proto::DecoratedEntry; 8 | use std::fs::Permissions; 9 | use std::os::unix::fs::PermissionsExt; 10 | use std::path::Path; 11 | use std::time::{Duration, SystemTime}; 12 | 13 | pub struct FuzzyFormatter { 14 | pub show_icons: bool, 15 | pub permission_format: String, 16 | } 17 | 18 | impl FuzzyFormatter { 19 | pub fn new(show_icons: bool, permission_format: String) -> Self { 20 | Self { 21 | show_icons, 22 | permission_format, 23 | } 24 | } 25 | 26 | fn format_entry( 27 | &self, 28 | entry: &DecoratedEntry, 29 | _query: &str, 30 | selected: bool, 31 | _plugin_manager: &mut PluginManager, 32 | ) -> String { 33 | let metadata = entry.metadata.as_ref().cloned().unwrap_or_default(); 34 | let path = Path::new(&entry.path); 35 | 36 | let colored_name = colorize_file_name(path).to_string(); 37 | let name_display = if self.show_icons { 38 | colorize_file_name_with_icon( 39 | path, 40 | format_with_icon(path, colored_name, self.show_icons), 41 | ) 42 | .to_string() 43 | } else { 44 | colored_name 45 | }; 46 | 47 | let perms = Permissions::from_mode(metadata.permissions); 48 | let perms_display = colorize_permissions(&perms, Some(&self.permission_format)); 49 | let size = colorize_size(metadata.size); 50 | let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(metadata.modified); 51 | let date = colorize_date(&modified); 52 | 53 | let prefix = if selected { "→" } else { " " }; 54 | 55 | format!( 56 | " {} {} {} {} {} {}", 57 | prefix, 58 | name_display, 59 | entry.path.bright_black(), 60 | perms_display, 61 | size, 62 | date 63 | ) 64 | } 65 | } 66 | 67 | impl FileFormatter for FuzzyFormatter { 68 | fn format_files( 69 | &self, 70 | files: &[DecoratedEntry], 71 | plugin_manager: &mut PluginManager, 72 | _depth: Option, 73 | ) -> Result { 74 | if files.is_empty() { 75 | return Ok(String::new()); 76 | } 77 | 78 | let mut output = String::new(); 79 | for (idx, file) in files.iter().enumerate() { 80 | let line = self.format_entry(file, "", idx == 0, plugin_manager); 81 | output.push_str(&line); 82 | output.push_str("\n"); 83 | } 84 | 85 | Ok(output) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /plugins/file_organizer/src/strategies/size.rs: -------------------------------------------------------------------------------- 1 | use super::OrganizationStrategy; 2 | use crate::config::SizeStrategyConfig; 3 | use std::{ 4 | fs, 5 | path::{Path, PathBuf}, 6 | }; 7 | 8 | pub struct SizeStrategy { 9 | config: SizeStrategyConfig, 10 | } 11 | 12 | impl SizeStrategy { 13 | pub fn new(config: SizeStrategyConfig) -> Self { 14 | Self { config } 15 | } 16 | 17 | fn get_size_category(&self, size: u64) -> Option { 18 | self.config 19 | .ranges 20 | .iter() 21 | .find(|range| match range.max_bytes { 22 | Some(max) => size <= max, 23 | None => true, 24 | }) 25 | .map(|range| range.name.clone()) 26 | } 27 | } 28 | 29 | impl OrganizationStrategy for SizeStrategy { 30 | fn organize(&self, dir: &Path, dry_run: bool) -> Result, String> { 31 | if !self.config.enabled { 32 | return Ok(Vec::new()); 33 | } 34 | 35 | let mut moves = Vec::new(); 36 | let entries = fs::read_dir(dir).map_err(|e| format!("Failed to read directory: {}", e))?; 37 | 38 | for entry in entries.filter_map(Result::ok) { 39 | let path = entry.path(); 40 | if path.is_dir() { 41 | continue; 42 | } 43 | 44 | let metadata = fs::metadata(&path) 45 | .map_err(|e| format!("Failed to get metadata for '{}': {}", path.display(), e))?; 46 | 47 | if let Some(category) = self.get_size_category(metadata.len()) { 48 | let target_dir = dir.join(&category); 49 | let target_path = target_dir.join(path.file_name().unwrap()); 50 | 51 | if !target_dir.exists() && !dry_run { 52 | fs::create_dir_all(&target_dir) 53 | .map_err(|e| format!("Failed to create directory: {}", e))?; 54 | } 55 | 56 | moves.push((path, target_path)); 57 | } 58 | } 59 | 60 | Ok(moves) 61 | } 62 | 63 | fn execute_moves(&self, moves: Vec<(PathBuf, PathBuf)>) -> Result<(), String> { 64 | for (source, target) in moves { 65 | if let Some(parent) = target.parent() { 66 | if !parent.exists() { 67 | fs::create_dir_all(parent) 68 | .map_err(|e| format!("Failed to create directory: {}", e))?; 69 | } 70 | } 71 | 72 | fs::rename(&source, &target).map_err(|e| { 73 | format!( 74 | "Failed to move '{}' to '{}': {}", 75 | source.display(), 76 | target.display(), 77 | e 78 | ) 79 | })?; 80 | } 81 | Ok(()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Workflows Overview 2 | 3 | This repository uses a staged workflow strategy so CI runs only when it matters and releases append assets incrementally without rebuilding what already exists. 4 | 5 | ## CI (`ci.yml`) 6 | - Triggered on pushes and pull requests to `main` **only** when Rust sources, manifests, proto files, scripts, or the CI definition itself change. 7 | - Runs formatting, Clippy (non-blocking), tests, and a release-mode build matrix (Linux + macOS, multiple targets). 8 | - Eliminating docs/completions-only triggers keeps CI fast and avoids redundant runners. 9 | 10 | ## Release Chain 11 | The release process is split into four independent workflows that communicate via `repository_dispatch` events. Each stage checks the GitHub Release for previously uploaded assets and skips work when everything is already present. 12 | 13 | 1. **`release.yml` – Release Core Bins** 14 | - Starts when a release PR (with the `release` label) merges into `main`, or via manual `workflow_dispatch`. 15 | - Ensures the version changed, builds the core binaries matrix, publishes crates to crates.io, and creates/updates the GitHub Release with notes + SHA256 sums. 16 | - Uploads only missing binaries/checksums and dispatches `release-plugins` when done. 17 | 18 | 2. **`release-plugins.yml` – Release Plugins** 19 | - Triggered by the dispatch from the core workflow (or manually). 20 | - Rebuilds plugin archives per target only when the release lacks the corresponding `.tar.gz`/`.zip`. 21 | - Uploads the missing archives and dispatches `release-packages`. 22 | 23 | 3. **`release-packages.yml` – Release Packages** 24 | - Downloads the Linux binaries directly from the release, runs nFPM to produce `.deb`, `.rpm`, `.apk`, `.pkg.tar.zst`, and uploads any missing package artifacts. 25 | - Dispatches `release-themes` once packaging succeeds. 26 | 27 | 4. **`release-themes.yml` – Release Themes** 28 | - Zips the `themes/` directory and uploads `themes.zip` only if it does not already exist on the release. 29 | 30 | ### Helper Script 31 | `.github/scripts/release_helpers.sh` provides reusable functions: 32 | - `ensure_release_exists` – create/edit releases safely. 33 | - `asset_exists` – check if an asset is already attached. 34 | - `dispatch_next_stage` – emit the next `repository_dispatch` event with tag + version payload. 35 | Every workflow sources this script to guarantee idempotent uploads and clean chaining. 36 | 37 | ## Manual Dispatch Tips 38 | - To resume a stage, open the desired workflow and use **Run workflow** supplying `release_tag` (e.g., `v1.2.3`). `release_version` defaults to the tag without `v` when omitted. 39 | - Each workflow will validate the release exists and skip gracefully if all assets are already present. 40 | 41 | ## Runner Requirements 42 | - GitHub-hosted runners already include `gh` and `jq`. For self-hosted runners, ensure both tools plus the Rust toolchain, protoc, and nFPM (for the packaging stage) are pre-installed. 43 | 44 | -------------------------------------------------------------------------------- /plugins/file_organizer/src/strategies/type.rs: -------------------------------------------------------------------------------- 1 | use super::OrganizationStrategy; 2 | use crate::config::TypeStrategyConfig; 3 | use std::{ 4 | collections::HashMap, 5 | fs, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | pub struct TypeStrategy { 10 | config: TypeStrategyConfig, 11 | extension_to_category: HashMap, 12 | } 13 | 14 | impl TypeStrategy { 15 | pub fn new(config: TypeStrategyConfig) -> Self { 16 | let mut extension_to_category = HashMap::new(); 17 | for (category, extensions) in &config.categories { 18 | for ext in extensions { 19 | extension_to_category.insert(ext.clone(), category.clone()); 20 | } 21 | } 22 | Self { 23 | config, 24 | extension_to_category, 25 | } 26 | } 27 | 28 | fn get_category(&self, path: &Path) -> Option { 29 | path.extension() 30 | .and_then(|ext| ext.to_str()) 31 | .map(|ext| ext.to_lowercase()) 32 | .and_then(|ext| self.extension_to_category.get(&ext)) 33 | .cloned() 34 | } 35 | } 36 | 37 | impl OrganizationStrategy for TypeStrategy { 38 | fn organize(&self, dir: &Path, dry_run: bool) -> Result, String> { 39 | if !self.config.enabled { 40 | return Ok(Vec::new()); 41 | } 42 | 43 | let mut moves = Vec::new(); 44 | let entries = fs::read_dir(dir).map_err(|e| format!("Failed to read directory: {}", e))?; 45 | 46 | for entry in entries.filter_map(Result::ok) { 47 | let path = entry.path(); 48 | if path.is_dir() { 49 | continue; 50 | } 51 | 52 | if let Some(category) = self.get_category(&path) { 53 | let target_dir = dir.join(&category); 54 | let target_path = target_dir.join(path.file_name().unwrap()); 55 | 56 | if !target_dir.exists() && !dry_run { 57 | fs::create_dir_all(&target_dir) 58 | .map_err(|e| format!("Failed to create directory: {}", e))?; 59 | } 60 | 61 | moves.push((path, target_path)); 62 | } 63 | } 64 | 65 | Ok(moves) 66 | } 67 | 68 | fn execute_moves(&self, moves: Vec<(PathBuf, PathBuf)>) -> Result<(), String> { 69 | for (source, target) in moves { 70 | if let Some(parent) = target.parent() { 71 | if !parent.exists() { 72 | fs::create_dir_all(parent) 73 | .map_err(|e| format!("Failed to create directory: {}", e))?; 74 | } 75 | } 76 | 77 | fs::rename(&source, &target).map_err(|e| { 78 | format!( 79 | "Failed to move '{}' to '{}': {}", 80 | source.display(), 81 | target.display(), 82 | e 83 | ) 84 | })?; 85 | } 86 | Ok(()) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lla/src/error.rs: -------------------------------------------------------------------------------- 1 | use colored::*; 2 | use std::fmt; 3 | use std::io; 4 | 5 | #[allow(dead_code)] 6 | #[derive(Debug)] 7 | pub enum ConfigErrorKind { 8 | InvalidFormat(String), 9 | InvalidValue(String, String), 10 | MissingField(String), 11 | InvalidPath(String), 12 | ValidationError(String), 13 | } 14 | 15 | impl fmt::Display for ConfigErrorKind { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | match self { 18 | ConfigErrorKind::InvalidFormat(msg) => write!(f, "{}", msg), 19 | ConfigErrorKind::InvalidValue(field, msg) => write!(f, "{}: {}", field.bold(), msg), 20 | ConfigErrorKind::MissingField(field) => write!(f, "missing field: {}", field.bold()), 21 | ConfigErrorKind::InvalidPath(path) => write!(f, "invalid path: {}", path.bold()), 22 | ConfigErrorKind::ValidationError(msg) => write!(f, "{}", msg), 23 | } 24 | } 25 | } 26 | 27 | #[allow(dead_code)] 28 | #[derive(Debug)] 29 | pub enum LlaError { 30 | Io(io::Error), 31 | Parse(String), 32 | Config(ConfigErrorKind), 33 | Plugin(String), 34 | Filter(String), 35 | Other(String), 36 | } 37 | 38 | impl fmt::Display for LlaError { 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 | match self { 41 | LlaError::Io(err) => write!(f, "{}", err), 42 | LlaError::Parse(msg) => write!(f, "{}", msg), 43 | LlaError::Config(kind) => write!(f, "{}", kind), 44 | LlaError::Plugin(msg) => write!(f, "{}", msg), 45 | LlaError::Filter(msg) => write!(f, "{}", msg), 46 | LlaError::Other(msg) => write!(f, "{}", msg), 47 | } 48 | } 49 | } 50 | 51 | impl std::error::Error for LlaError { 52 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 53 | match self { 54 | LlaError::Io(err) => Some(err), 55 | _ => None, 56 | } 57 | } 58 | } 59 | 60 | impl From for LlaError { 61 | fn from(err: io::Error) -> Self { 62 | LlaError::Io(err) 63 | } 64 | } 65 | 66 | impl From for LlaError { 67 | fn from(err: toml::de::Error) -> Self { 68 | LlaError::Config(ConfigErrorKind::InvalidFormat(err.to_string())) 69 | } 70 | } 71 | 72 | impl From for LlaError { 73 | fn from(err: dialoguer::Error) -> Self { 74 | LlaError::Plugin(format!("interactive mode error: {}", err)) 75 | } 76 | } 77 | 78 | impl From for LlaError { 79 | fn from(err: serde_json::Error) -> Self { 80 | LlaError::Plugin(err.to_string()) 81 | } 82 | } 83 | 84 | impl From for LlaError { 85 | fn from(err: csv::Error) -> Self { 86 | if let csv::ErrorKind::Io(io_err) = err.kind() { 87 | LlaError::Io(std::io::Error::new(io_err.kind(), io_err.to_string())) 88 | } else { 89 | LlaError::Other(format!("CSV error: {}", err)) 90 | } 91 | } 92 | } 93 | 94 | pub type Result = std::result::Result; 95 | -------------------------------------------------------------------------------- /themes/moonlight.toml: -------------------------------------------------------------------------------- 1 | name = "moonlight" 2 | author = "Mohamed Achaq" 3 | description = "A minimal theme with subtle blue-gray tones inspired by moonlit nights" 4 | 5 | [colors] 6 | file = "#C8D3E3" 7 | directory = "#E2E8F0" 8 | symlink = "#A9B8D2" 9 | executable = "#E2E8F0" 10 | 11 | size = "#7A8599" 12 | date = "#7A8599" 13 | user = "#A9B8D2" 14 | group = "#677387" 15 | 16 | permission_dir = "#E2E8F0" 17 | permission_read = "#C8D3E3" 18 | permission_write = "#B4C2D6" 19 | permission_exec = "#E2E8F0" 20 | permission_none = "#363B47" 21 | 22 | [special_files] 23 | [special_files.folders] 24 | "node_modules" = "#363B47" 25 | "target" = "#363B47" 26 | "dist" = "#363B47" 27 | ".git" = "#E2E8F0" 28 | "build" = "#363B47" 29 | ".cache" = "#363B47" 30 | "coverage" = "#363B47" 31 | "vendor" = "#363B47" 32 | 33 | "*-env" = "#E2E8F0" 34 | "venv" = "#E2E8F0" 35 | ".env" = "#E2E8F0" 36 | "env" = "#E2E8F0" 37 | "*.d" = "#A9B8D2" 38 | 39 | "*_cache" = "#363B47" 40 | "*-cache" = "#363B47" 41 | ".terraform" = "#363B47" 42 | 43 | [special_files.dotfiles] 44 | ".gitignore" = "#A9B8D2" 45 | ".env" = "#E2E8F0" 46 | ".dockerignore" = "#A9B8D2" 47 | ".editorconfig" = "#A9B8D2" 48 | ".prettierrc" = "#A9B8D2" 49 | ".eslintrc" = "#A9B8D2" 50 | ".babelrc" = "#A9B8D2" 51 | ".npmrc" = "#A9B8D2" 52 | ".yarnrc" = "#A9B8D2" 53 | ".zshrc" = "#A9B8D2" 54 | ".bashrc" = "#A9B8D2" 55 | 56 | [special_files.exact_match] 57 | "Dockerfile" = "#A9B8D2" 58 | "docker-compose.yml" = "#A9B8D2" 59 | "Makefile" = "#E2E8F0" 60 | "CMakeLists.txt" = "#E2E8F0" 61 | 62 | "README.md" = "#E2E8F0" 63 | "LICENSE" = "#E2E8F0" 64 | "CHANGELOG.md" = "#E2E8F0" 65 | "CONTRIBUTING.md" = "#E2E8F0" 66 | 67 | "package.json" = "#E2E8F0" 68 | "package-lock.json" = "#363B47" 69 | "yarn.lock" = "#363B47" 70 | "Cargo.toml" = "#E2E8F0" 71 | "Cargo.lock" = "#363B47" 72 | "composer.json" = "#E2E8F0" 73 | "go.mod" = "#A9B8D2" 74 | "go.sum" = "#363B47" 75 | 76 | [special_files.patterns] 77 | "*rc" = "#A9B8D2" 78 | "*.config.*" = "#A9B8D2" 79 | "*.conf" = "#A9B8D2" 80 | 81 | "*.min.*" = "#363B47" 82 | "*.map" = "#363B47" 83 | "*.lock" = "#363B47" 84 | 85 | "*.test.*" = "#E2E8F0" 86 | "*.spec.*" = "#E2E8F0" 87 | "*_test.*" = "#E2E8F0" 88 | "*_spec.*" = "#E2E8F0" 89 | 90 | [extensions.groups] 91 | source = ["rs", "py", "js", "ts", "java", "c", "cpp", "go", "rb", "php", "swift", "kt"] 92 | config = ["json", "yml", "yaml", "toml", "ini", "conf"] 93 | doc = ["md", "txt", "pdf", "doc", "docx", "rtf"] 94 | web = ["html", "css", "scss", "sass"] 95 | script = ["sh", "bash", "zsh", "fish"] 96 | data = ["csv", "sql", "db"] 97 | media = ["jpg", "png", "gif", "mp3", "mp4"] 98 | archive = ["zip", "tar", "gz", "7z"] 99 | 100 | [extensions.colors] 101 | source = "#C8D3E3" 102 | config = "#C8D3E3" 103 | doc = "#C8D3E3" 104 | web = "#C8D3E3" 105 | script = "#C8D3E3" 106 | data = "#C8D3E3" 107 | media = "#C8D3E3" 108 | archive = "#C8D3E3" 109 | 110 | rs = "#E2E8F0" 111 | py = "#E2E8F0" 112 | js = "#E2E8F0" 113 | ts = "#E2E8F0" 114 | go = "#E2E8F0" 115 | java = "#E2E8F0" 116 | -------------------------------------------------------------------------------- /themes/zen.toml: -------------------------------------------------------------------------------- 1 | name = "zen" 2 | author = "Mohamed Achaq" 3 | description = "A minimal monochromatic theme emphasizing simplicity and clarity through subtle shades" 4 | 5 | [colors] 6 | file = "#D4D4D4" 7 | directory = "#FFFFFF" 8 | symlink = "#A8A8A8" 9 | executable = "#FFFFFF" 10 | 11 | size = "#808080" 12 | date = "#808080" 13 | user = "#A8A8A8" 14 | group = "#707070" 15 | 16 | permission_dir = "#FFFFFF" 17 | permission_read = "#D4D4D4" 18 | permission_write = "#BEBEBE" 19 | permission_exec = "#FFFFFF" 20 | permission_none = "#404040" 21 | 22 | [special_files] 23 | [special_files.folders] 24 | "node_modules" = "#404040" 25 | "target" = "#404040" 26 | "dist" = "#404040" 27 | ".git" = "#FFFFFF" 28 | "build" = "#404040" 29 | ".cache" = "#404040" 30 | "coverage" = "#404040" 31 | "vendor" = "#404040" 32 | 33 | "*-env" = "#FFFFFF" 34 | "venv" = "#FFFFFF" 35 | ".env" = "#FFFFFF" 36 | "env" = "#FFFFFF" 37 | "*.d" = "#A8A8A8" 38 | 39 | "*_cache" = "#404040" 40 | "*-cache" = "#404040" 41 | ".terraform" = "#404040" 42 | 43 | [special_files.dotfiles] 44 | ".gitignore" = "#A8A8A8" 45 | ".env" = "#FFFFFF" 46 | ".dockerignore" = "#A8A8A8" 47 | ".editorconfig" = "#A8A8A8" 48 | ".prettierrc" = "#A8A8A8" 49 | ".eslintrc" = "#A8A8A8" 50 | ".babelrc" = "#A8A8A8" 51 | ".npmrc" = "#A8A8A8" 52 | ".yarnrc" = "#A8A8A8" 53 | ".zshrc" = "#A8A8A8" 54 | ".bashrc" = "#A8A8A8" 55 | 56 | [special_files.exact_match] 57 | "Dockerfile" = "#A8A8A8" 58 | "docker-compose.yml" = "#A8A8A8" 59 | "Makefile" = "#FFFFFF" 60 | "CMakeLists.txt" = "#FFFFFF" 61 | 62 | "README.md" = "#FFFFFF" 63 | "LICENSE" = "#FFFFFF" 64 | "CHANGELOG.md" = "#FFFFFF" 65 | "CONTRIBUTING.md" = "#FFFFFF" 66 | 67 | "package.json" = "#FFFFFF" 68 | "package-lock.json" = "#404040" 69 | "yarn.lock" = "#404040" 70 | "Cargo.toml" = "#FFFFFF" 71 | "Cargo.lock" = "#404040" 72 | "composer.json" = "#FFFFFF" 73 | "go.mod" = "#A8A8A8" 74 | "go.sum" = "#404040" 75 | 76 | [special_files.patterns] 77 | "*rc" = "#A8A8A8" 78 | "*.config.*" = "#A8A8A8" 79 | "*.conf" = "#A8A8A8" 80 | 81 | "*.min.*" = "#404040" 82 | "*.map" = "#404040" 83 | "*.lock" = "#404040" 84 | 85 | "*.test.*" = "#FFFFFF" 86 | "*.spec.*" = "#FFFFFF" 87 | "*_test.*" = "#FFFFFF" 88 | "*_spec.*" = "#FFFFFF" 89 | 90 | [extensions.groups] 91 | code = ["rs", "py", "js", "ts", "java", "c", "cpp", "go", "rb", "php", "swift", "kt"] 92 | markup = ["html", "xml", "md", "yml", "json", "toml"] 93 | style = ["css", "scss", "sass", "less"] 94 | shell = ["sh", "bash", "zsh", "fish"] 95 | data = ["csv", "sql", "sqlite", "db"] 96 | doc = ["txt", "pdf", "doc", "docx", "rtf"] 97 | media = ["jpg", "png", "gif", "mp3", "mp4", "wav"] 98 | archive = ["zip", "tar", "gz", "7z"] 99 | 100 | [extensions.colors] 101 | code = "#D4D4D4" 102 | markup = "#D4D4D4" 103 | style = "#D4D4D4" 104 | shell = "#D4D4D4" 105 | data = "#D4D4D4" 106 | doc = "#D4D4D4" 107 | media = "#D4D4D4" 108 | archive = "#D4D4D4" 109 | 110 | rs = "#FFFFFF" 111 | py = "#FFFFFF" 112 | js = "#FFFFFF" 113 | ts = "#FFFFFF" 114 | go = "#FFFFFF" 115 | java = "#FFFFFF" 116 | -------------------------------------------------------------------------------- /lla/src/formatter/csv.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use crate::plugin::PluginManager; 3 | use lla_plugin_interface::proto::DecoratedEntry; 4 | 5 | use super::serializable::{find_git_root, get_git_status_map, to_serializable}; 6 | 7 | pub fn write_csv_stream( 8 | entries: I, 9 | _plugin_manager: &mut PluginManager, 10 | include_git_status: bool, 11 | ) -> Result<()> 12 | where 13 | I: IntoIterator, 14 | { 15 | let stdout = std::io::stdout(); 16 | let handle = stdout.lock(); 17 | let mut wtr = csv::Writer::from_writer(handle); 18 | 19 | wtr.write_record(&[ 20 | "path", 21 | "name", 22 | "extension", 23 | "file_type", 24 | "size_bytes", 25 | "modified", 26 | "created", 27 | "accessed", 28 | "mode_octal", 29 | "owner_user", 30 | "owner_group", 31 | "inode", 32 | "hard_links", 33 | "symlink_target", 34 | "is_hidden", 35 | "git_status", 36 | ])?; 37 | 38 | let mut git_status_map = None; 39 | let mut git_root = None; 40 | 41 | for entry in entries { 42 | let git_status = if include_git_status { 43 | if git_root.is_none() { 44 | if let Some(parent) = std::path::Path::new(&entry.path).parent() { 45 | if let Some(root) = find_git_root(parent) { 46 | git_root = Some(root.clone()); 47 | git_status_map = Some(get_git_status_map(&root)); 48 | } 49 | } 50 | } 51 | if let Some(root) = &git_root { 52 | let full = std::path::Path::new(&entry.path); 53 | let rel = full.strip_prefix(root).unwrap_or(full); 54 | let rel_str = rel.to_string_lossy().to_string(); 55 | git_status_map 56 | .as_ref() 57 | .and_then(|m| m.get(&rel_str)) 58 | .cloned() 59 | } else { 60 | None 61 | } 62 | } else { 63 | None 64 | }; 65 | 66 | let serial = to_serializable(&entry, git_status); 67 | 68 | wtr.write_record(&[ 69 | serial.path, 70 | serial.name, 71 | serial.extension.unwrap_or_default(), 72 | serial.file_type, 73 | serial.size_bytes.to_string(), 74 | serial.modified, 75 | serial.created.unwrap_or_default(), 76 | serial.accessed.unwrap_or_default(), 77 | serial.mode_octal, 78 | serial.owner_user.unwrap_or_default(), 79 | serial.owner_group.unwrap_or_default(), 80 | serial.inode.map(|v| v.to_string()).unwrap_or_default(), 81 | serial.hard_links.map(|v| v.to_string()).unwrap_or_default(), 82 | serial.symlink_target.unwrap_or_default(), 83 | serial.is_hidden.to_string(), 84 | serial.git_status.unwrap_or_default(), 85 | ])?; 86 | } 87 | 88 | wtr.flush()?; 89 | Ok(()) 90 | } 91 | -------------------------------------------------------------------------------- /plugins/file_organizer/src/strategies/date.rs: -------------------------------------------------------------------------------- 1 | use super::OrganizationStrategy; 2 | use crate::config::DateStrategyConfig; 3 | use chrono::{DateTime, Local, TimeZone}; 4 | use std::{ 5 | fs, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | pub struct DateStrategy { 10 | config: DateStrategyConfig, 11 | } 12 | 13 | impl DateStrategy { 14 | pub fn new(config: DateStrategyConfig) -> Self { 15 | Self { config } 16 | } 17 | 18 | fn get_date_from_metadata(&self, path: &Path) -> Option> { 19 | path.metadata() 20 | .ok()? 21 | .modified() 22 | .ok()? 23 | .duration_since(std::time::UNIX_EPOCH) 24 | .ok() 25 | .map(|d| Local.timestamp_opt(d.as_secs() as i64, 0).single())? 26 | } 27 | 28 | fn format_date_path(&self, date: DateTime) -> PathBuf { 29 | let path_str = match self.config.group_by.as_str() { 30 | "year" => date.format("%Y").to_string(), 31 | "day" => date.format("%Y/%m/%d").to_string(), 32 | _ => date.format("%Y/%m").to_string(), 33 | }; 34 | PathBuf::from(path_str) 35 | } 36 | } 37 | 38 | impl OrganizationStrategy for DateStrategy { 39 | fn organize(&self, dir: &Path, dry_run: bool) -> Result, String> { 40 | if !self.config.enabled { 41 | return Ok(Vec::new()); 42 | } 43 | 44 | let mut moves = Vec::new(); 45 | let entries = fs::read_dir(dir).map_err(|e| format!("Failed to read directory: {}", e))?; 46 | 47 | for entry in entries.filter_map(Result::ok) { 48 | let path = entry.path(); 49 | if path.is_dir() { 50 | continue; 51 | } 52 | 53 | if let Some(date) = self.get_date_from_metadata(&path) { 54 | let relative_date_path = self.format_date_path(date); 55 | let target_dir = dir.join(&relative_date_path); 56 | let target_path = target_dir.join(path.file_name().unwrap()); 57 | 58 | if !target_dir.exists() && !dry_run { 59 | fs::create_dir_all(&target_dir) 60 | .map_err(|e| format!("Failed to create directory: {}", e))?; 61 | } 62 | 63 | moves.push((path, target_path)); 64 | } 65 | } 66 | 67 | Ok(moves) 68 | } 69 | 70 | fn execute_moves(&self, moves: Vec<(PathBuf, PathBuf)>) -> Result<(), String> { 71 | for (source, target) in moves { 72 | if let Some(parent) = target.parent() { 73 | if !parent.exists() { 74 | fs::create_dir_all(parent) 75 | .map_err(|e| format!("Failed to create directory: {}", e))?; 76 | } 77 | } 78 | 79 | fs::rename(&source, &target).map_err(|e| { 80 | format!( 81 | "Failed to move '{}' to '{}': {}", 82 | source.display(), 83 | target.display(), 84 | e 85 | ) 86 | })?; 87 | } 88 | Ok(()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /plugins/sizeviz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "colored" 7 | version = "2.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 10 | dependencies = [ 11 | "lazy_static", 12 | "windows-sys", 13 | ] 14 | 15 | [[package]] 16 | name = "lazy_static" 17 | version = "1.5.0" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 20 | 21 | [[package]] 22 | name = "lla_plugin_interface" 23 | version = "0.1.1" 24 | 25 | [[package]] 26 | name = "sizeviz" 27 | version = "0.1.0" 28 | dependencies = [ 29 | "colored", 30 | "lla_plugin_interface", 31 | ] 32 | 33 | [[package]] 34 | name = "windows-sys" 35 | version = "0.48.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 38 | dependencies = [ 39 | "windows-targets", 40 | ] 41 | 42 | [[package]] 43 | name = "windows-targets" 44 | version = "0.48.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 47 | dependencies = [ 48 | "windows_aarch64_gnullvm", 49 | "windows_aarch64_msvc", 50 | "windows_i686_gnu", 51 | "windows_i686_msvc", 52 | "windows_x86_64_gnu", 53 | "windows_x86_64_gnullvm", 54 | "windows_x86_64_msvc", 55 | ] 56 | 57 | [[package]] 58 | name = "windows_aarch64_gnullvm" 59 | version = "0.48.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 62 | 63 | [[package]] 64 | name = "windows_aarch64_msvc" 65 | version = "0.48.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 68 | 69 | [[package]] 70 | name = "windows_i686_gnu" 71 | version = "0.48.5" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 74 | 75 | [[package]] 76 | name = "windows_i686_msvc" 77 | version = "0.48.5" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 80 | 81 | [[package]] 82 | name = "windows_x86_64_gnu" 83 | version = "0.48.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 86 | 87 | [[package]] 88 | name = "windows_x86_64_gnullvm" 89 | version = "0.48.5" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 92 | 93 | [[package]] 94 | name = "windows_x86_64_msvc" 95 | version = "0.48.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 98 | -------------------------------------------------------------------------------- /plugins/git_status/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "colored" 7 | version = "2.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 10 | dependencies = [ 11 | "lazy_static", 12 | "windows-sys", 13 | ] 14 | 15 | [[package]] 16 | name = "git_status" 17 | version = "0.1.0" 18 | dependencies = [ 19 | "colored", 20 | "lla_plugin_interface", 21 | ] 22 | 23 | [[package]] 24 | name = "lazy_static" 25 | version = "1.5.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 28 | 29 | [[package]] 30 | name = "lla_plugin_interface" 31 | version = "0.1.1" 32 | 33 | [[package]] 34 | name = "windows-sys" 35 | version = "0.48.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 38 | dependencies = [ 39 | "windows-targets", 40 | ] 41 | 42 | [[package]] 43 | name = "windows-targets" 44 | version = "0.48.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 47 | dependencies = [ 48 | "windows_aarch64_gnullvm", 49 | "windows_aarch64_msvc", 50 | "windows_i686_gnu", 51 | "windows_i686_msvc", 52 | "windows_x86_64_gnu", 53 | "windows_x86_64_gnullvm", 54 | "windows_x86_64_msvc", 55 | ] 56 | 57 | [[package]] 58 | name = "windows_aarch64_gnullvm" 59 | version = "0.48.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 62 | 63 | [[package]] 64 | name = "windows_aarch64_msvc" 65 | version = "0.48.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 68 | 69 | [[package]] 70 | name = "windows_i686_gnu" 71 | version = "0.48.5" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 74 | 75 | [[package]] 76 | name = "windows_i686_msvc" 77 | version = "0.48.5" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 80 | 81 | [[package]] 82 | name = "windows_x86_64_gnu" 83 | version = "0.48.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 86 | 87 | [[package]] 88 | name = "windows_x86_64_gnullvm" 89 | version = "0.48.5" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 92 | 93 | [[package]] 94 | name = "windows_x86_64_msvc" 95 | version = "0.48.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 98 | -------------------------------------------------------------------------------- /plugins/categorizer/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "categorizer" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "colored", 10 | "lla_plugin_interface", 11 | ] 12 | 13 | [[package]] 14 | name = "colored" 15 | version = "2.1.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 18 | dependencies = [ 19 | "lazy_static", 20 | "windows-sys", 21 | ] 22 | 23 | [[package]] 24 | name = "lazy_static" 25 | version = "1.5.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 28 | 29 | [[package]] 30 | name = "lla_plugin_interface" 31 | version = "0.1.1" 32 | 33 | [[package]] 34 | name = "windows-sys" 35 | version = "0.48.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 38 | dependencies = [ 39 | "windows-targets", 40 | ] 41 | 42 | [[package]] 43 | name = "windows-targets" 44 | version = "0.48.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 47 | dependencies = [ 48 | "windows_aarch64_gnullvm", 49 | "windows_aarch64_msvc", 50 | "windows_i686_gnu", 51 | "windows_i686_msvc", 52 | "windows_x86_64_gnu", 53 | "windows_x86_64_gnullvm", 54 | "windows_x86_64_msvc", 55 | ] 56 | 57 | [[package]] 58 | name = "windows_aarch64_gnullvm" 59 | version = "0.48.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 62 | 63 | [[package]] 64 | name = "windows_aarch64_msvc" 65 | version = "0.48.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 68 | 69 | [[package]] 70 | name = "windows_i686_gnu" 71 | version = "0.48.5" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 74 | 75 | [[package]] 76 | name = "windows_i686_msvc" 77 | version = "0.48.5" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 80 | 81 | [[package]] 82 | name = "windows_x86_64_gnu" 83 | version = "0.48.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 86 | 87 | [[package]] 88 | name = "windows_x86_64_gnullvm" 89 | version = "0.48.5" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 92 | 93 | [[package]] 94 | name = "windows_x86_64_msvc" 95 | version = "0.48.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 98 | -------------------------------------------------------------------------------- /plugins/code_complexity/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "code_complexity" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "colored", 10 | "lla_plugin_interface", 11 | ] 12 | 13 | [[package]] 14 | name = "colored" 15 | version = "2.1.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 18 | dependencies = [ 19 | "lazy_static", 20 | "windows-sys", 21 | ] 22 | 23 | [[package]] 24 | name = "lazy_static" 25 | version = "1.5.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 28 | 29 | [[package]] 30 | name = "lla_plugin_interface" 31 | version = "0.1.1" 32 | 33 | [[package]] 34 | name = "windows-sys" 35 | version = "0.48.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 38 | dependencies = [ 39 | "windows-targets", 40 | ] 41 | 42 | [[package]] 43 | name = "windows-targets" 44 | version = "0.48.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 47 | dependencies = [ 48 | "windows_aarch64_gnullvm", 49 | "windows_aarch64_msvc", 50 | "windows_i686_gnu", 51 | "windows_i686_msvc", 52 | "windows_x86_64_gnu", 53 | "windows_x86_64_gnullvm", 54 | "windows_x86_64_msvc", 55 | ] 56 | 57 | [[package]] 58 | name = "windows_aarch64_gnullvm" 59 | version = "0.48.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 62 | 63 | [[package]] 64 | name = "windows_aarch64_msvc" 65 | version = "0.48.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 68 | 69 | [[package]] 70 | name = "windows_i686_gnu" 71 | version = "0.48.5" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 74 | 75 | [[package]] 76 | name = "windows_i686_msvc" 77 | version = "0.48.5" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 80 | 81 | [[package]] 82 | name = "windows_x86_64_gnu" 83 | version = "0.48.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 86 | 87 | [[package]] 88 | name = "windows_x86_64_gnullvm" 89 | version = "0.48.5" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 92 | 93 | [[package]] 94 | name = "windows_x86_64_msvc" 95 | version = "0.48.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 98 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/ui/text.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | use std::fmt::Display; 3 | 4 | #[derive(Clone, Copy)] 5 | pub enum TextStyle { 6 | Normal, 7 | Bold, 8 | Italic, 9 | Underline, 10 | } 11 | 12 | pub struct TextBlock { 13 | content: String, 14 | color: Option, 15 | style: TextStyle, 16 | } 17 | 18 | impl TextBlock { 19 | pub fn new(content: impl Into) -> Self { 20 | Self { 21 | content: content.into(), 22 | color: None, 23 | style: TextStyle::Normal, 24 | } 25 | } 26 | 27 | pub fn color(mut self, color: impl Into) -> Self { 28 | self.color = Some(color.into()); 29 | self 30 | } 31 | 32 | pub fn style(mut self, style: TextStyle) -> Self { 33 | self.style = style; 34 | self 35 | } 36 | 37 | pub fn build(&self) -> String { 38 | let mut text = self.content.clone(); 39 | 40 | if let Some(color) = &self.color { 41 | text = match color.as_str() { 42 | "black" => text.black().to_string(), 43 | "red" => text.red().to_string(), 44 | "green" => text.green().to_string(), 45 | "yellow" => text.yellow().to_string(), 46 | "blue" => text.blue().to_string(), 47 | "magenta" => text.magenta().to_string(), 48 | "cyan" => text.cyan().to_string(), 49 | "white" => text.white().to_string(), 50 | "bright_black" => text.bright_black().to_string(), 51 | "bright_red" => text.bright_red().to_string(), 52 | "bright_green" => text.bright_green().to_string(), 53 | "bright_yellow" => text.bright_yellow().to_string(), 54 | "bright_blue" => text.bright_blue().to_string(), 55 | "bright_magenta" => text.bright_magenta().to_string(), 56 | "bright_cyan" => text.bright_cyan().to_string(), 57 | "bright_white" => text.bright_white().to_string(), 58 | "dimmed" => text.dimmed().to_string(), 59 | _ => text, 60 | }; 61 | } 62 | 63 | match self.style { 64 | TextStyle::Normal => text, 65 | TextStyle::Bold => text.bold().to_string(), 66 | TextStyle::Italic => text.italic().to_string(), 67 | TextStyle::Underline => text.underline().to_string(), 68 | } 69 | } 70 | } 71 | 72 | impl Display for TextBlock { 73 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 74 | write!(f, "{}", self.build()) 75 | } 76 | } 77 | 78 | pub fn format_size(size: u64) -> String { 79 | const KB: u64 = 1024; 80 | const MB: u64 = KB * 1024; 81 | const GB: u64 = MB * 1024; 82 | const TB: u64 = GB * 1024; 83 | 84 | if size >= TB { 85 | format!("{:.2} TB", size as f64 / TB as f64) 86 | } else if size >= GB { 87 | format!("{:.2} GB", size as f64 / GB as f64) 88 | } else if size >= MB { 89 | format!("{:.2} MB", size as f64 / MB as f64) 90 | } else if size >= KB { 91 | format!("{:.2} KB", size as f64 / KB as f64) 92 | } else { 93 | format!("{} B", size) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /.github/workflows/release-themes.yml: -------------------------------------------------------------------------------- 1 | name: Release Themes 2 | 3 | on: 4 | repository_dispatch: 5 | types: 6 | - release-themes 7 | workflow_dispatch: 8 | inputs: 9 | release_tag: 10 | description: Release tag to target (e.g. v1.2.3) 11 | required: false 12 | release_version: 13 | description: Release version without the v prefix 14 | required: false 15 | 16 | jobs: 17 | guard: 18 | name: Resolve Release Context 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | outputs: 23 | release_tag: ${{ steps.resolve.outputs.release_tag }} 24 | release_version: ${{ steps.resolve.outputs.release_version }} 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: Resolve release metadata 29 | id: resolve 30 | env: 31 | INPUT_TAG: ${{ inputs.release_tag }} 32 | INPUT_VERSION: ${{ inputs.release_version }} 33 | PAYLOAD_TAG: ${{ github.event.client_payload.tag }} 34 | PAYLOAD_VERSION: ${{ github.event.client_payload.version }} 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | run: | 37 | set -euo pipefail 38 | 39 | tag="${PAYLOAD_TAG:-}" 40 | if [[ -z "$tag" ]]; then 41 | tag="${INPUT_TAG:-}" 42 | fi 43 | if [[ -z "$tag" ]]; then 44 | echo "::error::release_tag input is required when dispatch payload is absent" 45 | exit 1 46 | fi 47 | 48 | version="${PAYLOAD_VERSION:-}" 49 | if [[ -z "$version" ]]; then 50 | version="${INPUT_VERSION:-}" 51 | fi 52 | if [[ -z "$version" ]]; then 53 | version="${tag#v}" 54 | fi 55 | 56 | gh release view "$tag" >/dev/null 57 | 58 | echo "release_tag=$tag" >> "$GITHUB_OUTPUT" 59 | echo "release_version=$version" >> "$GITHUB_OUTPUT" 60 | 61 | themes: 62 | name: Upload Themes Archive 63 | needs: guard 64 | runs-on: ubuntu-latest 65 | permissions: 66 | contents: write 67 | env: 68 | RELEASE_TAG: ${{ needs.guard.outputs.release_tag }} 69 | steps: 70 | - uses: actions/checkout@v4 71 | 72 | - name: Prepare helper scripts 73 | run: chmod +x .github/scripts/release_helpers.sh 74 | 75 | - name: Check existing themes asset 76 | id: themes_guard 77 | env: 78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 79 | run: | 80 | source .github/scripts/release_helpers.sh 81 | if asset_exists "$RELEASE_TAG" "themes.zip"; then 82 | echo "skip=true" >> "$GITHUB_OUTPUT" 83 | log_note "themes.zip already present on $RELEASE_TAG" 84 | fi 85 | 86 | - name: Create themes archive 87 | if: steps.themes_guard.outputs.skip != 'true' 88 | run: | 89 | find themes -name "*.toml" -type f | zip themes.zip -@ 90 | 91 | - name: Upload themes archive 92 | if: steps.themes_guard.outputs.skip != 'true' 93 | env: 94 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 95 | run: | 96 | source .github/scripts/release_helpers.sh 97 | gh release upload "$RELEASE_TAG" themes.zip 98 | 99 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | RED='\033[0;31m' 6 | GREEN='\033[0;32m' 7 | BLUE='\033[0;34m' 8 | NC='\033[0m' 9 | 10 | print_step() { 11 | echo -e "${BLUE}==>${NC} $1" 12 | } 13 | 14 | print_success() { 15 | echo -e "${GREEN}==>${NC} $1" 16 | } 17 | 18 | print_error() { 19 | echo -e "${RED}==>${NC} $1" 20 | } 21 | 22 | detect_platform() { 23 | OS="$(uname -s)" 24 | ARCH="$(uname -m)" 25 | 26 | case "$OS" in 27 | Linux) OS="linux" ;; 28 | Darwin) OS="macos" ;; 29 | *) 30 | print_error "Unsupported operating system: $OS" 31 | exit 1 32 | ;; 33 | esac 34 | 35 | case "$ARCH" in 36 | x86_64) ARCH="amd64" ;; 37 | aarch64) ARCH="arm64" ;; 38 | arm64) ARCH="arm64" ;; 39 | i386) ARCH="i686" ;; 40 | i686) ARCH="i686" ;; 41 | *) 42 | print_error "Unsupported architecture: $ARCH" 43 | exit 1 44 | ;; 45 | esac 46 | 47 | PLATFORM="lla-${OS}-${ARCH}" 48 | } 49 | 50 | get_latest_version() { 51 | LATEST_RELEASE_URL="https://api.github.com/repos/chaqchase/lla/releases/latest" 52 | VERSION=$(curl -s $LATEST_RELEASE_URL | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') 53 | if [ -z "$VERSION" ]; then 54 | print_error "Failed to fetch latest version" 55 | exit 1 56 | fi 57 | } 58 | 59 | download_binary() { 60 | print_step "Downloading lla ${VERSION} for ${OS}-${ARCH}..." 61 | 62 | DOWNLOAD_URL="https://github.com/chaqchase/lla/releases/download/${VERSION}/${PLATFORM}" 63 | TMP_DIR=$(mktemp -d) 64 | curl -L "$DOWNLOAD_URL" -o "${TMP_DIR}/lla" 65 | 66 | if [ $? -ne 0 ]; then 67 | print_error "Failed to download binary" 68 | rm -rf "$TMP_DIR" 69 | exit 1 70 | fi 71 | } 72 | 73 | verify_checksum() { 74 | print_step "Verifying checksum..." 75 | 76 | CHECKSUM_URL="https://github.com/chaqchase/lla/releases/download/${VERSION}/SHA256SUMS" 77 | curl -L "$CHECKSUM_URL" -o "${TMP_DIR}/SHA256SUMS" 78 | 79 | cd "$TMP_DIR" 80 | mkdir "${PLATFORM}" 81 | cp lla "${PLATFORM}/${PLATFORM}" 82 | if ! sha256sum -c --ignore-missing SHA256SUMS; then 83 | print_error "Checksum verification failed" 84 | cd - > /dev/null 85 | rm -rf "$TMP_DIR" 86 | exit 1 87 | fi 88 | cd - > /dev/null 89 | } 90 | 91 | install_binary() { 92 | print_step "Installing lla to /usr/local/bin..." 93 | 94 | sudo mkdir -p /usr/local/bin 95 | sudo chmod +x "${TMP_DIR}/lla" 96 | sudo mv "${TMP_DIR}/lla" /usr/local/bin/ 97 | rm -rf "$TMP_DIR" 98 | print_success "lla ${VERSION} has been installed successfully!" 99 | print_success "Run 'lla init' to create your configuration file" 100 | } 101 | 102 | main() { 103 | print_step "Installing lla..." 104 | if ! command -v curl >/dev/null 2>&1; then 105 | print_error "curl is required but not installed" 106 | exit 1 107 | fi 108 | 109 | detect_platform 110 | get_latest_version 111 | download_binary 112 | verify_checksum 113 | install_binary 114 | } 115 | 116 | main 117 | -------------------------------------------------------------------------------- /plugins/jwt/README.md: -------------------------------------------------------------------------------- 1 | # lla JWT Plugin 2 | 3 | JWT decoder and analyzer for `lla` with beautiful formatting, search capabilities, and validation. 4 | 5 | ## Features 6 | 7 | - **Token Decoding**: Beautiful formatted display of JWT header, payload, and signature 8 | - **Expiration Checking**: Automatic expiration validation with time remaining 9 | - **Smart Search**: Regex-powered search through JWT contents 10 | - **History Management**: Store and manage decoded JWT tokens 11 | - **Multiple Input Methods**: Paste, clipboard, or select from history 12 | - **Flexible Copying**: Copy full token, header, payload, or specific claims 13 | - **Claim Highlighting**: Automatically highlight important JWT claims 14 | 15 | ## Usage 16 | 17 | ```bash 18 | # Decode and view JWT token 19 | lla plugin --name jwt --action decode 20 | 21 | # Search through JWT contents 22 | lla plugin --name jwt --action search 23 | 24 | # Manage JWT history 25 | lla plugin --name jwt --action history 26 | 27 | # Configure preferences 28 | lla plugin --name jwt --action preferences 29 | 30 | # Show help 31 | lla plugin --name jwt --action help 32 | ``` 33 | 34 | ## Configuration 35 | 36 | Config location: `~/.config/lla/plugins/jwt/config.toml` 37 | 38 | ```toml 39 | auto_check_expiration = true # Automatically check token expiration 40 | save_to_history = true # Save decoded tokens to history 41 | max_history_size = 50 # Maximum number of tokens in history 42 | highlight_claims = ["sub", "iss", "aud", "exp", "iat", "nbf"] # Claims to highlight 43 | 44 | [colors] 45 | success = "bright_green" 46 | info = "bright_cyan" 47 | warning = "bright_yellow" 48 | error = "bright_red" 49 | header = "bright_blue" 50 | payload = "bright_magenta" 51 | claim = "bright_green" 52 | expired = "bright_red" 53 | valid = "bright_green" 54 | ``` 55 | 56 | ## Display Examples 57 | 58 | Token Display: 59 | 60 | ``` 61 | ═══════════════════════════════════════════════════════════════════════════════ 62 | 🔐 JWT TOKEN DETAILS 63 | ═══════════════════════════════════════════════════════════════════════════════ 64 | 65 | ┌─ HEADER ─────────────────────────────────────────────────────────────┐ 66 | { 67 | alg: "HS256", 68 | typ: "JWT" 69 | } 70 | └──────────────────────────────────────────────────────────────────────┘ 71 | 72 | ┌─ PAYLOAD ────────────────────────────────────────────────────────────┐ 73 | { 74 | sub: "1234567890", 75 | name: "John Doe", 76 | iat: 1516239022, 77 | exp: 1735689600 78 | } 79 | └──────────────────────────────────────────────────────────────────────┘ 80 | 81 | ✅ Expires in 45 days 82 | 83 | ┌─ SIGNATURE ──────────────────────────────────────────────────────────┐ 84 | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 85 | └──────────────────────────────────────────────────────────────────────┘ 86 | ``` 87 | 88 | Search Results: 89 | 90 | ``` 91 | ═══════════════════════════════════════════════════════════════════════════════ 92 | 🔍 Found 3 matches for 'user' 93 | ═══════════════════════════════════════════════════════════════════════════════ 94 | 95 | ► payload » userId 96 | "12345" 97 | 98 | ► payload » username 99 | "john.doe" 100 | 101 | ► payload » user_role 102 | "admin" 103 | ``` 104 | -------------------------------------------------------------------------------- /themes/circuit_dreams.toml: -------------------------------------------------------------------------------- 1 | name = "circuit_dreams" 2 | author = "Mohamed Achaq" 3 | description = "A theme inspired by vintage circuit boards and electronic components, featuring copper traces, solder joints, and LED indicators" 4 | 5 | [colors] 6 | file = "#FFD484" 7 | directory = "#4AE0B8" 8 | symlink = "#73DFFF" 9 | executable = "#FF6B97" 10 | size = "#958993" 11 | date = "#958993" 12 | user = "#B79CFF" 13 | group = "#6B6B6B" 14 | permission_dir = "#4AE0B8" 15 | permission_read = "#73DFFF" 16 | permission_write = "#FFD484" 17 | permission_exec = "#FF6B97" 18 | permission_none = "#1F2433" 19 | 20 | [special_files] 21 | [special_files.folders] 22 | "node_modules" = { h = 0, s = 0, l = 0.15 } 23 | "target" = "#1F2433" 24 | "dist" = "#1F2433" 25 | ".git" = "#4AE0B8" 26 | "build" = "#1F2433" 27 | ".cache" = "#1F2433" 28 | "coverage" = "#1F2433" 29 | "vendor" = "#1F2433" 30 | "*-env" = "#FF6B97" 31 | "venv" = "#FF6B97" 32 | ".env" = "#FF6B97" 33 | "env" = "#FF6B97" 34 | "*.d" = "#73DFFF" 35 | "*_cache" = "#1F2433" 36 | "*-cache" = "#1F2433" 37 | ".terraform" = "#1F2433" 38 | 39 | [special_files.dotfiles] 40 | ".gitignore" = "#73DFFF" 41 | ".env" = "#FF6B97" 42 | ".dockerignore" = "#73DFFF" 43 | ".editorconfig" = "#73DFFF" 44 | ".prettierrc" = "#73DFFF" 45 | ".eslintrc" = "#73DFFF" 46 | ".babelrc" = "#73DFFF" 47 | ".npmrc" = "#73DFFF" 48 | ".yarnrc" = "#73DFFF" 49 | ".zshrc" = "#73DFFF" 50 | ".bashrc" = "#73DFFF" 51 | 52 | [special_files.exact_match] 53 | "Dockerfile" = "#73DFFF" 54 | "docker-compose.yml" = "#73DFFF" 55 | "Makefile" = "#FF6B97" 56 | "CMakeLists.txt" = "#FF6B97" 57 | "README.md" = "#4AE0B8" 58 | "LICENSE" = "#4AE0B8" 59 | "CHANGELOG.md" = "#4AE0B8" 60 | "CONTRIBUTING.md" = "#4AE0B8" 61 | "package.json" = "#4AE0B8" 62 | "package-lock.json" = "#1F2433" 63 | "yarn.lock" = "#1F2433" 64 | "Cargo.toml" = "#FF6B97" 65 | "Cargo.lock" = "#1F2433" 66 | "composer.json" = "#4AE0B8" 67 | "go.mod" = "#73DFFF" 68 | "go.sum" = "#1F2433" 69 | 70 | [special_files.patterns] 71 | "*rc" = "#73DFFF" 72 | "*.config.*" = "#73DFFF" 73 | "*.conf" = "#73DFFF" 74 | "*.min.*" = "#1F2433" 75 | "*.map" = "#1F2433" 76 | "*.lock" = "#1F2433" 77 | "*.test.*" = "#FF6B97" 78 | "*.spec.*" = "#FF6B97" 79 | "*_test.*" = "#FF6B97" 80 | "*_spec.*" = "#FF6B97" 81 | 82 | [extensions.groups] 83 | logic = ["rs", "cpp", "c", "h", "hpp"] 84 | compute = ["py", "java", "go", "rb", "php"] 85 | web = ["js", "ts", "jsx", "tsx"] 86 | markup = ["html", "xml", "svg", "vue"] 87 | style = ["css", "scss", "sass", "less"] 88 | config = ["json", "yaml", "toml", "ini"] 89 | shell = ["sh", "bash", "zsh", "fish"] 90 | docs = ["md", "txt", "pdf", "doc"] 91 | data = ["csv", "sql", "db", "sqlite"] 92 | media = ["png", "jpg", "mp3", "mp4"] 93 | archive = ["zip", "tar", "gz", "7z"] 94 | 95 | [extensions.colors] 96 | logic = "#FF6B97" 97 | compute = "#4AE0B8" 98 | web = "#FFD484" 99 | markup = "#B79CFF" 100 | style = "#73DFFF" 101 | config = "#4AE0B8" 102 | shell = "#FF6B97" 103 | docs = "#FFD484" 104 | data = "#73DFFF" 105 | media = "#B79CFF" 106 | archive = "#FF6B97" 107 | rs = "#FF6B97" 108 | cpp = "#FF6B97" 109 | py = "#4AE0B8" 110 | js = "#FFD484" 111 | ts = "#FFD484" 112 | go = "#4AE0B8" 113 | html = "#B79CFF" 114 | css = "#73DFFF" 115 | md = "#FFD484" 116 | sql = "#73DFFF" 117 | -------------------------------------------------------------------------------- /lla_plugin_utils/src/format.rs: -------------------------------------------------------------------------------- 1 | use lla_plugin_interface::DecoratedEntry; 2 | use std::collections::HashMap; 3 | 4 | pub trait EntryFormatter { 5 | fn format_field(&self, entry: &DecoratedEntry, format: &str) -> Option; 6 | } 7 | 8 | pub struct FieldFormatterBuilder { 9 | formatters: HashMap Option>>, 10 | } 11 | 12 | impl FieldFormatterBuilder { 13 | pub fn new() -> Self { 14 | Self { 15 | formatters: HashMap::new(), 16 | } 17 | } 18 | 19 | pub fn add_formatter(mut self, format: &str, formatter: F) -> Self 20 | where 21 | F: Fn(&DecoratedEntry) -> Option + 'static, 22 | { 23 | self.formatters 24 | .insert(format.to_string(), Box::new(formatter)); 25 | self 26 | } 27 | 28 | pub fn build(self) -> CustomFieldFormatter { 29 | CustomFieldFormatter { 30 | formatters: self.formatters, 31 | } 32 | } 33 | } 34 | 35 | pub struct CustomFieldFormatter { 36 | formatters: HashMap Option>>, 37 | } 38 | 39 | impl EntryFormatter for CustomFieldFormatter { 40 | fn format_field(&self, entry: &DecoratedEntry, format: &str) -> Option { 41 | self.formatters.get(format).and_then(|f| f(entry)) 42 | } 43 | } 44 | 45 | impl Default for FieldFormatterBuilder { 46 | fn default() -> Self { 47 | Self::new() 48 | } 49 | } 50 | 51 | pub fn format_permissions(permissions: u32) -> String { 52 | let mut result = String::with_capacity(10); 53 | 54 | result.push(if permissions & 0o040000 != 0 { 55 | 'd' 56 | } else { 57 | '-' 58 | }); 59 | 60 | result.push(if permissions & 0o400 != 0 { 'r' } else { '-' }); 61 | result.push(if permissions & 0o200 != 0 { 'w' } else { '-' }); 62 | result.push(if permissions & 0o100 != 0 { 'x' } else { '-' }); 63 | 64 | result.push(if permissions & 0o040 != 0 { 'r' } else { '-' }); 65 | result.push(if permissions & 0o020 != 0 { 'w' } else { '-' }); 66 | result.push(if permissions & 0o010 != 0 { 'x' } else { '-' }); 67 | 68 | result.push(if permissions & 0o004 != 0 { 'r' } else { '-' }); 69 | result.push(if permissions & 0o002 != 0 { 'w' } else { '-' }); 70 | result.push(if permissions & 0o001 != 0 { 'x' } else { '-' }); 71 | 72 | result 73 | } 74 | 75 | pub fn format_file_type(entry: &DecoratedEntry) -> String { 76 | if entry.metadata.is_dir { 77 | "Directory".to_string() 78 | } else if entry.metadata.is_symlink { 79 | "Symlink".to_string() 80 | } else if entry.metadata.is_file { 81 | match entry.path.extension() { 82 | Some(ext) => ext.to_string_lossy().to_uppercase(), 83 | None => "File".to_string(), 84 | } 85 | } else { 86 | "Unknown".to_string() 87 | } 88 | } 89 | 90 | pub fn format_ownership(uid: u32, gid: u32) -> String { 91 | use users::{get_group_by_gid, get_user_by_uid}; 92 | 93 | let user = get_user_by_uid(uid) 94 | .map(|u| u.name().to_string_lossy().into_owned()) 95 | .unwrap_or_else(|| uid.to_string()); 96 | 97 | let group = get_group_by_gid(gid) 98 | .map(|g| g.name().to_string_lossy().into_owned()) 99 | .unwrap_or_else(|| gid.to_string()); 100 | 101 | format!("{}:{}", user, group) 102 | } 103 | -------------------------------------------------------------------------------- /lla/src/formatter/grid.rs: -------------------------------------------------------------------------------- 1 | use super::FileFormatter; 2 | use crate::plugin::PluginManager; 3 | use crate::utils::color::colorize_file_name; 4 | use crate::utils::icons::format_with_icon; 5 | use crate::{error::Result, utils::color::colorize_file_name_with_icon}; 6 | use lla_plugin_interface::proto::DecoratedEntry; 7 | use std::path::Path; 8 | use terminal_size::{terminal_size, Width}; 9 | use unicode_width::UnicodeWidthStr; 10 | 11 | pub struct GridFormatter { 12 | pub show_icons: bool, 13 | pub grid_ignore: bool, 14 | pub max_width: usize, 15 | } 16 | 17 | impl GridFormatter { 18 | pub fn new(show_icons: bool, grid_ignore: bool, max_width: usize) -> Self { 19 | Self { 20 | show_icons, 21 | grid_ignore, 22 | max_width, 23 | } 24 | } 25 | } 26 | impl FileFormatter for GridFormatter { 27 | fn format_files( 28 | &self, 29 | files: &[DecoratedEntry], 30 | plugin_manager: &mut PluginManager, 31 | _depth: Option, 32 | ) -> Result { 33 | if files.is_empty() { 34 | return Ok(String::new()); 35 | } 36 | 37 | let term_width = if self.grid_ignore { 38 | self.max_width 39 | } else { 40 | terminal_size() 41 | .map(|(Width(w), _)| w as usize) 42 | .unwrap_or(80) 43 | }; 44 | 45 | let mut formatted_entries = Vec::with_capacity(files.len()); 46 | let mut max_width = 0; 47 | 48 | for file in files { 49 | let path = Path::new(&file.path); 50 | let colored_name = colorize_file_name(path).to_string(); 51 | let name_with_icon = colorize_file_name_with_icon( 52 | path, 53 | format_with_icon(path, colored_name, self.show_icons), 54 | ) 55 | .to_string(); 56 | let plugin_fields = plugin_manager.format_fields(file, "grid").join(" "); 57 | let total_str = if plugin_fields.is_empty() { 58 | name_with_icon.clone() 59 | } else { 60 | format!("{} {}", name_with_icon.clone(), plugin_fields) 61 | }; 62 | let width = 63 | String::from_utf8_lossy(&strip_ansi_escapes::strip(&total_str).unwrap_or_default()) 64 | .width(); 65 | max_width = max_width.max(width); 66 | formatted_entries.push((name_with_icon, plugin_fields)); 67 | } 68 | 69 | let column_width = max_width + 2; 70 | let num_columns = std::cmp::max(1, term_width / column_width); 71 | 72 | let mut output = String::new(); 73 | let mut current_col = 0; 74 | 75 | for (colored_name, plugin_fields) in formatted_entries { 76 | let entry = if plugin_fields.is_empty() { 77 | colored_name 78 | } else { 79 | format!("{} {}", colored_name, plugin_fields) 80 | }; 81 | 82 | if current_col >= num_columns { 83 | output.push('\n'); 84 | current_col = 0; 85 | } 86 | 87 | output.push_str(&entry); 88 | if current_col < num_columns - 1 { 89 | let stripped_bytes = strip_ansi_escapes::strip(&entry).unwrap_or_default(); 90 | let stripped = String::from_utf8_lossy(&stripped_bytes); 91 | let padding = column_width - stripped.width(); 92 | output.push_str(&" ".repeat(padding)); 93 | } 94 | 95 | current_col += 1; 96 | } 97 | 98 | Ok(output) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lla/src/formatter/recursive.rs: -------------------------------------------------------------------------------- 1 | use super::FileFormatter; 2 | use crate::error::Result; 3 | use crate::plugin::PluginManager; 4 | use crate::theme::{self, ColorValue}; 5 | use crate::utils::color::{self, colorize_file_name, colorize_file_name_with_icon}; 6 | use crate::utils::icons::format_with_icon; 7 | use chrono::{DateTime, Local}; 8 | use colored::*; 9 | use lla_plugin_interface::proto::DecoratedEntry; 10 | use std::collections::BTreeMap; 11 | use std::path::Path; 12 | use std::time::{Duration, UNIX_EPOCH}; 13 | 14 | pub struct RecursiveFormatter { 15 | pub show_icons: bool, 16 | } 17 | 18 | impl RecursiveFormatter { 19 | pub fn new(show_icons: bool) -> Self { 20 | Self { show_icons } 21 | } 22 | 23 | fn format_date(timestamp: u64) -> ColoredString { 24 | let datetime = UNIX_EPOCH + Duration::from_secs(timestamp); 25 | let datetime: DateTime = DateTime::from(datetime); 26 | let date_str = datetime.format("%Y-%m-%d %H:%M").to_string(); 27 | let color = theme::color_value_to_color(&ColorValue::Named("bright black".to_string())); 28 | date_str.color(color) 29 | } 30 | 31 | fn get_header_color() -> Color { 32 | let theme = color::get_theme(); 33 | theme::color_value_to_color(&theme.colors.directory) 34 | } 35 | 36 | fn get_separator_color() -> Color { 37 | theme::color_value_to_color(&ColorValue::Named("bright black".to_string())) 38 | } 39 | 40 | fn print_entry(&self, entry: &DecoratedEntry, plugin_manager: &mut PluginManager) { 41 | if let Some(metadata) = &entry.metadata { 42 | let date = Self::format_date(metadata.modified); 43 | let path = Path::new(&entry.path); 44 | let colored_name = colorize_file_name(path).to_string(); 45 | let name = colorize_file_name_with_icon( 46 | path, 47 | format_with_icon(path, colored_name, self.show_icons), 48 | ); 49 | 50 | print!("{:>16} │ {}", date, name); 51 | 52 | let plugin_fields = plugin_manager.format_fields(entry, "recursive").join(" "); 53 | if !plugin_fields.is_empty() { 54 | print!(" {}", plugin_fields); 55 | } 56 | println!(); 57 | } 58 | } 59 | 60 | fn get_parent_path(path: &str) -> String { 61 | Path::new(path) 62 | .parent() 63 | .and_then(|p| p.to_str()) 64 | .unwrap_or(".") 65 | .to_string() 66 | } 67 | } 68 | 69 | impl FileFormatter for RecursiveFormatter { 70 | fn format_files( 71 | &self, 72 | files: &[DecoratedEntry], 73 | plugin_manager: &mut PluginManager, 74 | _depth: Option, 75 | ) -> Result { 76 | if files.is_empty() { 77 | return Ok(String::new()); 78 | } 79 | 80 | let mut groups: BTreeMap> = BTreeMap::new(); 81 | 82 | for file in files { 83 | let parent = Self::get_parent_path(&file.path); 84 | groups.entry(parent).or_default().push(file); 85 | } 86 | 87 | let single_group = groups.len() == 1; 88 | 89 | for (parent, entries) in &groups { 90 | if !(single_group && parent == ".") { 91 | println!("\n{}", parent.color(Self::get_header_color()).bold()); 92 | println!("{}", "─".repeat(40).color(Self::get_separator_color())); 93 | } 94 | 95 | for entry in entries { 96 | self.print_entry(entry, plugin_manager); 97 | } 98 | } 99 | Ok(String::new()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /plugins/file_mover/README.md: -------------------------------------------------------------------------------- 1 | # lla File Mover Plugin 2 | 3 | A plugin for `lla` that provides an intuitive clipboard-based interface for moving files and directories. 4 | 5 | ## Features 6 | 7 | - **Clipboard Management**: Persistent clipboard for files and directories 8 | - **Interactive Selection**: Multi-select interface for files and operations 9 | - **Flexible Moving**: Move all or selected items from clipboard 10 | - **Path Flexibility**: Support for both current and specified directories 11 | - **Safe Operations**: Validation and error handling for move operations 12 | - **User Interface**: Colored output and interactive menus 13 | 14 | ## Configuration 15 | 16 | Config file: `~/.config/lla/mv_clipboard.json` 17 | 18 | ```toml 19 | [colors] 20 | success = "bright_green" 21 | info = "bright_blue" 22 | error = "bright_red" 23 | path = "bright_yellow" 24 | ``` 25 | 26 | ## Usage 27 | 28 | ### Basic Operations 29 | 30 | ```bash 31 | # Add files from current directory to clipboard 32 | lla plugin --name file_mover --action add 33 | 34 | # Add files from a specific directory to clipboard 35 | lla plugin --name file_mover --action add --args /path/to/source 36 | 37 | # Move all files from clipboard to current directory 38 | lla plugin --name file_mover --action move-all 39 | 40 | # Move all files from clipboard to specific directory 41 | lla plugin --name file_mover --action move-all --args /path/to/destination 42 | 43 | # Move selected files from clipboard to current directory 44 | lla plugin --name file_mover --action move-partial 45 | 46 | # Move selected files from clipboard to specific directory 47 | lla plugin --name file_mover --action move-partial --args /path/to/destination 48 | ``` 49 | 50 | ### Clipboard Management 51 | 52 | ```bash 53 | # View clipboard contents with option to remove items 54 | lla plugin --name file_mover --action show 55 | 56 | # Clear the clipboard 57 | lla plugin --name file_mover --action clear 58 | 59 | # Show help information 60 | lla plugin --name file_mover --action help 61 | ``` 62 | 63 | ## Common Workflows 64 | 65 | ### 1. Moving Files Between Directories (Using Explicit Paths) 66 | 67 | ```bash 68 | # Add files from source directory 69 | lla plugin --name file_mover --action add --args /path/to/source 70 | # Select files to move using space, confirm with enter 71 | 72 | # Move all files to target directory 73 | lla plugin --name file_mover --action move-all --args /path/to/target 74 | ``` 75 | 76 | ### 2. Moving Files Using Current Directory Navigation 77 | 78 | ```bash 79 | # In source directory 80 | cd /path/to/source 81 | lla plugin --name file_mover --action add 82 | # Select files to add to clipboard 83 | 84 | # Navigate to first target 85 | cd /path/to/target1 86 | lla plugin --name file_mover --action move-partial 87 | # Select subset of files to move here 88 | 89 | # Navigate to second target 90 | cd /path/to/target2 91 | lla plugin --name file_mover --action move-partial 92 | # Select another subset of files to move here 93 | ``` 94 | 95 | ### 3. Mixed Workflow (Current and Explicit Paths) 96 | 97 | ```bash 98 | # Add files from current directory 99 | lla plugin --name file_mover --action add 100 | # Select files to add to clipboard 101 | 102 | # Move selected files to a specific directory without changing location 103 | lla plugin --name file_mover --action move-partial --args /path/to/target 104 | ``` 105 | 106 | ## Display Format 107 | 108 | ``` 109 | ───────────────────────────────────── 110 | File Mover Clipboard 111 | ───────────────────────────────────── 112 | Current Items: 113 | → /path/to/file1.txt 114 | → /path/to/directory1 115 | → /path/to/file2.rs 116 | ───────────────────────────────────── 117 | Use Space to select, Enter to confirm 118 | ───────────────────────────────────── 119 | ``` 120 | -------------------------------------------------------------------------------- /plugins/file_copier/README.md: -------------------------------------------------------------------------------- 1 | # lla File Copier Plugin 2 | 3 | A plugin for `lla` that provides an intuitive clipboard-based interface for copying files and directories. 4 | 5 | ## Features 6 | 7 | - **Clipboard Management**: Persistent clipboard for files and directories 8 | - **Interactive Selection**: Multi-select interface for files and operations 9 | - **Flexible Copying**: Copy all or selected items from clipboard 10 | - **Path Flexibility**: Support for both current and specified directories 11 | - **Safe Operations**: Validation and error handling for copy operations 12 | - **User Interface**: Colored output and interactive menus 13 | 14 | ## Configuration 15 | 16 | Config file: `~/.config/lla/cp_clipboard.json` 17 | 18 | ```toml 19 | [colors] 20 | success = "bright_green" 21 | info = "bright_blue" 22 | error = "bright_red" 23 | path = "bright_yellow" 24 | ``` 25 | 26 | ## Usage 27 | 28 | ### Basic Operations 29 | 30 | ```bash 31 | # Add files from current directory to clipboard 32 | lla plugin --name file_copier --action add 33 | 34 | # Add files from a specific directory to clipboard 35 | lla plugin --name file_copier --action add --args /path/to/source 36 | 37 | # Copy all files from clipboard to current directory 38 | lla plugin --name file_copier --action copy-all 39 | 40 | # Copy all files from clipboard to specific directory 41 | lla plugin --name file_copier --action copy-all --args /path/to/destination 42 | 43 | # Copy selected files from clipboard to current directory 44 | lla plugin --name file_copier --action copy-partial 45 | 46 | # Copy selected files from clipboard to specific directory 47 | lla plugin --name file_copier --action copy-partial --args /path/to/destination 48 | ``` 49 | 50 | ### Clipboard Management 51 | 52 | ```bash 53 | # View clipboard contents with option to remove items 54 | lla plugin --name file_copier --action show 55 | 56 | # Clear the clipboard 57 | lla plugin --name file_copier --action clear 58 | 59 | # Show help information 60 | lla plugin --name file_copier --action help 61 | ``` 62 | 63 | ## Common Workflows 64 | 65 | ### 1. Copying Files Between Directories (Using Explicit Paths) 66 | 67 | ```bash 68 | # Add files from source directory 69 | lla plugin --name file_copier --action add --args /path/to/source 70 | # Select files to copy using space, confirm with enter 71 | 72 | # Copy all files to target directory 73 | lla plugin --name file_copier --action copy-all --args /path/to/target 74 | ``` 75 | 76 | ### 2. Copying Files Using Current Directory Navigation 77 | 78 | ```bash 79 | # In source directory 80 | cd /path/to/source 81 | lla plugin --name file_copier --action add 82 | # Select files to add to clipboard 83 | 84 | # Navigate to first target 85 | cd /path/to/target1 86 | lla plugin --name file_copier --action copy-partial 87 | # Select subset of files to copy here 88 | 89 | # Navigate to second target 90 | cd /path/to/target2 91 | lla plugin --name file_copier --action copy-partial 92 | # Select another subset of files to copy here 93 | ``` 94 | 95 | ### 3. Mixed Workflow (Current and Explicit Paths) 96 | 97 | ```bash 98 | # Add files from current directory 99 | lla plugin --name file_copier --action add 100 | # Select files to add to clipboard 101 | 102 | # Copy selected files to a specific directory without changing location 103 | lla plugin --name file_copier --action copy-partial --args /path/to/target 104 | ``` 105 | 106 | ## Display Format 107 | 108 | ``` 109 | ───────────────────────────────────── 110 | File Copier Clipboard 111 | ───────────────────────────────────── 112 | Current Items: 113 | → /path/to/file1.txt 114 | → /path/to/directory1 115 | → /path/to/file2.rs 116 | ───────────────────────────────────── 117 | Use Space to select, Enter to confirm 118 | ───────────────────────────────────── 119 | ``` 120 | -------------------------------------------------------------------------------- /lla/src/filter/composite.rs: -------------------------------------------------------------------------------- 1 | use super::FileFilter; 2 | use crate::error::{LlaError, Result}; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Debug, Clone, Copy)] 6 | pub enum FilterOperation { 7 | And, 8 | Or, 9 | Not, 10 | Xor, 11 | } 12 | 13 | pub struct CompositeFilter { 14 | filters: Vec>, 15 | operation: FilterOperation, 16 | } 17 | 18 | impl CompositeFilter { 19 | pub fn new(operation: FilterOperation) -> Self { 20 | CompositeFilter { 21 | filters: Vec::new(), 22 | operation, 23 | } 24 | } 25 | 26 | pub fn add_filter(&mut self, filter: Box) { 27 | self.filters.push(filter); 28 | } 29 | } 30 | 31 | impl FileFilter for CompositeFilter { 32 | fn filter_files(&self, files: &[PathBuf]) -> Result> { 33 | if self.filters.is_empty() { 34 | return Ok(files.to_vec()); 35 | } 36 | 37 | match self.operation { 38 | FilterOperation::And => { 39 | let mut result = files.to_vec(); 40 | for filter in &self.filters { 41 | result = filter 42 | .filter_files(&result) 43 | .map_err(|e| LlaError::Filter(format!("AND operation failed: {}", e)))?; 44 | } 45 | Ok(result) 46 | } 47 | FilterOperation::Or => { 48 | let mut result = Vec::new(); 49 | for filter in &self.filters { 50 | let filtered = filter 51 | .filter_files(files) 52 | .map_err(|e| LlaError::Filter(format!("OR operation failed: {}", e)))?; 53 | for file in filtered { 54 | if !result.contains(&file) { 55 | result.push(file); 56 | } 57 | } 58 | } 59 | Ok(result) 60 | } 61 | FilterOperation::Not => { 62 | if self.filters.len() != 1 { 63 | return Err(LlaError::Filter( 64 | "NOT operation requires exactly one filter".to_string(), 65 | )); 66 | } 67 | let filtered = self.filters[0] 68 | .filter_files(files) 69 | .map_err(|e| LlaError::Filter(format!("NOT operation failed: {}", e)))?; 70 | Ok(files 71 | .iter() 72 | .filter(|file| !filtered.contains(file)) 73 | .cloned() 74 | .collect()) 75 | } 76 | FilterOperation::Xor => { 77 | if self.filters.len() != 2 { 78 | return Err(LlaError::Filter( 79 | "XOR operation requires exactly two filters".to_string(), 80 | )); 81 | } 82 | let first = self.filters[0].filter_files(files).map_err(|e| { 83 | LlaError::Filter(format!("XOR operation failed on first filter: {}", e)) 84 | })?; 85 | let second = self.filters[1].filter_files(files).map_err(|e| { 86 | LlaError::Filter(format!("XOR operation failed on second filter: {}", e)) 87 | })?; 88 | 89 | Ok(files 90 | .iter() 91 | .filter(|file| { 92 | let in_first = first.contains(file); 93 | let in_second = second.contains(file); 94 | in_first ^ in_second 95 | }) 96 | .cloned() 97 | .collect()) 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /plugins.md: -------------------------------------------------------------------------------- 1 | # lla Plugins 2 | 3 | This is a list of all the plugins available for lla. 4 | 5 | ## Installation 6 | 7 | You can install all prebuilt plugins at once using the default installer: 8 | 9 | ```bash 10 | lla install 11 | ``` 12 | 13 | To force fetching the latest prebuilt archive (useful after a release): 14 | 15 | ```bash 16 | # install pre-built plugins 17 | lla install --prebuilt 18 | # or install from a repo 19 | lla install --git https://github.com/triyanox/lla 20 | ``` 21 | 22 | If you prefer to build from source, install them like this: 23 | 24 | ```bash 25 | git clone https://github.com/triyanox/lla 26 | cd lla/plugins/ 27 | cargo build --release 28 | ``` 29 | 30 | then copy the generated `.so`, `.dll`, or `.dylib` file from the `target/release` directory to your lla plugins directory. 31 | 32 | ## Available Plugins 33 | 34 | - [categorizer](https://github.com/chaqchase/lla/tree/main/plugins/categorizer): Categorizes files based on their extensions and metadata 35 | - [code_complexity](https://github.com/chaqchase/lla/tree/main/plugins/code_complexity): Analyzes code complexity using various metrics 36 | - [code_snippet_extractor](https://github.com/chaqchase/lla/tree/main/plugins/code_snippet_extractor): A plugin for extracting and managing code snippets 37 | - [dirs_meta](https://github.com/chaqchase/lla/tree/main/plugins/dirs_meta): Shows directories metadata 38 | - [duplicate_file_detector](https://github.com/chaqchase/lla/tree/main/plugins/duplicate_file_detector): A plugin for the lla that detects duplicate files. 39 | - [file_hash](https://github.com/chaqchase/lla/tree/main/plugins/file_hash): Displays the hash of each file 40 | - [file_meta](https://github.com/chaqchase/lla/tree/main/plugins/file_meta): Displays the file metadata of each file 41 | - [file_tagger](https://github.com/chaqchase/lla/tree/main/plugins/file_tagger): A plugin for tagging files and filtering by tags 42 | - [flush_dns](https://github.com/chaqchase/lla/tree/main/plugins/flush_dns): Flush DNS cache on macOS, Linux, and Windows 43 | - [git_status](https://github.com/chaqchase/lla/tree/main/plugins/git_status): Shows the git status of each file 44 | - [google_meet](https://github.com/chaqchase/lla/tree/main/plugins/google_meet): Google Meet plugin for creating meeting rooms and managing links 45 | - [google_search](https://github.com/chaqchase/lla/tree/main/plugins/google_search): Google search with autosuggestions, history management, and clipboard fallback 46 | - [jwt](https://github.com/chaqchase/lla/tree/main/plugins/jwt): JWT decoder and analyzer with search and validation capabilities 47 | - [keyword_search](https://github.com/chaqchase/lla/tree/main/plugins/keyword_search): Searches file contents for user-specified keywords 48 | - [last_git_commit](https://github.com/chaqchase/lla/tree/main/plugins/last_git_commit): A plugin for the lla that provides the last git commit hash 49 | - [npm](https://github.com/chaqchase/lla/tree/main/plugins/npm): NPM package search with bundlephobia integration and favorites management 50 | - [sizeviz](https://github.com/chaqchase/lla/tree/main/plugins/sizeviz): File size visualizer plugin for lla 51 | - [youtube](https://github.com/chaqchase/lla/tree/main/plugins/youtube): YouTube search with autosuggestions and history management 52 | - [file_mover](https://github.com/chaqchase/lla/tree/main/plugins/file_mover): A plugin that provides an intuitive clipboard-based interface for moving files and directories. 53 | - [file_copier](https://github.com/chaqchase/lla/tree/main/plugins/file_copier): A plugin that provides an intuitive clipboard-based interface for copying files and directories. 54 | - [file_remover](https://github.com/chaqchase/lla/tree/main/plugins/file_remover): A plugin that provides an interactive interface for safely removing files and directories. 55 | - [file_organizer](https://github.com/chaqchase/lla/tree/main/plugins/file_organizer): A plugin for organizing files using various strategies 56 | - [kill_process](https://github.com/chaqchase/lla/tree/main/plugins/kill_process): Interactive process management plugin for listing and terminating system processes with cross-platform support 57 | -------------------------------------------------------------------------------- /plugins/dirs_meta/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "colored" 7 | version = "2.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 10 | dependencies = [ 11 | "lazy_static", 12 | "windows-sys", 13 | ] 14 | 15 | [[package]] 16 | name = "dirs" 17 | version = "0.1.0" 18 | dependencies = [ 19 | "colored", 20 | "lla_plugin_interface", 21 | "walkdir", 22 | ] 23 | 24 | [[package]] 25 | name = "lazy_static" 26 | version = "1.5.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 29 | 30 | [[package]] 31 | name = "lla_plugin_interface" 32 | version = "0.1.1" 33 | 34 | [[package]] 35 | name = "same-file" 36 | version = "1.0.6" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 39 | dependencies = [ 40 | "winapi-util", 41 | ] 42 | 43 | [[package]] 44 | name = "walkdir" 45 | version = "2.5.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 48 | dependencies = [ 49 | "same-file", 50 | "winapi-util", 51 | ] 52 | 53 | [[package]] 54 | name = "winapi-util" 55 | version = "0.1.9" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 58 | dependencies = [ 59 | "windows-sys", 60 | ] 61 | 62 | [[package]] 63 | name = "windows-sys" 64 | version = "0.48.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 67 | dependencies = [ 68 | "windows-targets", 69 | ] 70 | 71 | [[package]] 72 | name = "windows-targets" 73 | version = "0.48.5" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 76 | dependencies = [ 77 | "windows_aarch64_gnullvm", 78 | "windows_aarch64_msvc", 79 | "windows_i686_gnu", 80 | "windows_i686_msvc", 81 | "windows_x86_64_gnu", 82 | "windows_x86_64_gnullvm", 83 | "windows_x86_64_msvc", 84 | ] 85 | 86 | [[package]] 87 | name = "windows_aarch64_gnullvm" 88 | version = "0.48.5" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 91 | 92 | [[package]] 93 | name = "windows_aarch64_msvc" 94 | version = "0.48.5" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 97 | 98 | [[package]] 99 | name = "windows_i686_gnu" 100 | version = "0.48.5" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 103 | 104 | [[package]] 105 | name = "windows_i686_msvc" 106 | version = "0.48.5" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 109 | 110 | [[package]] 111 | name = "windows_x86_64_gnu" 112 | version = "0.48.5" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 115 | 116 | [[package]] 117 | name = "windows_x86_64_gnullvm" 118 | version = "0.48.5" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 121 | 122 | [[package]] 123 | name = "windows_x86_64_msvc" 124 | version = "0.48.5" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 127 | -------------------------------------------------------------------------------- /plugins/file_organizer/src/strategies/extension.rs: -------------------------------------------------------------------------------- 1 | use super::OrganizationStrategy; 2 | use crate::config::ExtensionStrategyConfig; 3 | use std::{ 4 | collections::HashMap, 5 | fs, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | pub struct ExtensionStrategy { 10 | config: ExtensionStrategyConfig, 11 | } 12 | 13 | impl ExtensionStrategy { 14 | pub fn new(config: ExtensionStrategyConfig) -> Self { 15 | Self { config } 16 | } 17 | } 18 | 19 | impl OrganizationStrategy for ExtensionStrategy { 20 | fn organize(&self, dir: &Path, dry_run: bool) -> Result, String> { 21 | if !self.config.enabled { 22 | return Ok(Vec::new()); 23 | } 24 | 25 | let mut moves = Vec::new(); 26 | let mut extension_dirs: HashMap = HashMap::new(); 27 | 28 | let entries = fs::read_dir(dir).map_err(|e| format!("Failed to read directory: {}", e))?; 29 | for entry in entries.filter_map(Result::ok) { 30 | let path = entry.path(); 31 | if path.is_dir() { 32 | continue; 33 | } 34 | if let Some(ext) = path.extension().and_then(|e| e.to_str()) { 35 | let ext = ext.to_lowercase(); 36 | let target_dir = extension_dirs.entry(ext.clone()).or_insert_with(|| { 37 | let mut target = PathBuf::from(dir); 38 | if self.config.create_nested { 39 | match ext.as_str() { 40 | "jpg" | "jpeg" | "png" | "gif" | "bmp" | "svg" => { 41 | target.push("images"); 42 | target.push(ext); 43 | } 44 | "mp4" | "avi" | "mov" | "mkv" | "wmv" => { 45 | target.push("videos"); 46 | target.push(ext); 47 | } 48 | "mp3" | "wav" | "flac" | "m4a" | "ogg" => { 49 | target.push("audio"); 50 | target.push(ext); 51 | } 52 | "doc" | "docx" | "pdf" | "txt" | "rtf" | "md" => { 53 | target.push("documents"); 54 | target.push(ext); 55 | } 56 | "zip" | "rar" | "7z" | "tar" | "gz" => { 57 | target.push("archives"); 58 | target.push(ext); 59 | } 60 | _ => target.push(ext), 61 | } 62 | } else { 63 | target.push(ext); 64 | } 65 | target 66 | }); 67 | if !target_dir.exists() && !dry_run { 68 | fs::create_dir_all(target_dir.clone()) 69 | .map_err(|e| format!("Failed to create directory: {}", e))?; 70 | } 71 | let target_path = target_dir.join(path.file_name().unwrap()); 72 | moves.push((path, target_path)); 73 | } 74 | } 75 | 76 | Ok(moves) 77 | } 78 | 79 | fn execute_moves(&self, moves: Vec<(PathBuf, PathBuf)>) -> Result<(), String> { 80 | for (source, target) in moves { 81 | if let Some(parent) = target.parent() { 82 | if !parent.exists() { 83 | fs::create_dir_all(parent) 84 | .map_err(|e| format!("Failed to create directory: {}", e))?; 85 | } 86 | } 87 | 88 | fs::rename(&source, &target).map_err(|e| { 89 | format!( 90 | "Failed to move '{}' to '{}': {}", 91 | source.display(), 92 | target.display(), 93 | e 94 | ) 95 | })?; 96 | } 97 | Ok(()) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /scripts/build_plugins.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # Change to repo root (script is in scripts/) 6 | cd "$(dirname "$0")/.." 7 | 8 | usage() { 9 | echo "Usage: $0 [--target ]" 1>&2 10 | echo "Example: $0 --target x86_64-unknown-linux-gnu" 1>&2 11 | } 12 | 13 | TARGET_TRIPLE="" 14 | 15 | while [[ $# -gt 0 ]]; do 16 | case "$1" in 17 | --target) 18 | shift 19 | TARGET_TRIPLE="${1:-}" 20 | [[ -z "$TARGET_TRIPLE" ]] && { echo "--target requires an argument" 1>&2; usage; exit 2; } 21 | shift 22 | ;; 23 | -h|--help) 24 | usage 25 | exit 0 26 | ;; 27 | *) 28 | echo "Unknown argument: $1" 1>&2 29 | usage 30 | exit 2 31 | ;; 32 | esac 33 | done 34 | 35 | if [[ -z "$TARGET_TRIPLE" ]]; then 36 | # Detect host triple 37 | TARGET_TRIPLE=$(rustc -vV | sed -n 's/^host: \(.*\)$/\1/p') 38 | fi 39 | 40 | case "$TARGET_TRIPLE" in 41 | *apple-darwin) 42 | DL_EXT="dylib" 43 | OS_LABEL="macos" 44 | ;; 45 | *windows*) 46 | DL_EXT="dll" 47 | OS_LABEL="windows" 48 | ;; 49 | *) 50 | DL_EXT="so" 51 | OS_LABEL="linux" 52 | ;; 53 | esac 54 | 55 | case "$TARGET_TRIPLE" in 56 | aarch64-*) ARCH_LABEL="arm64" ;; 57 | x86_64-*) ARCH_LABEL="amd64" ;; 58 | i686-*) ARCH_LABEL="i686" ;; 59 | *) 60 | echo "Unsupported architecture in target triple: $TARGET_TRIPLE" 1>&2 61 | exit 1 62 | ;; 63 | esac 64 | 65 | STAGING_DIR="dist/plugins-${OS_LABEL}-${ARCH_LABEL}" 66 | ARCHIVE_TGZ="dist/plugins-${OS_LABEL}-${ARCH_LABEL}.tar.gz" 67 | ARCHIVE_ZIP="dist/plugins-${OS_LABEL}-${ARCH_LABEL}.zip" 68 | 69 | echo "Building all plugins for target: ${TARGET_TRIPLE}" 70 | echo "Output staging directory: ${STAGING_DIR}" 71 | 72 | rm -rf "$STAGING_DIR" 73 | mkdir -p "$STAGING_DIR" 74 | 75 | # Collect plugin crate names from plugins/*/Cargo.toml (Bash 3 compatible) 76 | PLUGIN_CRATES=() 77 | for f in plugins/*/Cargo.toml; do 78 | if [[ -f "$f" ]]; then 79 | name=$(awk -F ' = ' '/^name *=/ {gsub(/"/, "", $2); print $2; exit}' "$f" || true) 80 | if [[ -n "$name" ]]; then 81 | PLUGIN_CRATES+=("$name") 82 | fi 83 | fi 84 | done 85 | 86 | if [[ ${#PLUGIN_CRATES[@]} -eq 0 ]]; then 87 | echo "No plugins found under plugins/*" 1>&2 88 | exit 1 89 | fi 90 | 91 | echo "Found plugins: ${PLUGIN_CRATES[*]}" 92 | 93 | # Ensure target toolchain installed (no-op if already present) 94 | rustup target add "$TARGET_TRIPLE" >/dev/null 2>&1 || true 95 | 96 | # Build all plugin crates in one cargo invocation to leverage workspace caching 97 | BUILD_PKGS=( ) 98 | for crate in "${PLUGIN_CRATES[@]}"; do 99 | BUILD_PKGS+=( -p "$crate" ) 100 | done 101 | 102 | echo "Running: cargo build --release --target $TARGET_TRIPLE ${BUILD_PKGS[*]}" 103 | cargo build --release --target "$TARGET_TRIPLE" "${BUILD_PKGS[@]}" 104 | 105 | # Copy resulting dynamic libraries to staging 106 | for crate in "${PLUGIN_CRATES[@]}"; do 107 | if [[ "$DL_EXT" == "dll" ]]; then 108 | SRC="target/${TARGET_TRIPLE}/release/${crate}.dll" 109 | else 110 | SRC="target/${TARGET_TRIPLE}/release/lib${crate}.${DL_EXT}" 111 | fi 112 | 113 | if [[ ! -f "$SRC" ]]; then 114 | echo "Expected plugin artifact not found: $SRC" 1>&2 115 | exit 1 116 | fi 117 | 118 | cp "$SRC" "$STAGING_DIR/" 119 | done 120 | 121 | # Create archive per-OS format 122 | if [[ "$OS_LABEL" == "windows" ]]; then 123 | # Prefer zip on Windows; fallback to 7z if zip is unavailable 124 | rm -f "$ARCHIVE_ZIP" 125 | if command -v zip >/dev/null 2>&1; then 126 | (cd "$STAGING_DIR" && zip -9 -r "../$(basename "$ARCHIVE_ZIP")" . >/dev/null) 127 | elif command -v 7z >/dev/null 2>&1; then 128 | (cd "$STAGING_DIR" && 7z a -tzip -mx=9 "../$(basename "$ARCHIVE_ZIP")" . >/dev/null) 129 | else 130 | echo "Neither zip nor 7z found on PATH for Windows packaging" 1>&2 131 | exit 1 132 | fi 133 | echo "Created archive: $ARCHIVE_ZIP" 134 | else 135 | rm -f "$ARCHIVE_TGZ" 136 | tar -C "$STAGING_DIR" -czf "$ARCHIVE_TGZ" . 137 | echo "Created archive: $ARCHIVE_TGZ" 138 | fi 139 | 140 | echo "Done." 141 | 142 | 143 | -------------------------------------------------------------------------------- /themes/gruvbox_dark.toml: -------------------------------------------------------------------------------- 1 | name = "gruvbox_dark" 2 | author = "Mohamed Achaq" 3 | description = "A retro groove color scheme with warm, earthy tones and high contrast" 4 | 5 | [colors] 6 | file = "#EBDBB2" 7 | directory = "#B8BB26" 8 | symlink = "#83A598" 9 | executable = "#98971A" 10 | 11 | size = "#928374" 12 | date = "#928374" 13 | user = "#D3869B" 14 | group = "#928374" 15 | 16 | permission_dir = "#B8BB26" 17 | permission_read = "#98971A" 18 | permission_write = "#D3869B" 19 | permission_exec = "#FB4934" 20 | permission_none = "#3C3836" 21 | 22 | [special_files] 23 | [special_files.folders] 24 | "node_modules" = { h = 0, s = 0, l = 0.15 } 25 | "target" = "#3C3836" 26 | "dist" = "#3C3836" 27 | ".git" = "#B8BB26" 28 | "build" = "#3C3836" 29 | ".cache" = "#3C3836" 30 | "*-env" = "#98971A" 31 | "venv" = "#98971A" 32 | ".env" = "#98971A" 33 | "*.d" = "#83A598" 34 | "*_cache" = "#3C3836" 35 | "*-cache" = "#3C3836" 36 | 37 | [special_files.dotfiles] 38 | ".gitignore" = "#458588" 39 | ".env" = "#B8BB26" 40 | ".dockerignore" = "#458588" 41 | ".editorconfig" = "#458588" 42 | ".prettierrc" = "#458588" 43 | ".eslintrc" = "#458588" 44 | ".babelrc" = "#458588" 45 | 46 | [special_files.exact_match] 47 | "Dockerfile" = "#458588" 48 | "docker-compose.yml" = "#458588" 49 | "Makefile" = "#FB4934" 50 | "CMakeLists.txt" = "#FB4934" 51 | "README.md" = "#B8BB26" 52 | "LICENSE" = "#B8BB26" 53 | "package.json" = "#B8BB26" 54 | "Cargo.toml" = "#FB4934" 55 | "go.mod" = "#458588" 56 | "flake.nix" = "#B8BB26" 57 | "flake.lock" = "#3C3836" 58 | "shell.nix" = "#B8BB26" 59 | "default.nix" = "#B8BB26" 60 | 61 | [special_files.patterns] 62 | "*rc" = "#458588" 63 | "*.min.*" = "#3C3836" 64 | "*.test.*" = "#98971A" 65 | "*.spec.*" = "#98971A" 66 | "*.lock" = "#3C3836" 67 | "*.config.*" = "#458588" 68 | 69 | [extensions.groups] 70 | rust = ["rs", "toml"] 71 | python = ["py", "pyi", "pyw", "ipynb"] 72 | javascript = ["js", "mjs", "cjs", "jsx"] 73 | typescript = ["ts", "tsx", "d.ts"] 74 | java = ["java", "jar", "class"] 75 | csharp = ["cs", "csx"] 76 | cpp = ["cpp", "cc", "cxx", "c++", "hpp", "hxx", "h++"] 77 | go = ["go"] 78 | ruby = ["rb", "erb"] 79 | php = ["php", "phtml"] 80 | swift = ["swift"] 81 | kotlin = ["kt", "kts"] 82 | nix = ["nix"] 83 | 84 | markup = ["html", "htm", "xhtml", "xml", "svg", "vue", "wasm", "ejs"] 85 | style = ["css", "scss", "sass", "less", "styl"] 86 | web_config = ["json", "json5", "yaml", "yml", "toml", "ini", "conf", "config"] 87 | 88 | shell = ["sh", "bash", "zsh", "fish", "ps1", "psm1", "psd1"] 89 | script = ["pl", "pm", "t", "tcl", "lua", "vim", "vimrc", "r"] 90 | 91 | doc = [ 92 | "md", 93 | "rst", 94 | "txt", 95 | "org", 96 | "wiki", 97 | "adoc", 98 | "tex", 99 | "pdf", 100 | "epub", 101 | "doc", 102 | "docx", 103 | "rtf", 104 | ] 105 | 106 | image = ["png", "jpg", "jpeg", "gif", "bmp", "tiff", "webp", "ico", "heic"] 107 | video = ["mp4", "webm", "mov", "avi", "mkv", "flv", "wmv", "m4v", "3gp"] 108 | audio = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma"] 109 | 110 | data = ["csv", "tsv", "sql", "sqlite", "db", "json", "xml", "yaml", "yml"] 111 | archive = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar", "iso", "dmg"] 112 | 113 | [extensions.colors] 114 | rust = "#FB4934" 115 | python = "#98971A" 116 | javascript = "#FABD2F" 117 | typescript = "#83A598" 118 | java = "#FB4934" 119 | csharp = "#98971A" 120 | cpp = "#FB4934" 121 | go = "#83A598" 122 | ruby = "#FB4934" 123 | php = "#98971A" 124 | swift = "#FB4934" 125 | kotlin = "#98971A" 126 | nix = "#83A598" 127 | 128 | markup = "#D3869B" 129 | style = "#98971A" 130 | web_config = "#B8BB26" 131 | 132 | shell = "#98971A" 133 | script = "#B8BB26" 134 | 135 | doc = "#EBDBB2" 136 | 137 | image = "#FE8019" 138 | video = "#FE8019" 139 | audio = "#FE8019" 140 | 141 | data = "#B8BB26" 142 | archive = "#FB4934" 143 | 144 | rs = "#FB4934" 145 | py = "#98971A" 146 | js = "#FABD2F" 147 | ts = "#83A598" 148 | jsx = "#FABD2F" 149 | tsx = "#83A598" 150 | vue = "#98971A" 151 | css = "#98971A" 152 | scss = "#98971A" 153 | html = "#D3869B" 154 | md = "#EBDBB2" 155 | json = "#B8BB26" 156 | yaml = "#B8BB26" 157 | toml = "#B8BB26" 158 | sql = "#83A598" 159 | -------------------------------------------------------------------------------- /themes/nord.toml: -------------------------------------------------------------------------------- 1 | name = "nord" 2 | author = "Mohamed Achaq" 3 | description = "An arctic, north-bluish color palette with elegant pastel colors and soothing tones" 4 | 5 | [colors] 6 | file = "#D8DEE9" 7 | directory = "#88C0D0" 8 | symlink = "#81A1C1" 9 | executable = "#A3BE8C" 10 | 11 | size = "#4C566A" 12 | date = "#4C566A" 13 | user = "#B48EAD" 14 | group = "#4C566A" 15 | 16 | permission_dir = "#88C0D0" 17 | permission_read = "#A3BE8C" 18 | permission_write = "#B48EAD" 19 | permission_exec = "#BF616A" 20 | permission_none = "#4C566A" 21 | 22 | [special_files] 23 | [special_files.folders] 24 | "node_modules" = { h = 0, s = 0, l = 0.15 } 25 | "target" = "#4C566A" 26 | "dist" = "#4C566A" 27 | ".git" = "#88C0D0" 28 | "build" = "#4C566A" 29 | ".cache" = "#4C566A" 30 | "*-env" = "#A3BE8C" 31 | "venv" = "#A3BE8C" 32 | ".env" = "#A3BE8C" 33 | "*.d" = "#81A1C1" 34 | "*_cache" = "#4C566A" 35 | "*-cache" = "#4C566A" 36 | 37 | [special_files.dotfiles] 38 | ".gitignore" = "#5E81AC" 39 | ".env" = "#88C0D0" 40 | ".dockerignore" = "#5E81AC" 41 | ".editorconfig" = "#5E81AC" 42 | ".prettierrc" = "#5E81AC" 43 | ".eslintrc" = "#5E81AC" 44 | ".babelrc" = "#5E81AC" 45 | 46 | [special_files.exact_match] 47 | "Dockerfile" = "#5E81AC" 48 | "docker-compose.yml" = "#5E81AC" 49 | "Makefile" = "#BF616A" 50 | "CMakeLists.txt" = "#BF616A" 51 | "README.md" = "#88C0D0" 52 | "LICENSE" = "#88C0D0" 53 | "package.json" = "#88C0D0" 54 | "Cargo.toml" = "#BF616A" 55 | "go.mod" = "#5E81AC" 56 | "flake.nix" = "#88C0D0" 57 | "flake.lock" = "#4C566A" 58 | "shell.nix" = "#88C0D0" 59 | "default.nix" = "#88C0D0" 60 | 61 | [special_files.patterns] 62 | "*rc" = "#5E81AC" 63 | "*.min.*" = "#4C566A" 64 | "*.test.*" = "#A3BE8C" 65 | "*.spec.*" = "#A3BE8C" 66 | "*.lock" = "#4C566A" 67 | "*.config.*" = "#5E81AC" 68 | 69 | [extensions.groups] 70 | rust = ["rs", "toml"] 71 | python = ["py", "pyi", "pyw", "ipynb"] 72 | javascript = ["js", "mjs", "cjs", "jsx"] 73 | typescript = ["ts", "tsx", "d.ts"] 74 | java = ["java", "jar", "class"] 75 | csharp = ["cs", "csx"] 76 | cpp = ["cpp", "cc", "cxx", "c++", "hpp", "hxx", "h++"] 77 | go = ["go"] 78 | ruby = ["rb", "erb"] 79 | php = ["php", "phtml"] 80 | swift = ["swift"] 81 | kotlin = ["kt", "kts"] 82 | nix = ["nix"] 83 | 84 | markup = ["html", "htm", "xhtml", "xml", "svg", "vue", "wasm", "ejs"] 85 | style = ["css", "scss", "sass", "less", "styl"] 86 | web_config = ["json", "json5", "yaml", "yml", "toml", "ini", "conf", "config"] 87 | 88 | shell = ["sh", "bash", "zsh", "fish", "ps1", "psm1", "psd1"] 89 | script = ["pl", "pm", "t", "tcl", "lua", "vim", "vimrc", "r"] 90 | 91 | doc = [ 92 | "md", 93 | "rst", 94 | "txt", 95 | "org", 96 | "wiki", 97 | "adoc", 98 | "tex", 99 | "pdf", 100 | "epub", 101 | "doc", 102 | "docx", 103 | "rtf", 104 | ] 105 | 106 | image = ["png", "jpg", "jpeg", "gif", "bmp", "tiff", "webp", "ico", "heic"] 107 | video = ["mp4", "webm", "mov", "avi", "mkv", "flv", "wmv", "m4v", "3gp"] 108 | audio = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma"] 109 | 110 | data = ["csv", "tsv", "sql", "sqlite", "db", "json", "xml", "yaml", "yml"] 111 | archive = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar", "iso", "dmg"] 112 | 113 | [extensions.colors] 114 | rust = "#BF616A" 115 | python = "#A3BE8C" 116 | javascript = "#EBCB8B" 117 | typescript = "#81A1C1" 118 | java = "#BF616A" 119 | csharp = "#A3BE8C" 120 | cpp = "#BF616A" 121 | go = "#81A1C1" 122 | ruby = "#BF616A" 123 | php = "#A3BE8C" 124 | swift = "#BF616A" 125 | kotlin = "#A3BE8C" 126 | nix = "#81A1C1" 127 | 128 | markup = "#B48EAD" 129 | style = "#A3BE8C" 130 | web_config = "#88C0D0" 131 | 132 | shell = "#A3BE8C" 133 | script = "#88C0D0" 134 | 135 | doc = "#D8DEE9" 136 | 137 | image = "#D08770" 138 | video = "#D08770" 139 | audio = "#D08770" 140 | 141 | data = "#88C0D0" 142 | archive = "#BF616A" 143 | 144 | rs = "#BF616A" 145 | py = "#A3BE8C" 146 | js = "#EBCB8B" 147 | ts = "#81A1C1" 148 | jsx = "#EBCB8B" 149 | tsx = "#81A1C1" 150 | vue = "#A3BE8C" 151 | css = "#A3BE8C" 152 | scss = "#A3BE8C" 153 | html = "#B48EAD" 154 | md = "#D8DEE9" 155 | json = "#88C0D0" 156 | yaml = "#88C0D0" 157 | toml = "#88C0D0" 158 | sql = "#81A1C1" 159 | -------------------------------------------------------------------------------- /themes/vesper.toml: -------------------------------------------------------------------------------- 1 | name = "vesper" 2 | author = "Mohamed Achaq" 3 | description = "A minimalist dark theme with warm accents, inspired by the Vesper color scheme" 4 | 5 | [colors] 6 | file = "#FFFFFF" 7 | directory = "#FFC799" 8 | symlink = "#99FFE4" 9 | executable = "#99FFE4" 10 | 11 | size = "#A0A0A0" 12 | date = "#A0A0A0" 13 | user = "#FFC799" 14 | group = "#7E7E7E" 15 | 16 | permission_dir = "#FFC799" 17 | permission_read = "#99FFE4" 18 | permission_write = "#FFC799" 19 | permission_exec = "#FF8080" 20 | permission_none = "#505050" 21 | 22 | [special_files] 23 | [special_files.folders] 24 | "node_modules" = { h = 0, s = 0, l = 0.3 } 25 | "target" = "#7E7E7E" 26 | "dist" = "#7E7E7E" 27 | ".git" = "#FFC799" 28 | "build" = "#7E7E7E" 29 | ".cache" = "#7E7E7E" 30 | "*-env" = "#99FFE4" 31 | "venv" = "#99FFE4" 32 | ".env" = "#99FFE4" 33 | "*.d" = "#FFC799" 34 | "*_cache" = "#7E7E7E" 35 | "*-cache" = "#7E7E7E" 36 | 37 | [special_files.dotfiles] 38 | ".gitignore" = "#99FFE4" 39 | ".env" = "#FFC799" 40 | ".dockerignore" = "#99FFE4" 41 | ".editorconfig" = "#99FFE4" 42 | ".prettierrc" = "#99FFE4" 43 | ".eslintrc" = "#99FFE4" 44 | ".babelrc" = "#99FFE4" 45 | 46 | [special_files.exact_match] 47 | "Dockerfile" = "#99FFE4" 48 | "docker-compose.yml" = "#99FFE4" 49 | "Makefile" = "#FF8080" 50 | "CMakeLists.txt" = "#FF8080" 51 | "README.md" = "#FFC799" 52 | "LICENSE" = "#FFC799" 53 | "package.json" = "#FFC799" 54 | "Cargo.toml" = "#FF8080" 55 | "go.mod" = "#99FFE4" 56 | "flake.nix" = "#FFC799" 57 | "flake.lock" = "#7E7E7E" 58 | "shell.nix" = "#FFC799" 59 | "default.nix" = "#FFC799" 60 | 61 | [special_files.patterns] 62 | "*rc" = "#99FFE4" 63 | "*.min.*" = "#7E7E7E" 64 | "*.test.*" = "#99FFE4" 65 | "*.spec.*" = "#99FFE4" 66 | "*.lock" = "#7E7E7E" 67 | "*.config.*" = "#99FFE4" 68 | 69 | [extensions.groups] 70 | rust = ["rs", "toml"] 71 | python = ["py", "pyi", "pyw", "ipynb"] 72 | javascript = ["js", "mjs", "cjs", "jsx"] 73 | typescript = ["ts", "tsx", "d.ts"] 74 | java = ["java", "jar", "class"] 75 | csharp = ["cs", "csx"] 76 | cpp = ["cpp", "cc", "cxx", "c++", "hpp", "hxx", "h++"] 77 | go = ["go"] 78 | ruby = ["rb", "erb"] 79 | php = ["php", "phtml"] 80 | swift = ["swift"] 81 | kotlin = ["kt", "kts"] 82 | nix = ["nix"] 83 | 84 | markup = ["html", "htm", "xhtml", "xml", "svg", "vue", "wasm", "ejs"] 85 | style = ["css", "scss", "sass", "less", "styl"] 86 | web_config = ["json", "json5", "yaml", "yml", "toml", "ini", "conf", "config"] 87 | 88 | shell = ["sh", "bash", "zsh", "fish", "ps1", "psm1", "psd1"] 89 | script = ["pl", "pm", "t", "tcl", "lua", "vim", "vimrc", "r"] 90 | 91 | doc = [ 92 | "md", 93 | "rst", 94 | "txt", 95 | "org", 96 | "wiki", 97 | "adoc", 98 | "tex", 99 | "pdf", 100 | "epub", 101 | "doc", 102 | "docx", 103 | "rtf", 104 | ] 105 | 106 | image = ["png", "jpg", "jpeg", "gif", "bmp", "tiff", "webp", "ico", "heic"] 107 | video = ["mp4", "webm", "mov", "avi", "mkv", "flv", "wmv", "m4v", "3gp"] 108 | audio = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma"] 109 | 110 | data = ["csv", "tsv", "sql", "sqlite", "db", "json", "xml", "yaml", "yml"] 111 | archive = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar", "iso", "dmg"] 112 | 113 | [extensions.colors] 114 | rust = "#FF8080" 115 | python = "#99FFE4" 116 | javascript = "#FFC799" 117 | typescript = "#FFC799" 118 | java = "#FF8080" 119 | csharp = "#99FFE4" 120 | cpp = "#FF8080" 121 | go = "#99FFE4" 122 | ruby = "#FF8080" 123 | php = "#99FFE4" 124 | swift = "#FF8080" 125 | kotlin = "#99FFE4" 126 | nix = "#99FFE4" 127 | 128 | markup = "#FFFFFF" 129 | style = "#99FFE4" 130 | web_config = "#FFC799" 131 | 132 | shell = "#99FFE4" 133 | script = "#FFC799" 134 | 135 | doc = "#FFFFFF" 136 | 137 | image = "#FFFFFF" 138 | video = "#FFFFFF" 139 | audio = "#FFFFFF" 140 | 141 | data = "#FFC799" 142 | archive = "#FF8080" 143 | 144 | rs = "#FF8080" 145 | py = "#99FFE4" 146 | js = "#FFC799" 147 | ts = "#FFC799" 148 | jsx = "#FFC799" 149 | tsx = "#FFC799" 150 | vue = "#99FFE4" 151 | css = "#99FFE4" 152 | scss = "#99FFE4" 153 | html = "#FFFFFF" 154 | md = "#FFFFFF" 155 | json = "#FFC799" 156 | yaml = "#FFC799" 157 | toml = "#FFC799" 158 | sql = "#99FFE4" 159 | --------------------------------------------------------------------------------