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
3 changes: 3 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ export const globalSettingsSchema = z.object({
hasOpenedModeSelector: z.boolean().optional(),
lastModeExportPath: z.string().optional(),
lastModeImportPath: z.string().optional(),

// Interface settings
interfaceTextSize: z.enum(["small", "medium", "large"]).optional(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good use of Zod enum for type safety! However, consider creating a dedicated TypeScript enum or const assertion that can be imported and used throughout the codebase to avoid string literals.

})

export type GlobalSettings = z.infer<typeof globalSettingsSchema>
Expand Down
4 changes: 4 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,10 @@ export const webviewMessageHandler = async (
await updateGlobalState("language", message.text as Language)
await provider.postStateToWebview()
break
case "interfaceTextSize":
await updateGlobalState("interfaceTextSize", message.text as "small" | "medium" | "large" | undefined)
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 message handler case should have test coverage. Could we add a test to verify the state update and webview posting work correctly?

await provider.postStateToWebview()
break
case "openRouterImageApiKey":
await provider.contextProxy.setValue("openRouterImageApiKey", message.text)
await provider.postStateToWebview()
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export interface WebviewMessage {
| "browserConnectionResult"
| "remoteBrowserEnabled"
| "language"
| "interfaceTextSize"
| "maxReadFileLine"
| "maxImageFileSize"
| "maxTotalImageSize"
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const App = () => {
cloudApiUrl,
renderContext,
mdmCompliant,
interfaceTextSize,
} = useExtensionState()

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

// Do not conditionally load ChatView, it's expensive and there's state we
// don't want to lose (user input, disableInput, askResponse promise, etc.)
return showWelcome ? (
const content = showWelcome ? (
<WelcomeView />
) : (
<>
Expand Down Expand Up @@ -345,6 +346,9 @@ const App = () => {
)}
</>
)

// Wrap content with interface text size class
return <div className={`interface-text-${interfaceTextSize || "medium"}`}>{content}</div>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The default value 'medium' is hardcoded here. Consider extracting this to a constants file to maintain consistency across the codebase.

}

const queryClient = new QueryClient()
Expand Down
48 changes: 48 additions & 0 deletions webview-ui/src/components/settings/InterfaceSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react"
import { Type } from "lucide-react"
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { SetCachedStateField } from "./types"
import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"

interface InterfaceSettingsProps {
interfaceTextSize?: "small" | "medium" | "large"
setCachedStateField: SetCachedStateField<"interfaceTextSize">
}

export const InterfaceSettings = ({ interfaceTextSize = "medium", setCachedStateField }: InterfaceSettingsProps) => {
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 component needs test coverage. Could we add tests to verify the dropdown behavior and state updates work correctly?

const { t } = useAppTranslation()

return (
<div>
<SectionHeader>
<div className="flex items-center gap-2">
<Type className="w-4" />
<div>{t("settings:interface.title")}</div>
</div>
</SectionHeader>

<Section>
<div className="dropdown-container">
<label htmlFor="interface-text-size">{t("settings:interface.textSize.label")}</label>
<VSCodeDropdown
id="interface-text-size"
value={interfaceTextSize}
onChange={(e) => {
const value = (e.target as HTMLSelectElement).value as "small" | "medium" | "large"
setCachedStateField("interfaceTextSize", value)
}}>
<VSCodeOption value="small">{t("settings:interface.textSize.small")}</VSCodeOption>
<VSCodeOption value="medium">{t("settings:interface.textSize.medium")}</VSCodeOption>
<VSCodeOption value="large">{t("settings:interface.textSize.large")}</VSCodeOption>
</VSCodeDropdown>
<p className="text-xs text-vscode-descriptionForeground mt-1">
{t("settings:interface.textSize.description")}
</p>
</div>
</Section>
</div>
)
}
14 changes: 14 additions & 0 deletions webview-ui/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
MessageSquare,
LucideIcon,
SquareSlash,
Type,
} from "lucide-react"

import type { ProviderSettings, ExperimentId, TelemetrySetting } from "@roo-code/types"
Expand Down Expand Up @@ -66,6 +67,7 @@ import { About } from "./About"
import { Section } from "./Section"
import PromptsSettings from "./PromptsSettings"
import { SlashCommandsSettings } from "./SlashCommandsSettings"
import { InterfaceSettings } from "./InterfaceSettings"

export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
export const settingsTabList =
Expand All @@ -89,6 +91,7 @@ const sectionNames = [
"terminal",
"prompts",
"experimental",
"interface",
"language",
"about",
] as const
Expand Down Expand Up @@ -191,6 +194,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
includeTaskHistoryInEnhance,
openRouterImageApiKey,
openRouterImageGenerationSelectedModel,
interfaceTextSize,
} = cachedState

const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
Expand Down Expand Up @@ -372,6 +376,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
type: "openRouterImageGenerationSelectedModel",
text: openRouterImageGenerationSelectedModel,
})
vscode.postMessage({ type: "interfaceTextSize", text: interfaceTextSize })
setChangeDetected(false)
}
}
Expand Down Expand Up @@ -459,6 +464,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
{ id: "terminal", icon: SquareTerminal },
{ id: "prompts", icon: MessageSquare },
{ id: "experimental", icon: FlaskConical },
{ id: "interface", icon: Type },
{ id: "language", icon: Globe },
{ id: "about", icon: Info },
],
Expand Down Expand Up @@ -773,6 +779,14 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
/>
)}

{/* Interface Section */}
{activeTab === "interface" && (
<InterfaceSettings
interfaceTextSize={interfaceTextSize}
setCachedStateField={setCachedStateField}
/>
)}

{/* Language Section */}
{activeTab === "language" && (
<LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />
Expand Down
9 changes: 9 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setMaxDiagnosticMessages: (value: number) => void
includeTaskHistoryInEnhance?: boolean
setIncludeTaskHistoryInEnhance: (value: boolean) => void
interfaceTextSize?: "small" | "medium" | "large"
setInterfaceTextSize: (value: "small" | "medium" | "large") => void
}

export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
Expand Down Expand Up @@ -280,6 +282,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
global: {},
})
const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true)
const [interfaceTextSize, setInterfaceTextSize] = useState<"small" | "medium" | "large">("medium")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another hardcoded 'medium' default. This should use the same constant suggested earlier for consistency.


const setListApiConfigMeta = useCallback(
(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
Expand Down Expand Up @@ -317,6 +320,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
if ((newState as any).includeTaskHistoryInEnhance !== undefined) {
setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance)
}
// Update interfaceTextSize if present in state message
if ((newState as any).interfaceTextSize !== undefined) {
setInterfaceTextSize((newState as any).interfaceTextSize)
}
// Handle marketplace data if present in state message
if (newState.marketplaceItems !== undefined) {
setMarketplaceItems(newState.marketplaceItems)
Expand Down Expand Up @@ -538,6 +545,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
},
includeTaskHistoryInEnhance,
setIncludeTaskHistoryInEnhance,
interfaceTextSize,
setInterfaceTextSize,
}

return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
Expand Down
11 changes: 11 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"slashCommands": "Slash Commands",
"prompts": "Prompts",
"experimental": "Experimental",
"interface": "Interface",
"language": "Language",
"about": "About Roo Code"
},
Expand Down Expand Up @@ -875,5 +876,15 @@
"output": "Output",
"cacheReads": "Cache reads"
}
},
"interface": {
"title": "Interface",
"textSize": {
"label": "Text Size",
"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.",
"small": "Small",
"medium": "Medium (Default)",
"large": "Large"
}
}
}
20 changes: 20 additions & 0 deletions webview-ui/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
@theme {
--font-display: var(--vscode-font-family);

/* Default text sizes (medium) */
--text-xs: calc(var(--vscode-font-size) * 0.85);
--text-sm: calc(var(--vscode-font-size) * 0.9);
--text-base: var(--vscode-font-size);
Expand Down Expand Up @@ -490,3 +491,22 @@ input[cmdk-input]:focus {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}

/* Interface text size classes */
.interface-text-small {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These CSS variables affect all text globally within the wrapper. Have you considered potential conflicts with other UI elements that might need different scaling? Perhaps we could use more specific CSS custom properties or add additional classes for finer control.

--text-xs: calc(var(--vscode-font-size) * 0.75);
--text-sm: calc(var(--vscode-font-size) * 0.8);
--text-base: calc(var(--vscode-font-size) * 0.85);
--text-lg: calc(var(--vscode-font-size) * 0.95);
}

.interface-text-medium {
/* Default sizes - already defined above */
}

.interface-text-large {
--text-xs: calc(var(--vscode-font-size) * 0.95);
--text-sm: calc(var(--vscode-font-size) * 1);
--text-base: calc(var(--vscode-font-size) * 1.15);
--text-lg: calc(var(--vscode-font-size) * 1.25);
}
Loading