mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
input box style
This commit is contained in:
parent
a7aab9cf86
commit
a7c4a1b179
2 changed files with 101 additions and 85 deletions
|
|
@ -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 >
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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: '',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue