From 09d84044b55c0415bdb1d9c1a2fe3c443fa8ccac Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Oct 2024 21:38:17 -0700 Subject: [PATCH] monaco loads... need to adjust height --- extensions/void/build-tsx.js | 7 + extensions/void/package.json | 2 +- extensions/void/src/common/shared_types.ts | 3 +- extensions/void/src/extension.ts | 3 +- extensions/void/src/sidebar/SidebarChat.tsx | 85 ++++++-- .../src/sidebar/SidebarThreadSelector.tsx | 2 +- .../src/sidebar/components/SelectedFiles.tsx | 48 ----- .../void/src/sidebar/contextForThreads.tsx | 15 +- .../void/src/sidebar/markdown/BlockCode.tsx | 200 +++++------------- .../src/sidebar/markdown/MarkdownRender.tsx | 59 +++++- 10 files changed, 199 insertions(+), 225 deletions(-) delete mode 100644 extensions/void/src/sidebar/components/SelectedFiles.tsx 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") {