mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
state is broken
This commit is contained in:
parent
429224993b
commit
3e308f2082
10 changed files with 87 additions and 104 deletions
2
extensions/void/.vscode/settings.json
vendored
2
extensions/void/.vscode/settings.json
vendored
|
|
@ -8,7 +8,7 @@
|
|||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"out": false,
|
||||
"**/node_modules": false
|
||||
"**/node_modules": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
|
|
|
|||
|
|
@ -233,8 +233,8 @@
|
|||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "void.openThreadSelector",
|
||||
"title": "View all your past chats",
|
||||
"command": "void.toggleThreadSelector",
|
||||
"title": "View past chats",
|
||||
"icon": "$(history)"
|
||||
}, {
|
||||
"command": "void.openSettings",
|
||||
|
|
@ -280,7 +280,7 @@
|
|||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "void.openThreadSelector",
|
||||
"command": "void.toggleThreadSelector",
|
||||
"when": "view == 'void.viewnumberone'",
|
||||
"group": "navigation"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import OpenAI from 'openai';
|
||||
import { Ollama } from 'ollama/browser'
|
||||
import { getVSCodeAPI } from '../sidebar/getVscodeApi';
|
||||
|
||||
|
||||
// always compare these against package.json to make sure every setting in this type can actually be provided by the user
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => {
|
||||
webview.postMessage({ type: 'startNewThread' } satisfies WebviewMessage)
|
||||
}))
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.openThreadSelector', async () => {
|
||||
webview.postMessage({ type: 'openThreadSelector' } satisfies WebviewMessage)
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => {
|
||||
webview.postMessage({ type: 'toggleThreadSelector' } satisfies WebviewMessage)
|
||||
}))
|
||||
|
||||
// when config changes, send it to the sidebar
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ type WebviewMessage = (
|
|||
| { type: 'startNewThread' }
|
||||
|
||||
// editor -> sidebar
|
||||
| { type: 'openThreadSelector' }
|
||||
| { type: 'toggleThreadSelector' }
|
||||
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import MarkdownRender from "./markdown/MarkdownRender";
|
|||
import BlockCode from "./markdown/BlockCode";
|
||||
|
||||
import * as vscode from 'vscode'
|
||||
import { FilesSelector, IncludedFiles } from "./components/Files";
|
||||
import { useChat } from "./context";
|
||||
import { FilesSelector, SelectedFiles } from "./components/SelectedFiles";
|
||||
import { useChat } from "./chatContext";
|
||||
|
||||
|
||||
const filesStr = (fullFiles: File[]) => {
|
||||
|
|
@ -50,7 +50,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
|||
|
||||
if (role === 'user') {
|
||||
chatbubbleContents = <>
|
||||
<IncludedFiles files={chatMessage.files} />
|
||||
<SelectedFiles files={chatMessage.files} />
|
||||
{chatMessage.selection?.selectionStr && <BlockCode text={chatMessage.selection.selectionStr} hideToolbar />}
|
||||
{children}
|
||||
</>
|
||||
|
|
@ -68,21 +68,6 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
|||
</div>
|
||||
}
|
||||
|
||||
|
||||
// const [stateRef, setState] = useInstantState(initVal)
|
||||
// setState instantly changes the value of stateRef instead of having to wait until the next render
|
||||
const useInstantState = <T,>(initVal: T) => {
|
||||
const stateRef = useRef<T>(initVal)
|
||||
const [_, setS] = useState<T>(initVal)
|
||||
const setState = useCallback((newVal: T) => {
|
||||
setS(newVal);
|
||||
stateRef.current = newVal;
|
||||
}, [])
|
||||
return [stateRef as React.RefObject<T>, setState] as const // make s.current readonly - setState handles all changes
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ThreadSelector = ({ onClose }: { onClose: () => void }) => {
|
||||
const { allThreads, currentThread, switchToThread } = useChat()
|
||||
return (
|
||||
|
|
@ -173,16 +158,15 @@ const Sidebar = () => {
|
|||
setApiConfig(m.apiConfig)
|
||||
}
|
||||
|
||||
|
||||
// top navigation bar command - new chat
|
||||
// if they pressed the + to add a new chat
|
||||
else if (m.type === 'startNewThread') {
|
||||
setIsThreadSelectorOpen(false)
|
||||
startNewThread()
|
||||
}
|
||||
|
||||
// top navigation bar command - new chat
|
||||
else if (m.type === 'openThreadSelector') {
|
||||
setIsThreadSelectorOpen(true)
|
||||
// if they opened thread selector
|
||||
else if (m.type === 'toggleThreadSelector') {
|
||||
setIsThreadSelectorOpen(v => !v)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -273,67 +257,66 @@ const Sidebar = () => {
|
|||
{/* selected files */}
|
||||
<FilesSelector files={files} setFiles={setFiles} />
|
||||
{/* selected code */}
|
||||
{!selection?.selectionStr ? null
|
||||
: (
|
||||
<div className="relative">
|
||||
<div className="input">
|
||||
{/* selection */}
|
||||
{(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
||||
{/* selected files */}
|
||||
<FilesSelector files={files} setFiles={setFiles} />
|
||||
{/* selected code */}
|
||||
{!!selection?.selectionStr && (
|
||||
<BlockCode className="rounded bg-vscode-sidebar-bg" text={selection.selectionStr} toolbar={(
|
||||
<button
|
||||
onClick={clearSelection}
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)} />
|
||||
)}
|
||||
</div>}
|
||||
<form
|
||||
ref={formRef}
|
||||
className="flex flex-row items-center rounded-md p-2"
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }}
|
||||
|
||||
onSubmit={(e) => {
|
||||
console.log('submit!')
|
||||
e.preventDefault();
|
||||
onSubmit(e)
|
||||
}}>
|
||||
{/* input */}
|
||||
|
||||
<textarea
|
||||
onChange={(e) => { setInstructions(e.target.value) }}
|
||||
className="w-full p-2 leading-tight resize-none max-h-[50vh] overflow-hidden bg-transparent border-none !outline-none"
|
||||
placeholder="Ctrl+L to select"
|
||||
rows={1}
|
||||
onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||
/>
|
||||
{/* submit button */}
|
||||
{isLoading ?
|
||||
<button
|
||||
onClick={onStop}
|
||||
className="btn btn-primary rounded-r-lg max-h-10 p-2"
|
||||
type='button'
|
||||
>Stop</button>
|
||||
: <button
|
||||
className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
disabled={!instructions}
|
||||
type='submit'
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="19" x2="12" y2="5"></line>
|
||||
<polyline points="5 12 12 5 19 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="input">
|
||||
{/* selection */}
|
||||
{(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
||||
{/* selected files */}
|
||||
<FilesSelector files={files} setFiles={setFiles} />
|
||||
{/* selected code */}
|
||||
{!!selection?.selectionStr && (
|
||||
<BlockCode className="rounded bg-vscode-sidebar-bg" text={selection.selectionStr} toolbar={(
|
||||
<button
|
||||
onClick={clearSelection}
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)} />
|
||||
)}
|
||||
</div>}
|
||||
<form
|
||||
ref={formRef}
|
||||
className="flex flex-row items-center rounded-md p-2"
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }}
|
||||
|
||||
onSubmit={(e) => {
|
||||
console.log('submit!')
|
||||
e.preventDefault();
|
||||
onSubmit(e)
|
||||
}}>
|
||||
{/* input */}
|
||||
|
||||
<textarea
|
||||
onChange={(e) => { setInstructions(e.target.value) }}
|
||||
className="w-full p-2 leading-tight resize-none max-h-[50vh] overflow-hidden bg-transparent border-none !outline-none"
|
||||
placeholder="Ctrl+L to select"
|
||||
rows={1}
|
||||
onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||
/>
|
||||
{/* submit button */}
|
||||
{isLoading ?
|
||||
<button
|
||||
onClick={onStop}
|
||||
className="btn btn-primary rounded-r-lg max-h-10 p-2"
|
||||
type='button'
|
||||
>Stop</button>
|
||||
: <button
|
||||
className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
disabled={!instructions}
|
||||
type='submit'
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="19" x2="12" y2="5"></line>
|
||||
<polyline points="5 12 12 5 19 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,22 +32,23 @@ function ChatProvider({ children }: { children: ReactNode }) {
|
|||
}, [])
|
||||
|
||||
|
||||
const addMessageToHistory = (message: ChatMessage) => {
|
||||
let currentThread = !currentThreadId ? createNewThread() : allThreads[currentThreadId]
|
||||
setAllThreads((threads) => ({
|
||||
...threads,
|
||||
[currentThread.id]: {
|
||||
...currentThread,
|
||||
messages: [...currentThread.messages, message],
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<ChatContext.Provider
|
||||
value={{
|
||||
allThreads,
|
||||
addMessageToHistory,
|
||||
addMessageToHistory: (message: ChatMessage) => {
|
||||
let currentThread = currentThreadId === null ? createNewThread() : allThreads[currentThreadId]
|
||||
setAllThreads((threads) => ({
|
||||
...threads,
|
||||
[currentThread.id]: {
|
||||
...currentThread,
|
||||
messages: [...currentThread.messages, message],
|
||||
}
|
||||
}))
|
||||
getVSCodeAPI().postMessage({ type: "persistThread", thread: currentThread })
|
||||
},
|
||||
currentThread: currentThreadId !== null ? allThreads[currentThreadId] : null,
|
||||
switchToThread: (threadId: string) => { setCurrentThreadId(threadId); },
|
||||
startNewThread: () => {
|
||||
|
|
@ -51,7 +51,7 @@ export const FilesSelector = ({
|
|||
)
|
||||
}
|
||||
|
||||
export const IncludedFiles = ({ files }: { files: vscode.Uri[] }) => {
|
||||
export const SelectedFiles = ({ files }: { files: vscode.Uri[] }) => {
|
||||
return (
|
||||
files.length !== 0 && (
|
||||
<div className="text-xs my-2">
|
||||
|
|
@ -14,7 +14,7 @@ const awaiting: { [c in Command]: ((res: any) => void)[] } = {
|
|||
"getAllThreads": [],
|
||||
"allThreads": [],
|
||||
"persistThread": [],
|
||||
"openThreadSelector": []
|
||||
"toggleThreadSelector": []
|
||||
}
|
||||
|
||||
// use this function to await responses
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react"
|
||||
import * as ReactDOM from "react-dom/client"
|
||||
import Sidebar from "./Sidebar"
|
||||
import { ChatProvider } from "./context"
|
||||
import { ChatProvider } from "./chatContext"
|
||||
|
||||
// mount the sidebar on the id="root" element
|
||||
if (typeof document === "undefined") {
|
||||
|
|
|
|||
Loading…
Reference in a new issue