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

54 lines 3.04 kB
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; import { Box, Text } from 'ink'; import { memo, useMemo } from 'react'; import wrapAnsi from 'wrap-ansi'; import { useTerminalWidth } from '../hooks/useTerminalWidth.js'; import { useTheme } from '../hooks/useTheme.js'; import { parseMarkdown } from '../markdown-parser/index.js'; import { calculateTokens } from '../utils/token-calculator.js'; // Ink uses wrap-ansi with trim: false, which preserves the space at word // boundaries as leading whitespace on continuation lines. This function // wraps each original line individually and trims only the artifact spaces // from continuation lines, preserving intentional indentation. function wrapWithTrimmedContinuations(text, width) { if (width <= 0) return text; const originalLines = text.split('\n'); const result = []; for (const line of originalLines) { if (line === '') { result.push(''); continue; } const wrapped = wrapAnsi(line, width, { trim: false, hard: true }); const subLines = wrapped.split('\n'); result.push(subLines[0] ?? ''); for (let i = 1; i < subLines.length; i++) { // Trim the leading space that is a word-wrap artifact. // Handle ANSI escape codes that may precede the space. result.push((subLines[i] ?? '').replace(/^((?:\x1b\[[0-9;]*m)*)\s/, '$1')); } } return result.join('\n'); } export default memo(function AssistantMessage({ message, model, }) { const { colors } = useTheme(); const boxWidth = useTerminalWidth(); const tokens = calculateTokens(message); // Inner text width: outer width minus left border (1) and padding (1 each side) const textWidth = boxWidth - 3; // Render markdown to terminal-formatted text with theme colors // Pre-wrap to avoid Ink's trim:false leaving leading spaces on wrapped lines const renderedMessage = useMemo(() => { try { const parsed = parseMarkdown(message, colors, textWidth).trimEnd(); return wrapWithTrimmedContinuations(parsed, textWidth); } catch { // Fallback to plain text if markdown parsing fails return wrapWithTrimmedContinuations(message.trimEnd(), textWidth); } }, [message, colors, textWidth]); return (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.info, bold: true, children: [model, ":"] }) }), _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.secondary, children: _jsx(Text, { children: renderedMessage }) }), _jsx(Box, { marginBottom: 2, children: _jsxs(Text, { color: colors.secondary, dimColor: true, children: ["~", tokens.toLocaleString(), " tokens"] }) })] })); }); //# sourceMappingURL=assistant-message.js.map