+
{
+ if (isDropdown) { setIsOpen(v => !v); }
+ if (onClick) { onClick(); }
+ }}
+ >
+ {isDropdown && ( )}
{title}
- {desc1}
+ {desc1}
{/* right */}
@@ -1865,28 +1869,266 @@ const ChatBubble = ({ chatMessage, isCommitted, messageIdx, isLast, chatIsRunnin
+export const AcceptAllButtonWrapper = ({ text, onClick, className }: { text: string, onClick: () => void, className?: string }) => (
+
+ {text ? {text} : }
+
+)
+
+export const RejectAllButtonWrapper = ({ text, onClick, className }: { text: string, onClick: () => void, className?: string }) => (
+
+ {text ? {text} : }
+
+)
+
+
+
const CommandBarInChat = () => {
- const { state: commandBarState, sortedURIs: sortedCommandBarURIs } = useCommandBarState()
- const [isExpanded, setIsExpanded] = useState(false)
+ const { stateOfURI: commandBarStateOfURI, sortedURIs: sortedCommandBarURIs } = useCommandBarState()
+ const numFilesChanged = sortedCommandBarURIs.length
const accessor = useAccessor()
+ const editCodeService = accessor.get('IEditCodeService')
const commandService = accessor.get('ICommandService')
+ const chatThreadsState = useChatThreadsState()
+ const chatThreadsStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId)
- if (!sortedCommandBarURIs || sortedCommandBarURIs.length === 0) {
- return null
- }
+ const [isFileDetailsOpened, setFileDetailsOpened] = useState(false);
+
+ // close the file details if there are no files
+ useEffect(() => {
+ if (isFileDetailsOpened && numFilesChanged === 0) {
+ setFileDetailsOpened(false)
+ }
+ }, [isFileDetailsOpened, numFilesChanged, setFileDetailsOpened])
+
+
+ const isFinishedMakingThreadChanges = chatThreadsStreamState && !chatThreadsStreamState.isRunning && numFilesChanged !== 0
+
+ // ======== status of agent ========
+ // This icon answers the question "is the LLM doing work on this thread?"
+ // assume it is single threaded for now
+ // green = Running
+ // orange = Requires action
+ // dark = Done
+
+ const threadStatus = (
+ chatThreadsStreamState?.isRunning === 'awaiting_user' ? { title: 'Needs Approval', color: 'orange', } as const
+ : chatThreadsStreamState?.isRunning ? { title: 'Running', color: 'green', } as const
+ : { title: 'Done', color: 'dark', } as const
+ )
+
+
+ const threadStatusHTML =
+
+
+ // ======== info about changes ========
+ // num files changed
+ // acceptall + rejectall
+ // popup info about each change (each with num changes + acceptall + rejectall of their own)
+
+ const numFilesChangedStr = numFilesChanged === 0 ? 'No files with changes'
+ : `${sortedCommandBarURIs.length} file${numFilesChanged === 1 ? '' : 's'} changed`
+
+ const acceptAllButton = (
+
{
+ sortedCommandBarURIs.forEach(uri => {
+ editCodeService.acceptOrRejectAllDiffAreas({
+ uri,
+ removeCtrlKs: true,
+ behavior: "accept",
+ _addToHistory: true,
+ })
+ })
+ }}
+ />
+ )
+
+ const rejectAllButton = (
+ {
+ sortedCommandBarURIs.forEach(uri => {
+ editCodeService.acceptOrRejectAllDiffAreas({
+ uri,
+ removeCtrlKs: true,
+ behavior: "reject",
+ _addToHistory: true,
+ })
+ })
+ }}
+ />
+ )
+
+
+ const acceptRejectAllButtons = isFinishedMakingThreadChanges &&
+ {acceptAllButton}
+ {rejectAllButton}
+
+
+
+ // !select-text cursor-auto
+ const fileDetailsContent =
+ {sortedCommandBarURIs.map((uri, i) => {
+ const basename = getBasename(uri.fsPath)
+
+ const { sortedDiffIds, isStreaming } = commandBarStateOfURI[uri.fsPath] ?? {}
+ const isFinishedMakingFileChanges = !isStreaming
+
+ const numDiffs = sortedDiffIds?.length || 0
+
+ const fileStatus = (isFinishedMakingFileChanges
+ ? { title: 'Done', color: 'dark', } as const
+ : { title: 'Running', color: 'green', } as const
+ )
+
+ const acceptButton =
{ editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "accept", _addToHistory: true, }) }}
+ />
+
+ const rejectButton = { editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "reject", _addToHistory: true, }) }}
+ />
+
+ const fileNameHTML = commandService.executeCommand('vscode.open', uri, { preview: true })}
+ >
+
+ {basename}
+
+
+ const detailsContent = <>
+ {numDiffs} change{numDiffs !== 1 ? 's' : ''}
+ >
+
+ const acceptRejectButtons = isFinishedMakingFileChanges &&
+ {acceptButton}
+ {rejectButton}
+
+
+ const fileStatusHTML =
+
+ return (
+ // name, details
+
+
+ {fileNameHTML}
+ {detailsContent}
+
+
+ {acceptRejectButtons}
+ {fileStatusHTML}
+
+
+ )
+ })}
+
+
+ const fileDetailsButton = (
+ setFileDetailsOpened(!isFileDetailsOpened)}
+ type='button'
+ disabled={numFilesChanged === 0}
+ >
+
+
+ {numFilesChangedStr}
+
+ )
+
+
+ const changesContent = <>
+ {/*
+
{'chatThreadsStreamState' + chatThreadsStreamState}
+
{'isRunning' + chatThreadsStreamState?.isRunning}
+
{'isFinishedWithChanges' + isFinishedMakingThreadChanges}
+
*/}
+ {fileDetailsButton}
+ >
return (
-
- {sortedCommandBarURIs.map((uri, i) => (
- { commandService.executeCommand('vscode.open', uri, { preview: true }) }}
- />
- ))}
-
+ <>
+ {/* file details */}
+
+
+ {fileDetailsContent}
+
+
+ {/* main content */}
+
+
+ {changesContent}
+
+
+ {acceptRejectAllButtons}
+ {threadStatusHTML}
+
+
+ >
)
}
@@ -2079,33 +2321,40 @@ export const SidebarChat = () => {
}
}, [onSubmit, onAbort, isRunning])
- const inputForm =
-
{ textAreaRef.current?.focus() }}
+ const inputForm =
+
+ {previousMessages.length > 0 &&
+
+ }
+
+
- { chatThreadsService.setCurrentlyFocusedMessageIdx(undefined) }}
- ref={textAreaRef}
- fnsRef={textAreaFnsRef}
- multiline={true}
- />
+ { textAreaRef.current?.focus() }}
+ >
+ { chatThreadsService.setCurrentlyFocusedMessageIdx(undefined) }}
+ ref={textAreaRef}
+ fnsRef={textAreaFnsRef}
+ multiline={true}
+ />
-
+
+
return (
diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx
index 95060cb2..27254bbd 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx
@@ -348,9 +348,9 @@ export const useCommandBarURIListener = (listener: (uri: URI) => void) => {
export const useCommandBarState = () => {
const accessor = useAccessor()
const commandBarService = accessor.get('IVoidCommandBarService')
- const [s, ss] = useState({ state: commandBarService.stateOfURI, sortedURIs: commandBarService.sortedURIs });
+ const [s, ss] = useState({ stateOfURI: commandBarService.stateOfURI, sortedURIs: commandBarService.sortedURIs });
const listener = useCallback(() => {
- ss({ state: commandBarService.stateOfURI, sortedURIs: commandBarService.sortedURIs });
+ ss({ stateOfURI: commandBarService.stateOfURI, sortedURIs: commandBarService.sortedURIs });
}, [commandBarService])
useCommandBarURIListener(listener)
diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
index 1bd5cdfd..b144a34d 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
@@ -9,8 +9,9 @@ import { useAccessor, useCommandBarState, useIsDark } from '../util/services.js'
import '../styles.css'
import { useCallback, useEffect, useState, useRef } from 'react';
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js';
-import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBorder } from '../../../../common/helpers/colors.js';
+import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
import { VoidCommandBarProps } from '../../../voidCommandBarService.js';
+import { AcceptAllButtonWrapper, RejectAllButtonWrapper } from '../sidebar-tsx/SidebarChat.js';
export const VoidCommandBarMain = ({ uri, editor }: VoidCommandBarProps) => {
const isDark = useIsDark()
@@ -39,7 +40,7 @@ const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
const commandService = accessor.get('ICommandService')
const commandBarService = accessor.get('IVoidCommandBarService')
const voidModelService = accessor.get('IVoidModelService')
- const { state: commandBarState, sortedURIs: sortedCommandBarURIs } = useCommandBarState()
+ const { stateOfURI: commandBarState, sortedURIs: sortedCommandBarURIs } = useCommandBarState()
// useEffect(() => {
@@ -211,38 +212,47 @@ const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
if (!isADiffZoneInAnyFile) return null
- const acceptAllButton =
+ // Accept File
+ //
+
+ // const rejectAllButton =
+ // Reject File
+ //
+
+ const acceptAllButton =
- Accept File
-
+ />
-
- const rejectAllButton =
- Reject File
-
+ />
const acceptRejectAllButtons =
{acceptAllButton}