diff --git a/env/.local/scripts/hypr/install-webapp b/env/.local/scripts/hypr/install-webapp index a8f894c..c45f4ae 100755 --- a/env/.local/scripts/hypr/install-webapp +++ b/env/.local/scripts/hypr/install-webapp @@ -1,52 +1,58 @@ -#!/bin/zsh +#!/usr/bin/env bash # Adapted from https://github.com/basecamp/omarchy/tree/master?tab=readme-ov-file -# TODO: Use bash and only allow options, not positional arguments to make easier. +THIS=$(basename ${BASH_SOURCE[0]}) function usage() { cat <] [-u ] [-i ] [-e ] [-m ] [-f ] [-h] [args...] +USAGE: $THIS [OPTIONS] OPTIONS: - -n | --name: The name of the application. - -u | --url: The url used to launch the application. - -i | --icon: The icon for the application. - -e | --exec: Custom execution command (optional). - -m | --mime-types: MIME-types for the application (optional). - -f | --file: Install from a spec in a json file. - -h | --help: Show usage information. + -n | --name : The name of the application. + + -u | --url : The url used to launch the application. + + -i | --icon : The icon for the application. + + -e | --exec : Custom execution command (optional). + + -m | --mime-types : MIME-types for the application (optional). + + -f | --file : Install from a spec in a json file. + + -l | --launch: Launches in a new terminal window in interactive mode. + + --no-interactive: Don't proceed to interactive mode. If required properties aren't set, + then error. This is useful if using '--file' and there are parsing errors + or missing properties. + + --dry-run: Run in dry-run mode, which doesn't generate files or download icons. + + -h | --help: Show usage information. EXAMPLES: If no options or arguments are supplied, then it will start an interactive session that prompts for the values. - $ install-webapp + $ $THIS Calling the app with named arguments: - $ install-webapp \\ + $ $THIS \\ --name "My Awesome App" \\ --url "https://awesome.com" \\ --icon "https://awesome.com/assets/icon.png" Using a json file as input: - $ install-webapp --file myapp.json + $ $THIS --file myapp.json -It is also possible to use only positional arguments with out their key. They can be passed in the order as -they're listed. - - $ install-webapp "My Awesome App" \\ - "https://awesome.com" \\ - "https://awesome.com/assets/icon.png" - -NOTES: +NOTES: The icon option can either be a url where we will download a png from or a local file. Local files can either be the full path to the file or a file name of an icon located in '~/.local/share/applications/icons/'. @@ -55,7 +61,7 @@ Interactive sessions do not give the option to use a custom execution command or MIME types, which are less frequently used options. If using a json spec file, all keys are the same as their option name, except for mime-types use 'mime_types' as the -key in the json object. Although the 'exec' and 'mime_types' are not required in the spec file. An common json spec +key in the json object. Although the 'exec' and 'mime_types' are not required in the spec file. A common json spec file example would look like: { @@ -67,138 +73,238 @@ file example would look like: EOF } -declare -a app_name -declare -a app_url -declare -a icon_ref -declare -a custom_exec # Optional custom exec command -declare -a mime_types # Optional mime types -declare -a help_flag -declare -a file_mode -declare -a launch_flag -declare INTERACTIVE_MODE=false -SCRIPTS="${SCRIPTS}" - -window_class="com.ghostty.launch-webapp" +window_class="com.ghostty.install-webapp" window_padding_x="2" +app_name="" +app_url="" +exec_cmd="" +dry_run="0" +file_mode_flag="0" +icon_ref="" +json_file="" +mime_types="" +launch_flag="0" +interactive_flag="0" # This is an internal flag, to not log some things when launch is used. +interactive_mode=false +no_interactive_flag="0" +SCRIPTS="${SCRIPTS}" +XDG_DATA_HOME=${XDG_DATA_HOME} + if [[ -z "$SCRIPTS" ]]; then echo "SCRIPTS not set" echo "using ~/.local/scripts" SCRIPTS=$HOME/.local/scripts fi -zparseopts -D -F -K -- \ - {n,-name}:=app_name \ - {u,-url}:=app_url \ - {i,-icon}:=icon_ref \ - {e,-exec}:=custom_exec \ - {m,-mime-types}:=mime_types \ - {f,-file}:=file_mode \ - {h,-help}=help_flag \ - {l,-launch}=launch_flag +if [[ -z "$XDG_DATA_HOME" ]]; then + echo "XDG_DATA_HOME not set" + echo "using ~/.local/share" + XDG_DATA_HOME=$HOME/.local/share +fi -[ ${#help_flag[@]} -gt 0 ] && usage && exit 0 +while [[ $# -gt 0 ]]; do + if [[ $1 == "-h" ]] || [[ $1 == "--help" ]]; then + usage && exit 0 + elif [[ $1 == "-n" ]] || [[ $1 == "--name" ]]; then + shift + app_name=$1 + elif [[ $1 == "-u" ]] || [[ $1 == "--url" ]]; then + shift + app_url=$1 + elif [[ $1 == "-i" ]] || [[ $1 == "--icon" ]]; then + shift + icon_ref=$1 + elif [[ $1 == "-e" ]] || [[ $1 == "--exec" ]]; then + shift + exec_cmd=$1 + elif [[ $1 == "-m" ]] || [[ $1 == "--mime-types" ]]; then + shift + mime_types=$1 + elif [[ $1 == "-f" ]] || [[ $1 == "--file" ]]; then + file_mode_flag="1" + shift + json_file=$1 + elif [[ $1 == "-l" ]] || [[ $1 == "--launch" ]]; then + launch_flag="1" + elif [[ $1 == "--interactive" ]]; then + interactive_flag="1" + elif [[ $1 == "--no-interactive" ]]; then + no_interactive_flag="1" + elif [[ $1 =~ ^--dry ]]; then + dry_run="1" + fi + shift +done -if [[ ${#launch_flag[@]} -gt 0 ]]; then +log() { + if [[ $dry_run == "1" ]]; then + echo -e "\e[34m[DRY_RUN]=>\e[0m $1" + else + echo -e "$1" + fi +} + +log_error() { + log "\e[31m[ERROR]:\e[0m $1" +} + +launch() { ghostty --class=$window_class --window-padding-x=$window_padding_x \ --keybind="ctrl+c=quit" \ - -e $SCRIPTS/hypr/install-webapp - exit 0 -fi + -e "${BASH_SOURCE[0]}" --interactive +} -# If passed in as positional arguments, without flags. -[ -n "$1" ] && app_name+=("$1") -[ -n "$2" ] && app_url+=("$2") -[ -n "$3" ] && icon_ref+=("$3") -[ -n "$4" ] && custom_exec+=("$4") -[ -n "$5" ] && mime_types+=("$5") - -# If passed in a json spec file. -if [[ -n "$file_mode" ]]; then - file=$(cat ${file_mode[-1]}) - app_name+=$(echo $file | jq -r '.name') - app_url+=$(echo $file | jq -r '.url') - icon_ref+=$(echo $file | jq -r '.icon') - custom_exec+=$(echo $file | jq -r '.exec') - mime_types+=$(echo $file | jq -r '.mime_types') -fi - -# Check if proper arguments were passed in. Start interactive mode if not. -if [[ -z "$app_name[-1]" || -z "$app_url[-1]" || -z "$icon_ref[-1]" ]]; then - echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m" - app_name+=($(gum input --prompt "Name> " --placeholder "My favorite web app")) - app_url+=($(gum input --prompt "URL> " --placeholder "https://example.com")) - icon_ref+=($(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG!)")) - INTERACTIVE_MODE=true -else - INTERACTIVE_MODE=false -fi - -# Ensure valid execution -if [[ -z "$app_name[-1]" || "$app_name[-1]" == "null" || - -z "$app_url[-1]" || "$app_url[-1]" == "null" || - -z "$icon_ref[-1]" || "$icon_ref[-1]" == "null" ]]; then - echo "You must set app name, app URL, and icon URL!" - exit 1 -fi - -APP_NAME=$app_name[-1] -APP_URL=$app_url[-1] -ICON_REF=$icon_ref[-1] -CUSTOM_EXEC=$custom_exec[-1] -MIME_TYPES=$mime_types[-1] - -# Refer to local icon or fetch remotely from URL -ICON_DIR="$HOME/.local/share/applications/icons" -# Ensure the icon directory exists (useful if it's the first run.) -[ ! -d $ICON_DIR ] && mkdir -p $ICON_DIR - -if [[ $ICON_REF == https://* ]]; then - ICON_PATH="$ICON_DIR/$APP_NAME.png" - if curl -sL -o "$ICON_PATH" "$ICON_REF"; then - ICON_PATH="$ICON_DIR/$APP_NAME.png" - else - echo "Error: Failed to download icon." - exit 1 +check_properties() { + if [[ -z $app_name ]] || [[ -z $app_url ]] || [[ -z $icon_ref ]]; then + return 1 fi -else - # Check if the icon path is a file. - if [ -f $ICON_REF ]; then - ICON_PATH=$ICON_REF - else - ICON_PATH="$ICON_DIR/$ICON_REF" + return 0 +} + +load_from_file() { + if [[ ! -f $1 ]]; then + log_error "File '$1' is not found or readable." && exit 1 fi -fi + file=$(cat $1) + app_name=$(echo $file | jq -r '.name // ""') + app_url=$(echo $file | jq -r '.url // ""') + icon_ref=$(echo $file | jq -r '.icon // ""') + exec_cmd=$(echo $file | jq -r '.exec // ""') + mime_types=$(echo $file | jq -r '.mime_types // ""') +} -# Use custom exec if provided, otherwise default behavior -if [[ -n $CUSTOM_EXEC ]] && [[ ! $CUSTOM_EXEC == "null" ]]; then - EXEC_COMMAND="$CUSTOM_EXEC" -else - EXEC_COMMAND="${SCRIPTS:-$HOME/.local/scripts}/hypr/launch-webapp $APP_URL" -fi +print_properties() { + log "\e[33mAPP NAME:\e[0m $app_name" + log "\e[33mURL:\e[0m $app_url" + log "\e[33mICON:\e[0m $icon_ref" + log "\e[33mEXEC:\e[0m $exec_cmd" + log "\e[33mMIME:\e[0m $mime_types" +} -# Create application .desktop file -DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop" +prompt_for_properties() { + log "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m" -cat >"$DESKTOP_FILE" < " --placeholder "My favorite web app") + fi + + if [[ -z $app_url ]]; then + app_url=$(gum input --prompt "URL> " --placeholder "https://example.com") + fi + + if [[ -z $icon_ref ]]; then + icon_ref=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG!)") + fi +} + +set_icon_ref() { + # Refer to local icon or fetch remotely from URL + local icon_dir="$XDG_DATA_HOME/applications/icons" + local icon_path="" + + # Ensure the icon directory exists (useful if it's the first run.) + [ ! -d $icon_dir ] && mkdir -p $icon_dir + + if [[ $icon_ref == https://* ]]; then + icon_path="$icon_dir/$app_name.png" + log "Downloading icon: $icon_ref" + if [[ $dry_run == "0" ]]; then + if curl -sL -o "$icon_path" "$icon_ref"; then + icon_path="$icon_dir/$app_name.png" + else + log_error "Failed to download icon." && exit 1 + fi + fi + else + # Check if the icon path is a file. + if [ -f $icon_ref ]; then + icon_path=$icon_ref + else + icon_path="$icon_dir/$icon_ref" + fi + fi + + icon_ref=$icon_path +} + +generate_file() { + + cat >"$1" <>"$DESKTOP_FILE" + # Add mime types if provided + if [[ -n $mime_types ]]; then + echo "MimeType=$mime_types" >>"$1" + fi + + chmod +x "$1" +} + +################################################################################ +# MAIN +################################################################################ + +if [[ $launch_flag == "1" ]]; then + launch && exit 0 fi -chmod +x "$DESKTOP_FILE" - -if [[ $INTERACTIVE_MODE == true ]]; then - echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n" +if [[ $file_mode_flag == "1" ]]; then + load_from_file $json_file +fi + +# Check that all properties are set, prompt for missing values if not. +check_properties +if [[ "$?" == "1" ]]; then + + # Check if the '--no-interactive' flag was passed and exit with error. + [[ $no_interactive_flag == "1" ]] && + log_error "Required properties not set and '--no-interactive' flag was passed." && + exit 1 + + # Only log this if not in interactive mode. + [[ $interactive_mode == "0" ]] && + log "All required properties not set, prompting for missing properties." + + prompt_for_properties + + # Check properties again after prompting in interactive mode. + check_properties + if [[ "$?" == "1" ]]; then + # Exit if they were not set during interactive mode. + log_error "You must set app name, app URL, and icon URL!" && exit 1 + fi + + # Set flag that we are in interactive mode. + interactive_mode=true +fi + +desktop_file="$XDG_DATA_HOME/applications/$app_name.desktop" + +# Parse the icon ref and download icon, if applicable. +set_icon_ref + +# Check that an exec command is set, or default to the 'launch-webapp' script. +if [[ -z $exec_cmd ]]; then + exec_cmd="$SCRIPTS/hypr/launch-webapp $app_url" +fi + +log "\e[032mCreating web app:\e[0m $desktop_file" +print_properties + +if [[ $dry_run == "0" ]]; then + generate_file $desktop_file +fi + +if [[ $interactive_mode == true ]] && [[ $dry_run == "0" ]]; then + log "You can now find $app_name using the app launcher (SUPER + SPACE)\n" fi diff --git a/webapp b/webapp index 25d83e4..ca04270 100755 --- a/webapp +++ b/webapp @@ -63,7 +63,7 @@ install() { log "Installing webapp from spec: $file" if [[ $dry_run == "0" ]]; then - $script --file $file + $script --file $file --no-interactive fi }