lots of ux

This commit is contained in:
Mathew Pareles 2025-04-21 05:18:24 -07:00
parent d67cfb5b33
commit c52a754076
3 changed files with 49 additions and 16 deletions

View file

@ -1422,7 +1422,7 @@ const ToolRequestAcceptRejectButtons = ({ toolName }: { toolName: ToolName }) =>
<ToolApprovalTypeSwitch size='xs' approvalType={approvalType} desc='Auto-approve' />
</div> : null
return <div className="flex gap-2 mx-4 items-center">
return <div className="flex gap-2 items-center">
{approveButton}
{cancelButton}
{approvalToggle}
@ -2714,15 +2714,15 @@ export const SidebarChat = () => {
const sidebarRef = useRef<HTMLDivElement>(null)
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
const onSubmit = useCallback(async () => {
const onSubmit = useCallback(async (_forceSubmit?: string) => {
if (isDisabled) return
if (isDisabled && !_forceSubmit) return
if (isRunning) return
const threadId = chatThreadsService.state.currentThreadId
// send message to LLM
const userMessage = textAreaRef.current?.value ?? ''
const userMessage = _forceSubmit || textAreaRef.current?.value || ''
try {
await chatThreadsService.addUserMessageAndStreamResponse({ userMessage, threadId })
@ -2884,6 +2884,26 @@ export const SidebarChat = () => {
const isLandingPage = previousMessages.length === 0
const initiallySuggestedPromptsHTML = <div className='flex flex-col gap-2 w-full text-nowrap text-void-fg-3 select-none'>
{[
'Summarize my codebase',
'How do types work in Rust?',
'Create a .voidrules file for me'
].map((text, index) => (
<div
key={index}
className='py-1 px-2 rounded text-sm bg-zinc-700/5 hover:bg-zinc-700/10 dark:bg-zinc-300/5 dark:hover:bg-zinc-300/10 cursor-pointer opacity-80 hover:opacity-100'
onClick={() => onSubmit(text)}
>
{text}
</div>
))}
</div>
console.log('!!!', Object.keys(chatThreadsState.allThreads).length)
const threadPageInput = <div key={'input' + chatThreadsState.currentThreadId}>
<div className='px-4'>
<CommandBarInChat />
@ -2907,11 +2927,16 @@ export const SidebarChat = () => {
{landingPageInput}
</ErrorBoundary>
{Object.values(chatThreadsState.allThreads).length > 0 && // show if there are threads
{Object.keys(chatThreadsState.allThreads).length > 1 ? // show if there are threads
<ErrorBoundary>
<div className='pt-8 mb-2 text-void-fg-1 text-root'>Previous Threads</div>
<PastThreadsList />
</ErrorBoundary>
:
<ErrorBoundary>
<div className='pt-8 mb-2 text-void-fg-1 text-root'>Suggestions</div>
{initiallySuggestedPromptsHTML}
</ErrorBoundary>
}
</div>

View file

@ -176,8 +176,8 @@ export const PastThreadsList = ({ className = '' }: { className?: string }) => {
return (
<div className={`flex flex-col mb-2 gap-2 w-full text-nowrap text-void-fg-3 select-none relative ${className}`}>
{displayThreads.length === 0
? <></> // No chats yet... Suggestion: Tell me about my codebase Suggestion: Create a new .voidrules file in the root of my repo
{displayThreads.length === 0 // this should never happen
? <></>
: displayThreads.map((threadId, i) => {
const pastThread = allThreads[threadId];
if (!pastThread) {
@ -199,7 +199,7 @@ export const PastThreadsList = ({ className = '' }: { className?: string }) => {
{hasMoreThreads && !showAll && (
<div
className="text-void-fg-3 opacity-60 hover:opacity-100 hover:brightness-115 cursor-pointer p-1 text-xs"
className="text-void-fg-3 opacity-80 hover:opacity-100 hover:brightness-115 cursor-pointer p-1 text-xs"
onClick={() => setShowAll(true)}
>
Show {sortedThreadIds.length - numInitialThreads} more...
@ -207,7 +207,7 @@ export const PastThreadsList = ({ className = '' }: { className?: string }) => {
)}
{hasMoreThreads && showAll && (
<div
className="text-void-fg-3 opacity-60 hover:opacity-100 hover:brightness-115 cursor-pointer p-1 text-xs"
className="text-void-fg-3 opacity-80 hover:opacity-100 hover:brightness-115 cursor-pointer p-1 text-xs"
onClick={() => setShowAll(false)}
>
Show less

View file

@ -131,16 +131,25 @@ const FadeIn = ({ children, className, delayMs = 0, durationMs, ...props }: { ch
// prev/next
const NextButton = ({ onClick, ...props }: { onClick: () => void } & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
// Create a new props object without the disabled attribute
const { disabled, ...buttonProps } = props;
return (
<button
onClick={onClick}
className="px-6 py-2 bg-zinc-100 enabled:hover:bg-zinc-100 disabled:bg-zinc-100/40 disabled:cursor-not-allowed rounded text-black duration-600 transition-all"
{...props.disabled && {
onClick={disabled ? undefined : onClick}
onDoubleClick={onClick}
className={`px-6 py-2 bg-zinc-100 ${disabled
? 'bg-zinc-100/40 cursor-not-allowed'
: 'hover:bg-zinc-100'
} rounded text-black duration-600 transition-all
`}
{...disabled && {
'data-tooltip-id': 'void-tooltip',
'data-tooltip-content': 'Please enter all required fields or choose another provider',
'data-tooltip-place': 'top',
"data-tooltip-content": 'Please enter all required fields or choose another provider', // (double-click to proceed anyway, can come back in Settings)
"data-tooltip-place": 'top',
}}
{...props}
{...buttonProps}
>
Next
</button>
@ -481,7 +490,6 @@ const VoidOnboardingContent = () => {
const [selectedPrivateProvider, setSelectedPrivateProvider] = useState<ProviderName>('ollama');
const [selectedAffordableProvider, setSelectedAffordableProvider] = useState<ProviderName>('gemini');
const [selectedAllProvider, setSelectedAllProvider] = useState<ProviderName>('anthropic');
const [didDoubleClickSkip, setDidDoubleClickSkip] = useState(false)
// Helper function to get the current selected provider based on active tab
const getSelectedProvider = (): ProviderName => {