UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

289 lines 15.5 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Box, Text, useInput } from 'ink'; import BigText from 'ink-big-text'; import Gradient from 'ink-gradient'; import SelectInput from 'ink-select-input'; import { useMemo, useState } from 'react'; import { TitledBoxWithPreferences } from '../../components/ui/titled-box.js'; import { getNanocoderShape, updateNanocoderShape } from '../../config/preferences.js'; import { themes } from '../../config/themes.js'; import { useResponsiveTerminal } from '../../hooks/useTerminalWidth.js'; import { useTheme } from '../../hooks/useTheme.js'; import { useTitleShape } from '../../hooks/useTitleShape.js'; // Main settings menu function SettingsMainMenu({ onSelect, onCancel, }) { const { colors } = useTheme(); const { boxWidth, isNarrow } = useResponsiveTerminal(); const items = [ { label: 'Theme', value: 'theme', description: 'Change color scheme', }, { label: 'Title Shape', value: 'title-shape', description: 'Customize box title styles', }, { label: 'Nanocoder Shape', value: 'nanocoder-shape', description: 'Change welcome banner font', }, { label: 'Done', value: 'done', description: 'Exit settings', }, ]; useInput((_input, key) => { if (key.escape) { onCancel(); } }); // Narrow terminal: simplified layout (matches Status component pattern) if (isNarrow) { return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: colors.primary, paddingY: 1, paddingX: 2, width: "100%", children: [_jsx(Text, { color: colors.primary, bold: true, children: "Settings" }), _jsx(Text, { color: colors.text, children: " " }), _jsx(SelectInput, { items: items.map(item => ({ label: item.label, value: item.value, })), onSelect: item => { if (item.value === 'done') { onCancel(); } else { onSelect(item.value); } }, indicatorComponent: ({ isSelected }) => (_jsx(Text, { color: isSelected ? colors.primary : colors.text, children: isSelected ? '> ' : ' ' })), itemComponent: ({ isSelected, label }) => (_jsx(Text, { color: isSelected ? colors.primary : colors.text, children: label })) }), _jsx(Box, { marginBottom: 1 }), _jsx(Text, { color: colors.secondary, dimColor: true, children: "Enter/Esc" })] })); } return (_jsxs(TitledBoxWithPreferences, { title: "Settings", width: boxWidth, borderColor: colors.primary, paddingX: 1, paddingY: 1, flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.secondary, children: "Select a setting to configure:" }) }), _jsx(SelectInput, { items: items.map(item => ({ label: `${item.label} - ${item.description}`, value: item.value, })), onSelect: item => { if (item.value === 'done') { onCancel(); } else { onSelect(item.value); } }, indicatorComponent: ({ isSelected }) => (_jsx(Text, { color: isSelected ? colors.primary : colors.text, children: isSelected ? '> ' : ' ' })), itemComponent: ({ isSelected, label }) => (_jsx(Text, { color: isSelected ? colors.primary : colors.text, children: label })) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.secondary, dimColor: true, children: "Enter to select, Esc to exit" }) })] })); } // Theme settings panel function SettingsThemePanel({ onBack, onCancel, }) { const { boxWidth, isNarrow } = useResponsiveTerminal(); const { colors, currentTheme, setCurrentTheme } = useTheme(); const [originalTheme] = useState(currentTheme); useInput((_, key) => { if (key.escape) { setCurrentTheme(originalTheme); onCancel(); } if (key.shift && key.tab) { setCurrentTheme(originalTheme); onBack(); } }); const themeOptions = Object.values(themes).map(theme => ({ label: isNarrow ? theme.displayName + (theme.name === originalTheme ? ' *' : '') : theme.displayName + ' [' + theme.themeType.charAt(0).toUpperCase() + theme.themeType.slice(1) + ']' + (theme.name === originalTheme ? ' (current)' : ''), value: theme.name, })); const initialIndex = useMemo(() => { const index = themeOptions.findIndex(option => option.value === originalTheme); return index >= 0 ? index : 0; }, [originalTheme, themeOptions]); const handleSelect = (item) => { setCurrentTheme(item.value); onBack(); }; const handleHighlight = (item) => { setCurrentTheme(item.value); }; // Narrow terminal: simplified layout if (isNarrow) { return (_jsxs(TitledBoxWithPreferences, { title: "Themes", width: "100%", borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(SelectInput, { items: themeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight }), _jsx(Box, { marginBottom: 1 }), _jsx(Text, { color: colors.secondary, dimColor: true, children: "Enter/Shift+Tab/Esc" })] })); } return (_jsxs(TitledBoxWithPreferences, { title: "Choose your theme", width: boxWidth, borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.secondary, children: "Enter to apply, Shift+Tab to go back, Esc to exit" }) }), _jsx(SelectInput, { items: themeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight })] })); } // Title Shape settings panel function SettingsTitleShapePanel({ onBack, onCancel, }) { const { boxWidth, isNarrow } = useResponsiveTerminal(); const { colors } = useTheme(); const { currentTitleShape, setCurrentTitleShape } = useTitleShape(); const [originalShape] = useState(currentTitleShape); useInput((_, key) => { if (key.escape) { setCurrentTitleShape(originalShape); onCancel(); } if (key.shift && key.tab) { setCurrentTitleShape(originalShape); onBack(); } }); const shapeOptions = isNarrow ? [ { label: 'Pill', value: 'pill' }, { label: 'Rounded', value: 'rounded' }, { label: 'Square', value: 'square' }, { label: 'Double', value: 'double' }, { label: 'Arrow Left', value: 'arrow-left' }, { label: 'Arrow Right', value: 'arrow-right' }, { label: 'Arrow Double', value: 'arrow-double' }, { label: 'Angled Box', value: 'angled-box' }, { label: 'PL Angled', value: 'powerline-angled' }, { label: 'PL Angled Thin', value: 'powerline-angled-thin' }, { label: 'PL Block', value: 'powerline-block' }, { label: 'PL Block Alt', value: 'powerline-block-alt' }, { label: 'PL Curved', value: 'powerline-curved' }, { label: 'PL Curved Thin', value: 'powerline-curved-thin' }, { label: 'PL Flame', value: 'powerline-flame' }, { label: 'PL Flame Thin', value: 'powerline-flame-thin' }, { label: 'PL Graph', value: 'powerline-graph' }, { label: 'PL Ribbon', value: 'powerline-ribbon' }, { label: 'PL Segment', value: 'powerline-segment' }, { label: 'PL Segment Thin', value: 'powerline-segment-thin' }, ] : [ { label: 'Pill :- Demo Title', value: 'pill' }, { label: 'Rounded :- ╭ Demo Title ╮', value: 'rounded' }, { label: 'Square :- ┌ Demo Title ┐', value: 'square' }, { label: 'Double :- ╔ Demo Title ╗', value: 'double' }, { label: 'Arrow Left :- ← Demo Title →', value: 'arrow-left' }, { label: 'Arrow Right :- → Demo Title ←', value: 'arrow-right' }, { label: 'Arrow Double :- « Demo Title »', value: 'arrow-double' }, { label: 'Angled Box :- ╱ Demo Title ╲', value: 'angled-box' }, { label: 'Powerline Angled (Nerd Fonts)', value: 'powerline-angled', }, { label: 'Powerline Angled Thin (Nerd Fonts)', value: 'powerline-angled-thin', }, { label: 'Powerline Block (Nerd Fonts)', value: 'powerline-block', }, { label: 'Powerline Block Alt (Nerd Fonts)', value: 'powerline-block-alt', }, { label: 'Powerline Curved (Nerd Fonts)', value: 'powerline-curved', }, { label: 'Powerline Curved Thin (Nerd Fonts)', value: 'powerline-curved-thin', }, { label: 'Powerline Flame (Nerd Fonts)', value: 'powerline-flame', }, { label: 'Powerline Flame Thin (Nerd Fonts)', value: 'powerline-flame-thin', }, { label: 'Powerline Graph (Nerd Fonts)', value: 'powerline-graph', }, { label: 'Powerline Ribbon (Nerd Fonts)', value: 'powerline-ribbon', }, { label: 'Powerline Segment (Nerd Fonts)', value: 'powerline-segment', }, { label: 'Powerline Segment Thin (Nerd Fonts)', value: 'powerline-segment-thin', }, ]; const initialIndex = useMemo(() => { const index = shapeOptions.findIndex(option => option.value === originalShape); return index >= 0 ? index : 0; }, [originalShape, shapeOptions]); const handleSelect = (item) => { setCurrentTitleShape(item.value); onBack(); }; const handleHighlight = (item) => { setCurrentTitleShape(item.value); }; // Narrow terminal: use TitledBoxWithPreferences to preview shape changes if (isNarrow) { return (_jsxs(TitledBoxWithPreferences, { title: "Title Shapes", width: "100%", borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(SelectInput, { items: shapeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight }), _jsx(Box, { marginBottom: 1 }), _jsx(Text, { color: colors.secondary, dimColor: true, children: "Enter/Shift+Tab/Esc" })] })); } return (_jsxs(TitledBoxWithPreferences, { title: "Choose your title shape", width: boxWidth, borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.secondary, children: "Enter to apply, Shift+Tab to go back, Esc to exit" }) }), _jsx(SelectInput, { items: shapeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight })] })); } // Nanocoder Shape settings panel function SettingsNanocoderShapePanel({ onBack, onCancel, }) { const { boxWidth, isNarrow } = useResponsiveTerminal(); const { colors } = useTheme(); const savedShape = getNanocoderShape(); const initialShape = savedShape ?? 'tiny'; const [originalShape] = useState(initialShape); const [previewShape, setPreviewShape] = useState(initialShape); useInput((_, key) => { if (key.escape) { onCancel(); } if (key.shift && key.tab) { onBack(); } }); const shapeOptions = useMemo(() => [ { label: 'Tiny (default)', value: 'tiny' }, { label: 'Block', value: 'block' }, { label: 'Simple', value: 'simple' }, { label: 'Simple Block', value: 'simpleBlock' }, { label: 'Slick', value: 'slick' }, { label: 'Grid', value: 'grid' }, { label: 'Pallet', value: 'pallet' }, { label: 'Shade', value: 'shade' }, { label: '3D', value: '3d' }, { label: 'Simple 3D', value: 'simple3d' }, { label: 'Chrome', value: 'chrome' }, { label: 'Huge', value: 'huge' }, ], []); const initialIndex = useMemo(() => { const index = shapeOptions.findIndex(option => option.value === originalShape); return index >= 0 ? index : 0; }, [originalShape, shapeOptions]); const handleSelect = (item) => { updateNanocoderShape(item.value); onBack(); }; const handleHighlight = (item) => { setPreviewShape(item.value); }; const displayText = isNarrow ? 'NC' : 'Nanocoder'; // Narrow terminal: simplified layout with BigText outside box if (isNarrow) { return (_jsxs(_Fragment, { children: [_jsx(Gradient, { colors: [colors.primary, colors.tool], children: _jsx(BigText, { text: displayText, font: previewShape }) }), _jsxs(TitledBoxWithPreferences, { title: "Nanocoder Shape", width: "100%", borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(SelectInput, { items: shapeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight }), _jsx(Box, { marginBottom: 1 }), _jsx(Text, { color: colors.secondary, dimColor: true, children: "Enter/Shift+Tab/Esc" })] })] })); } return (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Gradient, { colors: [colors.primary, colors.tool], children: _jsx(BigText, { text: displayText, font: previewShape }) }) }), _jsxs(TitledBoxWithPreferences, { title: "Choose your branding style", width: boxWidth, borderColor: colors.primary, paddingX: 2, paddingY: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.secondary, children: "Enter to apply, Shift+Tab to go back, Esc to exit" }) }), _jsx(SelectInput, { items: shapeOptions, initialIndex: initialIndex, onSelect: handleSelect, onHighlight: handleHighlight })] })] })); } // Main settings selector with step navigation export function SettingsSelector({ onCancel }) { const [step, setStep] = useState('main'); switch (step) { case 'main': return _jsx(SettingsMainMenu, { onSelect: setStep, onCancel: onCancel }); case 'theme': return (_jsx(SettingsThemePanel, { onBack: () => setStep('main'), onCancel: onCancel })); case 'title-shape': return (_jsx(SettingsTitleShapePanel, { onBack: () => setStep('main'), onCancel: onCancel })); case 'nanocoder-shape': return (_jsx(SettingsNanocoderShapePanel, { onBack: () => setStep('main'), onCancel: onCancel })); } } //# sourceMappingURL=settings-selector.js.map