diff --git a/bin/omarchy-menu-emoji-insert b/bin/omarchy-menu-emoji-insert new file mode 100755 index 00000000..389cce6b --- /dev/null +++ b/bin/omarchy-menu-emoji-insert @@ -0,0 +1,23 @@ +#!/bin/bash + +# omarchy:summary=Insert an emoji into the focused application +# omarchy:group=menu +# omarchy:args= +# omarchy:hidden=true + +emoji="${1:-}" +emoji_ignore_file="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/omarchy-emoji-insert-ignore" +copy_pid="" + +[[ -n $emoji ]] || exit + +mkdir -p "${emoji_ignore_file%/*}" +printf '%s' "$emoji" >"$emoji_ignore_file" +printf '%s' "$emoji" | wl-copy --type text/plain --sensitive --foreground & +copy_pid=$! + +sleep 0.15 +wtype -M shift -k Insert -m shift 2>/dev/null || true +sleep 0.2 + +kill "$copy_pid" 2>/dev/null || true diff --git a/shell/plugins/clipboard/capture.sh b/shell/plugins/clipboard/capture.sh index 57bd465b..f0d87100 100755 --- a/shell/plugins/clipboard/capture.sh +++ b/shell/plugins/clipboard/capture.sh @@ -4,8 +4,26 @@ set -o pipefail STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy" IMAGE_DIR="$STATE_DIR/clipboard-images" +EMOJI_IGNORE_FILE="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/omarchy-emoji-insert-ignore" mkdir -p "$IMAGE_DIR" +should_ignore_next_copy() { + local marker_mtime=0 + + [[ -f $EMOJI_IGNORE_FILE ]] || return 1 + + marker_mtime=$(stat -c %Y "$EMOJI_IGNORE_FILE" 2>/dev/null || printf '0') + if (( $(date +%s) - marker_mtime > 5 )); then + rm -f "$EMOJI_IGNORE_FILE" + return 1 + fi + + rm -f "$EMOJI_IGNORE_FILE" + return 0 +} + +should_ignore_next_copy && exit 0 + types=$(wl-paste --list-types 2>/dev/null || true) emit_image() { diff --git a/shell/plugins/emojis/Emojis.qml b/shell/plugins/emojis/Emojis.qml index 1945c8d5..9c44d6df 100644 --- a/shell/plugins/emojis/Emojis.qml +++ b/shell/plugins/emojis/Emojis.qml @@ -44,7 +44,7 @@ Item { root.opened = true root.filterText = "" root.selectedIndex = 0 - root.cursorActive = false + root.cursorActive = true root.rebuildDisplay() Qt.callLater(function() { keyCatcher.forceActiveFocus() }) } @@ -81,6 +81,7 @@ Item { if (displayModel.count === 0) selectedIndex = 0 else if (selectedIndex >= displayModel.count) selectedIndex = displayModel.count - 1 else if (selectedIndex < 0) selectedIndex = 0 + cursorActive = displayModel.count > 0 Qt.callLater(function() { if (displayModel.count > 0) resultGrid.positionViewAtIndex(root.selectedIndex, GridView.Contain) @@ -132,7 +133,7 @@ Item { function setFilter(nextFilter) { root.filterText = nextFilter root.selectedIndex = 0 - root.cursorActive = false + root.cursorActive = true root.rebuildDisplay() } @@ -145,8 +146,9 @@ Item { function applySelected(emoji) { if (!emoji) return root.dismiss() - Quickshell.execDetached([root.omarchyPath + "/bin/omarchy-clipboard-paste-text", "--shift-insert", emoji]) + Quickshell.execDetached([root.omarchyPath + "/bin/omarchy-menu-emoji-insert", emoji]) } + ListModel { id: displayModel } FileView { diff --git a/test/shell.d/clipboard-test.sh b/test/shell.d/clipboard-test.sh index 7e464f93..1b22cad9 100644 --- a/test/shell.d/clipboard-test.sh +++ b/test/shell.d/clipboard-test.sh @@ -135,6 +135,15 @@ cat >"$TMPDIR/bin/wl-copy" <<'SH' cat >"$WL_COPY_OUT" SH +cat >"$TMPDIR/bin/wl-paste" <<'SH' +#!/bin/bash +if [[ $1 == "--list-types" ]]; then + printf 'text/plain\n' +elif [[ $1 == "--type" && $2 == "text" ]]; then + printf '%s' "${WL_PASTE_TEXT:-terminal copy}" +fi +SH + cat >"$TMPDIR/bin/wtype" <<'SH' #!/bin/bash printf '%s\n' "$*" >"$WTYPE_OUT" @@ -156,7 +165,19 @@ cat >"$TMPDIR/bin/satty" <<'SH' printf '%s\n' "$*" >"$SATTY_OUT" SH -chmod +x "$TMPDIR/bin/wl-copy" "$TMPDIR/bin/wtype" "$TMPDIR/bin/omarchy-launch-browser" "$TMPDIR/bin/omarchy-launch-editor" "$TMPDIR/bin/satty" +chmod +x "$TMPDIR/bin/wl-copy" "$TMPDIR/bin/wl-paste" "$TMPDIR/bin/wtype" "$TMPDIR/bin/omarchy-launch-browser" "$TMPDIR/bin/omarchy-launch-editor" "$TMPDIR/bin/satty" + +capture_output=$(CLIPBOARD_STATE=sensitive XDG_RUNTIME_DIR="$TMPDIR" PATH="$TMPDIR/bin:$PATH" "$ROOT/shell/plugins/clipboard/capture.sh") +[[ $capture_output == '{"type":"text","text":"terminal copy"}' ]] || fail "clipboard capture records sensitive text events" +pass "clipboard capture records sensitive text events" + +printf '😀' >"$TMPDIR/omarchy-emoji-insert-ignore" +capture_output=$(WL_PASTE_TEXT="😀" XDG_RUNTIME_DIR="$TMPDIR" PATH="$TMPDIR/bin:$PATH" "$ROOT/shell/plugins/clipboard/capture.sh") +[[ -z $capture_output ]] || fail "clipboard capture ignores transient emoji insert" +pass "clipboard capture ignores transient emoji insert" + +[[ ! -e "$TMPDIR/omarchy-emoji-insert-ignore" ]] || fail "clipboard capture consumes transient emoji marker" +pass "clipboard capture consumes transient emoji marker" jq -n --arg text "$(printf 'large block line 1\nlarge block line 2\n')" '[{type:"text", text:"ignored"}, {type:"text", text:$text}]' >"$TMPDIR/home/.local/state/omarchy/clipboard-history.json" diff --git a/test/shell.d/emojis-test.sh b/test/shell.d/emojis-test.sh index 58981cc7..983ebe00 100644 --- a/test/shell.d/emojis-test.sh +++ b/test/shell.d/emojis-test.sh @@ -45,3 +45,47 @@ assertEqual( 'emoji filtering finds face with tears of joy' ) JS + +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +mkdir -p "$TMPDIR/bin" "$TMPDIR/runtime" + +cat >"$TMPDIR/bin/wl-copy" <<'SH' +#!/bin/bash +args="$*" +target="$WL_COPY_OUT" +if [[ $args == "--type text/plain --sensitive --foreground" ]]; then + target="$WL_COPY_EMOJI_OUT" +fi + +printf '%s\n' "$args" >"$target.args" +cat >"$target" +SH + +cat >"$TMPDIR/bin/wtype" <<'SH' +#!/bin/bash +printf '%s\n' "$*" >"$WTYPE_OUT" +SH + +cat >"$TMPDIR/bin/sleep" <<'SH' +#!/bin/bash +exit 0 +SH + +chmod +x "$TMPDIR/bin/wl-copy" "$TMPDIR/bin/wtype" "$TMPDIR/bin/sleep" + +WL_COPY_OUT="$TMPDIR/copy" WL_COPY_EMOJI_OUT="$TMPDIR/emoji" WTYPE_OUT="$TMPDIR/wtype" PATH="$TMPDIR/bin:$PATH" \ + XDG_RUNTIME_DIR="$TMPDIR/runtime" "$ROOT/bin/omarchy-menu-emoji-insert" "😀" + +[[ $(<"$TMPDIR/emoji") == "😀" ]] || fail "emoji insert helper copies emoji transiently" +pass "emoji insert helper copies emoji transiently" + +[[ $(<"$TMPDIR/runtime/omarchy-emoji-insert-ignore") == "😀" ]] || fail "emoji insert helper marks transient emoji for history ignore" +pass "emoji insert helper marks transient emoji for history ignore" + +[[ $(<"$TMPDIR/emoji.args") == "--type text/plain --sensitive --foreground" ]] || fail "emoji insert helper serves transient clipboard in foreground" +pass "emoji insert helper serves transient clipboard in foreground" + +[[ $(<"$TMPDIR/wtype") == "-M shift -k Insert -m shift" ]] || fail "emoji insert helper pastes with shift insert" +pass "emoji insert helper pastes with shift insert"