import { Box, Text, useInput } from 'ink'
import * as React from 'react'
import { useState, useCallback } from 'react'
import figures from 'figures'
import { getTheme } from '../utils/theme'
import { getGlobalConfig, ModelPointerType } from '../utils/config.js'
import { getModelManager } from '../utils/model'
import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
import { ModelSelector } from './ModelSelector'
type Props = {
onClose: () => void
}
export function ModelListManager({ onClose }: Props): React.ReactNode {
const config = getGlobalConfig()
const theme = getTheme()
const [selectedIndex, setSelectedIndex] = useState(0)
const [showModelSelector, setShowModelSelector] = useState(false)
const [isDeleteMode, setIsDeleteMode] = useState(false)
const [refreshKey, setRefreshKey] = useState(0)
const exitState = useExitOnCtrlCD(onClose)
const modelManager = getModelManager()
const availableModels = modelManager.getAvailableModels()
// Create menu items: existing models + "Add New Model"
const menuItems = React.useMemo(() => {
const modelItems = availableModels.map(model => ({
id: model.modelName,
name: model.name,
provider: model.provider,
usedBy: getModelUsage(model.modelName),
type: 'model' as const,
}))
return [
...modelItems,
{
id: 'add-new',
name: '+ Add New Model',
provider: '',
usedBy: [],
type: 'action' as const,
},
]
}, [availableModels, config.modelPointers, refreshKey])
// Check which pointers are using this model
function getModelUsage(modelName: string): ModelPointerType[] {
const usage: ModelPointerType[] = []
const pointers: ModelPointerType[] = ['main', 'task', 'reasoning', 'quick']
pointers.forEach(pointer => {
if (config.modelPointers?.[pointer] === modelName) {
usage.push(pointer)
}
})
return usage
}
const handleDeleteModel = (modelName: string) => {
// Remove the model
modelManager.removeModel(modelName)
// The removeModel function should already clear the pointers,
// but let's ensure UI refreshes
setRefreshKey(prev => prev + 1)
setIsDeleteMode(false)
}
const handleAddNewModel = () => {
setShowModelSelector(true)
}
const handleModelConfigurationComplete = () => {
setShowModelSelector(false)
setRefreshKey(prev => prev + 1)
}
// Handle keyboard input
const handleInput = useCallback(
(input: string, key: any) => {
if (key.escape) {
if (isDeleteMode) {
setIsDeleteMode(false)
} else {
onClose()
}
} else if (input === 'd' && !isDeleteMode && availableModels.length > 1) {
setIsDeleteMode(true)
} else if (key.upArrow) {
setSelectedIndex(prev => Math.max(0, prev - 1))
} else if (key.downArrow) {
setSelectedIndex(prev => Math.min(menuItems.length - 1, prev + 1))
} else if (key.return || input === ' ') {
const item = menuItems[selectedIndex]
if (isDeleteMode && item.type === 'model') {
// Prevent deleting the last model
if (availableModels.length <= 1) {
setIsDeleteMode(false) // Exit delete mode
return
}
handleDeleteModel(item.id)
} else if (item.type === 'action') {
handleAddNewModel()
}
// Note: Remove any pointer switching functionality here
}
},
[selectedIndex, menuItems, onClose, isDeleteMode, availableModels.length],
)
useInput(handleInput)
// If showing ModelSelector, render it directly
if (showModelSelector) {
return (
)
}
// Main model list screen
return (
Manage Model List{isDeleteMode ? ' - DELETE MODE' : ''}
{exitState.pending
? ` (press ${exitState.keyName} again to exit)`
: ''}
{isDeleteMode
? availableModels.length <= 1
? 'Cannot delete the last model, Esc to cancel'
: 'Press Enter/Space to DELETE selected model, Esc to cancel'
: 'Navigate: ↑↓ | Select: Enter | Delete: d | Exit: Esc'}
{menuItems.map((item, i) => {
const isSelected = i === selectedIndex
return (
{isSelected ? figures.pointer : ' '} {item.name}
{item.type === 'model' && (
<>
({item.provider})
{item.usedBy.length > 0 && (
[Active: {item.usedBy.join(', ')}]
)}
{item.usedBy.length === 0 && (
[Available]
)}
>
)}
{item.type === 'action' && (
{isSelected ? '[Press Enter to add new model]' : ''}
)}
{isSelected && item.type === 'action' && (
Configure a new model and add it to your library
)}
)
})}
{isDeleteMode
? availableModels.length <= 1
? 'Cannot delete the last model - press Esc to cancel'
: 'DELETE MODE: Press Enter/Space to delete model, Esc to cancel'
: availableModels.length <= 1
? 'Use ↑/↓ to navigate, Enter to add new, Esc to exit (cannot delete last model)'
: 'Use ↑/↓ to navigate, d to delete model, Enter to add new, Esc to exit'}
)
}