feat(system): comprehensive branding, styling, and system feature update
- Relocate themes to assets/themes/ and update all references. - Implement custom SDDM theme and Plymouth theme enhancements. - Add themed templates for Alacritty, Hyprland, Waybar, and other apps. - Introduce Makima key remapper module and configuration. - Add Voxtype and Walker configurations. - Implement systemd power management and timeout optimizations. - Add Nautilus-python extensions for LocalSend. - Update branding assets and ASCII art integration.
This commit is contained in:
5
bin/apps/nomarchy-launch-about
Executable file
5
bin/apps/nomarchy-launch-about
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the fastfetch TUI that gives information about the current system.
|
||||
|
||||
exec nnomarchy-launch-or-focus-tui "bash -c 'fastfetch; read -n 1 -s'"
|
||||
5
bin/apps/nomarchy-launch-audio
Executable file
5
bin/apps/nomarchy-launch-audio
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nnomarchy audio controls TUI (provided by wiremix).
|
||||
|
||||
nnomarchy-launch-or-focus-tui wiremix
|
||||
7
bin/apps/nomarchy-launch-bluetooth
Executable file
7
bin/apps/nomarchy-launch-bluetooth
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nnomarchy bluetooth controls TUI (provided by bluetui).
|
||||
# Also attempts to unblock bluetooth service if rfkill had blocked it.
|
||||
|
||||
rfkill unblock bluetooth
|
||||
exec nnomarchy-launch-or-focus-tui bluetui
|
||||
17
bin/apps/nomarchy-launch-browser
Executable file
17
bin/apps/nomarchy-launch-browser
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the default browser as determined by xdg-settings.
|
||||
# Automatically converts --private into the correct flag for the given browser.
|
||||
|
||||
default_browser=$(xdg-settings get default-web-browser)
|
||||
browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1)
|
||||
|
||||
if $browser_exec --help | grep -q MOZ_LOG; then
|
||||
private_flag="--private-window"
|
||||
elif [[ $browser_exec =~ edge ]]; then
|
||||
private_flag="--inprivate"
|
||||
else
|
||||
private_flag="--incognito"
|
||||
fi
|
||||
|
||||
exec setsid uwsm-app -- "$browser_exec" "${@/--private/$private_flag}"
|
||||
15
bin/apps/nomarchy-launch-editor
Executable file
15
bin/apps/nomarchy-launch-editor
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the default editor as determined by $EDITOR (set via ~/.config/uwsm/default) (or nvim if missing).
|
||||
# Starts suitable editors in a terminal window and otherwise as a regular application.
|
||||
|
||||
nnomarchy-cmd-present "$EDITOR" || EDITOR=nvim
|
||||
|
||||
case "$EDITOR" in
|
||||
nvim | vim | nano | micro | hx | helix | fresh)
|
||||
exec nnomarchy-launch-tui "$EDITOR" "$@"
|
||||
;;
|
||||
*)
|
||||
exec setsid uwsm-app -- "$EDITOR" "$@"
|
||||
;;
|
||||
esac
|
||||
7
bin/apps/nomarchy-launch-floating-terminal-with-presentation
Executable file
7
bin/apps/nomarchy-launch-floating-terminal-with-presentation
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch a floating terminal with the Nnomarchy logo presentation, then execute the command passed in, and finally end with the nnomarchy-show-done presentation.
|
||||
# Used by actions such as Update System.
|
||||
|
||||
cmd="$*"
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nnomarchy.terminal --title=Nnomarchy -e bash -c "nnomarchy-show-logo; $cmd; if (( \$? != 130 )); then nnomarchy-show-done; fi"
|
||||
19
bin/apps/nomarchy-launch-or-focus
Executable file
19
bin/apps/nomarchy-launch-or-focus
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given command identified by the passed in window-pattern.
|
||||
# Use by some default bindings, like the one for Spotify, to ensure there is only one instance of the application open.
|
||||
|
||||
if (($# == 0)); then
|
||||
echo "Usage: nnomarchy-launch-or-focus [window-pattern] [launch-command]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WINDOW_PATTERN="$1"
|
||||
LAUNCH_COMMAND="${2:-"uwsm-app -- $WINDOW_PATTERN"}"
|
||||
WINDOW_ADDRESS=$(hyprctl clients -j | jq -r --arg p "$WINDOW_PATTERN" '.[]|select((.class|test("\\b" + $p + "\\b";"i")) or (.title|test("\\b" + $p + "\\b";"i")))|.address' | head -n1)
|
||||
|
||||
if [[ -n $WINDOW_ADDRESS ]]; then
|
||||
hyprctl dispatch focuswindow "address:$WINDOW_ADDRESS"
|
||||
else
|
||||
eval exec setsid $LAUNCH_COMMAND
|
||||
fi
|
||||
9
bin/apps/nomarchy-launch-or-focus-tui
Executable file
9
bin/apps/nomarchy-launch-or-focus-tui
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given TUI identified by the passed in as the command.
|
||||
# Use by commands like nnomarchy-launch-wifi to ensure there is only one wifi configuration screen open.
|
||||
|
||||
APP_ID="org.nnomarchy.$(basename "$1")"
|
||||
LAUNCH_COMMAND="nnomarchy-launch-tui $@"
|
||||
|
||||
exec nnomarchy-launch-or-focus "$APP_ID" "$LAUNCH_COMMAND"
|
||||
15
bin/apps/nomarchy-launch-or-focus-webapp
Executable file
15
bin/apps/nomarchy-launch-or-focus-webapp
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given web app identified by the window-pattern.
|
||||
# Use by some default bindings, like the one for WhatsApp, to ensure there is only one instance of the application open.
|
||||
|
||||
if (($# == 0)); then
|
||||
echo "Usage: nnomarchy-launch-or-focus-webapp [window-pattern] [url-and-flags...]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WINDOW_PATTERN="$1"
|
||||
shift
|
||||
LAUNCH_COMMAND="nnomarchy-launch-webapp $@"
|
||||
|
||||
exec nnomarchy-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND"
|
||||
56
bin/apps/nomarchy-launch-screensaver
Executable file
56
bin/apps/nomarchy-launch-screensaver
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nnomarchy screensaver in the default terminal on the system with the correct font configuration.
|
||||
|
||||
# Exit early if we don't have the tte show
|
||||
if ! command -v tte &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Exit early if screensave is already running
|
||||
pgrep -f org.nnomarchy.screensaver && exit 0
|
||||
|
||||
# Allow screensaver to be turned off but also force started
|
||||
# Skip if screensaver is disabled in configuration
|
||||
if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# Silently quit Walker on overlay
|
||||
walker -q
|
||||
|
||||
focused=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')
|
||||
terminal=$(xdg-terminal-exec --print-id)
|
||||
|
||||
for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do
|
||||
hyprctl dispatch focusmonitor $m
|
||||
|
||||
case $terminal in
|
||||
*Alacritty*)
|
||||
hyprctl dispatch exec -- \
|
||||
alacritty --class=org.nnomarchy.screensaver \
|
||||
--config-file ~/.local/share/nnomarchy/default/alacritty/screensaver.toml \
|
||||
-e nnomarchy-cmd-screensaver
|
||||
;;
|
||||
*ghostty*)
|
||||
hyprctl dispatch exec -- \
|
||||
ghostty --class=org.nnomarchy.screensaver \
|
||||
--config-file=~/.local/share/nnomarchy/default/ghostty/screensaver \
|
||||
--font-size=18 \
|
||||
-e nnomarchy-cmd-screensaver
|
||||
;;
|
||||
*kitty*)
|
||||
hyprctl dispatch exec -- \
|
||||
kitty --class=org.nnomarchy.screensaver \
|
||||
--override font_size=18 \
|
||||
--override window_padding_width=0 \
|
||||
-e nnomarchy-cmd-screensaver
|
||||
;;
|
||||
*)
|
||||
notify-send -u low "✋ Screensaver only runs in Alacritty, Ghostty, or Kitty"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
hyprctl dispatch focusmonitor $focused
|
||||
5
bin/apps/nomarchy-launch-tui
Executable file
5
bin/apps/nomarchy-launch-tui
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the TUI command passed in as an argument in the default terminal with an org.nnomarchy.COMMAND app id for styling.
|
||||
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nnomarchy.$(basename $1) -e "$1" "${@:2}"
|
||||
15
bin/apps/nomarchy-launch-walker
Executable file
15
bin/apps/nomarchy-launch-walker
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Walker application launcher while ensuring that it's data provider (called elephant) is running first.
|
||||
|
||||
# Ensure elephant is running before launching walker
|
||||
if ! pgrep -x elephant > /dev/null; then
|
||||
setsid uwsm-app -- elephant &
|
||||
fi
|
||||
|
||||
# Ensure walker service is running
|
||||
if ! pgrep -f "walker --gapplication-service" > /dev/null; then
|
||||
setsid uwsm-app -- walker --gapplication-service &
|
||||
fi
|
||||
|
||||
exec walker --width 644 --maxheight 300 --minheight 300 "$@"
|
||||
12
bin/apps/nomarchy-launch-webapp
Executable file
12
bin/apps/nomarchy-launch-webapp
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the passed in URL as a web app in the default browser (or chromium if the default doesn't support --app).
|
||||
|
||||
browser=$(xdg-settings get default-web-browser)
|
||||
|
||||
case $browser in
|
||||
google-chrome* | brave-browser* | microsoft-edge* | opera* | vivaldi* | helium*) ;;
|
||||
*) browser="chromium.desktop" ;;
|
||||
esac
|
||||
|
||||
exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}"
|
||||
7
bin/apps/nomarchy-launch-wifi
Executable file
7
bin/apps/nomarchy-launch-wifi
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nnomarchy wifi controls (provided by the Impala TUI).
|
||||
# Attempts to unblock the wifi service first in case it should be been blocked.
|
||||
|
||||
rfkill unblock wifi
|
||||
nnomarchy-launch-or-focus-tui impala
|
||||
7
bin/apps/nomarchy-restart-app
Executable file
7
bin/apps/nomarchy-restart-app
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart an application by killing it and relaunching via uwsm.
|
||||
# Usage: nnomarchy-restart-app <application-name> [application-args...]
|
||||
|
||||
pkill -x $1
|
||||
setsid uwsm-app -- "$@" >/dev/null 2>&1 &
|
||||
5
bin/apps/nomarchy-restart-btop
Executable file
5
bin/apps/nomarchy-restart-btop
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reload btop configuration (used by the Nnomarchy theme switching).
|
||||
|
||||
pkill -SIGUSR2 btop
|
||||
7
bin/apps/nomarchy-restart-opencode
Executable file
7
bin/apps/nomarchy-restart-opencode
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reload opencode configuration (used by the Nnomarchy theme switching).
|
||||
|
||||
if pgrep -x opencode >/dev/null; then
|
||||
killall -SIGUSR2 opencode
|
||||
fi
|
||||
13
bin/apps/nomarchy-restart-terminal
Executable file
13
bin/apps/nomarchy-restart-terminal
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
|
||||
touch ~/.config/alacritty/alacritty.toml
|
||||
fi
|
||||
|
||||
if pgrep -x kitty >/dev/null; then
|
||||
killall -SIGUSR1 kitty >/dev/null
|
||||
fi
|
||||
|
||||
if pgrep -x ghostty >/dev/null; then
|
||||
killall -SIGUSR2 ghostty
|
||||
fi
|
||||
7
bin/apps/nomarchy-restart-tmux
Executable file
7
bin/apps/nomarchy-restart-tmux
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart tmux if running with the latest configuration
|
||||
|
||||
if pgrep -x tmux; then
|
||||
tmux source-file ~/.config/tmux/tmux.conf
|
||||
fi
|
||||
59
bin/apps/nomarchy-tui-install
Executable file
59
bin/apps/nomarchy-tui-install
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if (( $# != 4 )); then
|
||||
echo -e "\e[32mLet's create a TUI shortcut you can start with the app launcher.\n\e[0m"
|
||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My TUI")
|
||||
APP_EXEC=$(gum input --prompt "Launch Command> " --placeholder "lazydocker or bash -c 'dust; read -n 1 -s'")
|
||||
WINDOW_STYLE=$(gum choose --header "Window style" float tile)
|
||||
ICON_URL=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG or SVG!)")
|
||||
else
|
||||
APP_NAME="$1"
|
||||
APP_EXEC="$2"
|
||||
WINDOW_STYLE="$3"
|
||||
ICON_URL="$4"
|
||||
fi
|
||||
|
||||
if [[ -z $APP_NAME || -z $APP_EXEC || -z $ICON_URL ]]; then
|
||||
echo "You must set app name, app command, and icon URL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
|
||||
if [[ ! $ICON_URL =~ ^https?:// ]] && [[ -f $ICON_URL ]]; then
|
||||
ICON_PATH="$ICON_URL"
|
||||
else
|
||||
ICON_PATH="$ICON_DIR/$APP_NAME.png"
|
||||
mkdir -p "$ICON_DIR"
|
||||
if ! curl -sL -o "$ICON_PATH" "$ICON_URL"; then
|
||||
echo "Error: Failed to download icon."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $WINDOW_STYLE == "float" ]]; then
|
||||
APP_CLASS="TUI.float"
|
||||
else
|
||||
APP_CLASS="TUI.tile"
|
||||
fi
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=xdg-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$ICON_PATH
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
chmod +x "$DESKTOP_FILE"
|
||||
|
||||
if (( $# != 4 )); then
|
||||
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
|
||||
fi
|
||||
43
bin/apps/nomarchy-tui-remove
Executable file
43
bin/apps/nomarchy-tui-remove
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all TUIs
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec).*-e' "$file"; then
|
||||
TUIS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
if ((${#TUIS[@]})); then
|
||||
IFS=$'\n' SORTED_TUIS=($(sort <<<"${TUIS[*]}"))
|
||||
unset IFS
|
||||
APP_NAMES_STRING=$(gum choose --no-limit --header "Select TUI to remove..." --selected-prefix="✗ " "${SORTED_TUIS[@]}")
|
||||
# Convert newline-separated string to array
|
||||
APP_NAMES=()
|
||||
while IFS= read -r line; do
|
||||
[[ -n $line ]] && APP_NAMES+=("$line")
|
||||
done <<< "$APP_NAMES_STRING"
|
||||
else
|
||||
echo "No TUIs to remove."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Use array to preserve spaces in app names
|
||||
APP_NAMES=("$@")
|
||||
fi
|
||||
|
||||
if (( ${#APP_NAMES[@]} == 0 )); then
|
||||
echo "You must provide TUI names."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for APP_NAME in "${APP_NAMES[@]}"; do
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
rm -f "$ICON_DIR/$APP_NAME.png"
|
||||
echo "Removed $APP_NAME"
|
||||
done
|
||||
36
bin/apps/nomarchy-tui-remove-all
Executable file
36
bin/apps/nomarchy-tui-remove-all
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Remove all TUIs installed via nnomarchy-tui-install.
|
||||
# Identifies TUIs by their Exec pattern (xdg-terminal-exec --app-id=TUI.).
|
||||
|
||||
set -e
|
||||
|
||||
APP_DIR="${1:-$HOME/.local/share/applications}"
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
echo "Scanning for TUIs in $APP_DIR..."
|
||||
|
||||
tui_desktop_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q "Exec=xdg-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then
|
||||
tui_desktop_files+=("$file")
|
||||
fi
|
||||
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
|
||||
|
||||
if (( ${#tui_desktop_files[@]} == 0 )); then
|
||||
echo "No TUIs found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "${tui_desktop_files[@]}"; do
|
||||
app_name=$(basename "$file" .desktop)
|
||||
echo "Removing TUI: $app_name"
|
||||
rm -f "$file"
|
||||
rm -f "$ICON_DIR/$app_name.png"
|
||||
done
|
||||
|
||||
if command -v update-desktop-database &>/dev/null; then
|
||||
update-desktop-database "$APP_DIR" &>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "TUIs removed successfully."
|
||||
6
bin/apps/nomarchy-voxtype-config
Executable file
6
bin/apps/nomarchy-voxtype-config
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Used by Voxtype waybar module to open config on right click
|
||||
|
||||
exec nnomarchy-launch-editor ~/.config/voxtype/config.toml
|
||||
21
bin/apps/nomarchy-voxtype-install
Executable file
21
bin/apps/nomarchy-voxtype-install
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Install voxtype and configure it for use.
|
||||
|
||||
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
|
||||
nnomarchy-pkg-add wtype voxtype-bin
|
||||
|
||||
# Setup voxtype
|
||||
mkdir -p ~/.config/voxtype
|
||||
cp ~/.config/nomarchy/default/voxtype/config.toml ~/.config/voxtype/
|
||||
|
||||
voxtype setup --download --no-post-install
|
||||
if nnomarchy-hw-vulkan; then
|
||||
voxtype setup gpu --enable || true
|
||||
fi
|
||||
voxtype setup systemd
|
||||
|
||||
nnomarchy-restart-waybar
|
||||
notify-send " Voxtype Dictation Ready" "Press Super + Ctrl + X to toggle dictation.\nEdit ~/.config/voxtype/config.toml for options." -t 10000
|
||||
fi
|
||||
5
bin/apps/nomarchy-voxtype-model
Executable file
5
bin/apps/nomarchy-voxtype-model
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
nnomarchy-launch-floating-terminal-with-presentation "voxtype setup model"
|
||||
nnomarchy-restart-waybar
|
||||
20
bin/apps/nomarchy-voxtype-remove
Executable file
20
bin/apps/nomarchy-voxtype-remove
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Remove voxtype and its configurations.
|
||||
|
||||
if nnomarchy-cmd-present voxtype; then
|
||||
echo "Uninstall Voxtype to remove dictation."
|
||||
|
||||
# Remove services
|
||||
systemctl --user stop voxtype.service 2>/dev/null || true
|
||||
rm -f ~/.config/systemd/user/voxtype*
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Remove packages and configs
|
||||
nnomarchy-pkg-drop wtype voxtype-bin
|
||||
rm -rf ~/.config/voxtype
|
||||
rm -rf ~/.local/share/voxtype
|
||||
else
|
||||
echo "Voxtype was not installed."
|
||||
fi
|
||||
9
bin/apps/nomarchy-voxtype-status
Executable file
9
bin/apps/nomarchy-voxtype-status
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
if nnomarchy-cmd-present voxtype; then
|
||||
voxtype status --follow --extended --format json | while read -r line; do
|
||||
echo "$line" | jq -c '. + {alt: .class}'
|
||||
done
|
||||
else
|
||||
echo '{"alt": "", "tooltip": ""}'
|
||||
fi
|
||||
11
bin/apps/nomarchy-webapp-handler-hey
Executable file
11
bin/apps/nomarchy-webapp-handler-hey
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
url="$1"
|
||||
web_url="https://app.hey.com"
|
||||
|
||||
# Handle mailto: URLs
|
||||
if [[ $url =~ ^mailto: ]]; then
|
||||
email=$(echo "$url" | sed 's/mailto://')
|
||||
web_url="https://app.hey.com/messages/new?to=$email"
|
||||
fi
|
||||
|
||||
exec nnomarchy-launch-webapp "$web_url"
|
||||
20
bin/apps/nomarchy-webapp-handler-zoom
Executable file
20
bin/apps/nomarchy-webapp-handler-zoom
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
url="$1"
|
||||
web_url="https://app.zoom.us/wc/home"
|
||||
|
||||
if [[ $url =~ ^zoom(mtg|us):// ]]; then
|
||||
confno=$(echo "$url" | sed -n 's/.*[?&]confno=\([^&]*\).*/\1/p')
|
||||
|
||||
if [[ -n $confno ]]; then
|
||||
pwd=$(echo "$url" | sed -n 's/.*[?&]pwd=\([^&]*\).*/\1/p')
|
||||
|
||||
if [[ -n $pwd ]]; then
|
||||
web_url="https://app.zoom.us/wc/join/$confno?pwd=$pwd"
|
||||
else
|
||||
web_url="https://app.zoom.us/wc/join/$confno"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exec nnomarchy-launch-webapp "$web_url"
|
||||
89
bin/apps/nomarchy-webapp-install
Executable file
89
bin/apps/nomarchy-webapp-install
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
if (( $# < 3 )); 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")
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
|
||||
# Try to fetch favicon automatically first.
|
||||
FAVICON_URL="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
|
||||
mkdir -p "$ICON_DIR"
|
||||
if curl -fsSL -o "$ICON_DIR/$APP_NAME.png" "$FAVICON_URL" && [[ -s $ICON_DIR/$APP_NAME.png ]]; then
|
||||
ICON_REF="$APP_NAME.png"
|
||||
else
|
||||
ICON_REF=$(gum input --prompt "Icon URL> " --placeholder "Could not fetch favicon automatically. Enter PNG icon URL (see https://dashboardicons.com)")
|
||||
fi
|
||||
|
||||
CUSTOM_EXEC=""
|
||||
MIME_TYPES=""
|
||||
INTERACTIVE_MODE=true
|
||||
else
|
||||
APP_NAME="$1"
|
||||
APP_URL="$2"
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
ICON_REF="$3"
|
||||
CUSTOM_EXEC="$4" # Optional custom exec command
|
||||
MIME_TYPES="$5" # Optional mime types
|
||||
INTERACTIVE_MODE=false
|
||||
fi
|
||||
|
||||
# Ensure valid execution
|
||||
if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
||||
echo "You must set app name and app URL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve icon from URL or from a local icon name.
|
||||
mkdir -p "$ICON_DIR"
|
||||
|
||||
if [[ -z $ICON_REF ]]; then
|
||||
ICON_REF="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
|
||||
fi
|
||||
|
||||
if [[ $ICON_REF =~ ^https?:// ]]; then
|
||||
ICON_PATH="$ICON_DIR/$APP_NAME.png"
|
||||
if ! curl -fsSL -o "$ICON_PATH" "$ICON_REF" || [[ ! -s $ICON_PATH ]]; then
|
||||
echo "Error: Failed to download icon."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
ICON_PATH="$ICON_DIR/$ICON_REF"
|
||||
fi
|
||||
|
||||
# Use custom exec if provided, otherwise default behavior
|
||||
EXEC_COMMAND="${CUSTOM_EXEC:-nnomarchy-launch-webapp $APP_URL}"
|
||||
|
||||
# Create application .desktop file
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=$EXEC_COMMAND
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$ICON_PATH
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
# Add mime types if provided
|
||||
if [[ -n $MIME_TYPES ]]; then
|
||||
echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE"
|
||||
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"
|
||||
fi
|
||||
43
bin/apps/nomarchy-webapp-remove
Executable file
43
bin/apps/nomarchy-webapp-remove
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all web apps
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(nnomarchy-launch-webapp\|nnomarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
if ((${#WEB_APPS[@]})); then
|
||||
IFS=$'\n' SORTED_WEB_APPS=($(sort <<<"${WEB_APPS[*]}"))
|
||||
unset IFS
|
||||
APP_NAMES_STRING=$(gum choose --no-limit --header "Select web app to remove..." --selected-prefix="✗ " "${SORTED_WEB_APPS[@]}")
|
||||
# Convert newline-separated string to array
|
||||
APP_NAMES=()
|
||||
while IFS= read -r line; do
|
||||
[[ -n $line ]] && APP_NAMES+=("$line")
|
||||
done <<< "$APP_NAMES_STRING"
|
||||
else
|
||||
echo "No web apps to remove."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Use array to preserve spaces in app names
|
||||
APP_NAMES=("$@")
|
||||
fi
|
||||
|
||||
if (( ${#APP_NAMES[@]} == 0 )); then
|
||||
echo "You must select at least one web app to remove."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for APP_NAME in "${APP_NAMES[@]}"; do
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
rm -f "$ICON_DIR/$APP_NAME.png"
|
||||
echo "Removed $APP_NAME"
|
||||
done
|
||||
36
bin/apps/nomarchy-webapp-remove-all
Executable file
36
bin/apps/nomarchy-webapp-remove-all
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Remove all web apps installed via nnomarchy-webapp-install.
|
||||
# Identifies web apps by their Exec pattern (nnomarchy-launch-webapp or nnomarchy-webapp-handler).
|
||||
|
||||
set -e
|
||||
|
||||
APP_DIR="${1:-$HOME/.local/share/applications}"
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
echo "Scanning for web apps in $APP_DIR..."
|
||||
|
||||
webapp_desktop_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q "Exec=nnomarchy-launch-webapp\|Exec=nnomarchy-webapp-handler" "$file" 2>/dev/null; then
|
||||
webapp_desktop_files+=("$file")
|
||||
fi
|
||||
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
|
||||
|
||||
if (( ${#webapp_desktop_files[@]} == 0 )); then
|
||||
echo "No web apps found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "${webapp_desktop_files[@]}"; do
|
||||
app_name=$(basename "$file" .desktop)
|
||||
echo "Removing web app: $app_name"
|
||||
rm -f "$file"
|
||||
rm -f "$ICON_DIR/$app_name.png"
|
||||
done
|
||||
|
||||
if command -v update-desktop-database &>/dev/null; then
|
||||
update-desktop-database "$APP_DIR" &>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "Web apps removed successfully."
|
||||
448
bin/apps/nomarchy-windows-vm
Executable file
448
bin/apps/nomarchy-windows-vm
Executable file
@@ -0,0 +1,448 @@
|
||||
#!/bin/bash
|
||||
COMPOSE_FILE="$HOME/.config/windows/docker-compose.yml"
|
||||
|
||||
check_prerequisites() {
|
||||
local DISK_SIZE_GB=${1:-64}
|
||||
local REQUIRED_SPACE=$((DISK_SIZE_GB + 10)) # Add 10GB for Windows ISO and overhead
|
||||
|
||||
# Check for KVM support
|
||||
if [[ ! -e /dev/kvm ]]; then
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
"❌ KVM virtualization not available!" \
|
||||
"" \
|
||||
"Please enable virtualization in BIOS or run:" \
|
||||
" sudo modprobe kvm-intel # for Intel CPUs" \
|
||||
" sudo modprobe kvm-amd # for AMD CPUs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
|
||||
if (( AVAILABLE_SPACE < REQUIRED_SPACE )); then
|
||||
echo "❌ Insufficient disk space!"
|
||||
echo " Available: ${AVAILABLE_SPACE}GB"
|
||||
echo " Required: ${REQUIRED_SPACE}GB (${DISK_SIZE_GB}GB disk + 10GB for Windows image)"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_windows() {
|
||||
# Set up trap to handle Ctrl+C
|
||||
trap "echo ''; echo 'Installation cancelled by user'; exit 1" INT
|
||||
|
||||
check_prerequisites
|
||||
|
||||
nnomarchy-pkg-add freerdp openbsd-netcat gum
|
||||
|
||||
mkdir -p "$HOME/.windows"
|
||||
mkdir -p "$HOME/.config/windows"
|
||||
mkdir -p "$HOME/.local/share/applications/icons"
|
||||
|
||||
# Install Windows VM icon and desktop file
|
||||
if [[ -f $NOMARCHY_PATH/applications/icons/windows.png ]]; then
|
||||
cp "$NOMARCHY_PATH/applications/icons/windows.png" "$HOME/.local/share/applications/icons/windows.png"
|
||||
fi
|
||||
|
||||
cat << EOF | tee "$HOME/.local/share/applications/windows-vm.desktop" > /dev/null
|
||||
[Desktop Entry]
|
||||
Name=Windows
|
||||
Comment=Start Windows VM via Docker and connect with RDP
|
||||
Exec=uwsm app -- nnomarchy-windows-vm launch
|
||||
Icon=$HOME/.local/share/applications/icons/windows.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=System;Virtualization;
|
||||
EOF
|
||||
|
||||
# Get system resources
|
||||
TOTAL_RAM=$(free -h | awk 'NR==2 {print $2}')
|
||||
TOTAL_RAM_GB=$(awk 'NR==1 {printf "%d", $2/1024/1024}' /proc/meminfo)
|
||||
TOTAL_CORES=$(nproc)
|
||||
|
||||
echo ""
|
||||
echo "System Resources Detected:"
|
||||
echo " Total RAM: $TOTAL_RAM"
|
||||
echo " Total CPU Cores: $TOTAL_CORES"
|
||||
echo ""
|
||||
|
||||
RAM_OPTIONS=""
|
||||
for size in 2 4 8 16 32 64; do
|
||||
if (( size <= TOTAL_RAM_GB )); then
|
||||
RAM_OPTIONS="$RAM_OPTIONS ${size}G"
|
||||
fi
|
||||
done
|
||||
|
||||
SELECTED_RAM=$(echo $RAM_OPTIONS | tr ' ' '\n' | gum choose --selected="4G" --header="How much RAM would you like to allocate to Windows VM?")
|
||||
|
||||
# Check if user cancelled
|
||||
if [[ -z $SELECTED_RAM ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_CORES=$(gum input --placeholder="Number of CPU cores (1-$TOTAL_CORES)" --value="2" --header="How many CPU cores would you like to allocate to Windows VM?" --char-limit=2)
|
||||
|
||||
# Check if user cancelled (Ctrl+C in gum input returns empty string)
|
||||
if [[ -z $SELECTED_CORES ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $SELECTED_CORES =~ ^[0-9]+$ ]] || (( SELECTED_CORES < 1 )) || (( SELECTED_CORES > TOTAL_CORES )); then
|
||||
echo "Invalid input. Using default: 2 cores"
|
||||
SELECTED_CORES=2
|
||||
fi
|
||||
|
||||
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
|
||||
MAX_DISK_GB=$((AVAILABLE_SPACE - 10)) # Leave 10GB for Windows image
|
||||
|
||||
# Check if we have enough space for minimum
|
||||
if (( MAX_DISK_GB < 32 )); then
|
||||
echo "❌ Insufficient disk space for Windows VM!"
|
||||
echo " Available: ${AVAILABLE_SPACE}GB"
|
||||
echo " Minimum required: 42GB (32GB disk + 10GB for Windows image)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISK_OPTIONS=""
|
||||
for size in 32 64 128 256 512; do
|
||||
if (( size <= MAX_DISK_GB )); then
|
||||
DISK_OPTIONS="$DISK_OPTIONS ${size}G"
|
||||
fi
|
||||
done
|
||||
|
||||
# Default to 64G if available, otherwise 32G
|
||||
DEFAULT_DISK="64G"
|
||||
if ! echo "$DISK_OPTIONS" | grep -q "64G"; then
|
||||
DEFAULT_DISK="32G"
|
||||
fi
|
||||
|
||||
SELECTED_DISK=$(echo $DISK_OPTIONS | tr ' ' '\n' | gum choose --selected="$DEFAULT_DISK" --header="How much disk space would you like to give Windows VM? (64GB+ recommended)")
|
||||
|
||||
# Check if user cancelled
|
||||
if [[ -z $SELECTED_DISK ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract just the number for prerequisite check
|
||||
DISK_SIZE_NUM=$(echo "$SELECTED_DISK" | sed 's/G//')
|
||||
|
||||
# Re-check prerequisites with selected disk size
|
||||
check_prerequisites "$DISK_SIZE_NUM"
|
||||
|
||||
# Prompt for username and password
|
||||
USERNAME=$(gum input --placeholder="Username (Press enter to use default: docker)" --header="Enter Windows username:")
|
||||
if [[ -z $USERNAME ]]; then
|
||||
USERNAME="docker"
|
||||
fi
|
||||
|
||||
PASSWORD=$(gum input --placeholder="Password (Press enter to use default: admin)" --password --header="Enter Windows password:")
|
||||
if [[ -z $PASSWORD ]]; then
|
||||
PASSWORD="admin"
|
||||
PASSWORD_DISPLAY="(default)"
|
||||
else
|
||||
PASSWORD_DISPLAY="(user-defined)"
|
||||
fi
|
||||
|
||||
# Display configuration summary
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align left \
|
||||
--bold \
|
||||
"Windows VM Configuration" \
|
||||
"" \
|
||||
"RAM: $SELECTED_RAM" \
|
||||
"CPU: $SELECTED_CORES cores" \
|
||||
"Disk: $SELECTED_DISK" \
|
||||
"Username: $USERNAME" \
|
||||
"Password: $PASSWORD_DISPLAY"
|
||||
|
||||
# Ask for confirmation
|
||||
echo ""
|
||||
if ! gum confirm "Proceed with this configuration?"; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $HOME/Windows
|
||||
|
||||
# Create docker-compose.yml in user config directory
|
||||
cat << EOF | tee "$COMPOSE_FILE" > /dev/null
|
||||
services:
|
||||
windows:
|
||||
image: dockurr/windows
|
||||
container_name: nnomarchy-windows
|
||||
environment:
|
||||
VERSION: "11"
|
||||
RAM_SIZE: "$SELECTED_RAM"
|
||||
CPU_CORES: "$SELECTED_CORES"
|
||||
DISK_SIZE: "$SELECTED_DISK"
|
||||
USERNAME: "$USERNAME"
|
||||
PASSWORD: "$PASSWORD"
|
||||
TZ: "$(timedatectl show -p Timezone --value 2>/dev/null || echo UTC)"
|
||||
ARGUMENTS: "-rtc base=localtime,clock=host,driftfix=slew"
|
||||
devices:
|
||||
- /dev/kvm
|
||||
- /dev/net/tun
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- 127.0.0.1:8006:8006
|
||||
- 127.0.0.1:3389:3389/tcp
|
||||
- 127.0.0.1:3389:3389/udp
|
||||
volumes:
|
||||
- $HOME/.windows:/storage
|
||||
- $HOME/Windows:/shared
|
||||
restart: unless-stopped
|
||||
stop_grace_period: 2m
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Starting Windows VM installation..."
|
||||
echo "This will download a Windows 11 image (may take 10-15 minutes)."
|
||||
echo ""
|
||||
echo "Monitor installation progress at: http://127.0.0.1:8006"
|
||||
echo ""
|
||||
|
||||
# Start docker-compose with user's config
|
||||
echo "Starting Windows VM with docker-compose..."
|
||||
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
|
||||
echo "❌ Failed to start Windows VM!"
|
||||
echo " Common issues:"
|
||||
echo " - Docker daemon not running: sudo systemctl start docker"
|
||||
echo " - Port already in use: check if another VM is running"
|
||||
echo " - Permission issues: make sure you're in the docker group"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Windows VM is starting up!"
|
||||
echo ""
|
||||
echo "Opening browser to monitor installation..."
|
||||
|
||||
# Open browser to monitor installation
|
||||
sleep 3
|
||||
xdg-open "http://127.0.0.1:8006"
|
||||
|
||||
echo ""
|
||||
echo "Installation is running in the background."
|
||||
echo "You can monitor progress at: http://127.0.0.1:8006"
|
||||
echo ""
|
||||
echo "Once finished, launch 'Windows' via Super + Space"
|
||||
echo ""
|
||||
echo "To stop the VM: nnomarchy-windows-vm stop"
|
||||
echo "To change resources: ~/.config/windows/docker-compose.yml"
|
||||
echo ""
|
||||
}
|
||||
|
||||
remove_windows() {
|
||||
if ! gum confirm --default=false "Remove Windows VM and delete all associated data?"; then
|
||||
echo "Removal cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Removing Windows VM..."
|
||||
|
||||
docker-compose -f "$COMPOSE_FILE" down 2>/dev/null || true
|
||||
|
||||
docker rmi dockurr/windows 2>/dev/null || echo "Image already removed or not found"
|
||||
|
||||
rm "$HOME/.local/share/applications/windows-vm.desktop"
|
||||
rm -rf "$HOME/.config/windows"
|
||||
rm -rf "$HOME/.windows"
|
||||
|
||||
echo ""
|
||||
echo "Windows VM removal completed!"
|
||||
}
|
||||
|
||||
launch_windows() {
|
||||
KEEP_ALIVE=false
|
||||
if [[ $1 = "--keep-alive" ]] || [[ $1 = "-k" ]]; then
|
||||
KEEP_ALIVE=true
|
||||
fi
|
||||
|
||||
# Check if config exists
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured. Please run: nnomarchy-windows-vm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract credentials from compose file
|
||||
WIN_USER=$(grep "USERNAME:" "$COMPOSE_FILE" | sed 's/.*USERNAME: "\(.*\)"/\1/')
|
||||
WIN_PASS=$(grep "PASSWORD:" "$COMPOSE_FILE" | sed 's/.*PASSWORD: "\(.*\)"/\1/')
|
||||
|
||||
# Use defaults if not found
|
||||
[[ -z $WIN_USER ]] && WIN_USER="docker"
|
||||
[[ -z $WIN_PASS ]] && WIN_PASS="admin"
|
||||
|
||||
# Check if container is already running
|
||||
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nnomarchy-windows 2>/dev/null)
|
||||
|
||||
if [[ $CONTAINER_STATUS != "running" ]]; then
|
||||
echo "Starting Windows VM..."
|
||||
|
||||
# Send desktop notification
|
||||
notify-send " Starting Windows VM" " This can take 15-30 seconds" -t 15000
|
||||
|
||||
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
|
||||
echo "❌ Failed to start Windows VM!"
|
||||
echo " Try checking: nnomarchy-windows-vm status"
|
||||
echo " View logs: docker logs nnomarchy-windows"
|
||||
notify-send -u critical "Windows VM" "Failed to start Windows VM"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting for Windows VM to start..."
|
||||
WAIT_COUNT=0
|
||||
until docker logs nnomarchy-windows 2>&1 | grep -qi "windows started successfully"; do
|
||||
sleep 2
|
||||
WAIT_COUNT=$((WAIT_COUNT + 1))
|
||||
if (( WAIT_COUNT > 60 )); then # 2 minutes timeout
|
||||
echo ""
|
||||
echo "❌ Timeout: Windows VM failed to start within 2 minutes"
|
||||
echo " Check logs: docker logs nnomarchy-windows"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Build the connection info
|
||||
if [[ $KEEP_ALIVE = "true" ]]; then
|
||||
LIFECYCLE="VM will keep running after RDP closes
|
||||
To stop: nnomarchy-windows-vm stop"
|
||||
else
|
||||
LIFECYCLE="VM will auto-stop when RDP closes"
|
||||
fi
|
||||
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align center \
|
||||
"Connecting to Windows VM" \
|
||||
"" \
|
||||
"$LIFECYCLE"
|
||||
|
||||
# Detect display scale from Hyprland
|
||||
HYPR_SCALE=$(hyprctl monitors -j | jq -r '.[] | select (.focused == true) | .scale')
|
||||
SCALE_PERCENT=$(echo "$HYPR_SCALE" | awk '{print int($1 * 100)}')
|
||||
|
||||
RDP_SCALE=""
|
||||
if (( SCALE_PERCENT >= 170 )); then
|
||||
RDP_SCALE="/scale:180"
|
||||
elif (( SCALE_PERCENT >= 130 )); then
|
||||
RDP_SCALE="/scale:140"
|
||||
fi
|
||||
# If scale is less than 130%, don't set any scale (use default 100)
|
||||
|
||||
# Connect with RDP in fullscreen (auto-detects resolution)
|
||||
xfreerdp3 /u:"$WIN_USER" /p:"$WIN_PASS" /v:127.0.0.1:3389 -grab-keyboard /sound /microphone /clipboard /cert:ignore /title:"Windows VM - Nnomarchy" /dynamic-resolution /gfx:AVC444 /floatbar:sticky:off,default:visible,show:fullscreen $RDP_SCALE
|
||||
|
||||
# After RDP closes, stop the container unless --keep-alive was specified
|
||||
if [[ $KEEP_ALIVE = "false" ]]; then
|
||||
echo ""
|
||||
echo "RDP session closed. Stopping Windows VM..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
echo "Windows VM stopped."
|
||||
else
|
||||
echo ""
|
||||
echo "RDP session closed. Windows VM is still running."
|
||||
echo "To stop it: nnomarchy-windows-vm stop"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_windows() {
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Stopping Windows VM..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
echo "Windows VM stopped."
|
||||
}
|
||||
|
||||
status_windows() {
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured."
|
||||
echo "To set up: nnomarchy-windows-vm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nnomarchy-windows 2>/dev/null)
|
||||
|
||||
if [[ -z $CONTAINER_STATUS ]]; then
|
||||
echo "Windows VM container not found."
|
||||
echo "To start: nnomarchy-windows-vm launch"
|
||||
elif [[ $CONTAINER_STATUS = "running" ]]; then
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align left \
|
||||
"Windows VM Status: RUNNING" \
|
||||
"" \
|
||||
"Web interface: http://127.0.0.1:8006" \
|
||||
"RDP available: port 3389" \
|
||||
"" \
|
||||
"To connect: nnomarchy-windows-vm launch" \
|
||||
"To stop: nnomarchy-windows-vm stop"
|
||||
else
|
||||
echo "Windows VM is stopped (status: $CONTAINER_STATUS)"
|
||||
echo "To start: nnomarchy-windows-vm launch"
|
||||
fi
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: nnomarchy-windows-vm [command] [options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " install Install and configure Windows VM"
|
||||
echo " remove Remove Windows VM and optionally its data"
|
||||
echo " launch [options] Start Windows VM (if needed) and connect via RDP"
|
||||
echo " Options:"
|
||||
echo " --keep-alive, -k Keep VM running after RDP closes"
|
||||
echo " stop Stop the running Windows VM"
|
||||
echo " status Show current VM status"
|
||||
echo " help Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " nnomarchy-windows-vm install # Set up Windows VM for first time"
|
||||
echo " nnomarchy-windows-vm launch # Connect to VM (auto-stop on exit)"
|
||||
echo " nnomarchy-windows-vm launch -k # Connect to VM (keep running)"
|
||||
echo " nnomarchy-windows-vm stop # Shut down the VM"
|
||||
}
|
||||
|
||||
# Main command dispatcher
|
||||
case "$1" in
|
||||
install)
|
||||
install_windows
|
||||
;;
|
||||
remove)
|
||||
remove_windows
|
||||
;;
|
||||
launch|start)
|
||||
launch_windows "$2"
|
||||
;;
|
||||
stop|down)
|
||||
stop_windows
|
||||
;;
|
||||
status)
|
||||
status_windows
|
||||
;;
|
||||
help|--help|-h|"")
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $1" >&2
|
||||
echo "" >&2
|
||||
show_usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user