diff --git a/extensions/void/build-tsx.js b/extensions/void/build-tsx.js
index 3faf04f0..d47616d0 100644
--- a/extensions/void/build-tsx.js
+++ b/extensions/void/build-tsx.js
@@ -5,6 +5,13 @@ esbuild.build({
entryPoints: ['src/sidebar/index.tsx'],
bundle: true,
minify: true,
+ loader: {
+ '.ttf': 'file',
+ '.woff': 'file',
+ '.woff2': 'file',
+ '.eot': 'file',
+ '.svg': 'file',
+ },
sourcemap: true,
outfile: 'dist/sidebar/index.js',
format: 'iife', // apparently iife is safe for browsers (safer than cjs)
diff --git a/extensions/void/package.json b/extensions/void/package.json
index ad63c2e2..531357e1 100644
--- a/extensions/void/package.json
+++ b/extensions/void/package.json
@@ -101,7 +101,7 @@
}
},
"scripts": {
- "vscode:prepublish": "npm run compile",
+ "vscode:prepublish": "echo \"running prepublish\"",
"watch": "tsc -watch -p ./",
"build": "rimraf dist && node build-tsx.js && node build-css.js",
"pretest": "tsc -p ./ && eslint src --ext ts",
diff --git a/extensions/void/src/common/shared_types.ts b/extensions/void/src/common/shared_types.ts
index 3c59f888..ae8363d6 100644
--- a/extensions/void/src/common/shared_types.ts
+++ b/extensions/void/src/common/shared_types.ts
@@ -65,7 +65,8 @@ type MessageFromSidebar = (
type ChatThreads = {
[id: string]: {
id: string; // store the id here too
- createdAt: string;
+ createdAt: string; // ISO string
+ lastModified: string; // ISO string
messages: ChatMessage[];
}
}
diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts
index 961d585c..7a55caa8 100644
--- a/extensions/void/src/extension.ts
+++ b/extensions/void/src/extension.ts
@@ -83,7 +83,8 @@ export function activate(context: vscode.ExtensionContext) {
// send contents to webview
webview.postMessage({ type: 'files', files, } satisfies MessageToSidebar)
- } else if (m.type === 'applyChanges') {
+ }
+ else if (m.type === 'applyChanges') {
const editor = vscode.window.activeTextEditor
if (!editor) {
diff --git a/extensions/void/src/sidebar/SidebarChat.tsx b/extensions/void/src/sidebar/SidebarChat.tsx
index bfd89da1..be668c40 100644
--- a/extensions/void/src/sidebar/SidebarChat.tsx
+++ b/extensions/void/src/sidebar/SidebarChat.tsx
@@ -4,7 +4,6 @@ import React, { FormEvent, useCallback, useEffect, useRef, useState } from "reac
import { marked } from 'marked';
import MarkdownRender from "./markdown/MarkdownRender";
import BlockCode from "./markdown/BlockCode";
-import { SelectedFiles } from "./components/SelectedFiles";
import { File, ChatMessage, CodeSelection } from "../common/shared_types";
import * as vscode from 'vscode'
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "./getVscodeApi";
@@ -63,6 +62,55 @@ Please edit the selected code following these instructions:
return str;
};
+
+
+
+
+const getBasename = (pathStr: string) => {
+ // "unixify" path
+ pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with /
+ const parts = pathStr.split("/") // split on /
+ return parts[parts.length - 1]
+}
+
+export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => {
+ return (
+ files.length !== 0 && (
+
+ {files.map((filename, i) => (
+
+ ))}
+
+ )
+ )
+}
+
+
const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
const role = chatMessage.role
@@ -76,16 +124,17 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
if (role === 'user') {
chatbubbleContents = <>
- {chatMessage.selection?.selectionStr && }
+ {chatMessage.selection?.selectionStr && }
{children}
>
}
else if (role === 'assistant') {
-
chatbubbleContents = // sectionsHTML
}
-
return
{chatbubbleContents}
@@ -224,8 +273,8 @@ export const SidebarChat = () => {
abortFnRef.current?.()
// if messageStream was not empty, add it to the history
- const llmContent = messageStream || '(canceled)'
- const newHistoryElt: ChatMessage = { role: 'assistant', displayContent: messageStream, content: llmContent }
+ const llmContent = messageStream || '(null)'
+ const newHistoryElt: ChatMessage = { role: 'assistant', content: llmContent, displayContent: messageStream, }
addMessageToHistory(newHistoryElt)
setMessageStream('')
@@ -233,14 +282,9 @@ export const SidebarChat = () => {
}, [captureChatEvent, messageStream, addMessageToHistory])
- //Clear code selection
- const clearSelection = () => {
- setSelection(null);
- };
-
return <>
-
+
{/* previous messages */}
{currentThread !== null && currentThread.messages.map((message, i) =>
@@ -261,14 +305,15 @@ export const SidebarChat = () => {
{/* selected code */}
{!!selection?.selectionStr && (
-
- Remove
-
- )} />
+ setSelection(null)}
+ className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
+ >
+ Remove
+
+ )} />
)}
}
diff --git a/extensions/void/src/sidebar/SidebarThreadSelector.tsx b/extensions/void/src/sidebar/SidebarThreadSelector.tsx
index 323d66e5..5f88eac8 100644
--- a/extensions/void/src/sidebar/SidebarThreadSelector.tsx
+++ b/extensions/void/src/sidebar/SidebarThreadSelector.tsx
@@ -15,7 +15,7 @@ export const SidebarThreadSelector = ({ onClose }: { onClose: () => void }) => {
const { allThreads, currentThread, switchToThread } = useThreads()
// sorted by most recent to least recent
- const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].createdAt > allThreads![threadId2].createdAt ? -1 : 1)
+ const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? 1 : -1)
return (
diff --git a/extensions/void/src/sidebar/components/SelectedFiles.tsx b/extensions/void/src/sidebar/components/SelectedFiles.tsx
deleted file mode 100644
index 0d5b478e..00000000
--- a/extensions/void/src/sidebar/components/SelectedFiles.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from "react"
-import * as vscode from "vscode"
-
-const getBasename = (pathStr: string) => {
- // "unixify" path
- pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with /
- const parts = pathStr.split("/") // split on /
- return parts[parts.length - 1]
-}
-
-export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => {
- return (
- files.length !== 0 && (
-
- {files.map((filename, i) => (
-
- ))}
-
- )
- )
-}
-
-
diff --git a/extensions/void/src/sidebar/contextForThreads.tsx b/extensions/void/src/sidebar/contextForThreads.tsx
index 41cd6d48..b8b1bfe9 100644
--- a/extensions/void/src/sidebar/contextForThreads.tsx
+++ b/extensions/void/src/sidebar/contextForThreads.tsx
@@ -14,11 +14,15 @@ type ConfigForThreadsValueType = {
const ThreadsContext = createContext
(undefined as unknown as ConfigForThreadsValueType)
-const createNewThread = () => ({
- id: new Date().getTime().toString(),
- createdAt: new Date().toISOString(),
- messages: [],
-})
+const createNewThread = () => {
+ const now = new Date().toISOString()
+ return {
+ id: new Date().getTime().toString(),
+ createdAt: now,
+ lastModified: now,
+ messages: [],
+ }
+}
// const [stateRef, setState] = useInstantState(initVal)
@@ -67,6 +71,7 @@ export function ThreadsProvider({ children }: { children: ReactNode }) {
...allThreads.current,
[currentThread.id]: {
...currentThread,
+ lastModified: new Date().toISOString(),
messages: [...currentThread.messages, message],
}
})
diff --git a/extensions/void/src/sidebar/markdown/BlockCode.tsx b/extensions/void/src/sidebar/markdown/BlockCode.tsx
index af08deba..811810b5 100644
--- a/extensions/void/src/sidebar/markdown/BlockCode.tsx
+++ b/extensions/void/src/sidebar/markdown/BlockCode.tsx
@@ -1,162 +1,70 @@
import React, { ReactNode, useCallback, useEffect, useState } from "react"
-import { getVSCodeAPI } from "../getVscodeApi"
-
-import SyntaxHighlighter from "react-syntax-highlighter";
-import { atomOneDarkReasonable } from "react-syntax-highlighter/dist/esm/styles/hljs";
import MonacoEditor from '@monaco-editor/react'
-import { editor } from 'monaco-editor'
+import * as monaco from 'monaco-editor';
+import { loader } from '@monaco-editor/react';
-enum CopyButtonState {
- Copy = "Copy",
- Copied = "Copied!",
- Error = "Could not copy",
-}
-
-const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
-
+loader.config({ monaco });
// code block with toolbar (Apply, Copy, etc) at top
-const BlockCode = ({
- text,
- language,
- toolbar,
- hideToolbar = false,
- className,
-}: {
- text: string
- language?: string
- toolbar?: ReactNode
- hideToolbar?: boolean
- className?: string
-}) => {
- const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
-
- const customStyle = {
- ...atomOneDarkReasonable,
- 'code[class*="language-"]': {
- ...atomOneDarkReasonable['code[class*="language-"]'],
- background: "none",
- },
- }
-
- useEffect(() => {
- if (copyButtonState !== CopyButtonState.Copy) {
- setTimeout(() => {
- setCopyButtonState(CopyButtonState.Copy)
- }, COPY_FEEDBACK_TIMEOUT)
- }
- }, [copyButtonState])
-
- const onCopy = useCallback(() => {
- navigator.clipboard.writeText(text).then(
- () => {
- setCopyButtonState(CopyButtonState.Copied)
- },
- () => {
- setCopyButtonState(CopyButtonState.Error)
- }
- )
- }, [text])
-
- const defaultToolbar = (
- <>
-
-
- >
- )
+const BlockCode = ({ text, language, buttonsOnHover, }: { text: string, language?: string, buttonsOnHover?: ReactNode, }) => {
return (<>
+
-
{
-
- const model = editor.getModel()
- model?.setEOL(monaco.editor.EndOfLineSequence.LF)
-
- // model?.updateOptions({ tabSize: 4 }) // apparently this should get set on the model, not the editor ()
-
- monaco?.editor.setTheme('dark')
-
-
- }}
- loading=''
- value={text}
- defaultValue={text}
- language={language}
- defaultLanguage={language}
-
- // onChange={() => { onChangeText?.() }}
- height={'100%'} // 100% or the exact pixel height
- theme={'dark'}
-
-
- options={{
- matchBrackets: 'always',
- detectIndentation: false, // we always want a tab size of 4
- tabSize: 4,
- insertSpaces: true,
-
- // fontSize: 15,
- wordWrapColumn: 10000, // we want this to be infinity
- // automaticLayout: true,
- wordWrap: 'bounded', // 'off'
- // wordBreak: 'keepAll',
- // automaticLayout: true,
- // lineDecorationsWidth: 0,
- lineNumbersMinChars: 4,
- lineNumbers: 'off',
- renderLineHighlight: 'none',
- minimap: { enabled: false },
- scrollBeyondLastColumn: 0,
- scrollBeyondLastLine: false,
- scrollbar: {
- alwaysConsumeMouseWheel: false, //height !== undefined
- // vertical: 'hidden',
- // horizontal: 'hidden'
- },
-
- overviewRulerLanes: 0,
- readOnly: true,
- readOnlyMessage: undefined,
- quickSuggestions: false,
-
- }}
- />
-
-
-
-
- {!hideToolbar && (
-
-
{toolbar || defaultToolbar}
+ {!toolbar ? null : (
+
+
{buttonsOnHover === null ? null : buttonsOnHover}
)}
-
-
- {text}
-
-
+
{
+ const model = editor.getModel()
+ model?.setEOL(monaco.editor.EndOfLineSequence.LF)
+ monaco?.editor.setTheme('vs-dark')
+ }}
+ loading='loading'
+ value={text}
+ language={language}
+
+ // onChange={() => { onChangeText?.() }}
+ height={'100%'} // 100% or the exact pixel height
+ theme={'vs-dark'}
+
+ options={{
+ matchBrackets: 'always',
+ detectIndentation: false, // we always want a tab size of 4
+ tabSize: 4,
+ insertSpaces: true,
+
+ // fontSize: 15,
+ wordWrapColumn: 10000, // we want this to be infinity
+ // automaticLayout: true,
+ wordWrap: 'bounded', // 'off'
+ // wordBreak: 'keepAll',
+ // automaticLayout: true,
+ // lineDecorationsWidth: 0,
+ lineNumbersMinChars: 4,
+ lineNumbers: 'off',
+ renderLineHighlight: 'none',
+ minimap: { enabled: false },
+ scrollBeyondLastColumn: 0,
+ scrollBeyondLastLine: false,
+ scrollbar: {
+ alwaysConsumeMouseWheel: true, // height !== undefined
+ // vertical: 'hidden',
+ // horizontal: 'hidden'
+ },
+
+ overviewRulerLanes: 0,
+ readOnly: true,
+ readOnlyMessage: undefined,
+ quickSuggestions: false,
+
+ }}
+ />
>
)
diff --git a/extensions/void/src/sidebar/markdown/MarkdownRender.tsx b/extensions/void/src/sidebar/markdown/MarkdownRender.tsx
index 79e6e91d..bc02ebf4 100644
--- a/extensions/void/src/sidebar/markdown/MarkdownRender.tsx
+++ b/extensions/void/src/sidebar/markdown/MarkdownRender.tsx
@@ -1,6 +1,57 @@
-import React, { JSX } from "react"
+import React, { JSX, useCallback, useEffect, useState } from "react"
import { marked, MarkedToken, Token, TokensList } from "marked"
import BlockCode from "./BlockCode"
+import { getVSCodeAPI } from "../getVscodeApi"
+
+
+enum CopyButtonState {
+ Copy = "Copy",
+ Copied = "Copied!",
+ Error = "Could not copy",
+}
+
+const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
+
+const CodeButtonsOnHover = ({ text }: { text: string }) => {
+ const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
+
+ useEffect(() => {
+ if (copyButtonState !== CopyButtonState.Copy) {
+ setTimeout(() => {
+ setCopyButtonState(CopyButtonState.Copy)
+ }, COPY_FEEDBACK_TIMEOUT)
+ }
+ }, [copyButtonState])
+
+ const onCopy = useCallback(() => {
+ navigator.clipboard.writeText(text).then(
+ () => {
+ setCopyButtonState(CopyButtonState.Copied)
+ },
+ () => {
+ setCopyButtonState(CopyButtonState.Error)
+ }
+ )
+ }, [text])
+
+ return <>
+
+
+ >
+}
+
const RenderToken = ({ token, nested = false }: { token: Token | string, nested?: boolean }): JSX.Element => {
@@ -12,7 +63,11 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
}
if (t.type === "code") {
- return
+ return
}
+ />
}
if (t.type === "heading") {