Implement terminal restart functionality and enhance model settings menu in InkApp. Add methods for restarting the terminal and adjusting model settings, improving user interaction and menu navigation. Update state management for better handling of model settings adjustments.

This commit is contained in:
sebseb7
2025-08-13 01:57:11 +00:00
parent 83ac8709b7
commit b49c798fc7
2 changed files with 74 additions and 16 deletions

View File

@@ -118,10 +118,16 @@ class TerminalService extends EventEmitter {
this.ptyProcess.kill();
this.ptyProcess = null;
}
this.started = false;
} catch {
// ignore
}
}
restart() {
try { this.dispose(); } catch {}
try { this.start(); } catch {}
}
}
const terminalService = new TerminalService();

View File

@@ -124,6 +124,8 @@ export default class InkApp extends React.Component {
this.toggleMenu = this.toggleMenu.bind(this);
this.onKeypress = this.onKeypress.bind(this);
this.menuAction = this.menuAction.bind(this);
this.getModelSettingsItems = this.getModelSettingsItems.bind(this);
this.handleModelSettingAdjust = this.handleModelSettingAdjust.bind(this);
}
componentDidMount() {
@@ -230,6 +232,8 @@ export default class InkApp extends React.Component {
const isUp = data.length >= 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x41;
const isDown = data.length >= 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x42;
const isEnter = data.length === 1 && data[0] === 0x0d;
const isLeft = data.length >= 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x44;
const isRight = data.length >= 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x43;
const isCtrlC = data.length === 1 && data[0] === 0x03;
if (!this.state.menuOpen) {
@@ -245,7 +249,32 @@ export default class InkApp extends React.Component {
return;
}
// Menu navigation
// Submenu: Model settings adjustments
if (this.state.menuOpen && this.state.menuMode === 'model') {
const items = this.getModelSettingsItems();
if (isUp) {
this.setState((s) => ({ menuIndex: (s.menuIndex - 1 + items.length) % items.length }));
return;
}
if (isDown) {
this.setState((s) => ({ menuIndex: (s.menuIndex + 1) % items.length }));
return;
}
if (isLeft || isRight) {
const idx = this.state.menuIndex;
const dir = isRight ? 1 : -1;
this.handleModelSettingAdjust(items[idx].key, dir);
return;
}
if (isEnter) {
// Enter exits model submenu back to main menu
this.setState({ menuMode: undefined, menuIndex: 0 });
return;
}
return;
}
// Menu navigation (main menu)
const items = this.getMenuItems();
if (isUp) {
this.setState((s) => ({ menuIndex: (s.menuIndex - 1 + items.length) % items.length }));
@@ -278,12 +307,11 @@ export default class InkApp extends React.Component {
try { terminalService.write('\x03'); } catch {}
break;
case 'Restart Terminal':
try { terminalService.dispose(); } catch {}
try { terminalService.start(); } catch {}
try { terminalService.restart(); } catch {}
break;
case 'Model settings':
// Toggle a sub-menu state
this.setState({ menuMode: 'model' });
this.setState({ menuMode: 'model', menuIndex: 0 });
break;
case 'Exit the app':
try { process.exit(0); } catch {}
@@ -296,6 +324,30 @@ export default class InkApp extends React.Component {
}
}
getModelSettingsItems() {
return [
{ key: 'model', label: 'Model', options: ['gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano'] },
{ key: 'reasoningEffort', label: 'Reasoning effort', options: ['minimal', 'low', 'medium', 'high'] },
{ key: 'outputVerbosity', label: 'Output verbosity', options: ['low', 'medium', 'high'] },
{ key: 'back', label: 'Back to main menu' }
];
}
handleModelSettingAdjust(key, dir) {
if (key === 'back') {
this.setState({ menuMode: undefined, menuIndex: 0 });
return;
}
const items = this.getModelSettingsItems();
const item = items.find((i) => i.key === key);
if (!item || !item.options) return;
const currentValue = this.state[key];
const idx = item.options.indexOf(currentValue);
const nextIdx = ((idx === -1 ? 0 : idx) + dir + item.options.length) % item.options.length;
const nextValue = item.options[nextIdx];
this.setState({ [key]: nextValue });
}
render() {
const { input, logs, terminal, chainOfThought, llmOutput } = this.state;
const totalCols = (process && process.stdout && process.stdout.columns) ? process.stdout.columns : 80;
@@ -337,7 +389,7 @@ export default class InkApp extends React.Component {
<Pane title="Logging" lines={logsView} maxWidth={paneContentWidth} />
</Box>
</Box>
{this.state.menuOpen && (
{this.state.menuOpen && this.state.menuMode !== 'model' && (
<Box borderStyle="round" paddingX={1} paddingY={0} marginTop={1} flexDirection="column">
<Text color="yellow">Main Menu (Up/Down to navigate, Enter to select)</Text>
{menuItems.map((label, i) => (
@@ -345,17 +397,17 @@ export default class InkApp extends React.Component {
{i === selected ? ' ' : ' '}{label}
</Text>
))}
{this.state.menuMode === 'model' && (
<Box flexDirection="column" marginTop={1}>
<Text>Model: {this.state.model}</Text>
<Text>Reasoning effort: {this.state.reasoningEffort}</Text>
<Text>Output verbosity: {this.state.outputVerbosity}</Text>
<Text dimColor>
(Adjustments pending wiring: model list [gpt-5, gpt-5-mini, gpt-5-nano, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano],
reasoning effort [minimal, low, medium, high], output verbosity [low, medium, high])
</Text>
</Box>
)}
</Box>
)}
{this.state.menuOpen && this.state.menuMode === 'model' && (
<Box borderStyle="round" paddingX={1} paddingY={0} marginTop={1} flexDirection="column">
<Text color="yellow">Model Settings (Up/Down select, Left/Right change, Enter back)</Text>
{this.getModelSettingsItems().map((item, i) => (
<Text key={item.key || item.label} color={i === selected ? 'cyan' : undefined}>
{i === selected ? ' ' : ' '}
{item.label}{item.options ? `: ${this.state[item.key]}` : ''}
</Text>
))}
</Box>
)}
<Box marginTop={1}>