InvalidConfigDialog.tsx
1 import React from 'react' 2 import { Box, Newline, Text, useInput } from 'ink' 3 import { getTheme } from '../utils/theme.js' 4 import { Select } from '@inkjs/ui' 5 import { render } from 'ink' 6 import { writeFileSync } from 'fs' 7 import { ConfigParseError } from '../utils/errors.js' 8 import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD.js' 9 interface InvalidConfigHandlerProps { 10 error: ConfigParseError 11 } 12 13 interface InvalidConfigDialogProps { 14 filePath: string 15 errorDescription: string 16 onExit: () => void 17 onReset: () => void 18 } 19 20 /** 21 * Dialog shown when the Claude config file contains invalid JSON 22 */ 23 function InvalidConfigDialog({ 24 filePath, 25 errorDescription, 26 onExit, 27 onReset, 28 }: InvalidConfigDialogProps): React.ReactNode { 29 const theme = getTheme() 30 31 // Handle escape key 32 useInput((_, key) => { 33 if (key.escape) { 34 onExit() 35 } 36 }) 37 38 const exitState = useExitOnCtrlCD(() => process.exit(0)) 39 40 // Handler for Select onChange 41 const handleSelect = (value: string) => { 42 if (value === 'exit') { 43 onExit() 44 } else { 45 onReset() 46 } 47 } 48 49 return ( 50 <> 51 <Box 52 flexDirection="column" 53 borderColor={theme.error} 54 borderStyle="round" 55 padding={1} 56 width={70} 57 gap={1} 58 > 59 <Text bold>Configuration Error</Text> 60 61 <Box flexDirection="column" gap={1}> 62 <Text> 63 The configuration file at <Text bold>{filePath}</Text> contains 64 invalid JSON. 65 </Text> 66 <Text>{errorDescription}</Text> 67 </Box> 68 69 <Box flexDirection="column"> 70 <Text bold>Choose an option:</Text> 71 <Select 72 options={[ 73 { label: 'Exit and fix manually', value: 'exit' }, 74 { label: 'Reset with default configuration', value: 'reset' }, 75 ]} 76 onChange={handleSelect} 77 /> 78 </Box> 79 </Box> 80 {exitState.pending ? ( 81 <Text dimColor>Press {exitState.keyName} again to exit</Text> 82 ) : ( 83 <Newline /> 84 )} 85 </> 86 ) 87 } 88 89 export function showInvalidConfigDialog({ 90 error, 91 }: InvalidConfigHandlerProps): Promise<void> { 92 return new Promise(resolve => { 93 render( 94 <InvalidConfigDialog 95 filePath={error.filePath} 96 errorDescription={error.message} 97 onExit={() => { 98 resolve() 99 process.exit(1) 100 }} 101 onReset={() => { 102 writeFileSync( 103 error.filePath, 104 JSON.stringify(error.defaultConfig, null, 2), 105 ) 106 resolve() 107 process.exit(0) 108 }} 109 />, 110 { exitOnCtrlC: false }, 111 ) 112 }) 113 }