Add codicon; add basic chat sidebar with a ResizingTextArea component

This commit is contained in:
Saoud Rizwan
2024-07-06 19:29:48 -04:00
parent 2feedd766b
commit b0f93bffe9
9 changed files with 350 additions and 63 deletions

View File

@@ -0,0 +1,94 @@
import React, { useState, useRef, useEffect, useCallback } from "react"
import { VSCodeButton, VSCodeTextArea, VSCodeDivider, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { vscode } from "../utilities/vscode"
import ResizingTextArea from "./ResizingTextArea"
interface Message {
id: number
text: string
sender: "user" | "assistant"
}
const ChatSidebar = () => {
const [messages, setMessages] = useState<Message[]>([])
const [inputValue, setInputValue] = useState("")
const messagesEndRef = useRef<HTMLDivElement>(null)
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}
useEffect(scrollToBottom, [messages])
const handleSendMessage = () => {
if (inputValue.trim()) {
const newMessage: Message = {
id: Date.now(),
text: inputValue.trim(),
sender: "user",
}
setMessages([...messages, newMessage])
setInputValue("")
// if (textAreaRef.current) {
// textAreaRef.current.style.height = "auto"
// }
// Here you would typically send the message to your extension's backend
vscode.postMessage({
command: "sendMessage",
text: newMessage.text,
})
}
}
return (
<div className="chat-sidebar" style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
<div className="message-list" style={{ flexGrow: 1, overflowY: "auto", padding: "10px" }}>
{messages.map((message) => (
<div
key={message.id}
className={`message ${message.sender}`}
style={{
marginBottom: "10px",
padding: "8px",
borderRadius: "4px",
backgroundColor:
message.sender === "user"
? "var(--vscode-editor-background)"
: "var(--vscode-sideBar-background)",
}}>
{message.text}
</div>
))}
<div ref={messagesEndRef} />
</div>
<VSCodeDivider />
<div className="input-area" style={{ padding: 20 }}>
<ResizingTextArea
value={inputValue}
onChange={setInputValue}
placeholder="Type a message..."
style={{ marginBottom: "10px", width: "100%" }}
/>
<VSCodeButton onClick={handleSendMessage}>Send</VSCodeButton>
<VSCodeTextField>
<section slot="end" style={{ display: "flex", alignItems: "center" }}>
<VSCodeButton appearance="icon" aria-label="Match Case">
<span className="codicon codicon-case-sensitive"></span>
</VSCodeButton>
<VSCodeButton appearance="icon" aria-label="Match Whole Word">
<span className="codicon codicon-whole-word"></span>
</VSCodeButton>
<VSCodeButton appearance="icon" aria-label="Use Regular Expression">
<span className="codicon codicon-regex"></span>
</VSCodeButton>
</section>
</VSCodeTextField>
<span slot="end" className="codicon codicon-chevron-right"></span>
<VSCodeButton onClick={handleSendMessage}>Send</VSCodeButton>
</div>
</div>
)
}
export default ChatSidebar

View File

@@ -0,0 +1,132 @@
import { vscode } from "../utilities/vscode"
import {
VSCodeBadge,
VSCodeButton,
VSCodeCheckbox,
VSCodeDataGrid,
VSCodeDataGridCell,
VSCodeDataGridRow,
VSCodeDivider,
VSCodeDropdown,
VSCodeLink,
VSCodeOption,
VSCodePanels,
VSCodePanelTab,
VSCodePanelView,
VSCodeProgressRing,
VSCodeRadio,
VSCodeRadioGroup,
VSCodeTag,
VSCodeTextArea,
VSCodeTextField,
} from "@vscode/webview-ui-toolkit/react"
function Demo() {
function handleHowdyClick() {
vscode.postMessage({
command: "hello",
text: "Hey there partner! 🤠",
})
}
const rowData = [
{
cell1: "Cell Data",
cell2: "Cell Data",
cell3: "Cell Data",
cell4: "Cell Data",
},
{
cell1: "Cell Data",
cell2: "Cell Data",
cell3: "Cell Data",
cell4: "Cell Data",
},
{
cell1: "Cell Data",
cell2: "Cell Data",
cell3: "Cell Data",
cell4: "Cell Data",
},
]
return (
<main>
<h1>Hello World!</h1>
<VSCodeButton onClick={handleHowdyClick}>Howdy!</VSCodeButton>
<div className="grid gap-3 p-2 place-items-start">
<VSCodeDataGrid>
<VSCodeDataGridRow row-type="header">
<VSCodeDataGridCell cell-type="columnheader" grid-column="1">
A Custom Header Title
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="2">
Another Custom Title
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="3">
Title Is Custom
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="4">
Custom Title
</VSCodeDataGridCell>
</VSCodeDataGridRow>
{rowData.map((row, index) => (
<VSCodeDataGridRow key={index}>
<VSCodeDataGridCell grid-column="1">{row.cell1}</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="2">{row.cell2}</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="3">{row.cell3}</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="4">{row.cell4}</VSCodeDataGridCell>
</VSCodeDataGridRow>
))}
</VSCodeDataGrid>
<VSCodeTextField>
<section slot="end" style={{ display: "flex", alignItems: "center" }}>
<VSCodeButton appearance="icon" aria-label="Match Case">
<span className="codicon codicon-case-sensitive"></span>
</VSCodeButton>
<VSCodeButton appearance="icon" aria-label="Match Whole Word">
<span className="codicon codicon-whole-word"></span>
</VSCodeButton>
<VSCodeButton appearance="icon" aria-label="Use Regular Expression">
<span className="codicon codicon-regex"></span>
</VSCodeButton>
</section>
</VSCodeTextField>
<span slot="end" className="codicon codicon-chevron-right"></span>
<span className="flex gap-3">
<VSCodeProgressRing />
<VSCodeTextField />
<VSCodeButton>Add</VSCodeButton>
<VSCodeButton appearance="secondary">Remove</VSCodeButton>
</span>
<VSCodeBadge>Badge</VSCodeBadge>
<VSCodeCheckbox>Checkbox</VSCodeCheckbox>
<VSCodeDivider />
<VSCodeDropdown>
<VSCodeOption>Option 1</VSCodeOption>
<VSCodeOption>Option 2</VSCodeOption>
</VSCodeDropdown>
<VSCodeLink href="#">Link</VSCodeLink>
<VSCodePanels>
<VSCodePanelTab id="tab-1">Tab 1</VSCodePanelTab>
<VSCodePanelTab id="tab-2">Tab 2</VSCodePanelTab>
<VSCodePanelView id="view-1">Panel View 1</VSCodePanelView>
<VSCodePanelView id="view-2">Panel View 2</VSCodePanelView>
</VSCodePanels>
<VSCodeRadioGroup>
<VSCodeRadio>Radio 1</VSCodeRadio>
<VSCodeRadio>Radio 2</VSCodeRadio>
</VSCodeRadioGroup>
<VSCodeTag>Tag</VSCodeTag>
<VSCodeTextArea placeholder="Text Area" />
</div>
</main>
)
}
export default Demo

View File

@@ -0,0 +1,46 @@
import React, { TextareaHTMLAttributes, CSSProperties, useRef, useEffect } from "react"
interface ResizingTextAreaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "onChange"> {
onChange: (value: string) => void
}
const ResizingTextArea= ({ style, value, onChange, ...props }: ResizingTextAreaProps) => {
const textAreaRef = useRef<HTMLTextAreaElement>(null)
const textareaStyle: CSSProperties = {
width: "100%",
minHeight: "60px",
backgroundColor: "var(--vscode-input-background, #3c3c3c)",
color: "var(--vscode-input-foreground, #cccccc)",
border: "1px solid var(--vscode-input-border, #3c3c3c)",
borderRadius: "2px",
padding: "4px 8px",
outline: "none",
fontFamily: "var(--vscode-editor-font-family)",
fontSize: "var(--vscode-editor-font-size, 13px)",
lineHeight: "var(--vscode-editor-line-height, 1.5)",
resize: "none",
overflow: "hidden",
...style,
}
const adjustTextAreaHeight = () => {
if (textAreaRef.current) {
textAreaRef.current.style.height = "auto"
textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`
}
}
const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
onChange(event.target.value)
adjustTextAreaHeight()
}
useEffect(() => {
adjustTextAreaHeight()
}, [value])
return <textarea ref={textAreaRef} style={textareaStyle} value={value} onChange={handleInputChange} {...props} />
}
export default ResizingTextArea