ChatSidebar.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import React, { useState, useRef, useEffect, useCallback, KeyboardEvent } from "react"
  2. import { VSCodeButton, VSCodeTextArea, VSCodeDivider, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
  3. import { vscode } from "../utilities/vscode"
  4. import DynamicTextArea from "react-textarea-autosize"
  5. interface Message {
  6. id: number
  7. text: string
  8. sender: "user" | "assistant"
  9. }
  10. const ChatSidebar = () => {
  11. const [messages, setMessages] = useState<Message[]>([])
  12. const [inputValue, setInputValue] = useState("")
  13. const messagesEndRef = useRef<HTMLDivElement>(null)
  14. const textAreaRef = useRef<HTMLTextAreaElement>(null)
  15. const [textAreaHeight, setTextAreaHeight] = useState<number | undefined>(undefined)
  16. const scrollToBottom = () => {
  17. // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
  18. messagesEndRef.current?.scrollIntoView({ behavior: "smooth", block: 'nearest', inline: 'start' })
  19. }
  20. useEffect(scrollToBottom, [messages])
  21. const handleSendMessage = () => {
  22. if (inputValue.trim()) {
  23. const newMessage: Message = {
  24. id: Date.now(),
  25. text: inputValue.trim(),
  26. sender: "user",
  27. }
  28. setMessages([...messages, newMessage])
  29. setInputValue("")
  30. // Here you would typically send the message to your extension's backend
  31. vscode.postMessage({
  32. command: "sendMessage",
  33. text: newMessage.text,
  34. })
  35. }
  36. }
  37. const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
  38. if (event.key === "Enter" && !event.shiftKey) {
  39. event.preventDefault()
  40. handleSendMessage()
  41. }
  42. }
  43. useEffect(() => {
  44. if (textAreaRef.current && !textAreaHeight) {
  45. setTextAreaHeight(textAreaRef.current.offsetHeight)
  46. }
  47. }, [])
  48. return (
  49. <div style={{ display: "flex", flexDirection: "column", height: "100vh", backgroundColor: "gray", overflow: "hidden" }}>
  50. <div style={{ flexGrow: 1, overflowY: "scroll", scrollbarWidth: "none" }}>
  51. {messages.map((message) => (
  52. <div
  53. key={message.id}
  54. style={{
  55. marginBottom: "10px",
  56. padding: "8px",
  57. borderRadius: "4px",
  58. backgroundColor:
  59. message.sender === "user"
  60. ? "var(--vscode-editor-background)"
  61. : "var(--vscode-sideBar-background)",
  62. }}>
  63. <span style={{ whiteSpace: "pre-line", overflowWrap: "break-word" }}>{message.text}</span>
  64. </div>
  65. ))}
  66. <div style={{ float:"left", clear: "both" }} ref={messagesEndRef} />
  67. </div>
  68. <div style={{ position: "relative", paddingTop: "16px", paddingBottom: "16px" }}>
  69. <DynamicTextArea
  70. ref={textAreaRef}
  71. value={inputValue}
  72. onChange={(e) => setInputValue(e.target.value)}
  73. onKeyDown={handleKeyDown}
  74. onHeightChange={() => scrollToBottom()}
  75. placeholder="Type a message..."
  76. maxRows={10}
  77. style={{
  78. width: "100%",
  79. boxSizing: "border-box",
  80. backgroundColor: "var(--vscode-input-background, #3c3c3c)",
  81. color: "var(--vscode-input-foreground, #cccccc)",
  82. border: "1px solid var(--vscode-input-border, #3c3c3c)",
  83. borderRadius: "2px",
  84. fontFamily:
  85. "var(--vscode-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif)",
  86. fontSize: "var(--vscode-editor-font-size, 13px)",
  87. lineHeight: "var(--vscode-editor-line-height, 1.5)",
  88. resize: "none",
  89. overflow: "hidden",
  90. paddingTop: "8px",
  91. paddingBottom: "8px",
  92. paddingLeft: "8px",
  93. paddingRight: "40px", // Make room for button
  94. }}
  95. />
  96. {textAreaHeight && (
  97. <div
  98. style={{
  99. position: "absolute",
  100. right: "12px",
  101. height: `${textAreaHeight}px`,
  102. bottom: "18px",
  103. display: "flex",
  104. alignItems: "center",
  105. }}>
  106. <VSCodeButton appearance="icon" aria-label="Send Message" onClick={handleSendMessage}>
  107. <span className="codicon codicon-send"></span>
  108. </VSCodeButton>
  109. </div>
  110. )}
  111. </div>
  112. </div>
  113. )
  114. }
  115. export default ChatSidebar