Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions webview-ui/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ module.exports = {
"^@vscode/webview-ui-toolkit/react$": "<rootDir>/src/__mocks__/@vscode/webview-ui-toolkit/react.ts",
"^@/(.*)$": "<rootDir>/src/$1",
'^@roo/(.*)$': '<rootDir>/../src/$1',
'^@src/(.*)$': '<rootDir>/src/$1',
'^@src/(.*)$': '<rootDir>/src/$1',
"^src/i18n/setup$": "<rootDir>/src/__mocks__/i18n/setup.ts",
"^\\.\\./setup$": "<rootDir>/src/__mocks__/i18n/setup.ts",
"^\\./setup$": "<rootDir>/src/__mocks__/i18n/setup.ts",
"^src/i18n/TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^\\.\\./TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^\\./TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx"
"^\\./TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^unist-util-visit$": "<rootDir>/src/__mocks__/unist-util-visit.ts",
"^shiki$": "<rootDir>/src/__mocks__/shiki.ts",
"^mermaid$": "<rootDir>/src/__mocks__/mermaid.ts"
},
reporters: [["jest-simple-dot-reporter", {}]],
transformIgnorePatterns: [
Expand Down
28 changes: 28 additions & 0 deletions webview-ui/src/__mocks__/mermaid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const mermaid = {
initialize: jest.fn(),
render: jest.fn().mockResolvedValue({ svg: "<svg></svg>" }),
parse: jest.fn(),
parseDirective: jest.fn(),
registerExternalDiagrams: jest.fn(),
contentLoaded: jest.fn(),
setParseErrorHandler: jest.fn(),
getDiagramFromText: jest.fn(),
getConfig: jest.fn().mockReturnValue({}),
setConfig: jest.fn(),
getSiteConfig: jest.fn(),
updateSiteConfig: jest.fn(),
reset: jest.fn(),
startOnLoad: true,
mermaidAPI: {
render: jest.fn().mockResolvedValue({ svg: "<svg></svg>" }),
parse: jest.fn(),
initialize: jest.fn(),
getConfig: jest.fn().mockReturnValue({}),
setConfig: jest.fn(),
getSiteConfig: jest.fn(),
updateSiteConfig: jest.fn(),
reset: jest.fn(),
},
}

export default mermaid
22 changes: 22 additions & 0 deletions webview-ui/src/__mocks__/shiki.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const bundledLanguages = {
javascript: {},
typescript: {},
html: {},
css: {},
json: {},
markdown: {},
}

export type ShikiTransformer = {
name: string
transform: (hast: any, options: any) => any
}

export const codeToHast = jest.fn()
export const codeToHtml = jest.fn()
export const codeToTokens = jest.fn()
export const codeToTokensBase = jest.fn()
export const codeToTokensWithThemes = jest.fn()
export const createHighlighter = jest.fn()
export const getLastGrammarState = jest.fn()
export const getSingletonHighlighter = jest.fn()
5 changes: 5 additions & 0 deletions webview-ui/src/__mocks__/unist-util-visit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const visit = jest.fn((tree, _nodeType, _visitor) => tree)

export const CONTINUE = Symbol("continue")
export const EXIT = Symbol("exit")
export const SKIP = Symbol("skip")
7 changes: 4 additions & 3 deletions webview-ui/src/components/chat/TaskHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { ClineMessage } from "@roo/shared/ExtensionMessage"

import { getMaxTokensForModel } from "@src/utils/model-utils"
import { formatLargeNumber } from "@src/utils/format"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
import { cn } from "@src/lib/utils"
import { Button } from "@src/components/ui"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel"
import { MarkdownBlock } from "@src/components/common/MarkdownBlock"

import Thumbnails from "../common/Thumbnails"

Expand Down Expand Up @@ -115,7 +116,7 @@ const TaskHeader = ({
WebkitLineClamp: "unset",
WebkitBoxOrient: "vertical",
}}>
<Mention text={task.text} />
<MarkdownBlock markdown={task.text} renderMentions />
</div>
</div>
{task.images && task.images.length > 0 && <Thumbnails images={task.images} />}
Expand Down
178 changes: 178 additions & 0 deletions webview-ui/src/components/common/CodeBlock.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import styled from "styled-components"

export const CODE_BLOCK_BG_COLOR = "var(--vscode-editor-background, --vscode-sideBar-background, rgb(30 30 30))"

const WRAPPER_ALPHA = "cc"

export const ButtonIcon = styled.span`
display: inline-block;
width: 1.2em;
text-align: center;
vertical-align: middle;
`

export const CodeBlockButton = styled.button`
background: transparent;
border: none;
color: var(--vscode-foreground);
cursor: var(--copy-button-cursor, default);
padding: 4px;
margin: 0 0px;
display: flex;
align-items: center;
opacity: 0.4;
border-radius: 3px;
pointer-events: var(--copy-button-events, none);
margin-left: 4px;
height: 24px;

&:hover {
background: var(--vscode-toolbar-hoverBackground);
opacity: 1;
}
`

export const CodeBlockButtonWrapper = styled.div`
position: fixed;
top: var(--copy-button-top);
right: var(--copy-button-right, 8px);
height: auto;
z-index: 100;
background: ${CODE_BLOCK_BG_COLOR}${WRAPPER_ALPHA};
overflow: visible;
pointer-events: none;
opacity: var(--copy-button-opacity, 0);
padding: 4px 6px;
border-radius: 3px;
display: inline-flex;
align-items: center;
justify-content: center;

&:hover {
background: var(--vscode-editor-background);
opacity: 1 !important;
}

${CodeBlockButton} {
position: relative;
top: 0;
right: 0;
}
`

export const CodeBlockContainer = styled.div`
position: relative;
overflow: hidden;
border-bottom: 4px solid var(--vscode-sideBar-background);
background-color: ${CODE_BLOCK_BG_COLOR};

${CodeBlockButtonWrapper} {
opacity: 0;
pointer-events: none;
transition: opacity 0.2s; /* Keep opacity transition for buttons */
}

&[data-partially-visible="true"]:hover ${CodeBlockButtonWrapper} {
opacity: 1;
pointer-events: all;
cursor: pointer;
}
`

export const StyledPre = styled.div<{
preStyle?: React.CSSProperties
wordwrap?: "true" | "false" | undefined
windowshade?: "true" | "false"
collapsedHeight?: number
}>`
background-color: ${CODE_BLOCK_BG_COLOR};
max-height: ${({ windowshade, collapsedHeight = 500 }) =>
windowshade === "true" ? `${collapsedHeight}px` : "none"};
overflow-y: auto;
padding: 10px;
border-radius: 5px;
${({ preStyle }) => preStyle && { ...preStyle }}

pre {
background-color: ${CODE_BLOCK_BG_COLOR};
border-radius: 5px;
margin: 0;
padding: 10px;
width: 100%;
box-sizing: border-box;
}

pre,
code {
/* Undefined wordwrap defaults to true (pre-wrap) behavior */
white-space: ${({ wordwrap }) => (wordwrap === "false" ? "pre" : "pre-wrap")};
word-break: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "normal")};
overflow-wrap: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "break-word")};
font-size: var(--vscode-editor-font-size, var(--vscode-font-size, 12px));
font-family: var(--vscode-editor-font-family);
}

pre > code {
.hljs-deletion {
background-color: var(--vscode-diffEditor-removedTextBackground);
display: inline-block;
width: 100%;
}
.hljs-addition {
background-color: var(--vscode-diffEditor-insertedTextBackground);
display: inline-block;
width: 100%;
}
}

.hljs {
color: var(--vscode-editor-foreground, #fff);
background-color: ${CODE_BLOCK_BG_COLOR};
}
`

export const LanguageSelect = styled.select`
font-size: 12px;
color: var(--vscode-foreground);
opacity: 0.4;
font-family: monospace;
appearance: none;
background: transparent;
border: none;
cursor: pointer;
padding: 4px;
margin: 0;
vertical-align: middle;
height: 24px;

& option {
background: var(--vscode-editor-background);
color: var(--vscode-foreground);
padding: 0;
margin: 0;
}

&::-webkit-scrollbar {
width: 6px;
}

&::-webkit-scrollbar-thumb {
background: var(--vscode-scrollbarSlider-background);
}

&::-webkit-scrollbar-track {
background: var(--vscode-editor-background);
}

&:hover {
opacity: 1;
background: var(--vscode-toolbar-hoverBackground);
border-radius: 3px;
}

&:focus {
opacity: 1;
outline: none;
border-radius: 3px;
}
`
Loading