From 569466e446097aca3ec56e661aff7b6b6ee54289 Mon Sep 17 00:00:00 2001 From: abhigyanpatwari Date: Sun, 11 Jan 2026 19:30:11 +0530 Subject: [PATCH] Agent UI improved. Prompt injected with repo directory structure, hotspots and enforced grounding --- src/components/RightPanel.tsx | 407 ++++++++++++++++---------------- src/core/llm/agent.ts | 94 +++++--- src/core/llm/context-builder.ts | 344 +++++++++++++++++++++++++++ src/core/llm/index.ts | 12 +- src/workers/ingestion.worker.ts | 23 +- 5 files changed, 639 insertions(+), 241 deletions(-) create mode 100644 src/core/llm/context-builder.ts diff --git a/src/components/RightPanel.tsx b/src/components/RightPanel.tsx index 9681385a4..400d47d1a 100644 --- a/src/components/RightPanel.tsx +++ b/src/components/RightPanel.tsx @@ -1,7 +1,7 @@ import { useState, useRef, useEffect, useCallback } from 'react'; -import { +import { Send, Sparkles, User, - PanelRightClose, Loader2, Settings, AlertTriangle + PanelRightClose, Loader2, Settings, AlertTriangle } from 'lucide-react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; @@ -29,7 +29,7 @@ const customTheme = { }; export const RightPanel = () => { - const { + const { isRightPanelOpen, setRightPanelOpen, fileContents, @@ -46,7 +46,7 @@ export const RightPanel = () => { sendChatMessage, clearChat, } = useAppState(); - + const [chatInput, setChatInput] = useState(''); const textareaRef = useRef(null); @@ -177,12 +177,12 @@ export const RightPanel = () => { return null; }, [extractTextFromChildren, isLikelyFileRefHref]); - + // Auto-resize textarea as user types const adjustTextareaHeight = useCallback(() => { const textarea = textareaRef.current; if (!textarea) return; - + // Reset height to get accurate scrollHeight textarea.style.height = 'auto'; // Set to scrollHeight, capped at max @@ -192,7 +192,7 @@ export const RightPanel = () => { // Show scrollbar if content exceeds max textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden'; }, []); - + // Adjust height when input changes useEffect(() => { adjustTextareaHeight(); @@ -235,7 +235,7 @@ export const RightPanel = () => { Nexus AI • Ask about the codebase - + {/* Close button */} - ))} - + + {/* Messages */} +
+ {chatMessages.length === 0 ? ( +
+
+ 🧠
- ) : ( -
- {chatMessages.map((message) => ( -
+ Ask me anything + +

+ I can help you understand the architecture, find functions, or explain connections. +

+
+ {chatSuggestions.map((suggestion) => ( + + ))} +
+
+ ) : ( +
+ {chatMessages.map((message) => ( +
+ {/* User message - compact label style */} + {message.role === 'user' && ( +
+
+ + You +
+
+ {message.content} +
-
- {message.role === 'assistant' ? ( - // Render steps in order (reasoning, tool calls, content interleaved) - message.steps && message.steps.length > 0 ? ( + )} + + {/* Assistant message - copilot style */} + {message.role === 'assistant' && ( +
+
+ + Nexus AI + {isChatLoading && message === chatMessages[chatMessages.length - 1] && ( + + )} +
+
+ {/* Render steps in order (reasoning, tool calls, content interleaved) */} + {message.steps && message.steps.length > 0 ? (
{message.steps.map((step) => (
{step.type === 'reasoning' && step.content && ( -
+
{ @@ -439,100 +431,104 @@ export const RightPanel = () => {
)} {step.type === 'tool_call' && step.toolCall && ( - +
+ +
)} {step.type === 'content' && step.content && ( - { - if (href && href.startsWith('code-ref:')) { - const inner = decodeURIComponent(href.slice('code-ref:'.length)); - const label = formatRefChipLabel(inner); +
+ { + if (href && href.startsWith('code-ref:')) { + const inner = decodeURIComponent(href.slice('code-ref:'.length)); + const label = formatRefChipLabel(inner); + return ( + { + e.preventDefault(); + handleGroundingClick(inner); + }} + className="inline-flex items-center px-2 py-0.5 rounded-md border border-cyan-300/55 bg-cyan-400/10 !text-cyan-200 visited:!text-cyan-200 font-mono text-[12px] !no-underline hover:!no-underline hover:bg-cyan-400/15 hover:border-cyan-200/70 transition-colors" + title={`Open in Code panel • ${inner}`} + {...props} + > + {label || children} + + ); + } + const internalRef = getInternalRefFromLink(href, children); + if (internalRef) { + const label = formatRefChipLabel(internalRef); + return ( + { + e.preventDefault(); + handleGroundingClick(internalRef); + }} + className="inline-flex items-center px-2 py-0.5 rounded-md border border-cyan-300/55 bg-cyan-400/10 !text-cyan-200 visited:!text-cyan-200 font-mono text-[12px] !no-underline hover:!no-underline hover:bg-cyan-400/15 hover:border-cyan-200/70 transition-colors" + title={`Open in Code panel • ${internalRef}`} + {...props} + > + {label || children} + + ); + } return ( { - e.preventDefault(); - handleGroundingClick(inner); - }} - className="inline-flex items-center px-2 py-0.5 rounded-md border border-cyan-300/55 bg-cyan-400/10 !text-cyan-200 visited:!text-cyan-200 font-mono text-[12px] !no-underline hover:!no-underline hover:bg-cyan-400/15 hover:border-cyan-200/70 transition-colors" - title={`Open in Code panel • ${inner}`} + className="text-accent underline underline-offset-2 hover:text-purple-300" + target="_blank" + rel="noopener noreferrer" {...props} > - {label || children} + {children} ); - } - const internalRef = getInternalRefFromLink(href, children); - if (internalRef) { - const label = formatRefChipLabel(internalRef); + }, + code: ({ className, children, ...props }) => { + const match = /language-(\w+)/.exec(className || ''); + const isInline = !className && !match; + const codeContent = String(children).replace(/\n$/, ''); + + if (isInline) { + return {children}; + } + + const language = match ? match[1] : 'text'; return ( - { - e.preventDefault(); - handleGroundingClick(internalRef); + - {label || children} - + {codeContent} + ); - } - return ( - - {children} - - ); - }, - code: ({ className, children, ...props }) => { - const match = /language-(\w+)/.exec(className || ''); - const isInline = !className && !match; - const codeContent = String(children).replace(/\n$/, ''); - - if (isInline) { - return {children}; - } - - const language = match ? match[1] : 'text'; - return ( - - {codeContent} - - ); - }, - pre: ({ children }) => <>{children}, - }} - > - {formatMarkdownForDisplay(step.content)} - + }, + pre: ({ children }) => <>{children}, + }} + > + {formatMarkdownForDisplay(step.content)} + +
)}
))}
) : ( // Fallback: render content + toolCalls separately (old format) - <> +
{ @@ -588,11 +584,11 @@ export const RightPanel = () => { const match = /language-(\w+)/.exec(className || ''); const isInline = !className && !match; const codeContent = String(children).replace(/\n$/, ''); - + if (isInline) { return {children}; } - + const language = match ? match[1] : 'text'; return ( { ))}
)} - - ) - ) : ( - message.content - )} +
+ )} +
-
- ))} -
- )} -
+ )} +
+ ))} + - {/* Input */} -
-
-