Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export const globalSettingsSchema = z.object({
hasOpenedModeSelector: z.boolean().optional(),
lastModeExportPath: z.string().optional(),
lastModeImportPath: z.string().optional(),
uiFontSize: z.number().min(50).max(200).optional(), // UI font size as percentage (50-200%)
})

export type GlobalSettings = z.infer<typeof globalSettingsSchema>
Expand Down
7 changes: 7 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2657,5 +2657,12 @@ export const webviewMessageHandler = async (
vscode.window.showWarningMessage(t("common:mdm.info.organization_requires_auth"))
break
}
case "uiFontSize": {
// Store the UI font size preference
const fontSize = message.value ?? 100
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add runtime validation here to ensure fontSize is within the expected range?

Suggested change
const fontSize = message.value ?? 100
const fontSize = message.value ?? 100
// Validate font size is within acceptable range
const validatedFontSize = Math.min(200, Math.max(50, fontSize))
await updateGlobalState("uiFontSize", validatedFontSize)

await updateGlobalState("uiFontSize", fontSize)
await provider.postStateToWebview()
break
}
}
}
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ export type ExtensionState = Pick<
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
profileThresholds: Record<string, number>
hasOpenedModeSelector: boolean
uiFontSize?: number // UI font size as percentage (50-200%)
}

export interface ClineSayTool {
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export interface WebviewMessage {
| "createCommand"
| "insertTextIntoTextarea"
| "showMdmAuthRequiredNotification"
| "uiFontSize"
text?: string
editedMessageContent?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
Expand Down
39 changes: 39 additions & 0 deletions webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ const App = () => {
cloudApiUrl,
renderContext,
mdmCompliant,
uiFontSize,
setUiFontSize,
} = useExtensionState()

// Create a persistent state manager
Expand Down Expand Up @@ -226,6 +228,43 @@ const App = () => {
}
}, [tab])

// Handle Ctrl + Mouse Wheel for font size adjustment
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wheel event handler logic is duplicated. Could we extract this into a custom hook like useUIFontSizeAdjustment to avoid duplication and ensure consistency?

useEffect(() => {
const handleWheel = (e: WheelEvent) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for this wheel event handler. Consider adding tests to verify:

  • The font size adjusts correctly with wheel up/down
  • The font size is clamped between 50% and 200%
  • The preventDefault() is called to prevent browser zoom

// Check if Ctrl key is pressed (or Cmd on Mac)
if (e.ctrlKey || e.metaKey) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Cmd+Mouse Wheel behavior on Mac intentional? The PR description and issue #7530 only mention Ctrl+Mouse Wheel, but this also responds to metaKey. If this is intended for Mac support, it should be documented in the PR description.

e.preventDefault() // Prevent default browser zoom

const currentSize = uiFontSize || 100
const delta = e.deltaY > 0 ? -5 : 5 // Decrease on scroll down, increase on scroll up
const newSize = Math.min(200, Math.max(50, currentSize + delta)) // Clamp between 50% and 200%

if (newSize !== currentSize) {
setUiFontSize(newSize)
// Send message to extension to persist the setting
vscode.postMessage({ type: "uiFontSize", value: newSize })
}
}
}

// Add event listener with passive: false to allow preventDefault
window.addEventListener("wheel", handleWheel, { passive: false })

return () => {
window.removeEventListener("wheel", handleWheel)
}
}, [uiFontSize, setUiFontSize])

// Apply font size to the document
useEffect(() => {
if (uiFontSize) {
// Apply the font size as a CSS variable that can be used throughout the app
document.documentElement.style.setProperty("--ui-font-scale", `${uiFontSize / 100}`)
// Also apply a direct font-size to the body for immediate effect
document.documentElement.style.fontSize = `${uiFontSize}%`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting both a CSS variable and direct fontSize might be redundant. Consider using just the CSS variable approach:

Suggested change
document.documentElement.style.fontSize = `${uiFontSize}%`
if (uiFontSize) {
// Apply the font size as a CSS variable that can be used throughout the app
document.documentElement.style.setProperty("--ui-font-scale", `${uiFontSize / 100}`)
// Use CSS to apply: html { font-size: calc(100% * var(--ui-font-scale)); }
}

}
}, [uiFontSize])

if (!didHydrateState) {
return null
}
Expand Down
13 changes: 13 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setMaxDiagnosticMessages: (value: number) => void
includeTaskHistoryInEnhance?: boolean
setIncludeTaskHistoryInEnhance: (value: boolean) => void
uiFontSize?: number
setUiFontSize: (value: number) => void
}

export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
Expand Down Expand Up @@ -239,6 +241,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
autoCondenseContext: true,
autoCondenseContextPercent: 100,
profileThresholds: {},
uiFontSize: 100, // Default UI font size as percentage (100 = 100% = normal size)
codebaseIndexConfig: {
codebaseIndexEnabled: true,
codebaseIndexQdrantUrl: "http://localhost:6333",
Expand Down Expand Up @@ -271,6 +274,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
global: {},
})
const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true)
const [, setUiFontSizeState] = useState(100) // Default to 100%

const setListApiConfigMeta = useCallback(
(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
Expand Down Expand Up @@ -308,6 +312,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
if ((newState as any).includeTaskHistoryInEnhance !== undefined) {
setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance)
}
// Update uiFontSize if present in state message
if (newState.uiFontSize !== undefined) {
setUiFontSizeState(newState.uiFontSize)
}
// Handle marketplace data if present in state message
if (newState.marketplaceItems !== undefined) {
setMarketplaceItems(newState.marketplaceItems)
Expand Down Expand Up @@ -524,6 +532,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
},
includeTaskHistoryInEnhance,
setIncludeTaskHistoryInEnhance,
uiFontSize: state.uiFontSize ?? 100,
setUiFontSize: (value) => {
setState((prevState) => ({ ...prevState, uiFontSize: value }))
setUiFontSizeState(value)
},
}

return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
Expand Down
Loading