From 4f285ce364014c5e3048d3a5effee309cb07d60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aneta=20Jastrz=C4=99bska?= <1544710+anetaj@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:03:00 +0200 Subject: [PATCH 01/10] rearrange markdown components --- .../void/src/sidebar/MarkdownRender.tsx | 160 ----------------- .../sidebar/MarkdownRender/MarkdownRender.tsx | 164 ++++++++++++++++++ extensions/void/src/sidebar/Sidebar.tsx | 2 +- 3 files changed, 165 insertions(+), 161 deletions(-) delete mode 100644 extensions/void/src/sidebar/MarkdownRender.tsx create mode 100644 extensions/void/src/sidebar/MarkdownRender/MarkdownRender.tsx diff --git a/extensions/void/src/sidebar/MarkdownRender.tsx b/extensions/void/src/sidebar/MarkdownRender.tsx deleted file mode 100644 index e9cc2b96..00000000 --- a/extensions/void/src/sidebar/MarkdownRender.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { JSX, useState } from 'react'; -import { MarkedToken, Token, TokensList } from 'marked'; -import { awaitVSCodeResponse, getVSCodeAPI } from './getVscodeApi'; - - -// code block with Apply button at top -export const BlockCode = ({ text, disableApplyButton = false }: { text: string, disableApplyButton?: boolean }) => { - return
- {disableApplyButton ? null :
- -
} -
-
-				{text}
-			
-
-
-} - -const Render = ({ token }: { token: Token }) => { - - // deal with built-in tokens first (assume marked token) - const t = token as MarkedToken - - if (t.type === "space") { - return {t.raw}; - } - - if (t.type === "code") { - return - } - - if (t.type === "heading") { - const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements; - return {t.text}; - } - - if (t.type === "table") { - return ( - - - - {t.header.map((cell: any, index: number) => ( - - ))} - - - - {t.rows.map((row: any[], rowIndex: number) => ( - - {row.map((cell: any, cellIndex: number) => ( - - ))} - - ))} - -
- {cell.raw} -
- {cell.raw} -
- ); - } - - if (t.type === "hr") { - return
; - } - - if (t.type === "blockquote") { - return
{t.text}
; - } - - if (t.type === "list") { - - const ListTag = t.ordered ? 'ol' : 'ul'; - return ( - - {t.items.map((item, index) => ( -
  • - {item.task && ( - - )} - {item.text} -
  • - ))} -
    - ); - } - - if (t.type === "paragraph") { - return

    - {t.tokens.map((token, index) => ( - - ))} -

    ; - } - - if (t.type === "html") { - return
    {``}{t.raw}{``}
    ; - } - - if (t.type === "text" || t.type === "escape") { - return {t.raw}; - } - - if (t.type === "def") { - return null; // Definitions are typically not rendered - } - - if (t.type === "link") { - return {t.text}; - } - - if (t.type === "image") { - return {t.text}; - } - - if (t.type === "strong") { - return {t.text}; - } - - if (t.type === "em") { - return {t.text}; - } - - // inline code - if (t.type === "codespan") { - return {t.text}; - } - - if (t.type === "br") { - return
    ; - } - - if (t.type === "del") { - return {t.text}; - } - - - // default - return
    - Unknown type: - {t.raw} -
    ; -}; - -const MarkdownRender = ({ tokens }: { tokens: TokensList }) => { - return ( - <> - {tokens.map((token, index) => ( - - ))} - - ); -}; - -export default MarkdownRender; diff --git a/extensions/void/src/sidebar/MarkdownRender/MarkdownRender.tsx b/extensions/void/src/sidebar/MarkdownRender/MarkdownRender.tsx new file mode 100644 index 00000000..c972342e --- /dev/null +++ b/extensions/void/src/sidebar/MarkdownRender/MarkdownRender.tsx @@ -0,0 +1,164 @@ +import React, { JSX, useState } from "react" +import { MarkedToken, Token, TokensList } from "marked" +import { awaitVSCodeResponse, getVSCodeAPI } from "../getVscodeApi" +import BlockCode from "./BlockCode" + +const Render = ({ token }: { token: Token }) => { + // deal with built-in tokens first (assume marked token) + const t = token as MarkedToken + + if (t.type === "space") { + return {t.raw} + } + + if (t.type === "code") { + return + } + + if (t.type === "heading") { + const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements + return {t.text} + } + + if (t.type === "table") { + return ( + + + + {t.header.map((cell: any, index: number) => ( + + ))} + + + + {t.rows.map((row: any[], rowIndex: number) => ( + + {row.map((cell: any, cellIndex: number) => ( + + ))} + + ))} + +
    + {cell.raw} +
    + {cell.raw} +
    + ) + } + + if (t.type === "hr") { + return
    + } + + if (t.type === "blockquote") { + return
    {t.text}
    + } + + if (t.type === "list") { + const ListTag = t.ordered ? "ol" : "ul" + return ( + + {t.items.map((item, index) => ( +
  • + {item.task && ( + + )} + {item.text} +
  • + ))} +
    + ) + } + + if (t.type === "paragraph") { + return ( +

    + {t.tokens.map((token, index) => ( + + ))} +

    + ) + } + + if (t.type === "html") { + return ( +
    +				{``}
    +				{t.raw}
    +				{``}
    +			
    + ) + } + + if (t.type === "text" || t.type === "escape") { + return {t.raw} + } + + if (t.type === "def") { + return null // Definitions are typically not rendered + } + + if (t.type === "link") { + return ( + + {t.text} + + ) + } + + if (t.type === "image") { + return {t.text} + } + + if (t.type === "strong") { + return {t.text} + } + + if (t.type === "em") { + return {t.text} + } + + // inline code + if (t.type === "codespan") { + return ( + + {t.text} + + ) + } + + if (t.type === "br") { + return
    + } + + if (t.type === "del") { + return {t.text} + } + + // default + return ( +
    + Unknown type: + {t.raw} +
    + ) +} + +const MarkdownRender = ({ tokens }: { tokens: TokensList }) => { + return ( + <> + {tokens.map((token, index) => ( + + ))} + + ) +} + +export default MarkdownRender diff --git a/extensions/void/src/sidebar/Sidebar.tsx b/extensions/void/src/sidebar/Sidebar.tsx index 4cba1ccb..e828e4ba 100644 --- a/extensions/void/src/sidebar/Sidebar.tsx +++ b/extensions/void/src/sidebar/Sidebar.tsx @@ -4,7 +4,7 @@ import { Command, File, Selection, WebviewMessage } from "../shared_types" import { awaitVSCodeResponse, getVSCodeAPI, resolveAwaitingVSCodeResponse } from "./getVscodeApi" import { marked } from 'marked'; -import MarkdownRender, { BlockCode } from "./MarkdownRender"; +import { MarkdownRender, BlockCode } from "./MarkdownRender"; import * as vscode from 'vscode' From e05a3991772f2099e298761dbb57f71a543bad43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aneta=20Jastrz=C4=99bska?= <1544710+anetaj@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:03:31 +0200 Subject: [PATCH 02/10] add copy button --- .../src/sidebar/MarkdownRender/BlockCode.tsx | 74 +++++++++++++++++++ .../void/src/sidebar/MarkdownRender/index.ts | 2 + 2 files changed, 76 insertions(+) create mode 100644 extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx create mode 100644 extensions/void/src/sidebar/MarkdownRender/index.ts diff --git a/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx new file mode 100644 index 00000000..812c7e2a --- /dev/null +++ b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx @@ -0,0 +1,74 @@ +import React, { useCallback, useEffect, useState } from "react" +import { getVSCodeAPI } from "../getVscodeApi" + +enum CopyButtonState { + Copy = "Copy", + Copied = "Copied!", + Error = "Could not copy", +} + +const COPY_FEEDBACK_TIMEOUT = 2000 + +// code block with Apply button at top +const BlockCode = ({ + text, + disableApplyButton = false, +}: { + text: string + disableApplyButton?: boolean +}) => { + 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 ( +
    +
    +
    + {!disableApplyButton && ( + + )} + +
    +
    +
    +
    {text}
    +
    +
    + ) +} + +export default BlockCode diff --git a/extensions/void/src/sidebar/MarkdownRender/index.ts b/extensions/void/src/sidebar/MarkdownRender/index.ts new file mode 100644 index 00000000..f37e9ea5 --- /dev/null +++ b/extensions/void/src/sidebar/MarkdownRender/index.ts @@ -0,0 +1,2 @@ +export { default as MarkdownRender } from "./MarkdownRender" +export { default as BlockCode } from "./BlockCode" From e893dc03279a34553c9d75882052944013f3b944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aneta=20Jastrz=C4=99bska?= <1544710+anetaj@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:12:22 +0200 Subject: [PATCH 03/10] add default style for small button --- .../src/sidebar/MarkdownRender/BlockCode.tsx | 16 ++++---- extensions/void/src/sidebar/styles.css | 40 ++++++++++--------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx index 812c7e2a..e165ce1e 100644 --- a/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx +++ b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx @@ -7,7 +7,7 @@ enum CopyButtonState { Error = "Could not copy", } -const COPY_FEEDBACK_TIMEOUT = 2000 +const COPY_FEEDBACK_TIMEOUT = 1000 // code block with Apply button at top const BlockCode = ({ @@ -42,9 +42,15 @@ const BlockCode = ({
    + {!disableApplyButton && ( )} -
    Date: Sat, 21 Sep 2024 14:14:31 +0200 Subject: [PATCH 04/10] hide entire toolbar in user messages --- .../src/sidebar/MarkdownRender/BlockCode.tsx | 28 +++++++++---------- extensions/void/src/sidebar/Sidebar.tsx | 4 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx index e165ce1e..37d5b5bd 100644 --- a/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx +++ b/extensions/void/src/sidebar/MarkdownRender/BlockCode.tsx @@ -12,10 +12,10 @@ const COPY_FEEDBACK_TIMEOUT = 1000 // code block with Apply button at top const BlockCode = ({ text, - disableApplyButton = false, + hideToolbar = false, }: { text: string - disableApplyButton?: boolean + hideToolbar?: boolean }) => { const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy) @@ -40,15 +40,15 @@ const BlockCode = ({ return (
    -
    -
    - - {!disableApplyButton && ( + {!hideToolbar && ( +
    +
    + - )} +
    -
    + )}
    {text}
    diff --git a/extensions/void/src/sidebar/Sidebar.tsx b/extensions/void/src/sidebar/Sidebar.tsx index e828e4ba..04ed3020 100644 --- a/extensions/void/src/sidebar/Sidebar.tsx +++ b/extensions/void/src/sidebar/Sidebar.tsx @@ -82,7 +82,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => { if (role === 'user') { chatbubbleContents = <> - {chatMessage.selection?.selectionStr && } + {chatMessage.selection?.selectionStr && } {children} } @@ -272,7 +272,7 @@ const Sidebar = () => { > X - +
    )}
    From ac9bc80e2098651876d64a7a68b8bb764e665da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aneta=20Jastrz=C4=99bska?= <1544710+anetaj@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:01:49 +0200 Subject: [PATCH 05/10] style included files list --- extensions/void/src/sidebar/Sidebar.tsx | 160 +++++++----------- .../void/src/sidebar/components/Files.tsx | 77 +++++++++ 2 files changed, 138 insertions(+), 99 deletions(-) create mode 100644 extensions/void/src/sidebar/components/Files.tsx diff --git a/extensions/void/src/sidebar/Sidebar.tsx b/extensions/void/src/sidebar/Sidebar.tsx index 04ed3020..1aefaf47 100644 --- a/extensions/void/src/sidebar/Sidebar.tsx +++ b/extensions/void/src/sidebar/Sidebar.tsx @@ -1,12 +1,13 @@ -import React, { useState, ChangeEvent, useEffect, useRef, useCallback, FormEvent } from "react" -import { ApiConfig, LLMMessage, sendLLMMessage } from "../common/sendLLMMessage" -import { Command, File, Selection, WebviewMessage } from "../shared_types" +import React, { useState, useEffect, useRef, useCallback, FormEvent } from "react" +import { ApiConfig, sendLLMMessage } from "../common/sendLLMMessage" +import { File, Selection, WebviewMessage } from "../shared_types" import { awaitVSCodeResponse, getVSCodeAPI, resolveAwaitingVSCodeResponse } from "./getVscodeApi" import { marked } from 'marked'; import { MarkdownRender, BlockCode } from "./MarkdownRender"; import * as vscode from 'vscode' +import { FilesSelector, IncludedFiles } from "./components/Files"; const filesStr = (fullFiles: File[]) => { @@ -35,40 +36,6 @@ If you make a change, rewrite the entire file. } -const FilesSelector = ({ files, setFiles }: { files: vscode.Uri[], setFiles: (files: vscode.Uri[]) => void }) => { - return files.length !== 0 &&
    - Include files: - {files.map((filename, i) => -
    - {/* X button on a file */} - -
    - )} -
    -} - -const IncludedFiles = ({ files }: { files: vscode.Uri[] }) => { - return files.length !== 0 &&
    - {files.map((filename, i) => -
    - -
    - )} -
    -} - - const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => { const role = chatMessage.role @@ -99,13 +66,6 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
    } -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] -} - type ChatMessage = { role: 'user' content: string, // content sent to the llm @@ -258,62 +218,64 @@ const Sidebar = () => {
    {/* chatbar */}
    - {/* selection */} -
    - {/* selected files */} - - {/* selected code */} - {!selection?.selectionStr ? null - : ( -
    - - -
    - )} +
    + {/* selection */} +
    + {/* selected files */} + + {/* selected code */} + {!selection?.selectionStr ? null + : ( +
    + + +
    + )} +
    +
    { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }} + + onSubmit={(e) => { + console.log('submit!') + e.preventDefault(); + onSubmit(e) + }}> + {/* input */} + +