Add search field to dropdown

This commit is contained in:
Saoud Rizwan
2024-10-03 23:40:25 -04:00
parent f6a14fdfb9
commit cfefd1b798

View File

@@ -1,25 +1,26 @@
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" import React, { useMemo, useState, useRef, useEffect, memo } from "react"
import React, { useMemo } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { useMount } from "react-use" import styled from "styled-components"
import { useExtensionState } from "../../context/ExtensionStateContext" import { useExtensionState } from "../../context/ExtensionStateContext"
import { useMount } from "react-use"
import { vscode } from "../../utils/vscode" import { vscode } from "../../utils/vscode"
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions" import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
import { memo, useEffect } from "react"
import { useRemark } from "react-remark" import { useRemark } from "react-remark"
import styled from "styled-components"
interface OpenRouterModelPickerProps {} const OpenRouterModelPicker: React.FC = () => {
const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = () => {
const { apiConfiguration, setApiConfiguration, openRouterModels } = useExtensionState() const { apiConfiguration, setApiConfiguration, openRouterModels } = useExtensionState()
const [searchTerm, setSearchTerm] = useState("")
const [isDropdownVisible, setIsDropdownVisible] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const handleModelChange = (event: any) => { const handleModelChange = (newModelId: string) => {
const newModelId = event.target.value
setApiConfiguration({ setApiConfiguration({
...apiConfiguration, ...apiConfiguration,
openRouterModelId: newModelId, openRouterModelId: newModelId,
openRouterModelInfo: openRouterModels[newModelId], openRouterModelInfo: openRouterModels[newModelId],
}) })
setSearchTerm(newModelId)
setIsDropdownVisible(false)
} }
const { selectedModelId, selectedModelInfo } = useMemo(() => { const { selectedModelId, selectedModelInfo } = useMemo(() => {
@@ -30,36 +31,52 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = () => {
vscode.postMessage({ type: "refreshOpenRouterModels" }) vscode.postMessage({ type: "refreshOpenRouterModels" })
}) })
const modelIds = useMemo(() => { const filteredModelIds = useMemo(() => {
return Object.keys(openRouterModels).sort((a, b) => a.localeCompare(b)) return Object.keys(openRouterModels)
}, [openRouterModels]) .filter((modelId) => modelId.toLowerCase().includes(searchTerm.toLowerCase()))
.sort((a, b) => a.localeCompare(b))
}, [openRouterModels, searchTerm])
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsDropdownVisible(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [])
return ( return (
<> <>
<div className="dropdown-container"> <DropdownWrapper ref={dropdownRef}>
<label htmlFor="model-id"> <label htmlFor="model-search">
<span style={{ fontWeight: 500 }}>Model</span> <span style={{ fontWeight: 500 }}>Model</span>
</label> </label>
<VSCodeDropdown <VSCodeTextField
id="model-id" id="model-search"
value={selectedModelId} placeholder="Search and select a model..."
onChange={handleModelChange} value={searchTerm}
style={{ width: "100%" }}> onChange={(e) => {
<VSCodeOption value="">Select a model...</VSCodeOption> setSearchTerm((e.target as HTMLInputElement).value)
{modelIds.map((modelId) => ( setIsDropdownVisible(true)
<VSCodeOption }}
key={modelId} onFocus={() => setIsDropdownVisible(true)}
value={modelId} style={{ width: "100%", zIndex: 1001 }}
style={{ />
whiteSpace: "normal", {isDropdownVisible && (
wordWrap: "break-word", <DropdownList>
maxWidth: "100%", {filteredModelIds.map((modelId) => (
}}> <DropdownItem key={modelId} onClick={() => handleModelChange(modelId)}>
{modelId} {modelId}
</VSCodeOption> </DropdownItem>
))} ))}
</VSCodeDropdown> </DropdownList>
</div> )}
</DropdownWrapper>
<ModelInfoView selectedModelId={selectedModelId} modelInfo={selectedModelInfo} /> <ModelInfoView selectedModelId={selectedModelId} modelInfo={selectedModelInfo} />
</> </>
@@ -68,6 +85,37 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = () => {
export default OpenRouterModelPicker export default OpenRouterModelPicker
// Dropdown
const DropdownWrapper = styled.div`
position: relative;
width: 100%;
`
const DropdownList = styled.div`
position: absolute;
top: calc(100% - 3px);
left: 0;
width: calc(100% - 2px);
max-height: 200px;
overflow-y: auto;
background-color: var(--vscode-dropdown-background);
border: 1px solid var(--vscode-list-activeSelectionBackground);
z-index: 1000;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
`
const DropdownItem = styled.div`
padding: 5px 10px;
cursor: pointer;
&:hover {
background-color: var(--vscode-list-activeSelectionBackground);
}
`
// Markdown
const StyledMarkdown = styled.div` const StyledMarkdown = styled.div`
font-family: var(--vscode-font-family), system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, font-family: var(--vscode-font-family), system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;