remove herdr overflow
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
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
|
||||
@@ -1,670 +0,0 @@
|
||||
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"
|
||||
@@ -1,122 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,126 +0,0 @@
|
||||
# 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 (`<C-h/j/k/l>`) and resizing (`<M-h/j/k/l>`) 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<C-h>`)
|
||||
- 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`).
|
||||
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,21 +0,0 @@
|
||||
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
|
||||
@@ -1,445 +0,0 @@
|
||||
# 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**: `<M-h/j/k/l>` 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<C-h>` moves three splits left; `5<M-l>` 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. <M-Left>).
|
||||
nav_keys = { left = '<C-h>', down = '<C-j>', up = '<C-k>', right = '<C-l>' },
|
||||
resize_keys = { left = '<M-h>', down = '<M-j>', up = '<M-k>', right = '<M-l>' },
|
||||
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 = {
|
||||
{ '<C-h>', function() require('herdr-splits').move_cursor_left() end, desc = 'Navigate left' },
|
||||
{ '<C-j>', function() require('herdr-splits').move_cursor_down() end, desc = 'Navigate down' },
|
||||
{ '<C-k>', function() require('herdr-splits').move_cursor_up() end, desc = 'Navigate up' },
|
||||
{ '<C-l>', function() require('herdr-splits').move_cursor_right() end, desc = 'Navigate right' },
|
||||
{ '<M-h>', function() require('herdr-splits').resize_left() end, desc = 'Resize left' },
|
||||
{ '<M-j>', function() require('herdr-splits').resize_down() end, desc = 'Resize down' },
|
||||
{ '<M-k>', function() require('herdr-splits').resize_up() end, desc = 'Resize up' },
|
||||
{ '<M-l>', 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. `<M-Left>`). 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 = '<M-Left>', down = '<M-Down>', up = '<M-Up>', right = '<M-Right>' },
|
||||
resize_keys = { left = '<M-S-Left>', down = '<M-S-Down>', up = '<M-S-Up>', right = '<M-S-Right>' },
|
||||
})
|
||||
```
|
||||
|
||||
- Pass Neovim notation in `setup()` (`<C-h>`, `<M-Left>`); 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: `<M-Left>` 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 <ratio> → 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<C-h> = 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
|
||||
@@ -1 +0,0 @@
|
||||
107273e004e4f7ef07f13c83164d2cb2c51df65d https://github.com/lmilojevicc/herdr-splits.nvim
|
||||
@@ -1 +0,0 @@
|
||||
107273e004e4f7ef07f13c83164d2cb2c51df65d
|
||||
@@ -1,10 +0,0 @@
|
||||
[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/*
|
||||
@@ -1 +0,0 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/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+"$@"}
|
||||
:
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/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
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/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 $/; <CHLD_OUT>};
|
||||
|
||||
# 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;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/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+"$@"}
|
||||
:
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/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 --
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/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"
|
||||
:
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/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:
|
||||
#
|
||||
# <local ref> <local oid> <remote ref> <remote oid>
|
||||
#
|
||||
# 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 </dev/null | tr '[0-9a-f]' '0')
|
||||
|
||||
while read local_ref local_oid remote_ref remote_oid
|
||||
do
|
||||
if test "$local_oid" = "$zero"
|
||||
then
|
||||
# Handle delete
|
||||
:
|
||||
else
|
||||
if test "$remote_oid" = "$zero"
|
||||
then
|
||||
# New branch, examine all commits
|
||||
range="$local_oid"
|
||||
else
|
||||
# Update to existing branch, examine new commits
|
||||
range="$remote_oid..$local_oid"
|
||||
fi
|
||||
|
||||
# Check for WIP commit
|
||||
commit=$(git rev-list -n 1 --grep '^WIP' "$range")
|
||||
if test -n "$commit"
|
||||
then
|
||||
echo >&2 "Found WIP commit in $local_ref, not pushing"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/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 </dev/null)
|
||||
fi
|
||||
|
||||
if ! git diff-index --quiet --cached --ignore-submodules $head --
|
||||
then
|
||||
die "Working directory has staged changes"
|
||||
fi
|
||||
|
||||
if ! git read-tree -u -m "$commit"
|
||||
then
|
||||
die "Could not update working tree to new HEAD"
|
||||
fi
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# An example hook script to validate a patch (and/or patch series) before
|
||||
# sending it via email.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an appropriate
|
||||
# message if it wants to prevent the email(s) from being sent.
|
||||
#
|
||||
# To enable this hook, rename this file to "sendemail-validate".
|
||||
#
|
||||
# By default, it will only check that the patch(es) can be applied on top of
|
||||
# the default upstream branch without conflicts in a secondary worktree. After
|
||||
# validation (successful or not) of the last patch of a series, the worktree
|
||||
# will be deleted.
|
||||
#
|
||||
# The following config variables can be set to change the default remote and
|
||||
# remote ref that are used to apply the patches against:
|
||||
#
|
||||
# sendemail.validateRemote (default: origin)
|
||||
# sendemail.validateRemoteRef (default: HEAD)
|
||||
#
|
||||
# Replace the TODO placeholders with appropriate checks according to your
|
||||
# needs.
|
||||
|
||||
validate_cover_letter () {
|
||||
file="$1"
|
||||
# TODO: Replace with appropriate checks (e.g. spell checking).
|
||||
true
|
||||
}
|
||||
|
||||
validate_patch () {
|
||||
file="$1"
|
||||
# Ensure that the patch applies without conflicts.
|
||||
git am -3 "$file" || return
|
||||
# TODO: Replace with appropriate checks for this patch
|
||||
# (e.g. checkpatch.pl).
|
||||
true
|
||||
}
|
||||
|
||||
validate_series () {
|
||||
# TODO: Replace with appropriate checks for the whole series
|
||||
# (e.g. quick build, coding style checks, etc.).
|
||||
true
|
||||
}
|
||||
|
||||
# main -------------------------------------------------------------------------
|
||||
|
||||
if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
|
||||
then
|
||||
remote=$(git config --default origin --get sendemail.validateRemote) &&
|
||||
ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
|
||||
worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
|
||||
git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
|
||||
git config --replace-all sendemail.validateWorktree "$worktree"
|
||||
else
|
||||
worktree=$(git config --get sendemail.validateWorktree)
|
||||
fi || {
|
||||
echo "sendemail-validate: error: failed to prepare worktree" >&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
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/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 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "usage: $0 <ref> <oldrev> <newrev>" >&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 </dev/null | tr '[0-9a-f]' '0')
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
newrev_type=delete
|
||||
else
|
||||
newrev_type=$(git cat-file -t $newrev)
|
||||
fi
|
||||
|
||||
case "$refname","$newrev_type" in
|
||||
refs/tags/*,commit)
|
||||
# un-annotated tag
|
||||
short_refname=${refname##refs/tags/}
|
||||
if [ "$allowunannotated" != "true" ]; then
|
||||
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&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
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
# 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]
|
||||
# *~
|
||||
@@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 107273e004e4f7ef07f13c83164d2cb2c51df65d Pablu <me@pablu.de> 1784924592 +0200 checkout: moving from main to FETCH_HEAD
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
x<01>S͎<53>0<><EE99A7>kB<>Ӫ=<3D><><17><><EFBFBD><EFBFBD>Cpcl<63><6C>d<EFBFBD>վ{<7B>&R6Z<36>Ro<52><6F>0<EFBFBD><30><EFBFBD>g><3E><><EFBFBD><EFBFBD>}<7D><><EFBFBD><06><>kQX3<14><><EFBFBD><EFBFBD><EFBFBD>I<EFBFBD>p܌3<DC8C><33>|_<>2?rS<72><53>~G<>a(<28>ܢ<>W<EFBFBD><57>42Z<13>p<>eQ<65><51><18>3^R<><52>l\<07><><EFBFBD><EFBFBD>T<><54>(<28><>'m<><6D>兾*;<3B><><EFBFBD>"<01>T<19>:{p<>qup<11><><EFBFBD><19>Ϡ<EFBFBD>Fw<13><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <09> <10><><EFBFBD><14><>(<28> <09>j<1E>{n<05><><EFBFBD><EFBFBD>.<2E>D,d<><64>><3E><>w<EFBFBD>qJ<71>O-><3E><><EFBFBD><01><18>8<EFBFBD>X7<58>X<EFBFBD><58>[Q <09>ƺ<EFBFBD><C6BA><EFBFBD><EFBFBD>;<3B>%kAHV<48><56><EFBFBD>:<3A>#<23>ɩ<EFBFBD>{Ӻ3<D3BA><33>ئ74<1B>W><3E>сD<D181>5<D788>/\Gh0<><30><1F>dl<64><17>N:^+<2B><06><><EFBFBD>l<EFBFBD>SO<53><4F>zJ <20>l<EFBFBD><6C><EFBFBD>#<23><>Z<EFBFBD>жC<D0B6>b<EFBFBD>=q<><71><EFBFBD><11><><EFBFBD>{<7B>w<EFBFBD><77>1:U%<25><><EFBFBD>#<23><>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
x<01>PKj<4B>0<0C>ڧ<EFBFBD><DAA7>N )<29>&<10>z<><7A><EFBFBD>r<EFBFBD><72><EFBFBD><EFBFBD>r<EFBFBD><72>w<EFBFBD><77> <09><>+I<><49><EFBFBD>66xߞl<DF9E>W9<><39>#̠p+<2B><<3C><><EFBFBD>A<EFBFBD>=<3D><0F>ɫ<EFBFBD>֜1<D69C><31><EFBFBD>)<29><><01><0E><19>;<3B><>=<14><><07>FK<08>x<EFBFBD>V<EFBFBD>ԏ<EFBFBD>-+<2B><18>S<><53><19><><EFBFBD><EFBFBD>:tA<19><>H<EFBFBD><48>U<EFBFBD>)N<><4E>V'6D<36><44>x2<0E>J<EFBFBD>)&<26><><EFBFBD>ht<68>#@J<><4A><EFBFBD><C298>Lfj<66>~<16>P<EFBFBD><50><EFBFBD><EFBFBD>H%<25><06>kqov[<06><>W<EFBFBD>6s-<2D><>w2<77><32><EFBFBD><EFBFBD><01>:<3A><><EFBFBD>fG=<3D><><EFBFBD><EFBFBD>n<EFBFBD>yhqy><0F><03><>W<EFBFBD><18>:}<7D><><EFBFBD><EFBFBD>ޱ_@$<24><>
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
xu<><75>R<EFBFBD>0D<><44><15>%)<29><><19><>hh<68><68>'rt<72>o"<22><>Ir<49><72>G<EFBFBD>a<EFBFBD>*<2A>n<EFBFBD>n<EFBFBD>n<17><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>1́s<CD81>q<EFBFBD> s(Gt"9eu<65>5Uv9<><39>@<40>o<10>ɣ<EFBFBD><C9A3>;I[<5B><><14>G<EFBFBD><47>mZ)<29>C<EFBFBD><43><EFBFBD><EFBFBD>y<EFBFBD><79><EFBFBD>q<EFBFBD><71><EFBFBD><EFBFBD><EFBFBD>'Mp!<21>.zC\<5C>>
|
||||
+<2B>w<EFBFBD><77>v<EFBFBD><76>5<EFBFBD>{T#;<3B><><EFBFBD><EFBFBD>*<2A>vs<76>P<>E<EFBFBD><45><EFBFBD><EFBFBD><EFBFBD><EFBFBD>d-dZ<64>׆<EFBFBD><D786><EFBFBD>8<EFBFBD>!<06>TG<54>J<EFBFBD><4A><EFBFBD><EFBFBD>y<79>(8.r<>J<EFBFBD><4A><EFBFBD>kf<6B>ߥ&W<><57>ė<EFBFBD><1A>8<0E><><1F>*Пy<D09F><79><02>4<EFBFBD><34>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
x<01><>[K1<10>}m~Ű<><C5B0>><3E>k}hiU<69>b<EFBFBD><17>"B<>n<EFBFBD>&<26><>.<2E>T<EFBFBD><54>;I<><49>܇0<DC87>M<EFBFBD>sN<73><4E>u<17><>'<27><><EFBFBD><1A>chwY<77><1A>%V\Y\5s<35><73><EFBFBD><1C><><EFBFBD><EFBFBD><EFBFBD>v6<76>n泧u?<3F>|<7C>-<2D>rf<>*B<>w<><77><EFBFBD><10>hx<><<3C>R<EFBFBD>o<EFBFBD>I<EFBFBD><49><EFBFBD>&$<24><1C>֕<EFBFBD><D695>Ny<4E>7<EFBFBD><37><EFBFBD>Q<EFBFBD><51><EFBFBD>vt<76><74>p@<40><><EFBFBD><EFBFBD>4<EFBFBD><34><EFBFBD>R&b^)<29><> ;ɹ<><C9B9>& <20>l<EFBFBD><19><>Ka<4B><61><EFBFBD>Z<5A>.U̓μ@4bP<62><50><11>9<EFBFBD>*<2A><><EFBFBD><EFBFBD>%3<>D<EFBFBD><44>H<EFBFBD>$L<>G<EFBFBD>h<EFBFBD>q_<71>rX<><58>x<EFBFBD><78>]<5D><>o!q<><71>*<2A>U<EFBFBD>#!<21>N'6<>1<<3C>R0<52><30>c|S7:<3A><><01>`<60>ZN<5A><1A>wsWZ<57><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>M<0C>T<EFBFBD><54><EFBFBD><EFBFBD><,pk<1D><>`<60><><EFBFBD><EFBFBD>o<EFBFBD><6F><14>I<EFBFBD><49>/Q
|
||||
<EFBFBD>-5l<35>w2<77><32><EFBFBD>N}<7D>t<EFBFBD> 0<>1<EFBFBD><18>|E<1B>w
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
107273e004e4f7ef07f13c83164d2cb2c51df65d
|
||||
@@ -1,100 +0,0 @@
|
||||
name: Bug report
|
||||
description: Report something that isn't working as expected
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to file a bug report. Please fill in the
|
||||
fields below so it can be reproduced.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear description of the bug.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: nvim-version
|
||||
attributes:
|
||||
label: Neovim version
|
||||
description: Output of `nvim --version` (first line is enough).
|
||||
placeholder: "NVIM v0.10.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: plugin-manager
|
||||
attributes:
|
||||
label: Plugin manager
|
||||
description: How is herdr-splits.nvim installed?
|
||||
options:
|
||||
- lazy.nvim
|
||||
- packer.nvim
|
||||
- None / manual (local path)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: herdr-version
|
||||
attributes:
|
||||
label: Herdr version
|
||||
description: Output of `herdr --version`.
|
||||
placeholder: "herdr 0.7.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: herdr-splits-version
|
||||
attributes:
|
||||
label: herdr-splits.nvim version
|
||||
description: The plugin version / commit you're on (check with your plugin manager).
|
||||
placeholder: "0.3.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: Minimal, numbered steps that trigger the bug.
|
||||
placeholder: |
|
||||
1. Open Neovim inside a Herdr session (`HERDR_ENV=1`)
|
||||
2. Create two vertical splits
|
||||
3. Press `<C-l>`
|
||||
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
|
||||
@@ -1,32 +0,0 @@
|
||||
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
|
||||
@@ -1,31 +0,0 @@
|
||||
<!-- Thanks for the PR! Fill in the sections below. -->
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- What does this change do, and why? One or two sentences. -->
|
||||
|
||||
## Changes
|
||||
|
||||
<!-- Bullet list of the notable changes. Mention any user-facing changes. -->
|
||||
|
||||
-
|
||||
|
||||
## Validation
|
||||
|
||||
<!-- Check the automated commands you ran and describe any manual coverage. -->
|
||||
|
||||
- [ ] `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 (`<C-h/j/k/l>`) works across Neovim splits and Herdr panes
|
||||
- [ ] Resizing (`<M-h/j/k/l>`) 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
|
||||
|
||||
<!-- Anything reviewers should pay attention to, or follow-up work. -->
|
||||
|
||||
Closes #NN
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
.pi-subagents/
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "deps/mini.test"]
|
||||
path = deps/mini.test
|
||||
url = https://github.com/nvim-mini/mini.test.git
|
||||
@@ -1,62 +0,0 @@
|
||||
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"]
|
||||
@@ -1,236 +0,0 @@
|
||||
---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. `<C-h>`, `<M-Left>`, `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 = {
|
||||
{ '<C-h>', 'ctrl+h' }, { '<C-j>', 'ctrl+j' }, { '<C-k>', 'ctrl+k' }, { '<C-l>', 'ctrl+l' },
|
||||
{ '<M-h>', 'alt+h' }, { '<M-Left>', 'alt+left' }, { '<S-Left>', 'shift+left' },
|
||||
{ '<D-x>', 'cmd+x' }, { '<C-M-h>', 'ctrl+alt+h' }, { 'h', 'h' }, { '<Esc>', '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
|
||||
@@ -1,146 +0,0 @@
|
||||
---@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. `<C-h>`, `<M-Left>`); stored/written in Herdr notation.
|
||||
---@field nav_keys table<string,string> Forward chords per direction: {left,down,up,right} (Neovim notation in opts; Herdr notation internally)
|
||||
---@field resize_keys table<string,string> 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
|
||||
@@ -1,46 +0,0 @@
|
||||
---:checkhealth herdr-splits integration.
|
||||
---Discovered automatically by Neovim's built-in `:checkhealth` via the
|
||||
---runtimefile lookup in `lua/<plugin>/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
|
||||
@@ -1,160 +0,0 @@
|
||||
---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 <dir> --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 <dir> --amount <float> --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
|
||||
@@ -1,162 +0,0 @@
|
||||
---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', '<C-h>', require('herdr-splits').move_cursor_left)
|
||||
--- vim.keymap.set('n', '<M-h>', 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
|
||||
@@ -1,206 +0,0 @@
|
||||
---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
|
||||
@@ -1,108 +0,0 @@
|
||||
---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
|
||||
@@ -1,107 +0,0 @@
|
||||
---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 -- "@<abs>/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
|
||||
@@ -1,154 +0,0 @@
|
||||
---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
|
||||
@@ -1,11 +0,0 @@
|
||||
-- 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').
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/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 <left|down|up|right>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
dir="${1:?usage: herdr-nav.sh <left|down|up|right>}"
|
||||
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
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/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 <left|down|up|right> [amount]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
dir="${1:?usage: herdr-resize.sh <left|down|up|right> [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
|
||||
@@ -1,14 +0,0 @@
|
||||
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,
|
||||
},
|
||||
})
|
||||
@@ -1,196 +0,0 @@
|
||||
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('<C-M-Left>'), 'ctrl+alt+left')
|
||||
expect.equality(translate('<A-Return>'), 'alt+enter')
|
||||
expect.equality(translate('<S-Space>'), 'shift+space')
|
||||
expect.equality(translate('H'), 'h')
|
||||
expect.equality(translate('<Escape>'), 'esc')
|
||||
expect.equality(translate(''), vim.NIL)
|
||||
expect.equality(translate(false), vim.NIL)
|
||||
expect.equality(translate('<C>'), 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 = '<C-x>' },
|
||||
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 = '<C-r>' } })
|
||||
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
|
||||
@@ -1,267 +0,0 @@
|
||||
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 '<nil>' }
|
||||
vim.env.HERDR_ENV = '1'
|
||||
vim.env.HERDR_PANE_ID = ''
|
||||
local empty = { herdr.is_in_session(), herdr.current_pane_id() or '<nil>' }
|
||||
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, '<nil>' },
|
||||
empty = { false, '<nil>' },
|
||||
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 '<nil>' or value
|
||||
end
|
||||
return { values = values, calls = calls }
|
||||
end)
|
||||
|
||||
expect.equality(result.values, {
|
||||
true, false, '<nil>', '<nil>', '<nil>', '<nil>', '<nil>', '<nil>', '<nil>',
|
||||
})
|
||||
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 '<nil>' or value
|
||||
end
|
||||
return { values = values, calls = calls }
|
||||
end)
|
||||
|
||||
expect.equality(result.values, {
|
||||
true, false, '<nil>', '<nil>', '<nil>', '<nil>', '<nil>', '<nil>',
|
||||
})
|
||||
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 '<nil>' or value }
|
||||
end
|
||||
for _ = 1, 3 do
|
||||
local ok, value = pcall(herdr.current_pane_is_zoomed)
|
||||
values[#values + 1] = { ok, value == nil and '<nil>' or value }
|
||||
end
|
||||
return values
|
||||
end)
|
||||
|
||||
expect.equality(result, {
|
||||
{ true, '<nil>' },
|
||||
{ true, '<nil>' },
|
||||
{ true, '<nil>' },
|
||||
{ true, '<nil>' },
|
||||
{ true, '<nil>' },
|
||||
{ true, '<nil>' },
|
||||
})
|
||||
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
|
||||
@@ -1,520 +0,0 @@
|
||||
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, '<nil>' }
|
||||
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 == '<nil>' 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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user