selection

This commit is contained in:
mp 2024-12-21 22:37:08 -08:00
parent 6298be394d
commit 9cf4980004
4 changed files with 126 additions and 19 deletions

View file

@ -9,6 +9,66 @@ import { atomOneDarkReasonable } from "react-syntax-highlighter/dist/esm/styles/
import { VoidCodeEditor } from '../util/inputs.js';
export function getLanguageFromFileName(fileName: string): string {
if (!fileName) return 'plaintext';
const ext = fileName.toLowerCase().split('.').pop();
if (!ext) return 'plaintext';
const extensionMap: { [key: string]: string } = {
// Web
'html': 'html',
'htm': 'html',
'css': 'css',
'scss': 'scss',
'less': 'less',
'js': 'javascript',
'jsx': 'javascript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'cc': 'cpp',
'h': 'cpp',
'hpp': 'cpp',
'cs': 'csharp',
'go': 'go',
'rs': 'rust',
'rb': 'ruby',
'php': 'php',
'sh': 'shell',
'bash': 'shell',
'zsh': 'shell',
// Markup/Config
'md': 'markdown',
'markdown': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Other
'sql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
'dockerfile': 'dockerfile',
'docker': 'dockerfile'
};
return extensionMap[ext] || 'plaintext';
}
export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, buttonsOnHover?: ReactNode, language?: string }) => {
const customStyle = {

View file

@ -9,7 +9,7 @@ import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, u
import { useAccessor, useThreadsState } from '../util/services.js';
import { ChatMessage, CodeSelection, CodeStagingSelection, IThreadHistoryService } from '../../../threadHistoryService.js';
import { BlockCode } from '../markdown/BlockCode.js';
import { BlockCode, getLanguageFromFileName } from '../markdown/BlockCode.js';
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
import { URI } from '../../../../../../../base/common/uri.js';
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
@ -86,6 +86,29 @@ const IconSquare = ({ size, className = '' }: { size: number, className?: string
);
};
export const IconWarning = ({ size, className = '' }: { size: number, className?: string }) => {
return (
<svg
className={className}
stroke="currentColor"
fill="currentColor"
strokeWidth="0"
viewBox="0 0 24 24"
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
>
{/* Warning triangle */}
<path d="M12 3L2 21h20L12 3zm0 3.3L19.3 19H4.7L12 6.3z" />
{/* Exclamation mark */}
<rect x="11" y="10" width="2" height="6" />
<circle cx="12" cy="18" r="1" />
</svg>
);
};
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement>
export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Required<Pick<ButtonProps, 'disabled'>>) => {
return <button
@ -109,7 +132,7 @@ export const ButtonStop = ({ className, ...props }: ButtonHTMLAttributes<HTMLBut
type='button'
{...props}
>
<IconSquare size={16} className="stroke-[2]" />
<IconSquare size={10} className="stroke-[2]" />
</button>
}
@ -192,7 +215,7 @@ export const SelectedFiles = (
>
{selections.map((selection, i) => {
const showSelectionText = selection.selectionStr && selectionIsOpened[i]
const showSelectionText = !!(selection.selectionStr && selectionIsOpened[i])
return (
<div key={i} // container for `selectionSummary` and `selectionText`
@ -201,9 +224,11 @@ export const SelectedFiles = (
{/* 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
className={`grid grid-rows-2 gap-1 relative p-1
select-none
bg-vscode-badge-bg border border-vscode-button-border rounded-md
bg-vscode-editor-bg hover:brightness-90
border border-vscode-button-border rounded-md
text-vscode-commandcenter-inactive-fg
w-fit h-fit min-w-[81px] p-1
`}
onClick={() => {
@ -222,7 +247,7 @@ export const SelectedFiles = (
</span>
{/* type of selection */}
<span className='truncate text-opacity-75'>{selection.selectionStr !== null ? 'Selection' : 'File'}</span>
<span className='truncate'>{selection.selectionStr !== null ? 'Selection' : 'File'}</span>
{/* X button */}
{type === 'staging' && // hoveredIdx === i
@ -241,7 +266,7 @@ export const SelectedFiles = (
{/* selection text */}
{showSelectionText &&
<div className='w-full'>
<BlockCode text={selection.selectionStr!} />
<BlockCode text={selection.selectionStr!} language={getLanguageFromFileName(selection.fileURI.path)} />
</div>
}
</div>

View file

@ -286,6 +286,9 @@ const normalizeIndentation = (code: string): string => {
}
export const VoidCodeEditor = ({ initValue, language }: { initValue: string, language: string | undefined }) => {
const MAX_HEIGHT = 200;
const divRef = useRef<HTMLDivElement | null>(null)
const accessor = useAccessor()
@ -302,10 +305,11 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan
container,
{
automaticLayout: true,
wordWrap: 'on',
wordWrap: 'off',
scrollbar: {
vertical: 'hidden',
vertical: 'auto',
horizontal: 'auto',
alwaysConsumeMouseWheel: false
},
lineNumbers: 'off',
folding: false,
@ -326,26 +330,35 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan
}
onCreateInstance={useCallback((editor: CodeEditorWidget) => {
const model = modelService.createModel(initValue, null)
editor.setModel(model);
model.setLanguage(language ?? 'plaintext')
const container = editor.getDomNode()
const parentNode = container?.parentElement
if (parentNode)
parentNode.style.height = `${editor.getScrollHeight() + 1}px` // the +1 is if there's a half pixel issue
if (parentNode) {
const height = Math.min(editor.getScrollHeight() + 1, MAX_HEIGHT);
parentNode.style.height = `${height}px`;
editor.layout();
}
return []
// Listen for content changes and update height
const disposable = editor.onDidContentSizeChange(() => {
if (parentNode) {
const height = Math.min(editor.getScrollHeight() + 1, MAX_HEIGHT);
parentNode.style.height = `${height}px`;
editor.layout();
}
});
return [disposable]
}, [modelService, initValue, language])}
dispose={useCallback((editor: CodeEditorWidget) => {
editor.dispose();
}, [])}
// ignored
propsFn={useCallback(() => { return [] }, [])}
/>
</div>

View file

@ -8,6 +8,7 @@ import { FeatureName, featureNames, ModelSelection, modelSelectionsEqual, Provid
import { useSettingsState, useRefreshModelState, useAccessor } from '../util/services.js'
import { VoidSelectBox } from '../util/inputs.js'
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
import { IconWarning } from '../sidebar-tsx/SidebarChat.js'
const ModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
@ -42,10 +43,18 @@ const ModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
}
const DummySelectBox = () => {
return <VoidSelectBox
options={[{ text: 'Please add a model!', value: null }]}
onChangeSelection={() => { }}
/>
return <div>
<IconWarning
size={24}
className='text-orange-900'
/>
<span className='text-orange-900'>Add a model</span>
</div>
// return <VoidSelectBox
// options={[{ text: 'Please add a model!', value: null }]}
// onChangeSelection={() => { }}
// />
}
export const ModelDropdown = ({ featureName }: { featureName: FeatureName }) => {