Skip to content

Commit 728bda6

Browse files
committed
feat: add interface text size setting for Roo UI
- Add interfaceTextSize setting to global-settings.ts with small/medium/large options - Create InterfaceSettings component for UI configuration - Add Interface section to Settings view - Implement dynamic CSS classes for text size adjustment - Add message handler for persisting text size preference - Add translation strings for the new interface settings - Apply text size class to root App component This addresses issue #8100 by providing a built-in text size setting that only affects Roo Code's UI without changing VS Code's zoom level or editor font size.
1 parent 87b45de commit 728bda6

File tree

9 files changed

+115
-1
lines changed

9 files changed

+115
-1
lines changed

packages/types/src/global-settings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ export const globalSettingsSchema = z.object({
151151
hasOpenedModeSelector: z.boolean().optional(),
152152
lastModeExportPath: z.string().optional(),
153153
lastModeImportPath: z.string().optional(),
154+
155+
// Interface settings
156+
interfaceTextSize: z.enum(["small", "medium", "large"]).optional(),
154157
})
155158

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

src/core/webview/webviewMessageHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,10 @@ export const webviewMessageHandler = async (
15581558
await updateGlobalState("language", message.text as Language)
15591559
await provider.postStateToWebview()
15601560
break
1561+
case "interfaceTextSize":
1562+
await updateGlobalState("interfaceTextSize", message.text as "small" | "medium" | "large" | undefined)
1563+
await provider.postStateToWebview()
1564+
break
15611565
case "openRouterImageApiKey":
15621566
await provider.contextProxy.setValue("openRouterImageApiKey", message.text)
15631567
await provider.postStateToWebview()

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ export interface WebviewMessage {
168168
| "browserConnectionResult"
169169
| "remoteBrowserEnabled"
170170
| "language"
171+
| "interfaceTextSize"
171172
| "maxReadFileLine"
172173
| "maxImageFileSize"
173174
| "maxTotalImageSize"

webview-ui/src/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const App = () => {
7878
cloudApiUrl,
7979
renderContext,
8080
mdmCompliant,
81+
interfaceTextSize,
8182
} = useExtensionState()
8283

8384
// Create a persistent state manager
@@ -245,7 +246,7 @@ const App = () => {
245246

246247
// Do not conditionally load ChatView, it's expensive and there's state we
247248
// don't want to lose (user input, disableInput, askResponse promise, etc.)
248-
return showWelcome ? (
249+
const content = showWelcome ? (
249250
<WelcomeView />
250251
) : (
251252
<>
@@ -345,6 +346,9 @@ const App = () => {
345346
)}
346347
</>
347348
)
349+
350+
// Wrap content with interface text size class
351+
return <div className={`interface-text-${interfaceTextSize || "medium"}`}>{content}</div>
348352
}
349353

350354
const queryClient = new QueryClient()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from "react"
2+
import { Type } from "lucide-react"
3+
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
4+
5+
import { useAppTranslation } from "@src/i18n/TranslationContext"
6+
import { SetCachedStateField } from "./types"
7+
import { SectionHeader } from "./SectionHeader"
8+
import { Section } from "./Section"
9+
10+
interface InterfaceSettingsProps {
11+
interfaceTextSize?: "small" | "medium" | "large"
12+
setCachedStateField: SetCachedStateField<"interfaceTextSize">
13+
}
14+
15+
export const InterfaceSettings = ({ interfaceTextSize = "medium", setCachedStateField }: InterfaceSettingsProps) => {
16+
const { t } = useAppTranslation()
17+
18+
return (
19+
<div>
20+
<SectionHeader>
21+
<div className="flex items-center gap-2">
22+
<Type className="w-4" />
23+
<div>{t("settings:interface.title")}</div>
24+
</div>
25+
</SectionHeader>
26+
27+
<Section>
28+
<div className="dropdown-container">
29+
<label htmlFor="interface-text-size">{t("settings:interface.textSize.label")}</label>
30+
<VSCodeDropdown
31+
id="interface-text-size"
32+
value={interfaceTextSize}
33+
onChange={(e) => {
34+
const value = (e.target as HTMLSelectElement).value as "small" | "medium" | "large"
35+
setCachedStateField("interfaceTextSize", value)
36+
}}>
37+
<VSCodeOption value="small">{t("settings:interface.textSize.small")}</VSCodeOption>
38+
<VSCodeOption value="medium">{t("settings:interface.textSize.medium")}</VSCodeOption>
39+
<VSCodeOption value="large">{t("settings:interface.textSize.large")}</VSCodeOption>
40+
</VSCodeDropdown>
41+
<p className="text-xs text-vscode-descriptionForeground mt-1">
42+
{t("settings:interface.textSize.description")}
43+
</p>
44+
</div>
45+
</Section>
46+
</div>
47+
)
48+
}

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
MessageSquare,
2525
LucideIcon,
2626
SquareSlash,
27+
Type,
2728
} from "lucide-react"
2829

2930
import type { ProviderSettings, ExperimentId, TelemetrySetting } from "@roo-code/types"
@@ -66,6 +67,7 @@ import { About } from "./About"
6667
import { Section } from "./Section"
6768
import PromptsSettings from "./PromptsSettings"
6869
import { SlashCommandsSettings } from "./SlashCommandsSettings"
70+
import { InterfaceSettings } from "./InterfaceSettings"
6971

7072
export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
7173
export const settingsTabList =
@@ -89,6 +91,7 @@ const sectionNames = [
8991
"terminal",
9092
"prompts",
9193
"experimental",
94+
"interface",
9295
"language",
9396
"about",
9497
] as const
@@ -191,6 +194,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
191194
includeTaskHistoryInEnhance,
192195
openRouterImageApiKey,
193196
openRouterImageGenerationSelectedModel,
197+
interfaceTextSize,
194198
} = cachedState
195199

196200
const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
@@ -372,6 +376,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
372376
type: "openRouterImageGenerationSelectedModel",
373377
text: openRouterImageGenerationSelectedModel,
374378
})
379+
vscode.postMessage({ type: "interfaceTextSize", text: interfaceTextSize })
375380
setChangeDetected(false)
376381
}
377382
}
@@ -459,6 +464,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
459464
{ id: "terminal", icon: SquareTerminal },
460465
{ id: "prompts", icon: MessageSquare },
461466
{ id: "experimental", icon: FlaskConical },
467+
{ id: "interface", icon: Type },
462468
{ id: "language", icon: Globe },
463469
{ id: "about", icon: Info },
464470
],
@@ -773,6 +779,14 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
773779
/>
774780
)}
775781

782+
{/* Interface Section */}
783+
{activeTab === "interface" && (
784+
<InterfaceSettings
785+
interfaceTextSize={interfaceTextSize}
786+
setCachedStateField={setCachedStateField}
787+
/>
788+
)}
789+
776790
{/* Language Section */}
777791
{activeTab === "language" && (
778792
<LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ export interface ExtensionStateContextType extends ExtensionState {
155155
setMaxDiagnosticMessages: (value: number) => void
156156
includeTaskHistoryInEnhance?: boolean
157157
setIncludeTaskHistoryInEnhance: (value: boolean) => void
158+
interfaceTextSize?: "small" | "medium" | "large"
159+
setInterfaceTextSize: (value: "small" | "medium" | "large") => void
158160
}
159161

160162
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@@ -280,6 +282,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
280282
global: {},
281283
})
282284
const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true)
285+
const [interfaceTextSize, setInterfaceTextSize] = useState<"small" | "medium" | "large">("medium")
283286

284287
const setListApiConfigMeta = useCallback(
285288
(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
@@ -317,6 +320,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
317320
if ((newState as any).includeTaskHistoryInEnhance !== undefined) {
318321
setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance)
319322
}
323+
// Update interfaceTextSize if present in state message
324+
if ((newState as any).interfaceTextSize !== undefined) {
325+
setInterfaceTextSize((newState as any).interfaceTextSize)
326+
}
320327
// Handle marketplace data if present in state message
321328
if (newState.marketplaceItems !== undefined) {
322329
setMarketplaceItems(newState.marketplaceItems)
@@ -538,6 +545,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
538545
},
539546
includeTaskHistoryInEnhance,
540547
setIncludeTaskHistoryInEnhance,
548+
interfaceTextSize,
549+
setInterfaceTextSize,
541550
}
542551

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

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"slashCommands": "Slash Commands",
3232
"prompts": "Prompts",
3333
"experimental": "Experimental",
34+
"interface": "Interface",
3435
"language": "Language",
3536
"about": "About Roo Code"
3637
},
@@ -875,5 +876,15 @@
875876
"output": "Output",
876877
"cacheReads": "Cache reads"
877878
}
879+
},
880+
"interface": {
881+
"title": "Interface",
882+
"textSize": {
883+
"label": "Text Size",
884+
"description": "Adjust the text size for Roo Code's interface. This only affects Roo Code's UI and does not change VS Code's zoom level or editor font size.",
885+
"small": "Small",
886+
"medium": "Medium (Default)",
887+
"large": "Large"
888+
}
878889
}
879890
}

webview-ui/src/index.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
@theme {
2626
--font-display: var(--vscode-font-family);
2727

28+
/* Default text sizes (medium) */
2829
--text-xs: calc(var(--vscode-font-size) * 0.85);
2930
--text-sm: calc(var(--vscode-font-size) * 0.9);
3031
--text-base: var(--vscode-font-size);
@@ -490,3 +491,22 @@ input[cmdk-input]:focus {
490491
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
491492
transition-duration: 150ms;
492493
}
494+
495+
/* Interface text size classes */
496+
.interface-text-small {
497+
--text-xs: calc(var(--vscode-font-size) * 0.75);
498+
--text-sm: calc(var(--vscode-font-size) * 0.8);
499+
--text-base: calc(var(--vscode-font-size) * 0.85);
500+
--text-lg: calc(var(--vscode-font-size) * 0.95);
501+
}
502+
503+
.interface-text-medium {
504+
/* Default sizes - already defined above */
505+
}
506+
507+
.interface-text-large {
508+
--text-xs: calc(var(--vscode-font-size) * 0.95);
509+
--text-sm: calc(var(--vscode-font-size) * 1);
510+
--text-base: calc(var(--vscode-font-size) * 1.15);
511+
--text-lg: calc(var(--vscode-font-size) * 1.25);
512+
}

0 commit comments

Comments
 (0)