mirror of
https://github.com/cline/cline.git
synced 2025-06-03 03:59:07 +00:00
feat: LaTeX formatting (#3242)
* initial * initial * restored package-lock.json * restored comment * One line * prettier * do not throw on error * escape backslashes in system notification * better prompt * reduce prompt size * prettier --------- Co-authored-by: canvrno <kevin@cline.bot>
This commit is contained in:
parent
61d2f42955
commit
eb6e4818d3
5
.changeset/curly-guests-raise.md
Normal file
5
.changeset/curly-guests-raise.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"claude-dev": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Full support for LaTeX rendering
|
@ -37,8 +37,12 @@ docs/**
|
|||||||
!node_modules/@vscode/codicons/dist/codicon.css
|
!node_modules/@vscode/codicons/dist/codicon.css
|
||||||
!node_modules/@vscode/codicons/dist/codicon.ttf
|
!node_modules/@vscode/codicons/dist/codicon.ttf
|
||||||
|
|
||||||
|
# Include KaTeX CSS and fonts for LaTeX rendering
|
||||||
|
!webview-ui/node_modules/katex/dist/katex.min.css
|
||||||
|
!webview-ui/node_modules/katex/dist/fonts/**
|
||||||
|
|
||||||
# Include default themes JSON files used in getTheme
|
# Include default themes JSON files used in getTheme
|
||||||
!src/integrations/theme/default-themes/**
|
!src/integrations/theme/default-themes/**
|
||||||
|
|
||||||
# Include icons
|
# Include icons
|
||||||
!assets/icons/**
|
!assets/icons/**
|
||||||
|
@ -573,6 +573,7 @@ CAPABILITIES
|
|||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.
|
- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.
|
||||||
|
- You can use LaTeX syntax in your responses to render mathematical expressions
|
||||||
|
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -181,6 +181,14 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
"codicon.css",
|
"codicon.css",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const katexCssUri = getUri(webview, this.context.extensionUri, [
|
||||||
|
"webview-ui",
|
||||||
|
"node_modules",
|
||||||
|
"katex",
|
||||||
|
"dist",
|
||||||
|
"katex.min.css",
|
||||||
|
])
|
||||||
|
|
||||||
// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.js"))
|
// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.js"))
|
||||||
|
|
||||||
// const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "reset.css"))
|
// const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "reset.css"))
|
||||||
@ -204,24 +212,25 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
|
|
||||||
// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
|
// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
|
||||||
return /*html*/ `
|
return /*html*/ `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
||||||
<link href="${codiconsUri}" rel="stylesheet" />
|
<link href="${codiconsUri}" rel="stylesheet" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; connect-src https://*.posthog.com https://*.firebaseauth.com https://*.firebaseio.com https://*.googleapis.com https://*.firebase.com; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https: data:; script-src 'nonce-${nonce}' 'unsafe-eval';">
|
<link href="${katexCssUri}" rel="stylesheet" />
|
||||||
<title>Cline</title>
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; connect-src https://*.posthog.com https://*.firebaseauth.com https://*.firebaseio.com https://*.googleapis.com https://*.firebase.com; font-src ${webview.cspSource} data:; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https: data:; script-src 'nonce-${nonce}' 'unsafe-eval';">
|
||||||
</head>
|
<title>Cline</title>
|
||||||
<body>
|
</head>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<body>
|
||||||
<div id="root"></div>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script type="module" nonce="${nonce}" src="${scriptUri}"></script>
|
<div id="root"></div>
|
||||||
</body>
|
<script type="module" nonce="${nonce}" src="${scriptUri}"></script>
|
||||||
</html>
|
</body>
|
||||||
`
|
</html>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -256,6 +265,15 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
"codicon.css",
|
"codicon.css",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Get KaTeX resources
|
||||||
|
const katexCssUri = getUri(webview, this.context.extensionUri, [
|
||||||
|
"webview-ui",
|
||||||
|
"node_modules",
|
||||||
|
"katex",
|
||||||
|
"dist",
|
||||||
|
"katex.min.css",
|
||||||
|
])
|
||||||
|
|
||||||
const scriptEntrypoint = "src/main.tsx"
|
const scriptEntrypoint = "src/main.tsx"
|
||||||
const scriptUri = `http://${localServerUrl}/${scriptEntrypoint}`
|
const scriptUri = `http://${localServerUrl}/${scriptEntrypoint}`
|
||||||
|
|
||||||
@ -271,7 +289,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
|
|
||||||
const csp = [
|
const csp = [
|
||||||
"default-src 'none'",
|
"default-src 'none'",
|
||||||
`font-src ${webview.cspSource}`,
|
`font-src ${webview.cspSource} data:`,
|
||||||
`style-src ${webview.cspSource} 'unsafe-inline' https://* http://${localServerUrl} http://0.0.0.0:${localPort}`,
|
`style-src ${webview.cspSource} 'unsafe-inline' https://* http://${localServerUrl} http://0.0.0.0:${localPort}`,
|
||||||
`img-src ${webview.cspSource} https: data:`,
|
`img-src ${webview.cspSource} https: data:`,
|
||||||
`script-src 'unsafe-eval' https://* http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`,
|
`script-src 'unsafe-eval' https://* http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`,
|
||||||
@ -287,6 +305,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
<meta http-equiv="Content-Security-Policy" content="${csp.join("; ")}">
|
<meta http-equiv="Content-Security-Policy" content="${csp.join("; ")}">
|
||||||
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
||||||
<link href="${codiconsUri}" rel="stylesheet" />
|
<link href="${codiconsUri}" rel="stylesheet" />
|
||||||
|
<link href="${katexCssUri}" rel="stylesheet" />
|
||||||
<title>Cline</title>
|
<title>Cline</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -74,7 +74,7 @@ export async function showSystemNotification(options: NotificationOptions): Prom
|
|||||||
const escapedOptions = {
|
const escapedOptions = {
|
||||||
...options,
|
...options,
|
||||||
title: title.replace(/"/g, '\\"'),
|
title: title.replace(/"/g, '\\"'),
|
||||||
message: message.replace(/"/g, '\\"'),
|
message: message.replace(/\\/g, "\\\\").replace(/"/g, '\\"'),
|
||||||
subtitle: options.subtitle?.replace(/"/g, '\\"') || "",
|
subtitle: options.subtitle?.replace(/"/g, '\\"') || "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1529
webview-ui/package-lock.json
generated
1529
webview-ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@
|
|||||||
"framer-motion": "^12.7.4",
|
"framer-motion": "^12.7.4",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"fzf": "^0.5.2",
|
"fzf": "^0.5.2",
|
||||||
|
"katex": "^0.16.22",
|
||||||
"mermaid": "^11.4.1",
|
"mermaid": "^11.4.1",
|
||||||
"posthog-js": "^1.224.0",
|
"posthog-js": "^1.224.0",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
@ -35,8 +36,10 @@
|
|||||||
"react-use": "^17.6.0",
|
"react-use": "^17.6.0",
|
||||||
"react-virtuoso": "^4.12.3",
|
"react-virtuoso": "^4.12.3",
|
||||||
"rehype-highlight": "^7.0.1",
|
"rehype-highlight": "^7.0.1",
|
||||||
|
"rehype-katex": "^7.0.1",
|
||||||
"rehype-parse": "^9.0.1",
|
"rehype-parse": "^9.0.1",
|
||||||
"rehype-remark": "^10.0.1",
|
"rehype-remark": "^10.0.1",
|
||||||
|
"remark-math": "^6.0.0",
|
||||||
"remark-stringify": "^11.0.0",
|
"remark-stringify": "^11.0.0",
|
||||||
"styled-components": "^6.1.15",
|
"styled-components": "^6.1.15",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
@ -50,6 +53,7 @@
|
|||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@types/dompurify": "^3.0.5",
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/katex": "^0.16.7",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
|
@ -2,6 +2,8 @@ import React, { memo, useEffect, useRef, useState } from "react"
|
|||||||
import type { ComponentProps } from "react"
|
import type { ComponentProps } from "react"
|
||||||
import { useRemark } from "react-remark"
|
import { useRemark } from "react-remark"
|
||||||
import rehypeHighlight, { Options } from "rehype-highlight"
|
import rehypeHighlight, { Options } from "rehype-highlight"
|
||||||
|
import rehypeKatex from "rehype-katex"
|
||||||
|
import remarkMath from "remark-math"
|
||||||
import styled from "styled-components"
|
import styled from "styled-components"
|
||||||
import { visit } from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import type { Node } from "unist"
|
import type { Node } from "unist"
|
||||||
@ -157,6 +159,34 @@ const StyledMarkdown = styled.div`
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KaTeX styling */
|
||||||
|
.katex {
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
font-family: KaTeX_Main, "Times New Roman", serif;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: normal;
|
||||||
|
text-indent: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.katex-display {
|
||||||
|
display: block;
|
||||||
|
margin: 1em 0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.5em;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
background-color: var(--vscode-textCodeBlock-background);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.katex-error {
|
||||||
|
color: var(--vscode-errorForeground);
|
||||||
|
border: 1px solid var(--vscode-inputValidation-errorBorder);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
font-family:
|
font-family:
|
||||||
var(--vscode-font-family),
|
var(--vscode-font-family),
|
||||||
system-ui,
|
system-ui,
|
||||||
@ -256,6 +286,7 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
|
|||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
remarkPreventBoldFilenames,
|
remarkPreventBoldFilenames,
|
||||||
remarkUrlToLink,
|
remarkUrlToLink,
|
||||||
|
remarkMath,
|
||||||
() => {
|
() => {
|
||||||
return (tree) => {
|
return (tree) => {
|
||||||
visit(tree, "code", (node: any) => {
|
visit(tree, "code", (node: any) => {
|
||||||
@ -273,6 +304,7 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
|
|||||||
{
|
{
|
||||||
// languages: {},
|
// languages: {},
|
||||||
} as Options,
|
} as Options,
|
||||||
|
rehypeKatex,
|
||||||
],
|
],
|
||||||
rehypeReactOptions: {
|
rehypeReactOptions: {
|
||||||
components: {
|
components: {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
/* Disable Tailwind's CSS reset to preserve existing styles */
|
/* Disable Tailwind's CSS reset to preserve existing styles */
|
||||||
/* @import "tailwindcss/preflight.css" layer(base); */
|
/* @import "tailwindcss/preflight.css" layer(base); */
|
||||||
@import "tailwindcss/utilities.css" layer(utilities);
|
@import "tailwindcss/utilities.css" layer(utilities);
|
||||||
|
@import "katex/dist/katex.min.css";
|
||||||
|
|
||||||
@config "../tailwind.config.js";
|
@config "../tailwind.config.js";
|
||||||
|
|
||||||
|
@ -23,7 +23,15 @@ export default defineConfig({
|
|||||||
inlineDynamicImports: true,
|
inlineDynamicImports: true,
|
||||||
entryFileNames: `assets/[name].js`,
|
entryFileNames: `assets/[name].js`,
|
||||||
chunkFileNames: `assets/[name].js`,
|
chunkFileNames: `assets/[name].js`,
|
||||||
assetFileNames: `assets/[name].[ext]`,
|
assetFileNames: (assetInfo) => {
|
||||||
|
if (
|
||||||
|
assetInfo.name &&
|
||||||
|
(assetInfo.name.endsWith(".woff2") || assetInfo.name.endsWith(".woff") || assetInfo.name.endsWith(".ttf"))
|
||||||
|
) {
|
||||||
|
return "assets/fonts/[name][extname]"
|
||||||
|
}
|
||||||
|
return "assets/[name][extname]"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
chunkSizeWarningLimit: 100000,
|
chunkSizeWarningLimit: 100000,
|
||||||
|
Loading…
Reference in New Issue
Block a user