From 8ed33fc7e8b68e16122ab4e28155f3965de40093 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Thu, 4 Jun 2026 11:22:33 -0700 Subject: [PATCH] Add Codex Desktop project launcher binding --- docs/tiling-wm-experience.md | 2 + dotfiles/config/hypr/hyprland/binds.lua | 1 + .../taffybar/TaffybarConfig/Workspaces.hs | 1 - dotfiles/config/taffybar/taffybar | 2 +- dotfiles/config/xmonad/xmonad.hs | 1 + .../lib/bin/rofi_codex_desktop_project.sh | 81 +++++++++++++++++++ nixos/flake.lock | 12 +-- 7 files changed, 92 insertions(+), 8 deletions(-) create mode 100755 dotfiles/lib/bin/rofi_codex_desktop_project.sh diff --git a/docs/tiling-wm-experience.md b/docs/tiling-wm-experience.md index eea0482f..af5f5559 100644 --- a/docs/tiling-wm-experience.md +++ b/docs/tiling-wm-experience.md @@ -464,6 +464,8 @@ Required behavior: - `Hyper+p` opens the password picker with `rofi-pass`. - `Hyper+h` opens the screenshot tool with the compositor/session-appropriate screenshot command. +- `Hyper+n` opens a Codex Desktop project picker and starts a new thread in + the selected saved project root. - `Hyper+c` opens the Codex launcher with `rofi_tmcodex.sh`. - `Hyper+Shift+c` opens the Codex launcher with `tmcodex resume`. - `Hyper+k` opens the process killer with `rofi_kill_process.sh`. diff --git a/dotfiles/config/hypr/hyprland/binds.lua b/dotfiles/config/hypr/hyprland/binds.lua index 36912dfa..ff231676 100644 --- a/dotfiles/config/hypr/hyprland/binds.lua +++ b/dotfiles/config/hypr/hyprland/binds.lua @@ -67,6 +67,7 @@ function M.setup(ctx) bind(main_mod .. " + X", exec("rofi_command.sh"), desc("Open command menu")) bind(hyper .. " + V", exec([[cliphist list | rofi -dmenu -p "Clipboard" | cliphist decode | wl-copy]]), desc("Open clipboard history")) bind(hyper .. " + P", exec("rofi-pass"), desc("Open password menu")) + bind(hyper .. " + N", exec("rofi_codex_desktop_project.sh"), desc("Start Codex Desktop thread from project")) bind(hyper .. " + C", exec("rofi_tmcodex.sh"), desc("Open Codex session menu")) bind(hyper .. " + SHIFT + C", exec("rofi_tmcodex.sh resume"), desc("Resume Codex session")) bind(hyper .. " + L", exec("hypr_rofi_layout"), desc("Open Hyprland layout menu")) diff --git a/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs b/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs index b1b2ad73..712e7ee5 100644 --- a/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs +++ b/dotfiles/config/taffybar/TaffybarConfig/Workspaces.hs @@ -191,6 +191,5 @@ workspaceWindowIconGetter :: Workspaces.WindowIconPixbufGetter workspaceWindowIconGetter = chromeFaviconIconGetter chromeFaviconConfig <|||> workspaceManualIconGetter - <|||> Workspaces.getWindowIconPixbufFromChrome <|||> Workspaces.defaultGetWindowIconPixbuf <|||> workspaceFallbackIcon diff --git a/dotfiles/config/taffybar/taffybar b/dotfiles/config/taffybar/taffybar index 31578b07..7d98ec9c 160000 --- a/dotfiles/config/taffybar/taffybar +++ b/dotfiles/config/taffybar/taffybar @@ -1 +1 @@ -Subproject commit 31578b0770b061c9a63109739776b7291de1ae9b +Subproject commit 7d98ec9c3cd119ba8e98c3c3b1ed1766a12766ca diff --git a/dotfiles/config/xmonad/xmonad.hs b/dotfiles/config/xmonad/xmonad.hs index fab09c0a..8e2c581d 100644 --- a/dotfiles/config/xmonad/xmonad.hs +++ b/dotfiles/config/xmonad/xmonad.hs @@ -1075,6 +1075,7 @@ addKeys conf@XConfig { modMask = modm } = , ((hyper, xK_p), spawn "rofi-pass") , ((0, xK_Print), spawn "flameshot gui") , ((hyper, xK_h), spawn "flameshot gui") + , ((hyper, xK_n), spawn "rofi_codex_desktop_project.sh") , ((hyper, xK_c), spawn "rofi_tmcodex.sh") , ((hyper .|. shiftMask, xK_c), spawn "rofi_tmcodex.sh resume") , ((hyper .|. shiftMask, xK_l), spawn "dm-tool lock") diff --git a/dotfiles/lib/bin/rofi_codex_desktop_project.sh b/dotfiles/lib/bin/rofi_codex_desktop_project.sh new file mode 100755 index 00000000..a48532a9 --- /dev/null +++ b/dotfiles/lib/bin/rofi_codex_desktop_project.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env zsh +set -euo pipefail + +# Pick a saved Codex Desktop project root via rofi, then start a new desktop +# thread in that project using the documented codex:// deep link. + +codex_home="${CODEX_HOME:-$HOME/.codex}" +state_file="${CODEX_DESKTOP_STATE_FILE:-$codex_home/.codex-global-state.json}" +prompt="${CODEX_DESKTOP_PROJECT_PROMPT:-Codex project}" + +notify() { + if command -v notify-send >/dev/null 2>&1; then + notify-send "Codex Desktop launcher" "$1" + else + printf '%s\n' "$1" >&2 + fi +} + +emit_candidates() { + if [[ ! -r "$state_file" ]]; then + notify "Cannot read Codex Desktop state: $state_file" + return 1 + fi + + jq -r ' + def local_paths($key): + .[$key] // [] + | .[]? + | select(type == "string" and startswith("/")); + + local_paths("pinned-project-ids"), + local_paths("electron-saved-workspace-roots"), + local_paths("project-order") + ' "$state_file" +} + +dedup() { + awk 'NF && !seen[$0]++' +} + +existing_dirs() { + local dir + while IFS= read -r dir; do + [[ -d "$dir" ]] && printf '%s\n' "$dir" + done +} + +if [[ "${1:-}" == "--print-candidates" ]]; then + emit_candidates | dedup | existing_dirs + exit 0 +fi + +selected_dir="$( + emit_candidates | dedup | existing_dirs | rofi -dmenu -i -p "$prompt" || true +)" + +[[ -n "$selected_dir" ]] || exit 0 + +case "$selected_dir" in + "~"|"~/"*) + selected_dir="$HOME${selected_dir:1}" + ;; +esac + +if command -v realpath >/dev/null 2>&1; then + selected_dir="$(realpath -m -- "$selected_dir" 2>/dev/null || printf '%s' "$selected_dir")" +fi + +if [[ ! -d "$selected_dir" ]]; then + notify "Directory not found: $selected_dir" + exit 1 +fi + +encoded_path="$(jq -rn --arg path "$selected_dir" '$path | @uri')" + +if ! command -v xdg-open >/dev/null 2>&1; then + notify "xdg-open is not available" + exit 1 +fi + +xdg-open "codex://threads/new?path=$encoded_path" >/dev/null 2>&1 &! diff --git a/nixos/flake.lock b/nixos/flake.lock index c0c0e780..37a84c79 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -137,11 +137,11 @@ ] }, "locked": { - "lastModified": 1780345858, - "narHash": "sha256-t3dxFjzEFbuMd7o8LWtbnqfOkXDKjzvHXUiXQwvwXtk=", + "lastModified": 1780543091, + "narHash": "sha256-qdsCTv+i0YHjFY/DUb2paaxUzUUFrO+BZzKhPKc1m2Q=", "owner": "sadjow", "repo": "codex-cli-nix", - "rev": "ea8119de14a2263330da99363e6303db10a0f84b", + "rev": "d95f4900331633d49b8df26cfb4142742baa537b", "type": "github" }, "original": { @@ -161,11 +161,11 @@ ] }, "locked": { - "lastModified": 1780502586, - "narHash": "sha256-gwCstYHkiHuC2OeqAklxCz8D29NYMwYfCjrjbkP+Hx8=", + "lastModified": 1780552140, + "narHash": "sha256-8IRa+p8+RDYfbKufmK5h0MQ9Ul23wFLY5BCT/A8p/+Y=", "owner": "ilysenko", "repo": "codex-desktop-linux", - "rev": "53d18dae0f627c3aef6c26816cfb8823eb040181", + "rev": "946adef45e88c16f146d79391e5b6c47bd32ad26", "type": "github" }, "original": {