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

160 lines 8.68 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Box, Text, useInput } from 'ink'; import SelectInput from 'ink-select-input'; import React, { useEffect, useState } from 'react'; import { defaultTheme, getThemeColors } from '../config/themes.js'; import { TIMEOUT_VSCODE_EXTENSION_SKIP_MS } from '../constants.js'; import { getExtensionStatus, installExtension, isVSCodeCliAvailable, } from '../vscode/extension-installer.js'; var InstallOption; (function (InstallOption) { InstallOption["Yes"] = "yes"; InstallOption["No"] = "no"; InstallOption["Select"] = "select"; })(InstallOption || (InstallOption = {})); /** * Ink component that prompts the user to install the VS Code extension * when running with --vscode flag and the extension isn't installed */ export function VSCodeExtensionPrompt({ onComplete, onSkip, }) { const [state, setState] = useState('checking'); const [statuses, setStatuses] = useState([]); const [message, setMessage] = useState(''); const [selectedClis, setSelectedClis] = useState([]); const [isSelecting, setIsSelecting] = useState(false); const colors = getThemeColors(defaultTheme); // Check status on mount useEffect(() => { async function check() { const available = await isVSCodeCliAvailable(); if (!available) { setState('no-cli'); return; } const currentStatuses = await getExtensionStatus(); setStatuses(currentStatuses); const missing = currentStatuses.filter(s => !s.extensionInstalled); if (missing.length === 0) { onComplete(); } else { setSelectedClis(missing.map(s => s.cli)); setState('prompt'); } } if (state === 'checking') { void check(); } }, [state, onComplete]); const handleInstall = React.useCallback(async (clis) => { setState('installing'); const result = await installExtension(clis); setMessage(result.message); if (result.success) { setState('success'); } else { setState('error'); // Auto-continue after showing error setTimeout(onSkip, TIMEOUT_VSCODE_EXTENSION_SKIP_MS); } }, [onSkip]); // Handle Enter key press in success state useInput((_input, key) => { if (state === 'success' && key.return) { onComplete(); } }, { isActive: state === 'success' }); // Handle no-cli case - auto-skip after showing message useEffect(() => { if (state === 'no-cli') { const timer = setTimeout(onSkip, TIMEOUT_VSCODE_EXTENSION_SKIP_MS); return () => clearTimeout(timer); } }, [state, onSkip]); const availableMissing = statuses.filter(s => !s.extensionInstalled); const items = [ { label: availableMissing.length > 1 ? `Yes, install to all (${availableMissing.map(s => s.cli).join(', ')})` : `Yes, install to ${availableMissing[0]?.cli}`, value: InstallOption.Yes, }, ...(availableMissing.length > 1 ? [ { label: 'Choose editors...', value: InstallOption.Select, }, ] : []), { label: 'No, skip for now', value: InstallOption.No, }, ]; const handleSelect = (item) => { if (item.value === InstallOption.Yes) { void handleInstall(availableMissing.map(s => s.cli)); } else if (item.value === InstallOption.Select) { setIsSelecting(true); } else { onSkip(); } }; const handleCliToggle = (cli) => { setSelectedClis(prev => prev.includes(cli) ? prev.filter(c => c !== cli) : [...prev, cli]); }; if (state === 'checking') { return (_jsx(Box, { flexDirection: "column", paddingY: 1, children: _jsx(Text, { color: colors.primary, children: "Checking VS Code extension..." }) })); } if (state === 'no-cli') { return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { color: colors.warning, children: "No supported VS Code flavor (Code, Cursor, VSCodium, Windsurf, Trae) found in PATH." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, children: "To enable VS Code integration:" }) }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.secondary, children: "1. Open VS Code or your preferred editor" }), _jsx(Text, { color: colors.secondary, children: "2. Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows/Linux)" }), _jsx(Text, { color: colors.secondary, children: "3. Search for \"Shell Command: Install 'code' command in PATH\"" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.secondary, children: "Continuing without VS Code integration..." }) })] })); } if (state === 'prompt') { if (isSelecting) { const selectionItems = availableMissing.map(s => ({ label: `${selectedClis.includes(s.cli) ? '[x]' : '[ ]'} ${s.cli}`, value: s.cli, })); return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { color: colors.primary, bold: true, children: "Select Editors" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [ ...selectionItems, { label: '--- Confirm ---', value: 'confirm' }, { label: '--- Back ---', value: 'back' }, ], onSelect: item => { if (item.value === 'confirm') { if (selectedClis.length > 0) { void handleInstall(selectedClis); } } else if (item.value === 'back') { setIsSelecting(false); } else { handleCliToggle(item.value); } } }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Space/Enter to toggle, select Confirm to proceed" }) })] })); } return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { color: colors.primary, bold: true, children: "VS Code Extension" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, children: "The VS Code extension enables live diff previews when Nanocoder modifies files." }) }), statuses.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: "Detected editors:" }), statuses.map(s => (_jsxs(Text, { color: s.extensionInstalled ? 'gray' : 'white', children: [s.extensionInstalled ? ' ✓' : ' !', " ", s.cli, ' ', s.extensionInstalled ? '(Installed)' : '(Missing)'] }, s.cli)))] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, children: "Install the extension now?" }) }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: items, onSelect: handleSelect }) })] })); } if (state === 'installing') { return (_jsx(Box, { flexDirection: "column", paddingY: 1, children: _jsx(Text, { color: colors.primary, children: "Installing VS Code extension..." }) })); } if (state === 'success') { return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsxs(Text, { color: colors.success, children: ["\u2713 ", message] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.secondary, children: "Press Enter to continue..." }) })] })); } if (state === 'error') { return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsxs(Text, { color: colors.error, children: ["\u2717 ", message] }), _jsx(Text, { color: colors.secondary, children: "Continuing without VS Code integration..." })] })); } return null; } /** * Check if we should show the extension install prompt * Returns true if --vscode flag is present * The component itself will check if it's already installed */ export function shouldPromptExtensionInstall() { return process.argv.includes('--vscode'); } //# sourceMappingURL=vscode-extension-prompt.js.map