Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 020a022

Browse files
feat: add focus_after_send option for terminal behavior
Change-Id: Iff065cea29c3935b76be2c78cd27e0bd9e9f9854 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent fbcc5d8 commit 020a022

File tree

6 files changed

+147
-1
lines changed

6 files changed

+147
-1
lines changed

‎README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ For deep technical details, see [ARCHITECTURE.md](./ARCHITECTURE.md).
249249
-- For local installations: "~/.claude/local/claude"
250250
-- For native binary: use output from 'which claude'
251251

252+
-- Send/Focus Behavior
253+
-- When true, successful sends will focus the Claude terminal if already connected
254+
focus_after_send = false,
255+
252256
-- Selection Tracking
253257
track_selection = true,
254258
visual_demotion_delay_ms = 50,

‎dev-config.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ return {
4848
-- log_level = "info", -- "trace", "debug", "info", "warn", "error"
4949
-- terminal_cmd = nil, -- Custom terminal command (default: "claude")
5050

51+
-- Send/Focus Behavior
52+
focus_after_send = true, -- Focus Claude terminal after successful send while connected
53+
5154
-- Selection Tracking
5255
-- track_selection = true, -- Enable real-time selection tracking
5356
-- visual_demotion_delay_ms = 50, -- Delay before demoting visual selection (ms)

‎lua/claudecode/config.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ M.defaults = {
1414
env = {}, -- Custom environment variables for Claude terminal
1515
log_level = "info",
1616
track_selection = true,
17+
-- When true, focus Claude terminal after a successful send while connected
18+
focus_after_send = false,
1719
visual_demotion_delay_ms = 50, -- Milliseconds to wait before demoting a visual selection
1820
connection_wait_delay = 200, -- Milliseconds to wait after connection before sending queued @ mentions
1921
connection_timeout = 10000, -- Maximum time to wait for Claude Code to connect (milliseconds)
@@ -85,6 +87,7 @@ function M.validate(config)
8587
assert(is_valid_log_level, "log_level must be one of: " .. table.concat(valid_log_levels, ", "))
8688

8789
assert(type(config.track_selection) == "boolean", "track_selection must be a boolean")
90+
assert(type(config.focus_after_send) == "boolean", "focus_after_send must be a boolean")
8891

8992
assert(
9093
type(config.visual_demotion_delay_ms) == "number" and config.visual_demotion_delay_ms >= 0,

‎lua/claudecode/init.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,12 @@ function M.send_at_mention(file_path, start_line, end_line, context)
269269
local success, error_msg = M._broadcast_at_mention(file_path, start_line, end_line)
270270
if success then
271271
local terminal = require("claudecode.terminal")
272-
terminal.ensure_visible()
272+
if M.state.config and M.state.config.focus_after_send then
273+
-- Open focuses the terminal without toggling/hiding if already focused
274+
terminal.open()
275+
else
276+
terminal.ensure_visible()
277+
end
273278
end
274279
return success, error_msg
275280
else

‎lua/claudecode/types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
---@field env table<string, string>
109109
---@field log_level ClaudeCodeLogLevel
110110
---@field track_selection boolean
111+
---@field focus_after_send boolean
111112
---@field visual_demotion_delay_ms number
112113
---@field connection_wait_delay number
113114
---@field connection_timeout number

‎tests/unit/focus_after_send_spec.lua

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
require("tests.busted_setup")
2+
require("tests.mocks.vim")
3+
4+
describe("focus_after_send behavior", function()
5+
local saved_require
6+
local claudecode
7+
8+
local mock_terminal
9+
local mock_logger
10+
local mock_server_facade
11+
12+
local function setup_mocks(focus_after_send)
13+
mock_terminal = {
14+
setup = function() end,
15+
open = spy.new(function() end),
16+
ensure_visible = spy.new(function() end),
17+
}
18+
19+
mock_logger = {
20+
setup = function() end,
21+
debug = function() end,
22+
info = function() end,
23+
warn = function() end,
24+
error = function() end,
25+
}
26+
27+
mock_server_facade = {
28+
broadcast = spy.new(function()
29+
return true
30+
end),
31+
}
32+
33+
local mock_config = {
34+
apply = function()
35+
-- Return only fields used in this test path
36+
return {
37+
auto_start = false,
38+
terminal_cmd = nil,
39+
env = {},
40+
log_level = "info",
41+
track_selection = false,
42+
focus_after_send = focus_after_send,
43+
diff_opts = {
44+
layout = "vertical",
45+
open_in_new_tab = false,
46+
keep_terminal_focus = false,
47+
on_new_file_reject = "keep_empty",
48+
},
49+
models = { { name = "Claude Sonnet 4 (Latest)", value = "sonnet" } },
50+
}
51+
end,
52+
}
53+
54+
saved_require = _G.require
55+
_G.require = function(mod)
56+
if mod == "claudecode.config" then
57+
return mock_config
58+
elseif mod == "claudecode.logger" then
59+
return mock_logger
60+
elseif mod == "claudecode.diff" then
61+
return { setup = function() end }
62+
elseif mod == "claudecode.terminal" then
63+
return mock_terminal
64+
elseif mod == "claudecode.server.init" then
65+
return {
66+
get_status = function()
67+
return { running = true, client_count = 1 }
68+
end,
69+
}
70+
else
71+
return saved_require(mod)
72+
end
73+
end
74+
end
75+
76+
local function teardown_mocks()
77+
_G.require = saved_require
78+
package.loaded["claudecode"] = nil
79+
package.loaded["claudecode.config"] = nil
80+
package.loaded["claudecode.logger"] = nil
81+
package.loaded["claudecode.diff"] = nil
82+
package.loaded["claudecode.terminal"] = nil
83+
package.loaded["claudecode.server.init"] = nil
84+
end
85+
86+
after_each(function()
87+
teardown_mocks()
88+
end)
89+
90+
it("focuses terminal with open() when enabled", function()
91+
setup_mocks(true)
92+
93+
claudecode = require("claudecode")
94+
claudecode.setup({})
95+
96+
-- Mark server as present and stub low-level broadcast to succeed
97+
claudecode.state.server = mock_server_facade
98+
claudecode._broadcast_at_mention = spy.new(function()
99+
return true, nil
100+
end)
101+
102+
-- Act
103+
local ok, err = claudecode.send_at_mention("/tmp/file.lua", nil, nil, "test")
104+
assert.is_true(ok)
105+
assert.is_nil(err)
106+
107+
-- Assert focus behavior
108+
assert.spy(mock_terminal.open).was_called()
109+
assert.spy(mock_terminal.ensure_visible).was_not_called()
110+
end)
111+
112+
it("only ensures visibility when disabled (default)", function()
113+
setup_mocks(false)
114+
115+
claudecode = require("claudecode")
116+
claudecode.setup({})
117+
118+
claudecode.state.server = mock_server_facade
119+
claudecode._broadcast_at_mention = spy.new(function()
120+
return true, nil
121+
end)
122+
123+
local ok, err = claudecode.send_at_mention("/tmp/file.lua", nil, nil, "test")
124+
assert.is_true(ok)
125+
assert.is_nil(err)
126+
127+
assert.spy(mock_terminal.ensure_visible).was_called()
128+
assert.spy(mock_terminal.open).was_not_called()
129+
end)
130+
end)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /