diff --git a/bin/omarchy-menu-images b/bin/omarchy-menu-images index 5c6ede58..6a7443e6 100755 --- a/bin/omarchy-menu-images +++ b/bin/omarchy-menu-images @@ -233,14 +233,19 @@ if [[ $preload == true ]]; then exit 0 fi -if ! omarchy-shell image-selector open \ +if ! open_result=$(omarchy-shell image-selector open \ "" \ "$rows_b64" \ "$selected_list_image" \ "$selection_file" \ "$done_file" \ "$show_labels" \ - "$filterable" >/dev/null; then + "$filterable"); then + echo "Image selector failed to accept request" >&2 + exit 1 +fi + +if [[ $open_result != "ok" ]]; then echo "Image selector failed to accept request" >&2 exit 1 fi diff --git a/shell/plugins/image-picker/ImagePicker.qml b/shell/plugins/image-picker/ImagePicker.qml index 65a56e4b..f85f32c0 100644 --- a/shell/plugins/image-picker/ImagePicker.qml +++ b/shell/plugins/image-picker/ImagePicker.qml @@ -302,9 +302,8 @@ Item { // Lifecycle hooks invoked by omarchy-shell summon/hide. shell.summon(id, // payloadJson) hands the JSON to open() here; shell.hide(id) calls close(). - // External CLI callers can either go through `shell summon omarchy.image- - // picker` (JSON payload), or hit the dedicated `image-selector` IpcHandler - // below for the lower-level positional call that omarchy-menu-images uses. + // The shell host owns the stable `image-selector` IPC target and forwards + // those lower-level positional calls here. function open(payload) { var args = {} if (payload) { @@ -347,44 +346,6 @@ Item { } } - // IPC surface. All arguments are strings (Quickshell IPC marshalling). - // imageRows can contain newlines/tabs, so the CLI caller base64-encodes - // it; everything else passes through verbatim. The two boolean-like - // fields use the literal strings "true" or "false". - IpcHandler { - target: "image-selector" - - function open(imageDirs: string, - imageRowsB64: string, - selectedImage: string, - selectionFile: string, - doneFile: string, - showLabels: string, - filterable: string): string { - var rows = Util.decodeBase64(imageRowsB64) - root.openSelector(imageDirs, rows, selectedImage, selectionFile, doneFile, - showLabels, filterable) - return "ok" - } - - function preload(imageRowsB64: string, - selectedImage: string, - showLabels: string, - filterable: string): string { - var rows = Util.decodeBase64(imageRowsB64) - root.preloadRows(rows, selectedImage, showLabels, filterable) - return "ok" - } - - function cancel(doneFile: string): void { - root.closeSelector(doneFile || "") - } - - function ping(): string { - return "ok" - } - } - Process { id: applyProc onExited: { diff --git a/shell/shell.qml b/shell/shell.qml index 2bac9598..d7098b9c 100644 --- a/shell/shell.qml +++ b/shell/shell.qml @@ -752,6 +752,62 @@ ShellRoot { } } + // --------------------------------------------------- image selector IPC + + function imagePickerItem() { + var loader = panelLoaders["omarchy.image-picker"] + return loader && loader.item ? loader.item : null + } + + IpcHandler { + target: "image-selector" + + function open(imageDirs: string, + imageRowsB64: string, + selectedImage: string, + selectionFile: string, + doneFile: string, + showLabels: string, + filterable: string): string { + var payload = JSON.stringify({ + imageDirs: imageDirs, + imageRows: Util.decodeBase64(imageRowsB64), + selectedImage: selectedImage, + selectionFile: selectionFile, + doneFile: doneFile, + showLabels: showLabels, + filterable: filterable + }) + return shell.summon("omarchy.image-picker", payload) ? "ok" : "unknown" + } + + function preload(imageRowsB64: string, + selectedImage: string, + showLabels: string, + filterable: string): string { + var picker = shell.imagePickerItem() + if (picker && typeof picker.preloadRows === "function") { + picker.preloadRows(Util.decodeBase64(imageRowsB64), selectedImage, + showLabels, filterable) + } + return "ok" + } + + function cancel(doneFile: string): string { + var picker = shell.imagePickerItem() + if (picker && typeof picker.closeSelector === "function") { + picker.closeSelector(doneFile || "") + } else { + shell.hide("omarchy.image-picker") + } + return "ok" + } + + function ping(): string { + return "ok" + } + } + // ---------------------------------------------------------- shell IPC IpcHandler { diff --git a/test/shell.d/runtime-smoke-test.sh b/test/shell.d/runtime-smoke-test.sh index 6ff80131..0f364d4f 100755 --- a/test/shell.d/runtime-smoke-test.sh +++ b/test/shell.d/runtime-smoke-test.sh @@ -134,6 +134,27 @@ jq -e '.locked | type == "boolean"' <<<"$(shell_ipc lock status)" >/dev/null || [[ $(shell_ipc osd close) == "ok" ]] || fail_with_log "OSD IPC closes" pass "plugin IPC contracts respond" +shell_ipc_quiet shell rescanPlugins >/dev/null +selector_rows_b64=$(printf '%s\t%s' "$TMPDIR/selector.png" "$TMPDIR/selector.png" | base64 -w 0) +selector_selection_file=$(mktemp "$TMPDIR/selector-selection.XXXXXX") +selector_done_file=$(mktemp "$TMPDIR/selector-done.XXXXXX") +rm -f "$selector_done_file" +selector_open="" +for _ in {1..80}; do + selector_open=$(shell_ipc image-selector open "" "$selector_rows_b64" "" "$selector_selection_file" "$selector_done_file" false false 2>/dev/null || true) + if [[ $selector_open == "ok" ]]; then + break + fi + if ! kill -0 "$QS_PID" 2>/dev/null; then + fail_with_log "test shell exited during plugin rescan" + fi + sleep 0.1 +done +[[ $selector_open == "ok" ]] || fail_with_log "image selector IPC survives plugin rescan" +shell_ipc_quiet image-selector cancel "$selector_done_file" >/dev/null +rm -f "$selector_selection_file" "$selector_done_file" +pass "image selector IPC survives plugin rescan" + shell_ipc_quiet omarchy.system-update refresh >/dev/null 2>&1 || true sleep 0.8