Compare commits

...
Author SHA1 Message Date
Evan 76c752a78b reopen pr 2025-02-18 14:12:52 -08:00
Evan 7ff8f9428c random change 2025-02-18 14:11:17 -08:00
Evan Fannin 293a7473d5 revert random change 2025-02-18 14:05:41 -08:00
Evan Fannin 25f93e8f56 reopen pr 2025-02-18 14:03:07 -08:00
Saoud Rizwan 97b005070e Update system.ts 2025-02-18 12:28:53 -08:00
Evan 84c7a7a23b updated system prompt 2025-02-17 17:39:37 -08:00
Evan 7aa68aaa10 remove regular markdown background styling for mermaid blocks 2025-02-17 16:35:14 -08:00
Evan 798429ba5b fix package-lock 2025-02-17 14:21:39 -08:00
Evan 512198788c clean up 2025-02-17 11:40:59 -08:00
Evan de92e0eede update webview package.lock 2025-02-17 11:16:56 -08:00
Evan 9dd9343f25 remove rehype-mermaid 2025-02-17 11:14:16 -08:00
Evan 3b1c43e810 added changeset 2025-02-17 10:47:42 -08:00
Evan 90747ae149 merge conflicts 2025-02-17 10:41:49 -08:00
Evan b44061f230 better mermaid theme for visibility 2025-02-16 22:46:33 -08:00
Evan f57a19a9a2 replace tag symbols when rendering code 2025-02-16 22:46:33 -08:00
Evan 22443d7566 add loading state; clean up styling 2025-02-16 22:46:33 -08:00
Evan 9113338bff fix bouncy screen by debouncing mermaid parsing 2025-02-16 22:46:33 -08:00
Evan f4f2d511d1 fix render failure on streaming 2025-02-16 22:46:33 -08:00
Evan 4e84477197 rendering mermaid graphs 2025-02-16 22:46:33 -08:00
Evan 8b54e8e75e install dependencies 2025-02-16 22:46:33 -08:00
Evan 46fad7dbd0 wip 2025-02-16 22:45:58 -08:00
7 changed files with 1278 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Add support for rendering mermaid graphs in the chat.
+3 -2
View File
@@ -865,9 +865,10 @@ In each user message, the environment_details will specify the current mode. The
## What is PLAN MODE?
- While you are usually in ACT MODE, the user may switch to PLAN MODE in order to have a back and forth with you to plan how to best accomplish the task.
- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task.
- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task.
- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task. You may return mermaid diagrams to visually display your understanding.
- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task. Returning mermaid diagrams may be helpful here as well.
- Then you might ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response.
- Finally once it seems like you've reached a good plan, ask the user to switch you back to ACT MODE to implement the solution.
====
+1109 -1
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -9,6 +9,7 @@
"fast-deep-equal": "^3.1.3",
"fuse.js": "^7.0.0",
"fzf": "^0.5.2",
"mermaid": "^11.4.1",
"pretty-bytes": "^6.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -1,10 +1,11 @@
import { memo, useEffect } from "react"
import React, { memo, useEffect } from "react"
import { useRemark } from "react-remark"
import rehypeHighlight, { Options } from "rehype-highlight"
import styled from "styled-components"
import { visit } from "unist-util-visit"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { CODE_BLOCK_BG_COLOR } from "./CodeBlock"
import MermaidBlock from "./MermaidBlock"
interface MarkdownBlockProps {
markdown?: string
@@ -220,7 +221,27 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
],
rehypeReactOptions: {
components: {
pre: ({ node, ...preProps }: any) => <StyledPre {...preProps} theme={theme} />,
pre: ({ node, children, ...preProps }: any) => {
if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) {
const child = children[0] as React.ReactElement<{ className?: string }>
if (child.props?.className?.includes("language-mermaid")) {
return child
}
}
return (
<StyledPre {...preProps} theme={theme}>
{children}
</StyledPre>
)
},
code: (props: any) => {
const className = props.className || ""
if (className.includes("language-mermaid")) {
const codeText = String(props.children || "")
return <MermaidBlock code={codeText} />
}
return <code {...props} />
},
},
},
})
@@ -0,0 +1,95 @@
import { useEffect, useRef, useState } from "react"
import mermaid from "mermaid"
import { useDebounceEffect } from "../../utils/useDebounceEffect"
import styled from "styled-components"
mermaid.initialize({
startOnLoad: false,
securityLevel: "loose",
theme: "dark",
themeVariables: {
background: "#1e1e1e",
textColor: "#ffffff", // make text much brighter
mainBkg: "#2d2d2d",
lineColor: "#cccccc", // light enough for contrast
fontSize: "16px",
primaryColor: "#3c3c3c", // node fill color, etc.
},
})
interface MermaidBlockProps {
code: string
}
export default function MermaidBlock({ code }: MermaidBlockProps) {
const containerRef = useRef<HTMLDivElement>(null)
const [isLoading, setIsLoading] = useState(false)
// 1) Whenever `code` changes, mark that we need to re-render a new chart
useEffect(() => {
setIsLoading(true)
}, [code])
// 2) Debounce the actual parse/render
useDebounceEffect(
() => {
if (containerRef.current) {
containerRef.current.innerHTML = ""
}
mermaid
.parse(code, { suppressErrors: true })
.then((isValid) => {
if (!isValid) {
throw new Error("Invalid or incomplete Mermaid code")
}
const id = `mermaid-${Math.random().toString(36).substring(2)}`
return mermaid.render(id, code)
})
.then(({ svg }) => {
if (containerRef.current) {
containerRef.current.innerHTML = svg
}
})
.catch((err) => {
console.warn("Mermaid parse/render failed:", err)
containerRef.current!.innerHTML = code.replace(/</g, "&lt;").replace(/>/g, "&gt;")
})
.finally(() => {
setIsLoading(false)
})
},
500, // Delay 500ms
[code], // Dependencies for scheduling
)
return (
<MermaidBlockContainer>
{isLoading && <LoadingMessage>Creating mermaid chart...</LoadingMessage>}
{/* The container for the final <svg> or raw code. */}
<SvgContainer ref={containerRef} $isLoading={isLoading} />
</MermaidBlockContainer>
)
}
const MermaidBlockContainer = styled.div`
position: relative;
margin: 8px 0;
`
const LoadingMessage = styled.div`
padding: 8px 0;
color: var(--vscode-descriptionForeground);
font-style: italic;
font-size: 0.9em;
`
interface SvgContainerProps {
$isLoading: boolean
}
const SvgContainer = styled.div<SvgContainerProps>`
opacity: ${(props) => (props.$isLoading ? 0.3 : 1)};
min-height: 20px;
transition: opacity 0.2s ease;
`
+42
View File
@@ -0,0 +1,42 @@
import { useEffect, useRef } from "react"
type VoidFn = () => void
/**
* Runs `effectRef.current()` after `delay` ms whenever any of the `deps` change,
* but cancels/re-schedules if they change again before the delay.
*/
export function useDebounceEffect(effect: VoidFn, delay: number, deps: any[]) {
const callbackRef = useRef<VoidFn>(effect)
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
// Keep callbackRef current
useEffect(() => {
callbackRef.current = effect
}, [effect])
useEffect(() => {
// Clear any queued call
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
// Schedule a new call
timeoutRef.current = setTimeout(() => {
// always call the *latest* version of effect
callbackRef.current()
}, delay)
// Cleanup on unmount or next effect
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
}
// We want to reschedule if any item in `deps` changed,
// or if `delay` changed.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [delay, ...deps])
}