input box style

This commit is contained in:
mp 2024-12-16 23:01:41 -08:00
parent a7aab9cf86
commit a7c4a1b179
2 changed files with 101 additions and 85 deletions

View file

@ -102,7 +102,7 @@ const ScrollToBottomContainer = ({ children, className, style }: { children: Rea
const isBottom = Math.abs( const isBottom = Math.abs(
div.scrollHeight - div.clientHeight - div.scrollTop div.scrollHeight - div.clientHeight - div.scrollTop
) < 1; ) < 4;
setIsAtBottom(isBottom); setIsAtBottom(isBottom);
}; };
@ -159,71 +159,66 @@ export const SelectedFiles = (
return ( return (
!!selections && selections.length !== 0 && ( !!selections && selections.length !== 0 && (
<div className='flex flex-wrap gap-4 p-2'> <div
{selections.map((selection, i) => ( className='flex flex-wrap gap-4 p-2 text-left'
<Fragment key={i}> >
{/* selected file summary */} {selections.map((selection, i) => {
<div
// className="relative rounded rounded-e-2xl flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default" const showSelectionText = selection.selectionStr && selectionIsOpened[i]
className={`grid grid-rows-2 gap-1 relative
return (
<div key={i} // container for `selectionSummary` and `selectionText`
className={`${showSelectionText ? 'w-full' : ''}`}
>
{/* selection summary */}
<div
// className="relative rounded rounded-e-2xl flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default"
className={`grid grid-rows-2 gap-1 relative
select-none select-none
bg-vscode-badge-bg border border-vscode-button-border rounded-md bg-vscode-badge-bg border border-vscode-button-border rounded-md
w-fit h-fit min-w-[80px] p-1 w-fit h-fit min-w-[81px] p-1
text-left
`} `}
onClick={() => { onClick={() => {
setSelectionIsOpened(s => { setSelectionIsOpened(s => {
const newS = [...s] const newS = [...s]
newS[i] = !newS[i] newS[i] = !newS[i]
return newS return newS
}); });
}} }}
> >
<span className='truncate'>
<span className='truncate'> {/* file name */}
{/* file name */} {getBasename(selection.fileURI.fsPath)}
{getBasename(selection.fileURI.fsPath)} {/* selection range */}
{/* selection range */} {selection.selectionStr !== null ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''}
{selection.selectionStr !== null ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''}
</span>
{/* type of selection */}
<span className='truncate text-opacity-75'>{selection.selectionStr !== null ? 'Selection' : 'File'}</span>
{/* X button */}
{type === 'staging' && // hoveredIdx === i
<span className='absolute right-0 top-0 translate-x-[50%] translate-y-[-50%] cursor-pointer bg-white rounded-full border border-vscode-input-border z-1'
onClick={(e) => {
e.stopPropagation();
if (type !== 'staging') return;
setStaging([...selections.slice(0, i), ...selections.slice(i + 1)])
setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)])
}}
>
<IconX size={16} className="p-[2px] stroke-[3]" />
</span> </span>
{/* type of selection */}
<span className='truncate text-opacity-75'>{selection.selectionStr !== null ? 'Selection' : 'File'}</span>
{/* X button */}
{type === 'staging' && // hoveredIdx === i
<span className='absolute right-0 top-0 translate-x-[50%] translate-y-[-50%] cursor-pointer bg-white rounded-full border border-vscode-input-border z-1'
onClick={(e) => {
e.stopPropagation();
if (type !== 'staging') return;
setStaging([...selections.slice(0, i), ...selections.slice(i + 1)])
setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)])
}}
>
<IconX size={16} className="p-[2px] stroke-[3]" />
</span>
}
</div>
{/* selection text */}
{showSelectionText &&
<div className='w-full'>
<BlockCode text={selection.selectionStr!} />
</div>
} }
</div> </div>
{/* selection full text */} )
{selection.selectionStr && selectionIsOpened[i] && })}
<BlockCode
text={selection.selectionStr}
// buttonsOnHover={(<button
// // onClick={() => { // clear the selection string but keep the file
// // setStaging([...selections.slice(0, i), { ...selection, selectionStr: null }, ...selections.slice(i + 1, Infinity)])
// // }}
// onClick={() => {
// if (type !== 'staging') return
// setStaging([...selections.slice(0, i), ...selections.slice(i + 1, Infinity)])
// }}
// className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
// >Remove</button>
// )}
/>
}
</Fragment>
))
}
</div> </div>
) )
) )
@ -296,7 +291,7 @@ export const SidebarChat = () => {
// state of current message // state of current message
const [instructions, setInstructions] = useState('') // the user's instructions const [instructions, setInstructions] = useState('') // the user's instructions
const isDisabled = !instructions.trim() const isDisabled = !instructions.trim()
const [formHeight, setFormHeight] = useState(0) const [formHeight, setFormHeight] = useState(0) // TODO should use resize observer instead
const [sidebarHeight, setSidebarHeight] = useState(0) const [sidebarHeight, setSidebarHeight] = useState(0)
const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions]) const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions])
@ -405,8 +400,7 @@ export const SidebarChat = () => {
const previousMessages = currentThread?.messages ?? [] const previousMessages = currentThread?.messages ?? []
// const [_test_messages, _set_test_messages] = useState<string[]>([])
const [_test, _setTest] = useState<string[]>([])
return <div return <div
ref={(ref) => { if (ref) { setSidebarHeight(ref.clientHeight); } }} ref={(ref) => { if (ref) { setSidebarHeight(ref.clientHeight); } }}
@ -414,7 +408,7 @@ export const SidebarChat = () => {
> >
<ScrollToBottomContainer <ScrollToBottomContainer
className={`overflow-x-hidden overflow-y-auto space-y-4`} className={`overflow-x-hidden overflow-y-auto space-y-4`}
style={{ height: sidebarHeight - formHeight - 30 }} style={{ maxHeight: sidebarHeight - formHeight - 30 }}
> >
{/* previous messages */} {/* previous messages */}
{previousMessages.map((message, i) => <ChatBubble key={i} chatMessage={message} />)} {previousMessages.map((message, i) => <ChatBubble key={i} chatMessage={message} />)}
@ -422,12 +416,11 @@ export const SidebarChat = () => {
{/* message stream */} {/* message stream */}
<ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream || null }} /> <ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream || null }} />
<button type='button' onClick={() => { _setTest(d => [...d, 'asdasdsadasd']) }}>more divs</button> {/* {_test_messages.map((_, i) => <div key={i}>div {i}</div>)}
{_test.map((_, i) => <div key={i}>div {i}</div>)} <div>{`totalHeight: ${sidebarHeight - formHeight - 30}`}</div>
<div>{`totalHeight: ${sidebarHeight - formHeight - 30}`}</div> <div>{`sidebarHeight: ${sidebarHeight}`}</div>
<div>{`sidebarHeight: ${sidebarHeight}`}</div> <div>{`formHeight: ${formHeight}`}</div>
<div>{`formHeight: ${formHeight}`}</div> <button type='button' onClick={() => { _set_test_messages(d => [...d, 'asdasdsadasd']) }}>add div</button> */}
<button type='button' onClick={() => { _setTest(d => [...d, 'asdasdsadasd']) }}>more divs</button>
</ScrollToBottomContainer> </ScrollToBottomContainer>
@ -454,9 +447,14 @@ export const SidebarChat = () => {
console.log('submit!') console.log('submit!')
onSubmit(e) onSubmit(e)
}} }}
onClick={(e) => {
if (e.currentTarget === e.target) {
inputBoxRef.current?.focus()
}
}}
> >
{/* top row */} {/* top row */}
<div className=''> <>
{/* selections */} {/* selections */}
{(selections && selections.length !== 0) && {(selections && selections.length !== 0) &&
<SelectedFiles type='staging' selections={selections} setStaging={threadsStateService.setStaging.bind(threadsStateService)} /> <SelectedFiles type='staging' selections={selections} setStaging={threadsStateService.setStaging.bind(threadsStateService)} />
@ -471,10 +469,24 @@ export const SidebarChat = () => {
showDismiss={true} showDismiss={true}
/> />
} }
</div> </>
{/* middle row */} {/* middle row */}
<div className=''> <div
className={
// // overwrite vscode styles (generated with this code):
// `bg-transparent outline-none text-vscode-input-fg min-h-[81px] max-h-[500px]`
// .split(' ')
// .map(style => `@@[&_textarea]:!void-${style}`) // apply styles to ancestor input and textarea elements
// .join(' ') +
// ` outline-none`
// .split(' ')
// .map(style => `@@[&_div.monaco-inputbox]:!void-${style}`) // apply styles to ancestor input and textarea elements
// .join(' ');
`@@[&_textarea]:!void-bg-transparent @@[&_textarea]:!void-outline-none @@[&_textarea]:!void-text-vscode-input-fg @@[&_textarea]:!void-min-h-[81px] @@[&_textarea]:!void-max-h-[500px]@@[&_div.monaco-inputbox]:!void- @@[&_div.monaco-inputbox]:!void-outline-none`
}
>
{/* text input */} {/* text input */}
<VoidInputBox <VoidInputBox
placeholder={`${getCmdKey()}+L to select`} placeholder={`${getCmdKey()}+L to select`}
@ -485,7 +497,9 @@ export const SidebarChat = () => {
</div> </div>
{/* bottom row */} {/* bottom row */}
<div className='flex flex-row justify-between items-end'> <div
className='flex flex-row justify-between items-end'
>
{/* submit options */} {/* submit options */}
<div> <div>
<ModelDropdown featureName='Ctrl+L' /> <ModelDropdown featureName='Ctrl+L' />
@ -495,7 +509,7 @@ export const SidebarChat = () => {
{isLoading ? {isLoading ?
// stop button // stop button
<button <button
className={`size-[24px] rounded-full bg-white cursor-pointer`} className={`size-[20px] rounded-full bg-white cursor-pointer flex items-center justify-center`}
onClick={onAbort} onClick={onAbort}
type='button' type='button'
> >
@ -504,7 +518,7 @@ export const SidebarChat = () => {
: :
// submit button (up arrow) // submit button (up arrow)
<button <button
className={`size-[24px] rounded-full shrink-0 grow-0 cursor-pointer className={`size-[20px] rounded-full shrink-0 grow-0 cursor-pointer
${isDisabled ? ${isDisabled ?
'bg-vscode-disabled-fg' // cursor-not-allowed 'bg-vscode-disabled-fg' // cursor-not-allowed
: 'bg-white' // cursor-pointer : 'bg-white' // cursor-pointer
@ -513,15 +527,15 @@ export const SidebarChat = () => {
disabled={isDisabled} disabled={isDisabled}
type='submit' type='submit'
> >
<IconArrowUp size={24} className="stroke-[2]" /> <IconArrowUp size={20} className="stroke-[2]" />
</button> </button>
} }
</div> </div>
</form> </form>
</div> </div >
</div> </div >
} }

View file

@ -3,9 +3,9 @@
* Void Editor additions licensed under the AGPL 3.0 License. * Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef } from 'react';
import { useService } from './services.js'; import { useService } from '../util/services.js';
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { , InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
import { defaultInputBoxStyles, defaultSelectBoxStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js'; import { defaultInputBoxStyles, defaultSelectBoxStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js';
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'; import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
@ -39,15 +39,15 @@ export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, prop
export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline }: { export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline, styles }: {
onChangeText: (value: string) => void; onChangeText: (value: string) => void;
styles?: Partial<IInputBoxStyles>,
onCreateInstance?: (instance: InputBox) => void | IDisposable[]; onCreateInstance?: (instance: InputBox) => void | IDisposable[];
inputBoxRef?: { current: InputBox | null }; inputBoxRef?: { current: InputBox | null };
placeholder: string; placeholder: string;
multiline: boolean; multiline: boolean;
}) => { }) => {
const contextViewProvider = useService('contextViewService'); const contextViewProvider = useService('contextViewService');
return <WidgetComponent return <WidgetComponent
@ -58,7 +58,9 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac
{ {
inputBoxStyles: { inputBoxStyles: {
...defaultInputBoxStyles, ...defaultInputBoxStyles,
inputBackground: 'transparent', // inputBackground: 'transparent',
// inputBorder: 'none',
...styles,
}, },
placeholder, placeholder,
tooltip: '', tooltip: '',