Launch shell commands on active workspace
This commit is contained in:
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Launch a command on the active Hyprland workspace
|
||||||
|
# omarchy:args=<command>
|
||||||
|
|
||||||
|
cmd="$*"
|
||||||
|
[[ -n $cmd ]] || exit 0
|
||||||
|
|
||||||
|
lua_quote() {
|
||||||
|
local value=$1
|
||||||
|
|
||||||
|
value=${value//\\/\\\\}
|
||||||
|
value=${value//\"/\\\"}
|
||||||
|
value=${value//$'\n'/\\n}
|
||||||
|
printf '"%s"' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
active_workspace() {
|
||||||
|
hyprctl activeworkspace -j 2>/dev/null | jq -r '.name // .id // empty' 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n ${HYPRLAND_INSTANCE_SIGNATURE:-} ]] && omarchy-cmd-present hyprctl && omarchy-cmd-present jq; then
|
||||||
|
workspace="$(active_workspace)"
|
||||||
|
if [[ -n $workspace ]] && hyprctl dispatch "function() hl.exec_cmd($(lua_quote "$cmd"), { workspace = $(lua_quote "$workspace") }) end" >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec bash -lc "$cmd"
|
||||||
@@ -14,30 +14,5 @@ shell_quote() {
|
|||||||
printf "'"
|
printf "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_quote() {
|
|
||||||
local value=$1
|
|
||||||
|
|
||||||
value=${value//\\/\\\\}
|
|
||||||
value=${value//\"/\\\"}
|
|
||||||
value=${value//$'\n'/\\n}
|
|
||||||
printf '"%s"' "$value"
|
|
||||||
}
|
|
||||||
|
|
||||||
active_workspace() {
|
|
||||||
hyprctl activeworkspace -j 2>/dev/null | jq -r '.name // .id // empty' 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
terminal_command="setsid uwsm-app -- xdg-terminal-exec --app-id=org.omarchy.terminal --title=Omarchy -e bash -c $(shell_quote "$presentation_script")"
|
terminal_command="setsid uwsm-app -- xdg-terminal-exec --app-id=org.omarchy.terminal --title=Omarchy -e bash -c $(shell_quote "$presentation_script")"
|
||||||
workspace="${OMARCHY_LAUNCH_WORKSPACE:-}"
|
exec omarchy-hyprland-launch "$terminal_command"
|
||||||
|
|
||||||
if [[ -z $workspace ]] && [[ -n ${HYPRLAND_INSTANCE_SIGNATURE:-} ]] && omarchy-cmd-present hyprctl && omarchy-cmd-present jq; then
|
|
||||||
workspace="$(active_workspace)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n $workspace ]] && [[ -n ${HYPRLAND_INSTANCE_SIGNATURE:-} ]] && omarchy-cmd-present hyprctl; then
|
|
||||||
if hyprctl dispatch "function() hl.exec_cmd($(lua_quote "$terminal_command"), { workspace = $(lua_quote "$workspace") }) end" >/dev/null 2>&1; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.omarchy.terminal --title=Omarchy -e bash -c "$presentation_script"
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
// Shared utility helpers used across plugins. Pure functions only — no
|
// Shared utility helpers used across plugins. Pure functions only — no
|
||||||
@@ -6,6 +7,8 @@ import QtQuick
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property string omarchyPath: Quickshell.env("OMARCHY_PATH")
|
||||||
|
|
||||||
function clamp(value, min, max) {
|
function clamp(value, min, max) {
|
||||||
var n = Number(value)
|
var n = Number(value)
|
||||||
if (!isFinite(n)) return min
|
if (!isFinite(n)) return min
|
||||||
@@ -38,15 +41,8 @@ QtObject {
|
|||||||
return "'" + String(value || "").replace(/'/g, "'\\''") + "'"
|
return "'" + String(value || "").replace(/'/g, "'\\''") + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
function luaQuote(value) {
|
|
||||||
return "\"" + String(value || "")
|
|
||||||
.replace(/\\/g, "\\\\")
|
|
||||||
.replace(/"/g, "\\\"")
|
|
||||||
.replace(/\n/g, "\\n") + "\""
|
|
||||||
}
|
|
||||||
|
|
||||||
function hyprExecCommand(command) {
|
function hyprExecCommand(command) {
|
||||||
return ["hyprctl", "dispatch", "hl.dsp.exec_cmd(" + luaQuote(command) + ")"]
|
return [omarchyPath + "/bin/omarchy-hyprland-launch", command]
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
@@ -25,7 +24,6 @@ Item {
|
|||||||
property int launchSerial: 0
|
property int launchSerial: 0
|
||||||
property int launchToplevelCount: 0
|
property int launchToplevelCount: 0
|
||||||
property var launchActiveToplevel: null
|
property var launchActiveToplevel: null
|
||||||
property string launchWorkspace: ""
|
|
||||||
property bool launchOsdOpen: false
|
property bool launchOsdOpen: false
|
||||||
property string launchOsdMessage: ""
|
property string launchOsdMessage: ""
|
||||||
property var configuredHiddenEntryIds: ({})
|
property var configuredHiddenEntryIds: ({})
|
||||||
@@ -77,7 +75,6 @@ Item {
|
|||||||
? root.contentMargin * 2 + root.searchHeight + root.contentSpacing + requestedListHeight
|
? root.contentMargin * 2 + root.searchHeight + root.contentSpacing + requestedListHeight
|
||||||
: 400
|
: 400
|
||||||
|
|
||||||
root.launchWorkspace = String(payload.workspace || "") || root.focusedWorkspaceName()
|
|
||||||
root.opened = true
|
root.opened = true
|
||||||
root.filterText = payload.query || ""
|
root.filterText = payload.query || ""
|
||||||
root.selectedIndex = 0
|
root.selectedIndex = 0
|
||||||
@@ -132,12 +129,6 @@ Item {
|
|||||||
return LauncherSearch.entrySearchText(entry)
|
return LauncherSearch.entrySearchText(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusedWorkspaceName() {
|
|
||||||
var workspace = Hyprland.focusedWorkspace
|
|
||||||
if (!workspace) return ""
|
|
||||||
return String(workspace.name || workspace.id || "")
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHiddenEntry(entry) {
|
function isHiddenEntry(entry) {
|
||||||
var id = String((entry && entry.id) || "")
|
var id = String((entry && entry.id) || "")
|
||||||
return root.configuredHiddenEntryIds[id] === true || root.desktopHiddenEntryIds[id] === true
|
return root.configuredHiddenEntryIds[id] === true || root.desktopHiddenEntryIds[id] === true
|
||||||
@@ -259,9 +250,12 @@ Item {
|
|||||||
if (index < 0 || index >= root.filteredEntries.length) return
|
if (index < 0 || index >= root.filteredEntries.length) return
|
||||||
var entry = root.filteredEntries[index]
|
var entry = root.filteredEntries[index]
|
||||||
if (!entry) return
|
if (!entry) return
|
||||||
|
var desktopId = String(entry.id || "")
|
||||||
|
if (!desktopId) return
|
||||||
|
|
||||||
root.beginLaunchFeedback(entry)
|
root.beginLaunchFeedback(entry)
|
||||||
root.dismiss()
|
root.dismiss()
|
||||||
entry.execute()
|
Quickshell.execDetached(Util.hyprExecCommand("gtk-launch " + Util.shellQuote(desktopId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestDeleteIndex(index) {
|
function requestDeleteIndex(index) {
|
||||||
@@ -286,10 +280,9 @@ Item {
|
|||||||
|
|
||||||
var desktopId = String(entry.id || "")
|
var desktopId = String(entry.id || "")
|
||||||
var name = root.entryName(entry)
|
var name = root.entryName(entry)
|
||||||
var command = [root.omarchyPath + "/bin/omarchy-remove-launcher-entry", desktopId, name]
|
var command = Util.shellQuote(root.omarchyPath + "/bin/omarchy-remove-launcher-entry") + " " + Util.shellQuote(desktopId) + " " + Util.shellQuote(name)
|
||||||
if (root.launchWorkspace !== "") command = ["env", "OMARCHY_LAUNCH_WORKSPACE=" + root.launchWorkspace].concat(command)
|
|
||||||
root.cancelDelete()
|
root.cancelDelete()
|
||||||
Quickshell.execDetached(command)
|
Quickshell.execDetached(Util.hyprExecCommand(command))
|
||||||
}
|
}
|
||||||
|
|
||||||
function beginLaunchFeedback(entry) {
|
function beginLaunchFeedback(entry) {
|
||||||
|
|||||||
@@ -37,10 +37,3 @@ dispatch=$(<"$TEST_LOG")
|
|||||||
[[ $dispatch == *'workspace = "2"'* ]] || fail "floating terminal targets active workspace" "$dispatch"
|
[[ $dispatch == *'workspace = "2"'* ]] || fail "floating terminal targets active workspace" "$dispatch"
|
||||||
[[ $dispatch == *"xdg-terminal-exec --app-id=org.omarchy.terminal"* ]] || fail "floating terminal dispatch launches Omarchy terminal" "$dispatch"
|
[[ $dispatch == *"xdg-terminal-exec --app-id=org.omarchy.terminal"* ]] || fail "floating terminal dispatch launches Omarchy terminal" "$dispatch"
|
||||||
pass "floating terminal targets active workspace"
|
pass "floating terminal targets active workspace"
|
||||||
|
|
||||||
: >"$TEST_LOG"
|
|
||||||
OMARCHY_LAUNCH_WORKSPACE=7 "$ROOT/bin/omarchy-launch-floating-terminal-with-presentation" "echo hello"
|
|
||||||
|
|
||||||
dispatch=$(<"$TEST_LOG")
|
|
||||||
[[ $dispatch == *'workspace = "7"'* ]] || fail "floating terminal honors explicit launch workspace" "$dispatch"
|
|
||||||
pass "floating terminal honors explicit launch workspace"
|
|
||||||
|
|||||||
@@ -80,11 +80,18 @@ assert(
|
|||||||
'launcher delete keeps launcher open after confirmation'
|
'launcher delete keeps launcher open after confirmation'
|
||||||
)
|
)
|
||||||
assert(
|
assert(
|
||||||
confirmDeleteMatch[1].includes('OMARCHY_LAUNCH_WORKSPACE='),
|
confirmDeleteMatch[1].includes('Util.hyprExecCommand(command)'),
|
||||||
'launcher delete passes launch workspace to remover'
|
'launcher delete routes remover through Hyprland'
|
||||||
|
)
|
||||||
|
|
||||||
|
const activateMatch = launcherQml.match(/function activateIndex\(index\) \{([\s\S]*?)\n \}/)
|
||||||
|
assert(activateMatch, 'launcher activateIndex function exists')
|
||||||
|
assert(
|
||||||
|
!activateMatch[1].includes('entry.execute()'),
|
||||||
|
'launcher does not execute desktop entries directly'
|
||||||
)
|
)
|
||||||
assert(
|
assert(
|
||||||
launcherQml.includes('function focusedWorkspaceName()'),
|
activateMatch[1].includes('gtk-launch') && activateMatch[1].includes('Util.hyprExecCommand'),
|
||||||
'launcher captures focused workspace'
|
'launcher routes desktop entry launch through Hyprland'
|
||||||
)
|
)
|
||||||
JS
|
JS
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
|
run_node_test <<'JS'
|
||||||
|
const fs = require('fs')
|
||||||
|
const utilQml = fs.readFileSync(path.join(root, 'shell/Commons/Util.qml'), 'utf8')
|
||||||
|
|
||||||
|
assert(
|
||||||
|
utilQml.includes('import Quickshell'),
|
||||||
|
'shell launch helper can read Omarchy path'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
utilQml.includes('Quickshell.env("OMARCHY_PATH")'),
|
||||||
|
'shell launch helper uses Omarchy path'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/function hyprExecCommand\(command\)[\s\S]*omarchy-hyprland-launch/.test(utilQml),
|
||||||
|
'shell launch helper routes through Hyprland launch command'
|
||||||
|
)
|
||||||
|
|
||||||
|
JS
|
||||||
|
|
||||||
|
tmp_dir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
|
|
||||||
|
cat >"$tmp_dir/hyprctl" <<'SCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
case "$1" in
|
||||||
|
activeworkspace)
|
||||||
|
printf '{"id":2,"name":"2"}\n'
|
||||||
|
;;
|
||||||
|
dispatch)
|
||||||
|
printf '%s\n' "$2" >>"$TEST_LOG"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$tmp_dir/hyprctl"
|
||||||
|
|
||||||
|
cat >"$tmp_dir/jq" <<'SCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
cat >/dev/null
|
||||||
|
printf '2\n'
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$tmp_dir/jq"
|
||||||
|
|
||||||
|
export TEST_LOG="$tmp_dir/log"
|
||||||
|
export PATH="$tmp_dir:$ROOT/bin:$PATH"
|
||||||
|
export HYPRLAND_INSTANCE_SIGNATURE=test
|
||||||
|
|
||||||
|
"$ROOT/bin/omarchy-hyprland-launch" "echo hello"
|
||||||
|
|
||||||
|
dispatch=$(<"$TEST_LOG")
|
||||||
|
[[ $dispatch == *'workspace = "2"'* ]] || fail "hyprland launch targets active workspace" "$dispatch"
|
||||||
|
[[ $dispatch == *'hl.exec_cmd("echo hello"'* ]] || fail "hyprland launch dispatches command" "$dispatch"
|
||||||
|
pass "hyprland launch targets active workspace"
|
||||||
|
|
||||||
|
unset HYPRLAND_INSTANCE_SIGNATURE
|
||||||
|
"$ROOT/bin/omarchy-hyprland-launch" "printf fallback > $(printf '%q' "$tmp_dir/fallback")"
|
||||||
|
[[ $(<"$tmp_dir/fallback") == "fallback" ]] || fail "hyprland launch falls back outside Hyprland"
|
||||||
|
pass "hyprland launch falls back outside Hyprland"
|
||||||
Reference in New Issue
Block a user