diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx
index 0d57ca81..65786cfe 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx
@@ -15,6 +15,9 @@ export type ChatMessageLocation = {
}
+const cn = (className: string) => className?.split(' ').map(c => c ? `void-${c}` : '').join(' ')
+
+
type ApplyBoxLocation = ChatMessageLocation & { tokenIdx: string }
const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => {
@@ -23,28 +26,93 @@ const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) =>
-export const CodeSpan = ({ children, className }: { children: React.ReactNode, className?: string }) => {
- return
- {children}
-
+// all classnames must go in tailwind.config.js/safelist
+export const noSpaceStyles = {
+ blockquote: 'pl-4 border-l-4 border-void-bg-2 italic',
+ br: '',
+ code: '',
+ codespan: 'bg-void-bg-1 px-1 rounded-sm font-mono font-medium break-all',
+ def: '',
+ del: 'line-through',
+ em: 'italic',
+ escape: '',
+ heading: {
+ h1: "text-4xl font-semibold pb-2 border-b border-void-bg-2",
+ h2: "text-3xl font-semibold pb-2 border-b border-void-bg-2",
+ h3: "text-2xl font-semibold",
+ h4: "text-xl font-semibold",
+ h5: "text-lg font-semibold",
+ h6: "text-base font-semibold text-gray-600"
+ },
+ hr: 'border-t border-void-bg-2',
+ html: '',
+ image: 'max-w-full h-auto rounded',
+ link: 'underline cursor-pointer',
+ list: 'list-inside pl-2',
+ list_item: '',
+ paragraph: '',
+ space: '',
+ strong: 'font-semibold',
+ table: 'overflow-x-auto',
+ text: '',
}
-const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, tokenIdx }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatMessageLocationForApply?: ChatMessageLocation, tokenIdx: string }): JSX.Element => {
+
+const defaultStyles = {
+ blockquote: 'mx-2 pl-4 border-l-4 border-void-bg-2 italic my-4',
+ br: '',
+ code: 'mx-2 my-4',
+ codespan: 'bg-void-bg-1 px-1 rounded-sm font-mono font-medium break-all',
+ def: '',
+ del: 'line-through',
+ em: 'italic',
+ escape: '',
+ heading: {
+ h1: 'mx-2 text-4xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2',
+ h2: 'mx-2 text-3xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2',
+ h3: 'mx-2 text-2xl font-semibold mt-6 mb-4',
+ h4: 'mx-2 text-xl font-semibold mt-6 mb-4',
+ h5: 'mx-2 text-lg font-semibold mt-6 mb-4',
+ h6: 'mx-2 text-base font-semibold mt-6 mb-4 text-gray-600'
+ },
+ hr: 'mx-2 my-6 border-t border-void-bg-2',
+ html: 'mx-2 my-4',
+ image: 'mx-2 my-4 max-w-full h-auto rounded',
+ link: 'mx-2 underline',
+ list: 'mx-2 my-2 list-inside pl-2',
+ list_item: 'mx-2 mb-2',
+ paragraph: 'mx-2 my-4',
+ space: '',
+ strong: 'mx-2 font-semibold',
+ table: 'mx-2 my-4 overflow-x-auto',
+ text: '',
+}
+
+
+
+type TokenClasses = typeof defaultStyles
+
+const RenderToken = ({ token, nested, chatMessageLocationForApply, tokenIdx, classes }: { token: Token | string, nested?: boolean, chatMessageLocationForApply?: ChatMessageLocation, tokenIdx: string, classes?: TokenClasses }): JSX.Element => {
// deal with built-in tokens first (assume marked token)
const t = token as MarkedToken
+ if(t.raw.trim() ===''){
+ return <>>;
+ }
+
+ // compute the className
+ const defaultClassName = defaultStyles[t.type]
+ const classNameOverride = classes?.[t.type]
+ const _className = classNameOverride ?? defaultClassName
+ let className: string = ''
+ if (typeof defaultClassName === 'string') {
+ className = _className as string
+ }
+
if (t.type === "space") {
- return {t.raw}
+ return {t.raw}
}
if (t.type === "code") {
@@ -55,32 +123,28 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
tokenIdx: tokenIdx,
}) : null
- return
+ return
}
- />
+ initValue={t.text}
+ language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]}
+ buttonsOnHover={applyBoxId &&
}
+ />
}
if (t.type === "heading") {
- const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements
- const headingClasses: { [h: string]: string } = {
- h1: "text-4xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2",
- h2: "text-3xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2",
- h3: "text-2xl font-semibold mt-6 mb-4",
- h4: "text-xl font-semibold mt-6 mb-4",
- h5: "text-lg font-semibold mt-6 mb-4",
- h6: "text-base font-semibold mt-6 mb-4 text-gray-600"
- }
- return
{t.text}
+
+ const HeadingTag = `h${t.depth}` as keyof typeof defaultStyles.heading
+
+ const className = classes?.heading[HeadingTag] ?? defaultStyles.heading[HeadingTag]
+
+ return
{t.text}
}
if (t.type === "table") {
return (
-
-
+
+
{t.header.map((cell: any, index: number) => (
@@ -100,7 +164,7 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
{row.map((cell: any, cellIndex: number) => (
{cell.raw}
@@ -115,22 +179,34 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
}
if (t.type === "hr") {
- return
+ return
}
if (t.type === "blockquote") {
- return {t.text}
+ return {t.text}
+ }
+
+ if (t.type === 'list_item') {
+
+
+ !!!!!!!!!!!!!
+
+
+
}
if (t.type === "list") {
const ListTag = t.ordered ? "ol" : "ul"
+
+ const itemClassName = classes?.['list_item'] ?? defaultStyles['list_item']
+
return (
{t.items.map((item, index) => (
-
+
{item.task && (
)}
@@ -145,10 +221,10 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
// return (
//
// {t.items.map((item, index) => (
- //
+ //
// {item.task && (
//
// )}
@@ -164,26 +240,26 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
if (t.type === "paragraph") {
const contents = <>
{t.tokens.map((token, index) => (
- // assign a unique tokenId to nested components
+ // assign a unique tokenId to nested components
))}
>
if (nested) return contents
- return
+ return
{contents}
}
if (t.type === "html") {
return (
-
+
{t.raw}
)
}
if (t.type === "text" || t.type === "escape") {
- return {t.raw}
+ return {t.raw}
}
if (t.type === "def") {
@@ -193,7 +269,7 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
if (t.type === "link") {
return (
{ window.open(t.href) }}
href={t.href}
title={t.title ?? undefined}
@@ -208,51 +284,52 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
src={t.href}
alt={t.text}
title={t.title ?? undefined}
- className={`max4w-full h-auto rounded ${noSpace ? '' : 'my-4'}`}
+ className={cn(className)}
/>
}
if (t.type === "strong") {
- return {t.text}
+ return {t.text}
}
if (t.type === "em") {
- return {t.text}
+ return {t.text}
}
// inline code
if (t.type === "codespan") {
return (
-
+
{t.text}
-
+
)
}
if (t.type === "br") {
- return
+ return
}
// strikethrough
if (t.type === "del") {
- return {t.text}
+ return {t.text}
}
// default
return (
Unknown type:
+ {t.type}
{t.raw}
)
}
-export const ChatMarkdownRender = ({ string, nested = false, noSpace, chatMessageLocationForApply }: { string: string, nested?: boolean, noSpace?: boolean, chatMessageLocationForApply?: ChatMessageLocation }) => {
+export const ChatMarkdownRender = ({ string, nested = false, classes, chatMessageLocationForApply }: { string: string, nested?: boolean, classes?: TokenClasses, chatMessageLocationForApply?: ChatMessageLocation }) => {
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
return (
<>
{tokens.map((token, index) => (
-
+
))}
>
)
diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx
index 6e21bc57..86df2e7c 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx
@@ -1,3 +1,6 @@
+//!!!! merged
+
+
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx
index 8e40d6c5..1ebbefc9 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx
@@ -15,7 +15,7 @@ import { isWindows, isLinux, isMacintosh } from '../../../../../../../base/commo
import { URI } from '../../../../../../../base/common/uri.js'
import { env } from '../../../../../../../base/common/process.js'
import { ModelDropdown } from './ModelDropdown.js'
-import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'
+import { ChatMarkdownRender, noSpaceStyles } from '../markdown/ChatMarkdownRender.js'
import { WarningBox } from './WarningBox.js'
import { os } from '../../../../common/helpers/systemInfo.js'
@@ -293,7 +293,7 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider
isPasswordField={isPasswordField}
/>
{subTextMd === undefined ? null :
-
+
}
@@ -413,11 +413,11 @@ export const FeaturesTab = () => {
{/* {`Void can access any model that you host locally. We automatically detect your local models by default.`} */}
{`Void can access any model that you host locally. We automatically detect your local models by default.`}
-
-
-
-
-
+
+
+
+
+
{/* TODO we should create UI for downloading models without user going into terminal */}
diff --git a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js
index 38a94f83..228b2847 100644
--- a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js
+++ b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js
@@ -167,6 +167,82 @@ module.exports = {
},
},
plugins: [],
- prefix: 'void-'
+ prefix: 'void-',
+ safelist: [
+ // Background colors
+ 'void-bg-void-bg-1',
+
+ // Borders
+ 'void-border-b',
+ 'void-border-l-4',
+ 'void-border-t',
+ 'void-border-void-bg-2',
+
+ // Typography
+ 'void-text-2xl',
+ 'void-text-3xl',
+ 'void-text-4xl',
+ 'void-text-base',
+ 'void-text-lg',
+ 'void-text-xl',
+ 'void-text-gray-600',
+ 'void-font-medium',
+ 'void-font-mono',
+ 'void-font-semibold',
+ 'void-italic',
+ 'void-line-through',
+ 'void-underline',
+
+ // Spacing
+ 'void-mt-1',
+ 'void-mt-2',
+ 'void-mt-4',
+ 'void-mt-6',
+ 'void-mb-1',
+ 'void-mb-2',
+ 'void-mb-4',
+ 'void-mx-1',
+ 'void-mx-2',
+ 'void-mx-4',
+ 'void-my-1',
+ 'void-my-2',
+ 'void-my-4',
+ 'void-my-6',
+ 'void-pb-1',
+ 'void-pb-2',
+ 'void-pb-4',
+ 'void-pl-1',
+ 'void-pl-2',
+ 'void-pl-4',
+ 'void-px-1',
+ 'void-px-2',
+ 'void-px-4',
+
+ // Sizing and layout
+ 'void-h-auto',
+ 'void-max-w-full',
+ 'void-overflow-x-auto',
+
+ // Lists
+ 'void-list-inside',
+ 'void-list-decimal',
+ 'void-list-disc',
+
+ // Effects and decoration
+ 'void-cursor-pointer',
+ 'void-ring-8',
+ 'void-ring-[#123456]',
+ 'void-rounded',
+ 'void-rounded-sm',
+
+ // misc
+ 'void-break-all',
+ 'void-bg-void-bg-1',
+ 'void-px-1',
+ 'void-rounded-sm',
+ 'void-font-mono',
+ 'void-font-medium',
+ 'void-break-all'
+ ]
}
|