Skip to content

Commit ebbfa6e

Browse files
committed
feat: add UI font size adjustment with Ctrl+Mouse Wheel
- Add uiFontSize state management to ExtensionStateContext - Implement Ctrl+Mouse Wheel event handler in App.tsx - Add font size persistence to global settings - Apply font size dynamically using CSS variables - Support font size range from 50% to 200% Fixes #7530
1 parent 1d46bd1 commit ebbfa6e

File tree

6 files changed

+62
-0
lines changed

6 files changed

+62
-0
lines changed

packages/types/src/global-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export const globalSettingsSchema = z.object({
148148
hasOpenedModeSelector: z.boolean().optional(),
149149
lastModeExportPath: z.string().optional(),
150150
lastModeImportPath: z.string().optional(),
151+
uiFontSize: z.number().min(50).max(200).optional(), // UI font size as percentage (50-200%)
151152
})
152153

153154
export type GlobalSettings = z.infer<typeof globalSettingsSchema>

src/core/webview/webviewMessageHandler.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,5 +2657,12 @@ export const webviewMessageHandler = async (
26572657
vscode.window.showWarningMessage(t("common:mdm.info.organization_requires_auth"))
26582658
break
26592659
}
2660+
case "uiFontSize": {
2661+
// Store the UI font size preference
2662+
const fontSize = message.value ?? 100
2663+
await updateGlobalState("uiFontSize", fontSize)
2664+
await provider.postStateToWebview()
2665+
break
2666+
}
26602667
}
26612668
}

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ export type ExtensionState = Pick<
326326
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
327327
profileThresholds: Record<string, number>
328328
hasOpenedModeSelector: boolean
329+
uiFontSize?: number // UI font size as percentage (50-200%)
329330
}
330331

331332
export interface ClineSayTool {

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export interface WebviewMessage {
212212
| "createCommand"
213213
| "insertTextIntoTextarea"
214214
| "showMdmAuthRequiredNotification"
215+
| "uiFontSize"
215216
text?: string
216217
editedMessageContent?: string
217218
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"

webview-ui/src/App.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ const App = () => {
7474
cloudApiUrl,
7575
renderContext,
7676
mdmCompliant,
77+
uiFontSize,
78+
setUiFontSize,
7779
} = useExtensionState()
7880

7981
// Create a persistent state manager
@@ -226,6 +228,43 @@ const App = () => {
226228
}
227229
}, [tab])
228230

231+
// Handle Ctrl + Mouse Wheel for font size adjustment
232+
useEffect(() => {
233+
const handleWheel = (e: WheelEvent) => {
234+
// Check if Ctrl key is pressed (or Cmd on Mac)
235+
if (e.ctrlKey || e.metaKey) {
236+
e.preventDefault() // Prevent default browser zoom
237+
238+
const currentSize = uiFontSize || 100
239+
const delta = e.deltaY > 0 ? -5 : 5 // Decrease on scroll down, increase on scroll up
240+
const newSize = Math.min(200, Math.max(50, currentSize + delta)) // Clamp between 50% and 200%
241+
242+
if (newSize !== currentSize) {
243+
setUiFontSize(newSize)
244+
// Send message to extension to persist the setting
245+
vscode.postMessage({ type: "uiFontSize", value: newSize })
246+
}
247+
}
248+
}
249+
250+
// Add event listener with passive: false to allow preventDefault
251+
window.addEventListener("wheel", handleWheel, { passive: false })
252+
253+
return () => {
254+
window.removeEventListener("wheel", handleWheel)
255+
}
256+
}, [uiFontSize, setUiFontSize])
257+
258+
// Apply font size to the document
259+
useEffect(() => {
260+
if (uiFontSize) {
261+
// Apply the font size as a CSS variable that can be used throughout the app
262+
document.documentElement.style.setProperty("--ui-font-scale", `${uiFontSize / 100}`)
263+
// Also apply a direct font-size to the body for immediate effect
264+
document.documentElement.style.fontSize = `${uiFontSize}%`
265+
}
266+
}, [uiFontSize])
267+
229268
if (!didHydrateState) {
230269
return null
231270
}

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ export interface ExtensionStateContextType extends ExtensionState {
151151
setMaxDiagnosticMessages: (value: number) => void
152152
includeTaskHistoryInEnhance?: boolean
153153
setIncludeTaskHistoryInEnhance: (value: boolean) => void
154+
uiFontSize?: number
155+
setUiFontSize: (value: number) => void
154156
}
155157

156158
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@@ -239,6 +241,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
239241
autoCondenseContext: true,
240242
autoCondenseContextPercent: 100,
241243
profileThresholds: {},
244+
uiFontSize: 100, // Default UI font size as percentage (100 = 100% = normal size)
242245
codebaseIndexConfig: {
243246
codebaseIndexEnabled: true,
244247
codebaseIndexQdrantUrl: "http://localhost:6333",
@@ -271,6 +274,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
271274
global: {},
272275
})
273276
const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true)
277+
const [, setUiFontSizeState] = useState(100) // Default to 100%
274278

275279
const setListApiConfigMeta = useCallback(
276280
(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
@@ -308,6 +312,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
308312
if ((newState as any).includeTaskHistoryInEnhance !== undefined) {
309313
setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance)
310314
}
315+
// Update uiFontSize if present in state message
316+
if (newState.uiFontSize !== undefined) {
317+
setUiFontSizeState(newState.uiFontSize)
318+
}
311319
// Handle marketplace data if present in state message
312320
if (newState.marketplaceItems !== undefined) {
313321
setMarketplaceItems(newState.marketplaceItems)
@@ -524,6 +532,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
524532
},
525533
includeTaskHistoryInEnhance,
526534
setIncludeTaskHistoryInEnhance,
535+
uiFontSize: state.uiFontSize ?? 100,
536+
setUiFontSize: (value) => {
537+
setState((prevState) => ({ ...prevState, uiFontSize: value }))
538+
setUiFontSizeState(value)
539+
},
527540
}
528541

529542
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>

0 commit comments

Comments
 (0)