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 :
-
-
}
-
-
-}
-
-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) => (
- |
- {cell.raw}
- |
- ))}
-
-
-
- {t.rows.map((row: any[], rowIndex: number) => (
-
- {row.map((cell: any, cellIndex: number) => (
- |
- {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
;
- }
-
- 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 e96006d0..cfc3e3b4 100644
--- a/extensions/void/src/sidebar/Sidebar.tsx
+++ b/extensions/void/src/sidebar/Sidebar.tsx
@@ -4,7 +4,8 @@ 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 from "./markdown/MarkdownRender";
+import BlockCode from "./markdown/BlockCode";
import * as vscode from 'vscode'
@@ -82,7 +83,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
if (role === 'user') {
chatbubbleContents = <>
- {chatMessage.selection?.selectionStr && }
+ {chatMessage.selection?.selectionStr && }
{children}
>
}
@@ -278,7 +279,7 @@ const Sidebar = () => {
>
X
-
+
)}
diff --git a/extensions/void/src/sidebar/markdown/BlockCode.tsx b/extensions/void/src/sidebar/markdown/BlockCode.tsx
new file mode 100644
index 00000000..5c45a9f5
--- /dev/null
+++ b/extensions/void/src/sidebar/markdown/BlockCode.tsx
@@ -0,0 +1,72 @@
+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 = 1000
+
+// code block with toolbar (Apply, Copy, etc) at top
+const BlockCode = ({
+ text,
+ hideToolbar = false,
+}: {
+ text: string
+ hideToolbar?: 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 (
+
+ {!hideToolbar && (
+
+
+
+
+
+
+ )}
+
+
+ )
+}
+
+export default BlockCode
diff --git a/extensions/void/src/sidebar/markdown/MarkdownRender.tsx b/extensions/void/src/sidebar/markdown/MarkdownRender.tsx
new file mode 100644
index 00000000..c972342e
--- /dev/null
+++ b/extensions/void/src/sidebar/markdown/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) => (
+ |
+ {cell.raw}
+ |
+ ))}
+
+
+
+ {t.rows.map((row: any[], rowIndex: number) => (
+
+ {row.map((cell: any, cellIndex: number) => (
+ |
+ {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
+ }
+
+ 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/styles.css b/extensions/void/src/sidebar/styles.css
index 8e6ff79f..6a1d2159 100644
--- a/extensions/void/src/sidebar/styles.css
+++ b/extensions/void/src/sidebar/styles.css
@@ -3,33 +3,37 @@
@tailwind utilities;
html {
- font-size: var(--vscode-font-size);
+ font-size: var(--vscode-font-size);
}
.btn {
- @apply cursor-pointer transition-colors;
+ @apply cursor-pointer transition-colors;
- &.btn-primary {
- @apply bg-vscode-button-bg text-vscode-button-fg;
+ &.btn-primary {
+ @apply bg-vscode-button-bg text-vscode-button-fg;
- &:not(:disabled) {
- @apply hover:bg-vscode-button-hoverBg;
- }
- }
+ &:not(:disabled) {
+ @apply hover:bg-vscode-button-hoverBg;
+ }
+ }
- &.btn-secondary {
- @apply bg-vscode-button-secondary-bg text-vscode-button-secondary-fg;
+ &.btn-sm {
+ @apply px-3 py-1 text-sm;
+ }
- &:not(:disabled) {
- @apply hover:bg-vscode-button-secondary-hoverBg;
- }
- }
+ &.btn-secondary {
+ @apply bg-vscode-button-secondary-bg text-vscode-button-secondary-fg;
- &:disabled {
- @apply opacity-75 cursor-not-allowed;
- }
+ &:not(:disabled) {
+ @apply hover:bg-vscode-button-secondary-hoverBg;
+ }
+ }
+
+ &:disabled {
+ @apply opacity-75 cursor-not-allowed;
+ }
}
.input {
- @apply bg-vscode-input-bg text-vscode-input-fg border-vscode-input-border;
+ @apply bg-vscode-input-bg text-vscode-input-fg border-vscode-input-border;
}