Drop walker + elephant
This commit is contained in:
@@ -61,7 +61,7 @@ Do not define:
|
|||||||
false flags or empty args
|
false flags or empty args
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
# omarchy:summary=Restart Walker and related user services
|
# omarchy:summary=Restart the shell
|
||||||
|
|
||||||
# omarchy:summary=Take a screenshot
|
# omarchy:summary=Take a screenshot
|
||||||
# omarchy:group=capture
|
# omarchy:group=capture
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ if [[ -f $FIRST_RUN_MODE ]]; then
|
|||||||
bash "$OMARCHY_PATH/install/first-run/gnome-theme.sh"
|
bash "$OMARCHY_PATH/install/first-run/gnome-theme.sh"
|
||||||
bash "$OMARCHY_PATH/install/first-run/gdk-scale.sh"
|
bash "$OMARCHY_PATH/install/first-run/gdk-scale.sh"
|
||||||
bash "$OMARCHY_PATH/install/first-run/gtk-primary-paste.sh"
|
bash "$OMARCHY_PATH/install/first-run/gtk-primary-paste.sh"
|
||||||
bash "$OMARCHY_PATH/install/first-run/elephant.sh"
|
|
||||||
omarchy-hook-install post-update "$OMARCHY_PATH/install/first-run/install-voxtype.hook"
|
omarchy-hook-install post-update "$OMARCHY_PATH/install/first-run/install-voxtype.hook"
|
||||||
sudo rm -f /etc/sudoers.d/first-run
|
sudo rm -f /etc/sudoers.d/first-run
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ install_admin_webapp() {
|
|||||||
mkdir -p "$WEBAPP_ICON_DIR"
|
mkdir -p "$WEBAPP_ICON_DIR"
|
||||||
cp "$SUNSHINE_ICON_SOURCE" "$WEBAPP_ICON_DIR/$SUNSHINE_ICON_NAME"
|
cp "$SUNSHINE_ICON_SOURCE" "$WEBAPP_ICON_DIR/$SUNSHINE_ICON_NAME"
|
||||||
omarchy-webapp-install "$SUNSHINE_ADMIN_APP" "$SUNSHINE_ADMIN_URL" "$SUNSHINE_ICON_NAME" "$SUNSHINE_ADMIN_EXEC"
|
omarchy-webapp-install "$SUNSHINE_ADMIN_APP" "$SUNSHINE_ADMIN_URL" "$SUNSHINE_ICON_NAME" "$SUNSHINE_ADMIN_EXEC"
|
||||||
omarchy-restart-walker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_hyprland_autostart() {
|
enable_hyprland_autostart() {
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ if omarchy-toggle-enabled screensaver-off && [[ $1 != "force" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Silently quit Walker on overlay
|
|
||||||
walker -q
|
|
||||||
|
|
||||||
focused=$(omarchy-hyprland-monitor-focused)
|
focused=$(omarchy-hyprland-monitor-focused)
|
||||||
terminal=$(xdg-terminal-exec --print-id)
|
terminal=$(xdg-terminal-exec --print-id)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# omarchy:summary=Launch Walker and ensure its Elephant data provider is running
|
|
||||||
|
|
||||||
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 -- env GSK_RENDERER=cairo walker --gapplication-service &
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec walker --width 644 --maxheight 300 --minheight 300 "$@"
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Pick a file with Walker
|
# omarchy:summary=Pick a file from a menu
|
||||||
# omarchy:group=menu
|
# omarchy:group=menu
|
||||||
# omarchy:name=file
|
# omarchy:name=file
|
||||||
# omarchy:args=label paths formats [walker args...]
|
# omarchy:args=label paths formats [menu args...]
|
||||||
# omarchy:examples=omarchy menu file "Select image" "$HOME/Pictures" "jpg png webp"|omarchy-menu-file "Select media" "$HOME/Pictures:$HOME/Videos" "jpg png mp4 mov" --width 800
|
# omarchy:examples=omarchy menu file "Select image" "$HOME/Pictures" "jpg png webp"|omarchy-menu-file "Select media" "$HOME/Pictures:$HOME/Videos" "jpg png mp4 mov" --width 800
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if (( $# < 3 )); then
|
if (( $# < 3 )); then
|
||||||
echo "Usage: omarchy-menu-file <label> <paths> <formats> [walker args...]" >&2
|
echo "Usage: omarchy-menu-file <label> <paths> <formats> [menu args...]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ IFS=: read -r -a paths <<<"$paths_arg"
|
|||||||
read -r -a formats <<<"$formats_arg"
|
read -r -a formats <<<"$formats_arg"
|
||||||
|
|
||||||
if (( ${#paths[@]} == 0 || ${#formats[@]} == 0 )); then
|
if (( ${#paths[@]} == 0 || ${#formats[@]} == 0 )); then
|
||||||
echo "Usage: omarchy-menu-file <label> <paths> <formats> [walker args...]" >&2
|
echo "Usage: omarchy-menu-file <label> <paths> <formats> [menu args...]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -45,4 +45,4 @@ done
|
|||||||
find_args+=(")" -printf '%T@\t%p\n')
|
find_args+=(")" -printf '%T@\t%p\n')
|
||||||
|
|
||||||
find "${find_args[@]}" 2>/dev/null | sort -rn | cut -f2- |
|
find "${find_args[@]}" 2>/dev/null | sort -rn | cut -f2- |
|
||||||
omarchy-launch-walker --dmenu --width 800 --minheight 1 --maxheight 500 -p "$label…" "$@" 2>/dev/null
|
omarchy-menu-select "$label" -- --width 800 --maxheight 500 "$@"
|
||||||
|
|||||||
+45
-3
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Prompt for text input with Walker
|
# omarchy:summary=Prompt for text input from a menu
|
||||||
# omarchy:group=menu
|
# omarchy:group=menu
|
||||||
# omarchy:name=input
|
# omarchy:name=input
|
||||||
# omarchy:args=prompt [walker args...]
|
# omarchy:args=prompt [menu args...]
|
||||||
# omarchy:examples=omarchy menu input Reminder|omarchy-menu-input "Reminder in minutes" --width 400
|
# omarchy:examples=omarchy menu input Reminder|omarchy-menu-input "Reminder in minutes" --width 400
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -13,4 +13,46 @@ if (( $# > 0 )); then
|
|||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
|
|
||||||
omarchy-launch-walker --dmenu --inputonly --width 295 --minheight 1 --maxheight 1 -p "$prompt…" "$@" 2>/dev/null
|
menu_width=""
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
--width)
|
||||||
|
shift
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
echo "omarchy-menu-input: --width requires a value" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
menu_width="$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
selection_file=$(mktemp)
|
||||||
|
done_file=$(mktemp)
|
||||||
|
rm -f "$done_file"
|
||||||
|
trap 'rm -f "$selection_file" "$done_file"' EXIT
|
||||||
|
|
||||||
|
payload=$(perl -MEncode=decode -MJSON::PP=encode_json -e '
|
||||||
|
my $payload = {
|
||||||
|
mode => "input",
|
||||||
|
prompt => decode("UTF-8", $ARGV[0]),
|
||||||
|
selectionFile => decode("UTF-8", $ARGV[1]),
|
||||||
|
doneFile => decode("UTF-8", $ARGV[2])
|
||||||
|
};
|
||||||
|
$payload->{width} = int($ARGV[3]) if length($ARGV[3] // "");
|
||||||
|
print encode_json($payload)
|
||||||
|
' "$prompt" "$selection_file" "$done_file" "$menu_width")
|
||||||
|
|
||||||
|
omarchy-shell shell summon omarchy.menu "$payload" >/dev/null
|
||||||
|
|
||||||
|
while [[ ! -e $done_file ]]; do
|
||||||
|
sleep 0.05
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -s $selection_file ]]; then
|
||||||
|
cat "$selection_file"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Display Hyprland keybindings defined in your configuration using walker for an interactive search menu.
|
# omarchy:summary=Display Hyprland keybindings defined in your configuration using an interactive search menu.
|
||||||
|
|
||||||
declare -A KEYCODE_SYM_MAP
|
declare -A KEYCODE_SYM_MAP
|
||||||
# Hyprland reports XKB keycodes for code: bindings. Keep a small fallback for
|
# Hyprland reports XKB keycodes for code: bindings. Keep a small fallback for
|
||||||
@@ -600,7 +600,7 @@ else
|
|||||||
monitor_height=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .height')
|
monitor_height=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .height')
|
||||||
menu_height=$((monitor_height * 40 / 100))
|
menu_height=$((monitor_height * 40 / 100))
|
||||||
selection=$(cut -f1 <<<"$records" |
|
selection=$(cut -f1 <<<"$records" |
|
||||||
walker --dmenu -p 'Keybindings' --width 800 --height "$menu_height")
|
omarchy-menu-select 'Keybindings' -- --width 800 --height "$menu_height")
|
||||||
|
|
||||||
if [[ -n $selection ]]; then
|
if [[ -n $selection ]]; then
|
||||||
record=$(awk -F '\t' -v selection="$selection" '$1 == selection { print; exit }' <<<"$records")
|
record=$(awk -F '\t' -v selection="$selection" '$1 == selection { print; exit }' <<<"$records")
|
||||||
|
|||||||
+63
-8
@@ -1,15 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Pick one option with Walker
|
# omarchy:summary=Pick one option from a menu
|
||||||
# omarchy:group=menu
|
# omarchy:group=menu
|
||||||
# omarchy:name=select
|
# omarchy:name=select
|
||||||
# omarchy:args=prompt option... [-- walker args...]
|
# omarchy:args=prompt [option...] [-- menu args...]
|
||||||
# omarchy:examples=omarchy menu select Format jpg png|omarchy-menu-select Resolution 4k 1080p 720p -- --width 400
|
# omarchy:examples=omarchy menu select Format jpg png|omarchy-menu-select Resolution 4k 1080p 720p -- --width 400
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if (( $# < 2 )); then
|
if (( $# < 1 )); then
|
||||||
echo "Usage: omarchy-menu-select <prompt> <option>... [-- walker args...]" >&2
|
echo "Usage: omarchy-menu-select <prompt> [option...] [-- menu args...]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -17,12 +17,34 @@ prompt="$1"
|
|||||||
shift
|
shift
|
||||||
|
|
||||||
options=()
|
options=()
|
||||||
walker_args=()
|
menu_width=""
|
||||||
|
menu_maxheight=""
|
||||||
|
|
||||||
while (( $# > 0 )); do
|
while (( $# > 0 )); do
|
||||||
if [[ $1 == "--" ]]; then
|
if [[ $1 == "--" ]]; then
|
||||||
shift
|
shift
|
||||||
walker_args=("$@")
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
--width)
|
||||||
|
shift
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
echo "omarchy-menu-select: --width requires a value" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
menu_width="$1"
|
||||||
|
;;
|
||||||
|
--height|--maxheight)
|
||||||
|
arg="$1"
|
||||||
|
shift
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
echo "omarchy-menu-select: $arg requires a value" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
menu_maxheight="$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -30,9 +52,42 @@ while (( $# > 0 )); do
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if (( ${#options[@]} == 0 )) && [[ ! -t 0 ]]; then
|
||||||
|
mapfile -t options
|
||||||
|
fi
|
||||||
|
|
||||||
if (( ${#options[@]} == 0 )); then
|
if (( ${#options[@]} == 0 )); then
|
||||||
echo "Usage: omarchy-menu-select <prompt> <option>... [-- walker args...]" >&2
|
echo "Usage: omarchy-menu-select <prompt> [option...] [-- menu args...]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "${options[@]}" | omarchy-launch-walker --dmenu --width 295 --minheight 1 --maxheight 300 -p "$prompt…" "${walker_args[@]}" 2>/dev/null
|
selection_file=$(mktemp)
|
||||||
|
done_file=$(mktemp)
|
||||||
|
rm -f "$done_file"
|
||||||
|
trap 'rm -f "$selection_file" "$done_file"' EXIT
|
||||||
|
|
||||||
|
options_json=$(perl -MEncode=decode -MJSON::PP=encode_json -e 'print encode_json([map { decode("UTF-8", $_) } @ARGV])' "${options[@]}")
|
||||||
|
payload=$(perl -MEncode=decode -MJSON::PP=encode_json,decode_json -e '
|
||||||
|
my $payload = {
|
||||||
|
mode => "select",
|
||||||
|
prompt => decode("UTF-8", $ARGV[0]),
|
||||||
|
options => decode_json($ARGV[1]),
|
||||||
|
selectionFile => decode("UTF-8", $ARGV[2]),
|
||||||
|
doneFile => decode("UTF-8", $ARGV[3])
|
||||||
|
};
|
||||||
|
$payload->{width} = int($ARGV[4]) if length($ARGV[4] // "");
|
||||||
|
$payload->{maxHeight} = int($ARGV[5]) if length($ARGV[5] // "");
|
||||||
|
print encode_json($payload)
|
||||||
|
' "$prompt" "$options_json" "$selection_file" "$done_file" "$menu_width" "$menu_maxheight")
|
||||||
|
|
||||||
|
omarchy-shell shell summon omarchy.menu "$payload" >/dev/null
|
||||||
|
|
||||||
|
while [[ ! -e $done_file ]]; do
|
||||||
|
sleep 0.05
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -s $selection_file ]]; then
|
||||||
|
cat "$selection_file"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Display Tmux keybindings defined in your configuration using walker for an interactive search menu.
|
# omarchy:summary=Display Tmux keybindings defined in your configuration using an interactive search menu.
|
||||||
# omarchy:args=[--print|-p] [--config <path>]
|
# omarchy:args=[--print|-p] [--config <path>]
|
||||||
|
|
||||||
print_only=false
|
print_only=false
|
||||||
@@ -233,11 +233,6 @@ fi
|
|||||||
|
|
||||||
records=$(output_keybindings)
|
records=$(output_keybindings)
|
||||||
|
|
||||||
if [[ -z $WAYLAND_DISPLAY ]] || omarchy-cmd-missing walker; then
|
|
||||||
printf '%s\n' "$records"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
monitor_height=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true) | .height' 2>/dev/null)
|
monitor_height=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true) | .height' 2>/dev/null)
|
||||||
|
|
||||||
if [[ ! $monitor_height =~ ^[0-9]+$ ]] || ((monitor_height <= 0)); then
|
if [[ ! $monitor_height =~ ^[0-9]+$ ]] || ((monitor_height <= 0)); then
|
||||||
@@ -245,4 +240,4 @@ if [[ ! $monitor_height =~ ^[0-9]+$ ]] || ((monitor_height <= 0)); then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
menu_height=$((monitor_height * 40 / 100))
|
menu_height=$((monitor_height * 40 / 100))
|
||||||
printf '%s\n' "$records" | walker --dmenu -p 'Tmux keybindings' --width 800 --height "$menu_height" >/dev/null
|
printf '%s\n' "$records" | omarchy-menu-select 'Tmux keybindings' -- --width 800 --height "$menu_height" >/dev/null || true
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# omarchy:summary=Overwrite the user configs for the Walker application launcher (which also powers the Omarchy Menu) and restart the services.
|
|
||||||
|
|
||||||
mkdir -p ~/.config/autostart/
|
|
||||||
cp $OMARCHY_PATH/default/walker/walker.desktop ~/.config/autostart/
|
|
||||||
|
|
||||||
# And restarts if it crashes or is killed
|
|
||||||
mkdir -p ~/.config/systemd/user/app-walker@autostart.service.d/
|
|
||||||
cp $OMARCHY_PATH/default/walker/restart.conf ~/.config/systemd/user/app-walker@autostart.service.d/restart.conf
|
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
|
|
||||||
# Refresh configs
|
|
||||||
omarchy-refresh-config walker/config.toml
|
|
||||||
omarchy-refresh-config elephant/calc.toml
|
|
||||||
omarchy-refresh-config elephant/desktopapplications.toml
|
|
||||||
|
|
||||||
# Link all elephant menus
|
|
||||||
mkdir -p ~/.config/elephant/menus
|
|
||||||
for menu in $OMARCHY_PATH/default/elephant/*.lua; do
|
|
||||||
ln -snf "$menu" ~/.config/elephant/menus/"$(basename "$menu")"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
omarchy-restart-walker
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# omarchy:summary=Restart Walker and related user services
|
|
||||||
|
|
||||||
restart_services() {
|
|
||||||
if systemctl --user is-enabled elephant.service &>/dev/null; then
|
|
||||||
systemctl --user restart elephant.service
|
|
||||||
fi
|
|
||||||
|
|
||||||
if systemctl --user is-enabled app-walker@autostart.service &>/dev/null; then
|
|
||||||
systemctl --user restart app-walker@autostart.service
|
|
||||||
else
|
|
||||||
echo -e "\e[31mUnable to restart Walker -- RESTART MANUALLY\e[0m"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
if (( EUID == 0 )); then
|
|
||||||
SCRIPT_OWNER=$(stat -c '%U' "$0")
|
|
||||||
USER_UID=$(id -u "$SCRIPT_OWNER")
|
|
||||||
systemd-run --uid="$SCRIPT_OWNER" --setenv=XDG_RUNTIME_DIR="/run/user/$USER_UID" \
|
|
||||||
bash -c "$(declare -f restart_services); restart_services"
|
|
||||||
else
|
|
||||||
restart_services
|
|
||||||
fi
|
|
||||||
+3
-3
@@ -74,9 +74,9 @@ perl -MCwd=abs_path -MEncode=encode,decode -MSocket -MIO::Handle \
|
|||||||
|
|
||||||
my $shell_qml_arg = shift @ARGV;
|
my $shell_qml_arg = shift @ARGV;
|
||||||
my $shell_qml = abs_path($shell_qml_arg);
|
my $shell_qml = abs_path($shell_qml_arg);
|
||||||
my $target = shift @ARGV;
|
my $target = decode("UTF-8", shift @ARGV);
|
||||||
my $function = shift @ARGV;
|
my $function = decode("UTF-8", shift @ARGV);
|
||||||
my @args = @ARGV;
|
my @args = map { decode("UTF-8", $_) } @ARGV;
|
||||||
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
||||||
my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
|
my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
|
||||||
my @candidates;
|
my @candidates;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Set Hyprland and Walker corners to sharp or round (the shell mirrors Hyprland)
|
# omarchy:summary=Set Hyprland corners to sharp or round (the shell mirrors Hyprland)
|
||||||
# omarchy:args=<sharp|round>
|
# omarchy:args=<sharp|round>
|
||||||
# omarchy:examples=omarchy style corners round | omarchy style corners sharp
|
# omarchy:examples=omarchy style corners round | omarchy style corners sharp
|
||||||
|
|
||||||
@@ -10,11 +10,9 @@ if [[ $1 != "sharp" && $1 != "round" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# The shell tracks Hyprland's `decoration:rounding` directly via
|
# The shell tracks Hyprland's `decoration:rounding` directly via
|
||||||
# Quickshell's qs.Commons.Style singleton; no separate quickshell-corners
|
# Quickshell's qs.Commons.Style singleton; no separate shell state file is
|
||||||
# state file is written. The old omarchy-style-corners-quickshell shim is
|
# written.
|
||||||
# kept as a no-op so external callers don't error.
|
|
||||||
omarchy-style-corners-hyprland "$1"
|
omarchy-style-corners-hyprland "$1"
|
||||||
omarchy-style-corners-walker "$1"
|
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
sharp) omarchy-notification-send "Sharp corners enabled" -g ;;
|
sharp) omarchy-notification-send "Sharp corners enabled" -g ;;
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
# omarchy:args=[toggle|sharp|round]
|
# omarchy:args=[toggle|sharp|round]
|
||||||
# omarchy:examples=omarchy style corners quickshell toggle | omarchy style corners quickshell round | omarchy style corners quickshell sharp
|
# omarchy:examples=omarchy style corners quickshell toggle | omarchy style corners quickshell round | omarchy style corners quickshell sharp
|
||||||
|
|
||||||
# qs.Commons.Style reads decoration:rounding from hyprctl and watches the
|
# qs.Commons.Style reads decoration:rounding from hyprctl. Theme changes
|
||||||
# theme name + rounded-corners toggle files for changes. There is no
|
# refresh it through shell IPC, while user corner toggles still trigger their
|
||||||
# separate quickshell-menu.json to write anymore. This shim stays around
|
# own refresh. There is no separate quickshell-menu.json to write anymore.
|
||||||
# so anyone still calling the old subcommand (or the dispatcher in
|
# This shim stays around so anyone still calling the old subcommand (or the
|
||||||
# omarchy-style-corners) doesn't error out.
|
# dispatcher in omarchy-style-corners) doesn't error out.
|
||||||
|
|
||||||
case "${1:-toggle}" in
|
case "${1:-toggle}" in
|
||||||
round|sharp|toggle) exit 0 ;;
|
round|sharp|toggle) exit 0 ;;
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# omarchy:summary=Set or toggle rounded Walker corners
|
|
||||||
# omarchy:args=[toggle|sharp|round]
|
|
||||||
# omarchy:examples=omarchy style corners walker toggle | omarchy style corners walker round | omarchy style corners walker sharp
|
|
||||||
|
|
||||||
TOGGLES_CSS="$HOME/.local/state/omarchy/toggles/walker.css"
|
|
||||||
|
|
||||||
set_radius() {
|
|
||||||
local radius="$1"
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$TOGGLES_CSS")"
|
|
||||||
printf '.box-wrapper { border-radius: %spx; }\n' "$radius" >"$TOGGLES_CSS"
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${1:-toggle}" in
|
|
||||||
round) set_radius 6 ;;
|
|
||||||
sharp) set_radius 0 ;;
|
|
||||||
toggle)
|
|
||||||
if [[ -f $TOGGLES_CSS ]] && grep -q 'border-radius: 6px;' "$TOGGLES_CSS"; then
|
|
||||||
set_radius 0
|
|
||||||
else
|
|
||||||
set_radius 6
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: omarchy-style-corners-walker [toggle|sharp|round]"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
omarchy-restart-walker
|
|
||||||
@@ -122,10 +122,6 @@ fi
|
|||||||
exec 9>"$THEME_SET_LOCK"
|
exec 9>"$THEME_SET_LOCK"
|
||||||
flock 9
|
flock 9
|
||||||
|
|
||||||
# Prevent file-watch reloads from changing shell colors before the background
|
|
||||||
# reveal is ready. The theme IPC below resumes reloads as it applies colors.
|
|
||||||
shell_ipc shell suspendThemeReloads || true
|
|
||||||
|
|
||||||
# Setup clean next theme directory (for atomic theme config swapping)
|
# Setup clean next theme directory (for atomic theme config swapping)
|
||||||
rm -rf "$NEXT_THEME_PATH"
|
rm -rf "$NEXT_THEME_PATH"
|
||||||
mkdir -p "$NEXT_THEME_PATH"
|
mkdir -p "$NEXT_THEME_PATH"
|
||||||
@@ -154,8 +150,8 @@ mv "$NEXT_THEME_PATH" "$CURRENT_THEME_PATH"
|
|||||||
# Store theme name for reference
|
# Store theme name for reference
|
||||||
echo "$THEME_NAME" >"$HOME/.config/omarchy/current/theme.name"
|
echo "$THEME_NAME" >"$HOME/.config/omarchy/current/theme.name"
|
||||||
|
|
||||||
# Make the running shell pick up the new palette immediately instead of waiting
|
# Make the running shell pick up the new palette immediately while the rest of
|
||||||
# for file-watch debounce while the rest of the theme hooks run.
|
# the theme hooks run.
|
||||||
colors_payload=$([[ -f $CURRENT_THEME_PATH/colors.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/colors.toml")
|
colors_payload=$([[ -f $CURRENT_THEME_PATH/colors.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/colors.toml")
|
||||||
shell_payload=$([[ -f $CURRENT_THEME_PATH/shell.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/shell.toml")
|
shell_payload=$([[ -f $CURRENT_THEME_PATH/shell.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/shell.toml")
|
||||||
if [[ $OMARCHY_THEME_SKIP_BACKGROUND == "1" ]]; then
|
if [[ $OMARCHY_THEME_SKIP_BACKGROUND == "1" ]]; then
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ usage() {
|
|||||||
Usage:
|
Usage:
|
||||||
omarchy transcode [--path path] [input] [format] [resolution]
|
omarchy transcode [--path path] [input] [format] [resolution]
|
||||||
|
|
||||||
With no input, pick a picture or video with Walker from ~/Pictures and ~/Videos,
|
With no input, pick a picture or video from ~/Pictures and ~/Videos,
|
||||||
or only from --path when provided. Then pick the output format and resolution.
|
or only from --path when provided. Then pick the output format and resolution.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|||||||
@@ -45,4 +45,6 @@ for APP_NAME in "${APP_NAMES[@]}"; do
|
|||||||
echo "Removed $APP_NAME"
|
echo "Removed $APP_NAME"
|
||||||
done
|
done
|
||||||
|
|
||||||
omarchy-restart-walker
|
if command -v update-desktop-database &>/dev/null; then
|
||||||
|
update-desktop-database "$DESKTOP_DIR" &>/dev/null || true
|
||||||
|
fi
|
||||||
|
|||||||
@@ -32,6 +32,4 @@ if command -v update-desktop-database &>/dev/null; then
|
|||||||
update-desktop-database "$APP_DIR" &>/dev/null || true
|
update-desktop-database "$APP_DIR" &>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
omarchy-restart-walker
|
|
||||||
|
|
||||||
echo "Web apps removed successfully."
|
echo "Web apps removed successfully."
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
async = false
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
show_actions = false
|
|
||||||
only_search_title = true
|
|
||||||
history = false
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
command = 'wl-copy && ((hyprctl dispatch "hl.dsp.send_key_state({ mods = \"SHIFT\", key = \"Insert\", state = \"down\", window = \"activewindow\" })" && sleep 0.05 && hyprctl dispatch "hl.dsp.send_key_state({ mods = \"SHIFT\", key = \"Insert\", state = \"up\", window = \"activewindow\" })") || hyprctl dispatch sendshortcut "SHIFT, Insert, activewindow")'
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
force_keyboard_focus = true # forces keyboard forcus to stay in Walker
|
|
||||||
selection_wrap = true # wrap list if at bottom or top
|
|
||||||
theme = "omarchy-default" # theme to use
|
|
||||||
additional_theme_location = "~/.local/share/omarchy/default/walker/themes/"
|
|
||||||
hide_action_hints = true # globally hide the action hints
|
|
||||||
|
|
||||||
[placeholders]
|
|
||||||
"default" = { input = " Search...", list = "No Results" } # placeholders for input and empty list, key is the providers name, so f.e. "desktopapplications" or "menus:other"
|
|
||||||
|
|
||||||
[keybinds]
|
|
||||||
quick_activate = []
|
|
||||||
|
|
||||||
[columns]
|
|
||||||
symbols = 1 # providers to be queried by default
|
|
||||||
|
|
||||||
[providers]
|
|
||||||
max_results = 256 # 256 should be enough for everyone
|
|
||||||
default = [
|
|
||||||
"desktopapplications",
|
|
||||||
"websearch",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = "/"
|
|
||||||
provider = "providerlist"
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = "."
|
|
||||||
provider = "files"
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = ":"
|
|
||||||
provider = "symbols"
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = "="
|
|
||||||
provider = "calc"
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = "@"
|
|
||||||
provider = "websearch"
|
|
||||||
|
|
||||||
[[providers.prefixes]]
|
|
||||||
prefix = "$"
|
|
||||||
provider = "clipboard"
|
|
||||||
|
|
||||||
[[emergencies]]
|
|
||||||
text = "Restart Walker"
|
|
||||||
command = "omarchy-restart-walker"
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
Name = "omarchyBackgroundSelector"
|
|
||||||
NamePretty = "Omarchy Background Selector"
|
|
||||||
Cache = false
|
|
||||||
HideFromProviderlist = true
|
|
||||||
SearchName = true
|
|
||||||
|
|
||||||
local function ShellEscape(s)
|
|
||||||
return "'" .. s:gsub("'", "'\\''") .. "'"
|
|
||||||
end
|
|
||||||
|
|
||||||
function FormatName(filename)
|
|
||||||
-- Remove leading number and dash
|
|
||||||
local name = filename:gsub("^%d+", ""):gsub("^%-", "")
|
|
||||||
-- Remove extension
|
|
||||||
name = name:gsub("%.[^%.]+$", "")
|
|
||||||
-- Replace dashes with spaces
|
|
||||||
name = name:gsub("-", " ")
|
|
||||||
-- Capitalize each word
|
|
||||||
name = name:gsub("%S+", function(word)
|
|
||||||
return word:sub(1, 1):upper() .. word:sub(2):lower()
|
|
||||||
end)
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
|
|
||||||
function GetEntries()
|
|
||||||
local entries = {}
|
|
||||||
local home = os.getenv("HOME")
|
|
||||||
|
|
||||||
-- Read current theme name
|
|
||||||
local theme_name_file = io.open(home .. "/.config/omarchy/current/theme.name", "r")
|
|
||||||
local theme_name = theme_name_file and theme_name_file:read("*l") or nil
|
|
||||||
if theme_name_file then
|
|
||||||
theme_name_file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Directories to search
|
|
||||||
local dirs = {
|
|
||||||
home .. "/.config/omarchy/current/theme/backgrounds",
|
|
||||||
}
|
|
||||||
if theme_name then
|
|
||||||
table.insert(dirs, home .. "/.config/omarchy/backgrounds/" .. theme_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Track added files to avoid duplicates
|
|
||||||
local seen = {}
|
|
||||||
|
|
||||||
for _, background_dir in ipairs(dirs) do
|
|
||||||
local handle = io.popen(
|
|
||||||
"find -L " .. ShellEscape(background_dir)
|
|
||||||
.. " -maxdepth 1 -type f \\( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' -o -name '*.gif' -o -name '*.bmp' -o -name '*.webp' \\) 2>/dev/null | sort"
|
|
||||||
)
|
|
||||||
if handle then
|
|
||||||
for background in handle:lines() do
|
|
||||||
local filename = background:match("([^/]+)$")
|
|
||||||
if filename and not seen[filename] then
|
|
||||||
seen[filename] = true
|
|
||||||
table.insert(entries, {
|
|
||||||
Text = FormatName(filename),
|
|
||||||
Value = background,
|
|
||||||
Actions = {
|
|
||||||
activate = "omarchy-theme-bg-set " .. ShellEscape(background),
|
|
||||||
},
|
|
||||||
Preview = background,
|
|
||||||
PreviewType = "file",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
handle:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return entries
|
|
||||||
end
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
--
|
|
||||||
-- Dynamic Omarchy Theme Menu for Elephant/Walker
|
|
||||||
--
|
|
||||||
Name = "omarchythemes"
|
|
||||||
NamePretty = "Omarchy Themes"
|
|
||||||
HideFromProviderlist = true
|
|
||||||
|
|
||||||
-- Check if file exists using Lua (no subprocess)
|
|
||||||
local function file_exists(path)
|
|
||||||
local f = io.open(path, "r")
|
|
||||||
if f then
|
|
||||||
f:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get first matching file from directory using ls (single call for fallback)
|
|
||||||
local function first_image_in_dir(dir)
|
|
||||||
local handle = io.popen("ls -1 '" .. dir .. "' 2>/dev/null | head -n 1")
|
|
||||||
if handle then
|
|
||||||
local file = handle:read("*l")
|
|
||||||
handle:close()
|
|
||||||
if file and file ~= "" then
|
|
||||||
return dir .. "/" .. file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Find preview.png, preview.jpg, or first backgrounds/ image in a theme dir
|
|
||||||
local function find_preview_path(dir)
|
|
||||||
local png = dir .. "/preview.png"
|
|
||||||
local jpg = dir .. "/preview.jpg"
|
|
||||||
if file_exists(png) then return png end
|
|
||||||
if file_exists(jpg) then return jpg end
|
|
||||||
return first_image_in_dir(dir .. "/backgrounds")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The main function elephant will call
|
|
||||||
function GetEntries()
|
|
||||||
local entries = {}
|
|
||||||
local user_theme_dir = os.getenv("HOME") .. "/.config/omarchy/themes"
|
|
||||||
local omarchy_path = os.getenv("OMARCHY_PATH") or ""
|
|
||||||
local default_theme_dir = omarchy_path .. "/themes"
|
|
||||||
|
|
||||||
local seen_themes = {}
|
|
||||||
|
|
||||||
-- Helper function to process themes from a directory
|
|
||||||
local function process_themes_from_dir(theme_dir)
|
|
||||||
-- Single find call to get all theme directories
|
|
||||||
local handle = io.popen("find -L '" .. theme_dir .. "' -mindepth 1 -maxdepth 1 -type d 2>/dev/null")
|
|
||||||
if not handle then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for theme_path in handle:lines() do
|
|
||||||
local theme_name = theme_path:match(".*/(.+)$")
|
|
||||||
|
|
||||||
if theme_name and not seen_themes[theme_name] then
|
|
||||||
seen_themes[theme_name] = true
|
|
||||||
|
|
||||||
-- Check the theme dir, then fall back to the default theme dir
|
|
||||||
-- (for partial user customizations that don't ship a preview)
|
|
||||||
local preview_path = find_preview_path(theme_path)
|
|
||||||
or find_preview_path(default_theme_dir .. "/" .. theme_name)
|
|
||||||
|
|
||||||
if preview_path and preview_path ~= "" then
|
|
||||||
local display_name = theme_name:gsub("_", " "):gsub("%-", " ")
|
|
||||||
display_name = display_name:gsub("(%a)([%w_']*)", function(first, rest)
|
|
||||||
return first:upper() .. rest:lower()
|
|
||||||
end)
|
|
||||||
display_name = display_name .. " "
|
|
||||||
|
|
||||||
table.insert(entries, {
|
|
||||||
Text = display_name,
|
|
||||||
Preview = preview_path,
|
|
||||||
PreviewType = "file",
|
|
||||||
Actions = {
|
|
||||||
activate = "omarchy-theme-set " .. theme_name,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
handle:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process user themes first (they take precedence)
|
|
||||||
process_themes_from_dir(user_theme_dir)
|
|
||||||
-- Then process default themes (only if not already seen)
|
|
||||||
process_themes_from_dir(default_theme_dir)
|
|
||||||
|
|
||||||
return entries
|
|
||||||
end
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
--
|
|
||||||
-- Dynamic Omarchy Unlocks Menu for Elephant/Walker
|
|
||||||
--
|
|
||||||
-- A "Default" entry restores the omarchy-shipped Plymouth via
|
|
||||||
-- omarchy-plymouth-reset. After that, every theme that has a preview-unlock.png
|
|
||||||
-- appears as a customised unlock; picking one runs omarchy-plymouth-set-by-theme
|
|
||||||
-- <theme>. Both run in a floating terminal so sudo can prompt.
|
|
||||||
--
|
|
||||||
Name = "omarchyunlocks"
|
|
||||||
NamePretty = "Omarchy Unlocks"
|
|
||||||
HideFromProviderlist = true
|
|
||||||
FixedOrder = true
|
|
||||||
|
|
||||||
local function file_exists(path)
|
|
||||||
local f = io.open(path, "r")
|
|
||||||
if f then
|
|
||||||
f:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function shell_escape(s)
|
|
||||||
return "'" .. s:gsub("'", "'\\''") .. "'"
|
|
||||||
end
|
|
||||||
|
|
||||||
function GetEntries()
|
|
||||||
local entries = {}
|
|
||||||
local home = os.getenv("HOME")
|
|
||||||
local user_themes_dir = home .. "/.config/omarchy/themes"
|
|
||||||
local omarchy_path = os.getenv("OMARCHY_PATH") or ""
|
|
||||||
local default_themes_dir = omarchy_path .. "/themes"
|
|
||||||
local default_preview = omarchy_path .. "/default/plymouth/preview-unlock.png"
|
|
||||||
|
|
||||||
local seen_themes = {}
|
|
||||||
|
|
||||||
local function process_themes_from_dir(themes_dir)
|
|
||||||
local handle = io.popen("find -L '" .. themes_dir .. "' -mindepth 1 -maxdepth 1 -type d 2>/dev/null")
|
|
||||||
if not handle then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for theme_path in handle:lines() do
|
|
||||||
local theme_name = theme_path:match(".*/(.+)$")
|
|
||||||
|
|
||||||
if theme_name and not seen_themes[theme_name] then
|
|
||||||
seen_themes[theme_name] = true
|
|
||||||
|
|
||||||
local preview_path = theme_path .. "/preview-unlock.png"
|
|
||||||
|
|
||||||
if file_exists(preview_path) then
|
|
||||||
local display_name = theme_name:gsub("_", " "):gsub("%-", " ")
|
|
||||||
display_name = display_name:gsub("(%a)([%w_']*)", function(first, rest)
|
|
||||||
return first:upper() .. rest:lower()
|
|
||||||
end)
|
|
||||||
display_name = display_name .. " "
|
|
||||||
|
|
||||||
table.insert(entries, {
|
|
||||||
Text = display_name,
|
|
||||||
Preview = preview_path,
|
|
||||||
PreviewType = "file",
|
|
||||||
Actions = {
|
|
||||||
activate = "omarchy-launch-floating-terminal-with-presentation "
|
|
||||||
.. shell_escape("omarchy-plymouth-set-by-theme " .. shell_escape(theme_name)),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
handle:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
process_themes_from_dir(user_themes_dir)
|
|
||||||
process_themes_from_dir(default_themes_dir)
|
|
||||||
|
|
||||||
-- Default entry last — restores the shipped Plymouth.
|
|
||||||
local default_entry = {
|
|
||||||
Text = "Default ",
|
|
||||||
Actions = {
|
|
||||||
activate = "omarchy-launch-floating-terminal-with-presentation "
|
|
||||||
.. shell_escape("omarchy-plymouth-reset"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if file_exists(default_preview) then
|
|
||||||
default_entry.Preview = default_preview
|
|
||||||
default_entry.PreviewType = "file"
|
|
||||||
end
|
|
||||||
table.insert(entries, default_entry)
|
|
||||||
|
|
||||||
return entries
|
|
||||||
end
|
|
||||||
@@ -6,7 +6,8 @@
|
|||||||
hl.layer_rule({ match = { namespace = "omarchy-bar" }, no_anim = true, animation = "none" })
|
hl.layer_rule({ match = { namespace = "omarchy-bar" }, no_anim = true, animation = "none" })
|
||||||
hl.layer_rule({ match = { namespace = "omarchy-menu" }, no_anim = true, animation = "none" })
|
hl.layer_rule({ match = { namespace = "omarchy-menu" }, no_anim = true, animation = "none" })
|
||||||
|
|
||||||
-- Image selector, emoji picker, and clipboard overlays should also pop without animation.
|
-- App launcher, image selector, emoji picker, and clipboard overlays should also pop without animation.
|
||||||
|
hl.layer_rule({ match = { namespace = "omarchy-app-launcher" }, no_anim = true, animation = "none" })
|
||||||
hl.layer_rule({ match = { namespace = "omarchy-image-selector" }, no_anim = true, animation = "none" })
|
hl.layer_rule({ match = { namespace = "omarchy-image-selector" }, no_anim = true, animation = "none" })
|
||||||
hl.layer_rule({ match = { namespace = "omarchy-emoji-picker" }, no_anim = true, animation = "none" })
|
hl.layer_rule({ match = { namespace = "omarchy-emoji-picker" }, no_anim = true, animation = "none" })
|
||||||
hl.layer_rule({ match = { namespace = "omarchy-clipboard-picker" }, no_anim = true, animation = "none" })
|
hl.layer_rule({ match = { namespace = "omarchy-clipboard-picker" }, no_anim = true, animation = "none" })
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
-- Application-specific animation.
|
|
||||||
hl.layer_rule({ match = { namespace = "walker" }, no_anim = true, animation = "none" })
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
hl.bind("SUPER + SPACE", hl.dsp.exec_cmd("omarchy-launch-walker"), { description = "Launch apps" })
|
hl.bind("SUPER + SPACE", hl.dsp.exec_cmd("omarchy-shell shell toggle omarchy.app-launcher \"{}\""), { description = "Launch apps" })
|
||||||
hl.bind("SUPER + CTRL + E", hl.dsp.exec_cmd("omarchy-shell shell toggle omarchy.emoji-picker \"{}\""), { description = "Emoji picker" })
|
hl.bind("SUPER + CTRL + E", hl.dsp.exec_cmd("omarchy-shell shell toggle omarchy.emoji-picker \"{}\""), { description = "Emoji picker" })
|
||||||
hl.bind("SUPER + CTRL + C", hl.dsp.exec_cmd("omarchy-shell menu toggle capture"), { description = "Capture menu" })
|
hl.bind("SUPER + CTRL + C", hl.dsp.exec_cmd("omarchy-shell menu toggle capture"), { description = "Capture menu" })
|
||||||
hl.bind("SUPER + CTRL + O", hl.dsp.exec_cmd("omarchy-shell menu toggle toggle"), { description = "Toggle menu" })
|
hl.bind("SUPER + CTRL + O", hl.dsp.exec_cmd("omarchy-shell menu toggle toggle"), { description = "Toggle menu" })
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
name: omarchy
|
name: omarchy
|
||||||
description: >
|
description: >
|
||||||
REQUIRED for end-user customization of Linux desktop, window manager, or system config.
|
REQUIRED for end-user customization of Linux desktop, window manager, or system config.
|
||||||
Use when editing ~/.config/hypr/, ~/.config/omarchy/, ~/.config/walker/,
|
Use when editing ~/.config/hypr/, ~/.config/omarchy/,
|
||||||
~/.config/alacritty/, ~/.config/foot/, ~/.config/kitty/, or ~/.config/ghostty/.
|
~/.config/alacritty/, ~/.config/foot/, ~/.config/kitty/, or ~/.config/ghostty/.
|
||||||
Triggers: Hyprland, window rules, animations, keybindings, monitors, gaps, borders,
|
Triggers: Hyprland, window rules, animations, keybindings, monitors, gaps, borders,
|
||||||
blur, opacity, omarchy-shell, bar, walker, terminal config, themes, background,
|
blur, opacity, omarchy-shell, bar, terminal config, themes, background,
|
||||||
night light, idle, lock screen, screenshots, reminders, layer rules, workspace
|
night light, idle, lock screen, screenshots, reminders, layer rules, workspace
|
||||||
settings, display config, and user-facing omarchy commands. Excludes Omarchy
|
settings, display config, and user-facing omarchy commands. Excludes Omarchy
|
||||||
source development in ~/.local/share/omarchy/ and `omarchy dev` workflows.
|
source development in ~/.local/share/omarchy/ and `omarchy dev` workflows.
|
||||||
@@ -23,7 +23,7 @@ It is not for contributing to Omarchy source code.
|
|||||||
**ALWAYS invoke this skill for end-user requests involving ANY of these:**
|
**ALWAYS invoke this skill for end-user requests involving ANY of these:**
|
||||||
|
|
||||||
- Editing ANY file in `~/.config/hypr/` (window rules, animations, keybindings, monitors, etc.)
|
- Editing ANY file in `~/.config/hypr/` (window rules, animations, keybindings, monitors, etc.)
|
||||||
- Editing `~/.config/omarchy/shell.json` (status bar layout, widgets) or `~/.config/walker/`
|
- Editing `~/.config/omarchy/shell.json` (status bar layout, widgets)
|
||||||
- Editing terminal configs (alacritty, foot, kitty, ghostty)
|
- Editing terminal configs (alacritty, foot, kitty, ghostty)
|
||||||
- Editing ANY file in `~/.config/omarchy/`
|
- Editing ANY file in `~/.config/omarchy/`
|
||||||
- Window behavior, animations, opacity, blur, gaps, borders
|
- Window behavior, animations, opacity, blur, gaps, borders
|
||||||
@@ -78,7 +78,7 @@ Omarchy is built on:
|
|||||||
| **Arch Linux** | Base OS | `/etc/`, `~/.config/` |
|
| **Arch Linux** | Base OS | `/etc/`, `~/.config/` |
|
||||||
| **Hyprland** | Wayland compositor/WM | `~/.config/hypr/` |
|
| **Hyprland** | Wayland compositor/WM | `~/.config/hypr/` |
|
||||||
| **Omarchy shell** | Status bar + notifications (Quickshell) | `~/.config/omarchy/shell.json` |
|
| **Omarchy shell** | Status bar + notifications (Quickshell) | `~/.config/omarchy/shell.json` |
|
||||||
| **Walker** | App launcher | `~/.config/walker/` |
|
| **App launcher** | Quickshell app launcher | `~/.config/omarchy/shell.json` |
|
||||||
| **Alacritty/Foot/Kitty/Ghostty** | Terminals | `~/.config/<terminal>/` |
|
| **Alacritty/Foot/Kitty/Ghostty** | Terminals | `~/.config/<terminal>/` |
|
||||||
| **Omarchy OSD** | On-screen display | Quickshell plugin |
|
| **Omarchy OSD** | On-screen display | Quickshell plugin |
|
||||||
|
|
||||||
@@ -182,7 +182,6 @@ changes. For more invasive changes (new plugin, packaged update):
|
|||||||
| lazygit | `~/.config/lazygit/config.yml` |
|
| lazygit | `~/.config/lazygit/config.yml` |
|
||||||
| starship | `~/.config/starship.toml` |
|
| starship | `~/.config/starship.toml` |
|
||||||
| git | `~/.config/git/config` |
|
| git | `~/.config/git/config` |
|
||||||
| walker | `~/.config/walker/config.toml` |
|
|
||||||
|
|
||||||
## Safe Customization Patterns
|
## Safe Customization Patterns
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ cp ~/.config/hypr/bindings.conf ~/.config/hypr/bindings.conf.bak.$(date +%s)
|
|||||||
# 4. Apply changes
|
# 4. Apply changes
|
||||||
# - Hyprland: auto-reloads on save, but MUST validate with `hyprctl reload` and `hyprctl configerrors`
|
# - Hyprland: auto-reloads on save, but MUST validate with `hyprctl reload` and `hyprctl configerrors`
|
||||||
# - Omarchy shell: shell.json hot-reloads; use `omarchy-restart-shell` for plugin/widget code changes
|
# - Omarchy shell: shell.json hot-reloads; use `omarchy-restart-shell` for plugin/widget code changes
|
||||||
# - Walker: MUST restart with `omarchy restart walker`
|
# - App launcher: restart with `omarchy restart shell`
|
||||||
# - Terminals: MUST restart with `omarchy restart terminal`
|
# - Terminals: MUST restart with `omarchy restart terminal`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// checked shell condition; append ✓ when it succeeds
|
// checked shell condition; append ✓ when it succeeds
|
||||||
|
|
||||||
// Root Menu
|
// Root Menu
|
||||||
"apps": {"icon":"","label":"Apps","aliases":["app","applications"],"keywords":"launch launcher","action":"omarchy-launch-walker -p 'Launch…'"},
|
"apps": {"icon":"","label":"Apps","aliases":["app","applications"],"keywords":"launch launcher quickshell","action":"omarchy-shell shell summon omarchy.app-launcher '{}'"},
|
||||||
"learn": {"icon":"","label":"Learn","keywords":"manual docs help reference"},
|
"learn": {"icon":"","label":"Learn","keywords":"manual docs help reference"},
|
||||||
"trigger": {"icon":"","label":"Trigger","keywords":"actions capture share toggle hardware reminder"},
|
"trigger": {"icon":"","label":"Trigger","keywords":"actions capture share toggle hardware reminder"},
|
||||||
"style": {"icon":"","label":"Style","keywords":"theme background font bar quickshell hyprland appearance"},
|
"style": {"icon":"","label":"Style","keywords":"theme background font bar quickshell hyprland appearance"},
|
||||||
@@ -87,7 +87,6 @@
|
|||||||
|
|
||||||
// Style
|
// Style
|
||||||
"style.theme": {"icon":"","label":"Theme","aliases":["theme","themes"],"keywords":"colors","action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
|
"style.theme": {"icon":"","label":"Theme","aliases":["theme","themes"],"keywords":"colors","action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
|
||||||
"style.unlock": {"icon":"","label":"Unlock","keywords":"themes","action":"omarchy-launch-walker -m menus:omarchyunlocks --width 800 --minheight 400"},
|
|
||||||
"style.font": {"icon":"","label":"Font","keywords":"typeface","provider":"fonts"},
|
"style.font": {"icon":"","label":"Font","keywords":"typeface","provider":"fonts"},
|
||||||
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
|
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
|
||||||
"style.bar": {"icon":"","label":"Bar","keywords":"quickshell top bottom left right settings"},
|
"style.bar": {"icon":"","label":"Bar","keywords":"quickshell top bottom left right settings"},
|
||||||
@@ -158,12 +157,11 @@
|
|||||||
"setup.dns.google": {"icon":"","label":"Google","checked":"[[ $(omarchy-dns) == \"Google\" ]]","action":"omarchy-dns Google"},
|
"setup.dns.google": {"icon":"","label":"Google","checked":"[[ $(omarchy-dns) == \"Google\" ]]","action":"omarchy-dns Google"},
|
||||||
"setup.dns.custom": {"icon":"","label":"Custom","checked":"[[ $(omarchy-dns) == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-dns Custom"},
|
"setup.dns.custom": {"icon":"","label":"Custom","checked":"[[ $(omarchy-dns) == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-dns Custom"},
|
||||||
"setup.security": {"icon":"","label":"Security","keywords":"fingerprint fido2"},
|
"setup.security": {"icon":"","label":"Security","keywords":"fingerprint fido2"},
|
||||||
"setup.config": {"icon":"","label":"Config","keywords":"files hyprland bar quickshell walker"},
|
"setup.config": {"icon":"","label":"Config","keywords":"files hyprland bar quickshell"},
|
||||||
"setup.security.fingerprint": {"icon":"","label":"Fingerprint","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint"},
|
"setup.security.fingerprint": {"icon":"","label":"Fingerprint","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint"},
|
||||||
"setup.security.fido2": {"icon":"","label":"Fido2","keywords":"key","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fido2"},
|
"setup.security.fido2": {"icon":"","label":"Fido2","keywords":"key","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fido2"},
|
||||||
"setup.config.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/hyprland.lua\""},
|
"setup.config.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/hyprland.lua\""},
|
||||||
"setup.config.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"night light","action":"omarchy-launch-config-editor ~/.config/hypr/hyprsunset.conf && omarchy-restart-hyprsunset"},
|
"setup.config.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"night light","action":"omarchy-launch-config-editor ~/.config/hypr/hyprsunset.conf && omarchy-restart-hyprsunset"},
|
||||||
"setup.config.walker": {"icon":"","label":"Walker","keywords":"launcher","action":"omarchy-launch-config-editor ~/.config/walker/config.toml && omarchy-restart-walker"},
|
|
||||||
"setup.config.bar": {"icon":"","label":"Bar","keywords":"quickshell shell config settings widgets position","action":"omarchy-launch-bar-settings"},
|
"setup.config.bar": {"icon":"","label":"Bar","keywords":"quickshell shell config settings widgets position","action":"omarchy-launch-bar-settings"},
|
||||||
"setup.config.xcompose": {"icon":"","label":"XCompose","keywords":"compose key","action":"omarchy-launch-config-editor ~/.XCompose && omarchy-restart-xcompose"},
|
"setup.config.xcompose": {"icon":"","label":"XCompose","keywords":"compose key","action":"omarchy-launch-config-editor ~/.XCompose && omarchy-restart-xcompose"},
|
||||||
|
|
||||||
@@ -311,14 +309,12 @@
|
|||||||
"update.channel.dev": {"icon":"🔴","label":"Dev","keywords":"development","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set dev'"},
|
"update.channel.dev": {"icon":"🔴","label":"Dev","keywords":"development","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set dev'"},
|
||||||
"update.process.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"restart","action":"omarchy-restart-hyprsunset"},
|
"update.process.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"restart","action":"omarchy-restart-hyprsunset"},
|
||||||
"update.process.osd": {"icon":"","label":"OSD","keywords":"restart","action":"omarchy-restart-shell"},
|
"update.process.osd": {"icon":"","label":"OSD","keywords":"restart","action":"omarchy-restart-shell"},
|
||||||
"update.process.walker": {"icon":"","label":"Walker","keywords":"restart launcher","action":"omarchy-restart-walker"},
|
|
||||||
"update.process.shell": {"icon":"","label":"Shell","keywords":"restart bar quickshell omarchy-shell","action":"omarchy-restart-shell"},
|
"update.process.shell": {"icon":"","label":"Shell","keywords":"restart bar quickshell omarchy-shell","action":"omarchy-restart-shell"},
|
||||||
"update.config.hyprland": {"icon":"","label":"Hyprland","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-hyprland"},
|
"update.config.hyprland": {"icon":"","label":"Hyprland","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-hyprland"},
|
||||||
"update.config.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-hyprsunset"},
|
"update.config.hyprsunset": {"icon":"","label":"Hyprsunset","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-hyprsunset"},
|
||||||
"update.config.plymouth": {"icon":"","label":"Plymouth","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-plymouth"},
|
"update.config.plymouth": {"icon":"","label":"Plymouth","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-plymouth"},
|
||||||
"update.config.osd": {"icon":"","label":"OSD","keywords":"refresh","action":"omarchy-restart-shell"},
|
"update.config.osd": {"icon":"","label":"OSD","keywords":"refresh","action":"omarchy-restart-shell"},
|
||||||
"update.config.tmux": {"icon":"","label":"Tmux","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-tmux"},
|
"update.config.tmux": {"icon":"","label":"Tmux","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-tmux"},
|
||||||
"update.config.walker": {"icon":"","label":"Walker","keywords":"refresh","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-walker"},
|
|
||||||
"update.config.shell": {"icon":"","label":"Shell","keywords":"refresh bar quickshell","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-shell"},
|
"update.config.shell": {"icon":"","label":"Shell","keywords":"refresh bar quickshell","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-shell"},
|
||||||
"update.hardware.audio": {"icon":"","label":"Audio","keywords":"restart pipewire","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-pipewire"},
|
"update.hardware.audio": {"icon":"","label":"Audio","keywords":"restart pipewire","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-pipewire"},
|
||||||
"update.hardware.wifi": {"icon":"","label":"Wi-Fi","keywords":"restart wireless","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-wifi"},
|
"update.hardware.wifi": {"icon":"","label":"Wi-Fi","keywords":"restart wireless","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-wifi"},
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
@define-color selected-text {{ accent }};
|
|
||||||
@define-color text {{ foreground }};
|
|
||||||
@define-color base {{ background }};
|
|
||||||
@define-color border {{ foreground }};
|
|
||||||
@define-color foreground {{ foreground }};
|
|
||||||
@define-color background {{ background }};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
RestartSec=2
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk" version="4.0"></requires>
|
|
||||||
<object class="GtkWindow" id="Window">
|
|
||||||
<style>
|
|
||||||
<class name="window"></class>
|
|
||||||
</style>
|
|
||||||
<property name="resizable">true</property>
|
|
||||||
<property name="title">Walker</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="BoxWrapper">
|
|
||||||
<style>
|
|
||||||
<class name="box-wrapper"></class>
|
|
||||||
</style>
|
|
||||||
<property name="width-request">644</property>
|
|
||||||
<property name="overflow">hidden</property>
|
|
||||||
<property name="orientation">horizontal</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="Box">
|
|
||||||
<style>
|
|
||||||
<class name="box"></class>
|
|
||||||
</style>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="hexpand-set">true</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="SearchContainer">
|
|
||||||
<style>
|
|
||||||
<class name="search-container"></class>
|
|
||||||
</style>
|
|
||||||
<property name="overflow">hidden</property>
|
|
||||||
<property name="orientation">horizontal</property>
|
|
||||||
<property name="halign">fill</property>
|
|
||||||
<property name="hexpand-set">true</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="Input">
|
|
||||||
<style>
|
|
||||||
<class name="input"></class>
|
|
||||||
</style>
|
|
||||||
<property name="halign">fill</property>
|
|
||||||
<property name="hexpand-set">true</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="ContentContainer">
|
|
||||||
<style>
|
|
||||||
<class name="content-container"></class>
|
|
||||||
</style>
|
|
||||||
<property name="orientation">horizontal</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<property name="vexpand">true</property>
|
|
||||||
<property name="vexpand-set">true</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="ElephantHint">
|
|
||||||
<style>
|
|
||||||
<class name="elephant-hint"></class>
|
|
||||||
</style>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<property name="height-request">100</property>
|
|
||||||
<property name="label">Waiting for elephant...</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="Placeholder">
|
|
||||||
<style>
|
|
||||||
<class name="placeholder"></class>
|
|
||||||
</style>
|
|
||||||
<property name="label">No Results</property>
|
|
||||||
<property name="yalign">0.0</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="Scroll">
|
|
||||||
<style>
|
|
||||||
<class name="scroll"></class>
|
|
||||||
</style>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<property name="can_focus">false</property>
|
|
||||||
<property name="overlay-scrolling">true</property>
|
|
||||||
<property name="max-content-width">600</property>
|
|
||||||
<property name="max-content-height">300</property>
|
|
||||||
<property name="min-content-height">0</property>
|
|
||||||
<property name="propagate-natural-height">true</property>
|
|
||||||
<property name="propagate-natural-width">true</property>
|
|
||||||
<property name="hscrollbar-policy">automatic</property>
|
|
||||||
<property name="vscrollbar-policy">automatic</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkGridView" id="List">
|
|
||||||
<style>
|
|
||||||
<class name="list"></class>
|
|
||||||
</style>
|
|
||||||
<property name="max_columns">1</property>
|
|
||||||
<property name="can_focus">false</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="Preview">
|
|
||||||
<style>
|
|
||||||
<class name="preview"></class>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="Keybinds">
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<property name="margin-top">10</property>
|
|
||||||
<style>
|
|
||||||
<class name="keybinds"></class>
|
|
||||||
</style>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="GlobalKeybinds">
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<style>
|
|
||||||
<class name="global-keybinds"></class>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="ItemKeybinds">
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<style>
|
|
||||||
<class name="item-keybinds"></class>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="Error">
|
|
||||||
<style>
|
|
||||||
<class name="error"></class>
|
|
||||||
</style>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="visible">false</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
@import "../../../../../../../.config/omarchy/current/theme/walker.css";
|
|
||||||
@import "../../../../../../../.local/state/omarchy/toggles/walker.css";
|
|
||||||
|
|
||||||
* {
|
|
||||||
all: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 18px;
|
|
||||||
color: @text;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.normal-icons {
|
|
||||||
-gtk-icon-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.large-icons {
|
|
||||||
-gtk-icon-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-wrapper {
|
|
||||||
background: alpha(@base, 0.95);
|
|
||||||
padding: 20px;
|
|
||||||
border: 2px solid @border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box {
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
background: @base;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input placeholder {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus,
|
|
||||||
.input:active {
|
|
||||||
box-shadow: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll {
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
}
|
|
||||||
|
|
||||||
child,
|
|
||||||
child > * {
|
|
||||||
}
|
|
||||||
|
|
||||||
child:hover .item-box {
|
|
||||||
}
|
|
||||||
|
|
||||||
child:selected .item-box {
|
|
||||||
}
|
|
||||||
|
|
||||||
child:selected .item-box * {
|
|
||||||
color: @selected-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
child:selected {
|
|
||||||
background: alpha(@text, 0.07);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-box {
|
|
||||||
padding-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-text-box {
|
|
||||||
all: unset;
|
|
||||||
padding: 14px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-subtext {
|
|
||||||
font-size: 0px;
|
|
||||||
min-height: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-image {
|
|
||||||
margin-right: 14px;
|
|
||||||
-gtk-icon-transform: scale(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.current {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keybind-hints {
|
|
||||||
background: @background;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=Walker
|
|
||||||
Comment=Walker Service
|
|
||||||
Exec=walker --gapplication-service
|
|
||||||
StartupNotify=false
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
@@ -20,7 +20,6 @@ run_logged $OMARCHY_INSTALL/config/user-dirs.sh
|
|||||||
run_logged $OMARCHY_INSTALL/config/toggles.sh
|
run_logged $OMARCHY_INSTALL/config/toggles.sh
|
||||||
run_logged $OMARCHY_INSTALL/config/nautilus-python.sh
|
run_logged $OMARCHY_INSTALL/config/nautilus-python.sh
|
||||||
run_logged $OMARCHY_INSTALL/config/localdb.sh
|
run_logged $OMARCHY_INSTALL/config/localdb.sh
|
||||||
run_logged $OMARCHY_INSTALL/config/walker-elephant.sh
|
|
||||||
run_logged $OMARCHY_INSTALL/config/fast-shutdown.sh
|
run_logged $OMARCHY_INSTALL/config/fast-shutdown.sh
|
||||||
run_logged $OMARCHY_INSTALL/config/unmount-fuse.sh
|
run_logged $OMARCHY_INSTALL/config/unmount-fuse.sh
|
||||||
run_logged $OMARCHY_INSTALL/config/sudoless-asdcontrol.sh
|
run_logged $OMARCHY_INSTALL/config/sudoless-asdcontrol.sh
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
# Make sure toggles are available
|
# Make sure toggles are available
|
||||||
mkdir -p ~/.local/state/omarchy/toggles
|
mkdir -p ~/.local/state/omarchy/toggles
|
||||||
touch ~/.local/state/omarchy/toggles/walker.css
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Ensure Walker service is started automatically on boot
|
|
||||||
mkdir -p ~/.config/autostart/
|
|
||||||
cp $OMARCHY_PATH/default/walker/walker.desktop ~/.config/autostart/
|
|
||||||
|
|
||||||
# And is restarted if it crashes or is killed
|
|
||||||
mkdir -p ~/.config/systemd/user/app-walker@autostart.service.d/
|
|
||||||
cp $OMARCHY_PATH/default/walker/restart.conf ~/.config/systemd/user/app-walker@autostart.service.d/restart.conf
|
|
||||||
|
|
||||||
# Create pacman hook to restart walker after updates
|
|
||||||
sudo mkdir -p /etc/pacman.d/hooks
|
|
||||||
sudo tee /etc/pacman.d/hooks/walker-restart.hook > /dev/null << EOF
|
|
||||||
[Trigger]
|
|
||||||
Type = Package
|
|
||||||
Operation = Upgrade
|
|
||||||
Target = walker
|
|
||||||
Target = walker-debug
|
|
||||||
Target = elephant*
|
|
||||||
|
|
||||||
[Action]
|
|
||||||
Description = Restarting Walker services after system update
|
|
||||||
When = PostTransaction
|
|
||||||
Exec = $OMARCHY_PATH/bin/omarchy-restart-walker
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Link the visual theme menu config
|
|
||||||
mkdir -p ~/.config/elephant/menus
|
|
||||||
ln -snf $OMARCHY_PATH/default/elephant/omarchy_themes.lua ~/.config/elephant/menus/omarchy_themes.lua
|
|
||||||
ln -snf $OMARCHY_PATH/default/elephant/omarchy_background_selector.lua ~/.config/elephant/menus/omarchy_background_selector.lua
|
|
||||||
ln -snf $OMARCHY_PATH/default/elephant/omarchy_unlocks.lua ~/.config/elephant/menus/omarchy_unlocks.lua
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
elephant service enable
|
|
||||||
systemctl --user start elephant.service
|
|
||||||
@@ -39,6 +39,7 @@ ffmpegthumbnailer
|
|||||||
fontconfig
|
fontconfig
|
||||||
foot
|
foot
|
||||||
fzf
|
fzf
|
||||||
|
git
|
||||||
github-cli
|
github-cli
|
||||||
gnome-calculator
|
gnome-calculator
|
||||||
gnome-keyring
|
gnome-keyring
|
||||||
@@ -88,7 +89,6 @@ nvim
|
|||||||
obs-studio
|
obs-studio
|
||||||
obsidian
|
obsidian
|
||||||
omarchy-nvim
|
omarchy-nvim
|
||||||
omarchy-walker
|
|
||||||
pamixer
|
pamixer
|
||||||
pinta
|
pinta
|
||||||
playerctl
|
playerctl
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ broadcom-wl
|
|||||||
btrfs-progs
|
btrfs-progs
|
||||||
dkms
|
dkms
|
||||||
egl-wayland
|
egl-wayland
|
||||||
git
|
|
||||||
gst-plugin-pipewire
|
gst-plugin-pipewire
|
||||||
gtk4-layer-shell
|
gtk4-layer-shell
|
||||||
libpulse
|
libpulse
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
echo "Replace wofi with walker as the default launcher"
|
|
||||||
|
|
||||||
if omarchy-cmd-missing walker; then
|
|
||||||
omarchy-pkg-add walker-bin libqalculate
|
|
||||||
|
|
||||||
omarchy-pkg-drop wofi
|
|
||||||
rm -rf ~/.config/wofi
|
|
||||||
|
|
||||||
mkdir -p ~/.config/walker
|
|
||||||
cp -r ~/.local/share/omarchy/config/walker/* ~/.config/walker/
|
|
||||||
fi
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
echo "Migrate to Walker 2.0.0"
|
|
||||||
|
|
||||||
NEEDS_MIGRATION=false
|
|
||||||
|
|
||||||
PACKAGES=(
|
|
||||||
"elephant"
|
|
||||||
"elephant-calc"
|
|
||||||
"elephant-clipboard"
|
|
||||||
"elephant-bluetooth"
|
|
||||||
"elephant-desktopapplications"
|
|
||||||
"elephant-files"
|
|
||||||
"elephant-menus"
|
|
||||||
"elephant-providerlist"
|
|
||||||
"elephant-runner"
|
|
||||||
"elephant-symbols"
|
|
||||||
"elephant-unicode"
|
|
||||||
"elephant-websearch"
|
|
||||||
"elephant-todo"
|
|
||||||
"walker"
|
|
||||||
"libqalculate"
|
|
||||||
)
|
|
||||||
|
|
||||||
for pkg in "${PACKAGES[@]}"; do
|
|
||||||
if ! omarchy-pkg-present "$pkg"; then
|
|
||||||
NEEDS_MIGRATION=true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
WALKER_MAJOR=$(walker -v 2>&1 | grep -oP '^\d+' || echo "0")
|
|
||||||
if (( WALKER_MAJOR < 2 )); then
|
|
||||||
NEEDS_MIGRATION=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure basic config is present
|
|
||||||
mkdir -p ~/.config/walker
|
|
||||||
cp -r ~/.local/share/omarchy/config/walker/* ~/.config/walker/
|
|
||||||
|
|
||||||
if $NEEDS_MIGRATION; then
|
|
||||||
kill -9 $(pgrep -x walker) 2>/dev/null || true
|
|
||||||
|
|
||||||
omarchy-pkg-drop walker-bin walker-bin-debug
|
|
||||||
|
|
||||||
omarchy-pkg-add "${PACKAGES[@]}"
|
|
||||||
|
|
||||||
source $OMARCHY_PATH/install/config/walker-elephant.sh
|
|
||||||
|
|
||||||
rm -rf ~/.config/walker/themes
|
|
||||||
|
|
||||||
omarchy-refresh-config walker/config.toml
|
|
||||||
omarchy-refresh-config elephant/calc.toml
|
|
||||||
omarchy-refresh-config elephant/desktopapplications.toml
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo # Assure final success
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
echo "Install omarchy-walker meta package"
|
|
||||||
omarchy-pkg-add omarchy-walker
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
echo "Switch Elephant to run as a systemd service and walker to be autostarted on login"
|
|
||||||
|
|
||||||
pkill elephant
|
|
||||||
elephant service enable
|
|
||||||
systemctl --user start elephant.service
|
|
||||||
|
|
||||||
pkill walker
|
|
||||||
mkdir -p ~/.config/autostart/
|
|
||||||
cp $OMARCHY_PATH/default/walker/walker.desktop ~/.config/autostart/
|
|
||||||
setsid walker --gapplication-service &
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
echo "Ensure emoji menu (Super + Ctrl + E) uses single column design"
|
|
||||||
|
|
||||||
omarchy-refresh-walker
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
echo "Add emergency entry for Walker"
|
|
||||||
CONFIG_FILE="$HOME/.config/walker/config.toml"
|
|
||||||
|
|
||||||
if [[ -f $CONFIG_FILE ]] && ! grep -q 'command = "omarchy-restart-walker"' "$CONFIG_FILE"; then
|
|
||||||
cat >> "$CONFIG_FILE" << 'EOF'
|
|
||||||
|
|
||||||
[[emergencies]]
|
|
||||||
text = "Restart Walker"
|
|
||||||
command = "omarchy-restart-walker"
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
echo "Enable auto-pasting for the emoji picker"
|
|
||||||
|
|
||||||
omarchy-refresh-config elephant/symbols.toml
|
|
||||||
omarchy-restart-walker
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
echo "Ensure walker service is restarted if it's killed or crashes"
|
|
||||||
|
|
||||||
mkdir -p ~/.config/systemd/user/app-walker@autostart.service.d/
|
|
||||||
cp $OMARCHY_PATH/default/walker/restart.conf ~/.config/systemd/user/app-walker@autostart.service.d/restart.conf
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
echo "Use interactive background selector menu"
|
|
||||||
|
|
||||||
mkdir -p ~/.config/elephant/menus
|
|
||||||
ln -snf $OMARCHY_PATH/default/elephant/omarchy_background_selector.lua ~/.config/elephant/menus/omarchy_background_selector.lua
|
|
||||||
omarchy-restart-walker
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
echo "Use interactive unlock (Plymouth) selector menu"
|
|
||||||
|
|
||||||
mkdir -p ~/.config/elephant/menus
|
|
||||||
ln -snf $OMARCHY_PATH/default/elephant/omarchy_unlocks.lua ~/.config/elephant/menus/omarchy_unlocks.lua
|
|
||||||
omarchy-restart-walker
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
echo "Create Walker toggle config"
|
|
||||||
|
|
||||||
mkdir -p ~/.local/state/omarchy/toggles
|
|
||||||
[[ -f ~/.local/state/omarchy/toggles/walker.css ]] || touch ~/.local/state/omarchy/toggles/walker.css
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
echo "Migrate emoji picker shortcut to use Quickshell emoji picker"
|
|
||||||
|
|
||||||
if [[ -f ~/.config/hypr/bindings/utilities.lua ]]; then
|
|
||||||
sed -i 's/omarchy-launch-walker -m symbols/omarchy-shell-ipc shell toggle omarchy.emoji-picker "{}"/' ~/.config/hypr/bindings/utilities.lua
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f ~/.config/hypr/bindings.lua ]]; then
|
|
||||||
sed -i 's/omarchy-launch-walker -m symbols/omarchy-shell-ipc shell toggle omarchy.emoji-picker "{}"/' ~/.config/hypr/bindings.lua
|
|
||||||
fi
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
echo "Migrate clipboard manager shortcut to use Quickshell clipboard picker"
|
|
||||||
|
|
||||||
if [[ -f ~/.config/hypr/bindings/clipboard.lua ]]; then
|
|
||||||
sed -i 's/walker -m clipboard/omarchy-shell-ipc shell toggle omarchy.clipboard-picker "{}"/' ~/.config/hypr/bindings/clipboard.lua
|
|
||||||
fi
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
echo "Remove Walker app-launcher services and Elephant providers"
|
||||||
|
|
||||||
|
pkill elephant 2>/dev/null || true
|
||||||
|
systemctl --user disable --now elephant.service 2>/dev/null || true
|
||||||
|
systemctl --user disable --now app-walker@autostart.service 2>/dev/null || true
|
||||||
|
rm -f ~/.config/autostart/walker.desktop
|
||||||
|
rm -rf ~/.config/elephant
|
||||||
|
rm -rf ~/.config/systemd/user/app-walker@autostart.service.d
|
||||||
|
sudo rm -f /etc/pacman.d/hooks/walker-restart.hook
|
||||||
|
|
||||||
|
for bindings_file in ~/.config/hypr/bindings/utilities.lua ~/.config/hypr/bindings.lua; do
|
||||||
|
if [[ -f $bindings_file ]]; then
|
||||||
|
sed -i 's#omarchy-launch-walker#omarchy-shell shell toggle omarchy.app-launcher \\"{}\\"#g' "$bindings_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
omarchy-pkg-drop \
|
||||||
|
omarchy-walker \
|
||||||
|
elephant-all \
|
||||||
|
elephant \
|
||||||
|
elephant-calc \
|
||||||
|
elephant-clipboard \
|
||||||
|
elephant-bluetooth \
|
||||||
|
elephant-desktopapplications \
|
||||||
|
elephant-files \
|
||||||
|
elephant-menus \
|
||||||
|
elephant-providerlist \
|
||||||
|
elephant-runner \
|
||||||
|
elephant-symbols \
|
||||||
|
elephant-unicode \
|
||||||
|
elephant-websearch \
|
||||||
|
elephant-todo
|
||||||
@@ -29,6 +29,7 @@ shell/
|
|||||||
plugins/
|
plugins/
|
||||||
bar/ first-party plugins (see plugins/README.md)
|
bar/ first-party plugins (see plugins/README.md)
|
||||||
settings/
|
settings/
|
||||||
|
app-launcher/
|
||||||
image-picker/
|
image-picker/
|
||||||
menu/
|
menu/
|
||||||
notifications/
|
notifications/
|
||||||
|
|||||||
+10
-1
@@ -13,6 +13,7 @@ User-installed plugins live alongside these conceptually but on disk under
|
|||||||
|---------------|---------------------------|-----------|---------------------------------------|
|
|---------------|---------------------------|-----------|---------------------------------------|
|
||||||
| Bar | `omarchy.bar` | `bar` | `bar/Bar.qml` |
|
| Bar | `omarchy.bar` | `bar` | `bar/Bar.qml` |
|
||||||
| Bar settings | `omarchy.settings` | `panel` | `settings/SettingsPanel.qml` |
|
| Bar settings | `omarchy.settings` | `panel` | `settings/SettingsPanel.qml` |
|
||||||
|
| App launcher | `omarchy.app-launcher` | `overlay` | `app-launcher/AppLauncher.qml` |
|
||||||
| Image picker | `omarchy.image-picker` | `overlay` | `image-picker/ImagePicker.qml` |
|
| Image picker | `omarchy.image-picker` | `overlay` | `image-picker/ImagePicker.qml` |
|
||||||
| Emoji picker | `omarchy.emoji-picker` | `overlay` | `emoji-picker/EmojiPicker.qml` |
|
| Emoji picker | `omarchy.emoji-picker` | `overlay` | `emoji-picker/EmojiPicker.qml` |
|
||||||
| Clipboard mgr | `omarchy.clipboard-picker`| `overlay` | `clipboard-picker/ClipboardPicker.qml`|
|
| Clipboard mgr | `omarchy.clipboard-picker`| `overlay` | `clipboard-picker/ClipboardPicker.qml`|
|
||||||
@@ -43,6 +44,14 @@ Visual editor for the bar layout. Summoned by
|
|||||||
- dynamic per-widget settings forms that write inline back to the
|
- dynamic per-widget settings forms that write inline back to the
|
||||||
corresponding shell.json entry
|
corresponding shell.json entry
|
||||||
|
|
||||||
|
## App launcher
|
||||||
|
|
||||||
|
Quickshell-powered app launcher. It uses Quickshell's native
|
||||||
|
`DesktopEntries` model for discovery/activation and renders inside the
|
||||||
|
long-running shell with the legacy launcher card dimensions, colors, row
|
||||||
|
spacing, icon sizing, and keyboard behavior. Summoned directly over shell IPC
|
||||||
|
by the `SUPER + SPACE` binding and the Omarchy menu Apps row.
|
||||||
|
|
||||||
## Image picker
|
## Image picker
|
||||||
|
|
||||||
Fullscreen image-grid selector overlay. Used by `omarchy-menu-images`
|
Fullscreen image-grid selector overlay. Used by `omarchy-menu-images`
|
||||||
@@ -87,7 +96,7 @@ runs inside the long-lived `omarchy-shell` process, replacing the old
|
|||||||
|
|
||||||
## Omarchy menu
|
## Omarchy menu
|
||||||
|
|
||||||
Quickshell-powered replacement for the legacy Walker-driven `omarchy-menu`.
|
Quickshell-powered Omarchy command menu.
|
||||||
The menu UI lives in `menu/Menu.qml` as a first-party `menu` plugin and is
|
The menu UI lives in `menu/Menu.qml` as a first-party `menu` plugin and is
|
||||||
summoned through the shell (`omarchy-shell shell summon omarchy.menu ...`),
|
summoned through the shell (`omarchy-shell shell summon omarchy.menu ...`),
|
||||||
so it shares the long-running `omarchy-shell` process instead of starting a
|
so it shares the long-running `omarchy-shell` process instead of starting a
|
||||||
|
|||||||
@@ -0,0 +1,434 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import qs.Commons
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string omarchyPath: Quickshell.env("OMARCHY_PATH")
|
||||||
|
property var shell: null
|
||||||
|
property var manifest: null
|
||||||
|
|
||||||
|
property bool opened: false
|
||||||
|
property string placeholder: "\uf002 Search..."
|
||||||
|
property string filterText: ""
|
||||||
|
property int selectedIndex: 0
|
||||||
|
property bool cursorActive: true
|
||||||
|
property bool hoverArmed: false
|
||||||
|
property var filteredEntries: []
|
||||||
|
|
||||||
|
property color accent: Color.menu.selected
|
||||||
|
property color background: Color.menu.background
|
||||||
|
property color foreground: Color.menu.text
|
||||||
|
property color border: foreground
|
||||||
|
property string fontFamily: Quickshell.env("OMARCHY_MENU_FONT") || "monospace"
|
||||||
|
|
||||||
|
property int cardWidth: 644
|
||||||
|
property int cardHeight: 400
|
||||||
|
property int contentMargin: 20
|
||||||
|
property int contentSpacing: 10
|
||||||
|
property int searchHeight: 44
|
||||||
|
property int rowHeight: 50
|
||||||
|
property int iconSlotWidth: 44
|
||||||
|
property int iconSize: 24
|
||||||
|
readonly property int listHeight: cardHeight - contentMargin * 2 - searchHeight - contentSpacing
|
||||||
|
|
||||||
|
function open(payloadJson) {
|
||||||
|
var payload = ({})
|
||||||
|
try { payload = JSON.parse(payloadJson || "{}") } catch (e) { payload = ({}) }
|
||||||
|
|
||||||
|
root.placeholder = payload.placeholder || "\uf002 Search..."
|
||||||
|
root.cardWidth = Math.max(300, Number(payload.width || 644))
|
||||||
|
var requestedListHeight = Number(payload.listHeight || payload.maxHeight || 0)
|
||||||
|
root.cardHeight = requestedListHeight > 0
|
||||||
|
? root.contentMargin * 2 + root.searchHeight + root.contentSpacing + requestedListHeight
|
||||||
|
: 400
|
||||||
|
|
||||||
|
root.opened = true
|
||||||
|
root.filterText = payload.query || ""
|
||||||
|
root.selectedIndex = 0
|
||||||
|
root.cursorActive = true
|
||||||
|
root.hoverArmed = false
|
||||||
|
root.rebuildDisplay()
|
||||||
|
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
root.opened = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
root.opened = false
|
||||||
|
if (root.shell && typeof root.shell.hide === "function")
|
||||||
|
root.shell.hide((root.manifest && root.manifest.id) || "omarchy.app-launcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
function withAlpha(color, alpha) {
|
||||||
|
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileUrl(path) {
|
||||||
|
return "file://" + String(path).split("/").map(encodeURIComponent).join("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconSource(icon) {
|
||||||
|
var value = String(icon || "")
|
||||||
|
if (value.length === 0) return Quickshell.iconPath("application-x-executable", true)
|
||||||
|
if (value.indexOf("file://") === 0 || value.indexOf("image://") === 0) return value
|
||||||
|
if (value.charAt(0) === "/") return root.fileUrl(value)
|
||||||
|
return Quickshell.iconPath(value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryName(entry) {
|
||||||
|
return String((entry && entry.name) || (entry && entry.id) || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function entrySubtext(entry) {
|
||||||
|
return String((entry && entry.genericName) || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function entrySortKey(entry) {
|
||||||
|
return String((entry && entry.id) || root.entryName(entry)).toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function entrySearchText(entry) {
|
||||||
|
if (!entry) return ""
|
||||||
|
var keywords = ""
|
||||||
|
try { keywords = (entry.keywords || []).join(" ") } catch (e) { keywords = "" }
|
||||||
|
return [entry.name, entry.genericName, entry.comment, keywords, entry.id].join(" ").toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHiddenEntry(entry) {
|
||||||
|
var id = String((entry && entry.id) || "")
|
||||||
|
return id === "avahi-discover" || id === "bssh" || id === "bvnc"
|
||||||
|
}
|
||||||
|
|
||||||
|
function fuzzyScore(entry, query) {
|
||||||
|
var q = String(query || "").trim().toLowerCase()
|
||||||
|
if (!q) return 0
|
||||||
|
|
||||||
|
var name = root.entryName(entry).toLowerCase()
|
||||||
|
var id = String((entry && entry.id) || "").toLowerCase()
|
||||||
|
var haystack = root.entrySearchText(entry)
|
||||||
|
var directName = name.indexOf(q)
|
||||||
|
var directId = id.indexOf(q)
|
||||||
|
if (directName === 0) return 10000 - name.length
|
||||||
|
if (directId === 0) return 9500 - id.length
|
||||||
|
if (directName > 0) return 8000 - directName * 10 - name.length
|
||||||
|
if (directId > 0) return 7600 - directId * 10 - id.length
|
||||||
|
|
||||||
|
var hayIndex = haystack.indexOf(q)
|
||||||
|
if (hayIndex >= 0) return 6000 - hayIndex
|
||||||
|
|
||||||
|
var pos = 0
|
||||||
|
var first = -1
|
||||||
|
var last = -1
|
||||||
|
for (var i = 0; i < haystack.length && pos < q.length; i++) {
|
||||||
|
if (haystack.charAt(i) !== q.charAt(pos)) continue
|
||||||
|
if (first < 0) first = i
|
||||||
|
last = i
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
if (pos !== q.length) return -1
|
||||||
|
return 3000 - (last - first) - haystack.length * 0.01
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortedEntries(query) {
|
||||||
|
var values = DesktopEntries.applications.values || []
|
||||||
|
var q = String(query || "").trim()
|
||||||
|
var rows = []
|
||||||
|
|
||||||
|
for (var i = 0; i < values.length; i++) {
|
||||||
|
var entry = values[i]
|
||||||
|
if (!entry || entry.noDisplay || root.isHiddenEntry(entry)) continue
|
||||||
|
var name = root.entryName(entry)
|
||||||
|
if (!name) continue
|
||||||
|
var score = root.fuzzyScore(entry, q)
|
||||||
|
if (score < 0) continue
|
||||||
|
rows.push({ entry: entry, score: score, key: root.entrySortKey(entry), name: name.toLowerCase() })
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.sort(function(a, b) {
|
||||||
|
if (q && a.score !== b.score) return b.score - a.score
|
||||||
|
if (a.key < b.key) return -1
|
||||||
|
if (a.key > b.key) return 1
|
||||||
|
if (a.name < b.name) return -1
|
||||||
|
if (a.name > b.name) return 1
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildDisplay() {
|
||||||
|
displayModel.clear()
|
||||||
|
var rows = root.sortedEntries(root.filterText)
|
||||||
|
var entries = []
|
||||||
|
var count = Math.min(rows.length, 256)
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var entry = rows[i].entry
|
||||||
|
entries.push(entry)
|
||||||
|
displayModel.append({
|
||||||
|
name: root.entryName(entry),
|
||||||
|
subtext: root.entrySubtext(entry),
|
||||||
|
icon: String(entry.icon || "")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
root.filteredEntries = entries
|
||||||
|
|
||||||
|
if (displayModel.count === 0) root.selectedIndex = 0
|
||||||
|
else if (root.selectedIndex >= displayModel.count) root.selectedIndex = displayModel.count - 1
|
||||||
|
else if (root.selectedIndex < 0) root.selectedIndex = 0
|
||||||
|
|
||||||
|
Qt.callLater(function() {
|
||||||
|
if (displayModel.count > 0) resultList.positionViewAtIndex(root.selectedIndex, ListView.Contain)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFilter(nextFilter) {
|
||||||
|
root.filterText = nextFilter
|
||||||
|
root.selectedIndex = 0
|
||||||
|
root.cursorActive = true
|
||||||
|
root.rebuildDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(delta) {
|
||||||
|
if (displayModel.count === 0) return
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = (root.selectedIndex + delta + displayModel.count) % displayModel.count
|
||||||
|
resultList.positionViewAtIndex(root.selectedIndex, ListView.Contain)
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateIndex(index) {
|
||||||
|
if (index < 0 || index >= root.filteredEntries.length) return
|
||||||
|
var entry = root.filteredEntries[index]
|
||||||
|
if (!entry) return
|
||||||
|
root.dismiss()
|
||||||
|
entry.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel { id: displayModel }
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: DesktopEntries.applications
|
||||||
|
function onValuesChanged() {
|
||||||
|
if (root.opened) root.rebuildDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: panel
|
||||||
|
visible: root.opened
|
||||||
|
anchors { top: true; bottom: true; left: true; right: true }
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.namespace: "omarchy-app-launcher"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: root.withAlpha(root.background, 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: card
|
||||||
|
width: Math.min(root.cardWidth, panel.width - Style.gapsOut * 2)
|
||||||
|
height: Math.min(root.cardHeight, panel.height - Style.gapsOut * 2)
|
||||||
|
radius: Style.cornerRadius
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: root.withAlpha(root.background, 0.95)
|
||||||
|
border.color: root.border
|
||||||
|
border.width: 2
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyCatcher
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.priority: Keys.BeforeItem
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
if (root.filterText.length > 0) root.setFilter("")
|
||||||
|
else root.dismiss()
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Backspace) {
|
||||||
|
if (root.filterText.length > 0) root.setFilter(root.filterText.slice(0, -1))
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Up) {
|
||||||
|
root.select(-1)
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Down) {
|
||||||
|
root.select(1)
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
|
root.select(-6)
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_PageDown) {
|
||||||
|
root.select(6)
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Home) {
|
||||||
|
if (displayModel.count > 0) {
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = 0
|
||||||
|
resultList.positionViewAtIndex(root.selectedIndex, ListView.Contain)
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_End) {
|
||||||
|
if (displayModel.count > 0) {
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = displayModel.count - 1
|
||||||
|
resultList.positionViewAtIndex(root.selectedIndex, ListView.Contain)
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
|
root.activateIndex(root.selectedIndex)
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127 && (event.modifiers === Qt.NoModifier || event.modifiers === Qt.ShiftModifier)) {
|
||||||
|
root.setFilter(root.filterText + event.text)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: root.contentMargin
|
||||||
|
spacing: root.contentSpacing
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: root.searchHeight
|
||||||
|
radius: 0
|
||||||
|
color: root.background
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.filterText || root.placeholder
|
||||||
|
color: root.foreground
|
||||||
|
opacity: root.filterText ? 1 : 0.5
|
||||||
|
font.family: root.fontFamily
|
||||||
|
font.pixelSize: 18
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - root.searchHeight - root.contentSpacing
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: resultList
|
||||||
|
anchors.fill: parent
|
||||||
|
model: displayModel
|
||||||
|
clip: true
|
||||||
|
spacing: 0
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: row
|
||||||
|
required property int index
|
||||||
|
required property string name
|
||||||
|
required property string subtext
|
||||||
|
required property string icon
|
||||||
|
|
||||||
|
readonly property bool hasCursor: root.cursorActive && row.index === root.selectedIndex
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
height: root.rowHeight
|
||||||
|
radius: 0
|
||||||
|
color: row.hasCursor ? root.withAlpha(root.foreground, 0.07) : "transparent"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: iconSlot
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 14
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: root.iconSlotWidth
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: appIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitSize: root.iconSize
|
||||||
|
width: root.iconSize
|
||||||
|
height: root.iconSize
|
||||||
|
source: root.iconSource(row.icon)
|
||||||
|
asynchronous: true
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: appIcon.status === Image.Error
|
||||||
|
text: "?"
|
||||||
|
color: row.hasCursor ? root.accent : root.foreground
|
||||||
|
font.family: root.fontFamily
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.left: iconSlot.right
|
||||||
|
anchors.leftMargin: 14
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 14
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: row.name
|
||||||
|
color: row.hasCursor ? root.accent : root.foreground
|
||||||
|
font.family: root.fontFamily
|
||||||
|
font.pixelSize: 18
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPositionChanged: function(mouse) {
|
||||||
|
root.hoverArmed = true
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = row.index
|
||||||
|
}
|
||||||
|
onContainsMouseChanged: if (containsMouse && root.hoverArmed) {
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = row.index
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.cursorActive = true
|
||||||
|
root.selectedIndex = row.index
|
||||||
|
root.activateIndex(row.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 14
|
||||||
|
visible: displayModel.count === 0
|
||||||
|
text: "No Results"
|
||||||
|
color: root.foreground
|
||||||
|
font.family: root.fontFamily
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "omarchy.app-launcher",
|
||||||
|
"name": "App launcher",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Omarchy",
|
||||||
|
"description": "A Quickshell-powered application launcher",
|
||||||
|
"kinds": [
|
||||||
|
"overlay"
|
||||||
|
],
|
||||||
|
"keepLoaded": true,
|
||||||
|
"entryPoints": {
|
||||||
|
"overlay": "AppLauncher.qml"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,15 @@ import qs.Commons
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property string omarchyPath: Quickshell.env("OMARCHY_PATH")
|
||||||
property bool opened: false
|
property bool opened: false
|
||||||
property string filterText: ""
|
property string filterText: ""
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property bool cursorActive: false
|
property bool cursorActive: false
|
||||||
property var items: []
|
property var history: []
|
||||||
|
|
||||||
|
property string historyPath: Quickshell.env("HOME") + "/.local/state/omarchy/clipboard-history.json"
|
||||||
|
property string captureScript: root.omarchyPath + "/shell/scripts/clipboard-capture.sh"
|
||||||
property color accent: Color.menu.selected
|
property color accent: Color.menu.selected
|
||||||
property color background: Color.menu.background
|
property color background: Color.menu.background
|
||||||
property color foreground: Color.menu.text
|
property color foreground: Color.menu.text
|
||||||
@@ -31,12 +34,7 @@ Item {
|
|||||||
root.filterText = ""
|
root.filterText = ""
|
||||||
root.selectedIndex = 0
|
root.selectedIndex = 0
|
||||||
root.cursorActive = false
|
root.cursorActive = false
|
||||||
|
root.rebuildDisplay()
|
||||||
// Trigger fetch
|
|
||||||
fetchProc.collected = ""
|
|
||||||
fetchProc.command = ["bash", "-lc", "elephant query --json 'clipboard;;100'"]
|
|
||||||
fetchProc.running = true
|
|
||||||
|
|
||||||
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,36 +51,112 @@ Item {
|
|||||||
return Qt.rgba(color.r, color.g, color.b, alpha)
|
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shellQuote(value) {
|
||||||
|
return "'" + String(value || "").replace(/'/g, "'\\''") + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileUrl(path) {
|
||||||
|
return "file://" + String(path).split("/").map(encodeURIComponent).join("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEntry(value) {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return value.length > 0 ? { type: "text", text: value } : null
|
||||||
|
}
|
||||||
|
if (!value || typeof value !== "object") return null
|
||||||
|
|
||||||
|
var type = String(value.type || value.kind || "")
|
||||||
|
if (type === "text") {
|
||||||
|
var text = String(value.text || "")
|
||||||
|
return text.length > 0 ? { type: "text", text: text } : null
|
||||||
|
}
|
||||||
|
if (type === "image") {
|
||||||
|
var path = String(value.path || "")
|
||||||
|
if (!path) return null
|
||||||
|
return {
|
||||||
|
type: "image",
|
||||||
|
path: path,
|
||||||
|
mime: String(value.mime || "image/png")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryKey(entry) {
|
||||||
|
if (!entry) return ""
|
||||||
|
if (entry.type === "image") return "image:" + String(entry.path || "")
|
||||||
|
return "text:" + String(entry.text || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadHistory(raw) {
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(String(raw || "[]"))
|
||||||
|
var next = []
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
for (var i = 0; i < parsed.length; i++) {
|
||||||
|
var entry = root.normalizeEntry(parsed[i])
|
||||||
|
if (entry) next.push(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.history = next
|
||||||
|
} catch (e) {
|
||||||
|
root.history = []
|
||||||
|
}
|
||||||
|
if (root.opened) root.rebuildDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveHistory() {
|
||||||
|
historyFile.setText(JSON.stringify(root.history.slice(0, 100), null, 2) + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
function addClipboardEntry(entry) {
|
||||||
|
var normalized = root.normalizeEntry(entry)
|
||||||
|
if (!normalized) return
|
||||||
|
|
||||||
|
var key = root.entryKey(normalized)
|
||||||
|
var next = [normalized]
|
||||||
|
for (var i = 0; i < root.history.length && next.length < 100; i++) {
|
||||||
|
var existing = root.normalizeEntry(root.history[i])
|
||||||
|
if (!existing || root.entryKey(existing) === key) continue
|
||||||
|
next.push(existing)
|
||||||
|
}
|
||||||
|
root.history = next
|
||||||
|
root.saveHistory()
|
||||||
|
if (root.opened) root.rebuildDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addClipboardJson(line) {
|
||||||
|
var raw = String(line || "").trim()
|
||||||
|
if (!raw) return
|
||||||
|
try { root.addClipboardEntry(JSON.parse(raw)) } catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
function rebuildDisplay() {
|
function rebuildDisplay() {
|
||||||
var query = root.filterText.trim().toLowerCase()
|
var query = root.filterText.trim().toLowerCase()
|
||||||
|
|
||||||
displayModel.clear()
|
displayModel.clear()
|
||||||
var outCount = 0
|
var outCount = 0
|
||||||
|
|
||||||
for (var i = 0; i < root.items.length; i++) {
|
for (var i = 0; i < root.history.length; i++) {
|
||||||
var entry = root.items[i]
|
var entry = root.normalizeEntry(root.history[i])
|
||||||
var isPassword = (entry.meta === "password" || entry.preview_type === "password")
|
if (!entry) continue
|
||||||
|
|
||||||
var textMatch = false
|
var isImage = entry.type === "image"
|
||||||
if (isPassword) {
|
var searchable = isImage ? ("image " + String(entry.mime || "")) : String(entry.text || "")
|
||||||
textMatch = false // Passwords shouldn't match plain text search queries
|
if (query && searchable.toLowerCase().indexOf(query) < 0) continue
|
||||||
} else {
|
|
||||||
textMatch = (entry.preview && entry.preview.toLowerCase().indexOf(query) >= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!query || textMatch) {
|
|
||||||
displayModel.append({
|
displayModel.append({
|
||||||
identifier: entry.identifier,
|
entryType: entry.type,
|
||||||
previewText: entry.preview_type === "text" ? entry.preview.replace(/\n/g, " ") : "",
|
fullText: isImage ? "" : String(entry.text || ""),
|
||||||
previewImage: entry.preview_type === "file" ? ("file://" + entry.preview) : "",
|
previewText: isImage ? "Image" : String(entry.text || "").replace(/\s+/g, " "),
|
||||||
previewType: entry.preview_type || "text",
|
previewImage: isImage ? root.fileUrl(entry.path) : "",
|
||||||
isPassword: isPassword,
|
path: isImage ? String(entry.path || "") : "",
|
||||||
|
mime: isImage ? String(entry.mime || "image/png") : "text/plain",
|
||||||
index: outCount
|
index: outCount
|
||||||
})
|
})
|
||||||
outCount++
|
outCount++
|
||||||
if (outCount >= 50) break
|
if (outCount >= 50) break
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (displayModel.count === 0) selectedIndex = 0
|
if (displayModel.count === 0) selectedIndex = 0
|
||||||
else if (selectedIndex >= displayModel.count) selectedIndex = displayModel.count - 1
|
else if (selectedIndex >= displayModel.count) selectedIndex = displayModel.count - 1
|
||||||
@@ -114,40 +188,60 @@ Item {
|
|||||||
function activateIndex(index) {
|
function activateIndex(index) {
|
||||||
if (index < 0 || index >= displayModel.count) return
|
if (index < 0 || index >= displayModel.count) return
|
||||||
var row = displayModel.get(index)
|
var row = displayModel.get(index)
|
||||||
root.applySelected(row.identifier)
|
root.applySelected(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySelected(identifier) {
|
function applySelected(row) {
|
||||||
if (!identifier) return
|
if (!row) return
|
||||||
root.opened = false
|
root.opened = false
|
||||||
var escId = identifier.replace(/'/g, "'\\''")
|
if (row.entryType === "image") {
|
||||||
Quickshell.execDetached(["bash", "-lc", "elephant activate 'clipboard;" + escId + ";copy;;'; sleep 0.15; wtype -M shift -k Insert -m shift 2>/dev/null || true"])
|
Quickshell.execDetached(["bash", "-lc", "wl-copy --type " + root.shellQuote(row.mime) + " < " + root.shellQuote(row.path) + "; sleep 0.15; wtype -M shift -k Insert -m shift 2>/dev/null || true"])
|
||||||
|
} else if (row.fullText) {
|
||||||
|
Quickshell.execDetached(["bash", "-lc", "printf %s " + root.shellQuote(row.fullText) + " | wl-copy; sleep 0.15; wtype -M shift -k Insert -m shift 2>/dev/null || true"])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: initProc.running = true
|
||||||
|
|
||||||
ListModel { id: displayModel }
|
ListModel { id: displayModel }
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: historyFile
|
||||||
|
path: root.historyPath
|
||||||
|
watchChanges: true
|
||||||
|
atomicWrites: true
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: root.loadHistory(text())
|
||||||
|
onLoadFailed: root.loadHistory("[]")
|
||||||
|
onFileChanged: reload()
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: fetchProc
|
id: initProc
|
||||||
property string collected: ""
|
command: ["bash", "-lc", "mkdir -p ~/.local/state/omarchy"]
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: function(data) { fetchProc.collected += data + "\n" }
|
|
||||||
}
|
|
||||||
onExited: {
|
onExited: {
|
||||||
var lines = fetchProc.collected.split("\n")
|
currentProc.command = [root.captureScript]
|
||||||
var newItems = []
|
currentProc.running = true
|
||||||
for (var i = 0; i < lines.length; i++) {
|
watchProc.command = ["wl-paste", "--watch", root.captureScript]
|
||||||
var line = lines[i].trim()
|
watchProc.running = true
|
||||||
if (!line) continue
|
|
||||||
try {
|
|
||||||
var parsed = JSON.parse(line)
|
|
||||||
if (parsed && parsed.item) {
|
|
||||||
newItems.push(parsed.item)
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
root.items = newItems
|
|
||||||
root.rebuildDisplay()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: currentProc
|
||||||
|
stdout: StdioCollector {
|
||||||
|
waitForEnd: true
|
||||||
|
onStreamFinished: root.addClipboardJson(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: watchProc
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: function(data) { root.addClipboardJson(data) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: panel
|
id: panel
|
||||||
visible: root.opened
|
visible: root.opened
|
||||||
@@ -261,10 +355,10 @@ Item {
|
|||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property int index
|
required property int index
|
||||||
required property string identifier
|
required property string entryType
|
||||||
required property string previewText
|
required property string previewText
|
||||||
required property string previewType
|
required property string fullText
|
||||||
required property bool isPassword
|
required property string previewImage
|
||||||
|
|
||||||
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
|
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
|
||||||
|
|
||||||
@@ -275,33 +369,33 @@ Item {
|
|||||||
border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||||
border.width: hasCursor ? Style.hoverBorderWidth : 0
|
border.width: hasCursor ? Style.hoverBorderWidth : 0
|
||||||
|
|
||||||
Rectangle {
|
Row {
|
||||||
visible: false
|
|
||||||
width: Style.space(4)
|
|
||||||
height: parent.height - Style.space(18)
|
|
||||||
radius: Math.min(root.cornerRadius, Style.space(4))
|
|
||||||
color: root.accent
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Style.space(8)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: Style.space(12)
|
anchors.leftMargin: Style.space(12)
|
||||||
anchors.rightMargin: Style.space(12)
|
anchors.rightMargin: Style.space(12)
|
||||||
anchors.topMargin: Style.space(8)
|
anchors.topMargin: Style.space(8)
|
||||||
anchors.bottomMargin: Style.space(8)
|
anchors.bottomMargin: Style.space(8)
|
||||||
|
spacing: Style.space(10)
|
||||||
|
|
||||||
|
Image {
|
||||||
|
visible: parent.parent.entryType === "image"
|
||||||
|
width: visible ? parent.height : 0
|
||||||
|
height: parent.height
|
||||||
|
source: parent.parent.previewImage
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
asynchronous: true
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width - (parent.parent.entryType === "image" ? parent.height + parent.spacing : 0)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
text: parent.parent.isPassword ? "••••••••" : (parent.parent.previewType === "text" ? parent.parent.previewText : "Image")
|
text: parent.parent.previewText
|
||||||
color: parent.parent.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
color: parent.parent.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.title
|
font.pixelSize: Style.font.title
|
||||||
font.italic: parent.parent.previewType === "file" || parent.parent.isPassword
|
font.italic: parent.parent.entryType === "image"
|
||||||
opacity: (parent.parent.previewType === "file" || parent.parent.isPassword) ? 0.6 : 1.0
|
opacity: parent.parent.entryType === "image" ? 0.72 : 1.0
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
@@ -309,7 +403,6 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
@@ -335,13 +428,13 @@ Item {
|
|||||||
border.width: Style.normalBorderWidth
|
border.width: Style.normalBorderWidth
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
property var activeRow: root.cursorActive && displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null
|
property var activeRow: displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: parent.activeRow && parent.activeRow.previewType === "text"
|
visible: parent.activeRow && parent.activeRow.entryType === "text"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Style.space(16)
|
anchors.margins: Style.space(16)
|
||||||
text: parent.activeRow ? (parent.activeRow.isPassword ? "••••••••" : parent.activeRow.previewText) : ""
|
text: parent.activeRow ? parent.activeRow.fullText : ""
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.title
|
font.pixelSize: Style.font.title
|
||||||
@@ -351,11 +444,13 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
visible: parent.activeRow && parent.activeRow.previewType === "file"
|
visible: parent.activeRow && parent.activeRow.entryType === "image"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Style.space(16)
|
anchors.margins: Style.space(16)
|
||||||
source: parent.activeRow ? parent.activeRow.previewImage : ""
|
source: parent.activeRow ? parent.activeRow.previewImage : ""
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
asynchronous: true
|
||||||
|
smooth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +471,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: root.items.length === 0 ? "Clipboard is empty" : "No matches for “" + root.filterText + "”"
|
text: root.history.length === 0 ? "Clipboard is empty" : "No matches for “" + root.filterText + "”"
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
|
|||||||
+160
-8
@@ -18,11 +18,15 @@ Item {
|
|||||||
var payload = ({})
|
var payload = ({})
|
||||||
try { payload = JSON.parse(payloadJson || "{}") } catch (e) { payload = ({}) }
|
try { payload = JSON.parse(payloadJson || "{}") } catch (e) { payload = ({}) }
|
||||||
|
|
||||||
root.pendingInitialMenu = payload.initialMenu || payload.menu || "root"
|
|
||||||
if (payload.fontFamily) root.fontFamily = payload.fontFamily
|
if (payload.fontFamily) root.fontFamily = payload.fontFamily
|
||||||
|
|
||||||
|
if (payload.mode === "select" || payload.mode === "input") {
|
||||||
|
root.openDmenu(payload)
|
||||||
|
} else {
|
||||||
|
root.pendingInitialMenu = payload.initialMenu || payload.menu || "root"
|
||||||
root.openExistingMenu(root.pendingInitialMenu)
|
root.openExistingMenu(root.pendingInitialMenu)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
root.cancel()
|
root.cancel()
|
||||||
@@ -37,6 +41,15 @@ Item {
|
|||||||
property var defaultMenuItems: []
|
property var defaultMenuItems: []
|
||||||
property var userMenuItems: []
|
property var userMenuItems: []
|
||||||
property bool opened: false
|
property bool opened: false
|
||||||
|
property string mode: "menu"
|
||||||
|
readonly property bool dmenuActive: mode === "select" || mode === "input"
|
||||||
|
property string dmenuPrompt: ""
|
||||||
|
property var dmenuOptions: []
|
||||||
|
property string selectionFile: ""
|
||||||
|
property string doneFile: ""
|
||||||
|
property int dmenuWidth: 300
|
||||||
|
property int dmenuMaxHeight: 0
|
||||||
|
property bool requestActive: false
|
||||||
property bool rowsLoaded: false
|
property bool rowsLoaded: false
|
||||||
property string activeMenu: "root"
|
property string activeMenu: "root"
|
||||||
property string filterText: ""
|
property string filterText: ""
|
||||||
@@ -64,14 +77,40 @@ Item {
|
|||||||
property int dividerHeight: Style.space(17)
|
property int dividerHeight: Style.space(17)
|
||||||
property bool searchDivider: false
|
property bool searchDivider: false
|
||||||
property int layoutSerial: 0
|
property int layoutSerial: 0
|
||||||
property int cardWidth: Math.min((root.activeMenu === "trigger.capture.screenrecord" || root.activeMenu === "style.font") ? Style.space(520) : Style.space(300), panel.width - Style.gapsOut * 2)
|
property int cardWidth: Math.min(root.dmenuActive ? Style.space(root.dmenuWidth) : ((root.activeMenu === "trigger.capture.screenrecord" || root.activeMenu === "style.font") ? Style.space(520) : Style.space(300)), panel.width - Style.gapsOut * 2)
|
||||||
property int visibleRowsHeight: rowListHeight(layoutSerial, displayModel.count, filterText, searchDivider)
|
property int visibleRowsHeight: root.dmenuActive ? dmenuRowListHeight(layoutSerial, displayModel.count, filterText) : rowListHeight(layoutSerial, displayModel.count, filterText, searchDivider)
|
||||||
property int cardHeight: Math.min(Math.max(Style.space(220), contentMargin * 2 + headerHeight + contentSpacing + visibleRowsHeight), panel.height - Style.gapsOut * 2)
|
property int cardHeight: root.dmenuActive
|
||||||
|
? Math.min(contentMargin * 2 + headerHeight + (mode === "input" ? 0 : contentSpacing + visibleRowsHeight), panel.height - Style.gapsOut * 2)
|
||||||
|
: Math.min(Math.max(Style.space(220), contentMargin * 2 + headerHeight + contentSpacing + visibleRowsHeight), panel.height - Style.gapsOut * 2)
|
||||||
|
|
||||||
function withAlpha(color, alpha) {
|
function withAlpha(color, alpha) {
|
||||||
return Qt.rgba(color.r, color.g, color.b, alpha)
|
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shellQuote(value) {
|
||||||
|
return "'" + String(value || "").replace(/'/g, "'\\''") + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishRequest(selection) {
|
||||||
|
if (!root.requestActive || !root.doneFile) {
|
||||||
|
root.opened = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeSelectionFile = root.selectionFile
|
||||||
|
var activeDoneFile = root.doneFile
|
||||||
|
root.requestActive = false
|
||||||
|
root.selectionFile = ""
|
||||||
|
root.doneFile = ""
|
||||||
|
|
||||||
|
if (selection === null || selection === undefined) {
|
||||||
|
resultProc.command = ["bash", "-lc", ": > " + root.shellQuote(activeDoneFile)]
|
||||||
|
} else {
|
||||||
|
resultProc.command = ["bash", "-lc", "printf '%s\\n' " + root.shellQuote(selection) + " > " + root.shellQuote(activeSelectionFile) + "; : > " + root.shellQuote(activeDoneFile)]
|
||||||
|
}
|
||||||
|
resultProc.running = true
|
||||||
|
}
|
||||||
|
|
||||||
function rowHeightForDetail(detail) {
|
function rowHeightForDetail(detail) {
|
||||||
return root.filterText && detail ? root.detailRowHeight : root.baseRowHeight
|
return root.filterText && detail ? root.detailRowHeight : root.baseRowHeight
|
||||||
}
|
}
|
||||||
@@ -94,6 +133,20 @@ Item {
|
|||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dmenuRowListHeight(_serial, _count, _filter) {
|
||||||
|
if (root.mode === "input") return 0
|
||||||
|
if (displayModel.count === 0) return root.baseRowHeight
|
||||||
|
|
||||||
|
var count = Math.min(displayModel.count, 10)
|
||||||
|
var total = 0
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
if (i > 0) total += root.rowSpacing
|
||||||
|
total += root.baseRowHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.dmenuMaxHeight > 0 ? Math.min(total, Style.space(root.dmenuMaxHeight)) : total
|
||||||
|
}
|
||||||
|
|
||||||
function item(id) {
|
function item(id) {
|
||||||
return root.items[id] || null
|
return root.items[id] || null
|
||||||
}
|
}
|
||||||
@@ -458,7 +511,52 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rebuildDmenuDisplay() {
|
||||||
|
displayModel.clear()
|
||||||
|
root.searchDivider = false
|
||||||
|
|
||||||
|
if (root.mode === "input") {
|
||||||
|
layoutSerial += 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = root.filterText.trim().toLowerCase()
|
||||||
|
for (var i = 0; i < root.dmenuOptions.length; i++) {
|
||||||
|
var label = String(root.dmenuOptions[i] || "")
|
||||||
|
if (query && label.toLowerCase().indexOf(query) < 0) continue
|
||||||
|
displayModel.append({
|
||||||
|
itemId: "dmenu." + i,
|
||||||
|
kind: "dmenu",
|
||||||
|
icon: "",
|
||||||
|
label: label,
|
||||||
|
target: "",
|
||||||
|
detail: "",
|
||||||
|
path: "",
|
||||||
|
childCount: 0,
|
||||||
|
action: "",
|
||||||
|
provider: "",
|
||||||
|
score: i,
|
||||||
|
section: ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutSerial += 1
|
||||||
|
|
||||||
|
if (displayModel.count === 0) selectedIndex = 0
|
||||||
|
else if (selectedIndex >= displayModel.count) selectedIndex = displayModel.count - 1
|
||||||
|
else if (selectedIndex < 0) selectedIndex = 0
|
||||||
|
|
||||||
|
Qt.callLater(function() {
|
||||||
|
if (displayModel.count > 0) resultList.positionViewAtIndex(root.selectedIndex, ListView.Contain)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function rebuildDisplay() {
|
function rebuildDisplay() {
|
||||||
|
if (root.dmenuActive) {
|
||||||
|
root.rebuildDmenuDisplay()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
displayModel.clear()
|
displayModel.clear()
|
||||||
|
|
||||||
if (!root.rowsLoaded) return
|
if (!root.rowsLoaded) return
|
||||||
@@ -540,7 +638,7 @@ Item {
|
|||||||
root.filterText = nextFilter
|
root.filterText = nextFilter
|
||||||
root.selectedIndex = 0
|
root.selectedIndex = 0
|
||||||
root.cursorActive = false
|
root.cursorActive = false
|
||||||
if (root.filterText.trim()) root.loadProvidersForSearch()
|
if (!root.dmenuActive && root.filterText.trim()) root.loadProvidersForSearch()
|
||||||
root.rebuildDisplay()
|
root.rebuildDisplay()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,6 +669,16 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function activateIndex(index) {
|
function activateIndex(index) {
|
||||||
|
if (root.dmenuActive) {
|
||||||
|
if (root.mode === "input") {
|
||||||
|
root.applyDmenuSelection(root.filterText)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (index < 0 || index >= displayModel.count) return
|
||||||
|
root.applyDmenuSelection(displayModel.get(index).label)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (index < 0 || index >= displayModel.count) return
|
if (index < 0 || index >= displayModel.count) return
|
||||||
|
|
||||||
var row = displayModel.get(index)
|
var row = displayModel.get(index)
|
||||||
@@ -583,6 +691,13 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyDmenuSelection(value) {
|
||||||
|
applySerial = requestSerial
|
||||||
|
opened = false
|
||||||
|
filterText = ""
|
||||||
|
root.finishRequest(value)
|
||||||
|
}
|
||||||
|
|
||||||
function applySelected(id, action) {
|
function applySelected(id, action) {
|
||||||
if (!id) { cancel(); return }
|
if (!id) { cancel(); return }
|
||||||
applySerial = requestSerial
|
applySerial = requestSerial
|
||||||
@@ -592,12 +707,17 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
if (root.dmenuActive) root.finishRequest(null)
|
||||||
opened = false
|
opened = false
|
||||||
filterText = ""
|
filterText = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function openExistingMenu(initialMenu) {
|
function openExistingMenu(initialMenu) {
|
||||||
requestSerial += 1
|
requestSerial += 1
|
||||||
|
mode = "menu"
|
||||||
|
requestActive = false
|
||||||
|
selectionFile = ""
|
||||||
|
doneFile = ""
|
||||||
activeMenu = root.item(initialMenu) ? initialMenu : "root"
|
activeMenu = root.item(initialMenu) ? initialMenu : "root"
|
||||||
navStack = []
|
navStack = []
|
||||||
filterText = ""
|
filterText = ""
|
||||||
@@ -610,6 +730,27 @@ Item {
|
|||||||
|
|
||||||
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openDmenu(payload) {
|
||||||
|
requestSerial += 1
|
||||||
|
mode = payload.mode === "input" ? "input" : "select"
|
||||||
|
dmenuPrompt = String(payload.prompt || (mode === "input" ? "Input" : "Select"))
|
||||||
|
dmenuOptions = Array.isArray(payload.options) ? payload.options : []
|
||||||
|
selectionFile = String(payload.selectionFile || "")
|
||||||
|
doneFile = String(payload.doneFile || "")
|
||||||
|
requestActive = !!doneFile
|
||||||
|
dmenuWidth = Math.max(1, Number(payload.width || 300))
|
||||||
|
dmenuMaxHeight = Math.max(0, Number(payload.maxHeight || 0))
|
||||||
|
activeMenu = "root"
|
||||||
|
navStack = []
|
||||||
|
filterText = ""
|
||||||
|
selectedIndex = 0
|
||||||
|
cursorActive = false
|
||||||
|
opened = true
|
||||||
|
rebuildDisplay()
|
||||||
|
|
||||||
|
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||||
|
}
|
||||||
ListModel { id: displayModel }
|
ListModel { id: displayModel }
|
||||||
|
|
||||||
// ----------------------------------------------------------- IPC surface
|
// ----------------------------------------------------------- IPC surface
|
||||||
@@ -697,6 +838,14 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: resultProc
|
||||||
|
onExited: {
|
||||||
|
if (root.applySerial === root.requestSerial)
|
||||||
|
root.opened = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The JSONC sources are watched so live edits to the default file (or the
|
// The JSONC sources are watched so live edits to the default file (or the
|
||||||
// user extension at ~/.config/omarchy/extensions/omarchy-menu.jsonc) take
|
// user extension at ~/.config/omarchy/extensions/omarchy-menu.jsonc) take
|
||||||
// effect without restarting the shell.
|
// effect without restarting the shell.
|
||||||
@@ -840,7 +989,10 @@ Item {
|
|||||||
if (!root.filterText) root.goBack()
|
if (!root.filterText) root.goBack()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Right) {
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Right) {
|
||||||
if (root.cursorActive) root.activateIndex(root.selectedIndex)
|
if (root.dmenuActive) {
|
||||||
|
if (root.mode === "input") root.applyDmenuSelection(root.filterText)
|
||||||
|
else if (displayModel.count > 0) root.activateIndex(root.cursorActive ? root.selectedIndex : 0)
|
||||||
|
} else if (root.cursorActive) root.activateIndex(root.selectedIndex)
|
||||||
else if (displayModel.count > 0) root.cursorActive = true
|
else if (displayModel.count > 0) root.cursorActive = true
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127 && (event.modifiers === Qt.NoModifier || event.modifiers === Qt.ShiftModifier)) {
|
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127 && (event.modifiers === Qt.NoModifier || event.modifiers === Qt.ShiftModifier)) {
|
||||||
@@ -866,7 +1018,7 @@ Item {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: root.filterText || ((root.item(root.activeMenu) ? root.item(root.activeMenu).label : "Go") + "…")
|
text: root.filterText || (root.dmenuActive ? (root.dmenuPrompt + "…") : ((root.item(root.activeMenu) ? root.item(root.activeMenu).label : "Go") + "…"))
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
opacity: root.filterText ? 1 : 0.58
|
opacity: root.filterText ? 1 : 0.58
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1034,7 +1186,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Style.space(8)
|
spacing: Style.space(8)
|
||||||
visible: displayModel.count === 0
|
visible: displayModel.count === 0 && root.mode !== "input"
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ""
|
text: ""
|
||||||
|
|||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy"
|
||||||
|
IMAGE_DIR="$STATE_DIR/clipboard-images"
|
||||||
|
mkdir -p "$IMAGE_DIR"
|
||||||
|
|
||||||
|
types=$(wl-paste --list-types 2>/dev/null || true)
|
||||||
|
|
||||||
|
emit_image() {
|
||||||
|
local mime="$1"
|
||||||
|
local ext="$2"
|
||||||
|
local tmp=""
|
||||||
|
local hash=""
|
||||||
|
local file=""
|
||||||
|
|
||||||
|
tmp=$(mktemp --tmpdir="$IMAGE_DIR" clipboard.XXXXXX) || return 0
|
||||||
|
if ! wl-paste --type "$mime" > "$tmp" 2>/dev/null || [[ ! -s $tmp ]]; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hash=$(sha256sum "$tmp" | awk '{print $1}')
|
||||||
|
file="$IMAGE_DIR/$hash.$ext"
|
||||||
|
if [[ -e $file ]]; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
else
|
||||||
|
mv "$tmp" "$file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq -cn --arg mime "$mime" --arg path "$file" '{type:"image", mime:$mime, path:$path}'
|
||||||
|
}
|
||||||
|
|
||||||
|
if grep -qx 'image/png' <<<"$types"; then
|
||||||
|
emit_image 'image/png' 'png'
|
||||||
|
elif grep -qx 'image/jpeg' <<<"$types"; then
|
||||||
|
emit_image 'image/jpeg' 'jpg'
|
||||||
|
elif grep -qx 'image/webp' <<<"$types"; then
|
||||||
|
emit_image 'image/webp' 'webp'
|
||||||
|
elif grep -qx 'image/gif' <<<"$types"; then
|
||||||
|
emit_image 'image/gif' 'gif'
|
||||||
|
elif grep -qx 'image/bmp' <<<"$types"; then
|
||||||
|
emit_image 'image/bmp' 'bmp'
|
||||||
|
elif grep -qx 'image/tiff' <<<"$types"; then
|
||||||
|
emit_image 'image/tiff' 'tiff'
|
||||||
|
elif grep -q '^text/' <<<"$types" || grep -qx 'UTF8_STRING' <<<"$types" || grep -qx 'STRING' <<<"$types"; then
|
||||||
|
wl-paste --type text --no-newline 2>/dev/null | jq -Rs 'select(length > 0) | {type:"text", text:.}'
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user