File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -18,7 +18,7 @@ import { getBlueprintUrl } from "../../utils/url.js";
1818import { colors } from "../../utils/theme.js" ;
1919import { getStatusDisplay } from "../../components/StatusBadge.js" ;
2020import { DevboxCreatePage } from "../../components/DevboxCreatePage.js" ;
21- import { exitAlternateScreenBuffer } from "../../utils/screen .js" ;
21+ import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC .js" ;
2222
2323const PAGE_SIZE = 10 ;
2424const MAX_FETCH = 100 ;
@@ -310,17 +310,14 @@ const ListBlueprintsUI = ({
310310 } ;
311311 } , [ ] ) ;
312312
313+ // Handle Ctrl+C to exit
314+ useExitOnCtrlC ( ) ;
315+
313316 // Handle input for all views - combined into single hook
314317 useInput ( ( input , key ) => {
315318 // Don't process input if unmounting
316319 if ( ! isMounted . current ) return ;
317320
318- // Handle Ctrl+C to force exit
319- if ( key . ctrl && input === "c" ) {
320- exitAlternateScreenBuffer ( ) ; // Exit alternate screen
321- process . exit ( 130 ) ;
322- }
323-
324321 // Handle operation input mode
325322 if ( executingOperation && ! operationResult && ! operationError ) {
326323 const currentOp = allOperations . find (
Original file line number Diff line number Diff line change @@ -18,7 +18,7 @@ import { ResourceActionsMenu } from "../../components/ResourceActionsMenu.js";
1818import { ActionsPopup } from "../../components/ActionsPopup.js" ;
1919import { getDevboxUrl } from "../../utils/url.js" ;
2020import { useViewportHeight } from "../../hooks/useViewportHeight.js" ;
21- import { exitAlternateScreenBuffer } from "../../utils/screen .js" ;
21+ import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC .js" ;
2222import { colors } from "../../utils/theme.js" ;
2323
2424interface ListOptions {
@@ -548,13 +548,10 @@ const ListDevboxesUI = ({
548548
549549 // Removed refresh icon animation to prevent constant re-renders and flashing
550550
551- useInput ( ( input , key ) => {
552- // Handle Ctrl+C to force exit
553- if ( key . ctrl && input === "c" ) {
554- exitAlternateScreenBuffer ( ) ; // Exit alternate screen
555- process . exit ( 130 ) ;
556- }
551+ // Handle Ctrl+C to exit
552+ useExitOnCtrlC ( ) ;
557553
554+ useInput ( ( input , key ) => {
558555 const pageDevboxes = currentDevboxes . length ;
559556
560557 // Skip input handling when in search mode - let TextInput handle it
Original file line number Diff line number Diff line change @@ -10,6 +10,7 @@ import { Breadcrumb } from "./Breadcrumb.js";
1010import { colors } from "../utils/theme.js" ;
1111import { useViewportHeight } from "../hooks/useViewportHeight.js" ;
1212import { useNavigation } from "../store/navigationStore.js" ;
13+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js" ;
1314import {
1415 getDevboxLogs ,
1516 execCommand ,
@@ -224,6 +225,9 @@ export const DevboxActionsMenu = ({
224225 }
225226 } , [ executingOperation ] ) ;
226227
228+ // Handle Ctrl+C to exit
229+ useExitOnCtrlC ( ) ;
230+
227231 useInput ( ( input , key ) => {
228232 // Handle operation input mode
229233 if ( executingOperation && ! operationResult && ! operationError ) {
Original file line number Diff line number Diff line change @@ -10,6 +10,7 @@ import { SuccessMessage } from "./SuccessMessage.js";
1010import { Breadcrumb } from "./Breadcrumb.js" ;
1111import { MetadataDisplay } from "./MetadataDisplay.js" ;
1212import { colors } from "../utils/theme.js" ;
13+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js" ;
1314
1415interface DevboxCreatePageProps {
1516 onBack : ( ) => void ;
@@ -135,6 +136,9 @@ export const DevboxCreatePage = ({
135136
136137 const currentFieldIndex = fields . findIndex ( ( f ) => f . key === currentField ) ;
137138
139+ // Handle Ctrl+C to exit
140+ useExitOnCtrlC ( ) ;
141+
138142 useInput ( ( input , key ) => {
139143 // Handle result screen
140144 if ( result ) {
Original file line number Diff line number Diff line change @@ -9,6 +9,7 @@ import { DevboxActionsMenu } from "./DevboxActionsMenu.js";
99import { getDevboxUrl } from "../utils/url.js" ;
1010import { colors } from "../utils/theme.js" ;
1111import { useViewportHeight } from "../hooks/useViewportHeight.js" ;
12+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js" ;
1213
1314interface DevboxDetailPageProps {
1415 devbox : any ;
@@ -171,6 +172,9 @@ export const DevboxDetailPage = ({
171172 ? formatTimeAgo ( selectedDevbox . create_time_ms )
172173 : "" ;
173174
175+ // Handle Ctrl+C to exit
176+ useExitOnCtrlC ( ) ;
177+
174178 useInput ( ( input , key ) => {
175179 // Don't process input if unmounting
176180 if ( ! isMounted . current ) return ;
@@ -605,7 +609,7 @@ export const DevboxDetailPage = ({
605609
606610 // Detailed info mode - full screen
607611 if ( showDetailedInfo ) {
608- const detailLines = [ < > </ > ] ; // buildDetailLines()]] ;
612+ const detailLines = buildDetailLines ( ) ;
609613 const viewportHeight = detailViewport . viewportHeight ;
610614 const maxScroll = Math . max ( 0 , detailLines . length - viewportHeight ) ;
611615 const actualScroll = Math . min ( detailScroll , maxScroll ) ;
Original file line number Diff line number Diff line change @@ -6,7 +6,7 @@ import { Breadcrumb } from "./Breadcrumb.js";
66import { VERSION } from "../cli.js" ;
77import { colors } from "../utils/theme.js" ;
88import { useViewportHeight } from "../hooks/useViewportHeight.js" ;
9- import { exitAlternateScreenBuffer } from "../utils/screen .js" ;
9+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC .js" ;
1010
1111interface MenuItem {
1212 key : string ;
@@ -51,6 +51,9 @@ export const MainMenu = ({ onSelect }: MainMenuProps) => {
5151 // Use centralized viewport hook for consistent layout
5252 const { terminalHeight } = useViewportHeight ( { overhead : 0 } ) ;
5353
54+ // Handle Ctrl+C to exit
55+ useExitOnCtrlC ( ) ;
56+
5457 useInput ( ( input , key ) => {
5558 if ( key . upArrow && selectedIndex > 0 ) {
5659 setSelectedIndex ( selectedIndex - 1 ) ;
@@ -66,9 +69,6 @@ export const MainMenu = ({ onSelect }: MainMenuProps) => {
6669 onSelect ( "blueprints" ) ;
6770 } else if ( input === "s" || input === "3" ) {
6871 onSelect ( "snapshots" ) ;
69- } else if ( key . ctrl && input === "c" ) {
70- exitAlternateScreenBuffer ( ) ;
71- process . exit ( 130 ) ;
7272 }
7373 } ) ;
7474
Original file line number Diff line number Diff line change @@ -8,7 +8,7 @@ import { ErrorMessage } from "./ErrorMessage.js";
88import { Table , Column } from "./Table.js" ;
99import { colors } from "../utils/theme.js" ;
1010import { useViewportHeight } from "../hooks/useViewportHeight.js" ;
11- import { exitAlternateScreenBuffer } from "../utils/screen .js" ;
11+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC .js" ;
1212
1313// Format time ago in a succinct way
1414export const formatTimeAgo = ( timestamp : number ) : string => {
@@ -211,17 +211,14 @@ export function ResourceListView<T>({ config }: ResourceListViewProps<T>) {
211211
212212 const selectedResource = currentResources [ selectedIndex ] ;
213213
214+ // Handle Ctrl+C to exit
215+ useExitOnCtrlC ( ) ;
216+
214217 // Input handling
215218 useInput ( ( input , key ) => {
216219 // Don't process input if unmounting
217220 if ( ! isMounted . current ) return ;
218221
219- // Handle Ctrl+C to force exit
220- if ( key . ctrl && input === "c" ) {
221- exitAlternateScreenBuffer ( ) ; // Exit alternate screen
222- process . exit ( 130 ) ;
223- }
224-
225222 const pageResourcesCount = currentResources . length ;
226223
227224 // Skip input handling when in search mode
Original file line number Diff line number Diff line change 1+ /**
2+ * Hook to handle Ctrl+C (SIGINT) consistently across all screens
3+ * Exits the program with proper cleanup of alternate screen buffer
4+ */
5+ import { useInput } from "ink" ;
6+ import { exitAlternateScreenBuffer } from "../utils/screen.js" ;
7+
8+ export function useExitOnCtrlC ( ) : void {
9+ useInput ( ( input , key ) => {
10+ if ( key . ctrl && input === "c" ) {
11+ exitAlternateScreenBuffer ( ) ;
12+ process . exit ( 130 ) ; // Standard exit code for SIGINT
13+ }
14+ } ) ;
15+ }
16+
Original file line number Diff line number Diff line change @@ -19,6 +19,7 @@ import { ActionsPopup } from "../components/ActionsPopup.js";
1919import { getDevboxUrl } from "../utils/url.js" ;
2020import { useViewportHeight } from "../hooks/useViewportHeight.js" ;
2121import { colors , sanitizeWidth } from "../utils/theme.js" ;
22+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js" ;
2223
2324export function DevboxListScreen ( ) {
2425 // Get state from store
@@ -393,12 +394,11 @@ export function DevboxListScreen() {
393394 } ,
394395 ] ;
395396
397+ // Handle Ctrl+C to exit
398+ useExitOnCtrlC ( ) ;
399+
396400 // Input handling
397401 useInput ( ( input , key ) => {
398- if ( key . ctrl && input === "c" ) {
399- process . exit ( 130 ) ;
400- }
401-
402402 const pageDevboxes = devboxes . length ;
403403
404404 // Search mode
Original file line number Diff line number Diff line change @@ -13,10 +13,14 @@ import {
1313import { Breadcrumb } from "../components/Breadcrumb.js" ;
1414import { colors } from "../utils/theme.js" ;
1515import figures from "figures" ;
16+ import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js" ;
1617
1718export function SSHSessionScreen ( ) {
1819 const { params, navigate } = useNavigation ( ) ;
1920
21+ // Handle Ctrl+C to exit (before SSH connects or on error)
22+ useExitOnCtrlC ( ) ;
23+
2024 // Extract SSH config from params
2125 const keyPath = params . keyPath ;
2226 const proxyCommand = params . proxyCommand ;
You can’t perform that action at this time.
0 commit comments