From 49f9494019211985f41da657ba3c6d9a51e7e1fd Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 1 May 2025 00:47:46 -0700 Subject: [PATCH 1/5] feat: support opening files at specific line numbers Added support for opening files at specific line numbers when clicking markdown links: - Extract line numbers from file:// URLs with :line syntax - Pass line numbers through WebviewMessage values - Update openFile to position cursor at specified line - Preserve absolute vs project-relative path handling Examples: [Open at line 10](file://example.ts:10) [Open relative](./src/file.ts:42) [Open absolute](/path/to/file.ts:123) Signed-off-by: Eric Wheeler --- src/core/webview/webviewMessageHandler.ts | 2 +- src/integrations/misc/open-file.ts | 8 ++++- .../src/components/common/MarkdownBlock.tsx | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 94bd68f9c94..d11d728913f 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -336,7 +336,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We openImage(message.text!) break case "openFile": - openFile(message.text!, message.values as { create?: boolean; content?: string }) + openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number }) break case "openMention": openMention(message.text) diff --git a/src/integrations/misc/open-file.ts b/src/integrations/misc/open-file.ts index b3724068b62..52352314b3e 100644 --- a/src/integrations/misc/open-file.ts +++ b/src/integrations/misc/open-file.ts @@ -23,6 +23,7 @@ export async function openImage(dataUri: string) { interface OpenFileOptions { create?: boolean content?: string + line?: number } export async function openFile(filePath: string, options: OpenFileOptions = {}) { @@ -75,7 +76,12 @@ export async function openFile(filePath: string, options: OpenFileOptions = {}) } catch {} // not essential, sometimes tab operations fail const document = await vscode.workspace.openTextDocument(uri) - await vscode.window.showTextDocument(document, { preview: false }) + const selection = + options.line !== undefined ? new vscode.Selection(options.line - 1, 0, options.line - 1, 0) : undefined + await vscode.window.showTextDocument(document, { + preview: false, + selection, + }) } catch (error) { if (error instanceof Error) { vscode.window.showErrorMessage(`Could not open file: ${error.message}`) diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index ba8eab1eb8e..94c605edb6d 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -3,6 +3,7 @@ import { useRemark } from "react-remark" import styled from "styled-components" import { visit } from "unist-util-visit" +import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import CodeBlock from "./CodeBlock" @@ -138,6 +139,38 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => { rehypePlugins: [], rehypeReactOptions: { components: { + a: ({ href, children }: any) => { + return ( + { + e.preventDefault() + // Handle absolute vs project-relative paths + let filePath = href.replace("file://", "") + + // Extract line number if present + const match = filePath.match(/(.*):(\d+)(-\d+)?$/) + let values = undefined + if (match) { + filePath = match[1] + values = { line: parseInt(match[2]) } + } + + // Add ./ prefix if needed + if (!filePath.startsWith("/") && !filePath.startsWith("./")) { + filePath = "./" + filePath + } + + vscode.postMessage({ + type: "openFile", + text: filePath, + values, + }) + }}> + {children} + + ) + }, pre: ({ node: _, children }: any) => { // Check for Mermaid diagrams first if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) { From 9a1503df3015cfb9ee8c57d193fb0db2d5946038 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 1 May 2025 01:22:45 -0700 Subject: [PATCH 2/5] feat: add markdown formatting with clickable file links Add markdown formatting section to system prompt that enforces: - Clickable file links with line numbers for code references - Consistent backtick formatting for code entities - Clear examples showing proper link syntax - Transition guidance between markdown and XML sections Signed-off-by: Eric Wheeler --- src/core/prompts/sections/index.ts | 1 + src/core/prompts/sections/markdown-formatting.ts | 7 +++++++ src/core/prompts/system.ts | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 src/core/prompts/sections/markdown-formatting.ts diff --git a/src/core/prompts/sections/index.ts b/src/core/prompts/sections/index.ts index a9f8c9e4c6d..d06dbbfde1d 100644 --- a/src/core/prompts/sections/index.ts +++ b/src/core/prompts/sections/index.ts @@ -7,3 +7,4 @@ export { getMcpServersSection } from "./mcp-servers" export { getToolUseGuidelinesSection } from "./tool-use-guidelines" export { getCapabilitiesSection } from "./capabilities" export { getModesSection } from "./modes" +export { markdownFormattingSection } from "./markdown-formatting" diff --git a/src/core/prompts/sections/markdown-formatting.ts b/src/core/prompts/sections/markdown-formatting.ts new file mode 100644 index 00000000000..fe152a1a41d --- /dev/null +++ b/src/core/prompts/sections/markdown-formatting.ts @@ -0,0 +1,7 @@ +export function markdownFormattingSection(): string { + return `==== + +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in ` +} diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index f56a9476637..92eba6bdc7c 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -24,6 +24,7 @@ import { getCapabilitiesSection, getModesSection, addCustomInstructions, + markdownFormattingSection, } from "./sections" import { loadSystemPromptFile } from "./sections/custom-system-prompt" import { formatLanguage } from "../../shared/language" @@ -65,6 +66,8 @@ async function generatePrompt( const basePrompt = `${roleDefinition} +${markdownFormattingSection()} + ${getSharedToolUseSection()} ${getToolDescriptionsForMode( From bd7cecdf51df21ae586e71e2015669c8c725b893 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 1 May 2025 16:44:46 -0700 Subject: [PATCH 3/5] feat: enhance link styling with VSCode theme colors Use VSCode theme colors for link styling in markdown: - Apply textLink-foreground color with dotted underline - Change to textLink-activeForeground with solid underline on hover - Remove duplicate link style declaration cc @mrubens Signed-off-by: Eric Wheeler --- webview-ui/src/components/common/MarkdownBlock.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index 94c605edb6d..d8c0102362e 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -110,11 +110,14 @@ const StyledMarkdown = styled.div` } a { - text-decoration: none; - } - a { + color: var(--vscode-textLink-foreground); + text-decoration-line: underline; + text-decoration-style: dotted; + text-decoration-color: var(--vscode-textLink-foreground); &:hover { - text-decoration: underline; + color: var(--vscode-textLink-activeForeground); + text-decoration-style: solid; + text-decoration-color: var(--vscode-textLink-activeForeground); } } ` From 6b53750b938e1f09c8822cf8547e80d91ebcf514 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 1 May 2025 21:12:53 -0700 Subject: [PATCH 4/5] feat: add URL tooltips and improve link safety Add tooltips showing full URL when hovering over links. Add safety checks to only process local file paths: - file:// protocol URLs - absolute paths starting with / - relative paths without protocol Move preventDefault() after path validation to allow external links to work normally. Signed-off-by: Eric Wheeler --- webview-ui/src/components/common/MarkdownBlock.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index d8c0102362e..27bc23ce96d 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -146,8 +146,18 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => { return ( { + // Only process file:// protocol or local file paths + const isLocalPath = + href.startsWith("file://") || href.startsWith("/") || !href.includes("://") + + if (!isLocalPath) { + return + } + e.preventDefault() + // Handle absolute vs project-relative paths let filePath = href.replace("file://", "") From 44b9e9cd1035dcfe8dba223daa18d313eff1f880 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Sat, 3 May 2025 22:11:21 -0700 Subject: [PATCH 5/5] test: updated system instruction snapshots for tests Signed-off-by: Eric Wheeler --- .../__snapshots__/system.test.ts.snap | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index ade0bd1f852..4aed3e9af57 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -5,6 +5,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -472,6 +478,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -939,6 +951,12 @@ exports[`SYSTEM_PROMPT should explicitly handle undefined mcpHub 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -1406,6 +1424,12 @@ exports[`SYSTEM_PROMPT should handle different browser viewport sizes 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -1929,6 +1953,12 @@ exports[`SYSTEM_PROMPT should include MCP server info when mcpHub is provided 1` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -2464,6 +2494,12 @@ exports[`SYSTEM_PROMPT should include browser actions when supportsComputerUse i ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -2987,6 +3023,12 @@ exports[`SYSTEM_PROMPT should include diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -3544,6 +3586,12 @@ exports[`SYSTEM_PROMPT should maintain consistent system prompt 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -4053,6 +4101,12 @@ exports[`addCustomInstructions should exclude MCP server creation info when disa ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -4597,6 +4651,12 @@ exports[`addCustomInstructions should generate correct prompt for architect mode ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -5055,6 +5115,12 @@ exports[`addCustomInstructions should generate correct prompt for ask mode 1`] = ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. @@ -5430,6 +5496,12 @@ exports[`addCustomInstructions should include MCP server creation info when enab ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.