Skip to content

Commit b410b1a

Browse files
diegosouzapwsauyon
andauthored
feat: add focusOnOpen option to Terminal (#2)
Add a focusOnOpen boolean option (default: true) that controls whether the terminal automatically focuses itself when open() is called. Setting it to false lets embedders open a terminal in the background without stealing keyboard focus from another element. Resolves coder#100. Inspired-by: coder#149 Co-authored-by: Sauyon Lee <git@sjle.co>
1 parent 2f64784 commit b410b1a

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

lib/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export interface ITerminalOptions {
1919
convertEol?: boolean; // Convert \n to \r\n (default: false)
2020
disableStdin?: boolean; // Disable keyboard input (default: false)
2121

22+
// Focus options
23+
focusOnOpen?: boolean; // Auto-focus terminal on open (default: true)
24+
2225
// Scrolling options
2326
smoothScrollDuration?: number; // Duration in ms for smooth scroll animation (default: 100, 0 = instant)
2427

lib/terminal.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,4 +2989,35 @@ describe('Synchronous open()', () => {
29892989

29902990
term.dispose();
29912991
});
2992+
2993+
test('focusOnOpen: false prevents auto-focus on open', async () => {
2994+
if (!container) return;
2995+
2996+
// Focus a different element first
2997+
const other = document.createElement('input');
2998+
document.body.appendChild(other);
2999+
other.focus();
3000+
expect(document.activeElement).toBe(other);
3001+
3002+
const term = await createIsolatedTerminal({ focusOnOpen: false });
3003+
term.open(container);
3004+
3005+
// The terminal should NOT have stolen focus
3006+
expect(document.activeElement).toBe(other);
3007+
3008+
other.remove();
3009+
term.dispose();
3010+
});
3011+
3012+
test('focusOnOpen defaults to true', async () => {
3013+
if (!container) return;
3014+
3015+
const term = await createIsolatedTerminal();
3016+
term.open(container);
3017+
3018+
// The terminal should have taken focus
3019+
expect(document.activeElement).toBe(container);
3020+
3021+
term.dispose();
3022+
});
29923023
});

lib/terminal.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export class Terminal implements ITerminalCore {
151151
convertEol: options.convertEol ?? false,
152152
disableStdin: options.disableStdin ?? false,
153153
smoothScrollDuration: options.smoothScrollDuration ?? 100, // Default: 100ms smooth scroll
154+
focusOnOpen: options.focusOnOpen ?? true,
154155
};
155156

156157
// Wrap in Proxy to intercept runtime changes (xterm.js compatibility)
@@ -526,7 +527,9 @@ export class Terminal implements ITerminalCore {
526527
this.startRenderLoop();
527528

528529
// Focus input (auto-focus so user can start typing immediately)
529-
this.focus();
530+
if (this.options.focusOnOpen !== false) {
531+
this.focus();
532+
}
530533
} catch (error) {
531534
// Clean up on error
532535
this.isOpen = false;

0 commit comments

Comments
 (0)