feat: add fullscreen TUI mode with Ink React components

This commit is contained in:
William Valentin
2026-02-05 00:41:17 -08:00
parent 53a8bd97eb
commit f671ea5ab5
4 changed files with 89 additions and 26 deletions
+27
View File
@@ -0,0 +1,27 @@
import React from 'react';
import { render } from 'ink';
import { App } from './components/index.js';
import type { ManagedSession } from '../../session/index.js';
import type { ModelClient } from '../../models/types.js';
export interface FullscreenTuiConfig {
session: ManagedSession;
modelClient: ModelClient;
systemPrompt: string;
model: string;
onExit?: () => void;
}
export async function startFullscreenTui(config: FullscreenTuiConfig): Promise<void> {
const { waitUntilExit } = render(
React.createElement(App, {
session: config.session,
modelClient: config.modelClient,
systemPrompt: config.systemPrompt,
model: config.model,
onExit: config.onExit,
})
);
await waitUntilExit();
}
+7
View File
@@ -5,3 +5,10 @@ export {
type TuiCommand,
type MinimalTuiConfig,
} from './minimal.js';
export {
startFullscreenTui,
type FullscreenTuiConfig,
} from './fullscreen.js';
export { App, StatusBar, MessageList, InputBar } from './components/index.js';
+54 -26
View File
@@ -1,7 +1,7 @@
import { loadConfig } from './config/index.js';
import { SessionStore, SessionManager } from './session/index.js';
import { AnthropicClient, OpenAIClient, OllamaClient, ModelRouter } from './models/index.js';
import { MinimalTui } from './frontends/tui/index.js';
import { MinimalTui, startFullscreenTui } from './frontends/tui/index.js';
import type { Config } from './config/index.js';
import { resolve } from 'path';
import { homedir } from 'os';
@@ -61,6 +61,9 @@ function createModelRouter(config: Config): ModelRouter {
}
async function main() {
const args = process.argv.slice(2);
const fullscreenMode = args.includes('--fullscreen') || args.includes('-f');
console.log('Flynn TUI starting...');
if (!existsSync(CONFIG_PATH)) {
@@ -83,36 +86,61 @@ async function main() {
// Get TUI session
const session = sessionManager.getSession('tui', 'local');
// Create and start minimal TUI
const tui = new MinimalTui({
session,
modelClient: modelRouter,
systemPrompt: SYSTEM_PROMPT,
onTransfer: (target) => {
if (target === 'telegram') {
const telegramUserId = String(config.telegram.allowed_chat_ids[0]);
sessionManager.transferSession('tui', 'local', 'telegram', telegramUserId);
console.log(`Session transferred to Telegram (${telegramUserId})\n`);
} else {
console.log(`Unknown transfer target: ${target}\n`);
}
},
onFullscreen: () => {
console.log('Fullscreen mode not yet implemented.\n');
},
});
// Handle shutdown
process.on('SIGINT', () => {
tui.stop();
const cleanup = () => {
sessionStore.close();
};
process.on('SIGINT', () => {
cleanup();
process.exit(0);
});
await tui.start();
if (fullscreenMode) {
// Start fullscreen Ink UI
await startFullscreenTui({
session,
modelClient: modelRouter,
systemPrompt: SYSTEM_PROMPT,
model: config.models.default.model,
onExit: cleanup,
});
} else {
// Start minimal readline UI
const tui = new MinimalTui({
session,
modelClient: modelRouter,
systemPrompt: SYSTEM_PROMPT,
onTransfer: (target) => {
if (target === 'telegram') {
const telegramUserId = String(config.telegram.allowed_chat_ids[0]);
sessionManager.transferSession('tui', 'local', 'telegram', telegramUserId);
console.log(`Session transferred to Telegram (${telegramUserId})\n`);
} else {
console.log(`Unknown transfer target: ${target}\n`);
}
},
onFullscreen: async () => {
tui.stop();
console.clear();
await startFullscreenTui({
session,
modelClient: modelRouter,
systemPrompt: SYSTEM_PROMPT,
model: config.models.default.model,
onExit: () => {
// Return to minimal mode would require re-init
// For now, just exit
cleanup();
process.exit(0);
},
});
},
});
// Cleanup
sessionStore.close();
await tui.start();
}
cleanup();
}
main().catch((error) => {