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

63 lines 3.55 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Box, Text } from 'ink'; import { memo } from 'react'; import { useTerminalWidth } from '../hooks/useTerminalWidth.js'; import { useTheme } from '../hooks/useTheme.js'; import { wrapWithTrimmedContinuations } from '../utils/text-wrapping.js'; import { calculateTokens } from '../utils/token-calculator.js'; // Strip VS Code context blocks from display (code is still sent to LLM) function stripVSCodeContext(message) { return message.replace(/<!--vscode-context-->[\s\S]*?<!--\/vscode-context-->/g, ''); } // Parse a line and return segments with file placeholders highlighted function parseLineWithPlaceholders(line) { const segments = []; const filePattern = /\[@[^\]]+\]/g; let lastIndex = 0; let match; while ((match = filePattern.exec(line)) !== null) { // Add text before the placeholder if (match.index > lastIndex) { segments.push({ text: line.slice(lastIndex, match.index), isPlaceholder: false, }); } // Add the placeholder segments.push({ text: match[0], isPlaceholder: true, }); lastIndex = match.index + match[0].length; } // Add remaining text if (lastIndex < line.length) { segments.push({ text: line.slice(lastIndex), isPlaceholder: false, }); } return segments; } export default memo(function UserMessage({ message, tokenContent, }) { const { colors } = useTheme(); const boxWidth = useTerminalWidth(); const tokens = calculateTokens(tokenContent ?? message); // Inner text width: outer width minus left border (1) and padding (1 each side) const textWidth = boxWidth - 3; // Strip VS Code context blocks and pre-wrap to avoid Ink's trim:false // leaving leading spaces on wrapped lines const displayMessage = wrapWithTrimmedContinuations(stripVSCodeContext(message), textWidth); const lines = displayMessage.split('\n'); return (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.primary, bold: true, children: "You:" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, backgroundColor: colors.base, width: boxWidth, padding: 1, borderStyle: "bold", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderLeftColor: colors.primary, children: _jsx(Box, { flexDirection: "column", children: lines.map((line, lineIndex) => { // Skip empty lines - they create paragraph spacing via marginBottom if (line.trim() === '') { return null; } const segments = parseLineWithPlaceholders(line); const isEndOfParagraph = lineIndex + 1 < lines.length && lines[lineIndex + 1].trim() === ''; return (_jsx(Box, { marginBottom: isEndOfParagraph ? 1 : 0, children: _jsx(Text, { children: segments.map((segment, segIndex) => (_jsx(Text, { color: segment.isPlaceholder ? colors.info : colors.text, bold: segment.isPlaceholder, children: segment.text }, segIndex))) }) }, lineIndex)); }) }) }), _jsx(Box, { marginBottom: 2, children: _jsxs(Text, { color: colors.secondary, dimColor: true, children: ["~", tokens.toLocaleString(), " tokens"] }) })] })); }); //# sourceMappingURL=user-message.js.map