Skip to content

Commit 8ab0de3

Browse files
authored
Use Lucide icons and translations in the code block (#3203)
1 parent da6c50b commit 8ab0de3

File tree

18 files changed

+193
-15
lines changed

18 files changed

+193
-15
lines changed

webview-ui/src/components/common/CodeBlock.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { useCopyToClipboard } from "@src/utils/clipboard"
44
import { getHighlighter, isLanguageLoaded, normalizeLanguage, ExtendedLanguage } from "@src/utils/highlighter"
55
import { bundledLanguages } from "shiki"
66
import type { ShikiTransformer } from "shiki"
7+
import { ChevronDown, ChevronUp, WrapText, AlignJustify, Copy, Check } from "lucide-react"
8+
import { useAppTranslation } from "@src/i18n/TranslationContext"
79
export const CODE_BLOCK_BG_COLOR = "var(--vscode-editor-background, --vscode-sideBar-background, rgb(30 30 30))"
810
export const WRAPPER_ALPHA = "cc" // 80% opacity
911
// Configuration constants
@@ -34,13 +36,6 @@ interface CodeBlockProps {
3436
onLanguageChange?: (language: string) => void
3537
}
3638

37-
const ButtonIcon = styled.span`
38-
display: inline-block;
39-
width: 1.2em;
40-
text-align: center;
41-
vertical-align: middle;
42-
`
43-
4439
const CodeBlockButton = styled.button`
4540
background: transparent;
4641
border: none;
@@ -50,16 +45,23 @@ const CodeBlockButton = styled.button`
5045
margin: 0 0px;
5146
display: flex;
5247
align-items: center;
48+
justify-content: center;
5349
opacity: 0.4;
5450
border-radius: 3px;
5551
pointer-events: var(--copy-button-events, none);
5652
margin-left: 4px;
5753
height: 24px;
54+
width: 24px;
5855
5956
&:hover {
6057
background: var(--vscode-toolbar-hoverBackground);
6158
opacity: 1;
6259
}
60+
61+
/* Style for Lucide icons to ensure consistent sizing and positioning */
62+
svg {
63+
display: block;
64+
}
6365
`
6466

6567
const CodeBlockButtonWrapper = styled.div`
@@ -229,6 +231,7 @@ const CodeBlock = memo(
229231
const preRef = useRef<HTMLDivElement>(null)
230232
const copyButtonWrapperRef = useRef<HTMLDivElement>(null)
231233
const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard()
234+
const { t } = useAppTranslation()
232235

233236
// Update current language when prop changes, but only if user hasn't made a selection
234237
useEffect(() => {
@@ -696,19 +699,17 @@ const CodeBlock = memo(
696699
WINDOW_SHADE_SETTINGS.transitionDelayS * 1000 + 50,
697700
)
698701
}}
699-
title={`${windowShade ? "Expand" : "Collapse"} code block`}>
700-
<ButtonIcon style={{ fontSize: "16px" }}>{windowShade ? "⌄" : "⌃"}</ButtonIcon>
702+
title={t(`chat:codeblock.tooltips.${windowShade ? "expand" : "collapse"}`)}>
703+
{windowShade ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
701704
</CodeBlockButton>
702705
)}
703706
<CodeBlockButton
704707
onClick={() => setWordWrap(!wordWrap)}
705-
title={`${wordWrap ? "Disable" : "Enable"} word wrap`}>
706-
<ButtonIcon style={{ fontSize: "16px", fontWeight: 900 }}>
707-
{wordWrap ? "⟼" : "⤸"}
708-
</ButtonIcon>
708+
title={t(`chat:codeblock.tooltips.${wordWrap ? "disable_wrap" : "enable_wrap"}`)}>
709+
{wordWrap ? <AlignJustify size={16} /> : <WrapText size={16} />}
709710
</CodeBlockButton>
710-
<CodeBlockButton onClick={handleCopy} title="Copy code">
711-
<ButtonIcon className={`codicon codicon-${showCopyFeedback ? "check" : "copy"}`} />
711+
<CodeBlockButton onClick={handleCopy} title={t("chat:codeblock.tooltips.copy_code")}>
712+
{showCopyFeedback ? <Check size={16} /> : <Copy size={16} />}
712713
</CodeBlockButton>
713714
</CodeBlockButtonWrapper>
714715
)}

webview-ui/src/components/common/__tests__/CodeBlock.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@ import { render, screen, fireEvent, act } from "@testing-library/react"
22
import "@testing-library/jest-dom"
33
import CodeBlock from "../CodeBlock"
44

5+
// Mock the translation context
6+
jest.mock("../../../i18n/TranslationContext", () => ({
7+
useAppTranslation: () => ({
8+
t: (key: string) => {
9+
// Return fixed English strings for tests
10+
const translations: { [key: string]: string } = {
11+
"chat:codeblock.tooltips.copy_code": "Copy code",
12+
"chat:codeblock.tooltips.expand": "Expand code block",
13+
"chat:codeblock.tooltips.collapse": "Collapse code block",
14+
"chat:codeblock.tooltips.enable_wrap": "Enable word wrap",
15+
"chat:codeblock.tooltips.disable_wrap": "Disable word wrap",
16+
}
17+
return translations[key] || key
18+
},
19+
}),
20+
}))
21+
522
// Mock shiki module
623
jest.mock("shiki", () => ({
724
bundledLanguages: {
@@ -11,6 +28,22 @@ jest.mock("shiki", () => ({
1128
},
1229
}))
1330

31+
// Mock all lucide-react icons with a proxy to handle any icon requested
32+
jest.mock("lucide-react", () => {
33+
return new Proxy(
34+
{},
35+
{
36+
get: function (obj, prop) {
37+
// Return a component factory for any icon that's requested
38+
if (prop === "__esModule") {
39+
return true
40+
}
41+
return () => <div data-testid={`${String(prop)}-icon`}>{String(prop)}</div>
42+
},
43+
},
44+
)
45+
})
46+
1447
// Mock the highlighter utility
1548
jest.mock("../../../utils/highlighter", () => {
1649
const mockHighlighter = {

webview-ui/src/i18n/locales/ca/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Tancar navegador"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Expandir bloc de codi",
239+
"collapse": "Contraure bloc de codi",
240+
"enable_wrap": "Activar ajustament de línia",
241+
"disable_wrap": "Desactivar ajustament de línia",
242+
"copy_code": "Copiar codi"
243+
}
244+
},
236245
"systemPromptWarning": "ADVERTÈNCIA: S'ha activat una substitució personalitzada d'instruccions del sistema. Això pot trencar greument la funcionalitat i causar un comportament impredictible.",
237246
"shellIntegration": {
238247
"title": "Advertència d'execució d'ordres",

webview-ui/src/i18n/locales/de/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Browser schließen"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Code-Block erweitern",
239+
"collapse": "Code-Block reduzieren",
240+
"enable_wrap": "Zeilenumbruch aktivieren",
241+
"disable_wrap": "Zeilenumbruch deaktivieren",
242+
"copy_code": "Code kopieren"
243+
}
244+
},
236245
"systemPromptWarning": "WARNUNG: Benutzerdefinierte Systemaufforderung aktiv. Dies kann die Funktionalität erheblich beeinträchtigen und zu unvorhersehbarem Verhalten führen.",
237246
"shellIntegration": {
238247
"title": "Befehlsausführungswarnung",

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Close browser"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Expand code block",
239+
"collapse": "Collapse code block",
240+
"enable_wrap": "Enable word wrap",
241+
"disable_wrap": "Disable word wrap",
242+
"copy_code": "Copy code"
243+
}
244+
},
236245
"systemPromptWarning": "WARNING: Custom system prompt override active. This can severely break functionality and cause unpredictable behavior.",
237246
"shellIntegration": {
238247
"title": "Command Execution Warning",

webview-ui/src/i18n/locales/es/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Cerrar navegador"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Expandir bloque de código",
239+
"collapse": "Contraer bloque de código",
240+
"enable_wrap": "Activar ajuste de línea",
241+
"disable_wrap": "Desactivar ajuste de línea",
242+
"copy_code": "Copiar código"
243+
}
244+
},
236245
"systemPromptWarning": "ADVERTENCIA: Anulación de instrucciones del sistema personalizada activa. Esto puede romper gravemente la funcionalidad y causar un comportamiento impredecible.",
237246
"shellIntegration": {
238247
"title": "Advertencia de ejecución de comandos",

webview-ui/src/i18n/locales/fr/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Fermer le navigateur"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Développer le bloc de code",
239+
"collapse": "Réduire le bloc de code",
240+
"enable_wrap": "Activer le retour à la ligne",
241+
"disable_wrap": "Désactiver le retour à la ligne",
242+
"copy_code": "Copier le code"
243+
}
244+
},
236245
"systemPromptWarning": "AVERTISSEMENT : Remplacement d'instructions système personnalisées actif. Cela peut gravement perturber la fonctionnalité et provoquer un comportement imprévisible.",
237246
"shellIntegration": {
238247
"title": "Avertissement d'exécution de commande",

webview-ui/src/i18n/locales/hi/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "ब्राउज़र बंद करें"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "कोड ब्लॉक का विस्तार करें",
239+
"collapse": "कोड ब्लॉक को संकुचित करें",
240+
"enable_wrap": "वर्ड रैप सक्षम करें",
241+
"disable_wrap": "वर्ड रैप अक्षम करें",
242+
"copy_code": "कोड कॉपी करें"
243+
}
244+
},
236245
"systemPromptWarning": "चेतावनी: कस्टम सिस्टम प्रॉम्प्ट ओवरराइड सक्रिय है। यह कार्यक्षमता को गंभीर रूप से बाधित कर सकता है और अनियमित व्यवहार का कारण बन सकता है.",
237246
"shellIntegration": {
238247
"title": "कमांड निष्पादन चेतावनी",

webview-ui/src/i18n/locales/it/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "Chiudi browser"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "Espandi blocco di codice",
239+
"collapse": "Comprimi blocco di codice",
240+
"enable_wrap": "Attiva a capo automatico",
241+
"disable_wrap": "Disattiva a capo automatico",
242+
"copy_code": "Copia codice"
243+
}
244+
},
236245
"systemPromptWarning": "ATTENZIONE: Sovrascrittura personalizzata delle istruzioni di sistema attiva. Questo può compromettere gravemente le funzionalità e causare comportamenti imprevedibili.",
237246
"shellIntegration": {
238247
"title": "Avviso di esecuzione comando",

webview-ui/src/i18n/locales/ja/chat.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@
233233
"close": "ブラウザを閉じる"
234234
}
235235
},
236+
"codeblock": {
237+
"tooltips": {
238+
"expand": "コードブロックを展開",
239+
"collapse": "コードブロックを折りたたむ",
240+
"enable_wrap": "折り返しを有効化",
241+
"disable_wrap": "折り返しを無効化",
242+
"copy_code": "コードをコピー"
243+
}
244+
},
236245
"systemPromptWarning": "警告:カスタムシステムプロンプトの上書きが有効です。これにより機能が深刻に損なわれ、予測不可能な動作が発生する可能性があります。",
237246
"shellIntegration": {
238247
"title": "コマンド実行警告",

0 commit comments

Comments
 (0)