project-launcher changes
This commit is contained in:
@@ -46,6 +46,7 @@ nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
|
|||||||
| `mem0` | AI memory assistant with vector storage |
|
| `mem0` | AI memory assistant with vector storage |
|
||||||
| `msty-studio` | Msty Studio application |
|
| `msty-studio` | Msty Studio application |
|
||||||
| `pomodoro-timer` | Pomodoro timer utility |
|
| `pomodoro-timer` | Pomodoro timer utility |
|
||||||
|
| `rofi-project-opener` | Rofi-based project launcher |
|
||||||
| `stt-ptt` | Push to Talk Speech to Text |
|
| `stt-ptt` | Push to Talk Speech to Text |
|
||||||
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |
|
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |
|
||||||
| `zellij-ps` | Project switcher for Zellij |
|
| `zellij-ps` | Project switcher for Zellij |
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ Documentation for all custom packages:
|
|||||||
- [mem0](./packages/mem0.md) - AI memory assistant with vector storage
|
- [mem0](./packages/mem0.md) - AI memory assistant with vector storage
|
||||||
- [msty-studio](./packages/msty-studio.md) - Msty Studio application
|
- [msty-studio](./packages/msty-studio.md) - Msty Studio application
|
||||||
- [pomodoro-timer](./packages/pomodoro-timer.md) - Pomodoro timer utility
|
- [pomodoro-timer](./packages/pomodoro-timer.md) - Pomodoro timer utility
|
||||||
|
- [rofi-project-opener](./packages/rofi-project-opener.md) - Rofi-based project launcher with custom args
|
||||||
- [stt-ptt](./packages/stt-ptt.md) - Push to Talk Speech to Text using Whisper
|
- [stt-ptt](./packages/stt-ptt.md) - Push to Talk Speech to Text using Whisper
|
||||||
- [tuxedo-backlight](./packages/tuxedo-backlight.md) - Backlight control for Tuxedo laptops
|
- [tuxedo-backlight](./packages/tuxedo-backlight.md) - Backlight control for Tuxedo laptops
|
||||||
- [zellij-ps](./packages/zellij-ps.md) - Project switcher for Zellij
|
- [zellij-ps](./packages/zellij-ps.md) - Project switcher for Zellij
|
||||||
@@ -50,6 +51,7 @@ Configuration modules for NixOS and Home Manager:
|
|||||||
#### Home Manager Modules
|
#### Home Manager Modules
|
||||||
- [Overview](./modules/home-manager/overview.md) - Home Manager modules overview
|
- [Overview](./modules/home-manager/overview.md) - Home Manager modules overview
|
||||||
- [CLI Tools](./modules/home-manager/cli/) - CLI-related modules
|
- [CLI Tools](./modules/home-manager/cli/) - CLI-related modules
|
||||||
|
- [rofi-project-opener](./modules/home-manager/cli/rofi-project-opener.md) - Rofi-based project launcher
|
||||||
- [stt-ptt](./modules/home-manager/cli/stt-ptt.md) - Push to Talk Speech to Text
|
- [stt-ptt](./modules/home-manager/cli/stt-ptt.md) - Push to Talk Speech to Text
|
||||||
- [zellij-ps](./modules/home-manager/cli/zellij-ps.md) - Zellij project switcher
|
- [zellij-ps](./modules/home-manager/cli/zellij-ps.md) - Zellij project switcher
|
||||||
- [Coding](./modules/home-manager/coding/) - Development-related modules
|
- [Coding](./modules/home-manager/coding/) - Development-related modules
|
||||||
|
|||||||
@@ -7,23 +7,56 @@
|
|||||||
with lib; let
|
with lib; let
|
||||||
cfg = config.cli.rofi-project-opener;
|
cfg = config.cli.rofi-project-opener;
|
||||||
|
|
||||||
# Convert list of paths to colon-separated string
|
# Project directory submodule type
|
||||||
projectDirsStr = concatStringsSep ":" cfg.projectDirs;
|
projectDirType = types.submodule {
|
||||||
|
options = {
|
||||||
|
path = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Base directory path to scan for project subdirectories.";
|
||||||
|
example = "~/dev";
|
||||||
|
};
|
||||||
|
args = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "Additional arguments to pass to opencode when launching projects from this directory.";
|
||||||
|
example = "--agent Planner-Sisyphus";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Convert projectDirs attrset to JSON for config file
|
||||||
|
projectDirsJson = builtins.toJSON (
|
||||||
|
mapAttrs (name: value: {
|
||||||
|
path = value.path;
|
||||||
|
args = value.args;
|
||||||
|
}) cfg.projectDirs
|
||||||
|
);
|
||||||
in {
|
in {
|
||||||
options.cli.rofi-project-opener = {
|
options.cli.rofi-project-opener = {
|
||||||
enable = mkEnableOption "Rofi-based project directory launcher";
|
enable = mkEnableOption "Rofi-based project directory launcher";
|
||||||
|
|
||||||
projectDirs = mkOption {
|
projectDirs = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.attrsOf projectDirType;
|
||||||
default = ["~/dev" "~/projects"];
|
default = {
|
||||||
|
dev = { path = "~/dev"; };
|
||||||
|
projects = { path = "~/projects"; };
|
||||||
|
};
|
||||||
description = ''
|
description = ''
|
||||||
List of base directories to scan for project subdirectories.
|
Attribute set of base directories to scan for project subdirectories.
|
||||||
Each directory will be scanned for immediate subdirectories (non-hidden).
|
Each directory will be scanned for immediate subdirectories (non-hidden).
|
||||||
Projects are displayed as "base_dir/project_name" in rofi.
|
Projects are displayed as "base_dir/project_name" in rofi.
|
||||||
|
|
||||||
Supports ~ for home directory expansion.
|
Each entry can specify:
|
||||||
|
- path: Base directory path (supports ~ for home directory)
|
||||||
|
- args: Optional arguments to pass to opencode for projects in this directory
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
nixpkgs = { path = "~/p/NIX/nixpkgs"; args = "--agent Planner-Sisyphus"; };
|
||||||
|
dev = { path = "~/dev"; };
|
||||||
|
work = { path = "~/work"; args = "--agent work-agent"; };
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
example = literalExpression ''["~/dev" "~/projects" "~/code"]'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
terminal = mkOption {
|
terminal = mkOption {
|
||||||
@@ -37,17 +70,21 @@ in {
|
|||||||
type = types.str;
|
type = types.str;
|
||||||
default = "";
|
default = "";
|
||||||
description = ''
|
description = ''
|
||||||
Custom command to pass to the terminal.
|
Custom command to run in the terminal.
|
||||||
|
|
||||||
Use %s as a placeholder for the project path.
|
Placeholders:
|
||||||
If empty, defaults to opening a shell in the project directory and running opencode.
|
- %s = project path
|
||||||
|
- %a = project args (from projectDirs.<name>.args)
|
||||||
|
|
||||||
|
If empty, defaults to: cd to project, run "opencode %a"
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
- "" (empty) - Uses default: cd to project, run opencode
|
- "" (empty) - Uses default: cd to project, run opencode with args
|
||||||
- "-e zsh -c 'cd %s && opencode'" - Custom shell with explicit path
|
- "opencode %a" - Run opencode with project-specific args
|
||||||
- "--hold -e nvim" - Open editor directly (no %s = no cd)
|
- "nvim" - Open editor (no args)
|
||||||
|
- "myapp %s %a" - Custom app with path and args
|
||||||
'';
|
'';
|
||||||
example = literalExpression ''"-e zsh -c 'cd %s && opencode'"'';
|
example = literalExpression ''"opencode %a"'';
|
||||||
};
|
};
|
||||||
|
|
||||||
rofiPrompt = mkOption {
|
rofiPrompt = mkOption {
|
||||||
@@ -77,10 +114,12 @@ in {
|
|||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
home.packages = [pkgs.rofi-project-opener];
|
home.packages = [pkgs.rofi-project-opener];
|
||||||
|
|
||||||
# Write config file (shell-independent)
|
# Write JSON config file for project directories
|
||||||
|
xdg.configFile."rofi-project-opener/projects.json".text = projectDirsJson;
|
||||||
|
|
||||||
|
# Write shell config file for other settings
|
||||||
xdg.configFile."rofi-project-opener/config".text = ''
|
xdg.configFile."rofi-project-opener/config".text = ''
|
||||||
# rofi-project-opener configuration
|
# rofi-project-opener configuration
|
||||||
PROJECT_DIRS="${projectDirsStr}"
|
|
||||||
TERMINAL="${if isDerivation cfg.terminal then "${cfg.terminal}/bin/${cfg.terminal.pname or (builtins.baseNameOf (toString cfg.terminal))}" else cfg.terminal}"
|
TERMINAL="${if isDerivation cfg.terminal then "${cfg.terminal}/bin/${cfg.terminal.pname or (builtins.baseNameOf (toString cfg.terminal))}" else cfg.terminal}"
|
||||||
${optionalString (cfg.terminalCommand != "") ''TERMINAL_CMD="${cfg.terminalCommand}"''}
|
${optionalString (cfg.terminalCommand != "") ''TERMINAL_CMD="${cfg.terminalCommand}"''}
|
||||||
ROFI_PROMPT="${cfg.rofiPrompt}"
|
ROFI_PROMPT="${cfg.rofiPrompt}"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
lib,
|
|
||||||
writeShellScriptBin,
|
writeShellScriptBin,
|
||||||
rofi,
|
rofi,
|
||||||
libnotify,
|
libnotify,
|
||||||
coreutils,
|
coreutils,
|
||||||
gnugrep,
|
gnugrep,
|
||||||
gnused,
|
gnused,
|
||||||
|
jq,
|
||||||
}:
|
}:
|
||||||
writeShellScriptBin "rofi-project-opener" ''
|
writeShellScriptBin "rofi-project-opener" ''
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
@@ -24,9 +24,12 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
BASENAME="${coreutils}/bin/basename"
|
BASENAME="${coreutils}/bin/basename"
|
||||||
GREP="${gnugrep}/bin/grep"
|
GREP="${gnugrep}/bin/grep"
|
||||||
SED="${gnused}/bin/sed"
|
SED="${gnused}/bin/sed"
|
||||||
|
JQ="${jq}/bin/jq"
|
||||||
|
|
||||||
# Configuration from config file or environment variables
|
# Configuration files
|
||||||
CONFIG_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/rofi-project-opener/config"
|
CONFIG_DIR="''${XDG_CONFIG_HOME:-$HOME/.config}/rofi-project-opener"
|
||||||
|
CONFIG_FILE="$CONFIG_DIR/config"
|
||||||
|
PROJECTS_JSON="$CONFIG_DIR/projects.json"
|
||||||
|
|
||||||
if [[ -f "$CONFIG_FILE" ]]; then
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
@@ -34,12 +37,19 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Fallback to environment variables or defaults
|
# Fallback to environment variables or defaults
|
||||||
PROJECT_DIRS="''${PROJECT_DIRS:-$HOME}"
|
|
||||||
TERMINAL="''${TERMINAL:-kitty}"
|
TERMINAL="''${TERMINAL:-kitty}"
|
||||||
TERMINAL_CMD="''${TERMINAL_CMD:-}"
|
TERMINAL_CMD="''${TERMINAL_CMD:-}"
|
||||||
ROFI_PROMPT="''${ROFI_PROMPT:-Select project}"
|
ROFI_PROMPT="''${ROFI_PROMPT:-Select project}"
|
||||||
ROFI_ARGS="''${ROFI_ARGS:--dmenu -i}"
|
ROFI_ARGS="''${ROFI_ARGS:--dmenu -i}"
|
||||||
|
|
||||||
|
# Check for projects.json
|
||||||
|
if [[ ! -f "$PROJECTS_JSON" ]]; then
|
||||||
|
echo "[rofi-project-opener] Error: No projects.json found at $PROJECTS_JSON" >&2
|
||||||
|
"$NOTIFY" -u critical -a "rofi-project-opener" "No projects.json configuration found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PROJECTS_JSON_DATA="$(cat "$PROJECTS_JSON")"
|
||||||
|
|
||||||
# Temporary file for project list
|
# Temporary file for project list
|
||||||
PROJECTS_LIST="''${XDG_RUNTIME_DIR:-/tmp}/rofi-project-opener.$$"
|
PROJECTS_LIST="''${XDG_RUNTIME_DIR:-/tmp}/rofi-project-opener.$$"
|
||||||
|
|
||||||
@@ -59,26 +69,33 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
"$NOTIFY" -u critical -a "rofi-project-opener" "$message"
|
"$NOTIFY" -u critical -a "rofi-project-opener" "$message"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build list of projects
|
# Build list of projects from JSON config
|
||||||
|
# Format: display_name|full_path|args
|
||||||
build_project_list() {
|
build_project_list() {
|
||||||
> "$PROJECTS_LIST"
|
> "$PROJECTS_LIST"
|
||||||
|
|
||||||
# Split PROJECT_DIRS by colon and iterate
|
# Parse JSON and iterate over each base directory entry
|
||||||
IFS=':' read -ra DIR_ARRAY <<< "$PROJECT_DIRS"
|
local keys
|
||||||
for base_dir in "''${DIR_ARRAY[@]}"; do
|
keys=$(echo "$PROJECTS_JSON_DATA" | "$JQ" -r 'keys[]')
|
||||||
|
|
||||||
|
for key in $keys; do
|
||||||
|
local base_path base_args
|
||||||
|
base_path=$(echo "$PROJECTS_JSON_DATA" | "$JQ" -r --arg k "$key" '.[$k].path')
|
||||||
|
base_args=$(echo "$PROJECTS_JSON_DATA" | "$JQ" -r --arg k "$key" '.[$k].args // ""')
|
||||||
|
|
||||||
# Expand ~ to $HOME
|
# Expand ~ to $HOME
|
||||||
base_dir="''${base_dir/#\~/$HOME}"
|
base_path="''${base_path/#\~/$HOME}"
|
||||||
# Expand $HOME variable (allows both ~ and $HOME in config)
|
# Expand $HOME variable (allows both ~ and $HOME in config)
|
||||||
base_dir="$(eval echo "$base_dir")"
|
base_path="$(eval echo "$base_path")"
|
||||||
|
|
||||||
# Skip if directory doesn't exist
|
# Skip if directory doesn't exist
|
||||||
if [[ ! -d "$base_dir" ]]; then
|
if [[ ! -d "$base_path" ]]; then
|
||||||
echo "[rofi-project-opener] Warning: Directory does not exist: $base_dir" >&2
|
echo "[rofi-project-opener] Warning: Directory does not exist: $base_path" >&2
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Find 1st level subdirectories (non-hidden)
|
# Find 1st level subdirectories (non-hidden)
|
||||||
for project in "$base_dir"/*/; do
|
for project in "$base_path"/*/; do
|
||||||
if [[ -d "$project" ]]; then
|
if [[ -d "$project" ]]; then
|
||||||
# Get directory name without trailing slash
|
# Get directory name without trailing slash
|
||||||
project_path="''${project%/}"
|
project_path="''${project%/}"
|
||||||
@@ -89,8 +106,8 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
base_display="$("$BASENAME" "$base_dir")"
|
# Use key as display prefix (e.g., "dev/myproject")
|
||||||
echo "$base_display/$project_name|$project_path" >> "$PROJECTS_LIST"
|
echo "$key/$project_name|$project_path|$base_args" >> "$PROJECTS_LIST"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
@@ -101,7 +118,7 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
|
|
||||||
# Build project list
|
# Build project list
|
||||||
if ! build_project_list; then
|
if ! build_project_list; then
|
||||||
show_error "No projects found in PROJECT_DIRS: $PROJECT_DIRS"
|
show_error "No projects found in configured directories"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -117,8 +134,10 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the full path from selection
|
# Get the full path and args from selection
|
||||||
project_path=$("$GREP" "^$selection|" "$PROJECTS_LIST" | "$CUT" -d '|' -f2)
|
selected_line=$("$GREP" "^$selection|" "$PROJECTS_LIST")
|
||||||
|
project_path=$(echo "$selected_line" | "$CUT" -d '|' -f2)
|
||||||
|
project_args=$(echo "$selected_line" | "$CUT" -d '|' -f3)
|
||||||
|
|
||||||
# Exit if path not found (shouldn't happen)
|
# Exit if path not found (shouldn't happen)
|
||||||
if [[ -z "$project_path" ]]; then
|
if [[ -z "$project_path" ]]; then
|
||||||
@@ -133,33 +152,30 @@ writeShellScriptBin "rofi-project-opener" ''
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build terminal command
|
# Build terminal command
|
||||||
|
# Placeholders: %s = project path, %a = project args
|
||||||
if [[ -n "$TERMINAL_CMD" ]]; then
|
if [[ -n "$TERMINAL_CMD" ]]; then
|
||||||
# Check if %s placeholder is present
|
# Substitute placeholders
|
||||||
if [[ "$TERMINAL_CMD" == *"%s"* ]]; then
|
final_cmd="$TERMINAL_CMD"
|
||||||
# Replace %s with project path and use as-is
|
final_cmd="''${final_cmd//%s/$project_path}"
|
||||||
final_cmd="''${TERMINAL_CMD//%s/$project_path}"
|
final_cmd="''${final_cmd//%a/$project_args}"
|
||||||
# shellcheck disable=SC2086
|
|
||||||
exec "$TERMINAL" $final_cmd
|
|
||||||
else
|
|
||||||
# Treat as command to run: wrap in shell with cd (--hold keeps terminal open)
|
|
||||||
# Source /etc/profile (base PATH) and HM session vars (sessionPath) for NixOS
|
|
||||||
exec "$TERMINAL" --hold -e bash --login -c "
|
|
||||||
unset __HM_SESS_VARS_SOURCED
|
|
||||||
for f in ~/.nix-profile/etc/profile.d/hm-session-vars.sh /etc/profiles/per-user/\$USER/etc/profile.d/hm-session-vars.sh; do
|
|
||||||
[ -f \"\$f\" ] && . \"\$f\"
|
|
||||||
done
|
|
||||||
cd '$project_path' && exec $TERMINAL_CMD
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# Default: open terminal, cd to project, run opencode
|
|
||||||
# Source /etc/profile (base PATH) and HM session vars (sessionPath) for NixOS
|
# Source /etc/profile (base PATH) and HM session vars (sessionPath) for NixOS
|
||||||
exec "$TERMINAL" --hold -e bash --login -c "
|
exec "$TERMINAL" --hold -e bash --login -c "
|
||||||
unset __HM_SESS_VARS_SOURCED
|
unset __HM_SESS_VARS_SOURCED
|
||||||
for f in ~/.nix-profile/etc/profile.d/hm-session-vars.sh /etc/profiles/per-user/\$USER/etc/profile.d/hm-session-vars.sh; do
|
for f in ~/.nix-profile/etc/profile.d/hm-session-vars.sh /etc/profiles/per-user/\$USER/etc/profile.d/hm-session-vars.sh; do
|
||||||
[ -f \"\$f\" ] && . \"\$f\"
|
[ -f \"\$f\" ] && . \"\$f\"
|
||||||
done
|
done
|
||||||
cd '$project_path' && opencode
|
cd '$project_path' && exec $final_cmd
|
||||||
|
"
|
||||||
|
else
|
||||||
|
# Default: open terminal, cd to project, run opencode with args
|
||||||
|
# Source /etc/profile (base PATH) and HM session vars (sessionPath) for NixOS
|
||||||
|
exec "$TERMINAL" --hold -e bash --login -c "
|
||||||
|
unset __HM_SESS_VARS_SOURCED
|
||||||
|
for f in ~/.nix-profile/etc/profile.d/hm-session-vars.sh /etc/profiles/per-user/\$USER/etc/profile.d/hm-session-vars.sh; do
|
||||||
|
[ -f \"\$f\" ] && . \"\$f\"
|
||||||
|
done
|
||||||
|
cd '$project_path' && opencode $project_args
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
|
|||||||
Reference in New Issue
Block a user