Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/selection-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class SelectionManager {
// Store bound event handlers for cleanup
private boundMouseUpHandler: ((e: MouseEvent) => void) | null = null;
private boundContextMenuHandler: ((e: MouseEvent) => void) | null = null;
private boundClickHandler: ((e: MouseEvent) => void) | null = null;

constructor(
terminal: Terminal,
Expand Down Expand Up @@ -291,6 +292,12 @@ export class SelectionManager {
this.boundContextMenuHandler = null;
}

// Clean up document click listener
if (this.boundClickHandler) {
document.removeEventListener('click', this.boundClickHandler);
this.boundClickHandler = null;
}

// Canvas event listeners will be cleaned up when canvas is removed from DOM
}

Expand Down Expand Up @@ -444,6 +451,21 @@ export class SelectionManager {
};

canvas.addEventListener('contextmenu', this.boundContextMenuHandler);

// Click outside canvas - clear selection
// This allows users to deselect by clicking anywhere outside the terminal
this.boundClickHandler = (e: MouseEvent) => {
// Check if the click is outside the canvas
const target = e.target as Node;
if (!canvas.contains(target)) {
// Clicked outside the canvas - clear selection
if (this.hasSelection()) {
this.clearSelection();
}
}
};

document.addEventListener('click', this.boundClickHandler);
}

/**
Expand Down
23 changes: 23 additions & 0 deletions lib/terminal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,29 @@ describe('select()', () => {
expect(fired).toBe(true);
term.dispose();
});

test('should clear selection when clicking outside canvas', async () => {
const term = new Terminal({ cols: 80, rows: 24 });
// Using shared container from beforeEach
if (!container) return;
await term.open(container);

// Create a selection
term.select(0, 0, 10);
expect(term.hasSelection()).toBe(true);

// Simulate click outside the canvas (on document body)
const clickEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
});
document.body.dispatchEvent(clickEvent);

// Selection should be cleared
expect(term.hasSelection()).toBe(false);
term.dispose();
});
});
});

Expand Down