From 75f2079ddf60f8901bd436e1cb908c189a8a9037 Mon Sep 17 00:00:00 2001 From: Pablu Date: Wed, 29 Jul 2026 08:36:58 +0200 Subject: [PATCH] Add jj and herdr --- dot_config/herdr/config.toml | 106 +++ dot_config/herdr/empty_dot_plugins.lock | 0 dot_config/herdr/herdr-client.log | 4 + dot_config/herdr/herdr-server.log | 670 ++++++++++++++++++ dot_config/herdr/plugins.json | 122 ++++ .../config/herdr-splits/herdr-splits.conf | 15 + .../herdr-splits-d880c4924f2b/CONTRIBUTING.md | 126 ++++ .../github/herdr-splits-d880c4924f2b/LICENSE | 21 + .../github/herdr-splits-d880c4924f2b/Makefile | 21 + .../herdr-splits-d880c4924f2b/README.md | 445 ++++++++++++ .../deps/mini.test/.keep | 0 .../dot_git/FETCH_HEAD | 1 + .../herdr-splits-d880c4924f2b/dot_git/HEAD | 1 + .../herdr-splits-d880c4924f2b/dot_git/config | 10 + .../dot_git/description | 1 + .../hooks/executable_applypatch-msg.sample | 15 + .../hooks/executable_commit-msg.sample | 24 + .../executable_fsmonitor-watchman.sample | 174 +++++ .../hooks/executable_post-update.sample | 8 + .../hooks/executable_pre-applypatch.sample | 14 + .../hooks/executable_pre-commit.sample | 49 ++ .../hooks/executable_pre-merge-commit.sample | 13 + .../dot_git/hooks/executable_pre-push.sample | 53 ++ .../hooks/executable_pre-rebase.sample | 169 +++++ .../hooks/executable_pre-receive.sample | 24 + .../executable_prepare-commit-msg.sample | 42 ++ .../hooks/executable_push-to-checkout.sample | 78 ++ .../executable_sendemail-validate.sample | 77 ++ .../dot_git/hooks/executable_update.sample | 128 ++++ .../herdr-splits-d880c4924f2b/dot_git/index | Bin 0 -> 3078 bytes .../dot_git/info/exclude | 6 + .../dot_git/logs/HEAD | 1 + ...nly_5337796b0a74934b058105cc428c5616fd0f0d | Bin 0 -> 2321 bytes ...nly_fc9295e33d59a441e3be5e0e642938ffbda0e0 | Bin 0 -> 574 bytes ...nly_7273e004e4f7ef07f13c83164d2cb2c51df65d | Bin 0 -> 188 bytes ...nly_d0b0db945ddf3d303278b53db08991f21ee859 | Bin 0 -> 1118 bytes ...nly_63803d3dbf5f7131e57db9486568c5fb744450 | Bin 0 -> 2771 bytes ...nly_4f4a2cc4bd8534e7df9f92f3fe62c91693463e | Bin 0 -> 52 bytes ...nly_52c3623b72f8cad4b4f68f961bc9a12df4fe84 | 1 + ...nly_51026d393d97f55d261e1e20dbce0bb707b784 | Bin 0 -> 678 bytes ...nly_8209034800d64adf3487b3819bd6a531ae0606 | Bin 0 -> 1305 bytes ...nly_dd672a9627ea5b0f567efa2a860492622b1415 | Bin 0 -> 2252 bytes ...nly_e8254ac9e3b167cd84e4c427d87f8af76e23ea | 1 + ...nly_399a89bfca1a0095f7a53d1f89810861242e79 | Bin 0 -> 97 bytes ...nly_f6821b845d7069c2c68f7c7082be8e2c152b04 | Bin 0 -> 1539 bytes ...nly_32363ad9bc23df7acbbd83598565e140168682 | 2 + ...nly_1d507c6ba403c265e84aa3d6b5a66aef442953 | Bin 0 -> 650 bytes ...nly_f002f346b87b770ebb3747d801684ff4d5f56a | Bin 0 -> 1980 bytes ...nly_ecca37040cae81738e7dc8c44e8e705d7f744c | Bin 0 -> 2470 bytes ...nly_ecb9315d55b29f0f5f1a0efcc21b4ee10e6507 | Bin 0 -> 394 bytes ...nly_ecca042405c772eac6ec40f362327ad8e36693 | Bin 0 -> 282 bytes ...nly_9cd53e889a0af1c8ac06d90a7f529692fb7082 | Bin 0 -> 7582 bytes ...nly_9f3e285bae904c5dad085e888395b10ceeab26 | Bin 0 -> 984 bytes ...nly_bc9e7a54d4cd045744e453334f71ee63780c03 | Bin 0 -> 95 bytes ...nly_188bae7a779d87d71fb99f7953ed0bda60c6eb | Bin 0 -> 132 bytes ...nly_2c6604f456443ea726645083a1e33ab3ba3abc | Bin 0 -> 542 bytes ...nly_dbbb7b9d0d61f7384a4d05f12dc7e2075fba7c | Bin 0 -> 2049 bytes ...nly_e4fc94c4ef9904a9c3dc43a7692439c8692023 | Bin 0 -> 54 bytes ...nly_485ad8e3a1602c46b2e9811abd0452761c1e70 | Bin 0 -> 1553 bytes ...nly_cd3e093eccea0460991fdc918bdda0b58c5c11 | Bin 0 -> 1891 bytes ...nly_312fd11e35846f0f2f2fb994b40fe1dc4cf948 | Bin 0 -> 1248 bytes ...nly_d3716f06cd3b80418b5bd06b7ddcf844502a58 | Bin 0 -> 60 bytes ...nly_7b920f681623037f9c4d83442aae19716c0744 | Bin 0 -> 4244 bytes ...nly_09b5ac3451619c127ef249a2d75e8e4f0693be | Bin 0 -> 433 bytes ...nly_a3be96e59ca11039dd2bd5c482613e90fc0c76 | 2 + ...nly_c8ddd059a98b318cf0225cff3bb67ab6d66ac1 | Bin 0 -> 136 bytes ...nly_ef3949f5d970786e78d88f1eb2687e68d3d476 | Bin 0 -> 1606 bytes ...nly_6d5aead0ff3e1663dd2e5e9f179ba88e886dc0 | Bin 0 -> 490 bytes ...nly_a3be9442717a2d5028222e5310175c26bf7eb3 | Bin 0 -> 206 bytes ...nly_5b8b893f1aa6d80da27731c2a685771d9a614f | Bin 0 -> 31 bytes ...nly_8a80ddfe620609eed45752ddf4cf4cb3cd3ffd | Bin 0 -> 3829 bytes ...nly_e44002117933a436ae94729bd1133236ab9f6c | Bin 0 -> 51 bytes ...nly_94999bc31a7ddc26674e3dbda98f68e0c133b6 | Bin 0 -> 1491 bytes .../dot_git/objects/info/.keep | 0 .../dot_git/objects/pack/.keep | 0 .../dot_git/refs/heads/.keep | 0 .../dot_git/refs/tags/.keep | 0 .../herdr-splits-d880c4924f2b/dot_git/shallow | 1 + .../dot_github/ISSUE_TEMPLATE/bug_report.yml | 100 +++ .../ISSUE_TEMPLATE/feature_request.yml | 32 + .../dot_github/PULL_REQUEST_TEMPLATE.md | 31 + .../dot_github/workflows/ci.yml | 43 ++ .../herdr-splits-d880c4924f2b/dot_gitignore | 1 + .../herdr-splits-d880c4924f2b/dot_gitmodules | 3 + .../herdr-plugin.toml | 62 ++ .../lua/herdr-splits/conf.lua | 236 ++++++ .../lua/herdr-splits/config.lua | 146 ++++ .../lua/herdr-splits/health.lua | 46 ++ .../lua/herdr-splits/herdr.lua | 160 +++++ .../lua/herdr-splits/init.lua | 162 +++++ .../lua/herdr-splits/nav.lua | 206 ++++++ .../lua/herdr-splits/resize.lua | 108 +++ .../lua/herdr-splits/sync.lua | 107 +++ .../lua/herdr-splits/win.lua | 154 ++++ .../plugin/herdr-splits.lua | 11 + .../scripts/executable_herdr-nav.sh | 104 +++ .../scripts/executable_herdr-resize.sh | 58 ++ .../scripts/minimal_init.lua | 14 + .../tests/test_conf.lua | 196 +++++ .../tests/test_herdr.lua | 267 +++++++ .../tests/test_nav.lua | 520 ++++++++++++++ .../tests/test_plugin.lua | 60 ++ .../tests/test_resize.lua | 302 ++++++++ .../tests/test_win.lua | 138 ++++ dot_config/herdr/session.json | 40 ++ dot_config/nvim/lua/plugins/herdr.lua | 98 +++ dot_config/private_jj/config.toml | 5 + .../repos/90d6ab3cd758e327650c/config.toml | 4 + .../private_metadata.binpb | 2 + dot_config/starship.toml | 12 + dot_zshrc.tmpl | 2 + 111 files changed, 5855 insertions(+) create mode 100644 dot_config/herdr/config.toml create mode 100644 dot_config/herdr/empty_dot_plugins.lock create mode 100644 dot_config/herdr/herdr-client.log create mode 100644 dot_config/herdr/herdr-server.log create mode 100644 dot_config/herdr/plugins.json create mode 100644 dot_config/herdr/plugins/config/herdr-splits/herdr-splits.conf create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/CONTRIBUTING.md create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/LICENSE create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/Makefile create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/README.md create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/deps/mini.test/.keep create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/FETCH_HEAD create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/HEAD create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/config create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/description create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_applypatch-msg.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_commit-msg.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_fsmonitor-watchman.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_post-update.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-applypatch.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-commit.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-merge-commit.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-push.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-rebase.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-receive.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_prepare-commit-msg.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_push-to-checkout.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_sendemail-validate.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_update.sample create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/index create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/info/exclude create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/logs/HEAD create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/00/readonly_5337796b0a74934b058105cc428c5616fd0f0d create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/0a/readonly_fc9295e33d59a441e3be5e0e642938ffbda0e0 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/10/readonly_7273e004e4f7ef07f13c83164d2cb2c51df65d create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/16/readonly_d0b0db945ddf3d303278b53db08991f21ee859 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/17/readonly_63803d3dbf5f7131e57db9486568c5fb744450 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/27/readonly_4f4a2cc4bd8534e7df9f92f3fe62c91693463e create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/27/readonly_52c3623b72f8cad4b4f68f961bc9a12df4fe84 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/37/readonly_51026d393d97f55d261e1e20dbce0bb707b784 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/37/readonly_8209034800d64adf3487b3819bd6a531ae0606 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/3c/readonly_dd672a9627ea5b0f567efa2a860492622b1415 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/3f/readonly_e8254ac9e3b167cd84e4c427d87f8af76e23ea create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/41/readonly_399a89bfca1a0095f7a53d1f89810861242e79 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/47/readonly_f6821b845d7069c2c68f7c7082be8e2c152b04 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/4d/readonly_32363ad9bc23df7acbbd83598565e140168682 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/51/readonly_1d507c6ba403c265e84aa3d6b5a66aef442953 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/52/readonly_f002f346b87b770ebb3747d801684ff4d5f56a create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/56/readonly_ecca37040cae81738e7dc8c44e8e705d7f744c create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/6f/readonly_ecb9315d55b29f0f5f1a0efcc21b4ee10e6507 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/7b/readonly_ecca042405c772eac6ec40f362327ad8e36693 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/7f/readonly_9cd53e889a0af1c8ac06d90a7f529692fb7082 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/8e/readonly_9f3e285bae904c5dad085e888395b10ceeab26 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/8f/readonly_bc9e7a54d4cd045744e453334f71ee63780c03 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/92/readonly_188bae7a779d87d71fb99f7953ed0bda60c6eb create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/99/readonly_2c6604f456443ea726645083a1e33ab3ba3abc create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/9c/readonly_dbbb7b9d0d61f7384a4d05f12dc7e2075fba7c create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/a3/readonly_e4fc94c4ef9904a9c3dc43a7692439c8692023 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/a5/readonly_485ad8e3a1602c46b2e9811abd0452761c1e70 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ae/readonly_cd3e093eccea0460991fdc918bdda0b58c5c11 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/c8/readonly_312fd11e35846f0f2f2fb994b40fe1dc4cf948 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ca/readonly_d3716f06cd3b80418b5bd06b7ddcf844502a58 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d1/readonly_7b920f681623037f9c4d83442aae19716c0744 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d4/readonly_09b5ac3451619c127ef249a2d75e8e4f0693be create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d8/readonly_a3be96e59ca11039dd2bd5c482613e90fc0c76 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/dd/readonly_c8ddd059a98b318cf0225cff3bb67ab6d66ac1 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/dd/readonly_ef3949f5d970786e78d88f1eb2687e68d3d476 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/df/readonly_6d5aead0ff3e1663dd2e5e9f179ba88e886dc0 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/e2/readonly_a3be9442717a2d5028222e5310175c26bf7eb3 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/f3/readonly_5b8b893f1aa6d80da27731c2a685771d9a614f create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/f8/readonly_8a80ddfe620609eed45752ddf4cf4cb3cd3ffd create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/fd/readonly_e44002117933a436ae94729bd1133236ab9f6c create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ff/readonly_94999bc31a7ddc26674e3dbda98f68e0c133b6 create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/info/.keep create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/pack/.keep create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/refs/heads/.keep create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/refs/tags/.keep create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/shallow create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/PULL_REQUEST_TEMPLATE.md create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/workflows/ci.yml create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitignore create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitmodules create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/herdr-plugin.toml create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/conf.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/config.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/health.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/herdr.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/init.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/nav.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/resize.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/sync.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/win.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/plugin/herdr-splits.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-nav.sh create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-resize.sh create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/minimal_init.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_conf.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_herdr.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_nav.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_plugin.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_resize.lua create mode 100644 dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_win.lua create mode 100644 dot_config/herdr/session.json create mode 100644 dot_config/nvim/lua/plugins/herdr.lua create mode 100644 dot_config/private_jj/config.toml create mode 100644 dot_config/private_jj/repos/90d6ab3cd758e327650c/config.toml create mode 100644 dot_config/private_jj/repos/90d6ab3cd758e327650c/private_metadata.binpb diff --git a/dot_config/herdr/config.toml b/dot_config/herdr/config.toml new file mode 100644 index 0000000..3a5e277 --- /dev/null +++ b/dot_config/herdr/config.toml @@ -0,0 +1,106 @@ +onboarding = false + +[terminal] +# New panes inherit the focused pane's working directory. +new_cwd = "follow" + +[ui] +# Keep the project/workspace sidebar visible and useful. +sidebar_width = 28 +sidebar_min_width = 20 +sidebar_max_width = 38 + +# Visually distinguish Herdr panes without wasting much space. +pane_borders = true +pane_gaps = false + +# Hide the tab bar when a workspace only has one tab. +hide_tab_bar_when_single_tab = true + +# Mouse support is useful on the Mac, particularly while learning Herdr. +mouse_capture = true +copy_on_select = true + +[keys] +prefix = "ctrl+space" + +# Project switching. +workspace_picker = "prefix+w" +goto = "prefix+g" +switch_workspace = "prefix+shift+1..9" +previous_workspace = "prefix+shift+[" +next_workspace = "prefix+shift+]" + +# Quickly return to the previously focused pane, even across workspaces. +last_pane = "prefix+backtick" + +# Tabs inside the current project. +new_tab = "prefix+c" +previous_tab = "prefix+p" +next_tab = "prefix+n" +switch_tab = "prefix+1..9" + +# Pane operations. +split_vertical = "prefix+/" +split_horizontal = "prefix+minus" +zoom = "prefix+z" +close_pane = "prefix+x" + +# Keep prefixed navigation as an escape hatch. +focus_pane_left = "prefix+h" +focus_pane_down = "prefix+j" +focus_pane_up = "prefix+k" +focus_pane_right = "prefix+l" + +# Direct Ctrl+HJKL is handled by herdr-splits below. + +[[keys.command]] +key = "ctrl+h" +type = "plugin_action" +command = "herdr-splits.nav-left" +description = "navigate left through Neovim and Herdr" + +[[keys.command]] +key = "ctrl+j" +type = "plugin_action" +command = "herdr-splits.nav-down" +description = "navigate down through Neovim and Herdr" + +[[keys.command]] +key = "ctrl+k" +type = "plugin_action" +command = "herdr-splits.nav-up" +description = "navigate up through Neovim and Herdr" + +[[keys.command]] +key = "ctrl+l" +type = "plugin_action" +command = "herdr-splits.nav-right" +description = "navigate right through Neovim and Herdr" + +[[keys.command]] +key = "alt+h" +type = "plugin_action" +command = "herdr-splits.resize-left" +description = "resize left" + +[[keys.command]] +key = "alt+j" +type = "plugin_action" +command = "herdr-splits.resize-down" +description = "resize down" + +[[keys.command]] +key = "alt+k" +type = "plugin_action" +command = "herdr-splits.resize-up" +description = "resize up" + +[[keys.command]] +key = "alt+l" +type = "plugin_action" +command = "herdr-splits.resize-right" +description = "resize right" + +[ui.toast] +delivery = "herdr" diff --git a/dot_config/herdr/empty_dot_plugins.lock b/dot_config/herdr/empty_dot_plugins.lock new file mode 100644 index 0000000..e69de29 diff --git a/dot_config/herdr/herdr-client.log b/dot_config/herdr/herdr-client.log new file mode 100644 index 0000000..f9036a6 --- /dev/null +++ b/dot_config/herdr/herdr-client.log @@ -0,0 +1,4 @@ +2026-07-24T20:15:47.544152Z INFO herdr::logging: herdr starting event="app.startup" subsystem="client" outcome="started" pid=8768 +2026-07-24T20:15:47.544192Z INFO herdr::client: connecting to server path=/Users/zamp/.config/herdr/herdr-client.sock +2026-07-24T20:15:47.774089Z INFO herdr::client: handshake succeeded version=17 encoding=SemanticFrame +2026-07-27T08:17:29.705493Z INFO herdr::logging: herdr exiting event="app.shutdown" subsystem="client" outcome="completed" pid=8768 diff --git a/dot_config/herdr/herdr-server.log b/dot_config/herdr/herdr-server.log new file mode 100644 index 0000000..1588c09 --- /dev/null +++ b/dot_config/herdr/herdr-server.log @@ -0,0 +1,670 @@ +2026-07-24T20:15:47.495927Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 +2026-07-24T20:15:47.496202Z INFO herdr::api::server: api server listening path=/Users/zamp/.config/herdr/herdr.sock +2026-07-24T20:15:47.496722Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 +2026-07-24T20:15:47.510661Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-24T20:15:47.515100Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=1 rows=24 cols=80 scrollback_limit_bytes=10000000 +2026-07-24T20:15:47.517632Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=1 pid=8794 +2026-07-24T20:15:47.517974Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w1" pane_id=1 +2026-07-24T20:15:47.517985Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w1" +2026-07-24T20:15:47.518010Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:15:47.518044Z INFO herdr::server::headless: created startup workspace cwd=/Users/zamp +2026-07-24T20:15:47.518254Z INFO herdr::server::headless: client protocol socket listening path=/Users/zamp/.config/herdr/herdr-client.sock +2026-07-24T20:15:47.518289Z INFO herdr::server::headless: herdr server started api_socket=/Users/zamp/.config/herdr/herdr.sock client_socket=/Users/zamp/.config/herdr/herdr-client.sock +2026-07-24T20:15:47.518602Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=8769 +2026-07-24T20:15:47.774119Z INFO herdr::server::headless: client connected client_id=2 cols=216 rows=61 cell_width_px=0 cell_height_px=0 render_encoding=SemanticFrame +2026-07-24T20:15:52.523465Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:16:19.387859Z INFO herdr::pane: agent changed pane=1 previous_agent=None agent=Some(Codex) process=codex pgid=Some(9125) +2026-07-24T20:16:24.558399Z INFO herdr::pane: agent changed pane=1 previous_agent=Some(Codex) agent=None pgid=Some(8794) +2026-07-24T20:16:53.702239Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=2 rows=60 cols=190 scrollback_limit_bytes=10000000 +2026-07-24T20:16:53.707432Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=2 pid=9204 +2026-07-24T20:16:53.707741Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:16:58.718080Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:17:00.098164Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=2 status="ExitStatus { code: 0, signal: None }" +2026-07-24T20:17:00.098404Z INFO herdr::pane: pane session terminated pane=2 pid=9204 signal=Hangup +2026-07-24T20:17:05.103386Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:19:32.845531Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=3 rows=60 cols=190 scrollback_limit_bytes=10000000 +2026-07-24T20:19:32.853251Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=3 pid=9357 +2026-07-24T20:19:32.853741Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w2" pane_id=3 +2026-07-24T20:19:32.853805Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:32.853986Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t1" +2026-07-24T20:19:35.669009Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w1" +2026-07-24T20:19:35.669587Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:19:36.322288Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:36.322643Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t1" +2026-07-24T20:19:36.894143Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w1" +2026-07-24T20:19:36.894634Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:19:40.710704Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:40.711158Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t1" +2026-07-24T20:19:40.885895Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:40.886509Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t1" +2026-07-24T20:19:41.652479Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:41.652930Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t1" +2026-07-24T20:19:44.824051Z INFO herdr::logging: workspace closed event="workspace.close" subsystem="workspace" outcome="ok" workspace_id="w2" +2026-07-24T20:19:44.825788Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=3 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:19:44.846478Z INFO herdr::pane: pane session terminated pane=3 pid=9357 signal=Hangup +2026-07-24T20:19:44.846780Z WARN herdr::app::actions: PaneDied for unknown pane pane=3 +2026-07-24T20:19:49.853275Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:23:03.120646Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:server:reload-config" method="server.reload_config" changes_ui=true +2026-07-24T20:23:03.121645Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:server:reload-config" method="server.reload_config" +2026-07-24T20:27:56.051742Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=4 rows=61 cols=188 scrollback_limit_bytes=10000000 +2026-07-24T20:27:56.059988Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=4 pid=10184 +2026-07-24T20:27:56.060708Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:01.068501Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:28:03.484689Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:03.484817Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.484853Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.484898Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:03.854267Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:03.854376Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.854411Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.854440Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:03.988757Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:03.988858Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.988891Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:03.988918Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.140185Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.140300Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.140337Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.140365Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.219212Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.219322Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.219353Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.219384Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.349179Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.349296Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.349332Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.349361Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.396051Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.396151Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.396179Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.396227Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.512651Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.512742Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.512771Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.512801Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.595812Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.595897Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.595931Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.595960Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:04.736016Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:04.736118Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.736151Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:04.736180Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:05.059772Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:05.059874Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.059910Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.059937Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:05.329296Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:05.329399Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.329433Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.329460Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:05.572759Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:05.572861Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.572894Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:05.572932Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:06.126137Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:06.126241Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:06.126275Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:06.126302Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:06.367841Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:06.367947Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:06.367981Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:06.368007Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:08.880422Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=5 rows=61 cols=94 scrollback_limit_bytes=10000000 +2026-07-24T20:28:08.888439Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=5 pid=10510 +2026-07-24T20:28:08.889393Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t2" +2026-07-24T20:28:10.493117Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:11.439894Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t2" +2026-07-24T20:28:12.083552Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:14.070228Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t2" +2026-07-24T20:28:14.842639Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:16.199704Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:16.199808Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:16.199844Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:16.199875Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:18.846218Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:18.846633Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:18.846675Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:18.846704Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:19.555043Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:19.555154Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:19.555190Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:19.555220Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:19.887077Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:19.887175Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:19.887211Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:19.887240Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:24.892830Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:28:43.487460Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:43.487559Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:43.487593Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:43.487620Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:44.050842Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:44.050945Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.050980Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.051008Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:44.367429Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:44.367515Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.367549Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.367577Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:44.567157Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:44.567267Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.567301Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.567332Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:44.755395Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:44.755487Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.755520Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.755553Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:44.883669Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:44.883761Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.883794Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:44.883821Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:45.109183Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:45.109278Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:45.109314Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:45.109345Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:45.358467Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:45.358568Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:45.358602Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:45.358629Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:47.622638Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=6 rows=60 cols=94 scrollback_limit_bytes=10000000 +2026-07-24T20:28:47.627635Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=6 pid=11042 +2026-07-24T20:28:47.627926Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:51.147656Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=6 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:28:51.167453Z INFO herdr::pane: pane session terminated pane=6 pid=11042 signal=Hangup +2026-07-24T20:28:51.168762Z WARN herdr::app::actions: PaneDied for unknown pane pane=6 +2026-07-24T20:28:52.245707Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:28:52.245804Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:52.245839Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:28:52.245886Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:28:57.255281Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:29:01.690454Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:29:01.690556Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:29:01.690591Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:29:01.690617Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:29:02.356107Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=4 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:29:02.378950Z INFO herdr::pane: pane session terminated pane=4 pid=10184 signal=Hangup +2026-07-24T20:29:02.380356Z WARN herdr::app::actions: PaneDied for unknown pane pane=4 +2026-07-24T20:29:07.384503Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:29:08.117360Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w1" +2026-07-24T20:29:08.117845Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:29:08.118208Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t2" +2026-07-24T20:29:08.121378Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=5 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:29:08.140690Z INFO herdr::pane: pane session terminated pane=5 pid=10510 signal=Hangup +2026-07-24T20:29:08.142026Z WARN herdr::app::actions: PaneDied for unknown pane pane=5 +2026-07-24T20:29:13.144294Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:29:14.368664Z INFO herdr::server::headless: client resize client_id=2 cols=216 rows=63 cell_width_px=0 cell_height_px=0 +2026-07-24T20:29:55.026465Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=7 rows=63 cols=188 scrollback_limit_bytes=10000000 +2026-07-24T20:29:55.034370Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=7 pid=11194 +2026-07-24T20:29:55.035200Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:30:00.260708Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=7 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:30:00.283775Z INFO herdr::pane: pane session terminated pane=7 pid=11194 signal=Hangup +2026-07-24T20:30:00.285320Z WARN herdr::app::actions: PaneDied for unknown pane pane=7 +2026-07-24T20:30:05.292500Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:37:31.320771Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=8 rows=63 cols=188 scrollback_limit_bytes=10000000 +2026-07-24T20:37:31.323406Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=8 pid=14997 +2026-07-24T20:37:31.323602Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:37:36.330791Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:39:29.429357Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:39:29.429466Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:39:29.429500Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:39:29.429532Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:39:34.437047Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:39:49.443605Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:39:49.443713Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:39:49.443751Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:39:49.443780Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:39:50.986759Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=8 status="ExitStatus { code: 1, signal: None }" +2026-07-24T20:39:51.009203Z INFO herdr::pane: pane session terminated pane=8 pid=14997 signal=Hangup +2026-07-24T20:39:51.010458Z WARN herdr::app::actions: PaneDied for unknown pane pane=8 +2026-07-24T20:39:56.013104Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:45:47.507377Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-24T20:48:10.440858Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=9 rows=63 cols=188 scrollback_limit_bytes=10000000 +2026-07-24T20:48:10.448173Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=9 pid=18572 +2026-07-24T20:48:10.448699Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T20:48:10.448952Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T20:48:15.460482Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:48:16.971538Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:48:21.981885Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:48:25.785934Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=10 rows=62 cols=188 scrollback_limit_bytes=10000000 +2026-07-24T20:48:25.794666Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=10 pid=18695 +2026-07-24T20:48:25.795478Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:48:27.872492Z INFO herdr::pane: agent changed pane=10 previous_agent=None agent=Some(Codex) process=codex pgid=Some(18777) +2026-07-24T20:48:30.805081Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:48:45.389622Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:48:45.389730Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:48:45.389768Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:48:45.389796Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:48:50.398066Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:49:29.975339Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:49:29.975447Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:49:29.975483Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:49:29.975513Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:49:34.981613Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:51:22.915523Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:51:22.915626Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:22.915659Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:22.915691Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:51:23.760451Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:51:23.760555Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:23.760592Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:23.760621Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:51:24.474175Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:51:24.474303Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:24.474344Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:24.474376Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:51:24.979965Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:51:24.980079Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:24.980112Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:51:24.980139Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:51:29.985734Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:57:01.048251Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:57:06.060548Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:57:46.869917Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T20:57:46.870032Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:57:46.870089Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:57:46.870122Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T20:57:51.877083Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T20:57:53.710265Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T20:57:58.719990Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:00:26.212153Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:02:39.786251Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:06:20.511195Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:06:21.779877Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:06:26.789479Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:06:49.204781Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:06:54.213071Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:07:02.222091Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:07:07.230383Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:07:18.660887Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:07:22.499473Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:07:27.332116Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=11 rows=62 cols=212 scrollback_limit_bytes=10000000 +2026-07-24T21:07:27.339837Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=11 pid=20255 +2026-07-24T21:07:27.340448Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:07:32.347272Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:07:43.146396Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:07:44.572325Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:07:44.572390Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:07:44.572399Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:07:44.572426Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:07:49.580441Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:08:14.437071Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:08:15.777810Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:08:15.777886Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:08:15.777902Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:08:15.777937Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:08:20.781601Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:11:07.825989Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:11:11.140914Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:11:11.140989Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:11:11.141005Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:11:11.141033Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:11:16.146935Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:12:09.439364Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:12:14.443736Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:12:18.452091Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:12:20.209770Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:12:25.220470Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:14:02.865464Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:14:07.873823Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:14:37.745692Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:14:37.745757Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:14:37.745767Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:14:37.745797Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:14:42.751030Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:15:47.514610Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-24T21:19:07.660869Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:19:12.668963Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:26:03.469834Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:26:08.480447Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:27:50.532495Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:27:55.544999Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:32:14.768807Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:32:14.768872Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:32:14.768887Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:32:14.768917Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:32:19.777694Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:33:04.426832Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:33:09.439697Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:36:01.598714Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T21:36:06.613697Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:38:49.966643Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:38:51.093872Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:38:51.093930Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:38:51.093942Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:38:51.093969Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:38:56.103494Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:41:27.064655Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:resize" method="pane.resize" changes_ui=true +2026-07-24T21:41:27.064757Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:resize" method="pane.resize" +2026-07-24T21:41:28.837157Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:resize" method="pane.resize" changes_ui=true +2026-07-24T21:41:28.837256Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:resize" method="pane.resize" +2026-07-24T21:41:29.581641Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:41:29.581693Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:41:29.581705Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:41:29.581730Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:41:34.587134Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:43:30.874776Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:43:35.889111Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:45:47.516638Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-24T21:45:54.626666Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:45:54.626748Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:45:54.626762Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:45:54.626793Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:45:59.635321Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:49:08.994570Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:49:08.994650Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:49:08.994679Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:49:08.994712Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:49:14.002985Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:52:13.003183Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:52:13.003242Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:52:13.003253Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:52:13.003277Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:52:18.012305Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:53:26.751038Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:53:26.751109Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:53:26.751124Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:53:26.751152Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:53:31.757250Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T21:57:46.030420Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T21:57:46.030493Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:57:46.030507Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T21:57:46.030537Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T21:57:51.039160Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:01:40.805870Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:01:45.817930Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:01:54.710020Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T22:01:54.710073Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:01:54.710082Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:01:54.710105Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T22:01:59.529421Z INFO herdr::app::actions: copied selection to clipboard +2026-07-24T22:01:59.720718Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:02:00.407938Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:02:05.417311Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:04:30.290820Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:04:35.301373Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:05:06.520174Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T22:05:06.520243Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:05:06.520256Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:05:06.520284Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T22:05:11.530318Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:05:19.850836Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T22:05:24.858198Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:05:32.018640Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:05:37.029531Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:05:48.426692Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T22:05:53.436963Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:06:01.164550Z INFO herdr::app::actions: copied selection to clipboard +2026-07-24T22:06:02.103445Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:06:03.184718Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:06:08.197923Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:07:10.241276Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T22:07:10.241340Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:07:10.241349Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:07:10.241380Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T22:07:15.244260Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:07:33.896022Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-24T22:07:36.790544Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:07:41.800511Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-24T22:09:20.154594Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-24T22:09:20.154659Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:09:20.154669Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-24T22:09:20.154699Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-24T22:09:25.163183Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T03:20:52.664554Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T08:33:40.425668Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:33:40.425733Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:33:40.425746Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:33:40.425779Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:33:45.439002Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:34:14.220647Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:34:14.221769Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:34:14.221794Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:34:14.221831Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:34:18.468246Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:34:18.468321Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:34:18.468338Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:34:18.468368Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:34:23.476684Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:35:23.015877Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:35:28.032512Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:37:58.545558Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:38:02.481869Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:38:02.482722Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:38:02.482738Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:38:02.482770Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:38:07.492669Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:38:10.772436Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:38:15.780131Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:41:53.205279Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:41:58.222280Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:42:00.144382Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:42:00.144449Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:42:00.144458Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:42:00.144487Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:42:05.151595Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:47:43.263886Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=12 rows=62 cols=106 scrollback_limit_bytes=10000000 +2026-07-25T08:47:43.269625Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=12 pid=28595 +2026-07-25T08:47:43.269860Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:47:48.277627Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:47:50.558718Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:47:50.558780Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:47:50.558791Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:47:50.558825Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:47:55.564254Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:48:08.070987Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:48:08.071053Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:08.071065Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:08.071093Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:48:09.887251Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:48:09.887303Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:09.887313Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:09.887345Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:48:14.891509Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:48:23.083718Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:48:23.083794Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:23.083809Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:23.083840Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:48:28.089241Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:48:39.175711Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:44.186460Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:48:47.336661Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:52.346317Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:48:59.110141Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:48:59.110210Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:59.110224Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:48:59.110254Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:49:04.117759Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:49:19.315625Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:49:19.315717Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:49:19.315735Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:49:19.315771Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:49:24.322972Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:56:00.518594Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:56:00.518693Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:56:00.518713Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:56:00.518746Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:56:05.523856Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T08:56:13.147947Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T08:57:22.557892Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:57:22.557968Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:57:22.557982Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:57:22.558016Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:57:23.381280Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T08:57:23.381337Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:57:23.381347Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T08:57:23.381377Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T08:57:28.384413Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:01:14.040058Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=13 rows=31 cols=106 scrollback_limit_bytes=10000000 +2026-07-25T09:01:14.049736Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=13 pid=29177 +2026-07-25T09:01:14.050307Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:01:14.050404Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:01:17.569986Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:01:21.332959Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=12 status="ExitStatus { code: 1, signal: None }" +2026-07-25T09:01:21.352047Z INFO herdr::pane: pane session terminated pane=12 pid=28595 signal=Hangup +2026-07-25T09:01:21.353403Z WARN herdr::app::actions: PaneDied for unknown pane pane=12 +2026-07-25T09:01:22.456339Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:01:27.464643Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:04:38.947773Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:04:43.958494Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:04:44.920527Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-25T09:04:46.791110Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:04:49.847732Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:04:51.464113Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:04:56.474945Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:05:05.746218Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:05:07.638231Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:05:12.645021Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:06:29.197143Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=14 rows=62 cols=212 scrollback_limit_bytes=10000000 +2026-07-25T09:06:29.204615Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=14 pid=29271 +2026-07-25T09:06:29.205045Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:06:34.210446Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:06:37.525010Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:06:37.525078Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:06:37.525096Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:06:37.525125Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:06:42.533462Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:06:55.876025Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:06:55.876117Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:06:55.876133Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:06:55.876168Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:07:00.881839Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:07:22.906891Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:07:22.906967Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:07:22.906983Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:07:22.907020Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:07:27.910421Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:07:56.330518Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:07:56.330585Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:07:56.330596Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:07:56.330627Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:08:01.340699Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:08:02.853921Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:08:02.853990Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:08:02.854001Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:08:02.854030Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:08:07.862980Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:15:47.482698Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-25T09:15:52.490243Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:17:15.436479Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:17:19.487520Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:17:19.487587Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:17:19.487596Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:17:19.487626Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:17:24.495824Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:19:07.888874Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:09.664094Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:13.805505Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:15.805554Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:20.814809Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:19:37.170473Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:19:37.170556Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:37.170571Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:19:37.170603Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:19:42.177066Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:22:31.663169Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" +2026-07-25T09:22:36.672066Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:23:33.068860Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:23:38.081380Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:26:13.183920Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T09:27:18.062214Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:27:23.072259Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:28:02.470711Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:28:07.480177Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:30:03.106417Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:30:03.106487Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:30:03.106502Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:30:03.106528Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:30:05.528427Z INFO herdr::pane: agent changed pane=14 previous_agent=None agent=Some(Codex) process=codex pgid=Some(30779) +2026-07-25T09:30:08.110643Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:30:11.304221Z INFO herdr::pane: agent changed pane=14 previous_agent=Some(Codex) agent=None pgid=Some(29271) +2026-07-25T09:30:15.845291Z INFO herdr::pane: agent changed pane=14 previous_agent=None agent=Some(Codex) process=codex pgid=Some(30989) +2026-07-25T09:31:36.914954Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:31:41.919085Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T09:32:23.005800Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-25T09:32:23.005867Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:32:23.005876Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" +2026-07-25T09:32:23.005908Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-25T09:32:24.769763Z INFO herdr::pane: agent changed pane=14 previous_agent=Some(Codex) agent=None pgid=Some(29271) +2026-07-25T09:32:25.294189Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=14 status="ExitStatus { code: 1, signal: None }" +2026-07-25T09:32:25.314788Z INFO herdr::pane: pane session terminated pane=14 pid=29271 signal=Hangup +2026-07-25T09:32:25.316478Z WARN herdr::app::actions: PaneDied for unknown pane pane=14 +2026-07-25T09:32:26.951276Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-25T09:32:31.961966Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-25T10:13:30.232263Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T11:16:23.997646Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T11:46:24.000054Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T12:16:24.002870Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-25T21:27:21.050030Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-26T05:41:02.085859Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-26T21:23:23.272165Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T08:17:12.366384Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:focus" method="pane.focus_direction" changes_ui=true +2026-07-27T08:17:12.366679Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-27T08:17:12.366693Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" +2026-07-27T08:17:12.366729Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:focus" method="pane.focus_direction" +2026-07-27T08:17:13.176887Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=10 status="ExitStatus { code: 1, signal: None }" +2026-07-27T08:17:13.195909Z INFO herdr::pane: pane session terminated pane=10 pid=18695 signal=Hangup +2026-07-27T08:17:13.196693Z WARN herdr::app::actions: PaneDied for unknown pane pane=10 +2026-07-27T08:17:14.600139Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=1 status="ExitStatus { code: 1, signal: None }" +2026-07-27T08:17:14.620176Z INFO herdr::pane: pane session terminated pane=1 pid=8794 signal=Hangup +2026-07-27T08:17:14.621568Z WARN herdr::app::actions: PaneDied for unknown pane pane=1 +2026-07-27T08:17:15.556356Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=9 status="ExitStatus { code: 1, signal: None }" +2026-07-27T08:17:15.577929Z INFO herdr::pane: pane session terminated pane=9 pid=18572 signal=Hangup +2026-07-27T08:17:15.579241Z WARN herdr::app::actions: PaneDied for unknown pane pane=9 +2026-07-27T08:17:16.216807Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=11 status="ExitStatus { code: 1, signal: None }" +2026-07-27T08:17:16.238969Z INFO herdr::pane: pane session terminated pane=11 pid=20255 signal=Hangup +2026-07-27T08:17:16.240401Z WARN herdr::app::actions: PaneDied for unknown pane pane=11 +2026-07-27T08:17:16.875575Z INFO herdr::logging: workspace closed event="workspace.close" subsystem="workspace" outcome="ok" workspace_id="w1" +2026-07-27T08:17:16.879383Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=13 status="ExitStatus { code: 1, signal: None }" +2026-07-27T08:17:16.898617Z INFO herdr::pane: pane session terminated pane=13 pid=29177 signal=Hangup +2026-07-27T08:17:16.900560Z WARN herdr::app::actions: PaneDied for unknown pane pane=13 +2026-07-27T08:17:16.900799Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=15 rows=63 cols=212 scrollback_limit_bytes=10000000 +2026-07-27T08:17:16.910328Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=15 pid=37908 +2026-07-27T08:17:16.910899Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w3" pane_id=15 +2026-07-27T08:17:16.910964Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w3" +2026-07-27T08:17:16.910994Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w3" tab_id="w3:t1" +2026-07-27T08:17:19.902825Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=15 status="ExitStatus { code: 0, signal: None }" +2026-07-27T08:17:19.903131Z INFO herdr::pane: pane session terminated pane=15 pid=37908 signal=Hangup +2026-07-27T08:17:19.903230Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=16 rows=63 cols=212 scrollback_limit_bytes=10000000 +2026-07-27T08:17:19.906898Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=16 pid=37972 +2026-07-27T08:17:19.907051Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w4" pane_id=16 +2026-07-27T08:17:19.907058Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w4" +2026-07-27T08:17:19.907064Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w4" tab_id="w4:t1" +2026-07-27T08:17:21.725815Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=16 status="ExitStatus { code: 0, signal: None }" +2026-07-27T08:17:21.726361Z INFO herdr::pane: pane session terminated pane=16 pid=37972 signal=Hangup +2026-07-27T08:17:21.726515Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=17 rows=63 cols=212 scrollback_limit_bytes=10000000 +2026-07-27T08:17:21.732198Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=17 pid=38019 +2026-07-27T08:17:21.732424Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w5" pane_id=17 +2026-07-27T08:17:21.732435Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w5" +2026-07-27T08:17:21.732443Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w5" tab_id="w5:t1" +2026-07-27T08:17:26.742162Z INFO herdr::logging: session saved event="persist.save" subsystem="persist" outcome="ok" path=/Users/zamp/.config/herdr/session.json workspaces=1 +2026-07-27T08:17:29.704424Z INFO herdr::server::headless: client detach requested via keybind client_id=2 +2026-07-27T08:17:29.709419Z INFO herdr::server::headless: client disconnected client_id=2 +2026-07-27T08:22:06.703297Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T09:32:36.426244Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T10:56:16.883118Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T11:26:16.857274Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T11:56:16.872766Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T12:26:16.877180Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-27T22:00:33.686857Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T07:19:40.075642Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T07:49:40.084916Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T08:19:40.096188Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T08:49:40.089294Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T09:19:40.067308Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T09:49:40.090768Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T10:54:13.991411Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T11:32:26.492804Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T12:02:26.482344Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T12:32:26.474783Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T13:02:26.488631Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T13:32:26.464326Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T14:02:26.496166Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T15:54:34.086319Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T18:11:57.017781Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T18:44:07.003930Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T19:14:06.972607Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T19:44:06.959635Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T20:14:06.999784Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T20:44:07.011166Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" +2026-07-28T21:34:00.934106Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" diff --git a/dot_config/herdr/plugins.json b/dot_config/herdr/plugins.json new file mode 100644 index 0000000..31d5f40 --- /dev/null +++ b/dot_config/herdr/plugins.json @@ -0,0 +1,122 @@ +[ + { + "plugin_id": "herdr-splits", + "name": "Herdr Splits", + "version": "0.5.1", + "min_herdr_version": "0.7.0", + "description": "Seamless navigation and resizing between Herdr panes and Neovim splits", + "manifest_path": "/Users/zamp/.config/herdr/plugins/github/herdr-splits-d880c4924f2b/herdr-plugin.toml", + "plugin_root": "/Users/zamp/.config/herdr/plugins/github/herdr-splits-d880c4924f2b", + "enabled": true, + "platforms": [ + "linux", + "macos" + ], + "actions": [ + { + "id": "nav-down", + "title": "Navigate down (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-nav.sh", + "down" + ] + }, + { + "id": "nav-left", + "title": "Navigate left (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-nav.sh", + "left" + ] + }, + { + "id": "nav-right", + "title": "Navigate right (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-nav.sh", + "right" + ] + }, + { + "id": "nav-up", + "title": "Navigate up (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-nav.sh", + "up" + ] + }, + { + "id": "resize-down", + "title": "Resize down (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-resize.sh", + "down" + ] + }, + { + "id": "resize-left", + "title": "Resize left (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-resize.sh", + "left" + ] + }, + { + "id": "resize-right", + "title": "Resize right (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-resize.sh", + "right" + ] + }, + { + "id": "resize-up", + "title": "Resize up (Neovim/Herder)", + "contexts": [ + "global" + ], + "command": [ + "bash", + "scripts/herdr-resize.sh", + "up" + ] + } + ], + "source": { + "kind": "github", + "owner": "lmilojevicc", + "repo": "herdr-splits.nvim", + "resolved_commit": "107273e004e4f7ef07f13c83164d2cb2c51df65d", + "managed_path": "/Users/zamp/.config/herdr/plugins/github/herdr-splits-d880c4924f2b", + "installed_unix_ms": 1784924593009 + } + } +] \ No newline at end of file diff --git a/dot_config/herdr/plugins/config/herdr-splits/herdr-splits.conf b/dot_config/herdr/plugins/config/herdr-splits/herdr-splits.conf new file mode 100644 index 0000000..c16b7dc --- /dev/null +++ b/dot_config/herdr/plugins/config/herdr-splits/herdr-splits.conf @@ -0,0 +1,15 @@ +# herdr-splits-generated-v1 +# herdr-splits plugin config — GENERATED by herdr-splits.nvim setup(). +# Regenerated from defaults + setup() opts on every setup(); do not edit by hand. +# A headerless/legacy conf is adopted once on migration (see the startup +# notice), then overwritten thereafter — remove an opt in setup() to revert. +nav_key_left=ctrl+h +nav_key_down=ctrl+j +nav_key_up=ctrl+k +nav_key_right=ctrl+l +resize_key_left=alt+h +resize_key_down=alt+j +resize_key_up=alt+k +resize_key_right=alt+l +unzoom_on_nav=true +nav_at_edge=wrap diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/CONTRIBUTING.md b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/CONTRIBUTING.md new file mode 100644 index 0000000..3cdd672 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/CONTRIBUTING.md @@ -0,0 +1,126 @@ +# Contributing to herdr-splits.nvim + +Thanks for your interest in improving herdr-splits.nvim! This guide covers how +to set up a local development environment and submit changes. + +herdr-splits.nvim is a Neovim plugin (Lua) that also ships a Herdr-side plugin +(the bash scripts under `scripts/` plus the `herdr-plugin.toml` manifest). The +two halves cooperate — please consider both when making changes to navigation +or resize behaviour. + +## Requirements + +- Neovim ≥ 0.10 +- [Herdr](https://herdr.dev) ≥ 0.7.0 (only needed to exercise cross-pane + behaviour; you can develop the Lua side without a running Herdr session) +- A Herdr-compatible terminal with Option/Alt enabled (see the README's macOS + notes if you're on macOS) + +## Repository layout + +``` +plugin/herdr-splits.lua Neovim entrypoint +lua/herdr-splits/ Neovim plugin source + init.lua setup() + public API + nav.lua resize.lua navigation and resizing + win.lua float / sidebar classification + sync.lua Herdr-side script syncing + config.lua herdr.lua config + core + health.lua :checkhealth herdr-splits +scripts/herdr-nav.sh Herdr-side navigation action +scripts/herdr-resize.sh Herdr-side resize action +scripts/minimal_init.lua Isolated Neovim test bootstrap +tests/ mini.test suites +Makefile Local and CI validation commands +herdr-plugin.toml Herdr plugin manifest (actions + metadata) +``` + +## Local development setup + +### Neovim side + +Point your plugin manager at a local clone instead of the published repo: + +```lua +-- lazy.nvim +{ + dir = '~/Projects/herdr-splits', + cond = vim.env.HERDR_ENV == '1', + config = function() + require('herdr-splits').setup({}) + end, +} +``` + +Changes to files under `lua/` take effect after `:source` or a Neovim restart +(unless your plugin manager hot-reloads). + +### Herdr side + +Link the clone so Herdr loads its scripts from your working tree: + +```bash +herdr plugin link /path/to/herdr-splits +``` + +After changing `scripts/*.sh` or `herdr-plugin.toml`, run +`herdr server reload-config` to pick them up. + +## Coding style + +- Match the surrounding Lua style — indentation, quoting, and table layout in + the file you're editing are the source of truth. +- Run [StyLua](https://github.com/JohnnyMorganz/StyLua) if you use it, but note + that no `.stylua.toml` is committed yet; when one is added it will be the + canonical formatter config. +- Run [luacheck](https://github.com/mpeterv/luacheck) where practical to catch + undefined globals and unused variables. +- Keep the bash scripts POSIX-friendly where possible; they run on both Linux + and macOS as declared in `herdr-plugin.toml`. + +## Validation + +The Make targets initialize and update the pinned test dependency automatically. +To prepare it separately, run `make deps`. + +Run the automated checks with: + +```bash +make test # isolated mini.test suite in headless Neovim +make check-shell # bash syntax checks for both shipped scripts +make check # all of the above +``` + +Cross-pane behaviour still requires manual validation in a Herdr session: + +1. Load the plugin in Neovim inside a Herdr session (`HERDR_ENV=1`). +2. Run `:checkhealth herdr-splits` and confirm it reports no failures. +3. Exercise navigation (``) and resizing (``) across both + Neovim splits and Herdr panes, including the edge cases you changed: + - `at_edge` behaviour (`wrap` / `stop` / `split` / custom) + - count prefixes (e.g. `3`) + - floating windows and embedded-sidebar floats + - auto-unzoom when crossing into a sibling Herdr pane + +## Submitting changes + +1. Fork the repo and create a branch from `main`. +2. Make focused commits with descriptive messages. +3. Open a pull request against `main` and fill in the PR template — include the + manual validation steps you ran. +4. If your change affects user-facing behaviour or config, update `README.md` + and, for manifest-level changes, `herdr-plugin.toml`. + +Keep PRs scoped to one concern. Large changes are easier to review when split +into smaller, independently understandable commits. + +## Reporting bugs and ideas + +Use the GitHub issue templates (bug report / feature request). Include the +output of `nvim --version`, your plugin manager, the Herdr version +(`herdr --version`), and a minimal reproduction. + +## License + +By contributing you agree that your contributions are licensed under the MIT +License, the same terms as the rest of the project (see `LICENSE`). diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/LICENSE b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/LICENSE new file mode 100644 index 0000000..511d507 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Luka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/Makefile b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/Makefile new file mode 100644 index 0000000..d8a3be9 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/Makefile @@ -0,0 +1,21 @@ +ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +NVIM ?= nvim + +.PHONY: deps test check-shell check + +deps: + @if git -C "$(ROOT)" ls-files --error-unmatch deps/mini.test >/dev/null 2>&1; then \ + git -C "$(ROOT)" submodule update --init --depth 1 --checkout deps/mini.test; \ + else \ + test -f "$(ROOT)/deps/mini.test/lua/mini/test.lua"; \ + fi + +test: deps + cd "$(ROOT)" && "$(NVIM)" --headless --noplugin -i NONE \ + -u "$(ROOT)/scripts/minimal_init.lua" -c "lua MiniTest.run()" + +check-shell: + bash -n "$(ROOT)/scripts/herdr-nav.sh" + bash -n "$(ROOT)/scripts/herdr-resize.sh" + +check: check-shell test diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/README.md b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/README.md new file mode 100644 index 0000000..7f9cd53 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/README.md @@ -0,0 +1,445 @@ +# herdr-splits.nvim + +Seamless navigation and resizing between Neovim splits and [Herdr](https://herdr.dev) panes. Makes Herdr terminal splits behave like native Neovim windows — move and resize as if they were all part of the same editor. + +Inspired by [smart-splits.nvim](https://github.com/mrjones2014/smart-splits.nvim), ported to Herdr's CLI. + +## Features + +- **Seamless navigation**: Same keys work in Neovim and plain shells — Herdr forwards them to Neovim when appropriate, Neovim delegates to Herdr at edges. +- **Seamless resizing**: `` resizes Neovim splits natively, delegates to Herdr when a window fills the terminal. +- **at_edge behaviours**: `wrap` (default), `stop`, `split`, or a custom function. +- **Plugin-aware**: Ignores snacks/neo-tree/dadbod-ui/aerial sidebars and embedded floats (zindex < 50) by default — your keybinds never get trapped inside a picker. +- **Smart unzoom + wrap-around**: Auto-unzooms when leaving a zoomed pane. At a layout edge, navigation wraps to the opposite side — crossing into a sibling Herdr pane when one exists (e.g. `pane | nvim` → `ctrl+l` wraps to the pane), otherwise wrapping within Neovim. Herdr-pane navigation also wraps around at edges (past the last pane → the first). Does not unzoom when moving between Neovim splits in one pane. +- **Count prefix support**: `3` moves three splits left; `5` resizes five steps right. + +## Requirements + +- Neovim ≥ 0.10 +- [Herdr](https://herdr.dev) ≥ 0.7.0 (for plugin actions) + +## Installation + +Three steps — install on both the Herdr side and the Neovim side, then add keybinds. + +### 1. Install the Herdr plugin + +This provides the `nav-*` and `resize-*` actions that Herdr keybinds invoke. + +```bash +# From GitHub (once published): +herdr plugin install lmilojevicc/herdr-splits.nvim + +# Local development: +herdr plugin link /path/to/herdr-splits +``` + +### 2. Install the Neovim plugin + +## lazy.nvim + +```lua +{ + 'lmilojevicc/herdr-splits.nvim', + -- For local development, swap the repo line for `dir = '/path/to/herdr-splits'` + -- (see "Local development" below). + cond = vim.env.HERDR_ENV == '1', + event = 'VeryLazy', + -- Optional: auto-sync the Herdr-side scripts when lazy updates this plugin. + -- Requires `auto_sync_herdr = true` in setup() below to take effect. + -- build = 'lua require("herdr-splits").sync_herdr()', + config = function() + require('herdr-splits').setup({ + -- Defaults shown. All fields optional. + default_amount = 0.03, -- Herdr resize ratio + neovim_amount = 3, -- Neovim resize cells + at_edge = 'wrap', -- 'wrap' | 'stop' | 'split' | function + ignored_buftypes = { 'nofile', 'quickfix', 'prompt', 'help', 'terminal' }, + ignored_filetypes = { + 'NvimTree', + -- sidebars + 'neo-tree', + 'snacks_dashboard', + 'snacks_explorer', + 'snacks_picker', + -- DB / REPL / data sidebars + 'dadbod-ui', + 'dbout', + -- outlines / symbols + 'aerial', + 'Outline', + -- diagnostics / quick lists + 'Trouble', + 'quickfix', + }, + move_cursor_same_row = false, + herdr_bin = nil, -- auto-detected from HERDR_BIN_PATH + floating_zindex_max = 50, -- floats with zindex < this are treated as embedded sidebars + ignore_previewwindows = false, -- opt-in: also treat previewwindow windows (e.g. .dbout) as sidebars + -- auto_sync_herdr = true, -- opt-in: sync Herdr-side scripts on update + -- Managed keys — written to the generated herdr-splits.conf so the + -- Herdr-side scripts agree. Pass Neovim notation (e.g. ). + nav_keys = { left = '', down = '', up = '', right = '' }, + resize_keys = { left = '', down = '', up = '', right = '' }, + unzoom_on_nav = true, -- auto-unzoom when navigating away from a zoomed pane + nav_at_edge = 'wrap', -- 'wrap' | 'stop' — Herdr pane-boundary wrap (distinct from at_edge) + }) + end, + keys = { + { '', function() require('herdr-splits').move_cursor_left() end, desc = 'Navigate left' }, + { '', function() require('herdr-splits').move_cursor_down() end, desc = 'Navigate down' }, + { '', function() require('herdr-splits').move_cursor_up() end, desc = 'Navigate up' }, + { '', function() require('herdr-splits').move_cursor_right() end, desc = 'Navigate right' }, + { '', function() require('herdr-splits').resize_left() end, desc = 'Resize left' }, + { '', function() require('herdr-splits').resize_down() end, desc = 'Resize down' }, + { '', function() require('herdr-splits').resize_up() end, desc = 'Resize up' }, + { '', function() require('herdr-splits').resize_right() end, desc = 'Resize right' }, + }, +} +``` + +## packer.nvim + +```lua +use { + 'lmilojevicc/herdr-splits.nvim', + config = function() + require('herdr-splits').setup() + end, +} +``` + +### 3. Add keybinds to Herdr config + +Add these to `~/.config/herdr/config.toml`, then run `herdr server reload-config`: + +```toml +[[keys.command]] +key = "ctrl+h" +type = "plugin_action" +command = "herdr-splits.nav-left" + +[[keys.command]] +key = "ctrl+j" +type = "plugin_action" +command = "herdr-splits.nav-down" + +[[keys.command]] +key = "ctrl+k" +type = "plugin_action" +command = "herdr-splits.nav-up" + +[[keys.command]] +key = "ctrl+l" +type = "plugin_action" +command = "herdr-splits.nav-right" + +[[keys.command]] +key = "alt+h" +type = "plugin_action" +command = "herdr-splits.resize-left" + +[[keys.command]] +key = "alt+j" +type = "plugin_action" +command = "herdr-splits.resize-down" + +[[keys.command]] +key = "alt+k" +type = "plugin_action" +command = "herdr-splits.resize-up" + +[[keys.command]] +key = "alt+l" +type = "plugin_action" +command = "herdr-splits.resize-right" +``` + +> **Note for macOS**: Terminals treat Option as a special character modifier by default. You need to set Option = Alt/Meta: +> +> - **Ghostty**: `macos-option-as-alt = true` +> - **Alacritty**: `option_as_alt = "Both"` +> - **kitty**: `macos_option_as_alt yes` +> - **iTerm**: Profiles → Keys → Left Option key → Esc+ +> +> If you're using [smart-splits.nvim](https://github.com/mrjones2014/smart-splits.nvim) for tmux, add `cond = vim.env.HERDR_ENV ~= '1'` to its spec so the two plugins don't conflict. + +### Auto-unzoom and wrap-around + +**Auto-unzoom.** When you navigate away from a **zoomed** pane, herdr-splits +unzooms first so the destination is visible — whether you're crossing into a +sibling Herdr pane or wrapping at a layout edge. It does not unzoom when +moving between Neovim splits inside the same pane. + +**Wrap-around.** When you reach a layout edge in the direction you pressed +(nowhere further to go that way), the default `wrap` behaviour continues on +the opposite side: + +- If a Herdr pane sits on the opposite side, focus crosses into it — so + `pane | nvim` wraps `ctrl+l` back to the pane, and + `pane | nvim win1 | win2` wraps from win2 to the pane. With `nav_at_edge=stop` + (see below) this cross-to-opposite-pane is suppressed and the wrap stays + within Neovim instead. +- Otherwise it wraps within Neovim (smart-splits default). +- Between plain Herdr panes, navigation wraps around at edges too (past the + last pane → the first). +- Wrap works even when you're on a sidebar (dbui, neo-tree, quickfix, ...), + so you can leave it at an edge; only embedded floating overlays are gated. + +**To disable auto-unzoom entirely**, pass `unzoom_on_nav = false` to `setup()`: + +```lua +require('herdr-splits').setup({ unzoom_on_nav = false }) +``` + +These options are written to a generated `herdr-splits.conf` (default +`~/.config/herdr/plugins/config/herdr-splits/herdr-splits.conf`; print the +path with `herdr plugin config-dir herdr-splits`) so the Herdr-side scripts +agree — you configure them once, from `setup()`. **Do not hand-edit that +file:** it is regenerated on every `setup()`. Any values already present are +adopted once the first time `setup()` runs after an upgrade, then +overwritten thereafter. Set `HERDR_SPLITS_CONFIG` to override the path on +both sides. + +**To stop at layout edges instead of wrapping**, pass `nav_at_edge = 'stop'`: + +```lua +require('herdr-splits').setup({ nav_at_edge = 'stop' }) +``` + +This single switch controls wrap-across-boundary on **both** sides: + +- **Plain Herdr panes:** `wrap` (the default) wraps to the opposite pane at an + edge; `stop` halts. +- **Neovim edge wrap:** when Neovim's `at_edge='wrap'`, `wrap` (the default) + lets the wrap cross to the Herdr pane on the opposite side (e.g. `ctrl+l` from + the last nvim split lands on the pane); `stop` keeps the wrap within Neovim + (e.g. `ctrl+l` from the last split cycles to the first split instead of + leaving Neovim). `at_edge='stop'` halts regardless of `nav_at_edge`. + +Note: `at_edge` (Neovim window-edge behaviour: `wrap`/`stop`/`split`/function) +and `nav_at_edge` (Herdr pane-boundary wrap: `wrap`/`stop`) are two different +things despite the shared name — `at_edge` is Neovim-side only and is not +written to the conf. + +Note: when a pane is zoomed and `unzoom_on_nav=false`, the edge flags can't be +trusted so `stop` can't be detected on the plain-pane side — navigation proceeds +in the requested direction in that case. With the default `unzoom_on_nav=true`, +pressing toward an edge while zoomed first unzooms the pane (so the edge can be +detected) and then halts — the pane unzooms even though focus doesn't move. + +**To remap the keys forwarded into Neovim**, pass `nav_keys` / `resize_keys` +to `setup()` in Neovim notation (e.g. ``). Override only the +directions you rebind; the rest keep their defaults. By default the scripts +forward `ctrl+h/j/k/l` for navigation and `alt+h/j/k/l` for resizing — the +chords the documented Neovim keymaps bind: + +```lua +require('herdr-splits').setup({ + nav_keys = { left = '', down = '', up = '', right = '' }, + resize_keys = { left = '', down = '', up = '', right = '' }, +}) +``` + +- Pass Neovim notation in `setup()` (``, ``); it is translated to the Herdr chord notation (`ctrl+h`, `alt+left`) the generated conf writes and `herdr pane send-keys` accepts. +- Any direction left unset keeps its default. +- The chord forwarded into Neovim must match the Neovim keymap that catches it: `` in `setup()` is the same key `alt+left` the script forwards, so keep your `vim.keymap.set` in sync with `nav_keys`. + +> **Eager load required for custom keys.** The generated conf is written by +> `setup()`, so custom `nav_keys`/`resize_keys` need `setup()` to run eagerly +> or at `VeryLazy` — do **not** lazy-load this plugin *only* from the same +> custom key mappings it must publish. Otherwise the conf carrying your custom +> chords isn't written before Herdr forwards them, and those keys can't reach +> Neovim to trigger the lazy load. The recommended spec above already uses +> `event = 'VeryLazy'`. + +## Local development + +Both sides load straight from your clone, so edits take effect immediately +(on the Herdr side; Neovim picks them up on reload/restart). + +1. **Link the Herdr plugin** to your local repo: + + ```bash + herdr plugin link /path/to/herdr-splits + ``` + +2. **Point lazy.nvim at the same path** — change the first line of the spec + from the GitHub repo to `dir`: + + ```lua + { + dir = '/path/to/herdr-splits', + cond = vim.env.HERDR_ENV == '1', + -- ...rest of the spec unchanged + } + ``` + +Run `herdr plugin list` to confirm it shows `herdr-splits ... [local:/...]`. +To switch back to the published version: `herdr plugin unlink herdr-splits`, +then `herdr plugin install lmilojevicc/herdr-splits.nvim`, and revert the +lazy spec to `'lmilojevicc/herdr-splits.nvim'`. + +## Lua API + +```lua +require('herdr-splits').setup(opts) + +-- Resize (amount defaults to config.default_amount, multiplied by count prefix) +require('herdr-splits').resize_left(amount) +require('herdr-splits').resize_down(amount) +require('herdr-splits').resize_up(amount) +require('herdr-splits').resize_right(amount) + +-- Navigate (opts can override same_row and at_edge per-call) +require('herdr-splits').move_cursor_left({ same_row = true, at_edge = 'stop' }) +require('herdr-splits').move_cursor_down(opts) +require('herdr-splits').move_cursor_up(opts) +require('herdr-splits').move_cursor_right(opts) +``` + +## Compatibility + +`herdr-splits.nvim` ships with an opinionated default `ignored_filetypes` list so your resize keybinds don't get trapped inside common sidebars and pickers. These filetypes are treated as sidebars: `split`-at-edge and Herdr resize delegation are skipped from them, and the flag is passed to a custom `at_edge` function as `is_sidebar`. `wrap`, however, still works from a sidebar so you can leave it (e.g. `ctrl+l` on a `dadbod-ui` drawer wraps to the opposite side); only embedded floating overlays are gated. + +The default `ignored_filetypes` covers: + +- `NvimTree` — `nvim-tree/nvim-tree.lua` default file tree (`buftype=nofile`, `winfixwidth`). +- `neo-tree` — `nvim-neo-tree/neo-tree.nvim` LazyVim default (`winfixwidth` sidebar). +- `snacks_dashboard` / `snacks_explorer` / `snacks_picker` — `folke/snacks` sidebar/picker windows (the float case is handled by `is_embedded_floating_window` below). +- `dadbod-ui` / `dbout` — `kristijanhusak/vim-dadbod-ui` drawer and `tpope/vim-dadbod` preview result buffer. +- `aerial` / `Outline` — code-outline sidebars. +- `Trouble` / `quickfix` — diagnostics and quickfix lists. + +The default `ignored_buftypes` covers `nofile`, `quickfix`, `prompt`, `help`, and `terminal`. + +**Embedded floating windows** (snacks explorer in float mode, neo-tree in float mode, aerial in float mode) are detected via the `zindex < floating_zindex_max` heuristic (default threshold 50, Neovim's default float zindex) and treated as sidebars for navigation/resize decisions. Override the threshold via the `floating_zindex_max` config field. + +**Add your own at runtime** from any `VeryLazy` autocmd: + +```lua +require('herdr-splits').add_ignored_filetype('lazy') +require('herdr-splits').add_ignored_buftype('help') +``` + +**Debug with `:checkhealth herdr-splits`** (Neovim ≥ 0.10) to see which filetypes/buftypes are ignored, whether Herdr is in session, and whether the current window is classified as a sidebar. + +If you want to **completely replace** the default list (e.g. to opt out of one of the entries), note that `vim.tbl_deep_extend('force', M, opts)` **concatenates** the `ignored_filetypes` / `ignored_buftypes` tables you pass; you keep what you pass AND the defaults are appended. To opt out, assign directly after `setup()`: + +```lua +require('herdr-splits').setup({ ignored_filetypes = {} }) -- inherit nothing +require('herdr-splits').add_ignored_filetype('mything') -- then add back what you want +``` + +## How It Works + +Two sides cooperate for seamless two-way navigation: + +```text +You press C-h in a Herdr pane: + + ┌─ Herdr intercepts the key (plugin_action keybind) + │ + ├─ Is the focused pane running Neovim? + │ │ + │ ├─ YES → forward "ctrl+h" into that pane + │ │ └─ Neovim plugin receives it + │ │ ├─ Has a window to the left? → wincmd h + │ │ └─ At Neovim edge? → herdr pane focus --direction left + │ │ + │ └─ NO (plain shell, etc.) → herdr pane focus --direction left + │ + └─ Result: same keys move you everywhere +``` + +### Navigation + +```text +1. Try moving within Neovim (wincmd h/j/k/l) + ├─ Window changed → done (stayed within Neovim splits) + └─ Window didn't change → at Neovim edge + ├─ Zoomed? → unzoom first (neighbours are reliable once visible) + ├─ Check if Herdr is running (HERDR_ENV=1) + ├─ Check if Herdr pane has a neighbour in this direction + │ └─ Yes → herdr pane focus --direction → done + └─ No (at both Neovim AND Herdr edge) → + apply at_edge behaviour (`wrap` crosses to the opposite Herdr + pane if one exists and nav_at_edge=wrap (the default), else + wraps within Neovim) +``` + +### Resizing + +```text +1. Only one Neovim window in this dimension AND fills terminal? + └─ Yes → herdr pane resize --direction --amount → done +2. Otherwise: + └─ Native Neovim resize with position-aware +/- operators +``` + +### Float and embedded-float classification + +Two predicates classify the current window before any nav/resize decision: + +- `win.is_floating(winid)` — `nvim_win_get_config(winid).relative ~= ''` (Neovim's built-in float check). +- `win.is_embedded_floating_window(winid)` — true only for floats with `zindex < floating_zindex_max` (default 50); matches snacks/neo-tree/aerial float-mode sidebars. + +True floats forward to Herdr; embedded floats behave as sidebars (no movement, no Herdr delegation). + +## Herdr Detection + +Detected automatically through environment variables Herdr injects into every pane: + +- `HERDR_ENV=1` — set when running inside a Herdr-managed pane +- `HERDR_PANE_ID` — the public pane ID (e.g., `w1:p1`) +- `HERDR_BIN_PATH` — path to the herdr binary (for subprocess calls) + +## Comparison with vim-herdr-navigation + +| Feature | vim-herdr-navigation | herdr-splits.nvim | +| --------------------------- | -------------------- | ---------------------------- | +| Navigation | ✓ | ✓ | +| Resizing | ✗ | ✓ | +| at_edge behaviours | ✗ | wrap / stop / split / custom | +| Count prefix | ✗ | ✓ (3 = move 3 left) | +| Auto-unzoom + wrap-around | ✗ | ✓ | +| Floating window handling | ✗ | ✓ | +| Herdr plugin (for keybinds) | ✓ | ✓ | +| Neovim plugin | ✓ | ✓ | + +## Updating + +The Neovim side (lua) auto-updates via lazy.nvim — it pulls `main` on +`LazySync`. The Herdr side (the bash scripts under the Herdr-managed +checkout) does **not** auto-update by default: Herdr v1 has no +`herdr plugin update`, so a managed checkout stays frozen at the commit it +was installed from. + +**Option A — opt into automatic sync (recommended):** set +`auto_sync_herdr = true` in `setup()`. The plugin then reinstalls the +Herdr-managed checkout pinned to the exact commit lazy fetched, so the bash +scripts always match the lua side. It is a no-op when already in sync, when +in local-dev (`plugin link`) mode, or when the `herdr` binary is unavailable. + +For maximum efficiency, also add a `build` hook so the sync fires only when +lazy actually updates the plugin, instead of on every Neovim startup: + +```lua +{ + 'lmilojevicc/herdr-splits.nvim', + build = 'lua require("herdr-splits").sync_herdr()', + config = function() + require('herdr-splits').setup({ auto_sync_herdr = true }) + end, +} +``` + +**Option B — manual:** after an update, re-run +`herdr plugin install lmilojevicc/herdr-splits.nvim` to refresh the +Herdr-side scripts. + +Release notes for each version live in [GitHub Releases](https://github.com/lmilojevicc/herdr-splits.nvim/releases). + +## License + +MIT diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/deps/mini.test/.keep b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/deps/mini.test/.keep new file mode 100644 index 0000000..e69de29 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/FETCH_HEAD b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/FETCH_HEAD new file mode 100644 index 0000000..c02fa3c --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/FETCH_HEAD @@ -0,0 +1 @@ +107273e004e4f7ef07f13c83164d2cb2c51df65d https://github.com/lmilojevicc/herdr-splits.nvim diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/HEAD b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/HEAD new file mode 100644 index 0000000..c367d86 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/HEAD @@ -0,0 +1 @@ +107273e004e4f7ef07f13c83164d2cb2c51df65d diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/config b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/config new file mode 100644 index 0000000..35a60e8 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/config @@ -0,0 +1,10 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = https://github.com/lmilojevicc/herdr-splits.nvim.git + fetch = +refs/heads/*:refs/remotes/origin/* diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/description b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_applypatch-msg.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_applypatch-msg.sample new file mode 100644 index 0000000..a5d7b84 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_commit-msg.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_commit-msg.sample new file mode 100644 index 0000000..b58d118 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_fsmonitor-watchman.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_fsmonitor-watchman.sample new file mode 100644 index 0000000..23e856f --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_fsmonitor-watchman.sample @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + my $last_update_line = ""; + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + $last_update_line = qq[\n"since": $last_update_token,]; + } + my $query = <<" END"; + ["query", "$git_work_tree", {$last_update_line + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_post-update.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_post-update.sample new file mode 100644 index 0000000..ec17ec1 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-applypatch.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-applypatch.sample new file mode 100644 index 0000000..4142082 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-commit.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-commit.sample new file mode 100644 index 0000000..29ed5ee --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff-index --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-merge-commit.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-merge-commit.sample new file mode 100644 index 0000000..399eab1 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-push.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-push.sample new file mode 100644 index 0000000..4ce688d --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-rebase.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-rebase.sample new file mode 100644 index 0000000..6cbef5c --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-receive.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-receive.sample new file mode 100644 index 0000000..a1fd29e --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_prepare-commit-msg.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_prepare-commit-msg.sample new file mode 100644 index 0000000..10fa14c --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_push-to-checkout.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_push-to-checkout.sample new file mode 100644 index 0000000..af5a0c0 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_push-to-checkout.sample @@ -0,0 +1,78 @@ +#!/bin/sh + +# An example hook script to update a checked-out tree on a git push. +# +# This hook is invoked by git-receive-pack(1) when it reacts to git +# push and updates reference(s) in its repository, and when the push +# tries to update the branch that is currently checked out and the +# receive.denyCurrentBranch configuration variable is set to +# updateInstead. +# +# By default, such a push is refused if the working tree and the index +# of the remote repository has any difference from the currently +# checked out commit; when both the working tree and the index match +# the current commit, they are updated to match the newly pushed tip +# of the branch. This hook is to be used to override the default +# behaviour; however the code below reimplements the default behaviour +# as a starting point for convenient modification. +# +# The hook receives the commit with which the tip of the current +# branch is going to be updated: +commit=$1 + +# It can exit with a non-zero status to refuse the push (when it does +# so, it must not modify the index or the working tree). +die () { + echo >&2 "$*" + exit 1 +} + +# Or it can make any necessary changes to the working tree and to the +# index to bring them to the desired state when the tip of the current +# branch is updated to the new commit, and exit with a zero status. +# +# For example, the hook can simply run git read-tree -u -m HEAD "$1" +# in order to emulate git fetch that is run in the reverse direction +# with git push, as the two-tree form of git read-tree -u -m is +# essentially the same as git switch or git checkout that switches +# branches while keeping the local changes in the working tree that do +# not interfere with the difference between the branches. + +# The below is a more-or-less exact translation to shell of the C code +# for the default behaviour for git's push-to-checkout hook defined in +# the push_to_deploy() function in builtin/receive-pack.c. +# +# Note that the hook will be executed from the repository directory, +# not from the working tree, so if you want to perform operations on +# the working tree, you will have to adapt your code accordingly, e.g. +# by adding "cd .." or using relative paths. + +if ! git update-index -q --ignore-submodules --refresh +then + die "Up-to-date check failed" +fi + +if ! git diff-files --quiet --ignore-submodules -- +then + die "Working directory has unstaged changes" +fi + +# This is a rough translation of: +# +# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +if git cat-file -e HEAD 2>/dev/null +then + head=HEAD +else + head=$(git hash-object -t tree --stdin &2 + exit 1 +} + +unset GIT_DIR GIT_WORK_TREE +cd "$worktree" && + +if grep -q "^diff --git " "$1" +then + validate_patch "$1" +else + validate_cover_letter "$1" +fi && + +if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL" +then + git config --unset-all sendemail.validateWorktree && + trap 'git worktree remove -ff "$worktree"' EXIT && + validate_series +fi diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_update.sample b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_update.sample new file mode 100644 index 0000000..c4d426b --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/hooks/executable_update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to block unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero=$(git hash-object --stdin &2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/index b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/index new file mode 100644 index 0000000000000000000000000000000000000000..0d60dea5970bd6a46b842410ed0834918e5a9f8a GIT binary patch literal 3078 zcmZ|Rd010d8VBG5gdkCgq6UPDOi{3ii7W+y35p~tG%O~dV*xQhAc2sCBjBVv1;4Wp09s>7t=EdshW)(oGqbiA z<{xOB^Zb-8Sk8=uWv{_k#1002uLneYgPvE^(fRU#vwAOzgn2b+5O6cz<2%!5?dm&T}7Jd9OWB z=(T@hPBNI~FN_z(NTjf9==VqJY>32;n(XlYkW+KdtFVgpOUG&I16mIMVA*eqJYdY{ zdUO1_nlnScKNfjKn+`76Ne18%td^K7uv@+Oh^=5K0F5U`xlmBb=h(*@jz z)AzM=4HNsBWS^Shwpl;HoqgzKxUH3y^~3LGUo^T{FoQ1^38iZBbpNmPd{jS7>}QdE z)5XU(ecC^Kbce%+bN|k@Xrb_vKd@2&i{I6cnxw;P`_sttK|W0EXOn#f(=E@~U=!%` z8*TlCy{SruT$tF+A-f;&wmj={qs(c@Rh49QvKhhW1GcdN?3R&UznlaN|8#EF4AcT zaxYO25sCd=vTw3x@P)hY%Lj^7S!#c&)j4st`1{`E8Tz40lSRGn$Ccun$c2gBJhD6Q z`NmgymIdJoN!zW`OhsO6iNm6mlo|Sw0_R4vy|ve`B}9AwgNfY&va6Ts@8s(7;Hu3~ z`kj{iZM#H|*D-eI0W<9cl12yj0@Zol^3OzKcQiCBBfI$GAsTt@VWUK1w{Th?YS%tp?$aTdG)^{}EGn zB%MBVHts<#OzakuU7KAE-Co9C-;PjrR4yI(ym;_P^S-e8V4;pXZK9EPXI%TdfQj9G zWY_2Lo4q>+CnKrG&wID>2gm=$YwBM67MSX|Qxm6C?|0>4$cKsD&&aOfdD=wTfnoNx zdhg-Z?IzJ5t$EjS+PsiPUQZv7NbHu7UAtx=a7&A)smnhx%r*RW{k|=X zxAeIrciJQ)@9)2!Boe!&WOv?Vha>B*)yD;LI*WC=;ynHF0PojLq&`ednzj$Yd@g*v z>v;iZM{8GFuW|Efm(9W&)9g{-WB0e01X9aeHD~5-?MSP8GEvd~qLOm*#(>Y4l4b54 z5^EdXXB7O>YFfs#F3K{)+sct!&(@7aI;Hmy$CPUxw!je{vCaDdu6J#yP*&ev z;N*I$R!V-c22QXJZ}+OAYh4!L!0Y4erA4{3gv*#|c%FbWJYHsA+>oAfc+dT%muu4m hKh6Hfj$8kkT);>Ojcexk%ss;>+j=3epgZL1{{Um`CvN}% literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/info/exclude b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/logs/HEAD b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/logs/HEAD new file mode 100644 index 0000000..26d428e --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 107273e004e4f7ef07f13c83164d2cb2c51df65d Pablu 1784924592 +0200 checkout: moving from main to FETCH_HEAD diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/00/readonly_5337796b0a74934b058105cc428c5616fd0f0d b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/00/readonly_5337796b0a74934b058105cc428c5616fd0f0d new file mode 100644 index 0000000000000000000000000000000000000000..33e7d1cbf915b78ce436d9b1295140f81dc8850c GIT binary patch literal 2321 zcmV+s3GViI0qt5#Z`()`-m`y219KpSNP^`|28b~}4t6g)d)VBJEJ2YisS%3J<-?9+ z3%gaD%gs-3{#Mv5E!4XU`KdovI&0O9 z`ll()JKeNvdE-832UFz3ucf|E_+?URduNXE;=++OwyQG@+FWnD{d%ukQ|@fMS*qyY z(PAy19c^tb@x=P|uEf9esE;-`yN6iFuA6QfUoEioy1G7GA0lvhcPd7s+xYQ9se`p2 z8hoVRm8xo;WNAZ!?7A{*jV&P~eNl=9U)Rtwt+7@`t5s>MqT8D?0Gbyv3LsH?O+{O2Z8>-9(_-DGK{V^Ve@8|X4$ij=l#zaVDQ+R91p zmVI7cK#+@fw^7&VMzsg6GF#VVzk{vKCtJ3#EYvffv}(-0RB5ZWwmsk>*G=^P^i0GB za$U439iSXf(WMEUTzqnTRhoTB_j+B}G}rlUbkKEPubQebZL{VFW*}Bj$~1{76Uf=X z0wczduCgp9#Ym>}uXmXdCttTNV%SUhzNK zoR6$F`vZkQTX%Z!mDk)cMOY;&LL`!uwn!_pF7Zp!rrQc8NeTmRB7*qRv%;rBBk17g z^kLZtL%LL3Po=yqM%!XRg78f$+Zc|Yba8w2Ui~~~{)B2vR8ALI)$ouRt9QBk3HZKTpNR~uz(b@krwAxE&)J6ls=kGAx3u(kRF5Svr5BO2mhpryL< zn>sfq^AIZNK=3q>7EeQ=`bWolj7_`3+PWbiEKFuvmFc2T>CvL9YZ!~NB0W}( zDs3sb@J_3{wCKLqQm#0e4^MMyDLHwco@wvzB@`EqqY`DZgxY+)QYrG4!`K{Op;}TL zZ<_i^L6xiZTl9FJbZx_{OS~xcK5Y$R&C{)ZT&Q`t&~rfOyN%I-4tCrOi<4tlv$Bo2>Ssg3uYk!gFOwC{6#oJyd4Po!R5er+X;*?BZS?)-nP}#8I&F z>NnObJt&Zy3z$rIM*$2*z0nIgqj`PNIcI-yR}^}p`Qc2IAN0d9t=2T{hz zp<%Q!Z8Y&_OkZ3ZHF5O%BfEuIBi*&ShA;GifL1b& zMmnFiY`pxm9g&TE|Fu*<^*sg9gCGJ?yX%T#1%~L>siQy)Tf9W?nCo4N;6~%iaf_eS zR)$RPlKj(f?!}&(fh2!-CyCh&0R)ClgiDw@of})cWH>^Vc0U`z=*l(CRa)u6`$y|kceCmM{S_+B7f;e)53>=E8}AnvNvQ z76wfNTu55s8K>ram)MMlJud(%=M6nlU9K-*v*E;Pg%>!T4~FT!Jqo#^XTQ; zjU1*{hIzz{Pvi?)C>!@-G(7$S=>vGQPPYCb$HR%fA@V+wNa$Rt+m+M{jW`T_iTq$f2$I!SboG|- zxG|1ENDUXwX-m5^1^xC2SleND{0}ZDoX91`-$RLb_Bftp@!aMCxX4?l*tv>yd`ULW zCor|NGUv{PV~nl@IT&YUpumN*#?ZAnPYk$9s_DDQ%pQ+u4p)Uie+13||B3tbz8bC4 zrscViSi9A z0>^29|5)s!A>oXQg8SSKPdZR4hA0zLp+3cOY61R!0k^$*V(SEX`_5>(fC|lPke01E zmX+%+k0bL}1O$D$Uf zCQ?=p-Dp<4ShmhF;j15S-w0fkddkJB&^<(ywJH>Xk?u?TUfvjYanzpdXbsB~@C<9cK?Wphr$;avZ#07k8UIZB3i0AsQ3I~RxyUiGukia3T0KqTH+ zx@Bjn7T9W~w2^)%6r))yAO7#Y-hGNE@qB{AX@`E|HKXxhy_&^@rklr~w+x!*jLH(qL^j#Z-s?XF);3N)#E&lexV$DXh+^(FFG>>cvvLI<3Sqe?ieV>dl@uBH!z1 z;Du!2$#}@g>>mq*rHb;R|u3{F0v>A*Qk`9KW)SXIwdTx#tRgEh`Q%4x1md*nI{R1DJ808i2)px zN(o(xP*7a;;#5AywRQb@{AB%x;G?C^<)qEx1r)wC(m70$-BI~>Iax$Q2hacGgh4sSz5FJ literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/16/readonly_d0b0db945ddf3d303278b53db08991f21ee859 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/16/readonly_d0b0db945ddf3d303278b53db08991f21ee859 new file mode 100644 index 0000000000000000000000000000000000000000..390cb64845c53155a3f0905fdd8eb3253e7ebf87 GIT binary patch literal 1118 zcmV-k1flzQ0j*WrZrer>?K8h(x)v%?DMd>`U+CIG8#}gu)G?eG1uEObiX4g?QA>tP zO12{d{fK^HzofIfl*F`fABqAtK<&<)xty~zvn-#%X!OJIn}cutT9y7xNFU`2W?ZGt z0bF1imr$Y-PY5Yy1(xvV?|(q8FoxM99qIc8K9XOoAjwNm$d?%^1=TwD*8H*Y9ykYt z_iKt0_!f}-pvSZ7RqB887k=h~P*6}bjV`2(9+2B`0B=%^79bL+QlvPM$)Mm8wNF_~ zDdZe3F<*(LcC}U|&da=(F+q$5FiGGMRiLw6^D;IzI)y0B>6egIPDi`~gW9uLT}mjc z=Y*JY8B<*#Dwp718#tK<+gfa=jB6}PjH>L>RQy|>FF{qjGKprGR8Ui-I!dX=-jY}J zp+GHpS$V3+M5P9jYUnBo5(~v=8HUR`t3;9E1D2uWt8h(S44(*?AYx27UAwlwH;T_O zu(WuVwt-6FM;&()=WDsCi%lu!X>|fqzNFq%caEd50uSpP3Q-W8$Q*}iIA)zM!(d$h z2gqmUSa6Lo3cSO?;cGfGYSBj*XV<6K;m?!H@am_J7lCIk$iRuXLSP+=&WI2PKqu~k zR@gB!N?}J3?UE85|vvm#(z!_333VZQ6L%1^KjMEnfDji^j+F(%t8m zz3gL58@Ij-NU{TFD&I2|kT@2SQX=lPh(s;}ufmj$@)8kKzY^$q*({#JwRl29{ z!_~XV#}DC~_m^)c=Yh9n^y=NsIgx6Q!_&z%U0uBY<;)*g6yKEV+ZUz$_F*IbXKS*3 z3fh8nLC<~)UP;PmWy9d^Naw6&VvlPo)5xOJ(-Xv#;GF?e{{3_ssDeir+}-s%Um6V3 z%yawVxc#Fyz2Es9+`V!K{f>WoJM@Y7za_DOie8KkHgqWyo2uMdZF6d8+i0vQW@ka( ziE!wT*f}R|zM68AQ!s=oELT`MdeDR-KJN5b$267Mu@%*2iISePxBVKO_%W`0S!Wq+ zU|tf@UJhN>Ah28259psw@7Z0Sb;%}0PR$$Am+oR$`GY>YrH0V`t;?tnL%-`boIFvX zTcj2rG^!%HX=i3%DkZj4HW`?Q?s;}t&~N*}_USk6*iHYhdeWNwG%|fXH-Vci)XaTa zCV52bMC;wG9(p}OeQ%Ub^y}5pECVXkEP!T%D7^}BJNhF|?-3&rBjM2SrK=KXTQnu{ z?cOo-8)ygWT>*JQ%PO}^Y~TnLQhLXSx^K2&GRglzUz4vV=yKl#XmRN7G*3WwhBO3_ z3)?aLS)38ak}q*baBn{Dtpu(&*;tM#m+`~c?ZqNV+_6i%w_@x(B{vFjGj_|Epg90->_$9E15PzyJUM literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/17/readonly_63803d3dbf5f7131e57db9486568c5fb744450 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/17/readonly_63803d3dbf5f7131e57db9486568c5fb744450 new file mode 100644 index 0000000000000000000000000000000000000000..af4be11b6a61db0c8f4e42715a21ecef0216b10c GIT binary patch literal 2771 zcmV;^3M}<_0liu6j@!5u?eBSt3lxED#P*~J+M<~_*kpHu1l>u3Y>NH}0uL?GHaoJY zQj$H+N70AqGxWjwBt7?%lqt_7i|vmt7Li4fmzS64o_okymUc;h_|qRB{AN0xewUZ3 zY3PM^h5NZKP1}5LtCd-w@wb)HWkH2rsjh4nYHPcyrK;O5wWGJXF&{OBMioquQ?0Qy zT9sC{6VkKwj2_G$Jft~2yf;paR@&~&_M0|W)=>p>J58^oH#wQJp^>Rty@nIHF3V;T zJ*0=}460q|Vy!9LJ5^_&v!=E3;6-MiS6!9E(<++Yo2sz;sf<|ajoKN^J}OMp8g!(U zvs*Z`TSTi%UD|y*omp4b>0+_$R_&qI4K=MZ)%yJhde*I0@YT15Zd7#qHLNI!OEj}G zr9QFqyl?s?BU`)Ei@bAjKVX2p zEA^^9cV@k56Aw2GmBrFvsI2gFWlDOZ+6}dq!bw>ociL1snhdRNT! zPFHObv8Nc9Zwm_Tu0l*1Yi8J#mko~YF(!%B#v<>YB$=0 zf4WtbTEoqJqw{OqwY;3IYICe>V;U;e#{=bdyEQG1Y~4;xbbAq3oD4n=VYc_?NL`LQG!YLJT!VQyp^Hc=x7ivr#@rqD~p zQ1w|#zkhHMFbxZ_QEUf$shd8mi`wa((R)8}(<7x17czCWx9{=Y6dz`T-oabsq~v;` zilr^+YKBMGUX4jPjhJi20D$YkU3C9?2hb0DCxH9O)%XH1=EOaZvjEt_hW+4K zf-a_jYkg>_Rm)O8_8Bnok05VPBmk__~HJmsL5 zS!1(6J8G{Eo^chi5kRL_l|EvelQ@k+O3VYg4?*P7Qvk9mn3lL6AouR(_`vUzkd{b% zW{9v!cNK~88Q^gW*^qaD`4CEg=sTEeu*wn-+qqnm6;=X&Vw2bwPY8@Ltl{g|q0jWA z>uGLlg9rg1mM#OQsLD4)b0I@$s~sxhQZ+oeOw{fz<*EYwsBmW0&dy4ks}js7Y`ExG zGN(^xBqdPrk5?%VDC}9@y7K-eLmdXT`-Z=vp{x7XYrG2P`<61JFB8~IF`SxJWjnCS z??SQhwhFz@&ss);c8*dw= zP_Gg-zzfSN%Viyb$o}4${CZ_>_;(F$QMdeSqsy9~$n-5rXk5*0P2k-m%3gt*-=TJA z>pyASrn?-EG?)ijEn91rY=E-?xeYfvwb7x_~99y)7u~4 z{DeoLT6Hq1F9OrfL^DzhjxM4yl%_&CJE!KbUD~qo&MNH;7VVdkz4pD1!wRFIu^`BD z9w`x|5&|uZe&=ilPL9Km5r%QW*`2CrPExyhPSFb@Z00OfxG6?H2oc#t$hjYxi+Q8| zYAXNR&v2?e663MDr-ERPVtt(Dvr7eSZsZlJXw zy>3l;MWaCWQjDg|5OFg|t-%fe2h`HbLT_seq17@xkfcz%`q5xI8pRDcPXc^Ch~0S- zI4ROjFFOOdD=_U>bzfu-X$A3%f>T}qgjFxXSptP>C527j$VqGs1_ihF^kGv`fkj`N}?B~k3rWJ2zHBOPEvEP4m?9u7t|PA@g@E8%kCt-j~*qBzsjir2~oP29Lw48bJ)^hqhD%VWI|)N zt)(A>t+eR~kT*S2hm=z4waO3Y8?6fMN{wTy^UumvdI0t(qk2UjlYX;1P zcaQ|wIFy;di{Or%)|18&w2>z#v^8sQI3Ox@cf^$8QI5T40xkf&Zo$np26}HN7SH&d~M_%00$`HXXaKuWS8=x4ay7%)~ zzB6f1>iO%IfrV`BvBboTrU)L?D8O>SG7C^1F5H;)jHf+5b%i?{7VOEM;PlC)BL)-7 z+#M-1;^#fv{6JFKk|6|Ug$M6{>m9jIzheR&W%rudy=H!|$;OX>Nn9b0csx$W%*Z=y<9sJvl~7q2d)K_#05a67)rux zrCrZTd@q~X%U91|izW7vBY_8){ne@VjrY0X4gy!!{?8ec{!H5cg9XD~D~00M>F%)Cs!lGNf72Ge6T+dtjOth;dhKnla3PqVs$ KWNQIw2NJm}kQX3Ip܌3|_2?rS~Ga(ܢW42ZpeQ3^Rl\T('m兾*;"T:{pqupϠFw ( j{n.D,d>wqJO->8X7X[Q ƺ;%kAHV:#ɩ{Ӻ3ئ74W>сD׈5/\Gh0dlN:^+lƒSOzJ l#ZжCb=q{w1:U%# )QG~gBQ,VFӖ(!ĕwm>A/NJ1LZO`e[߆b;_ϸ}@pzM֊.E \ No newline at end of file diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/37/readonly_51026d393d97f55d261e1e20dbce0bb707b784 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/37/readonly_51026d393d97f55d261e1e20dbce0bb707b784 new file mode 100644 index 0000000000000000000000000000000000000000..f5ce5f81952b449afb84d113507a48382254a4fb GIT binary patch literal 678 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 literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/3f/readonly_e8254ac9e3b167cd84e4c427d87f8af76e23ea b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/3f/readonly_e8254ac9e3b167cd84e4c427d87f8af76e23ea new file mode 100644 index 0000000..cb29c04 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/3f/readonly_e8254ac9e3b167cd84e4c427d87f8af76e23ea @@ -0,0 +1 @@ +xPKj0 ڧN )&zrrw +I66xߞlW 9#̠p+W:}ޱ_@$ \ No newline at end of file diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/41/readonly_399a89bfca1a0095f7a53d1f89810861242e79 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/41/readonly_399a89bfca1a0095f7a53d1f89810861242e79 new file mode 100644 index 0000000000000000000000000000000000000000..df88450670bdfbab2561e451a2fe860d2b097d6b GIT binary patch literal 97 zcmV-n0G|JN0V^p=O;xb4WH2-^Ff%bxNGeT_FG?-QFDlWi%*|own{THPy>5a}>{^bv zj^?QwdETv7gQ`qRO)M!bN(HJeEKMzjD9?Yh(=ax4(|rDTDZW34r2QW9rLqG6h|43< DqiZV0 literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/47/readonly_f6821b845d7069c2c68f7c7082be8e2c152b04 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/47/readonly_f6821b845d7069c2c68f7c7082be8e2c152b04 new file mode 100644 index 0000000000000000000000000000000000000000..470206c1e98642d3cbe4ac5913ce4d83fc26f36d GIT binary patch literal 1539 zcmV+e2K@PW0kv1la@#f(?OI=PM_o{wkyyJ)Hffxu({!d;xQk@bC5Iv*5fKG20BG59 z(&}!KT(5di?0ouoy$phI2Yg7s+{8~E0z86KETT8zSQ<^utZlP&b>msk|k}l}>jRD0M6^Fc|L@G-u6xk3=zysaJd=&0XEV}xssqK($I$ki;Sfi6>N zGq#s`GYhYKIU@w)Hy}M}-pEwp0U|2f2{#rcWbtfC7Z=2m8qwD6LD`v53AwAB zkNB6O(5*r^T&?0zhNTr*m%G;1Qi&Disw9_h_~qMYDL62sDKtd*iWUe-U{@@yV@jgi z$Z8OWw@to-$OU~rNpLN#%i`*S%Mdmo@u$L_T;?~i#NrH1M%-PC&X3%>kSQ+ExzChW z^_K@i~j_+LAu@PJ8W0(HHN{oS~mOhdF@gcTg?7m(c7i4J0 z@U`#47oTtc+-0@cx=fTi;WIumwpA(8rOcvl@cv6uVWMM`->=?qH-EAI`oa30ZoXLi z{=wQe+BtprVgqkS23`VK8OsD6fsEo#>;t}%RDNj!%5{FDTgMQ_2$0iGUI|4dVAex5 zEh=r~d#xORk{enWCjrsRF%k^Y$((CFPd+4bUlsq@u&2|{-kW3?2kg5{r>!|G_5+c4{}R}V zW9cpWo#U>%Yb!@F-PtvM5<=xQ-`|7Eq0t6~aZ~R}4-ejpN%x%YicW-0QCD;zj#qzZ z=JBKhj(G%-4dSR z2!7$v$0c_q`BA}neV?tZti{8OMaes@ofPp7<|8d8ds}Oi3^hbHF^l&VZak!)VKkkx zp)4a<@AJlU%ef0#9+xCFqJ}qDg8stLCOy)hwj%qkXf8m;%2Jf`Pvf9}|Ne(Gu+LSw zBS?c}R*EsIzVWaY!(L2HE>SUuGTW9V(dt!x1;$Xzy+E?8EX5`_v=HeoC9CpcZ&{nT zZfeYfS*9JgO>CrugJbWJU~$d9JUROb-ru;2^Uix^Aswt);URY;YEWnIxJ3)*b)cuA zK)n9|F6lhg)Z!Z41+{Y(VImKJwxc4w*KRe{tJcUorhUtf`w5tuej)^@} z+tJr&aJf_5cjTM=fQGOk!iW=a(YFr*!Zm>J9F`g*uB142#luOg0F!Pl)qRr|0r#t~ z7SSVZvd)1?ZT$5k%D}a5@uW1)v_oZ3(+Ms{%^@`3qk+JNV(9&fnjc=W8gCkoUpC;zAjMNGv)q7?);3$@#I_!?G^FxwZC7%Swse21h5?@ zw$O(IO26iNM6AH#-P0Al`vHGHFM0HE#D&VeQVa%!3oN3G(wzoNCYM&wZ|CQ@GockA zA`IrX#jK)1jawJe6s-)mt$TPZZi$^v+&7enbH+&-AuH%H@D%qnBBi1x=s%?T-UH&g;{{iSvx3;}|1(E;& literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/4d/readonly_32363ad9bc23df7acbbd83598565e140168682 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/4d/readonly_32363ad9bc23df7acbbd83598565e140168682 new file mode 100644 index 0000000..fa1342d --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/4d/readonly_32363ad9bc23df7acbbd83598565e140168682 @@ -0,0 +1,2 @@ +xuR0D%)hh'rto"IrGa*nnn1́sq s(Gt"9eu5Uv9@oɣ;I[GmZ)Cyq'Mp!.zC\> ++wv5{T#;*vsPEd-dZ׆8!TGJy(8.rJkfߥ&Wė8*Пy4 \ No newline at end of file diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/51/readonly_1d507c6ba403c265e84aa3d6b5a66aef442953 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/51/readonly_1d507c6ba403c265e84aa3d6b5a66aef442953 new file mode 100644 index 0000000000000000000000000000000000000000..4f5f5da66d99677ff1c93cadafe5ccec97f9a559 GIT binary patch literal 650 zcmV;50(Jd(0bNo{kDD+M?%BU$l+&(+w!3@SLvIe4ja7n?aW>77hZEcq9AVRF|9!{g zQMD2h@_6QZjqBdl@bdij@@35xWTw%B(}cwK^f{VC=i#AwgxBZSH^`=66(NNlkH$G; z2QUsgJ?i=s4r4WV-A1q*2?%!j4q=?# z`*SsFY;K`)&Nik(JZNn*9rfTVANcmB*A5po*@n0v#k^M9dNQt3fM{6;% znenG?e5eHFd)xQ+9IQ2VXif0zz7eAEs;cd8J$tzDJ|%=f>|C*)r-C`GEaR5IWt6jFTfge9<`C7u@%Y`I#A zjRNeDG*>$iDbRce-+7)yzsY-AxyhV%)-<~&a&vamJF)f2K} z4cG@Bpj^=`3si(|P`wQLAQr_=^7oa3mB!beXXW6`G1O%GVJjbWPubItc<% zBnfR7b+BErPzrn$zgTgR2OF`-mBeF&E~UDUZh6Tfppus%kW`9wL_(Z6f#?W=b2+=9 kgsA6$?i<0D5ZDGy-?Tu&Xogt0o_w}=Yknes0HSJB`aAYN$^ZZW literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/52/readonly_f002f346b87b770ebb3747d801684ff4d5f56a b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/52/readonly_f002f346b87b770ebb3747d801684ff4d5f56a new file mode 100644 index 0000000000000000000000000000000000000000..57513587083b4afdd1e89d9498cda119a840687b GIT binary patch literal 1980 zcmV;t2SfOH0mWF|ZrnB&?zNwSKrf_8W|VEwUevoTkTz~&AaMfQ1qx)d0xfZ7xYm%W zMA?}J2KFKL3HM3%J3ORD^8Ccvi|xShjL5_D`<)-^y0X{e`yYPz?yJfcvJzKE{H;FL zs&L{&ywOHqsn%t@61Iz_uFB!AQFpmt%#E_QdX-L&0%>czrcj_Q)pfVX7RqTex9K-i zkz6K|jJE@AV=c+V`D||RCs%4^OFdtwOm^IM*Xg$t96hd&m&ePbmoo26;j}g4uF+2A zg*9`%NGokrJ2?`DJ)ID2el6WHD5A`5_UiQF#YKMk?)BN#W&ZQq z^Iy(hf~km#u5L=*q&u`GHl!>R#a9h=2dBKIb6u&C7E?ioh)|hwDuQPCeROp7(BZ9e z=_gPwt-WcnNxy}t8`v*fa(Qnj3yywh5Kr0QtVm`vW9zD0Xp=z0Sx5iR-=3fPC*41Z z`!R=NRi+8Vi>5P9uN3M0kCQ}XS@ekyo@xtOtCYez88bjA()aJZzN9mI-9fJ$t_+?B z1e{vcgaW{c$mkQ?%s!7N2HuS{i)^WyvdP=J(yq-TC3wf5BbGkQZ{M5__Q{&$+0w35 zvPJsai!X&pU%$nch9e0Z82Kuw$)}a z^PKjbe)syi9-;SGzJS1Zss9@fV4vD?#&dgk*2LA4v8DJ7REYBRU!ERyGF?nl#S%W z?OR!OiVbg+>l(ASdO$?PWuR=-@11T`nrz2!l0_}k{+v(oUY<{K1iCo%_6dN%WL$=WAu?y%EeUVba0`zp1N z#aggfai`H^E7>|;z_T$ZSliwlwRG-r$=4z<;|`v1eM*Fpw>i=)r;H%aQ-;{8jQhjw z6TgYUMS$5zjW&|Qc>Ra+47(caC~3Mn?sn!6Ygf57IoO_fr^GFiBVDe_g*u_)7S}s- zW9*$FjwgCyY@=8;s$;PW{z#x6 zL}&vKh52>>mD%`+)5mo|YNwDS+g9OzqHShtW&F)*&ozw3!X(@+Sm5=V5(Q}nR>$Py zG$#!R;OxY?ulsuPa64cp>@bB?LOraMqjk89KZDCRY71 ziQxN|PY%5h|M}Oy#mm$4(~IX2%W%1tHXSpZTMZNUXGYIc7@CJTw>U0 z#nVVFFh{fkH)4gVu#T&LMT*f_EL5qTmxhsM!1y`D%ThI%a@uEu7ArKSo-mrgd_{~` zdVyL*m+rJxO1PyG{$9E1kYAH1UCZk%V{?QK(W8T!1nf4HQ9gMF1%K zsSxo7hITZnU2|UyFtxi7D?d~o3K+vUQhcd@|A*0x+)Kcpuc;1&H&f|^%8N%pUm3|| zhuW%Mpm6yk?B9$l0nfmDdMWORAAcM)#*w{{wwY#N8Ffz9X097d;_)34^A6))kR3bs zu5oWnxk-%iZ1_->z<9LGS@5v84payYm+9_zd1pLh^dkct(~x7v&ACw$JOe`J0=8WNl_X8Hg05Kl+`MaCy|Kn}D2o+4j^_2I{(rU8$d$Y_V#csTnQ z60RT3f(QI?JW5F+MhF;T^dS&NLl(QZw_&d;u{|)e7&TV2Ii%+1;1_6k*T{I^^V4ap Ol3~`kI{Gigg?4RP5bL1; literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/56/readonly_ecca37040cae81738e7dc8c44e8e705d7f744c b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/56/readonly_ecca37040cae81738e7dc8c44e8e705d7f744c new file mode 100644 index 0000000000000000000000000000000000000000..2c6d3f6aa64494d1e2c25e6216508d1faa57288c GIT binary patch literal 2470 zcmV;X30d}d0nJ%mkK4u-?eqSMTlYZ{D$&`(Xn{30t?O>!KCBzq3#3305F>IVu}g92 z8FH1a?I1s*zp%fg=id1sB}?m%fZtgGUJoL1XfJ4ah{r?s-|nYq_^+S!c$?IRkg+|tKamM)zis_Z7sa<%zvKD!f5$+a64dyB=@K8U z>DL=^{JGgKD@4It6yJJ_G1a;;R#RO$L~K`e8yTRtOI?<_T&!w?1va|37*G{HhE{jI zZD?sstyL2uu~1EzC}C;=i}JH7uUen2E*G_C;wE)WO%M^P}Rm8xqW7^w#8Oh5$^%iH(B zxY<;W@J9-cD)Uwv^nyRL^-OuG8#^Q&)f_$x)MMlNzR zb&V=+-Qz}^MQ3%e{a9&RfnH^y*@d^$ZH&{AxLl|138Wb5+awTEp&Gi?y2a;CTLrK- zYi}WORa+Ie+Gco*I0YVwPJvvw3otW&lwpRn2wi6@Q}iz9)-lu-S5%}l{0zbLH3oR+ zxB4ELo~6PbDg&|y*fXiWf*>^ zrF$aNPr;gKj1ffz5ZDOA&ruY$hKSM$&cTT5m#sDEif-Z5N@Jiwr7&EIngJ@^U|V9R zv!`V6WOo(gwyIf>G-hF11|F*My0X?-M{cWZ7ex{mF@5`re)r^$Pxb*CoXI&5oOuKS z<)4UAkQ{Tu2=qMCND%P@;Kk#hrf>` z{5w5CFvcXo`Y$7u_nr zbdHd~){cfSMLiZP{J^T*KPDWm2~A7AQhnV;horZB$C3hP6Es7!1Ij^vIFwkkr4#QEnt~5o^tq#e^Wp?q5N5#+T4QlCtRP?<4zjE?`FM&xhg5*1=CoC}N&D}&R-E?bItZc?hpU|q zE(ZGY)aAX-b;C!`5)dCm#hyAq9yvTRBvtg-M1V0u`%0n~;LDziK8j(K`o|R&s-^)~ z<6h>jaupUA3?xz?qkklErMAAL6UxM2r=LZ>{^E`%dpX+EMiBT&R~!1-_*W|Q;UyCC z8tJd4Y)~+yRyZ(*Qg|wtASl~|8xXE6oR0tzE|x>o0N%UUF}Y}8$X%k$>5Rbyf8^LN z#!uIro~Na@f?Peg9pLrT!G!B|B*pmgGf%anppUDL*(+3@JK1wiF-%~<{SYMFASXBQ zPva_1jc#K3s1KRuhIX9MbI~3grLy;&kr-mW<0+GzKdrsPcKmP-(f@n=5TR$d zv;}iS-gx%%Z=*s(Cu<@{^66z?rp*FFKWkfbtkCp9HU82nqv^8-?4vau1e~WH2TLdvIDEt$N8~ zT`oTTjl2x68s2^&3Qv!MT4^EKXg1_cx=sj7GMAo^JfWbc42lPD7P!*mwI17p&rLG# kH!-5-kD3cpxo(q{Ux-$*6W{;|EDhabk*?4F2hK1(RX(iN9RL6T literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/6f/readonly_ecb9315d55b29f0f5f1a0efcc21b4ee10e6507 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/6f/readonly_ecb9315d55b29f0f5f1a0efcc21b4ee10e6507 new file mode 100644 index 0000000000000000000000000000000000000000..d9c674a91bde7c838768487b261dfb1bc0120f49 GIT binary patch literal 394 zcmV;50d@X(0j*Q9a@9Z#q<&wqT^f=Z8fM}qlZKiZ3JQio_US#}yz`xrbPn$F5qu$E zg3dq)guJAPJDpe3u2$+(>2dky?W;@^cX-ESRT#owDy!`R>mYHqSaOFC^>FY>N#nLPn<`}!v_7l>q}U>9dHg?XYXtL)kv89 z30}N|m+cczTTEsW`)aH$rF~n`4)5=QfWNqY9LatcK`5-SF)FmAg|E>5o+*YZ;>^>E zktF=mQ&gk3h854d+8QpDVxTsJI|gk)QP`Q9RCd&(O1x!lsZUCoi*jfT|G@tz>Za5b zu$GDxkNj}rKZzq|HKy9vSctcFfcPQQAp0uOVi6KO=P%OJ&8X<`_BQ-InBm=CJfG0@xlZhw@oh_rS`G}mC48zKow=?WtKn;YT{(}V7TUW z-=uwW5I*!mJ5sFI@8;>@a4u&V!4 zX3jn=ReMJ*-OqOK%KnT82aUHuRTWp}C4<%6eQ)Xc^=3guUd4@mxlI{$8JDk=LDiIJ g=7E*Ce`}I%i7m)HbgaLoplM&9j;J;Z06!>wm_R>`KL7v# literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/7f/readonly_9cd53e889a0af1c8ac06d90a7f529692fb7082 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/7f/readonly_9cd53e889a0af1c8ac06d90a7f529692fb7082 new file mode 100644 index 0000000000000000000000000000000000000000..1988750f761e3ef3b8dfebf5f2982ea044dcd849 GIT binary patch literal 7582 zcmV;P9bw{l0li$`ZyQN+&hz{g4f}zVRwV7)J0vHs9C+8>vk+T0Y~320#ey^Bw8XK7 zGtA7;GWT{5hkFPRAWw;byxeR4G5;m`zN+fskfJPk6T}Y|fAmy$S5?=Kuc|r8%Za=H z```Y<@Bi+uoB1lO2K6G(ntE8g%jQw^+9&he*R?B>ciDN;WM$!!B6XFov!Al!+)aFQ z>3!i&e2E8~J}g6icuI0V_GeAAs1HXYp*~FgyFIr^3SST1^W?(UPKLS0SM#h$@<3tY zXURM7^6bK6?k2-)o8mGn((yT5?kcH#b>Mc)aVhI@DnECYaTi!}D|($fX~tX$ z6blNFmCLG@$uFy95xais&yr={fYWi^l#7^usjqlZRxWYVB|e_Jvt==*t}4lw`SLs~ z1_`+2N$mK%D6unF7s>RZ9u>YEG?n+GG)X6AI#_0-#Dind#Vqxcq^d>5{Cwim6v92r z%cQAYANrsA_wFnAyL)?7AzPa$<_Zg^948oq!{gui%ANbhHQ2@iG_r!UvEW5Ez3@;* z(Fv-cTNXc+*uoc%TOK4;xhzty`q8o}2l^$ngS^Z=ID|8pI6eWYJXb^asKF z54wk!L`Cl8e7RVZb=G*(U4+ zXIjm5{Y6rP!Pr*L7xE<6!B5Vzs&4j%?r{mR7iG(2VIzzP=ihC~up;i2)Ko~{Kpvo8 z`Lpc3tC!$l)rf|E`PDar8BPXm5)~U76^vQ#&zc7={vA$G_w1a}s@08$K&tF~)~KC$ z<^N>~t2FmTBeud$*vEhSZ+CBa{~k2u6P8s+{r&Ks>%%O-Vo>v4B8Q>g6FUT}*T61{ zEsvs8U8gL978Q!gZ?S1NfsllIDtuK~m?1JW9D6A70YAVTY1%5hY~-%HKXf)(hm0zBn8vRgujVKvje!w6cJwrmM|&F5KOzV+|2=~TT`*A0MY?pZm7HGu2#dAXoq zwg};fT)5F9X=bCQ+#tb)k?VKeZ#J1>3=G`BfIRtWWnLE#<;x`cQ{-IllZ)#eV8~$L zzJ;vw?L{8A`Z8Gv>npz~;Y*>&)QULGDtF|1J6GI`m1Mt${@%H=^E)^vd3m`v#HeWr zZ8&l`@k3v{8$NyV>hY`blaue<5s2THwSi?gEqME#uU5~n!?5BP3)<%-KXeK0TD>Z! zYb61qe|F}lID0~8vdnVssR#bU$H__T-w{ft6nFN9YwG^Fy)JR&T#Rc-8d6{uC7ejOeqj2xb z1O1Mqo{EO+@o^PTxjhIs2T84;YaimXc+fP`o}4nsuqT5{23u&Ydto-26i@1>?q}Dd zPwvSJI%U2J95GUvxZZR;S)Mhk1rGj^`;+SxC87Zjx;-#_dI8JI*DwzAMZ;H!csOe> z1c^QO3l*RL0zndJDcW~9Jtx@mDJ-#wNuR)igI`~d19Kz>=#1-WcrZ?3S0*I_tR3I^ z_lq2%M7859b?lulFz@59-N?Oq^70w}K$GcSHNaReq>Vv3PxcU&EWwC&6iDF%p642r z>eYNww!*@uHG1ic@4e8`osU2w%@RnXZn7z<2mMfWjGT_Ui&~m4#F5w5xyw0d_$8c9xO}jLFyYEN(Ngr=W1ELK0X=0 ze02KM=9MrEj(eA~i-9u8r0ZWgwtKa)6i*)+A-p(8hLK0#WT6 z!`r{h{AI{I!k%QgIG=;8fEiSs2l>N}AyB)elsCvCYvF?}+2PQ57x(y9%n?#*{J>#!C!3fF&r>q(XiRON@L7 z=QTsFV{>Wlq|WCT{_#c1qbK|ZS7d$|+`#K#o#7ONv)lGn-BV3bVJL~x5!F));zW@F zgx&bI<(y6;0{qNLD5&cv;kR3phyXux5{UzJA3JukgcCEfru%?O9JH?Nmdp}O4I8<_ zEcte07Sq#PY{XQ3r!-y>nAsr<)B4*}-zf&8enkj&if&JRC;yH57Wd^q-irIimz|6^ z+FiCo!oP(09EVHta7Dc={$3(Iy=4sk>mtDRgtqIr>%K(v*GZ+;5RFa|Mf?_K8Eb2()3|U##0sDsT z`*I0<5AhRDBl5aH;s`lhGkWfu|J#@tP{xCzd-K+#H!V=1Qpvm1JsR!67j*q0O zHu_PXAOQ~Jb>ujy$2#)P*Fb0P#5VS#9Vc|ho1<604g)jpm< zXN<*+2|W7z>`6WSLbh;x=71^o5IHXeBK$vr%tao}eEI%B5TqD3X*Z_uXM`H}dokIJ z@`Omo0#w&rmWFiI2=j_wLuBGC&zRK<_R@h)1zlq7WXNh1?eBNK9`5hEH-xjW(*Tf1u5(AW*67Z6he4j?Ps!Z@QFFixf-k`73LM8Qrl=FOT<8;rqOna(z+Ccc85AgQd7 zxUoz`4k8n(YoA8_qQp+2IcLjC_6sSVGrPbJz*_*q@~omDF6IW~bp?Rp1Ou1YS6M_H zxdB|me+?XzGwH<1NUoqHSjCmmh$Ko>37oJR6b0zc8j(g#tRxoXI3U?v3Q(nhWls<$ zPz6a=#ScHZ^$;_@`+z?1=QjcUmNb?hbRQnNH_S|ldxmu2NU9njDDay?ZI}#NBkLUi zDhqp8Y#iSZgjPO_a&u{1HIU_G#aw;^SQmiTCn>?~7Z|D_Joc{}-5lHRmbe1)ghX!O zzP7TTmZY94s*2-K?WPy(m9S8azpl5F?I)dFU&FxwbJJR`YirS!m{Oy?+G5KMe0$@KM56HnoO&E8 z9+8NUxaeO9K$Vo(=j?ny92zRv7MVT!{$FYhSd12%T$7=a+fs1|!twYS!w4wca&cZIp>z^a!&*UsA6~`{ z-D`vsv0`7ZUp_lNeLeo>#mTqFe-P#2Lsc-_!e`hr@6-cn^H6n4 zu;jDWm#p=*V~r4zD5U!t8oKN8j)Kx&0kp%ZsbOp2MQGbAz?ZU;0YV-WAlE1dPO;s3_+c3I_-jGg%@AK%8QcIZ zDMynu=a6EAeu3hCY$(o=qBbl??^pwEoQFH&y7;x)^56;9o7m~ELVY!OAnG^>l9m`w zW~^Fg#CsN-&FRrQ?V;!>4g~-ZtBmaeHq1PzSq-;4$9e`Su)FDMibjyIXp3)%@^`dJ zHmWsgWf4lQYZggyAXWzR&;e4H))Hu8*9FEQOX? z64spM2^I)PhfsLpBV-jg?LW^crF3)Ec9Wsa}_lSN5Q;M2A&MqG& zNW#R0OU2$Lgj6YSno*96QCo>MAr){sj$n({egWJv-G(}JadWoYFdcgs;}E?F#$NOX zONd&2*AmoJo}DHSWObOVkps_n@UX^tG+ceXZc^bktiT%CPAtl`9-mk;vySXV@n9Uk z*cc!?qu`9O(R5Z?FA_45>5|oQu*miZhJc5)<6;$dx<$|ft3szDOy8~<7>_pb{l@@? zI+cUxgFh}dO9#&fuh?%9aQ=>T;PE;YiRXjYDuYe>uLoCVc)eqe*WFn{h&I?=D}frU zb)p|@D+#>GS_vLlw1N(zqDJ=ym}*W4&fEWzK(WiZs3t% z#Soj&M$Xzm-qk?I2dcoybm~!vXWY+Ns8u~J-5{_*oT3hfNtdY`N1tLgUDwXT;^weR z0Am;iaA6xtf=|SV;7ev{jW(d^iNJ~LunV?PwAotcI{VenrHvpQS0pGf7hEmR4SFHi zy`XBzvb^>Quo;0aQk{k{0Y-oj(i0TZ*lK~`$wGCMD3Ov|-x-H8Q|89E*o;*MV?SAe zRx55`M0Jq}rtjhu&Sg>-G|t zVL2$cH%JX5v?a-+_2AOaJ_y@l*MPEtbx({_sD4D7j^;q*-VwU0_V=H$V$aJWlb{pipj|CjwNd@fDZ14 z4%h8SAcztx00Hrlopm9x*0mGNalhCaqUhDSc4B9S9TJj&QRTC0&Xd8e9V7$7Sxc%p ziTgpsa5%#2AE8;NK!`Qz*?^HkdzyojuYsJw(rw6SS%{Rn)Z+ufU~O5)mUgRtHaG;H zFSIClG@F7-BxgrX359S&!&x{WXpegI@>nWIH@k2o$ZS3Y>tAVkvTxl>AvB@h3O~=$ zATAxg)~9p;tCKew0(zWe^w>^2-IsgUq9x|g>GN!+8^^N3aoe#dUh4X9q$(ANb}$)m z5~}5WGDX7GHj{6gW+baR(Emx!rM2H^!$P1I5_)Fj*I`XvdcqbJ z-))A2Gq)eC%+c`zU%~KxgO)D@Fp~_4ReEWTjB(g9M3Ca1fuxk^9cCk;&7$56yc`0lummO zjk?sAk=ZdBB($gzVB7P)&N)Nk%T>f# zJ+|co%F;v`HH-3+=O4bRK^wjStTNpiQQZZXZs7!6FU)pmn~z;uCAfEiKK4GH~kr}9L)hXHv8mDcqs z4MSP3+P*CefnhAWlTuXH8R@`?w#Wt|k5@) zW*$lU3}WaML#P0WaZXNSa$qI0~97{==4hK(HEYt<2fr*h5p0`jGXi zqhSDo)oH9H=}pS7L=9M@I=lx|t)VU`I;xd6tE>;?xJMZqa~PpUy( z7&aTsj0fQK*}=>L1aH?^AdcCq%-5&}BWa~JFr{UF-c0gwij;lq-=lRk?e~x{PT4R1 z95;y=TgI}4VhZ2OPykoPWb$1u1Ewp8G%yX}JnMLhq|viJpfV{)aLGmRofPvPojmUD zN^5Mq(Zx&?5kb2%uotX5FAOXJTeSAmrtr5$zjk-H|AGKca+$1vfRTcLV}zxz4eGjD z8PulVo3G@l9%yN51d-tY;WSBx(r%uDZd>J^qOJS5ac@|ah2aD?PQL?nDPh~$ODw0p z4mU>uo)6eRv4#?L?+t_*eIJ?x94=2$J-bDG$pt@t`1g+={t{FeU?L>X$eGH801lO8 z$z-h;jUO?lV`tsR4}X>a|HkK!^$Dr{L!SjWhLtF2i1m>Dqdo#Yga`jBujTRYpS*T# zhB2g~?TEvYq0XYIB-tfFj>^A}A3ksec9bnw147|({fofL&WG$tnf$5rBdNRKapTsR z+x(*}37^B5x!I@4$O0ZUVGS%2=)+=glpER>FFIyg8-muly=?&t-%-`+_*aK4!i z_heDGq$?DK)+=%Uhl(f&?87ufP97uG6)Y8Mt(GR-4QUh%nKmy{mjH~MqH>Gp)Vor( z!;brbOL~D-I;1~-5bnN_D9NCUR925BB1DDkpMa_`(^wOdP#(!F=^k>+M;8TyW!Et`Zi^Vv`t^ZL942ql+W^BX$*f^8?9*!B%gMb_QnI&e&S zY)6K`bo17n}{8co^&AcMK?m<@;MH?$B8 zkuUEC@~)4C<`TDQ3TJbL4Yre77PT=q@fgj4;Y&7dDBTdEhr92*ZvH^4CSa3h4x=L6 zVYie(wmo3>(8-XZQ_d}4wdG6t6FCqMwpyRCU;PP&4wT{0$0sFGR`nRA*#TYS7X+xG z=z4A+OL19*t1lTyBKAhb2SU!F_Q)}$=!)y3SydJ!i&h)ly_axIsbs}l_#?)ySgQB} z@mDJ4j%`!XvKA$U^I-k}iXu7*am4_A?Q$PVyfuN%VzJ2 z8n`08yS#t6xF7Ek4$ud~oiu$?+HlO=%)|~O7U5+bxr|*eCsIF0Qy6z|*RcihLhgkF zE~kpgvphRk2TW1)bNKlPv3%SA{fsp4>QiVzR6^>r1e)kR`7@{vuK$O}cYS%y$6=@= z)suC{KzBlU{j>Y{xBtBIDL%ONi!hj!g9Qzrb^nsUe|0|yuWv>9iXW-NfyGP=5ne&ScZ(RUx zE~C)i?-XnI*kT709>pAV^(w5C=i3}wS!WUl*H|sJRYjr@ z;ARDqDcC(FmB9giQUc+s?SNH2v}h>g$2NcNi1WuY`VwHV>P#@l622kHa=J+{`dbXf z?dNR1ob%TVvT5dvDSm@Tt|T*=2S_hJYyh0EEH5Zwk`?Zwgo!9ZFnP|W#XvA*DgHU= zG>7Xd4@Az^JPMzs4mlhN)~J^Y&F@(jyyfk=|JPq#kkfiqwOA3e`?7W$Z-3wP_iNE# z90@Rm3fgJo2f||9 zC>JnT`(R>_GeCz<{a^)k8(#1H1*=c~o(Mu|nW?ro?pZcP^5LWC`SEG=KYy3rb=NhK A!vFvP literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/8e/readonly_9f3e285bae904c5dad085e888395b10ceeab26 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/8e/readonly_9f3e285bae904c5dad085e888395b10ceeab26 new file mode 100644 index 0000000000000000000000000000000000000000..eca2d305218c1c87ab50ac2bd78b01223c9981d1 GIT binary patch literal 984 zcmV;}11J1=0j*ZsZW}ic&9lE^;FriicPqyM3@nlsZeqlFh-KJ8fFcMY?(RrSh)ZtB zSt;t}1NtHTv3^O1+>2InrP^r?*oLU#aL&w`Gm|Ql!n5elKP6ZhyYNTC0e-D8Bg~JKNKv^2KBo2j(@|>jb#ep{ z0j(>3r4)9No0aAx06}DBS`#{N;(wMx)ok@!`w`%MA@st*%vc~1-l775WXXRF%%nmP zz%R9-$8e)iIP~8rkeY+V-q%c`${lccX2p0w5@bU2XMfq6yv{I>*}99B@GA+6A~=wF z0)xDSv-|M}@Cm%$B(Qyba5aGo$T&(n^ap0eZ0%un zfl6J~6mIz_^-`A660PIA;c#!5*J#)lhPE&aDuGE&?Aix6^=={R98Y01lhzR^sgcVY?GDB(W&;B#(B?LBm6GJ*09I;8LMhCD3{L=<&=P$;d;ygBWgV@Fij%?B33H$U#GUYs^>2U-i@KKpS03{N>mS?>2=LF9Zmh=f z`L`5$o5u}~rXU^d4%d2kqbcDq3p-_h&)wD#sY?J?Co^TqvamMwQ;T3UjIsi=1*{|$ z{?v9?T0ZUJ`#3&(5dN7^ZrI586ekGNVbHA4LA$(EfFrD2%cm{Lc_+0jh58P3U8cw) z(wH`4&MG|TM%osjogUsu@@Wi{3N^Iq!CfMAufkV>Ob(qF9vDrpE^lAoe!jf^@bYxJ z;rO!{Ua_VU>-Wm|%#-wu(3H9FgfkX$%bsBR;#H)6nD$>hk0Fj9uaNf=+m9=+wR`*H zvK?wXBMPyUT>lWrV|w%6%OWEdfZ%8EyBERc|9myjxIm$(IQ;Z+GBDi|MKEC)H2S^M zOgDm=7x-gQ;ZkUx8Jp3u*CkJpmPLR4@hABC&^MGt9cG6sP&L^)nDu|{q|E4SmevZ%|Cr z;d|LLxnm53wv@Uh+71eCU(2fnFK%wlbt?ma?gC(0D?z+)jkMvS${Dx27nex5=tB9` zkB7ZTc`kZ^EJZfC>#Mds_QQ8@`kcdgQy53Ih7q3`7d4aAdt^q!%dpU#JYX|u%KSGk G66Fk*-7GG{O}FfcbYRmey!N-5IKODxkX&S02xdw2C*-o)<~UcRg! zb&o${kKa{;qOK^lII}7hq*Uy}hTBtO@7o#}Rcy80&^hsw+>1z{QZo}1h1|@%%-qDB mcpz1xms6U^VE;nZ>*V8&>1SJ>98tef-}OCD`4s?pAvaB0%s;UJ literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/99/readonly_2c6604f456443ea726645083a1e33ab3ba3abc b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/99/readonly_2c6604f456443ea726645083a1e33ab3ba3abc new file mode 100644 index 0000000000000000000000000000000000000000..e5186ab9779bb53493f6faf416c83f030b02f02a GIT binary patch literal 542 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 MjmK$|0fHhET5^LJS^xk5 literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/a5/readonly_485ad8e3a1602c46b2e9811abd0452761c1e70 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/a5/readonly_485ad8e3a1602c46b2e9811abd0452761c1e70 new file mode 100644 index 0000000000000000000000000000000000000000..aa72db6885ba00cc2659f788652ef0130d2c7a4f GIT binary patch literal 1553 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 K+r>ZPgP~@4nO1@T literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ca/readonly_d3716f06cd3b80418b5bd06b7ddcf844502a58 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ca/readonly_d3716f06cd3b80418b5bd06b7ddcf844502a58 new file mode 100644 index 0000000000000000000000000000000000000000..18edeb571c40693a8cbe982877e65770c1116302 GIT binary patch literal 60 zcmV-C0K@-y0V^p=O;s>4VK6i>Ff%bx$Ve?pDbg)2$jK}z*2^hPWbidIv%0xQ`F_>u Sz0HxWsSh2*+L{2sc@t)xUm3Fi literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d1/readonly_7b920f681623037f9c4d83442aae19716c0744 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d1/readonly_7b920f681623037f9c4d83442aae19716c0744 new file mode 100644 index 0000000000000000000000000000000000000000..742e5aaed8cef49efc801c1c99d5238ff0466505 GIT binary patch literal 4244 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 literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d4/readonly_09b5ac3451619c127ef249a2d75e8e4f0693be b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d4/readonly_09b5ac3451619c127ef249a2d75e8e4f0693be new file mode 100644 index 0000000000000000000000000000000000000000..bd8b8767bf1b66a1fce92c0d89a27d77433a1816 GIT binary patch literal 433 zcmV;i0Z#sS0V^p=O;s>4GiNX{00ITQ^vsfs(jvj$J5!>FW8kKP&S|@dkOQQ)E8chuWenH_1;A@7^=iKF*`LaGbfed#^Qa`p3Ye) zV0l;j>XD{Iy9s}I%Ajh3Tpe9}T|su$&$(*XF^lWti8X9Dx$1+aP5NEX1PO(d)PiCL zb$>6NBYRs-p5LE8>GQv&lVX$I?4V|3q!y(V=@#Uarf25qmE`B0_D b_f2sstkMn8P|^z)5RXyYU$+?m+?u@2ll$WA literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d8/readonly_a3be96e59ca11039dd2bd5c482613e90fc0c76 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d8/readonly_a3be96e59ca11039dd2bd5c482613e90fc0c76 new file mode 100644 index 0000000..4cca567 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/d8/readonly_a3be96e59ca11039dd2bd5c482613e90fc0c76 @@ -0,0 +1,2 @@ +x[K1}m~Ű>k}hiUb"Bn&.T;I܇0MsNu'chwY%V\Y\5sv6n泧u?|-r f*Bwhx7vSctZ00ITi;NVc#_z+j$03XK?R|ZGRS)Kb&Nij_QzSLH} zvymfFMX%D(z`)GJL?Iy5$0t6>H89jQI0UXiFE@o@rcN5mmoOK*PJVeY!`~+kOoElhOU%|yDVlvz*vM@4{2Tz~YA?%ntUPQ0 literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/dd/readonly_ef3949f5d970786e78d88f1eb2687e68d3d476 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/dd/readonly_ef3949f5d970786e78d88f1eb2687e68d3d476 new file mode 100644 index 0000000000000000000000000000000000000000..c697993b9fe4ba025d9ea186be4c6225236b5025 GIT binary patch literal 1606 zcmV-M2D$lo0gYGNZrer>?X$jOkQYjTNykpn0RUm=Rl}Y>!Ms4spu=jA0NBb?ZM1ZbPZ?V6 zU<=XIK-U0v3)E%wexV$#Y*A}TxwcO7J1{pF<(OdS_HJkE0nSThm=aLWNaRP6`wj~S zPcw;x$YPINyutA@v^L~^!fHWI6_Tcnm+8z_DSz1}?c8`(NrV9oZEDLd)N280OjsI| z1tU%Q;`t zE2Zs?T&q0a-vP%3n*?N6wF#Nw%x3gX%p{8pL~E8k=p(OI@(2aB4h2)$jmrkJI#wyY z%J{|MXh5W9P$W~5-IdDUQ7i1n;39!BUswE9nqq*DNUvS)k*ru*CHo(CPJ;FB610O0 zt)mdrgpZIugp^6M&Mwcd&aNgez|`B*_m})6l9dG4K*<+pCX*j!UVE0_5Ee!Rln1vE zdmy(kiPDs=q_cVrmbkQIrV>YXW3~;DDr-FrxlluGqVSCzWx;)nw9Whp6>?h7v$^!j z%$q1BwU+8VohIy@XPZ0FJY zNJG@h%qe*u*@$pr6q5k^q@!nM#7d&X)T}`$tz`flm*WNhNrKtBq-SGdr^jhuVaF-J z9j9#a4(-YnD3;vYsu}hjXBj2?*<5)TJ~^SM;WY~N(k6_(a8-ts-Fr29kH?v}E8W_; zpHij+4FDYu*7P=Q!C@=Nkm(Dwwc8|i)9>>9^sJvtJG}wl4AC{t%hm8RB^(8)N`Jw> zJ)k(M@X zf!67(rq6abKoIP|OD%u@l>>b=#8WZ`7<*y32B|XN4)?Z`-KANJXI23e3>svY zEv%WLmve#GFSN@;2Ve$edkv+X%$FRRvL`aA2A9yCAvdT%GsE> zs6W@2qB6?frq0_%?n`|kst&33Y6_*}2BpzR1Xk|UAX8Y{4m^_jt?U3>40}WiS#y#W z31Y!Dx}7rch&E{%dgl!>MkBPc^S$^8!pPQ<*i_ZqJYYqt!-ZBGC8>sABN9S8VtWUw zhcF`auAK=xh@zkf{A?{F@vI^qIZ$z_=UTs)zDs>~_`ri4e>o4v?eH0V9$NE^?~1#* z*MPF{XI;0>_G(zCy{+>{9h}i$xfY;lcq(y`F|4}Tq+k@{t+&DJ52Hw-@GzwlO?n>q z3Nk$}r%G?^NFwE|HE({Uu@9oEnkF|83uFU4C4&k&jlRy8LWoW9F7IL;?}Prms2Dxq zeaMFx*iTaP0iMJgv6VE&<76XHe)5Wy>Ksone6zzeo!eVcy{3urbI>Hcqcrn$h-IG6 zLBd)`IlLN1zToS%q{KOBvf@FY^z&jd)jy=4@s~ceYqHdTjZ=tTc&8AZ);SBJ>U(iP*&J}0##{d8T literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/e2/readonly_a3be9442717a2d5028222e5310175c26bf7eb3 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/e2/readonly_a3be9442717a2d5028222e5310175c26bf7eb3 new file mode 100644 index 0000000000000000000000000000000000000000..589eb50b27bf8dc5943828c8dce807832e6c1c90 GIT binary patch literal 206 zcmV;<05Sh~0V^p=O;s>5v1BkbFfcPQQ7B0*E{RXh&r8$GDNSSu`oQ$rZAW!E-)?jF z8;lwLU#@=5LROfOT9i@*R=Mu19jD!yS1buLw0Xj=T=yWjz`(xTMj z%&JtdQifpj%51KZ$=da^CBxPhZ=dpQ2(NwX-hu8zW&ZBTlgQ|@%iZi I066GnqG2^`g#Z8m literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/f3/readonly_5b8b893f1aa6d80da27731c2a685771d9a614f b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/f3/readonly_5b8b893f1aa6d80da27731c2a685771d9a614f new file mode 100644 index 0000000000000000000000000000000000000000..855b148c00ecee7dfcd429c868c0369182de6f28 GIT binary patch literal 31 ncmbzD8 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 J3jiz%5v%%%6_fw~ literal 0 HcmV?d00001 diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ff/readonly_94999bc31a7ddc26674e3dbda98f68e0c133b6 b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_git/objects/ff/readonly_94999bc31a7ddc26674e3dbda98f68e0c133b6 new file mode 100644 index 0000000000000000000000000000000000000000..92e68841a1c1f84b828b9a7f05fdd9953a99aeac GIT binary patch literal 1491 zcmV;^1uXh_0limEkK9BM)7{=l zLO}coej&evSJk#>ygLgEheX;W?vLuK_o`miu4}iZ&%XHl$tTO@@_QZ3drh_5n(~T* zR!yx#AfJ}kI_|Z$bfev^X(+U{iGh@@=v(b8Ppz^#)-ZXw)-^tz zPmUctAh(g{tcE{$jn>-OZI4bxYO9P5aRlI}9k5c7)Bu>Swi=Jc9a%@C5o2K#$Hw3< z1X7MGImT{mOkD?gJZj$Y0J-8|U{5091^O@F9*oFKYz`W#I=QEO9)mT?uxs zX+^*OHtWAiXE$bxC9i+!z{NZROqCz#O553@KP`I?9+utlEFyXEB=&G_cAcA0NIVSc ziZ-eadd9(j<(jsOiRuZbh#BbKY99>;lfaO$UqlKKnReNjs;c!>zgKiEg`GF{O`!R5 zaWO1&s?pZFJ(kZGg9RWLX})$bI;1HX%P#@ozyA1hxCndWwW+nEXA%*u@~SzoOxao2 zo@F1hGk*Lm<3OK#9XoHab$%vWOZ?NLz3FT!jWVH83}Mg5#97P;rOQaPE&xFQo8zaz zm*J|Ko-~_uY(<$sl_BHDl>}SHIc8y%5U+SPzC|nCW6v|m*6duaXUhzyQD^x3-)=0^ zjq_~A=O4lI&Uy3RiKsk+X)fE7T#J|-Seufal;$+oV(~uP$=)OA=KI2`-1r}i9{`v# z`B6C+=l>h&$5-yNp7aRSg;E~V>!Fgliy_MAeifxkQ;q8&?zU^z+JCU7rdKE#Zz}Ym zw1p@8R#s}rYfhrrIC()`(1BBuQ=k8!QRTOJ$rhi|rx0)MfJJYIWyq>%(2ejvIfOHn z-l(pQ$A?6P^VwL+*Lh0h1D()*@$FE`$=qtP(sM3R`ZN`sr21q?l>jhAN@}LGK%4BN>Ki9g zHAPkx&KIrMw?-pL;Q(@Z)G29$EK1VMrpvwTjV*KpGj(P>aG(cy$~g%4a}^6VSTOg~ zWifcXo|2F~h4&cLxyQ4InkOgD(g&VQIWRRIU3+Z0_aqHJ#~Tm6DNUcT8FGdLI)ary zt&?e+b)Zj$f>Ysy1dLV=S=o!h-6xVm{VXq~Sit;y=Nerdm;d7|j%-cUV*Zd^Jfo+H zx{oFwH>uO~oh!*O$WH!BBRi#Tjf%S*s_6W@?=Y+gca?6^*ZN#fCZDb730F~?28U;! za` + 4. ... + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behaviour + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behaviour + validations: + required: true + - type: textarea + id: checkhealth + attributes: + label: ":checkhealth herdr-splits output" + description: Paste the relevant output of `:checkhealth herdr-splits` (Neovim ≥ 0.10). + render: shell + validations: + required: false + - type: textarea + id: config + attributes: + label: Setup / config + description: Your `setup({})` call and any relevant keybinds. + render: lua + validations: + required: false + - type: textarea + id: os + attributes: + label: OS / terminal + description: Operating system and terminal emulator (note if Option-as-Alt is enabled on macOS). + placeholder: "macOS 14 / Ghostty (macos-option-as-alt = true)" + validations: + required: false diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/ISSUE_TEMPLATE/feature_request.yml b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..6fecb93 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature request +description: Suggest an idea or improvement for herdr-splits.nvim +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What problem does this solve? What's currently hard or impossible to do? + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: What would you like herdr-splits.nvim to do instead? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any workarounds or other approaches you've tried. + validations: + required: false + - type: textarea + id: context + attributes: + label: Additional context + description: Anything else relevant — links, screenshots, related config. + validations: + required: false diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/PULL_REQUEST_TEMPLATE.md b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..992c660 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +## Summary + + + +## Changes + + + +- + +## Validation + + + +- [ ] `make test` passes +- [ ] `make check-shell` passes +- [ ] Plugin loads with no errors in a Neovim + Herdr session (`HERDR_ENV=1`) +- [ ] `:checkhealth herdr-splits` reports no failures +- [ ] Navigation (``) works across Neovim splits and Herdr panes +- [ ] Resizing (``) works across Neovim splits and Herdr panes +- [ ] Edge cases relevant to this change exercised (at_edge, count prefix, + floats, auto-unzoom) +- [ ] `README.md` / `herdr-plugin.toml` updated (if user-facing) + +## Notes for review + + + +Closes #NN diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/workflows/ci.yml b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/workflows/ci.yml new file mode 100644 index 0000000..2752c36 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + shell: + name: Bash syntax + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + - run: make check-shell + + test: + name: Neovim ${{ matrix.neovim-version }} + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + neovim-version: [v0.10.0, stable] + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + submodules: true + - uses: rhysd/action-setup-vim@febef33995d6649302e9d88dda81e071b68f16a7 # v1.6.1 + with: + neovim: true + version: ${{ matrix.neovim-version }} + - run: make test diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitignore b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitignore new file mode 100644 index 0000000..f35b8b8 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitignore @@ -0,0 +1 @@ +.pi-subagents/ diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitmodules b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitmodules new file mode 100644 index 0000000..8fbc9e7 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/dot_gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/mini.test"] + path = deps/mini.test + url = https://github.com/nvim-mini/mini.test.git diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/herdr-plugin.toml b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/herdr-plugin.toml new file mode 100644 index 0000000..df6d5ae --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/herdr-plugin.toml @@ -0,0 +1,62 @@ +id = "herdr-splits" +name = "Herdr Splits" +version = "0.5.1" +min_herdr_version = "0.7.0" +description = "Seamless navigation and resizing between Herdr panes and Neovim splits" +platforms = ["linux", "macos"] + +# Herdr-side navigation actions. These check if the focused pane is running +# Neovim and either forward the key chord or move Herdr pane focus. +# Bind them in ~/.config/herdr/config.toml: +# [[keys.command]] +# key = "ctrl+h" +# type = "plugin_action" +# command = "herdr-splits.nav-left" + +[[actions]] +id = "nav-left" +title = "Navigate left (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-nav.sh", "left"] + +[[actions]] +id = "nav-down" +title = "Navigate down (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-nav.sh", "down"] + +[[actions]] +id = "nav-up" +title = "Navigate up (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-nav.sh", "up"] + +[[actions]] +id = "nav-right" +title = "Navigate right (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-nav.sh", "right"] + +[[actions]] +id = "resize-left" +title = "Resize left (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-resize.sh", "left"] + +[[actions]] +id = "resize-down" +title = "Resize down (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-resize.sh", "down"] + +[[actions]] +id = "resize-up" +title = "Resize up (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-resize.sh", "up"] + +[[actions]] +id = "resize-right" +title = "Resize right (Neovim/Herder)" +contexts = ["global"] +command = ["bash", "scripts/herdr-resize.sh", "right"] diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/conf.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/conf.lua new file mode 100644 index 0000000..d17b920 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/conf.lua @@ -0,0 +1,236 @@ +---herdr-splits shared config file (generated artifact) + notation translator. +---Owns everything about `herdr-splits.conf`: path resolution, reading existing +---managed values, translating Neovim key notation to Herdr chord notation, +---and writing the resolved config back (atomically). Requires nothing else +---from this plugin (avoiding a require-cycle with config.lua). +---@class HerdrSplitsConf +local M = {} + +-- Neovim modifier prefix -> Herdr modifier name. +local MOD = { C = 'ctrl', M = 'alt', A = 'alt', S = 'shift', D = 'cmd' } +-- Herdr's documented specials: lowercase the terminal key when present. +local SPECIAL = { + left = 'left', right = 'right', up = 'up', down = 'down', + enter = 'enter', ['return'] = 'enter', tab = 'tab', + esc = 'esc', escape = 'esc', space = 'space', backspace = 'backspace', + del = 'delete', delete = 'delete', home = 'home', + ['end'] = 'end', pageup = 'pageup', pagedown = 'pagedown', +} + +-- Marker line stamped into every generated conf. Its presence means the file +-- is the plugin's own output: setup() is authoritative, so read_managed() does +-- NOT adopt it back (removing an opt reverts to the default on the next start). +local MARKER = '# herdr-splits-generated-v1' + +local uv = vim.uv or vim.loop + +-- Lazily-resolved cache of the shared config path, so setup() shells out to +-- `herdr plugin config-dir` at most once per process. +local resolved_path + +---Resolve the herdr binary: a config.herdr_bin override (when the config +---module is loaded), then HERDR_BIN_PATH, then 'herdr'. pcall-required so +---conf.lua never hard-requires config.lua at load time (cycle-safe). +---@return string +local function resolve_herdr_bin() + local ok, cfg = pcall(require, 'herdr-splits.config') + if ok and cfg.herdr_bin then return cfg.herdr_bin end + return vim.env.HERDR_BIN_PATH or 'herdr' +end + +---Best-effort `herdr plugin config-dir herdr-splits` query. Returns the +---directory printed by herdr (trimmed), or nil on any failure / empty output. +---@return string|nil +local function query_config_dir() + local ok, obj = pcall(vim.system, { resolve_herdr_bin(), 'plugin', 'config-dir', 'herdr-splits' }, { text = true }) + if not ok or not obj then return nil end + local wok, res = pcall(obj.wait, obj) + if not wok or not res or res.code ~= 0 then return nil end + local out = (res.stdout or ''):gsub('^%s+', ''):gsub('%s+$', '') + return out ~= '' and out or nil +end + +---Path to the shared herdr-splits config file (same file the Herdr-side +---scripts read). Precedence: (1) HERDR_SPLITS_CONFIG; (2) HERDR_PLUGIN_CONFIG_DIR +---+ `/herdr-splits.conf`; (3) `herdr plugin config-dir herdr-splits` (via the +---herdr binary) + `/herdr-splits.conf`; (4) XDG_CONFIG_HOME / ~/.config +---fallback. Resolved once and cached, so setup() shells out at most once. +---@return string +function M.path() + if resolved_path then return resolved_path end + if vim.env.HERDR_SPLITS_CONFIG and vim.env.HERDR_SPLITS_CONFIG ~= '' then + resolved_path = vim.env.HERDR_SPLITS_CONFIG + return resolved_path + end + if vim.env.HERDR_PLUGIN_CONFIG_DIR and vim.env.HERDR_PLUGIN_CONFIG_DIR ~= '' then + resolved_path = vim.env.HERDR_PLUGIN_CONFIG_DIR .. '/herdr-splits.conf' + return resolved_path + end + local dir = query_config_dir() + if dir then + resolved_path = dir .. '/herdr-splits.conf' + return resolved_path + end + local xdg = vim.env.XDG_CONFIG_HOME + local base = (xdg and xdg:sub(1, 1) == '/') and xdg + or ((vim.env.HOME or '~') .. '/.config') + resolved_path = base .. '/herdr/plugins/config/herdr-splits/herdr-splits.conf' + return resolved_path +end + +---Translate a Neovim key notation (e.g. ``, ``, `h`) into the +---Herdr chord notation the scripts and `herdr pane send-keys` expect +---(e.g. `ctrl+h`, `alt+left`, `h`). Returns nil for modifier-only / empty +---input so callers can fall back to a per-direction default. +---@param nvim_key string +---@return string|nil +function M.to_herdr(nvim_key) + if type(nvim_key) ~= 'string' or nvim_key == '' then return nil end + if not nvim_key:match('^<.+>$') then return nvim_key:lower() end -- plain "h" -> "h" + local inner = nvim_key:sub(2, -2) + local mods, key = {}, nil + for part in inner:gmatch('[^-]+') do + local m = MOD[part:upper()] + if m then + mods[#mods + 1] = m + else + key = SPECIAL[part:lower()] or part:lower() + end + end + if not key then return nil end + mods[#mods + 1] = key + return table.concat(mods, '+') +end + +---Parse the shared config file for the managed keys only. When the file +---carries the generated MARKER it is the plugin's own output and is NOT read +---back (setup() is authoritative — removing an opt reverts to default). When +---the MARKER is absent (a legacy/hand-edited conf) the managed values are +---parsed and adopted once; the second return value is true only when at +---least one managed value was found, so the caller can emit a one-time +---migration notice. Returns `({}, false)` for a missing/marker-tagged file; +---last occurrence wins (matches the scripts' `tail -n 1`). +---@return table values, boolean adopted +function M.read_managed() + local f = io.open(M.path(), 'r') + if not f then return {}, false end + local out = { nav_keys = {}, resize_keys = {} } + for line in f:lines() do + -- Generated marker anywhere => our own output; do not adopt it back. + if line:match('^%s*' .. vim.pesc(MARKER) .. '%s*$') then + f:close() + return {}, false + end + local k, v = line:match('^%s*([%w_]+)%s*=%s*([^%s#]+)') + if k and v then + local dir = k:match('^nav_key_(%a+)$') + if dir then + out.nav_keys[dir] = v + else + dir = k:match('^resize_key_(%a+)$') + if dir then + out.resize_keys[dir] = v + elseif k == 'unzoom_on_nav' then + out.unzoom_on_nav = (v ~= 'false') + elseif k == 'nav_at_edge' then + out.nav_at_edge = (v == 'stop' and 'stop' or 'wrap') + end + end + end + end + f:close() + local adopted = false + for _, t in pairs({ out.nav_keys, out.resize_keys }) do + for _ in pairs(t) do adopted = true; break end + end + if out.unzoom_on_nav ~= nil or out.nav_at_edge ~= nil then adopted = true end + return out, adopted +end + +---Write the full managed key set to the shared config with a generated-file +---header (including the MARKER), atomically via a unique same-dir temp + +---os.rename so a script never observes a partial write and two concurrent +---Neovim instances don't share a temp inode. Skips the write entirely when the +---content is unchanged (preserves mtime, reduces multi-instance contention). +---Returns (true, nil) on success or (false, err) on failure; callers wrap in +---pcall so a write failure never crashes startup. +---@param resolved table resolved managed config (nav_keys, resize_keys, unzoom_on_nav, nav_at_edge) +---@return boolean ok, string|nil err +function M.write(resolved) + local path = M.path() + local lines = { + MARKER, + '# herdr-splits plugin config — GENERATED by herdr-splits.nvim setup().', + '# Regenerated from defaults + setup() opts on every setup(); do not edit by hand.', + '# A headerless/legacy conf is adopted once on migration (see the startup', + '# notice), then overwritten thereafter — remove an opt in setup() to revert.', + 'nav_key_left=' .. resolved.nav_keys.left, + 'nav_key_down=' .. resolved.nav_keys.down, + 'nav_key_up=' .. resolved.nav_keys.up, + 'nav_key_right=' .. resolved.nav_keys.right, + 'resize_key_left=' .. resolved.resize_keys.left, + 'resize_key_down=' .. resolved.resize_keys.down, + 'resize_key_up=' .. resolved.resize_keys.up, + 'resize_key_right=' .. resolved.resize_keys.right, + 'unzoom_on_nav=' .. (resolved.unzoom_on_nav == false and 'false' or 'true'), + 'nav_at_edge=' .. (resolved.nav_at_edge == 'stop' and 'stop' or 'wrap'), + } + local content = table.concat(lines, '\n') .. '\n' + + -- Skip when unchanged: only when the existing file's bytes match exactly. + local existing = io.open(path, 'r') + if existing then + local prev = existing:read('*a') + existing:close() + if prev == content then return true end + end + + vim.fn.mkdir(vim.fn.fnamemodify(path, ':h'), 'p') + -- Unique same-dir temp so os.rename stays same-filesystem (atomic) and two + -- Neovim instances don't collide on a shared temp inode. + local tmp = path .. '.tmp.' .. vim.fn.getpid() .. '.' .. tostring(uv.hrtime()) + local f, err = io.open(tmp, 'w') + if not f then + pcall(os.remove, tmp) + vim.notify('herdr-splits: failed to open conf temp file: ' .. tostring(err), vim.log.levels.WARN) + return false, err + end + local wok, werr = f:write(content) + -- file:close() flushes buffered output and can itself fail with a delayed + -- write error (disk full, NFS, quota). Its result MUST be checked before + -- os.rename: otherwise an incomplete temp replaces the live config and the + -- atomic-write guarantee is lost. LuaJIT returns true on success or + -- (nil, err) on a flush failure — same form as file:write, checked here too. + local cok, cerr = f:close() + if (not wok) or (not cok) then + local why = (not wok) and werr or cerr + pcall(os.remove, tmp) + vim.notify('herdr-splits: failed to write conf temp file: ' .. tostring(why), vim.log.levels.WARN) + return false, why + end + local ok, rerr = os.rename(tmp, path) + if not ok then + pcall(os.remove, tmp) + vim.notify('herdr-splits: failed to write conf: ' .. tostring(rerr), vim.log.levels.WARN) + end + return ok, rerr +end + +---Runnable self-check for the notation translator. Asserts on the documented +---mapping table; returns true on success. Intended for headless validation: +---`nvim --headless -c "lua assert(require('herdr-splits.conf')._selfcheck())" -c "qa"`. +---@return boolean +function M._selfcheck() + local cases = { + { '', 'ctrl+h' }, { '', 'ctrl+j' }, { '', 'ctrl+k' }, { '', 'ctrl+l' }, + { '', 'alt+h' }, { '', 'alt+left' }, { '', 'shift+left' }, + { '', 'cmd+x' }, { '', 'ctrl+alt+h' }, { 'h', 'h' }, { '', 'esc' }, + } + for _, c in ipairs(cases) do + local got = M.to_herdr(c[1]) + assert(got == c[2], ('to_herdr(%q)=%q want %q'):format(c[1], tostring(got), c[2])) + end + return true +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/config.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/config.lua new file mode 100644 index 0000000..1763803 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/config.lua @@ -0,0 +1,146 @@ +---@class HerdrSplitsConfig +---@field default_amount number Resize amount as Herdr ratio (float, e.g. 0.03 = 3%) +---@field neovim_amount number Resize amount for native Neovim resizes (integer cells, default 3) +---@field at_edge 'wrap'|'stop'|'split'|function Neovim window-edge behavior (distinct from nav_at_edge below) +---@field ignored_buftypes string[] Buffer types ignored during resize +---@field ignored_filetypes string[] Filetypes ignored during resize +---@field move_cursor_same_row boolean Keep cursor on same screen row when moving left/right +---@field herdr_bin string|nil Path to herdr binary (auto-detected if nil) +---@field ignored_events string[] Autocmd events to ignore during resize operations +---@field auto_sync_herdr boolean|nil If true, auto-sync the Herdr-managed checkout to match this lazy commit (opt-in; default false) +---@field floating_zindex_max number Threshold below which a floating window's zindex classifies it as an embedded sidebar (default 50; Neovim's default float zindex) +---@field ignore_previewwindows boolean If true, vim.wo[winid].previewwindow windows (e.g. dadbod `.dbout`) are treated as sidebars (opt-in; default false) +--- Managed config (written to herdr-splits.conf by setup()). Pass Neovim +--- notation (e.g. ``, ``); stored/written in Herdr notation. +---@field nav_keys table Forward chords per direction: {left,down,up,right} (Neovim notation in opts; Herdr notation internally) +---@field resize_keys table Resize forward chords per direction: {left,down,up,right} (Neovim notation in opts; Herdr notation internally) +---@field unzoom_on_nav boolean If true, auto-unzoom when navigating away from a zoomed pane (default true) +---@field nav_at_edge 'wrap'|'stop' Herdr pane-boundary wrap behavior (distinct from at_edge, which is the Neovim window-edge behavior) + +-- Managed config defaults (Herdr notation). The single source of truth for +-- the key set written to herdr-splits.conf; deep-copied into M below and into +-- the setup() merge base so the two cannot drift. +local managed_defaults = { + nav_keys = { left = 'ctrl+h', down = 'ctrl+j', up = 'ctrl+k', right = 'ctrl+l' }, + resize_keys = { left = 'alt+h', down = 'alt+j', up = 'alt+k', right = 'alt+l' }, + unzoom_on_nav = true, + nav_at_edge = 'wrap', +} + +local M = { + default_amount = 0.03, + neovim_amount = 3, + at_edge = 'wrap', + ignored_buftypes = { 'nofile', 'quickfix', 'prompt', 'help', 'terminal' }, + ignored_filetypes = { + 'NvimTree', + -- sidebars + 'neo-tree', + 'snacks_dashboard', + 'snacks_explorer', + 'snacks_picker', + -- DB / REPL / data sidebars + 'dadbod-ui', + 'dbout', + -- outlines / symbols + 'aerial', + 'Outline', + -- diagnostics / quick lists + 'Trouble', + 'quickfix', + }, + move_cursor_same_row = false, + herdr_bin = nil, + ignored_events = { 'BufEnter', 'WinEnter' }, + auto_sync_herdr = false, + floating_zindex_max = 50, + ignore_previewwindows = false, + -- Managed config: deep-copied from managed_defaults (single source). + -- Stored in Herdr chord notation; setup() translates user Neovim notation + -- and writes these to herdr-splits.conf for the scripts. + nav_keys = vim.deepcopy(managed_defaults.nav_keys), + resize_keys = vim.deepcopy(managed_defaults.resize_keys), + unzoom_on_nav = managed_defaults.unzoom_on_nav, + nav_at_edge = managed_defaults.nav_at_edge, +} + +local conf = require('herdr-splits.conf') + +---Apply user configuration on top of defaults and publish the managed keys +---to the shared `herdr-splits.conf` (so the Herdr-side scripts agree). +---Idempotent in outcome: managed keys (nav_keys/resize_keys/unzoom_on_nav/nav_at_edge) +---rebuild from defaults + opts each call; non-managed opts merge into existing config. +--- +---Managed keys (nav_keys/resize_keys/unzoom_on_nav/nav_at_edge) resolve with +---precedence: defaults -> existing conf value (adopt-once) -> explicit opt. +---User chords are given in Neovim notation and translated to Herdr notation +---before merging; the resolved set is written to the conf atomically (write +---failures never crash startup). +---@param opts table|nil +function M.setup(opts) + opts = opts or {} + + -- Apply herdr_bin from opts before the first conf.path() resolution so the + -- documented precedence (config.herdr_bin -> HERDR_BIN_PATH -> 'herdr') holds. + if opts.herdr_bin ~= nil then M.herdr_bin = opts.herdr_bin end + + -- Managed opts: translate user chords nvim -> herdr, capture only what was passed. + local user = {} + if opts.nav_keys then + user.nav_keys = {} + for dir, k in pairs(opts.nav_keys) do user.nav_keys[dir] = conf.to_herdr(k) end + end + if opts.resize_keys then + user.resize_keys = {} + for dir, k in pairs(opts.resize_keys) do user.resize_keys[dir] = conf.to_herdr(k) end + end + if opts.unzoom_on_nav ~= nil then user.unzoom_on_nav = opts.unzoom_on_nav end + if opts.nav_at_edge ~= nil then user.nav_at_edge = opts.nav_at_edge end + + -- Precedence: defaults -> legacy/headerless conf (adopt once) -> explicit + -- user opts. read_managed returns {} (no adoption) once the conf carries + -- our generated marker, so removing an opt reverts to the default next start. + local existing, migrated = conf.read_managed() + local resolved = vim.deepcopy(managed_defaults) + resolved = vim.tbl_deep_extend('force', resolved, existing) + resolved = vim.tbl_deep_extend('force', resolved, user) + + M.nav_keys, M.resize_keys = resolved.nav_keys, resolved.resize_keys + M.unzoom_on_nav, M.nav_at_edge = resolved.unzoom_on_nav, resolved.nav_at_edge + + -- One-time migration notice: a headerless/legacy conf was adopted. Fires + -- once — after this setup() the conf carries the marker, so later setups + -- don't adopt. Tell the user to move these into setup() to persist them. + if migrated then + local parts = {} + for _, kind in ipairs({ 'nav_keys', 'resize_keys' }) do + for dir, v in pairs(existing[kind] or {}) do + parts[#parts + 1] = ('%s.%s=%s'):format(kind, dir, v) + end + end + if existing.unzoom_on_nav ~= nil then + parts[#parts + 1] = 'unzoom_on_nav=' .. tostring(existing.unzoom_on_nav) + end + if existing.nav_at_edge ~= nil then + parts[#parts + 1] = 'nav_at_edge=' .. existing.nav_at_edge + end + vim.notify( + 'herdr-splits: adopted legacy conf values (' .. table.concat(parts, ', ') + .. '). Add them to setup() (Neovim notation) to keep them — the conf is ' + .. 'now regenerated on every setup().', + vim.log.levels.INFO + ) + end + + -- Non-managed opts (default_amount, at_edge, ignored_filetypes, …) merge as before. + local other = vim.deepcopy(opts) + other.nav_keys, other.resize_keys = nil, nil + other.unzoom_on_nav, other.nav_at_edge = nil, nil + local merged = vim.tbl_deep_extend('force', M, other) + for k, v in pairs(merged) do M[k] = v end + + -- Publish to the shared conf for the Herdr scripts (never fatal). + pcall(conf.write, resolved) +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/health.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/health.lua new file mode 100644 index 0000000..3751026 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/health.lua @@ -0,0 +1,46 @@ +---:checkhealth herdr-splits integration. +---Discovered automatically by Neovim's built-in `:checkhealth` via the +---runtimefile lookup in `lua//health.lua`. +---Requires Neovim ≥ 0.10 for `vim.health`. + +local M = {} + +function M.check() + local config = require('herdr-splits.config') + local herdr = require('herdr-splits.herdr') + local win = require('herdr-splits.win') + + vim.health.start('herdr-splits') + + if herdr.is_in_session() then + vim.health.ok( + string.format( + 'Running inside a Herdr session (HERDR_ENV=1, HERDR_PANE_ID=%s)', + tostring(vim.env.HERDR_PANE_ID) + ) + ) + else + vim.health.warn( + 'Not inside a Herdr session (HERDR_ENV=' + .. tostring(vim.env.HERDR_ENV) + .. ') — nav/resize will not cross pane boundaries.' + ) + end + + vim.health.info(string.format('ignored_buftypes: %s', vim.inspect(config.ignored_buftypes))) + vim.health.info(string.format('ignored_filetypes: %s', vim.inspect(config.ignored_filetypes))) + vim.health.info(string.format('floating_zindex_max: %s', tostring(config.floating_zindex_max))) + vim.health.info(string.format('ignore_previewwindows: %s', tostring(config.ignore_previewwindows))) + + if win.is_embedded_floating_window() then + vim.health.warn('Current window is an embedded floating window (zindex < 50) — treat as sidebar.') + end + if win.is_ignored_win() then + vim.health.info('Current window matches an ignored filetype/buftype.') + end + if win.is_floating() and not win.is_embedded_floating_window() then + vim.health.info('Current window is a true floating popup — nav/resize will forward to Herdr.') + end +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/herdr.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/herdr.lua new file mode 100644 index 0000000..a5485ad --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/herdr.lua @@ -0,0 +1,160 @@ +---Herdr CLI wrapper. All Herdr subprocess calls go through this module. +---@class HerdrSplitsHerdr +local M = {} + +local config = require('herdr-splits.config') + +---Resolve the herdr binary path. +---@return string +function M.herdr_bin() + return config.herdr_bin or vim.env.HERDR_BIN_PATH or 'herdr' +end + +---Check if Neovim is running inside a Herdr session. +---@return boolean +function M.is_in_session() + return vim.env.HERDR_ENV == '1' and vim.env.HERDR_PANE_ID ~= nil and #vim.env.HERDR_PANE_ID > 0 +end + +---Get the current Herdr pane ID. +---@return string|nil +function M.current_pane_id() + if not M.is_in_session() then + return nil + end + return vim.env.HERDR_PANE_ID +end + +---Run a herdr CLI command and return stdout, stderr, and exit code. +---@param args string[] +---@return string stdout, string stderr, number exit_code +local function herdr_exec(args) + local cmd = vim.list_extend({ M.herdr_bin() }, args, 1, #args) + local obj = vim.system(cmd, { text = true }):wait() + return obj.stdout, obj.stderr, obj.code +end + +---Check if the current Herdr pane is at the layout boundary in the given direction. +---Calls `herdr pane edges --current` and parses the JSON response. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@return boolean|nil true if at edge, false if neighbor exists, nil on error +function M.current_pane_at_edge(direction) + if not M.is_in_session() then + return nil + end + + local edge_key = direction + local stdout, _, code = herdr_exec({ 'pane', 'edges', '--current' }) + + if code ~= 0 or not stdout or #stdout == 0 then + return nil + end + + local ok, data = pcall(vim.json.decode, stdout) + if + not ok + or type(data) ~= 'table' + or type(data.result) ~= 'table' + or type(data.result.edges) ~= 'table' + then + return nil + end + + local at_edge = data.result.edges[edge_key] + if type(at_edge) ~= 'boolean' then + return nil + end + return at_edge +end + +---Check if the current Herdr pane is zoomed. +---Calls `herdr pane layout --current` and parses JSON. +---@return boolean|nil true if zoomed, false if not, nil on error +function M.current_pane_is_zoomed() + if not M.is_in_session() then + return nil + end + + local stdout, _, code = herdr_exec({ 'pane', 'layout', '--current' }) + + if code ~= 0 or not stdout or #stdout == 0 then + return nil + end + + local ok, data = pcall(vim.json.decode, stdout) + if + not ok + or type(data) ~= 'table' + or type(data.result) ~= 'table' + or type(data.result.layout) ~= 'table' + then + return nil + end + + local zoomed = data.result.layout.zoomed + if type(zoomed) ~= 'boolean' then + return nil + end + return zoomed +end + +---Focus a Herdr pane in the given direction. +---Calls `herdr pane focus --direction --current`. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@return boolean true on success +function M.focus_pane(direction) + if not M.is_in_session() then + return false + end + + local _, _, code = herdr_exec({ 'pane', 'focus', '--direction', direction, '--current' }) + return code == 0 +end + +---Unzoom the current Herdr pane (turn off zoom). +---Calls `herdr pane zoom --off --current`. +---@return boolean true on success +function M.unzoom() + if not M.is_in_session() then + return false + end + + local _, _, code = herdr_exec({ 'pane', 'zoom', '--off', '--current' }) + return code == 0 +end + +---Check whether auto-unzoom is enabled. +---Reads `config.unzoom_on_nav` (resolved from setup(); default: enabled). +---@return boolean +function M.unzoom_enabled() + return config.unzoom_on_nav ~= false +end + +---Edge-wrap behavior across the Herdr pane boundary, read from the resolved +---in-memory config (the same value written to the shared conf that the +---Herdr-side scripts read). `stop` suppresses wrap-across-boundary on both +---the plain-pane side (handled by the Herdr script) and the Neovim +---edge-wrap side (handled here). Anything else defaults to `wrap`. +---@return '"wrap"'|'"stop"' +function M.nav_at_edge() + return config.nav_at_edge == 'stop' and 'stop' or 'wrap' +end + +---Resize the current Herdr pane in the given direction. +---Calls `herdr pane resize --direction --amount --current`. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@param amount number Float ratio (e.g., 0.03 = 3% of terminal dimension) +---@return boolean true on success +function M.resize_pane(direction, amount) + if not M.is_in_session() then + return false + end + + local amount_str = string.format('%.4f', amount) + local _, _, code = herdr_exec({ + 'pane', 'resize', '--direction', direction, '--amount', amount_str, '--current', + }) + return code == 0 +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/init.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/init.lua new file mode 100644 index 0000000..3782090 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/init.lua @@ -0,0 +1,162 @@ +---herdr-splits.nvim — Seamless navigation and resizing between Neovim splits +---and Herdr panes. Makes Herdr terminal splits behave like native Neovim windows. +--- +---Usage: +--- require('herdr-splits').setup({ ... }) +--- vim.keymap.set('n', '', require('herdr-splits').move_cursor_left) +--- vim.keymap.set('n', '', require('herdr-splits').resize_left) + +local config = require('herdr-splits.config') +local nav = require('herdr-splits.nav') +local resize_mod = require('herdr-splits.resize') +local win = require('herdr-splits.win') +local sync = require('herdr-splits.sync') + +local M = {} + +---Apply configuration. Idempotent — calling again merges into existing config. +---@param opts table|nil Configuration options (see config.lua for defaults) +function M.setup(opts) + config.setup(opts) + pcall(sync.sync) +end + +---Sync the Herdr-managed checkout to match this lazy commit. +---Intended as a lazy.nvim `build` hook entry point; respects `auto_sync_herdr`. +---Safe to call at any time; no-op unless opted in. +function M.sync_herdr() + pcall(sync.sync) +end + +---Resize the current split to the left. +---Amount defaults to config.default_amount. Multiplied by vim.v.count1. +---@param amount number|nil +function M.resize_left(amount) + local eventignore_orig = vim.o.eventignore + vim.o.eventignore = table.concat(config.ignored_events, ',') + resize_mod.is_resizing = true + local cur_win = vim.api.nvim_get_current_win() + local ok, err = pcall(resize_mod.resize, 'left', amount) + if not ok then + vim.notify('herdr-splits: resize_left failed: ' .. tostring(err), vim.log.levels.ERROR) + end + pcall(vim.api.nvim_set_current_win, cur_win) + resize_mod.is_resizing = false + vim.o.eventignore = eventignore_orig +end + +---Resize the current split downward. +---@param amount number|nil +function M.resize_down(amount) + local eventignore_orig = vim.o.eventignore + vim.o.eventignore = table.concat(config.ignored_events, ',') + resize_mod.is_resizing = true + local cur_win = vim.api.nvim_get_current_win() + local ok, err = pcall(resize_mod.resize, 'down', amount) + if not ok then + vim.notify('herdr-splits: resize_down failed: ' .. tostring(err), vim.log.levels.ERROR) + end + pcall(vim.api.nvim_set_current_win, cur_win) + resize_mod.is_resizing = false + vim.o.eventignore = eventignore_orig +end + +---Resize the current split upward. +---@param amount number|nil +function M.resize_up(amount) + local eventignore_orig = vim.o.eventignore + vim.o.eventignore = table.concat(config.ignored_events, ',') + resize_mod.is_resizing = true + local cur_win = vim.api.nvim_get_current_win() + local ok, err = pcall(resize_mod.resize, 'up', amount) + if not ok then + vim.notify('herdr-splits: resize_up failed: ' .. tostring(err), vim.log.levels.ERROR) + end + pcall(vim.api.nvim_set_current_win, cur_win) + resize_mod.is_resizing = false + vim.o.eventignore = eventignore_orig +end + +---Resize the current split to the right. +---@param amount number|nil +function M.resize_right(amount) + local eventignore_orig = vim.o.eventignore + vim.o.eventignore = table.concat(config.ignored_events, ',') + resize_mod.is_resizing = true + local cur_win = vim.api.nvim_get_current_win() + local ok, err = pcall(resize_mod.resize, 'right', amount) + if not ok then + vim.notify('herdr-splits: resize_right failed: ' .. tostring(err), vim.log.levels.ERROR) + end + pcall(vim.api.nvim_set_current_win, cur_win) + resize_mod.is_resizing = false + vim.o.eventignore = eventignore_orig +end + +---Move cursor to the left, crossing into Herdr pane if at Neovim edge. +---@param opts table|nil { same_row: boolean|nil, at_edge: string|function|nil } +function M.move_cursor_left(opts) + resize_mod.is_resizing = false + local ok, err = pcall(nav.move_cursor, 'left', opts) + if not ok then + vim.notify('herdr-splits: move_cursor_left failed: ' .. tostring(err), vim.log.levels.ERROR) + end +end + +---Move cursor downward, crossing into Herdr pane if at Neovim edge. +---@param opts table|nil +function M.move_cursor_down(opts) + resize_mod.is_resizing = false + local ok, err = pcall(nav.move_cursor, 'down', opts) + if not ok then + vim.notify('herdr-splits: move_cursor_down failed: ' .. tostring(err), vim.log.levels.ERROR) + end +end + +---Move cursor upward, crossing into Herdr pane if at Neovim edge. +---@param opts table|nil +function M.move_cursor_up(opts) + resize_mod.is_resizing = false + local ok, err = pcall(nav.move_cursor, 'up', opts) + if not ok then + vim.notify('herdr-splits: move_cursor_up failed: ' .. tostring(err), vim.log.levels.ERROR) + end +end + +---Move cursor to the right, crossing into Herdr pane if at Neovim edge. +---@param opts table|nil +function M.move_cursor_right(opts) + resize_mod.is_resizing = false + local ok, err = pcall(nav.move_cursor, 'right', opts) + if not ok then + vim.notify('herdr-splits: move_cursor_right failed: ' .. tostring(err), vim.log.levels.ERROR) + end +end + +---Append a filetype to ignored_filetypes at runtime (de-duped). +---@param name string Filetype name, e.g. 'fugitive' +---@return boolean true if added, false if already present +function M.add_ignored_filetype(name) + for _, v in ipairs(config.ignored_filetypes) do + if v == name then + return false + end + end + table.insert(config.ignored_filetypes, name) + return true +end + +---Append a buftype to ignored_buftypes at runtime (de-duped). +---@param name string Buffer type, e.g. 'help' +---@return boolean true if added, false if already present +function M.add_ignored_buftype(name) + for _, v in ipairs(config.ignored_buftypes) do + if v == name then + return false + end + end + table.insert(config.ignored_buftypes, name) + return true +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/nav.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/nav.lua new file mode 100644 index 0000000..56ecca3 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/nav.lua @@ -0,0 +1,206 @@ +---Navigation logic: seamless movement between Neovim splits and Herdr panes. +---@class HerdrSplitsNav +local M = {} + +local config = require('herdr-splits.config') +local herdr = require('herdr-splits.herdr') +local win = require('herdr-splits.win') + +---Treat the current window as a sidebar that should not be navigated through. +---Combines the configured ignore lists with the embedded-float heuristic. +---@return boolean +local function is_sidebar() + return win.is_ignored_or_preview() or win.is_embedded_floating_window() +end + +---Split a new Neovim window using the user's placement preferences. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +local function split_edge(direction) + if direction == 'left' or direction == 'right' then + vim.cmd('vsp') + else + vim.cmd('sp') + end +end + +---Move cursor between Neovim splits, falling through to Herdr at edges. +---This is the core navigation function. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@param opts table|nil { same_row: boolean|nil, at_edge: string|function|nil } +function M.move_cursor(direction, opts) + local same_row = config.move_cursor_same_row + local at_edge_behavior = config.at_edge + + if type(opts) == 'table' then + if opts.same_row ~= nil then + same_row = opts.same_row + end + if opts.at_edge ~= nil then + at_edge_behavior = opts.at_edge + end + end + + -- Handle floating windows: just forward to Herdr, + -- EXCEPT for embedded sidebars (snacks/neo-tree float/aerial float): + -- those should stay in Neovim so the user can keep interacting + -- with the picker. + if win.is_floating() and not win.is_embedded_floating_window() then + herdr.focus_pane(direction) + return + end + + local dir_key = win.dir_keys[direction] + local offset = vim.fn.winline() + vim.api.nvim_win_get_position(0)[1] + + -- Save current window to detect if wincmd changes it + local prev_win = vim.api.nvim_get_current_win() + + -- Try moving within Neovim first + local will_wrap = false + local count = vim.v.count1 + local target_winnr = vim.fn.winnr(count .. dir_key) + if count > 1 then + local prev_winnr = vim.fn.winnr((count - 1) .. dir_key) + will_wrap = target_winnr == prev_winnr + else + will_wrap = target_winnr == vim.fn.winnr() + end + + -- Command-line window (q:, q/, q?): Neovim forbids all window commands + -- (E11). Never wincmd; at a Neovim screen edge, delegate to Herdr + -- (subprocess-safe, does not close the cmdwin); otherwise silent no-op. + -- Mirrors smart-splits.nvim PR #464. + if win.is_command_line_window() then + if will_wrap and herdr.is_in_session() then + local at_herdr_edge = herdr.current_pane_at_edge(direction) + if at_herdr_edge == false then + herdr.focus_pane(direction) + elseif at_herdr_edge == true and herdr.nav_at_edge() ~= 'stop' then + herdr.focus_pane(win.reverse_direction[direction]) + end + end + return + end + + -- Execute the wincmd + if will_wrap and count == 1 then + vim.cmd('wincmd ' .. dir_key) + else + vim.cmd(count .. 'wincmd ' .. dir_key) + end + + if vim.api.nvim_get_current_win() ~= prev_win then + -- Moved within Neovim. Restore same-row if configured. + if (direction == 'left' or direction == 'right') and same_row then + local row = offset - vim.api.nvim_win_get_position(0)[1] + if row > 0 then + vim.cmd('normal! ' .. row .. 'H') + end + end + return + end + + -- We're at a Neovim edge. Try to cross into Herdr. + if not herdr.is_in_session() then + if will_wrap and count == 1 then + local sidebar = is_sidebar() + if type(at_edge_behavior) == 'function' then + at_edge_behavior({ + direction = direction, + split = function() split_edge(direction) end, + is_sidebar = sidebar, + wrap = function() + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end, + }) + elseif at_edge_behavior == 'stop' then + return + elseif at_edge_behavior == 'split' then + if not sidebar then + split_edge(direction) + end + else -- 'wrap' (default) + -- Wrap is allowed even from filetype-based sidebars (dbui, neo-tree, + -- ...) so the user can leave them; only embedded floats stay gated. + if not win.is_embedded_floating_window() then + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end + end + end + return + end + + -- Check zoom state: unzoom first, then retry Neovim navigation. + -- Must happen BEFORE the at_herdr_edge check — when zoomed, the pane fills + -- the screen so herdr reports it as being at every edge, making + -- at_herdr_edge useless until we unzoom. + if herdr.unzoom_enabled() and herdr.current_pane_is_zoomed() then + herdr.unzoom() + -- Retry wincmd — other Neovim splits may now be visible + vim.cmd('wincmd ' .. dir_key) + if vim.api.nvim_get_current_win() ~= prev_win then + if (direction == 'left' or direction == 'right') and same_row then + local row = offset - vim.api.nvim_win_get_position(0)[1] + if row > 0 then + vim.cmd('normal! ' .. row .. 'H') + end + end + return + end + -- Still at edge after unzoom; fall through to Herdr edge check. + end + + -- Check if we're at the Herdr edge too + local at_herdr_edge = herdr.current_pane_at_edge(direction) + if at_herdr_edge == nil then + if will_wrap and count == 1 and not win.is_embedded_floating_window() then + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end + return + end + + if not at_herdr_edge then + -- There's a Herdr pane in this direction. Cross the boundary. + local moved = herdr.focus_pane(direction) + if not moved and will_wrap and count == 1 and not win.is_embedded_floating_window() then + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end + return + end + + -- At both Neovim AND Herdr edges (no herdr pane to cross into). + -- Apply at_edge behavior. (Any needed unzoom already happened above.) + if type(at_edge_behavior) == 'function' then + at_edge_behavior({ + direction = direction, + split = function() split_edge(direction) end, + is_sidebar = is_sidebar(), + wrap = function() + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end, + }) + elseif at_edge_behavior == 'stop' then + return + elseif at_edge_behavior == 'split' then + if not is_sidebar() then + split_edge(direction) + end + else -- 'wrap' (default) + -- Wrap is allowed from filetype-based sidebars (dbui, neo-tree, ...) so + -- you can leave them at an edge; only embedded floating overlays stay gated. + if will_wrap and count == 1 and not win.is_embedded_floating_window() then + -- Wrap to the opposite side. If a Herdr pane exists there AND nav_at_edge + -- allows wrap-across-boundary (the default), cross into it; otherwise + -- wrap within Neovim. nav_at_edge=stop keeps the wrap inside Neovim. + if herdr.nav_at_edge() ~= 'stop' + and herdr.current_pane_at_edge(win.reverse_direction[direction]) == false + then + herdr.focus_pane(win.reverse_direction[direction]) + else + vim.cmd('wincmd ' .. win.dir_keys_reverse[direction]) + end + end + end +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/resize.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/resize.lua new file mode 100644 index 0000000..ff94999 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/resize.lua @@ -0,0 +1,108 @@ +---Resize logic: seamless resize between Neovim splits and Herdr panes. +--- +---Mental model (same as smart-splits.nvim): +--- The direction (up/down/left/right) is which way to move the split border. +--- The +/- operator depends on whether current window is above/below or +--- left/right of the neighbor being resized against. +--- +---When at a Neovim edge with no neighbor in that direction, and the window +---fills the terminal, the resize is forwarded to Herdr. +---@class HerdrSplitsResize +local M = {} + +local config = require('herdr-splits.config') +local herdr = require('herdr-splits.herdr') +local win = require('herdr-splits.win') + +M.is_resizing = false + +---Compute the +/- operator for a vertical resize. +---At start/middle: resize_up shrinks (-), resize_down grows (+). +---At last (bottom): inverted — resize_up grows (+), resize_down shrinks (-). +---@param direction '"up"'|'"down"' +---@return '"+""'|'"-""' +local function compute_vertical(direction) + local pos = win.win_position(direction) + if pos == 'start' or pos == 'middle' then + return direction == 'down' and '+' or '-' + end + return direction == 'down' and '-' or '+' +end + +---Compute the +/- operator for a horizontal resize. +---At start/middle: resize_left shrinks (-), resize_right grows (+). +---At last (right): inverted — resize_left grows (+), resize_right shrinks (-). +---@param direction '"left"'|'"right"' +---@return '"+""'|'"-""' +local function compute_horizontal(direction) + local pos = win.win_position(direction) + if pos == 'start' or pos == 'middle' then + return direction == 'right' and '+' or '-' + end + return direction == 'right' and '-' or '+' +end + +---Resize in a direction. +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@param amount number|nil Override amount in Neovim cells. When nil, uses +--- vim.v.count1 * neovim_amount for native or count * default_amount for Herdr. +function M.resize(direction, amount) + local count = vim.v.count1 + local has_explicit = amount ~= nil + + -- Embedded floating sidebars (snacks float, neo-tree float, aerial float): + -- refuse to resize; the picker owns its own dimensions. + if win.is_embedded_floating_window() then + return + end + + -- Floating windows: forward to Herdr + if win.is_floating() then + local ratio = has_explicit and amount or (count * config.default_amount) + herdr.resize_pane(direction, ratio) + return + end + + -- Delegate to Herdr ONLY when at both Neovim edges in this dimension + -- AND the window fills the terminal (full_width or full_height). + local delegate = false + -- Never delegate from inside a sidebar; the ignore list applies to resize + -- too. The command-line window is exempted: it is `nofile` (so it trips this + -- rule) yet a full-width cmdwin should still resize the Herdr pane sideways. + local in_sidebar = win.is_ignored_or_preview() + if direction == 'left' or direction == 'right' then + delegate = win.is_full_width() and win.at_left_edge() and win.at_right_edge() and herdr.is_in_session() + else + delegate = win.is_full_height() and win.at_top_edge() and win.at_bottom_edge() and herdr.is_in_session() + end + if delegate and in_sidebar and not win.is_command_line_window() then + delegate = false + end + + if delegate and herdr.current_pane_is_zoomed() then + delegate = false + end + + if delegate then + local ratio = (has_explicit and amount < 1) and amount or (count * config.default_amount) + herdr.resize_pane(direction, ratio) + return + end + + -- Native Neovim resize + local cells = has_explicit and math.floor(amount) or (count * config.neovim_amount) + if cells <= 0 then + cells = 1 + end + + local is_horiz = direction == 'left' or direction == 'right' + local op = is_horiz and compute_horizontal(direction) or compute_vertical(direction) + + if is_horiz then + pcall(vim.cmd, 'vertical resize ' .. op .. cells) + else + pcall(vim.cmd, 'resize ' .. op .. cells) + end +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/sync.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/sync.lua new file mode 100644 index 0000000..ddef394 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/sync.lua @@ -0,0 +1,107 @@ +---Auto-sync the Herdr-managed checkout to match the lazy.nvim checkout. +--- +---Herdr has no `plugin update` command in v1; the only sanctioned refresh is +---reinstall from GitHub. When lazy.nvim pulls a new commit, the bash scripts +---under the Herdr-managed checkout stay frozen at the old commit unless the +---user manually reinstalls. This module closes that gap: it reinstalls the +---Herdr-managed checkout pinned to the exact commit lazy fetched, so the bash +---scripts match the lua side byte-for-byte. +--- +---Entirely a no-op unless `auto_sync_herdr = true` is set in setup(). Also a +---no-op when: the herdr binary is unavailable, the plugin is installed as a +---local link (dev mode — reinstall would be refused anyway), the plugin is not +---installed, or the commits already match. +local M = {} + +local config = require('herdr-splits.config') + +---Plugin id and GitHub source as used by `herdr plugin install`. +local PLUGIN_ID = 'herdr-splits' +local PLUGIN_SOURCE = 'lmilojevicc/herdr-splits.nvim' + +---Run the sync. Safe to call at any time; never throws. +function M.sync() + if config.auto_sync_herdr ~= true then + return + end + + local ok, err = pcall(function() + local bin = config.herdr_bin or vim.env.HERDR_BIN_PATH or 'herdr' + if vim.fn.executable(bin) ~= 1 then + return + end + + -- Resolve this plugin's own checkout root (the lazy clone). + local source = debug.getinfo(1, 'S').source -- "@/lua/herdr-splits/sync.lua" + local this_file = source:sub(2) -- strip leading '@' + local plugin_root = vim.fn.fnamemodify(this_file, ':p:h:h:h') -- up 3: file<-herdr-splits<-lua<-root + if vim.fn.isdirectory(plugin_root .. '/.git') == 0 + and vim.fn.filereadable(plugin_root .. '/herdr-plugin.toml') == 0 then + return -- not a real checkout + end + + -- Get the lazy checkout HEAD. + local obj = vim.system({ 'git', '-C', plugin_root, 'rev-parse', 'HEAD' }, { text = true }):wait() + if obj.code ~= 0 then + return + end + local lazy_sha = vim.trim(obj.stdout or '') + if lazy_sha == '' then + return + end + + -- Get the managed install info. + local obj2 = vim.system({ bin, 'plugin', 'list', '--plugin', PLUGIN_ID, '--json' }, { text = true }):wait() + if obj2.code ~= 0 then + return + end + + local decoded, data = pcall(vim.json.decode, obj2.stdout or '') + if not decoded or type(data) ~= 'table' then + return + end + + -- Output may be a single object or an array; find our entry. + local entry = data + if data[1] ~= nil then + for _, e in ipairs(data) do + if e.plugin_id == PLUGIN_ID then + entry = e + break + end + end + end + + local src = entry and entry.source + if not src then + return + end + -- Dev mode (plugin link) or unknown kind: nothing to do. + if src.kind ~= 'github' then + return + end + + -- Already in sync? + local managed_sha = vim.trim(src.resolved_commit or '') + if managed_sha ~= '' and managed_sha:lower() == lazy_sha:lower() then + return + end + + -- Reinstall pinned to the lazy commit. + local obj3 = vim.system( + { bin, 'plugin', 'install', PLUGIN_SOURCE, '--ref', lazy_sha, '--yes' }, + { text = true } + ):wait() + if obj3.code == 0 then + vim.notify('herdr-splits: synced Herdr-side scripts to ' .. lazy_sha:sub(1, 7), vim.log.levels.INFO) + else + vim.notify('herdr-splits: Herdr-side sync skipped (install failed; local link or offline)', vim.log.levels.INFO) + end + end) + + if not ok then + vim.notify('herdr-splits: sync error: ' .. tostring(err), vim.log.levels.DEBUG) + end +end + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/win.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/win.lua new file mode 100644 index 0000000..47f6821 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/win.lua @@ -0,0 +1,154 @@ +---Window utility functions for Neovim split detection. +---@class HerdrSplitsWin +local M = {} + +local config = require('herdr-splits.config') + +---Check if the current window spans the full terminal width. +---@param winid number|nil window ID, defaults to current +---@return boolean +function M.is_full_width(winid) + return vim.api.nvim_win_get_width(winid or 0) == vim.o.columns +end + +---Check if the current window spans the full terminal height. +---Accounts for cmdheight, statusline, and tabline. +---@param winid number|nil window ID, defaults to current +---@return boolean +function M.is_full_height(winid) + local height = vim.o.lines - vim.o.cmdheight + local tabpages = #vim.api.nvim_list_tabpages() + local wins = #vim.api.nvim_tabpage_list_wins(0) + + if (vim.o.laststatus == 1 and wins > 1) or vim.o.laststatus > 1 then + height = height - 1 + end + if (vim.o.showtabline == 1 and tabpages > 1) or vim.o.showtabline == 2 then + height = height - 1 + end + + return vim.api.nvim_win_get_height(winid or 0) == height +end + +---@return boolean +function M.at_left_edge() + return vim.fn.winnr() == vim.fn.winnr('h') +end + +---@return boolean +function M.at_right_edge() + return vim.fn.winnr() == vim.fn.winnr('l') +end + +---@return boolean +function M.at_top_edge() + return vim.fn.winnr() == vim.fn.winnr('k') +end + +---@return boolean +function M.at_bottom_edge() + return vim.fn.winnr() == vim.fn.winnr('j') +end + +---Determine where the current window sits in the Neovim split layout +---for a given direction (horizontal or vertical). +---@param direction '"left"'|'"right"'|'"up"'|'"down"' +---@return '"start"'|'"middle"'|'"last"' +function M.win_position(direction) + if direction == 'left' or direction == 'right' then + if M.at_left_edge() then + return 'start' + end + if M.at_right_edge() then + return 'last' + end + return 'middle' + end + + if M.at_top_edge() then + return 'start' + end + if M.at_bottom_edge() then + return 'last' + end + return 'middle' +end + +---Check if a window should be ignored during resize operations. +---@param winid number|nil window ID, defaults to current +---@return boolean +function M.is_ignored_win(winid) + local bufnr = vim.api.nvim_win_get_buf(winid or 0) + return vim.tbl_contains(config.ignored_buftypes, vim.api.nvim_get_option_value('buftype', { buf = bufnr })) + or vim.tbl_contains(config.ignored_filetypes, vim.api.nvim_get_option_value('filetype', { buf = bufnr })) +end + +---Check if the current window is a floating window. +---@param winid number|nil +---@return boolean +function M.is_floating(winid) + return vim.api.nvim_win_get_config(winid or 0).relative ~= '' +end + +---Check if a window is an "embedded" floating window — one that is technically +---floating (relative ~= '') but visually behaves like a sidebar (e.g. snacks +---explorer). Neovim's default floating zindex is 50; anything explicitly set +---below that signals the window is meant to coexist with normal splits. +---@param winid number|nil window ID, defaults to current +---@return boolean +function M.is_embedded_floating_window(winid) + if not M.is_floating(winid) then + return false + end + local cfg = vim.api.nvim_win_get_config(winid or 0) + local threshold = config.floating_zindex_max or 50 + return cfg.zindex ~= nil and cfg.zindex < threshold +end + +---Same as M.is_ignored_win but also checks previewwindow when opt-in. +---@param winid number|nil window ID, defaults to current +---@return boolean +function M.is_ignored_or_preview(winid) + if M.is_ignored_win(winid) then + return true + end + if config.ignore_previewwindows then + local ok, pw = pcall(vim.api.nvim_win_get_option, winid or 0, 'previewwindow') + if ok and pw then + return true + end + end + return false +end + +---Returns true while Neovim's command-line window (q:, q/, q?) is open. +---Inside it all window commands raise E11, so callers must short-circuit. +---@return boolean +function M.is_command_line_window() + return vim.fn.getcmdwintype() ~= '' +end + +---Direction key shorthand for wincmd. +M.dir_keys = { + left = 'h', + right = 'l', + up = 'k', + down = 'j', +} + +M.dir_keys_reverse = { + left = 'l', + right = 'h', + up = 'j', + down = 'k', +} + +---Reverse of a direction name: left<->right, up<->down. +M.reverse_direction = { + left = 'right', + right = 'left', + up = 'down', + down = 'up', +} + +return M diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/plugin/herdr-splits.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/plugin/herdr-splits.lua new file mode 100644 index 0000000..4d32363 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/plugin/herdr-splits.lua @@ -0,0 +1,11 @@ +-- herdr-splits.nvim plugin bootstrap. +-- This file is loaded by Neovim when the plugin is found on the runtimepath. +-- It defers all work to `require('herdr-splits')`. + +if vim.g.loaded_herdr_splits then + return +end +vim.g.loaded_herdr_splits = true + +-- Defer loading until user calls setup() or invokes a function. +-- The main module is available as require('herdr-splits'). diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-nav.sh b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-nav.sh new file mode 100644 index 0000000..9cdbbb7 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-nav.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Herdr navigation helper — used by herdr keybinds for seamless two-way nav. +# +# When a navigation key is pressed in Herdr: +# 1. Check if the focused pane is running Neovim in the foreground. +# - If yes: forward the key chord into that pane. The Neovim plugin owns +# in-split movement, edge crossing, and unzoom — do NOT unzoom here, or +# moving between Neovim splits would incorrectly unzoom the pane. +# 2. If no (a plain Herdr pane): unzoom if needed, then move Herdr pane focus. +# At a layout edge, focus wraps around to the opposite side (smart-splits +# style), so navigating past the last pane lands on the first — unless +# `nav_at_edge=stop`, in which case it halts at the edge. +# +# Auto-unzoom is configurable via `unzoom_on_nav=false` in +# `~/.config/herdr/plugins/config/herdr-splits/herdr-splits.conf` +# (same file the Neovim plugin reads). Default: enabled. +# +# Edge behaviour for plain Herdr panes is configurable via `nav_at_edge` in +# the same conf file: `wrap` (default; wraps to the opposite side at an edge) +# or `stop` (navigation halts at the edge instead of wrapping). +# +# Usage: herdr-nav.sh + +set -euo pipefail + +dir="${1:?usage: herdr-nav.sh }" +herdr="${HERDR_BIN_PATH:-herdr}" + +case "$dir" in + left) key="ctrl+h"; config_key="nav_key_left"; opp="right" ;; + down) key="ctrl+j"; config_key="nav_key_down"; opp="up" ;; + up) key="ctrl+k"; config_key="nav_key_up"; opp="down" ;; + right) key="ctrl+l"; config_key="nav_key_right"; opp="left" ;; + *) echo "herdr-nav.sh: unknown direction: $dir" >&2; exit 2 ;; +esac + +# Resolve `unzoom_on_nav` and `nav_at_edge` from the shared config file. +# Defaults: unzoom enabled, nav_at_edge=wrap (backward compatible). +unzoom=1 +nav_at_edge=wrap +config_path="${HERDR_SPLITS_CONFIG:-${HERDR_PLUGIN_CONFIG_DIR:-$HOME/.config/herdr/plugins/config/herdr-splits}/herdr-splits.conf}" +if [ -r "$config_path" ]; then + configured_key=$(sed -n -E "s/^[[:space:]]*${config_key}[[:space:]]*=[[:space:]]*([^[:space:]#]+).*$/\\1/p" "$config_path" | tail -n 1) + if [ -n "$configured_key" ]; then + key="$configured_key" + fi + if grep -Eq '^[[:space:]]*unzoom_on_nav[[:space:]]*=[[:space:]]*false' "$config_path"; then + unzoom=0 + fi + if grep -Eq '^[[:space:]]*nav_at_edge[[:space:]]*=[[:space:]]*stop' "$config_path"; then + nav_at_edge=stop + fi +fi + +# Get focused pane ID from server (best-effort; empty is fine — we fall +# through to the non-vim path below, which uses --current directly). +pane_id=$("$herdr" pane current --current 2>/dev/null | grep -o '"pane_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/' || true) + +# Check if focused pane is running vim/nvim as foreground process. +is_vim=0 +if [ -n "$pane_id" ] && pane_info=$("$herdr" pane process-info --current 2>/dev/null); then + if echo "$pane_info" | grep -qiE '"name"[[:space:]]*:[[:space:]]*"(g?(view|l?n?vim?x?)(diff)?)"' 2>/dev/null; then + is_vim=1 + fi +fi + +# Vim pane: forward the chord; the Neovim plugin decides movement + unzoom. +if [ "$is_vim" -eq 1 ]; then + exec "$herdr" pane send-keys "$pane_id" "$key" +fi + +# --- Non-vim Herdr pane: unzoom if needed, then focus (wrapping at edges) --- + +# `pane edges` reports both edge flags and the zoomed state in one call. +edges_out=$("$herdr" pane edges --current 2>/dev/null || true) + +# A zoomed pane fills the tab and reports itself at every edge, so the edge +# flags are useless for the wrap decision. Unzoom (if enabled) so we can +# trust them. If unzoom is disabled we leave zoom untouched and must NOT use +# the (all-true) edge flags — wrap is skipped below in that case. +edges_trusted=1 +if printf '%s' "$edges_out" | grep -q '"zoomed"[[:space:]]*:[[:space:]]*true'; then + if [ "$unzoom" -eq 1 ]; then + "$herdr" pane zoom --off --current 2>/dev/null || true + # Layout changed; re-read edges so the wrap check is accurate. + edges_out=$("$herdr" pane edges --current 2>/dev/null || true) + else + edges_trusted=0 + fi +fi + +# Move to the neighbor in the requested direction. When already at the +# requested edge (no neighbor there): wrap to the opposite side, or — when +# nav_at_edge=stop — do nothing. Skip the edge check when the flags are +# unreliable (still zoomed, unzoom disabled); in that case we can't tell +# whether we're at an edge, so we just focus in the requested direction. +if [ "$edges_trusted" -eq 1 ] && printf '%s' "$edges_out" | grep -q "\"$dir\"[[:space:]]*:[[:space:]]*true"; then + if [ "$nav_at_edge" = stop ]; then + exit 0 + fi + exec "$herdr" pane focus --direction "$opp" --current +else + exec "$herdr" pane focus --direction "$dir" --current +fi diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-resize.sh b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-resize.sh new file mode 100644 index 0000000..16d0b0d --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/executable_herdr-resize.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Herdr resize helper — used by herdr keybinds for seamless two-way resize. +# +# When a resize key (alt+h/j/k/l) is pressed in Herdr: +# 1. Check if the focused pane is running Neovim in the foreground +# 2. If yes: forward the key chord into that pane (Neovim's plugin handles it) +# 3. If no: resize Herdr pane directly +# +# Zoom state is left untouched (matches smart-splits.nvim's +# disable_multiplexer_nav_when_zoomed default). +# +# Usage: herdr-resize.sh [amount] + +set -euo pipefail + +dir="${1:?usage: herdr-resize.sh [amount]}" +amount="${2:-0.03}" +herdr="${HERDR_BIN_PATH:-herdr}" + +case "$dir" in + left) key="alt+h"; config_key="resize_key_left" ;; + down) key="alt+j"; config_key="resize_key_down" ;; + up) key="alt+k"; config_key="resize_key_up" ;; + right) key="alt+l"; config_key="resize_key_right" ;; + *) echo "herdr-resize.sh: unknown direction: $dir" >&2; exit 2 ;; +esac + +# Resolve the forward chord from the shared config file. +# Unset values keep the alt+hjkl default. +config_path="${HERDR_SPLITS_CONFIG:-${HERDR_PLUGIN_CONFIG_DIR:-$HOME/.config/herdr/plugins/config/herdr-splits}/herdr-splits.conf}" +if [ -r "$config_path" ]; then + configured_key=$(sed -n -E "s/^[[:space:]]*${config_key}[[:space:]]*=[[:space:]]*([^[:space:]#]+).*$/\\1/p" "$config_path" | tail -n 1) + if [ -n "$configured_key" ]; then + key="$configured_key" + fi +fi + +# Get focused pane ID from server +pane_id=$("$herdr" pane current --current 2>/dev/null | grep -o '"pane_id"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/') +if [ -z "$pane_id" ]; then + exec "$herdr" pane resize --direction "$dir" --amount "$amount" --current +fi + +# Check if focused pane is running vim/nvim as foreground process. +# When it is, the key is forwarded into Neovim so the plugin can decide +# whether to resize a Neovim split or hand off to Herdr. +is_vim=0 +if pane_info=$("$herdr" pane process-info --current 2>/dev/null); then + if echo "$pane_info" | grep -qiE '"name"\s*:\s*"(g?(view|l?n?vim?x?)(diff)?)"' 2>/dev/null; then + is_vim=1 + fi +fi + +if [ "$is_vim" -eq 1 ]; then + exec "$herdr" pane send-keys "$pane_id" "$key" +else + exec "$herdr" pane resize --direction "$dir" --amount "$amount" --current +fi diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/minimal_init.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/minimal_init.lua new file mode 100644 index 0000000..3fe8254 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/scripts/minimal_init.lua @@ -0,0 +1,14 @@ +local source = debug.getinfo(1, 'S').source +assert(source:sub(1, 1) == '@', 'could not locate minimal_init.lua') + +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') +vim.opt.runtimepath:prepend(root) +vim.opt.runtimepath:prepend(root .. '/deps/mini.test') + +require('mini.test').setup({ + collect = { + find_files = function() + return vim.fn.glob(root .. '/tests/test_*.lua', true, true) + end, + }, +}) diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_conf.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_conf.lua new file mode 100644 index 0000000..52f002f --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_conf.lua @@ -0,0 +1,196 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +local function write_config(lines) + child.lua_func(function(config_lines) + local path = vim.env.HERDR_SPLITS_CONFIG + vim.fn.mkdir(vim.fn.fnamemodify(path, ':h'), 'p') + vim.fn.writefile(config_lines, path) + end, lines) +end + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + child.lua([[ + local test_dir = vim.fn.tempname() + vim.fn.mkdir(test_dir, 'p') + vim.g.herdr_splits_test_dir = test_dir + vim.env.HOME = test_dir .. '/home' + vim.env.XDG_CONFIG_HOME = test_dir .. '/xdg' + vim.env.HERDR_SPLITS_CONFIG = test_dir .. '/nested/herdr-splits.conf' + vim.env.HERDR_BIN_PATH = test_dir .. '/missing-herdr' + vim.env.HERDR_PLUGIN_CONFIG_DIR = nil + ]]) + end, + post_case = function() + local test_dir = child.g.herdr_splits_test_dir + child.stop() + vim.fn.delete(test_dir, 'rf') + end, + }, +}) + +T['to_herdr translates key notation'] = function() + local translate = function(value) + return child.lua_func(function(key) + return require('herdr-splits.conf').to_herdr(key) + end, value) + end + + expect.equality(translate(''), 'ctrl+alt+left') + expect.equality(translate(''), 'alt+enter') + expect.equality(translate(''), 'shift+space') + expect.equality(translate('H'), 'h') + expect.equality(translate(''), 'esc') + expect.equality(translate(''), vim.NIL) + expect.equality(translate(false), vim.NIL) + expect.equality(translate(''), vim.NIL) +end + +T['read_managed handles a missing file'] = function() + local result = child.lua_func(function() + local values, adopted = require('herdr-splits.conf').read_managed() + return { adopted = adopted, count = vim.tbl_count(values) } + end) + + expect.equality(result, { adopted = false, count = 0 }) +end + +T['read_managed adopts managed legacy values with last value winning'] = function() + write_config({ + '# legacy config', + ' nav_key_left = ctrl+old', + 'nav_key_left=ctrl+new # last value wins', + 'resize_key_up = alt+up', + 'unzoom_on_nav=false', + 'nav_at_edge=stop', + 'unknown_key=ignored', + }) + + local result = child.lua_func(function() + local values, adopted = require('herdr-splits.conf').read_managed() + return { + adopted = adopted, + nav_left = values.nav_keys.left, + resize_up = values.resize_keys.up, + unzoom_on_nav = values.unzoom_on_nav, + nav_at_edge = values.nav_at_edge, + unknown = values.unknown_key, + } + end) + + expect.equality(result, { + adopted = true, + nav_left = 'ctrl+new', + resize_up = 'alt+up', + unzoom_on_nav = false, + nav_at_edge = 'stop', + }) +end + +T['write creates generated content without leftover temporary files'] = function() + local result = child.lua_func(function() + local conf = require('herdr-splits.conf') + local ok, err = conf.write({ + nav_keys = { left = 'ctrl+h', down = 'ctrl+j', up = 'ctrl+k', right = 'ctrl+l' }, + resize_keys = { left = 'alt+h', down = 'alt+j', up = 'alt+k', right = 'alt+l' }, + unzoom_on_nav = false, + nav_at_edge = 'stop', + }) + local values, adopted = conf.read_managed() + return { + ok = ok, + err = err, + adopted = adopted, + adopted_count = vim.tbl_count(values), + } + end) + + expect.equality(result, { ok = true, adopted = false, adopted_count = 0 }) + + local content = child.lua_func(function() + local file = assert(io.open(vim.env.HERDR_SPLITS_CONFIG, 'r')) + local bytes = file:read('*a') + file:close() + return bytes + end) + expect.equality(content, table.concat({ + '# herdr-splits-generated-v1', + '# herdr-splits plugin config — GENERATED by herdr-splits.nvim setup().', + '# Regenerated from defaults + setup() opts on every setup(); do not edit by hand.', + '# A headerless/legacy conf is adopted once on migration (see the startup', + '# notice), then overwritten thereafter — remove an opt in setup() to revert.', + 'nav_key_left=ctrl+h', + 'nav_key_down=ctrl+j', + 'nav_key_up=ctrl+k', + 'nav_key_right=ctrl+l', + 'resize_key_left=alt+h', + 'resize_key_down=alt+j', + 'resize_key_up=alt+k', + 'resize_key_right=alt+l', + 'unzoom_on_nav=false', + 'nav_at_edge=stop', + '', + }, '\n')) + expect.equality(child.lua_get([[vim.fn.glob(vim.env.HERDR_SPLITS_CONFIG .. '.tmp.*', false, true)]]), {}) +end + +T['config setup applies precedence and resets omitted managed options'] = function() + write_config({ + 'nav_key_left=ctrl+legacy', + 'resize_key_left=alt+legacy', + 'unzoom_on_nav=false', + 'nav_at_edge=stop', + }) + + local result = child.lua_func(function() + vim.notify = function() end + local config = require('herdr-splits.config') + + config.setup({ + nav_keys = { left = '' }, + unzoom_on_nav = true, + }) + local first = { + nav_left = config.nav_keys.left, + resize_left = config.resize_keys.left, + unzoom_on_nav = config.unzoom_on_nav, + nav_at_edge = config.nav_at_edge, + } + + config.setup({ nav_keys = { right = '' } }) + local second = { + nav_left = config.nav_keys.left, + nav_right = config.nav_keys.right, + resize_left = config.resize_keys.left, + unzoom_on_nav = config.unzoom_on_nav, + nav_at_edge = config.nav_at_edge, + } + return { first = first, second = second } + end) + + expect.equality(result, { + first = { + nav_left = 'ctrl+x', + resize_left = 'alt+legacy', + unzoom_on_nav = true, + nav_at_edge = 'stop', + }, + second = { + nav_left = 'ctrl+h', + nav_right = 'ctrl+r', + resize_left = 'alt+h', + unzoom_on_nav = true, + nav_at_edge = 'wrap', + }, + }) +end + +return T diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_herdr.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_herdr.lua new file mode 100644 index 0000000..aecd3e0 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_herdr.lua @@ -0,0 +1,267 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + end, + post_case = function() + child.stop() + end, + }, +}) + +T['resolves the binary and requires a complete session environment'] = function() + local result = child.lua_func(function() + local config = require('herdr-splits.config') + local herdr = require('herdr-splits.herdr') + + vim.env.HERDR_BIN_PATH = '/env/herdr' + config.herdr_bin = nil + local from_env = herdr.herdr_bin() + config.herdr_bin = '/config/herdr' + local from_config = herdr.herdr_bin() + config.herdr_bin = nil + vim.env.HERDR_BIN_PATH = nil + local fallback = herdr.herdr_bin() + + vim.env.HERDR_ENV = nil + vim.env.HERDR_PANE_ID = nil + local absent = { herdr.is_in_session(), herdr.current_pane_id() or '' } + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = '' + local empty = { herdr.is_in_session(), herdr.current_pane_id() or '' } + vim.env.HERDR_PANE_ID = 'pane-7' + local present = { herdr.is_in_session(), herdr.current_pane_id() } + + return { + binaries = { from_env, from_config, fallback }, + absent = absent, + empty = empty, + present = present, + } + end) + + expect.equality(result, { + binaries = { '/env/herdr', '/config/herdr', 'herdr' }, + absent = { false, '' }, + empty = { false, '' }, + present = { true, 'pane-7' }, + }) +end + +T['parses edge responses and rejects invalid command output'] = function() + local result = child.lua_func(function() + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = 'pane-7' + local config = require('herdr-splits.config') + config.herdr_bin = '/fake/herdr' + + local queue = { + { stdout = vim.json.encode({ result = { edges = { left = true } } }), stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = { edges = { left = false } } }), stderr = '', code = 0 }, + { stdout = '{bad', stderr = '', code = 0 }, + { stdout = '', stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = {} }), stderr = '', code = 0 }, + { stdout = '{}', stderr = 'failed', code = 2 }, + { stdout = vim.json.encode({ result = { edges = {} } }), stderr = '', code = 0 }, + { stdout = '{"result":{"edges":{"left":null}}}', stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = { edges = { left = 'yes' } } }), stderr = '', code = 0 }, + } + local calls = {} + vim.system = function(argv, opts) + calls[#calls + 1] = { argv = vim.deepcopy(argv), opts = vim.deepcopy(opts) } + local item = table.remove(queue, 1) + return { wait = function() return item end } + end + + package.loaded['herdr-splits.herdr'] = nil + local herdr = require('herdr-splits.herdr') + local values = {} + for i = 1, 9 do + local value = herdr.current_pane_at_edge('left') + values[i] = value == nil and '' or value + end + return { values = values, calls = calls } + end) + + expect.equality(result.values, { + true, false, '', '', '', '', '', '', '', + }) + expect.equality(#result.calls, 9) + for _, call in ipairs(result.calls) do + expect.equality(call, { + argv = { '/fake/herdr', 'pane', 'edges', '--current' }, + opts = { text = true }, + }) + end +end + +T['parses zoom responses and rejects invalid command output'] = function() + local result = child.lua_func(function() + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = 'pane-7' + local queue = { + { stdout = vim.json.encode({ result = { layout = { zoomed = true } } }), stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = { layout = { zoomed = false } } }), stderr = '', code = 0 }, + { stdout = '{bad', stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = {} }), stderr = '', code = 0 }, + { stdout = '{}', stderr = '', code = 1 }, + { stdout = vim.json.encode({ result = { layout = {} } }), stderr = '', code = 0 }, + { stdout = '{"result":{"layout":{"zoomed":null}}}', stderr = '', code = 0 }, + { stdout = vim.json.encode({ result = { layout = { zoomed = 1 } } }), stderr = '', code = 0 }, + } + local calls = {} + vim.system = function(argv, opts) + calls[#calls + 1] = { argv = vim.deepcopy(argv), opts = vim.deepcopy(opts) } + local item = table.remove(queue, 1) + return { wait = function() return item end } + end + + package.loaded['herdr-splits.herdr'] = nil + local herdr = require('herdr-splits.herdr') + local values = {} + for i = 1, 8 do + local value = herdr.current_pane_is_zoomed() + values[i] = value == nil and '' or value + end + return { values = values, calls = calls } + end) + + expect.equality(result.values, { + true, false, '', '', '', '', '', '', + }) + expect.equality(#result.calls, 8) + for _, call in ipairs(result.calls) do + expect.equality(call.argv, { 'herdr', 'pane', 'layout', '--current' }) + expect.equality(call.opts, { text = true }) + end +end + +T['rejects JSON null for pane queries'] = function() + local result = child.lua_func(function() + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = 'pane-7' + local queue = { + 'null', + '{"result":null}', + '{"result":{"edges":null}}', + 'null', + '{"result":null}', + '{"result":{"layout":null}}', + } + vim.system = function() + local stdout = table.remove(queue, 1) + return { + wait = function() + return { stdout = stdout, stderr = '', code = 0 } + end, + } + end + + package.loaded['herdr-splits.herdr'] = nil + local herdr = require('herdr-splits.herdr') + local values = {} + for _ = 1, 3 do + local ok, value = pcall(herdr.current_pane_at_edge, 'left') + values[#values + 1] = { ok, value == nil and '' or value } + end + for _ = 1, 3 do + local ok, value = pcall(herdr.current_pane_is_zoomed) + values[#values + 1] = { ok, value == nil and '' or value } + end + return values + end) + + expect.equality(result, { + { true, '' }, + { true, '' }, + { true, '' }, + { true, '' }, + { true, '' }, + { true, '' }, + }) +end + +T['focus and unzoom use exact commands and report exit status'] = function() + local result = child.lua_func(function() + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = 'pane-7' + local queue = { + { stdout = '', stderr = '', code = 0 }, + { stdout = '', stderr = 'no neighbor', code = 1 }, + { stdout = '', stderr = '', code = 0 }, + { stdout = '', stderr = 'failed', code = 1 }, + } + local calls = {} + vim.system = function(argv, opts) + calls[#calls + 1] = { argv = vim.deepcopy(argv), opts = vim.deepcopy(opts) } + local item = table.remove(queue, 1) + return { wait = function() return item end } + end + + package.loaded['herdr-splits.herdr'] = nil + local herdr = require('herdr-splits.herdr') + return { + values = { + herdr.focus_pane('right'), + herdr.focus_pane('left'), + herdr.unzoom(), + herdr.unzoom(), + }, + calls = calls, + } + end) + + expect.equality(result.values, { true, false, true, false }) + expect.equality(result.calls, { + { argv = { 'herdr', 'pane', 'focus', '--direction', 'right', '--current' }, opts = { text = true } }, + { argv = { 'herdr', 'pane', 'focus', '--direction', 'left', '--current' }, opts = { text = true } }, + { argv = { 'herdr', 'pane', 'zoom', '--off', '--current' }, opts = { text = true } }, + { argv = { 'herdr', 'pane', 'zoom', '--off', '--current' }, opts = { text = true } }, + }) +end + +T['resize formats ratios and reports exit status'] = function() + local result = child.lua_func(function() + vim.env.HERDR_ENV = '1' + vim.env.HERDR_PANE_ID = 'pane-7' + local queue = { + { stdout = '', stderr = '', code = 0 }, + { stdout = '', stderr = 'failed', code = 1 }, + } + local calls = {} + vim.system = function(argv, opts) + calls[#calls + 1] = { argv = vim.deepcopy(argv), opts = vim.deepcopy(opts) } + local item = table.remove(queue, 1) + return { wait = function() return item end } + end + + package.loaded['herdr-splits.herdr'] = nil + local herdr = require('herdr-splits.herdr') + return { + values = { herdr.resize_pane('down', 0.03), herdr.resize_pane('left', 1 / 3) }, + calls = calls, + } + end) + + expect.equality(result.values, { true, false }) + expect.equality(result.calls, { + { + argv = { 'herdr', 'pane', 'resize', '--direction', 'down', '--amount', '0.0300', '--current' }, + opts = { text = true }, + }, + { + argv = { 'herdr', 'pane', 'resize', '--direction', 'left', '--amount', '0.3333', '--current' }, + opts = { text = true }, + }, + }) +end + +return T diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_nav.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_nav.lua new file mode 100644 index 0000000..f88a80d --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_nav.lua @@ -0,0 +1,520 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + end, + post_case = function() + child.stop() + end, + }, +}) + +T['moves through native horizontal and vertical splits without delegation'] = function() + local result = child.lua_func(function() + local calls = {} + package.loaded['herdr-splits.herdr'] = { + focus_pane = function(direction) calls[#calls + 1] = direction end, + is_in_session = function() return false end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + vim.o.splitright = true + vim.cmd('vsplit') + local horizontal = vim.api.nvim_tabpage_list_wins(0) + table.sort(horizontal, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(horizontal[1]) + nav.move_cursor('right') + local moved_right = vim.api.nvim_get_current_win() == horizontal[2] + + vim.cmd('only') + vim.o.splitbelow = true + vim.cmd('split') + local vertical = vim.api.nvim_tabpage_list_wins(0) + table.sort(vertical, function(a, b) + return vim.api.nvim_win_get_position(a)[1] < vim.api.nvim_win_get_position(b)[1] + end) + vim.api.nvim_set_current_win(vertical[1]) + nav.move_cursor('down') + local moved_down = vim.api.nvim_get_current_win() == vertical[2] + + return { moved_right = moved_right, moved_down = moved_down, calls = calls } + end) + + expect.equality(result, { moved_right = true, moved_down = true, calls = {} }) +end + +T['applies typed navigation counts without wrapping or delegation'] = function() + child.lua_func(function() + _G.nav_delegations = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + focus_pane = function(direction) + _G.nav_delegations[#_G.nav_delegations + 1] = direction + return true + end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + vim.o.splitright = true + for _ = 1, 3 do + vim.cmd('vsplit') + end + _G.nav_count_wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(_G.nav_count_wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(_G.nav_count_wins[1]) + vim.keymap.set('n', 'x', function() + nav.move_cursor('right', { at_edge = 'stop' }) + end) + end) + + child.type_keys('2x') + local after_two = child.lua_get('vim.api.nvim_get_current_win() == _G.nav_count_wins[3]') + child.type_keys('5x') + local exhausted_at_last = child.lua_get('vim.api.nvim_get_current_win() == _G.nav_count_wins[4]') + child.type_keys('5x') + local stayed_at_last = child.lua_get('vim.api.nvim_get_current_win() == _G.nav_count_wins[4]') + local delegations = child.lua_get('_G.nav_delegations') + + expect.equality({ + after_two = after_two, + exhausted_at_last = exhausted_at_last, + stayed_at_last = stayed_at_last, + delegations = delegations, + }, { + after_two = true, + exhausted_at_last = true, + stayed_at_last = true, + delegations = {}, + }) +end + +T['delegates normal floats but not embedded floats'] = function() + local result = child.lua_func(function() + local calls = {} + package.loaded['herdr-splits.herdr'] = { + focus_pane = function(direction) + calls[#calls + 1] = direction + return true + end, + is_in_session = function() return false end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + local normal = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), true, { + relative = 'editor', row = 1, col = 1, width = 12, height = 3, zindex = 50, + }) + nav.move_cursor('right') + local normal_stayed = vim.api.nvim_get_current_win() == normal + + vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), true, { + relative = 'editor', row = 2, col = 2, width = 12, height = 3, zindex = 49, + }) + nav.move_cursor('left') + + return { calls = calls, normal_stayed = normal_stayed } + end) + + expect.equality(result, { + calls = { 'right' }, + normal_stayed = true, + }) +end + +T['applies no-session stop, wrap, and callback edge behavior'] = function() + local result = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + local function horizontal_pair() + vim.cmd('only') + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[1]) + return wins + end + + local stop_wins = horizontal_pair() + nav.move_cursor('left', { at_edge = 'stop' }) + local stopped = vim.api.nvim_get_current_win() == stop_wins[1] + + local wrap_wins = horizontal_pair() + nav.move_cursor('left', { at_edge = 'wrap' }) + local wrapped = vim.api.nvim_get_current_win() == wrap_wins[2] + + local callback_wins = horizontal_pair() + local callback = {} + nav.move_cursor('left', { + at_edge = function(ctx) + callback.direction = ctx.direction + callback.is_sidebar = ctx.is_sidebar + callback.has_split = type(ctx.split) == 'function' + callback.has_wrap = type(ctx.wrap) == 'function' + ctx.wrap() + end, + }) + callback.wrapped = vim.api.nvim_get_current_win() == callback_wins[2] + + return { + stopped = stopped, + wrapped = wrapped, + callback = callback, + } + end) + + expect.equality(result, { + stopped = true, + wrapped = true, + callback = { + direction = 'left', + is_sidebar = false, + has_split = true, + has_wrap = true, + wrapped = true, + }, + }) +end + +T['creates local edge splits and preserves split preferences'] = function() + local result = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + local cases = {} + + local function exercise(direction, preference) + vim.cmd('only') + vim.o.splitright = preference + vim.o.splitbelow = preference + local before = #vim.api.nvim_tabpage_list_wins(0) + local original = vim.api.nvim_get_current_win() + + nav.move_cursor(direction, { at_edge = 'split' }) + + local current = vim.api.nvim_get_current_win() + local original_position = vim.api.nvim_win_get_position(original) + local current_position = vim.api.nvim_win_get_position(current) + local placement_matches_preference + if direction == 'left' or direction == 'right' then + if preference then + placement_matches_preference = current_position[2] > original_position[2] + else + placement_matches_preference = current_position[2] < original_position[2] + end + elseif preference then + placement_matches_preference = current_position[1] > original_position[1] + else + placement_matches_preference = current_position[1] < original_position[1] + end + + cases[#cases + 1] = { + direction = direction, + preference = preference, + created_one = #vim.api.nvim_tabpage_list_wins(0) == before + 1, + focused_new = current ~= original, + placement_matches_preference = placement_matches_preference, + splitright_restored = vim.o.splitright == preference, + splitbelow_restored = vim.o.splitbelow == preference, + } + end + + for _, direction in ipairs({ 'left', 'right', 'up', 'down' }) do + exercise(direction, false) + exercise(direction, true) + end + return cases + end) + + local expected = {} + for _, direction in ipairs({ 'left', 'right', 'up', 'down' }) do + for _, preference in ipairs({ false, true }) do + expected[#expected + 1] = { + direction = direction, + preference = preference, + created_one = true, + focused_new = true, + placement_matches_preference = true, + splitright_restored = true, + splitbelow_restored = true, + } + end + end + expect.equality(result, expected) +end + +T['uses Herdr neighbors and falls back locally on focus or edge failures'] = function() + local result = child.lua_func(function() + local edge_queue = { false, false, '' } + local focus_queue = { true, false } + local calls = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + unzoom_enabled = function() return false end, + current_pane_at_edge = function(direction) + calls[#calls + 1] = 'edge:' .. direction + local value = table.remove(edge_queue, 1) + if value == '' then return nil end + return value + end, + focus_pane = function(direction) + calls[#calls + 1] = 'focus:' .. direction + return table.remove(focus_queue, 1) + end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + local function at_left() + vim.cmd('only') + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[1]) + return wins + end + + local success_wins = at_left() + nav.move_cursor('left') + local focus_success_stays = vim.api.nvim_get_current_win() == success_wins[1] + + local failure_wins = at_left() + nav.move_cursor('left') + local focus_failure_wraps = vim.api.nvim_get_current_win() == failure_wins[2] + + local unknown_wins = at_left() + nav.move_cursor('left') + local unknown_wraps = vim.api.nvim_get_current_win() == unknown_wins[2] + + return { + focus_success_stays = focus_success_stays, + focus_failure_wraps = focus_failure_wraps, + unknown_wraps = unknown_wraps, + calls = calls, + } + end) + + expect.equality(result, { + focus_success_stays = true, + focus_failure_wraps = true, + unknown_wraps = true, + calls = { + 'edge:left', 'focus:left', + 'edge:left', 'focus:left', + 'edge:left', + }, + }) +end + +T['unzooms and retries native movement before querying Herdr edges'] = function() + local result = child.lua_func(function() + local calls = {} + local start_win = vim.api.nvim_get_current_win() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + unzoom_enabled = function() return true end, + current_pane_is_zoomed = function() + calls[#calls + 1] = 'zoomed' + return true + end, + unzoom = function() + calls[#calls + 1] = 'unzoom' + local old_splitright = vim.o.splitright + vim.o.splitright = true + vim.cmd('vsplit') + vim.api.nvim_set_current_win(start_win) + vim.o.splitright = old_splitright + return true + end, + current_pane_at_edge = function() + calls[#calls + 1] = 'edge' + return false + end, + focus_pane = function() + calls[#calls + 1] = 'focus' + return true + end, + } + package.loaded['herdr-splits.nav'] = nil + require('herdr-splits.nav').move_cursor('right') + + return { + moved = vim.api.nvim_get_current_win() ~= start_win, + windows = #vim.api.nvim_tabpage_list_wins(0), + calls = calls, + } + end) + + expect.equality(result, { moved = true, windows = 2, calls = { 'zoomed', 'unzoom' } }) +end + +T['wraps through the reverse Herdr neighbor unless nav_at_edge stops it'] = function() + local result = child.lua_func(function() + local mode = 'wrap' + local calls = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + unzoom_enabled = function() return false end, + current_pane_at_edge = function(direction) + calls[#calls + 1] = 'edge:' .. direction + return direction == 'left' + end, + nav_at_edge = function() return mode end, + focus_pane = function(direction) + calls[#calls + 1] = 'focus:' .. direction + return true + end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + local function at_left() + vim.cmd('only') + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[1]) + return wins + end + + local herdr_wrap_wins = at_left() + nav.move_cursor('left') + local herdr_wrap_stays = vim.api.nvim_get_current_win() == herdr_wrap_wins[1] + + mode = 'stop' + local local_wrap_wins = at_left() + nav.move_cursor('left') + local local_wraps = vim.api.nvim_get_current_win() == local_wrap_wins[2] + + return { herdr_wrap_stays = herdr_wrap_stays, local_wraps = local_wraps, calls = calls } + end) + + expect.equality(result, { + herdr_wrap_stays = true, + local_wraps = true, + calls = { 'edge:left', 'edge:right', 'focus:right', 'edge:left' }, + }) +end + +T['command-line window delegates to Herdr at the screen edge and never wincmds'] = function() + local result = child.lua_func(function() + local calls = {} + local edge = false + local nav_mode = 'wrap' + package.loaded['herdr-splits.win'] = { + is_command_line_window = function() return true end, + is_floating = function() return false end, + is_embedded_floating_window = function() return false end, + is_ignored_or_preview = function() return false end, + dir_keys = { left = 'h', right = 'l', up = 'k', down = 'j' }, + dir_keys_reverse = { left = 'l', right = 'h', up = 'j', down = 'k' }, + reverse_direction = { left = 'right', right = 'left', up = 'down', down = 'up' }, + } + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_at_edge = function(direction) + calls[#calls + 1] = 'edge:' .. direction + return edge + end, + nav_at_edge = function() return nav_mode end, + focus_pane = function(direction) + calls[#calls + 1] = 'focus:' .. direction + return true + end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + -- single window: will_wrap is true in every direction, mirroring the + -- full-width/bottom command-line window geometry. + vim.cmd('only') + + edge = false + nav.move_cursor('left') + local c1 = vim.deepcopy(calls); calls = {} + + edge = true + nav_mode = 'wrap' + nav.move_cursor('right') + local c2 = vim.deepcopy(calls); calls = {} + + nav_mode = 'stop' + nav.move_cursor('right') + local c3 = vim.deepcopy(calls) + + return { neighbor = c1, wrap_reverse = c2, stop_noop = c3 } + end) + + expect.equality(result, { + neighbor = { 'edge:left', 'focus:left' }, + wrap_reverse = { 'edge:right', 'focus:left' }, + stop_noop = { 'edge:right' }, + }) +end + +T['command-line window no-ops silently when not at a screen edge'] = function() + local result = child.lua_func(function() + package.loaded['herdr-splits.win'] = { + is_command_line_window = function() return true end, + is_floating = function() return false end, + is_embedded_floating_window = function() return false end, + is_ignored_or_preview = function() return false end, + dir_keys = { left = 'h', right = 'l', up = 'k', down = 'j' }, + dir_keys_reverse = { left = 'l', right = 'h', up = 'j', down = 'k' }, + reverse_direction = { left = 'right', right = 'left', up = 'down', down = 'up' }, + } + local focused = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_at_edge = function() return false end, + nav_at_edge = function() return 'wrap' end, + focus_pane = function(d) focused[#focused + 1] = d; return true end, + } + package.loaded['herdr-splits.nav'] = nil + local nav = require('herdr-splits.nav') + + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + -- left window: 'right' has a Neovim neighbor -> will_wrap == false + vim.api.nvim_set_current_win(wins[1]) + nav.move_cursor('right') + return { stayed = vim.api.nvim_get_current_win() == wins[1], focused = focused } + end) + + -- Without the guard, wincmd l would move to wins[2]; with it, silent no-op. + expect.equality(result, { stayed = true, focused = {} }) +end + +return T diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_plugin.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_plugin.lua new file mode 100644 index 0000000..0afc929 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_plugin.lua @@ -0,0 +1,60 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + end, + post_case = function() + child.stop() + end, + }, +}) + +T['public module loads its expected API'] = function() + local result = child.lua_func(function() + local plugin = require('herdr-splits') + local names = { + 'setup', + 'sync_herdr', + 'resize_left', + 'resize_down', + 'resize_up', + 'resize_right', + 'move_cursor_left', + 'move_cursor_down', + 'move_cursor_up', + 'move_cursor_right', + 'add_ignored_filetype', + 'add_ignored_buftype', + } + local types = {} + for _, name in ipairs(names) do + types[name] = type(plugin[name]) + end + return types + end) + + expect.equality(result, { + setup = 'function', + sync_herdr = 'function', + resize_left = 'function', + resize_down = 'function', + resize_up = 'function', + resize_right = 'function', + move_cursor_left = 'function', + move_cursor_down = 'function', + move_cursor_up = 'function', + move_cursor_right = 'function', + add_ignored_filetype = 'function', + add_ignored_buftype = 'function', + }) +end + +return T diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_resize.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_resize.lua new file mode 100644 index 0000000..0053377 --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_resize.lua @@ -0,0 +1,302 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + end, + post_case = function() + child.stop() + end, + }, +}) + +T['uses the correct horizontal resize sign at both sides'] = function() + local deltas = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.resize'] = nil + local resize = require('herdr-splits.resize') + vim.o.equalalways = false + vim.o.winminwidth = 1 + + local function delta(side, direction) + vim.cmd('only') + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[side == 'left' and 1 or 2]) + local before = vim.api.nvim_win_get_width(0) + resize.resize(direction, 2) + return vim.api.nvim_win_get_width(0) - before + end + + return { + left_left = delta('left', 'left'), + left_right = delta('left', 'right'), + right_left = delta('right', 'left'), + right_right = delta('right', 'right'), + } + end) + + expect.equality(deltas, { left_left = -2, left_right = 2, right_left = 2, right_right = -2 }) +end + +T['uses the correct vertical resize sign at top and bottom'] = function() + local deltas = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.resize'] = nil + local resize = require('herdr-splits.resize') + vim.o.equalalways = false + vim.o.winminheight = 1 + + local function delta(side, direction) + vim.cmd('only') + vim.o.splitbelow = true + vim.cmd('split') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[1] < vim.api.nvim_win_get_position(b)[1] + end) + vim.api.nvim_set_current_win(wins[side == 'top' and 1 or 2]) + local before = vim.api.nvim_win_get_height(0) + resize.resize(direction, 2) + return vim.api.nvim_win_get_height(0) - before + end + + return { + top_up = delta('top', 'up'), + top_down = delta('top', 'down'), + bottom_up = delta('bottom', 'up'), + bottom_down = delta('bottom', 'down'), + } + end) + + expect.equality(deltas, { top_up = -2, top_down = 2, bottom_up = 2, bottom_down = -2 }) +end + +T['floors explicit cell amounts and clamps nonpositive values'] = function() + local deltas = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.resize'] = nil + local resize = require('herdr-splits.resize') + vim.o.equalalways = false + vim.o.winminwidth = 1 + + local function delta(amount) + vim.cmd('only') + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[1]) + local before = vim.api.nvim_win_get_width(0) + resize.resize('right', amount) + return vim.api.nvim_win_get_width(0) - before + end + + return { floored = delta(2.9), zero = delta(0), negative = delta(-4) } + end) + + expect.equality(deltas, { floored = 2, zero = 1, negative = 1 }) +end + +T['multiplies native resize cells by the typed count'] = function() + local before = child.lua_func(function() + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return false end, + } + package.loaded['herdr-splits.resize'] = nil + local config = require('herdr-splits.config') + config.neovim_amount = 2 + vim.o.equalalways = false + vim.o.winminwidth = 1 + vim.o.splitright = true + vim.cmd('vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + vim.api.nvim_set_current_win(wins[1]) + vim.keymap.set('n', 'x', function() + require('herdr-splits.resize').resize('right') + end) + return vim.api.nvim_win_get_width(0) + end) + + child.type_keys('3x') + local after = child.lua_get('vim.api.nvim_win_get_width(0)') + expect.equality(after - before, 6) +end + +T['delegates full-terminal ratios for defaults, counts, and explicit amounts'] = function() + child.lua_func(function() + _G.resize_calls = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_is_zoomed = function() return false end, + resize_pane = function(direction, amount) + _G.resize_calls[#_G.resize_calls + 1] = { direction, amount } + return true + end, + } + package.loaded['herdr-splits.resize'] = nil + local config = require('herdr-splits.config') + config.default_amount = 0.03 + local resize = require('herdr-splits.resize') + resize.resize('left') + resize.resize('right', 0.2) + resize.resize('left', 2) + vim.keymap.set('n', 'x', function() resize.resize('right') end) + end) + + child.type_keys('3x') + local calls = child.lua_get('_G.resize_calls') + expect.equality(calls, { + { 'left', 0.03 }, + { 'right', 0.2 }, + { 'left', 0.03 }, + { 'right', 0.09 }, + }) +end + +T['delegates vertical full-terminal ratios using real geometry'] = function() + local result = child.lua_func(function() + local calls = {} + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_is_zoomed = function() return false end, + resize_pane = function(direction, amount) + calls[#calls + 1] = { direction, amount } + return true + end, + } + package.loaded['herdr-splits.resize'] = nil + local config = require('herdr-splits.config') + config.default_amount = 0.03 + local win = require('herdr-splits.win') + local resize = require('herdr-splits.resize') + + local geometry = { + full_height = win.is_full_height(), + at_top = win.at_top_edge(), + at_bottom = win.at_bottom_edge(), + } + resize.resize('up') + resize.resize('down', 0.2) + return { geometry = geometry, calls = calls } + end) + + expect.equality(result, { + geometry = { full_height = true, at_top = true, at_bottom = true }, + calls = { { 'up', 0.03 }, { 'down', 0.2 } }, + }) +end + +T['delegates normal floats but suppresses embedded, sidebar, and zoomed windows'] = function() + local result = child.lua_func(function() + local calls = {} + local zoomed = false + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_is_zoomed = function() return zoomed end, + resize_pane = function(direction, amount) + calls[#calls + 1] = { direction, amount } + return true + end, + } + package.loaded['herdr-splits.resize'] = nil + local resize = require('herdr-splits.resize') + local base = vim.api.nvim_get_current_win() + + local normal = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), true, { + relative = 'editor', row = 1, col = 1, width = 12, height = 3, zindex = 50, + }) + resize.resize('right', 0.15) + + local embedded = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), true, { + relative = 'editor', row = 2, col = 2, width = 12, height = 3, zindex = 49, + }) + resize.resize('left') + + vim.api.nvim_set_current_win(base) + vim.api.nvim_win_close(embedded, true) + vim.api.nvim_win_close(normal, true) + local bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_set_option_value('buftype', 'nofile', { buf = bufnr }) + resize.resize('left') + + vim.api.nvim_set_option_value('buftype', '', { buf = bufnr }) + zoomed = true + resize.resize('right') + + return { calls = calls, current = vim.api.nvim_get_current_win() == base } + end) + + expect.equality(result, { calls = { { 'right', 0.15 } }, current = true }) +end + +T['command-line window horizontal resize delegates past the nofile sidebar rule'] = function() + local result = child.lua_func(function() + local calls = {} + local cmdwin = true + package.loaded['herdr-splits.win'] = { + is_command_line_window = function() return cmdwin end, + is_embedded_floating_window = function() return false end, + is_floating = function() return false end, + is_ignored_or_preview = function() return true end, + is_full_width = function() return true end, + is_full_height = function() return false end, + at_left_edge = function() return true end, + at_right_edge = function() return true end, + at_top_edge = function() return true end, + at_bottom_edge = function() return true end, + win_position = function() return 'start' end, + } + package.loaded['herdr-splits.herdr'] = { + is_in_session = function() return true end, + current_pane_is_zoomed = function() return false end, + resize_pane = function(direction, amount) + calls[#calls + 1] = { direction, amount } + return true + end, + } + package.loaded['herdr-splits.resize'] = nil + local config = require('herdr-splits.config') + config.default_amount = 0.03 + local resize = require('herdr-splits.resize') + + -- cmdwin + full-width + nofile: horizontal resize delegates to Herdr + cmdwin = true + resize.resize('left') + resize.resize('right', 0.2) + + -- ordinary nofile buffer (not cmdwin): sidebar rule still blocks delegation + cmdwin = false + resize.resize('left') + + return { calls = calls } + end) + + expect.equality(result, { + calls = { { 'left', 0.03 }, { 'right', 0.2 } }, + }) +end + +return T diff --git a/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_win.lua b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_win.lua new file mode 100644 index 0000000..c8312fd --- /dev/null +++ b/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/tests/test_win.lua @@ -0,0 +1,138 @@ +local T +local expect = MiniTest.expect +local child = MiniTest.new_child_neovim() + +local source = debug.getinfo(1, 'S').source +local root = vim.fn.fnamemodify(source:sub(2), ':p:h:h') + +T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ '--noplugin', '-u', 'NONE', '-i', 'NONE' }) + child.cmd('set runtimepath^=' .. vim.fn.fnameescape(root)) + end, + post_case = function() + child.stop() + end, + }, +}) + +T['win_position identifies horizontal split positions'] = function() + local positions = child.lua_func(function() + vim.cmd('vsplit | vsplit') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[2] < vim.api.nvim_win_get_position(b)[2] + end) + + local out = {} + for _, win in ipairs(wins) do + vim.api.nvim_set_current_win(win) + out[#out + 1] = require('herdr-splits.win').win_position('left') + end + return out + end) + + expect.equality(positions, { 'start', 'middle', 'last' }) +end + +T['win_position identifies vertical split positions'] = function() + local positions = child.lua_func(function() + vim.cmd('split | split') + local wins = vim.api.nvim_tabpage_list_wins(0) + table.sort(wins, function(a, b) + return vim.api.nvim_win_get_position(a)[1] < vim.api.nvim_win_get_position(b)[1] + end) + + local out = {} + for _, win in ipairs(wins) do + vim.api.nvim_set_current_win(win) + out[#out + 1] = require('herdr-splits.win').win_position('up') + end + return out + end) + + expect.equality(positions, { 'start', 'middle', 'last' }) +end + +T['floating classification distinguishes embedded floats'] = function() + local result = child.lua_func(function() + local win = require('herdr-splits.win') + local normal = vim.api.nvim_get_current_win() + local low = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), false, { + relative = 'editor', row = 1, col = 1, width = 10, height = 3, zindex = 49, + }) + local default = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, true), false, { + relative = 'editor', row = 2, col = 2, width = 10, height = 3, zindex = 50, + }) + + return { + normal_floating = win.is_floating(normal), + normal_embedded = win.is_embedded_floating_window(normal), + low_floating = win.is_floating(low), + low_embedded = win.is_embedded_floating_window(low), + default_floating = win.is_floating(default), + default_embedded = win.is_embedded_floating_window(default), + } + end) + + expect.equality(result, { + normal_floating = false, + normal_embedded = false, + low_floating = true, + low_embedded = true, + default_floating = true, + default_embedded = false, + }) +end + +T['ignored window classification uses buffer type and filetype'] = function() + local result = child.lua_func(function() + local win = require('herdr-splits.win') + local bufnr = vim.api.nvim_get_current_buf() + local regular = win.is_ignored_win() + + vim.api.nvim_set_option_value('buftype', 'nofile', { buf = bufnr }) + local ignored_buftype = win.is_ignored_win() + vim.api.nvim_set_option_value('buftype', '', { buf = bufnr }) + + vim.api.nvim_set_option_value('filetype', 'neo-tree', { buf = bufnr }) + local ignored_filetype = win.is_ignored_win() + return { + regular = regular, + ignored_buftype = ignored_buftype, + ignored_filetype = ignored_filetype, + } + end) + + expect.equality(result, { + regular = false, + ignored_buftype = true, + ignored_filetype = true, + }) +end + +T['preview windows are ignored only when configured'] = function() + local result = child.lua_func(function() + local win = require('herdr-splits.win') + local config = require('herdr-splits.config') + vim.wo.previewwindow = true + + config.ignore_previewwindows = false + local disabled = win.is_ignored_or_preview() + config.ignore_previewwindows = true + local enabled = win.is_ignored_or_preview() + return { disabled = disabled, enabled = enabled } + end) + + expect.equality(result, { disabled = false, enabled = true }) +end + +T['command-line window is not active in a normal buffer'] = function() + local result = child.lua_func(function() + return require('herdr-splits.win').is_command_line_window() + end) + expect.equality(result, false) +end + +return T diff --git a/dot_config/herdr/session.json b/dot_config/herdr/session.json new file mode 100644 index 0000000..fa51fc2 --- /dev/null +++ b/dot_config/herdr/session.json @@ -0,0 +1,40 @@ +{ + "version": 3, + "workspaces": [ + { + "id": "w5", + "custom_name": null, + "identity_cwd": "/Users/zamp", + "public_pane_numbers": { + "17": 1 + }, + "next_public_pane_number": 2, + "public_tab_numbers": [ + 1 + ], + "next_public_tab_number": 2, + "tabs": [ + { + "custom_name": null, + "layout": { + "Pane": 17 + }, + "panes": { + "17": { + "cwd": "/Users/zamp" + } + }, + "zoomed": false, + "focused": 17, + "root_pane": 17 + } + ], + "active_tab": 0 + } + ], + "active": 0, + "selected": 0, + "sidebar_width": 28, + "sidebar_section_split": 0.5081967, + "collapsed_space_keys": [] +} \ No newline at end of file diff --git a/dot_config/nvim/lua/plugins/herdr.lua b/dot_config/nvim/lua/plugins/herdr.lua new file mode 100644 index 0000000..d12f729 --- /dev/null +++ b/dot_config/nvim/lua/plugins/herdr.lua @@ -0,0 +1,98 @@ +return { + "lmilojevicc/herdr-splits.nvim", + cond = vim.env.HERDR_ENV == "1", + event = "VeryLazy", + + -- Keep the Neovim and Herdr halves on the same commit. + build = function() + require("herdr-splits").sync_herdr() + end, + + opts = { + auto_sync_herdr = true, + + -- Spatial navigation should stop at the outside edge. + -- Wrapping would make Ctrl+HJKL less predictable. + at_edge = "stop", + + -- Neovim split resizing is measured in cells. + neovim_amount = 3, + + -- Herdr pane resizing is a fraction of the terminal. + default_amount = 0.03, + + nav_keys = { + left = "", + down = "", + up = "", + right = "", + }, + + resize_keys = { + left = "", + down = "", + up = "", + right = "", + }, + }, + + keys = { + { + "", + function() + require("herdr-splits").move_cursor_left() + end, + desc = "Move left", + }, + { + "", + function() + require("herdr-splits").move_cursor_down() + end, + desc = "Move down", + }, + { + "", + function() + require("herdr-splits").move_cursor_up() + end, + desc = "Move up", + }, + { + "", + function() + require("herdr-splits").move_cursor_right() + end, + desc = "Move right", + }, + + { + "", + function() + require("herdr-splits").resize_left() + end, + desc = "Resize left", + }, + { + "", + function() + require("herdr-splits").resize_down() + end, + desc = "Resize down", + }, + { + "", + function() + require("herdr-splits").resize_up() + end, + desc = "Resize up", + }, + { + "", + function() + require("herdr-splits").resize_right() + end, + desc = "Resize right", + }, + }, +} diff --git a/dot_config/private_jj/config.toml b/dot_config/private_jj/config.toml new file mode 100644 index 0000000..cda9781 --- /dev/null +++ b/dot_config/private_jj/config.toml @@ -0,0 +1,5 @@ +#:schema https://docs.jj-vcs.dev/latest/config-schema.json + +[user] +name = "pablu" +email = "me@pablu.de" diff --git a/dot_config/private_jj/repos/90d6ab3cd758e327650c/config.toml b/dot_config/private_jj/repos/90d6ab3cd758e327650c/config.toml new file mode 100644 index 0000000..399f7a6 --- /dev/null +++ b/dot_config/private_jj/repos/90d6ab3cd758e327650c/config.toml @@ -0,0 +1,4 @@ +#:schema https://docs.jj-vcs.dev/latest/config-schema.json + +[snapshot] +max-new-file-size = 2345872 diff --git a/dot_config/private_jj/repos/90d6ab3cd758e327650c/private_metadata.binpb b/dot_config/private_jj/repos/90d6ab3cd758e327650c/private_metadata.binpb new file mode 100644 index 0000000..15c70a7 --- /dev/null +++ b/dot_config/private_jj/repos/90d6ab3cd758e327650c/private_metadata.binpb @@ -0,0 +1,2 @@ + +&/Users/zamp/projects/hochzeit/.jj/repo \ No newline at end of file diff --git a/dot_config/starship.toml b/dot_config/starship.toml index 37a9b22..aae12aa 100644 --- a/dot_config/starship.toml +++ b/dot_config/starship.toml @@ -2,6 +2,8 @@ palette = "catppuccin_mocha" +format = "$directory${custom.jj}$all" + [palettes.catppuccin_mocha] rosewater = "#f5e0dc" flamingo = "#f2cdcd" @@ -34,3 +36,13 @@ crust = "#11111b" truncation_length = 0 truncate_to_repo = false +[custom.jj] +when = "jj-starship detect" +shell = ["jj-starship"] +format = "$output " + +[git_branch] +disabled = true + +[git_status] +disabled = true diff --git a/dot_zshrc.tmpl b/dot_zshrc.tmpl index c1d46e9..2395fa2 100644 --- a/dot_zshrc.tmpl +++ b/dot_zshrc.tmpl @@ -77,6 +77,8 @@ source <(kubectl completion zsh) source <(kubebuilder completion zsh) source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh +source <(COMPLETE=zsh jj) + {{ else if eq .chezmoi.hostname "ubuntu" }} eval "$(direnv hook zsh)"