mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
edit backend
This commit is contained in:
parent
2470e0de7f
commit
48defe849d
4 changed files with 144 additions and 77 deletions
|
|
@ -33,6 +33,13 @@ export type FileSelection = {
|
|||
export type StagingSelectionItem = CodeSelection | FileSelection
|
||||
|
||||
|
||||
export type StagingInfo = {
|
||||
isBeingEdited: boolean;
|
||||
selections: StagingSelectionItem[] | null; // staging selections in edit mode
|
||||
text: string;
|
||||
}
|
||||
|
||||
|
||||
// WARNING: changing this format is a big deal!!!!!! need to migrate old format to new format on users' computers so people don't get errors.
|
||||
export type ChatMessage =
|
||||
| {
|
||||
|
|
@ -40,7 +47,7 @@ export type ChatMessage =
|
|||
content: string | null; // content sent to the llm - allowed to be '', will be replaced with (empty)
|
||||
displayContent: string | null; // content displayed to user - allowed to be '', will be ignored
|
||||
selections: StagingSelectionItem[] | null; // the user's selection
|
||||
stagingSelections: StagingSelectionItem[] | null; // staging selections in edit mode
|
||||
staging: StagingInfo | null
|
||||
}
|
||||
| {
|
||||
role: 'assistant';
|
||||
|
|
@ -60,7 +67,7 @@ export type ChatThreads = {
|
|||
createdAt: string; // ISO string
|
||||
lastModified: string; // ISO string
|
||||
messages: ChatMessage[];
|
||||
stagingSelections: StagingSelectionItem[] | null;
|
||||
staging: StagingInfo
|
||||
focusedMessageIdx?: number | undefined; // index of the message that is being edited (undefined if none)
|
||||
};
|
||||
}
|
||||
|
|
@ -87,7 +94,11 @@ const newThreadObject = () => {
|
|||
lastModified: now,
|
||||
messages: [],
|
||||
focusedMessageIdx: undefined,
|
||||
stagingSelections: null,
|
||||
staging: {
|
||||
isBeingEdited: true,
|
||||
selections: [],
|
||||
text: '',
|
||||
}
|
||||
} satisfies ChatThreads[string]
|
||||
}
|
||||
|
||||
|
|
@ -110,9 +121,10 @@ export interface IChatThreadService {
|
|||
switchToThread(threadId: string): void;
|
||||
|
||||
getFocusedMessageIdx(): number | undefined;
|
||||
isFocusingMessage(): boolean;
|
||||
setFocusedMessageIdx(messageIdx: number | undefined): void;
|
||||
|
||||
_useStagingSelectionsState(messageIdx?: number | undefined): readonly [StagingSelectionItem[], (selections: StagingSelectionItem[]) => void];
|
||||
_useFocusedStagingState(messageIdx?: number | undefined): readonly [StagingInfo, (selections: StagingInfo) => void];
|
||||
|
||||
editUserMessageAndStreamResponse(userMessage: string, messageIdx: number): Promise<void>;
|
||||
addUserMessageAndStreamResponse(userMessage: string): Promise<void>;
|
||||
|
|
@ -152,6 +164,9 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// for now just write the version, anticipating bigger changes in the future where we'll want to access this
|
||||
this._storageService.store(THREAD_VERSION_KEY, THREAD_VERSION, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
|
||||
|
||||
setInterval(() => { console.log(this.getFocusedMessageIdx()) }, 1000)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -218,24 +233,25 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}, true)
|
||||
|
||||
// stream the edit
|
||||
this.addUserMessageAndStreamResponse(userMessage, messageToReplace.stagingSelections)
|
||||
this.addUserMessageAndStreamResponse(userMessage, messageToReplace.staging)
|
||||
|
||||
}
|
||||
|
||||
async addUserMessageAndStreamResponse(userMessage: string, selectionsOverride?: StagingSelectionItem[] | null) {
|
||||
async addUserMessageAndStreamResponse(userMessage: string, stagingOverride?: StagingInfo | null) {
|
||||
|
||||
|
||||
const thread = this.getCurrentThread()
|
||||
const threadId = thread.id
|
||||
|
||||
let defaultThreadSelections = thread.stagingSelections
|
||||
let defaultThreadSelections = thread.staging
|
||||
|
||||
const currSelns = selectionsOverride ?? defaultThreadSelections ?? [] // don't use _useFocusedStagingState to avoid race conditions with focusing
|
||||
const currStaging = stagingOverride ?? defaultThreadSelections ?? [] // don't use _useFocusedStagingState to avoid race conditions with focusing
|
||||
const { selections: currSelns, } = currStaging
|
||||
|
||||
// add user's message to chat history
|
||||
const instructions = userMessage
|
||||
const content = await chat_userMessage(instructions, currSelns, this._modelService)
|
||||
const userHistoryElt: ChatMessage = { role: 'user', content: content, displayContent: instructions, selections: currSelns, stagingSelections: [], }
|
||||
const userHistoryElt: ChatMessage = { role: 'user', content: content, displayContent: instructions, selections: currSelns, staging: null, }
|
||||
this._addMessageToThread(threadId, userHistoryElt)
|
||||
|
||||
this._setStreamState(threadId, { error: undefined })
|
||||
|
|
@ -288,7 +304,21 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
getFocusedMessageIdx() {
|
||||
const thread = this.getCurrentThread()
|
||||
return thread.focusedMessageIdx
|
||||
|
||||
// get the focusedMessageIdx
|
||||
const focusedMessageIdx = thread.focusedMessageIdx
|
||||
if (focusedMessageIdx === undefined) return;
|
||||
|
||||
// check that the message is actually being edited
|
||||
const focusedMessage = thread.messages[focusedMessageIdx]
|
||||
if (focusedMessage.role !== 'user') return;
|
||||
if (!focusedMessage.staging?.isBeingEdited) return;
|
||||
|
||||
return focusedMessageIdx
|
||||
}
|
||||
|
||||
isFocusingMessage() {
|
||||
return this.getFocusedMessageIdx() !== undefined
|
||||
}
|
||||
|
||||
switchToThread(threadId: string) {
|
||||
|
|
@ -357,7 +387,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
|
||||
// set thread.messages[messageIdx].stagingSelections
|
||||
private setEditMessageStagingSelections(stagingSelections: StagingSelectionItem[], messageIdx: number): void {
|
||||
private setEditMessageStaging(staging: StagingInfo, messageIdx: number): void {
|
||||
|
||||
const thread = this.getCurrentThread()
|
||||
const message = thread.messages[messageIdx]
|
||||
|
|
@ -369,7 +399,10 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
[thread.id]: {
|
||||
...thread,
|
||||
messages: thread.messages.map((m, i) =>
|
||||
i === messageIdx ? { ...m, stagingSelections } : m
|
||||
i === messageIdx ? {
|
||||
...m,
|
||||
staging,
|
||||
} : m
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -378,8 +411,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
|
||||
// set thread.stagingSelections
|
||||
private setDefaultStagingSelections(stagingSelections: StagingSelectionItem[]): void {
|
||||
|
||||
private setDefaultStaging(staging: StagingInfo): void {
|
||||
|
||||
console.log('Default1')
|
||||
const thread = this.getCurrentThread()
|
||||
|
|
@ -391,7 +423,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
...this.state.allThreads,
|
||||
[thread.id]: {
|
||||
...thread,
|
||||
stagingSelections
|
||||
staging,
|
||||
}
|
||||
}
|
||||
}, true)
|
||||
|
|
@ -399,10 +431,12 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
|
||||
// gets `staging` and `setStaging` of the currently focused element, given the index of the currently selected message (or undefined if no message is selected)
|
||||
_useStagingSelectionsState(messageIdx?: number | undefined) {
|
||||
_useFocusedStagingState(messageIdx?: number | undefined) {
|
||||
|
||||
let staging: StagingSelectionItem[] = []
|
||||
let setStaging: (selections: StagingSelectionItem[]) => void = () => { }
|
||||
const defaultStaging = { isBeingEdited: false, selections: [], text: '' }
|
||||
|
||||
let staging: StagingInfo = defaultStaging
|
||||
let setStaging: (selections: StagingInfo) => void = () => { }
|
||||
|
||||
const thread = this.getCurrentThread()
|
||||
const isFocusingMessage = messageIdx !== undefined
|
||||
|
|
@ -410,21 +444,20 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
const message = thread.messages[messageIdx!]
|
||||
if (message.role === 'user') {
|
||||
staging = message.stagingSelections || []
|
||||
setStaging = (s) => this.setEditMessageStagingSelections(s, messageIdx)
|
||||
staging = message.staging || defaultStaging
|
||||
setStaging = (s) => this.setEditMessageStaging(s, messageIdx)
|
||||
}
|
||||
|
||||
}
|
||||
else { // is editing the default input box
|
||||
staging = thread.stagingSelections || []
|
||||
setStaging = this.setDefaultStagingSelections.bind(this)
|
||||
staging = thread.staging || defaultStaging
|
||||
setStaging = this.setDefaultStaging.bind(this)
|
||||
}
|
||||
|
||||
return [staging, setStaging] as const
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IChatThreadService, ChatThreadService, InstantiationType.Eager);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, K
|
|||
|
||||
|
||||
import { useAccessor, useSidebarState, useChatThreadsState, useChatThreadsStreamState, useUriState, useSettingsState } from '../util/services.js';
|
||||
import { ChatMessage, StagingSelectionItem } from '../../../chatThreadService.js';
|
||||
import { ChatMessage, StagingInfo, StagingSelectionItem } from '../../../chatThreadService.js';
|
||||
|
||||
import { BlockCode } from '../markdown/BlockCode.js';
|
||||
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
|
||||
|
|
@ -152,9 +152,12 @@ interface VoidInputFormProps {
|
|||
className?: string;
|
||||
showModelDropdown?: boolean;
|
||||
showSelections?: boolean;
|
||||
selections?: any[];
|
||||
showProspectiveSelections?: boolean;
|
||||
onSelectionsChange?: (selections: any[]) => void;
|
||||
|
||||
staging?: StagingInfo
|
||||
setStaging?: (s: StagingInfo) => void
|
||||
// selections?: any[];
|
||||
// onSelectionsChange?: (selections: any[]) => void;
|
||||
|
||||
// Optional close button
|
||||
onClose?: () => void;
|
||||
|
|
@ -172,9 +175,9 @@ export const VoidInputForm: React.FC<VoidInputFormProps> = ({
|
|||
showModelDropdown = true,
|
||||
featureName,
|
||||
showSelections = false,
|
||||
selections = [],
|
||||
showProspectiveSelections = true,
|
||||
onSelectionsChange,
|
||||
staging,
|
||||
setStaging,
|
||||
}) => {
|
||||
return (
|
||||
<form
|
||||
|
|
@ -189,11 +192,11 @@ export const VoidInputForm: React.FC<VoidInputFormProps> = ({
|
|||
`}
|
||||
>
|
||||
{/* Selections section */}
|
||||
{showSelections && onSelectionsChange && (
|
||||
{showSelections && staging && setStaging && (
|
||||
<SelectedFiles
|
||||
type='staging'
|
||||
selections={selections}
|
||||
setSelections={onSelectionsChange}
|
||||
selections={staging.selections || []}
|
||||
setSelections={(selections) => setStaging({ ...staging, selections })}
|
||||
showProspectiveSelections={showProspectiveSelections}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -541,23 +544,36 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
const chatThreadsService = accessor.get('IChatThreadService')
|
||||
|
||||
// edit mode state
|
||||
const [staging, setStaging] = chatThreadsService._useStagingSelectionsState(messageIdx)
|
||||
const [mode, setMode] = useState<ChatBubbleMode>('display')
|
||||
const [staging, setStaging] = chatThreadsService._useFocusedStagingState(messageIdx)
|
||||
const mode: ChatBubbleMode = staging.isBeingEdited ? 'edit' : 'display'
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const textAreaRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
const [isDisabled, setIsDisabled] = useState(false)
|
||||
const [textAreaRefState, setTextAreaRef] = useState<HTMLTextAreaElement | null>(null)
|
||||
const textAreaFnsRef = useRef<TextAreaFns | null>(null)
|
||||
const [isEditDisabled, setIsEditDisabled] = useState(false)
|
||||
// initialize on first render, and when edit was just enabled
|
||||
const _mustInitialize = useRef(true)
|
||||
const _justEnabledEdit = useRef(false)
|
||||
useEffect(() => {
|
||||
if (role === 'user') {
|
||||
setStaging(chatMessage.selections || [])
|
||||
const canInitialize = role === 'user' && mode === 'edit' && textAreaRefState
|
||||
const shouldInitialize = _justEnabledEdit.current || _mustInitialize.current
|
||||
if (canInitialize && shouldInitialize) {
|
||||
setStaging({
|
||||
...staging,
|
||||
selections: chatMessage.selections || [],
|
||||
text: chatMessage.displayContent || '',
|
||||
})
|
||||
if (textAreaFnsRef.current)
|
||||
textAreaFnsRef.current.setValue(chatMessage.displayContent || '')
|
||||
if (textAreaRef.current)
|
||||
textAreaRef.current.value = chatMessage.displayContent || ''
|
||||
}
|
||||
}, [role])
|
||||
|
||||
const EditSymbol = mode === 'edit' ? Pencil : X
|
||||
textAreaRefState.focus();
|
||||
|
||||
_justEnabledEdit.current = false
|
||||
_mustInitialize.current = false
|
||||
}
|
||||
|
||||
}, [role, mode, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current])
|
||||
|
||||
const EditSymbol = mode === 'display' ? Pencil : X
|
||||
|
||||
// set chat bubble contents
|
||||
let chatbubbleContents: React.ReactNode
|
||||
|
|
@ -572,18 +588,21 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
|
||||
const onSubmit = async () => {
|
||||
|
||||
if (isEditDisabled) return;
|
||||
if (!textAreaRef.current) return;
|
||||
if (isDisabled) return;
|
||||
if (!textAreaRefState) return;
|
||||
if (messageIdx === undefined) return;
|
||||
|
||||
// reset visual state
|
||||
setMode('display');
|
||||
// cancel any streams on this thread
|
||||
const thread = chatThreadsService.getCurrentThread()
|
||||
chatThreadsService.cancelStreaming(thread.id)
|
||||
|
||||
// reset state
|
||||
setStaging({ ...staging, isBeingEdited: false })
|
||||
chatThreadsService.setFocusedMessageIdx(undefined)
|
||||
|
||||
// stream the edit
|
||||
const userMessage = textAreaRef.current.value;
|
||||
const userMessage = textAreaRefState.value;
|
||||
await chatThreadsService.editUserMessageAndStreamResponse(userMessage, messageIdx)
|
||||
|
||||
textAreaFnsRef.current?.setValue('');
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
|
|
@ -591,14 +610,14 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
chatThreadsService.cancelStreaming(threadId)
|
||||
}
|
||||
|
||||
const onKeyDown = useCallback((e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Escape') {
|
||||
setMode('display')
|
||||
setStaging({ ...staging, isBeingEdited: false })
|
||||
}
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
onSubmit()
|
||||
}
|
||||
}, [onSubmit, setMode])
|
||||
}
|
||||
|
||||
if (!chatMessage.content && !isLoading) { // don't show if empty and not loading (if loading, want to show)
|
||||
return null
|
||||
|
|
@ -609,19 +628,20 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
onSubmit={onSubmit}
|
||||
onAbort={onAbort}
|
||||
isStreaming={false}
|
||||
isDisabled={isEditDisabled}
|
||||
isDisabled={isDisabled}
|
||||
showSelections={true}
|
||||
selections={staging || []}
|
||||
showProspectiveSelections={false}
|
||||
onSelectionsChange={setStaging}
|
||||
featureName="Ctrl+L"
|
||||
staging={staging}
|
||||
setStaging={setStaging}
|
||||
>
|
||||
<VoidInputBox2
|
||||
ref={setTextAreaRef}
|
||||
className='min-h-[81px] max-h-[500px] p-1'
|
||||
placeholder="Edit your message..."
|
||||
onChangeText={(text) => setIsEditDisabled(!text)}
|
||||
onChangeText={(text) => setIsDisabled(!text)}
|
||||
onFocus={() => { console.log('CHAT FOCUS'); chatThreadsService.setFocusedMessageIdx(messageIdx) }}
|
||||
onKeyDown={onKeyDown}
|
||||
ref={textAreaRef}
|
||||
fnsRef={textAreaFnsRef}
|
||||
multiline={true}
|
||||
/>
|
||||
|
|
@ -670,8 +690,14 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
${isHovered ? 'opacity-100' : 'opacity-0'}
|
||||
`}
|
||||
onClick={() => {
|
||||
setMode(m => m === 'display' ? 'edit' : 'display')
|
||||
chatThreadsService.setFocusedMessageIdx(messageIdx)
|
||||
if (mode === 'display') {
|
||||
setStaging({ ...staging, isBeingEdited: true })
|
||||
chatThreadsService.setFocusedMessageIdx(messageIdx)
|
||||
_justEnabledEdit.current = true
|
||||
} else if (mode === 'edit') {
|
||||
setStaging({ ...staging, isBeingEdited: false })
|
||||
chatThreadsService.setFocusedMessageIdx(undefined)
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
|
|
@ -694,8 +720,8 @@ export const SidebarChat = () => {
|
|||
useEffect(() => {
|
||||
const disposables: IDisposable[] = []
|
||||
disposables.push(
|
||||
sidebarStateService.onDidFocusChat(() => { textAreaRef.current?.focus(); chatThreadsService.setFocusedMessageIdx(undefined) }),
|
||||
sidebarStateService.onDidBlurChat(() => { textAreaRef.current?.blur() })
|
||||
sidebarStateService.onDidFocusChat(() => { !chatThreadsService.isFocusingMessage() && textAreaRef.current?.focus() }),
|
||||
sidebarStateService.onDidBlurChat(() => { !chatThreadsService.isFocusingMessage() && textAreaRef.current?.blur() })
|
||||
)
|
||||
return () => disposables.forEach(d => d.dispose())
|
||||
}, [sidebarStateService, textAreaRef])
|
||||
|
|
@ -707,7 +733,7 @@ export const SidebarChat = () => {
|
|||
|
||||
const currentThread = chatThreadsService.getCurrentThread()
|
||||
const previousMessages = currentThread?.messages ?? []
|
||||
const [selections, setSelections] = chatThreadsService._useStagingSelectionsState()
|
||||
const [staging, setStaging] = chatThreadsService._useFocusedStagingState()
|
||||
|
||||
// stream state
|
||||
const currThreadStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId)
|
||||
|
|
@ -743,7 +769,7 @@ export const SidebarChat = () => {
|
|||
await chatThreadsService.addUserMessageAndStreamResponse(userMessage)
|
||||
console.log('done streaming',)
|
||||
|
||||
setSelections([]) // clear staging
|
||||
setStaging({ ...staging, selections: [], text: '' }) // clear staging
|
||||
console.log('set staging',)
|
||||
textAreaFnsRef.current?.setValue('')
|
||||
console.log('set value',)
|
||||
|
|
@ -751,7 +777,7 @@ export const SidebarChat = () => {
|
|||
console.log('textAreaRef', textAreaRef.current)
|
||||
console.log('focus',)
|
||||
|
||||
}, [chatThreadsService, isDisabled, isStreaming, textAreaRef, textAreaFnsRef, setSelections])
|
||||
}, [chatThreadsService, isDisabled, isStreaming, textAreaRef, textAreaFnsRef, staging, setStaging])
|
||||
|
||||
const onAbort = () => {
|
||||
const threadId = currentThread.id
|
||||
|
|
@ -772,7 +798,7 @@ export const SidebarChat = () => {
|
|||
|
||||
const prevMessagesHTML = useMemo(() => {
|
||||
return previousMessages.map((message, i) =>
|
||||
<ChatBubble key={i} chatMessage={message} messageIdx={i} />
|
||||
<ChatBubble key={`${message.displayContent}-${i}`} chatMessage={message} messageIdx={i} />
|
||||
)
|
||||
}, [previousMessages])
|
||||
|
||||
|
|
@ -836,9 +862,9 @@ export const SidebarChat = () => {
|
|||
isStreaming={isStreaming}
|
||||
isDisabled={isDisabled}
|
||||
showSelections={true}
|
||||
selections={selections || []}
|
||||
showProspectiveSelections={prevMessagesHTML.length === 0}
|
||||
onSelectionsChange={setSelections}
|
||||
staging={staging}
|
||||
setStaging={setStaging}
|
||||
// onSelectionsChange={chatThreadsService.setStagingSelections.bind(chatThreadsService)}
|
||||
featureName="Ctrl+L"
|
||||
>
|
||||
|
|
@ -847,6 +873,7 @@ export const SidebarChat = () => {
|
|||
placeholder={`${keybindingString ? `${keybindingString} to select. ` : ''}Enter instructions...`}
|
||||
onChangeText={onChangeText}
|
||||
onKeyDown={onKeyDown}
|
||||
onFocus={() => { chatThreadsService.setFocusedMessageIdx(undefined) }}
|
||||
ref={textAreaRef}
|
||||
fnsRef={textAreaFnsRef}
|
||||
multiline={true}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,11 @@ type InputBox2Props = {
|
|||
className?: string;
|
||||
onChangeText?: (value: string) => void;
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
||||
onFocus?: (e: React.FocusEvent<HTMLTextAreaElement>) => void;
|
||||
onBlur?: (e: React.FocusEvent<HTMLTextAreaElement>) => void;
|
||||
onChangeHeight?: (newHeight: number) => void;
|
||||
}
|
||||
export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(function X({ initValue, placeholder, multiline, fnsRef, className, onKeyDown, onChangeText }, ref) {
|
||||
export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(function X({ initValue, placeholder, multiline, fnsRef, className, onKeyDown, onFocus, onBlur, onChangeText }, ref) {
|
||||
|
||||
// mirrors whatever is in ref
|
||||
const textAreaRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
|
@ -114,6 +116,9 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
|
|||
adjustHeight()
|
||||
}, [fnsRef, fns, setEnabled, adjustHeight, ref])}
|
||||
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
|
||||
disabled={!isEnabled}
|
||||
|
||||
className={`w-full resize-none max-h-[500px] overflow-y-auto text-void-fg-1 placeholder:text-void-fg-3 ${className}`}
|
||||
|
|
@ -335,7 +340,7 @@ export const VoidCustomSelectBox = <T extends any>({
|
|||
} = useFloating({
|
||||
open: isOpen,
|
||||
onOpenChange: setIsOpen,
|
||||
placement:'bottom-start',
|
||||
placement: 'bottom-start',
|
||||
|
||||
middleware: [
|
||||
offset(gap),
|
||||
|
|
@ -367,7 +372,7 @@ export const VoidCustomSelectBox = <T extends any>({
|
|||
}),
|
||||
],
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy:'fixed',
|
||||
strategy: 'fixed',
|
||||
});
|
||||
|
||||
// if the selected option is null, use the 0th option
|
||||
|
|
|
|||
|
|
@ -134,22 +134,24 @@ registerAction2(class extends Action2 {
|
|||
// update the staging selections
|
||||
const chatThreadService = accessor.get(IChatThreadService)
|
||||
|
||||
const messageIdx = chatThreadService.getFocusedMessageIdx()
|
||||
const [staging, setStaging] = chatThreadService._useStagingSelectionsState(messageIdx)
|
||||
const focusedMessageIdx = chatThreadService.getFocusedMessageIdx()
|
||||
const [staging, setStaging] = chatThreadService._useFocusedStagingState(focusedMessageIdx)
|
||||
const selections = staging.selections || []
|
||||
const setSelections = (s: StagingSelectionItem[]) => setStaging({ ...staging, selections: s })
|
||||
|
||||
// if matches with existing selection, overwrite (since text may change)
|
||||
const currentStagingEltIdx = findMatchingStagingIndex(staging, selection)
|
||||
const currentStagingEltIdx = findMatchingStagingIndex(selections, selection)
|
||||
|
||||
if (currentStagingEltIdx !== undefined && currentStagingEltIdx !== -1) {
|
||||
setStaging([
|
||||
...staging!.slice(0, currentStagingEltIdx),
|
||||
setSelections([
|
||||
...selections!.slice(0, currentStagingEltIdx),
|
||||
selection,
|
||||
...staging!.slice(currentStagingEltIdx + 1, Infinity)
|
||||
...selections!.slice(currentStagingEltIdx + 1, Infinity)
|
||||
])
|
||||
}
|
||||
// if no match, add it
|
||||
else {
|
||||
setStaging([...(staging ?? []), selection])
|
||||
setSelections([...(selections ?? []), selection])
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue