mirror of
https://github.com/m-housh/dotfiles.git
synced 2026-02-14 14:12:41 +00:00
feat: Unifies webapp scripts into a single script with subcommands.
This commit is contained in:
304
env/.local/scripts/hypr/utils/webapp/install
vendored
Executable file
304
env/.local/scripts/hypr/utils/webapp/install
vendored
Executable file
@@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Adapted from https://github.com/basecamp/omarchy/tree/master?tab=readme-ov-file
|
||||
|
||||
THIS_FILE=${BASH_SOURCE[0]}
|
||||
THIS=${THIS:-$(basename "$THIS_FILE")}
|
||||
LOG_FILE=${LOG_FILE:-"$THIS.log"}
|
||||
LOG_LABEL=$(basename "$THIS_FILE")
|
||||
|
||||
function usage() {
|
||||
cat <<EOF
|
||||
Generates a '.desktop' file for a web application, so that it can act as a stand alone application and
|
||||
be launched from an application launcher.
|
||||
|
||||
USAGE: $THIS [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-n | --name <name>: The name of the application.
|
||||
|
||||
-u | --url <url>: The url used to launch the application.
|
||||
|
||||
-i | --icon <icon>: The icon for the application.
|
||||
|
||||
-e | --exec <cmd>: Custom execution command (optional).
|
||||
|
||||
-m | --mime-types <types>: MIME-types for the application (optional).
|
||||
|
||||
-f | --file <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.
|
||||
|
||||
$ $THIS
|
||||
|
||||
Calling the app with named arguments:
|
||||
|
||||
$ $THIS \\
|
||||
--name "My Awesome App" \\
|
||||
--url "https://awesome.com" \\
|
||||
--icon "https://awesome.com/assets/icon.png"
|
||||
|
||||
Using a json file as input:
|
||||
|
||||
$ $THIS --file myapp.json
|
||||
|
||||
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/'.
|
||||
|
||||
Interactive sessions do not give the option to use a custom execution command or supply the
|
||||
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. A common json spec
|
||||
file example would look like:
|
||||
|
||||
{
|
||||
"name": "My Awesome App",
|
||||
"url": "https://awesome.com",
|
||||
"icon: "https://awesome.com/assets/icon.png"
|
||||
}
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
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:-$HOME/.local/scripts}"
|
||||
XDG_DATA_HOME=${XDG_DATA_HOME}
|
||||
|
||||
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
|
||||
|
||||
log() {
|
||||
logging log --source "$THIS_FILE" "$@"
|
||||
}
|
||||
|
||||
launch() {
|
||||
ghostty --class=$window_class --window-padding-x=$window_padding_x \
|
||||
--keybind="ctrl+c=quit" \
|
||||
-e "${BASH_SOURCE[0]}" --interactive
|
||||
}
|
||||
|
||||
check_properties() {
|
||||
if [[ -z $app_name ]] || [[ -z $app_url ]] || [[ -z $icon_ref ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
load_from_file() {
|
||||
if [[ ! -f $1 ]]; then
|
||||
log --error "File '$1' is not found or readable." && exit 1
|
||||
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 // ""')
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
prompt_for_properties() {
|
||||
log "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||
|
||||
if [[ -z $app_name ]]; then
|
||||
app_name=$(gum input --prompt "Name> " --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" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$app_name
|
||||
Comment=$app_name
|
||||
Exec=$exec_cmd
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$icon_ref
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
# Add mime types if provided
|
||||
if [[ -n $mime_types ]]; then
|
||||
echo "MimeType=$mime_types" >>"$1"
|
||||
fi
|
||||
|
||||
chmod +x "$1"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# MAIN
|
||||
################################################################################
|
||||
|
||||
# Setup logging file and label
|
||||
source "$SCRIPTS/hypr/logging"
|
||||
setup-logging "$LOG_FILE" "$LOG_LABEL"
|
||||
export LOG_ENABLE_DRY_RUN="$dry_run"
|
||||
|
||||
if [[ -z "$XDG_DATA_HOME" ]]; then
|
||||
log "XDG_DATA_HOME not set"
|
||||
log "using ~/.local/share"
|
||||
XDG_DATA_HOME=$HOME/.local/share
|
||||
fi
|
||||
|
||||
if [[ $launch_flag == "1" ]]; then
|
||||
launch && exit 0
|
||||
fi
|
||||
|
||||
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/webapp launch $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
|
||||
102
env/.local/scripts/hypr/utils/webapp/launch
vendored
Executable file
102
env/.local/scripts/hypr/utils/webapp/launch
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
# Adapted from https://github.com/basecamp/omarchy/tree/master?tab=readme-ov-file
|
||||
SCRIPTS="${SCRIPTS:-$HOME/.local/scripts}"
|
||||
THIS_FILE=${BASH_SOURCE[0]}
|
||||
LOG_LABEL=$(basename "$THIS_FILE")
|
||||
THIS=${THIS:-$(basename "$THIS_FILE")}
|
||||
LOG_FILE=${LOG_FILE:-"$LOG_LABEL.log"}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
|
||||
Launches a url as a web application. This script relys on the 'launch' script. This
|
||||
essentially just generates the pattern and launch command to pass into that script.
|
||||
|
||||
USAGE:
|
||||
|
||||
$ $THIS [OPTIONS] <url> [ARGS...]
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-f | --or-focus: If a window exists matching the url's domain, focus it
|
||||
instead of launching new window.
|
||||
-s | --special <name>: Launch in the special workspace name, or toggle the special
|
||||
workspace.
|
||||
-h | --help: Show this help page.
|
||||
|
||||
NOTES:
|
||||
|
||||
Any extra arguments after '--' get passed directly to the browser invocation.
|
||||
|
||||
$ $THIS https://example.com -- --some-random-flag-for-browser=1
|
||||
|
||||
Any options passed in prior to the '--' get sent to the 'launch' script, so you can pass
|
||||
options that are not specifically shown here, but the ones shown would be the most commonly
|
||||
used, so they are documented here.
|
||||
|
||||
Using the '--special' flag is useful for apps that you want to have a "summoning" like behavior.
|
||||
Upon first launch the application will be opened and the special workspace will be shown.
|
||||
Calling it again will keep the application open in the special workspace but hide the workspace.
|
||||
Further calls will not open another instance of the application, but will toggle the visiblity
|
||||
of the special workspace.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
browser="brave-browser.desktop"
|
||||
url=""
|
||||
launch_args=()
|
||||
app_args=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ $1 =~ ^--special ]] || [[ $1 =~ ^-s ]]; then
|
||||
launch_args+=("$1")
|
||||
launch_args+=("$2")
|
||||
shift # Second shift get's handled below
|
||||
elif [[ $1 =~ ^--help ]] || [[ $1 =~ ^-h ]]; then
|
||||
usage && exit 0
|
||||
elif [[ -z $url ]] && [[ ! $1 =~ ^- ]]; then
|
||||
url=$1
|
||||
elif [[ $1 == "--" ]]; then
|
||||
shift
|
||||
break
|
||||
else
|
||||
launch_args+=("$1")
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
# Strips url down to just the domain, so that we can match window classes.
|
||||
pattern() {
|
||||
pattern=${url/#https:\/\//}
|
||||
pattern=${pattern/#http:\/\//}
|
||||
pattern=${pattern%%/*}
|
||||
echo $pattern
|
||||
}
|
||||
|
||||
log() {
|
||||
logging log --source "$THIS_FILE" "$@"
|
||||
}
|
||||
|
||||
##################################################
|
||||
# MAIN
|
||||
##################################################
|
||||
|
||||
# setup logging file and label
|
||||
source "$SCRIPTS/hypr/logging"
|
||||
setup-logging "$LOG_FILE" "$LOG_LABEL"
|
||||
|
||||
if [[ -z $url ]]; then
|
||||
log --error "Must supply a url." && usage && exit 1
|
||||
fi
|
||||
|
||||
# Any left over args after "--"
|
||||
app_args="$@"
|
||||
|
||||
log "Launching URL: $url"
|
||||
log " Launch args: ${launch_args[@]}"
|
||||
log " App args: ${app_args}"
|
||||
|
||||
$SCRIPTS/hypr/launch "${launch_args[@]}" "$(pattern)" \
|
||||
setsid uwsm app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$url" "$app_args"
|
||||
Reference in New Issue
Block a user