@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
JavaScript
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