fix: merge patches and build

This commit is contained in:
Baptiste Augrain
2026-02-24 20:51:31 +01:00
parent ccf4d751c8
commit 1dec128c07
7 changed files with 1959 additions and 1877 deletions

64
dev/merge-patches.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -e
normalize_file() {
if [[ "${1}" == *patch ]]; then
FILE="${1}"
else
FILE="${1}.patch"
fi
if [[ "${FILE}" == patches/* ]]; then
FILE="../${FILE}"
else
FILE="../patches/${FILE}"
fi
}
cd vscode || { echo "'vscode' dir not found"; exit 1; }
git add .
git reset -q --hard HEAD
while [[ -n "$( git log -1 | grep "VSCODIUM HELPER" )" ]]; do
git reset -q --hard HEAD~
done
git apply --reject "../patches/helper/settings.patch"
git add .
git commit --no-verify -q -m "VSCODIUM HELPER"
while [ $# -gt 1 ]; do
normalize_file "${1}"
echo "FILE: ${FILE}"
if [[ -f "${FILE}" ]]; then
if [[ -f "${FILE}.bak" ]]; then
mv -f $FILE{.bak,}
fi
git apply --reject "${FILE}" || true
fi
while [[ -n "$( find . -name '*.rej' -print )" ]]; do
echo
read -rp "Press any key when the conflict have been resolved..." -n1 -s
done
shift
done
normalize_file "${1}"
git add .
git diff --staged -U1 > "${FILE}"
if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then
git reset -q --hard HEAD
else
git reset -q --hard HEAD~
fi
echo "The patch has been generated."

View File

@@ -1,40 +1,81 @@
#!/usr/bin/env node
import path from 'node:path';
import process from 'node:process';
import fse from '@zokugun/fs-extra-plus/async';
import { err, OK, type Result, stringifyError, xtry } from '@zokugun/xtry';
import fg from 'fast-glob';
import postcss, { type Rule } from 'postcss';
const PREFIX_MAIN = '.monaco-workbench .part.sidebar';
const PREFIX_AUX = '.monaco-workbench .part.auxiliarybar';
const DEFAULT_GLOB = '../vscode/**/*.css';
type Area = {
name: string;
defaultSize: number;
files: string[];
prefixes: string[];
};
const PX_REGEX = /(-?\d+(\.\d+)?)px\b/g;
const COEFF_PRECISION = 6;
const HEADER = '/*** Generated for Custom Font Size ***/';
const AREAS: Record<string, Area> = {
activitybar: {
name: 'activitybar',
defaultSize: 16,
files: ['src/vs/workbench/browser/parts/activitybar/media/activityaction.css'],
prefixes: ['.monaco-workbench .activitybar'],
},
bottompane: {
name: 'bottompane',
defaultSize: 13,
files: ['src/vs/workbench/browser/parts/panel/media/panelpart.css'],
prefixes: ['.monaco-workbench .part.panel'],
},
statusbar: {
name: 'statusbar',
defaultSize: 12,
files: ['src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css'],
prefixes: ['.monaco-workbench .part.statusbar'],
},
tabs: {
name: 'tabs',
defaultSize: 13,
files: ['src/vs/workbench/browser/parts/editor/media/editortabscontrol.css', 'src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css', 'src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css'],
prefixes: ['.monaco-workbench .part.editor > .content .editor-group-container'],
},
};
function formatCoefficient(n: number): string {
const fixed = n.toFixed(COEFF_PRECISION);
return fixed.replace(/\.?0+$/, '');
}
function replacePx(_match: string, numStr: string): string {
const pxValue = Number.parseFloat(numStr);
const coeff = formatCoefficient(pxValue / 13);
function replacePx(area: Area) {
return (match: string, numStr: string): string => {
const pxValue = Number.parseFloat(numStr);
return `calc(var(--vscode-workbench-sidebar-font-size) * ${coeff})`;
if(pxValue === 1) {
return match;
}
const coeff = formatCoefficient(pxValue / area.defaultSize);
return `calc(var(--vscode-workbench-${area.name}-font-size) * ${coeff})`;
};
}
function transformPxValue(value: string): string {
return value.replaceAll(PX_REGEX, replacePx);
function transformPxValue(value: string, area: Area): string {
return value.replaceAll(PX_REGEX, replacePx(area));
}
async function processFile(filePath: string): Promise<Result<void, string>> {
const cssResult = await fse.readFile(filePath, 'utf8');
if(cssResult.fails) {
return err(stringifyError(cssResult.error));
async function processFile(filePath: string, area: Area): Promise<Result<void, string>> {
const readResult = await fse.readFile(filePath, 'utf8');
if(readResult.fails) {
return err(stringifyError(readResult.error));
}
const postcssResult = xtry(() => postcss.parse(cssResult.value, { from: filePath }));
const content = extractOriginal(readResult.value);
const postcssResult = xtry(() => postcss.parse(content, { from: filePath }));
if(postcssResult.fails) {
return err(`Failed to parse ${filePath}: ${stringifyError(postcssResult.error)}`);
}
@@ -47,7 +88,7 @@ async function processFile(filePath: string): Promise<Result<void, string>> {
rule.walkDecls((declaration) => {
if(PX_REGEX.test(declaration.value)) {
const newValue = transformPxValue(declaration.value);
const newValue = transformPxValue(declaration.value, area);
declarationsToAdd.push({ prop: declaration.prop, value: newValue });
@@ -57,9 +98,16 @@ async function processFile(filePath: string): Promise<Result<void, string>> {
if(hasPx && declarationsToAdd.length > 0) {
const selectors = (rule.selectors && rule.selectors.length > 0) ? rule.selectors : [rule.selector];
const mainSelectors = selectors.map((selector) => prefixSelector(selector, PREFIX_MAIN)).join(', ');
const auxSelectors = selectors.map((selector) => prefixSelector(selector, PREFIX_AUX)).join(', ');
const newRule = postcss.rule({ selector: `${mainSelectors}, ${auxSelectors}` });
const prefixeds: string[] = [];
for(const prefix of area.prefixes) {
const parts = prefix.split(' ');
const prefixed = selectors.map((s) => prefixSelector(s, parts)).join(', ');
prefixeds.push(prefixed);
}
const newRule = postcss.rule({ selector: `${prefixeds.join(', ')}` });
let length = 0;
@@ -77,9 +125,9 @@ async function processFile(filePath: string): Promise<Result<void, string>> {
});
if(generatedRoot.nodes && generatedRoot.nodes.length > 0) {
const writeResult = await fse.writeFile(filePath, cssResult.value + '\n\n\n' + generatedRoot.toString(), 'utf8');
const writeResult = await fse.writeFile(filePath, content + `\n\n\n${HEADER}\n\n` + generatedRoot.toString(), 'utf8');
if(writeResult.fails) {
return err(stringifyError(cssResult.error));
return err(stringifyError(readResult.error));
}
console.log(`Generated: ${filePath}`);
@@ -91,49 +139,78 @@ async function processFile(filePath: string): Promise<Result<void, string>> {
return OK;
}
async function main(): Promise<void> {
const pattern = process.argv[2] || DEFAULT_GLOB;
const entries = await fg(pattern, { dot: true, onlyFiles: true });
function extractOriginal(content: string): string {
const index = content.indexOf(HEADER);
if(entries.length === 0) {
console.log(`No files matched pattern: ${pattern}`);
if(index === -1) {
return content;
}
return content.slice(0, Math.max(0, index - 3));
}
function extractStyle(selector: string): string {
const match = /^(\.\w+)/.exec(selector);
return match?.[1] ?? '';
}
function mergeSelector(selectors: string[], prefixes: string[], index: number): void {
if(index >= prefixes.length) {
return;
}
for(const file of entries) {
const result = await processFile(file);
const selector = selectors[index];
const prefix = prefixes[index];
const style = extractStyle(prefix);
if(selector === style) {
if(prefix.length > style.length) {
selectors[index] = prefix;
}
mergeSelector(selectors, prefixes, index + 1);
}
else if(selector.startsWith(style)) {
mergeSelector(selectors, prefixes, index + 1);
}
else {
selectors.splice(index + 1, 0, ...prefixes.slice(index));
}
}
function prefixSelector(selector: string, prefixParts: string[]): string {
const parts = selector.split(' ');
if(parts[0] === '.mac' || parts[0] === '.linux' || parts[0] === '.windows') {
parts[0] = `${prefixParts[0]}${parts[0]}`;
parts.splice(1, 0, ...prefixParts.slice(1));
}
else {
mergeSelector(parts, prefixParts, 0);
}
return parts.join(' ');
}
async function main(): Promise<void> {
const name = process.argv[2] || 'sidebar';
const area = AREAS[name];
if(!area) {
console.log(`No area found for ${name}`);
console.log(`\nAvailable areas:\n- ${Object.keys(AREAS).join('\n- ')}`);
return;
}
for(const file of area.files) {
const result = await processFile(path.join('..', 'vscode', file), area);
if(result.fails) {
console.error(`Error processing ${file}:`, result.error);
}
}
}
function prefixSelector(selector: string, prefix: string): string {
const parts = selector.split(' ');
if(parts[0].startsWith('.monaco-workbench')) {
if(parts[1] === '.part') {
parts.splice(0, 2);
}
else {
parts.splice(0, 1);
}
parts.unshift(prefix);
return parts.join(' ');
}
else if(parts[0] === '.mac' || parts[0] === '.linux' || parts[0] === '.windows') {
const prefixParts = prefix.split(' ');
parts[0] = `${prefixParts.shift()}${parts[0]}`;
parts.splice(1, 0, ...prefixParts);
return parts.join(' ');
}
else {
return `${prefix} ${selector}`;
}
}
await main();

View File

@@ -1,117 +0,0 @@
diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css
index 13ff794..c19b125 100644
--- a/src/vs/workbench/browser/media/style.css
+++ b/src/vs/workbench/browser/media/style.css
@@ -11,20 +11,20 @@
-.monaco-workbench.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
-.monaco-workbench.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; }
-.monaco-workbench.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; }
-.monaco-workbench.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; }
-.monaco-workbench.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", sans-serif; }
-
-.monaco-workbench.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; }
-.monaco-workbench.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
-.monaco-workbench.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
-.monaco-workbench.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; }
-.monaco-workbench.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
+.monaco-workbench.mac { --monaco-font: -apple-system, BlinkMacSystemFont, sans-serif; }
+.monaco-workbench.mac:lang(zh-Hans) { --monaco-font: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; }
+.monaco-workbench.mac:lang(zh-Hant) { --monaco-font: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; }
+.monaco-workbench.mac:lang(ja) { --monaco-font: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; }
+.monaco-workbench.mac:lang(ko) { --monaco-font: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", sans-serif; }
+
+.monaco-workbench.windows { --monaco-font: "Segoe WPC", "Segoe UI", sans-serif; }
+.monaco-workbench.windows:lang(zh-Hans) { --monaco-font: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
+.monaco-workbench.windows:lang(zh-Hant) { --monaco-font: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
+.monaco-workbench.windows:lang(ja) { --monaco-font: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; }
+.monaco-workbench.windows:lang(ko) { --monaco-font: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
/* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */
-.monaco-workbench.linux { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; }
-.monaco-workbench.linux:lang(zh-Hans) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
-.monaco-workbench.linux:lang(zh-Hant) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; }
-.monaco-workbench.linux:lang(ja) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
-.monaco-workbench.linux:lang(ko) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
+.monaco-workbench.linux { --monaco-font: system-ui, "Ubuntu", "Droid Sans", sans-serif; }
+.monaco-workbench.linux:lang(zh-Hans) { --monaco-font: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
+.monaco-workbench.linux:lang(zh-Hant) { --monaco-font: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; }
+.monaco-workbench.linux:lang(ja) { --monaco-font: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
+.monaco-workbench.linux:lang(ko) { --monaco-font: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
@@ -55,2 +55,3 @@ body {
color: var(--vscode-foreground);
+ font-family: var(--vscode-workbench-font-family, var(--monaco-font));
}
diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts
index b1c5637..a09585e 100644
--- a/src/vs/workbench/browser/workbench.contribution.ts
+++ b/src/vs/workbench/browser/workbench.contribution.ts
@@ -675,2 +675,7 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
},
+ 'workbench.experimental.fontFamily': {
+ type: 'string',
+ description: localize('workbench.fontFamily', "Controls the font family in the workbench."),
+ 'tags': ['experimental']
+ },
'workbench.settings.editor': {
diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts
index 10e2c3e..7259aff 100644
--- a/src/vs/workbench/browser/workbench.ts
+++ b/src/vs/workbench/browser/workbench.ts
@@ -19,3 +19,3 @@ import { Position, Parts, IWorkbenchLayoutService, positionToString } from '../s
import { IStorageService, WillSaveStateReason, StorageScope, StorageTarget } from '../../platform/storage/common/storage.js';
-import { IConfigurationChangeEvent, IConfigurationService } from '../../platform/configuration/common/configuration.js';
+import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
import { IInstantiationService } from '../../platform/instantiation/common/instantiation.js';
@@ -233,3 +233,10 @@ export class Workbench extends Layout {
// Configuration changes
- this._register(configurationService.onDidChangeConfiguration(e => this.updateFontAliasing(e, configurationService)));
+ this._register(configurationService.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('workbench.fontAliasing')) {
+ this.updateFontAliasing(configurationService);
+ }
+ else if (e.affectsConfiguration('workbench.experimental.fontFamily')) {
+ this.updateFontFamily(configurationService);
+ }
+ }));
@@ -270,3 +277,3 @@ export class Workbench extends Layout {
private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined;
- private updateFontAliasing(e: IConfigurationChangeEvent | undefined, configurationService: IConfigurationService) {
+ private updateFontAliasing(configurationService: IConfigurationService) {
if (!isMacintosh) {
@@ -275,6 +282,2 @@ export class Workbench extends Layout {
- if (e && !e.affectsConfiguration('workbench.fontAliasing')) {
- return;
- }
-
const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing');
@@ -296,2 +299,19 @@ export class Workbench extends Layout {
+ private fontFamily: string | undefined;
+ private updateFontFamily(configurationService: IConfigurationService) {
+ let family = configurationService.getValue<string>('workbench.experimental.fontFamily');
+
+ if (this.fontFamily === family) {
+ return;
+ }
+
+ this.fontFamily = family;
+
+ if (family) {
+ this.mainContainer.style.setProperty('--vscode-workbench-font-family', family);
+ } else {
+ this.mainContainer.style.removeProperty('--vscode-workbench-font-family');
+ }
+ }
+
private restoreFontInfo(storageService: IStorageService, configurationService: IConfigurationService): void {
@@ -339,3 +359,5 @@ export class Workbench extends Layout {
// Apply font aliasing
- this.updateFontAliasing(undefined, configurationService);
+ this.updateFontAliasing(configurationService);
+
+ this.updateFontFamily(configurationService);

View File

@@ -1,477 +0,0 @@
diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css
index 473c1f8..57ad58c 100644
--- a/src/vs/workbench/browser/media/style.css
+++ b/src/vs/workbench/browser/media/style.css
@@ -54,6 +54,7 @@ body {
overflow: hidden;
color: var(--vscode-foreground);
font-family: var(--vscode-workbench-font-family, var(--monaco-font));
+ font-size: var(--vscode-workbench-font-size, 13px);
}
.monaco-workbench.web {
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
index 080b687..cbd14af 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
@@ -38,6 +38,7 @@ import { IExtensionService } from '../../../services/extensions/common/extension
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { IViewsService } from '../../../services/views/common/viewsService.js';
import { SwitchCompositeViewAction } from '../compositeBarActions.js';
+import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
export class ActivitybarPart extends Part {
@@ -65,8 +66,15 @@ export class ActivitybarPart extends Part {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
) {
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
+
+ this._register(configurationService.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('workbench.activityBar.experimental.fontFamily')) {
+ this.applyActivityBarFontFamily();
+ }
+ }));
}
private createCompositeBar(): PaneCompositeBar {
@@ -107,9 +115,24 @@ export class ActivitybarPart extends Part {
this.show();
}
+ this.applyActivityBarFontFamily(parent);
+
return this.content;
}
+ private applyActivityBarFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.activityBar.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-activitybar-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-activitybar-font-family');
+ }
+ }
+
getPinnedPaneCompositeIds(): string[] {
return this.compositeBar.value?.getPinnedPaneCompositeIds() ?? [];
}
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
index abe1427..7cd25d0 100644
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
@@ -6,6 +6,7 @@
.monaco-workbench .part.activitybar {
width: 48px;
height: 100%;
+ font-family: var(--vscode-workbench-activitybar-font-family, inherit);
}
.monaco-workbench .activitybar.bordered::before {
diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
index d3fa112..31d8df8 100644
--- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
+++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
@@ -137,6 +137,9 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) {
this.applyAuxiliaryBarFontSize();
}
+ if (e.affectsConfiguration('workbench.sideBar.experimental.fontFamily')) {
+ this.applyAuxiliaryBarFontFamily();
+ }
}));
}
@@ -178,6 +181,20 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px';
this.applyAuxiliaryBarFontSize(container);
+ this.applyAuxiliaryBarFontFamily(container);
+ }
+
+ private applyAuxiliaryBarFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.sideBar.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-sidebar-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-sidebar-font-family');
+ }
}
protected getCompositeBarOptions(): IPaneCompositeBarOptions {
diff --git a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css
index 6ec40df..398bbe1 100644
--- a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css
+++ b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css
@@ -28,6 +28,7 @@
.monaco-workbench .part.auxiliarybar > .content {
font-size: var(--vscode-workbench-sidebar-font-size, 13px);
+ font-family: var(--vscode-workbench-sidebar-font-family, inherit);
line-height: 1.4em;
}
diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
index b0a44e2..a946cb7 100644
--- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
+++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
@@ -46,6 +46,7 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser
import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js';
+import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js';
export class EditorCommandsContextActionRunner extends ActionRunner {
@@ -140,6 +141,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
@IThemeService themeService: IThemeService,
@IEditorResolverService private readonly editorResolverService: IEditorResolverService,
@IHostService private readonly hostService: IHostService,
+ @IConfigurationService protected readonly configurationService: IConfigurationService,
) {
super(themeService);
@@ -147,6 +149,12 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
const container = this.create(parent);
+ this._register(configurationService.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('workbench.tabs.experimental.fontFamily')) {
+ this.applyTabsFontFamily();
+ }
+ }));
+
// Context Keys
this.contextMenuContextKeyService = this._register(this.contextKeyService.createScoped(container));
const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection(
@@ -169,9 +177,23 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
protected create(parent: HTMLElement): HTMLElement {
this.updateTabHeight();
+ this.applyTabsFontFamily(parent);
return parent;
}
+ private applyTabsFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.parent;
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.tabs.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-tabs-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-tabs-font-family');
+ }
+ }
+
private get editorActionsEnabled(): boolean {
return this.groupsView.partOptions.editorActionsLocation === 'default' && this.groupsView.partOptions.showTabs !== 'none';
}
diff --git a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
index 57ab8ca..e9e0b2b 100644
--- a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
@@ -7,6 +7,7 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title {
cursor: pointer;
+ font-family: var(--vscode-workbench-tabs-font-family, inherit);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label,
diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css
index e1c147d..76c7b07 100644
--- a/src/vs/workbench/browser/parts/panel/media/panelpart.css
+++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css
@@ -8,6 +8,10 @@
visibility: hidden !important;
}
+.monaco-workbench .part.panel > .content {
+ font-family: var(--vscode-workbench-panel-font-family, inherit);
+}
+
.monaco-workbench .part.panel.bottom .composite.title {
border-top-width: 1px;
border-top-style: solid;
diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts
index 585bc16..a5d3ff4 100644
--- a/src/vs/workbench/browser/parts/panel/panelPart.ts
+++ b/src/vs/workbench/browser/parts/panel/panelPart.ts
@@ -110,6 +110,9 @@ export class PanelPart extends AbstractPaneCompositePart {
if (e.affectsConfiguration('workbench.panel.showLabels')) {
this.updateCompositeBar(true);
}
+ if (e.affectsConfiguration('workbench.bottomPane.experimental.fontFamily')) {
+ this.applyPanelFontFamily();
+ }
}));
}
@@ -126,6 +129,21 @@ export class PanelPart extends AbstractPaneCompositePart {
if (this.titleArea) {
this.titleArea.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || '';
}
+
+ this.applyPanelFontFamily(container);
+ }
+
+ private applyPanelFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.bottomPane.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-panel-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-panel-font-family');
+ }
}
protected getCompositeBarOptions(): IPaneCompositeBarOptions {
diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
index 7088a97..3a5ff82 100644
--- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
+++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
@@ -198,4 +198,8 @@
.monaco-workbench .part.sidebar .monaco-custom-toggle {
height: calc(var(--vscode-workbench-sidebar-font-size) * 1.539);
width: calc(var(--vscode-workbench-sidebar-font-size) * 1.539);
-} */
\ No newline at end of file
+} */
+
+.monaco-workbench .part.sidebar > .content {
+ font-family: var(--vscode-workbench-sidebar-font-family, inherit);
+}
\ No newline at end of file
diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts
index 9b6ae18..322f69b 100644
--- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts
+++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts
@@ -114,6 +114,9 @@ export class SidebarPart extends AbstractPaneCompositePart {
if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) {
this.applySidebarFontSize();
}
+ if (e.affectsConfiguration('workbench.sideBar.experimental.fontFamily')) {
+ this.applySidebarFontFamily();
+ }
}));
this.registerActions();
@@ -155,6 +158,7 @@ export class SidebarPart extends AbstractPaneCompositePart {
container.style.outlineColor = this.getColor(SIDE_BAR_DRAG_AND_DROP_BACKGROUND) ?? '';
this.applySidebarFontSize(container);
+ this.applySidebarFontFamily(container);
}
override layout(width: number, height: number, top: number, left: number): void {
@@ -297,6 +301,19 @@ export class SidebarPart extends AbstractPaneCompositePart {
target.style.setProperty('--vscode-workbench-sidebar-font-size', `${FONT.sidebarSize}px`);
}
+ private applySidebarFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.sideBar.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-sidebar-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-sidebar-font-family');
+ }
+ }
+
private registerActions(): void {
const that = this;
this._register(registerAction2(class extends Action2 {
diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
index 7faaf9e..b633ee1 100644
--- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
+++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
@@ -9,6 +9,7 @@
width: 100%;
height: 22px;
font-size: 12px;
+ font-family: var(--vscode-workbench-statusbar-font-family, inherit);
display: flex;
overflow: hidden;
}
diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
index 4bd53d4..62e78d0 100644
--- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
+++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
@@ -36,6 +36,7 @@ import { StatusBarFocused } from '../../../common/contextkeys.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { IView } from '../../../../base/browser/ui/grid/grid.js';
import { isManagedHoverTooltipHTMLElement, isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js';
+import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
export interface IStatusbarEntryContainer extends IDisposable {
@@ -160,9 +161,16 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
) {
super(id, { hasTitle: false }, themeService, storageService, layoutService);
+ this._register(configurationService.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('workbench.statusBar.experimental.fontFamily')) {
+ this.applyStatusBarFontFamily();
+ }
+ }));
+
this.viewModel = this._register(new StatusbarViewModel(storageService));
this.onDidChangeEntryVisibility = this.viewModel.onDidChangeEntryVisibility;
@@ -427,9 +435,24 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer {
// Initial status bar entries
this.createInitialStatusbarEntries();
+ this.applyStatusBarFontFamily(this.element);
+
return this.element;
}
+ private applyStatusBarFontFamily(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+ const family = this.configurationService.getValue<string>('workbench.statusBar.experimental.fontFamily');
+ if (family) {
+ target.style.setProperty('--vscode-workbench-statusbar-font-family', family);
+ } else {
+ target.style.removeProperty('--vscode-workbench-statusbar-font-family');
+ }
+ }
+
private createInitialStatusbarEntries(): void {
// Add items in order according to alignment
diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts
index 3eaad2e..8a9eb0e 100644
--- a/src/vs/workbench/browser/workbench.contribution.ts
+++ b/src/vs/workbench/browser/workbench.contribution.ts
@@ -667,6 +667,44 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
description: localize('workbench.fontFamily', "Controls the font family in the workbench."),
'tags': ['experimental']
},
+ 'workbench.experimental.fontSize': {
+ type: 'number',
+ default: 13,
+ minimum: 6,
+ maximum: 32,
+ markdownDescription: localize('workbench.fontSize', "Controls the font size in pixels for the workbench."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.sideBar.experimental.fontFamily': {
+ type: 'string',
+ default: '',
+ markdownDescription: localize('sideBarFontFamily', "Controls the font family in the side bar."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.statusBar.experimental.fontFamily': {
+ type: 'string',
+ default: '',
+ markdownDescription: localize('statusBarFontFamily', "Controls the font family in the status bar."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.tabs.experimental.fontFamily': {
+ type: 'string',
+ default: '',
+ markdownDescription: localize('tabsFontFamily', "Controls the font family in editor tabs."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.bottomPane.experimental.fontFamily': {
+ type: 'string',
+ default: '',
+ markdownDescription: localize('bottomPaneFontFamily', "Controls the font family in the bottom panel."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.activityBar.experimental.fontFamily': {
+ type: 'string',
+ default: '',
+ markdownDescription: localize('activityBarFontFamily', "Controls the font family in the activity bar."),
+ 'tags': ['accessibility', 'experimental']
+ },
'workbench.settings.editor': {
'type': 'string',
'enum': ['ui', 'json'],
diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts
index 7259aff..ee4cbf8 100644
--- a/src/vs/workbench/browser/workbench.ts
+++ b/src/vs/workbench/browser/workbench.ts
@@ -235,9 +235,12 @@ export class Workbench extends Layout {
if (e.affectsConfiguration('workbench.fontAliasing')) {
this.updateFontAliasing(configurationService);
}
- else if (e.affectsConfiguration('workbench.experimental.fontFamily')) {
+ if (e.affectsConfiguration('workbench.experimental.fontFamily')) {
this.updateFontFamily(configurationService);
}
+ if (e.affectsConfiguration('workbench.experimental.fontSize')) {
+ this.updateFontSize(configurationService);
+ }
}));
// Font Info
@@ -297,6 +300,33 @@ export class Workbench extends Layout {
}
}
+ private fontSize: number | undefined;
+ private fontSizeUserSet: boolean | undefined;
+ private updateFontSize(configurationService: IConfigurationService) {
+ const raw = configurationService.getValue<number>('workbench.experimental.fontSize');
+ const size = Math.max(6, Math.min(32, typeof raw === 'number' ? raw : 13));
+
+ const inspected = configurationService.inspect<number>('workbench.experimental.fontSize');
+ const isUserSet = inspected.userValue !== undefined
+ || inspected.userLocalValue !== undefined
+ || inspected.userRemoteValue !== undefined
+ || inspected.workspaceValue !== undefined
+ || inspected.workspaceFolderValue !== undefined;
+
+ if (this.fontSize === size && this.fontSizeUserSet === isUserSet) {
+ return;
+ }
+
+ this.fontSize = size;
+ this.fontSizeUserSet = isUserSet;
+
+ if (isUserSet) {
+ this.mainContainer.style.setProperty('--vscode-workbench-font-size', `${size}px`);
+ } else {
+ this.mainContainer.style.removeProperty('--vscode-workbench-font-size');
+ }
+ }
+
private fontFamily: string | undefined;
private updateFontFamily(configurationService: IConfigurationService) {
let family = configurationService.getValue<string>('workbench.experimental.fontFamily');
@@ -360,6 +390,7 @@ export class Workbench extends Layout {
this.updateFontAliasing(configurationService);
this.updateFontFamily(configurationService);
+ this.updateFontSize(configurationService);
// Warm up font cache information before building up too many dom elements
this.restoreFontInfo(storageService, configurationService);

View File

@@ -1,917 +0,0 @@
diff --git a/src/vs/base/common/font.ts b/src/vs/base/common/font.ts
index 2be97d7..8a8d60e 100644
--- a/src/vs/base/common/font.ts
+++ b/src/vs/base/common/font.ts
@@ -1,3 +1,27 @@
+/**
+ * Inspect a configuration value and return whether it was explicitly set by the user,
+ * along with the clamped numeric value.
+ */
+export function inspectFontSize(
+ configurationService: { inspect<T>(key: string): { userValue?: T; userLocalValue?: T; userRemoteValue?: T; workspaceValue?: T; workspaceFolderValue?: T }; getValue<T>(key: string): T },
+ key: string,
+ defaultSize: number,
+ min: number = 6,
+ max: number = 32
+): { isUserSet: boolean; size: number } {
+ const inspected = configurationService.inspect<number>(key);
+ const isUserSet = inspected.userValue !== undefined
+ || inspected.userLocalValue !== undefined
+ || inspected.userRemoteValue !== undefined
+ || inspected.workspaceValue !== undefined
+ || inspected.workspaceFolderValue !== undefined;
+
+ const raw = configurationService.getValue<number>(key);
+ const size = Math.max(min, Math.min(max, typeof raw === 'number' ? raw : defaultSize));
+
+ return { isUserSet, size };
+}
+
export const FONT = {
sidebarSize: 13,
sidebarSize8: 8,
@@ -16,8 +40,29 @@ export const FONT = {
sidebarSize44: 44,
sidebarSize62: 62,
sidebarSize72: 72,
+
+ statusBarSize: 12,
+ statusBarSize22: 22,
+
+ panelSize: 13,
+ panelSize22: 22,
+
+ activityBarSize: 16,
+ activityBarSize16: 16,
+ activityBarSize24: 24,
+ activityBarSize32: 32,
+ activityBarSize36: 36,
+ activityBarSize48: 48,
+
+ tabsSize: 13,
+ tabsSize22: 22,
+ tabsSize35: 35,
+ tabsSize38: 38,
+ tabsSize80: 80,
+ tabsSize120: 120,
};
+// Sidebar coefficients (base 13)
const COEFF_8 = 8/13;
const COEFF_10 = 10/13;
const COEFF_16 = 16/13;
@@ -54,3 +99,51 @@ export function updateSidebarSize(size: number): void {
FONT.sidebarSize62 = size * COEFF_62;
FONT.sidebarSize72 = size * COEFF_72;
}
+
+// Status bar coefficients (base 12)
+const SB_COEFF_22 = 22/12;
+
+export function updateStatusBarSize(size: number): void {
+ FONT.statusBarSize = size;
+ FONT.statusBarSize22 = size * SB_COEFF_22;
+}
+
+// Panel coefficients (base 13)
+const PN_COEFF_22 = 22/13;
+
+export function updatePanelSize(size: number): void {
+ FONT.panelSize = size;
+ FONT.panelSize22 = size * PN_COEFF_22;
+}
+
+// Activity bar coefficients (base 16)
+const AB_COEFF_16 = 16/16;
+const AB_COEFF_24 = 24/16;
+const AB_COEFF_32 = 32/16;
+const AB_COEFF_36 = 36/16;
+const AB_COEFF_48 = 48/16;
+
+export function updateActivityBarSize(size: number): void {
+ FONT.activityBarSize = size;
+ FONT.activityBarSize16 = size * AB_COEFF_16;
+ FONT.activityBarSize24 = size * AB_COEFF_24;
+ FONT.activityBarSize32 = size * AB_COEFF_32;
+ FONT.activityBarSize36 = size * AB_COEFF_36;
+ FONT.activityBarSize48 = size * AB_COEFF_48;
+}
+
+// Tabs coefficients (base 13)
+const TB_COEFF_22 = 22/13;
+const TB_COEFF_35 = 35/13;
+const TB_COEFF_38 = 38/13;
+const TB_COEFF_80 = 80/13;
+const TB_COEFF_120 = 120/13;
+
+export function updateTabsSize(size: number): void {
+ FONT.tabsSize = size;
+ FONT.tabsSize22 = size * TB_COEFF_22;
+ FONT.tabsSize35 = size * TB_COEFF_35;
+ FONT.tabsSize38 = size * TB_COEFF_38;
+ FONT.tabsSize80 = size * TB_COEFF_80;
+ FONT.tabsSize120 = size * TB_COEFF_120;
+}
diff --git a/src/vs/base/test/common/font.test.ts b/src/vs/base/test/common/font.test.ts
new file mode 100644
index 0000000..dcde0e7
--- /dev/null
+++ b/src/vs/base/test/common/font.test.ts
@@ -0,0 +1,482 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) VSCodium. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import assert from 'assert';
+import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../test/common/utils.js';
+import { FONT, updateSidebarSize, updateStatusBarSize, updatePanelSize, updateActivityBarSize, updateTabsSize } from '../../common/font.js';
+
+/**
+ * Test file for src/vs/base/common/font.ts
+ *
+ * Tests all update*Size() functions for:
+ * - Default values match upstream VS Code hardcoded constants
+ * - Proportional scaling preserves ratios at non-default sizes
+ * - Boundary values (minimum=6, maximum=32) produce positive values
+ * - Reset to default restores original values
+ * - Non-divisible sizes produce consistent coefficient-based output
+ * - Cross-area updates do not mutate unrelated fields
+ */
+
+const EPSILON = 1e-9;
+
+/** Assert two numbers are equal within floating-point tolerance */
+function assertClose(actual: number, expected: number, message?: string): void {
+ const diff = Math.abs(actual - expected);
+ assert.ok(diff < EPSILON, `${message ?? ''} expected ${expected}, got ${actual} (diff: ${diff})`);
+}
+
+/**
+ * Snapshot all FONT fields to detect unintended mutations.
+ * Returns a plain object copy of every enumerable property.
+ */
+function snapshotFont(): Record<string, number> {
+ const snap: Record<string, number> = {};
+ for (const key of Object.keys(FONT)) {
+ snap[key] = (FONT as Record<string, number>)[key];
+ }
+ return snap;
+}
+
+/** Assert that specific fields in FONT have not changed from a snapshot */
+function assertFieldsUnchanged(snapshot: Record<string, number>, prefix: string, message: string): void {
+ for (const key of Object.keys(snapshot)) {
+ if (key.startsWith(prefix)) {
+ assertClose((FONT as Record<string, number>)[key], snapshot[key], `${message}: ${key}`);
+ }
+ }
+}
+
+suite('FONT - Sidebar Size', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ // Capture defaults before any test mutates them
+ const DEFAULTS = snapshotFont();
+
+ teardown(() => {
+ // Reset after each test
+ updateSidebarSize(13);
+ });
+
+ test('defaults match upstream VS Code constants', () => {
+ assert.strictEqual(FONT.sidebarSize, 13);
+ assert.strictEqual(FONT.sidebarSize8, 8);
+ assert.strictEqual(FONT.sidebarSize10, 10);
+ assert.strictEqual(FONT.sidebarSize16, 16);
+ assert.strictEqual(FONT.sidebarSize17, 17);
+ assert.strictEqual(FONT.sidebarSize18, 18);
+ assert.strictEqual(FONT.sidebarSize20, 20);
+ assert.strictEqual(FONT.sidebarSize22, 22);
+ assert.strictEqual(FONT.sidebarSize23, 23);
+ assert.strictEqual(FONT.sidebarSize24, 24);
+ assert.strictEqual(FONT.sidebarSize26, 26);
+ assert.strictEqual(FONT.sidebarSize28, 28);
+ assert.strictEqual(FONT.sidebarSize34, 34);
+ assert.strictEqual(FONT.sidebarSize39, 39);
+ assert.strictEqual(FONT.sidebarSize44, 44);
+ assert.strictEqual(FONT.sidebarSize62, 62);
+ assert.strictEqual(FONT.sidebarSize72, 72);
+ });
+
+ test('proportional scaling preserves ratios at 2x', () => {
+ updateSidebarSize(26); // 2x default
+ assert.strictEqual(FONT.sidebarSize, 26);
+ assert.strictEqual(FONT.sidebarSize8, 16);
+ assert.strictEqual(FONT.sidebarSize22, 44);
+ assert.strictEqual(FONT.sidebarSize44, 88);
+ assert.strictEqual(FONT.sidebarSize72, 144);
+ });
+
+ test('non-divisible size produces consistent coefficient-based values', () => {
+ updateSidebarSize(7);
+ assertClose(FONT.sidebarSize22, 7 * (22 / 13));
+ assertClose(FONT.sidebarSize44, 7 * (44 / 13));
+ assertClose(FONT.sidebarSize72, 7 * (72 / 13));
+ });
+
+ test('another non-divisible size (31)', () => {
+ updateSidebarSize(31);
+ assertClose(FONT.sidebarSize22, 31 * (22 / 13));
+ assertClose(FONT.sidebarSize44, 31 * (44 / 13));
+ });
+
+ test('minimum value (6) produces positive values', () => {
+ updateSidebarSize(6);
+ assert.strictEqual(FONT.sidebarSize, 6);
+ assert.ok(FONT.sidebarSize8 > 0, 'sidebarSize8 must be positive');
+ assert.ok(FONT.sidebarSize10 > 0, 'sidebarSize10 must be positive');
+ assert.ok(FONT.sidebarSize22 > 0, 'sidebarSize22 must be positive');
+ assert.ok(FONT.sidebarSize72 > 0, 'sidebarSize72 must be positive');
+ });
+
+ test('maximum value (32) produces reasonable values', () => {
+ updateSidebarSize(32);
+ assert.strictEqual(FONT.sidebarSize, 32);
+ assert.ok(FONT.sidebarSize22 > 40, 'row height should scale up');
+ assert.ok(FONT.sidebarSize22 < 80, 'row height should not be extreme');
+ });
+
+ test('reset to default restores all values', () => {
+ updateSidebarSize(20);
+ assert.notStrictEqual(FONT.sidebarSize22, DEFAULTS.sidebarSize22);
+ updateSidebarSize(13);
+ assertFieldsUnchanged(DEFAULTS, 'sidebar', 'after reset');
+ });
+
+ test('multiple updates in sequence are idempotent at same value', () => {
+ updateSidebarSize(18);
+ const snap = snapshotFont();
+ updateSidebarSize(18);
+ assertFieldsUnchanged(snap, 'sidebar', 'idempotent');
+ });
+});
+
+suite('FONT - Status Bar Size', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ teardown(() => {
+ updateStatusBarSize(12);
+ });
+
+ test('defaults match upstream VS Code status bar constants', () => {
+ assert.strictEqual(FONT.statusBarSize, 12);
+ assert.strictEqual(FONT.statusBarSize22, 22); // StatusbarPart.HEIGHT
+ });
+
+ test('proportional scaling at 2x', () => {
+ updateStatusBarSize(24);
+ assert.strictEqual(FONT.statusBarSize, 24);
+ assert.strictEqual(FONT.statusBarSize22, 44);
+ });
+
+ test('non-divisible size (7)', () => {
+ updateStatusBarSize(7);
+ assertClose(FONT.statusBarSize22, 7 * (22 / 12));
+ });
+
+ test('minimum value (6) produces positive height', () => {
+ updateStatusBarSize(6);
+ assert.strictEqual(FONT.statusBarSize, 6);
+ assert.ok(FONT.statusBarSize22 > 0, 'height must be positive');
+ assertClose(FONT.statusBarSize22, 6 * (22 / 12));
+ });
+
+ test('maximum value (32) produces reasonable height', () => {
+ updateStatusBarSize(32);
+ assertClose(FONT.statusBarSize22, 32 * (22 / 12));
+ assert.ok(FONT.statusBarSize22 > 50, 'height should scale up');
+ assert.ok(FONT.statusBarSize22 < 70, 'height should not be extreme');
+ });
+
+ test('reset to default restores values', () => {
+ updateStatusBarSize(20);
+ updateStatusBarSize(12);
+ assert.strictEqual(FONT.statusBarSize, 12);
+ assert.strictEqual(FONT.statusBarSize22, 22);
+ });
+});
+
+suite('FONT - Panel Size', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ teardown(() => {
+ updatePanelSize(13);
+ });
+
+ test('defaults match sidebar defaults (same base size)', () => {
+ assert.strictEqual(FONT.panelSize, 13);
+ assert.strictEqual(FONT.panelSize22, 22);
+ });
+
+ test('panel and sidebar can have independent sizes', () => {
+ updatePanelSize(18);
+ assert.strictEqual(FONT.panelSize, 18);
+ assert.notStrictEqual(FONT.panelSize22, 22);
+ // Sidebar unchanged
+ assert.strictEqual(FONT.sidebarSize, 13);
+ assert.strictEqual(FONT.sidebarSize22, 22);
+ });
+
+ test('proportional scaling at 2x', () => {
+ updatePanelSize(26);
+ assert.strictEqual(FONT.panelSize22, 44);
+ });
+
+ test('non-divisible size (7)', () => {
+ updatePanelSize(7);
+ assertClose(FONT.panelSize22, 7 * (22 / 13));
+ });
+
+ test('minimum value (6)', () => {
+ updatePanelSize(6);
+ assert.ok(FONT.panelSize22 > 0, 'row height must be positive');
+ assert.ok(FONT.panelSize22 >= 10, 'row height at minimum should be usable');
+ });
+
+ test('maximum value (32)', () => {
+ updatePanelSize(32);
+ assert.ok(FONT.panelSize22 > 50, 'row height should scale up');
+ });
+
+ test('reset to default restores values', () => {
+ updatePanelSize(20);
+ updatePanelSize(13);
+ assert.strictEqual(FONT.panelSize, 13);
+ assert.strictEqual(FONT.panelSize22, 22);
+ });
+});
+
+suite('FONT - Activity Bar Size', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ teardown(() => {
+ updateActivityBarSize(16);
+ });
+
+ test('defaults match upstream VS Code activity bar constants', () => {
+ assert.strictEqual(FONT.activityBarSize, 16);
+ assert.strictEqual(FONT.activityBarSize16, 16); // COMPACT_ICON_SIZE
+ assert.strictEqual(FONT.activityBarSize24, 24); // ICON_SIZE
+ assert.strictEqual(FONT.activityBarSize32, 32); // COMPACT_ACTION_HEIGHT
+ assert.strictEqual(FONT.activityBarSize36, 36); // COMPACT_ACTIVITYBAR_WIDTH
+ assert.strictEqual(FONT.activityBarSize48, 48); // ACTION_HEIGHT / ACTIVITYBAR_WIDTH
+ });
+
+ test('proportional scaling at 2x', () => {
+ updateActivityBarSize(32);
+ assert.strictEqual(FONT.activityBarSize, 32);
+ assert.strictEqual(FONT.activityBarSize16, 32); // compact icon = base size
+ assert.strictEqual(FONT.activityBarSize24, 48);
+ assert.strictEqual(FONT.activityBarSize32, 64);
+ assert.strictEqual(FONT.activityBarSize36, 72);
+ assert.strictEqual(FONT.activityBarSize48, 96);
+ });
+
+ test('compact constants scale correctly', () => {
+ updateActivityBarSize(20);
+ // Compact icon size = base size * (16/16) = 20
+ assertClose(FONT.activityBarSize16, 20 * (16 / 16));
+ // Compact width = base * (36/16) = 45
+ assertClose(FONT.activityBarSize36, 20 * (36 / 16));
+ // Compact action height = base * (32/16) = 40
+ assertClose(FONT.activityBarSize32, 20 * (32 / 16));
+ });
+
+ test('non-divisible size (7)', () => {
+ updateActivityBarSize(7);
+ assertClose(FONT.activityBarSize24, 7 * (24 / 16));
+ assertClose(FONT.activityBarSize48, 7 * (48 / 16));
+ assertClose(FONT.activityBarSize36, 7 * (36 / 16));
+ });
+
+ test('minimum value (6) produces positive values', () => {
+ updateActivityBarSize(6);
+ assert.strictEqual(FONT.activityBarSize, 6);
+ assert.ok(FONT.activityBarSize16 > 0, 'compact icon size must be positive');
+ assert.ok(FONT.activityBarSize24 > 0, 'icon size must be positive');
+ assert.ok(FONT.activityBarSize36 > 0, 'compact width must be positive');
+ assert.ok(FONT.activityBarSize48 > 0, 'action height must be positive');
+ });
+
+ test('maximum value (32) produces reasonable values', () => {
+ updateActivityBarSize(32);
+ assert.strictEqual(FONT.activityBarSize48, 96);
+ assert.ok(FONT.activityBarSize48 <= 100, 'action height should be bounded');
+ });
+
+ test('reset to default restores values', () => {
+ updateActivityBarSize(24);
+ updateActivityBarSize(16);
+ assert.strictEqual(FONT.activityBarSize, 16);
+ assert.strictEqual(FONT.activityBarSize16, 16);
+ assert.strictEqual(FONT.activityBarSize24, 24);
+ assert.strictEqual(FONT.activityBarSize32, 32);
+ assert.strictEqual(FONT.activityBarSize36, 36);
+ assert.strictEqual(FONT.activityBarSize48, 48);
+ });
+});
+
+suite('FONT - Tabs Size', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ teardown(() => {
+ updateTabsSize(13);
+ });
+
+ test('defaults match upstream VS Code tab constants', () => {
+ assert.strictEqual(FONT.tabsSize, 13);
+ assert.strictEqual(FONT.tabsSize22, 22); // EDITOR_TAB_HEIGHT compact
+ assert.strictEqual(FONT.tabsSize35, 35); // EDITOR_TAB_HEIGHT normal
+ assert.strictEqual(FONT.tabsSize38, 38); // TAB_WIDTH compact
+ assert.strictEqual(FONT.tabsSize80, 80); // TAB_WIDTH shrink
+ assert.strictEqual(FONT.tabsSize120, 120); // TAB_WIDTH fit
+ });
+
+ test('proportional scaling preserves tab height/width ratios at 2x', () => {
+ updateTabsSize(26);
+ assert.strictEqual(FONT.tabsSize, 26);
+ assert.strictEqual(FONT.tabsSize22, 44);
+ assert.strictEqual(FONT.tabsSize35, 70);
+ assert.strictEqual(FONT.tabsSize38, 76);
+ assert.strictEqual(FONT.tabsSize80, 160);
+ assert.strictEqual(FONT.tabsSize120, 240);
+ });
+
+ test('non-divisible size (7)', () => {
+ updateTabsSize(7);
+ assertClose(FONT.tabsSize22, 7 * (22 / 13));
+ assertClose(FONT.tabsSize35, 7 * (35 / 13));
+ assertClose(FONT.tabsSize80, 7 * (80 / 13));
+ assertClose(FONT.tabsSize120, 7 * (120 / 13));
+ });
+
+ test('minimum value (6) produces usable tab dimensions', () => {
+ updateTabsSize(6);
+ assert.strictEqual(FONT.tabsSize, 6);
+ assert.ok(FONT.tabsSize35 > 10, 'normal tab height must be clickable');
+ assert.ok(FONT.tabsSize22 > 8, 'compact tab height must be usable');
+ assert.ok(FONT.tabsSize38 > 0, 'compact tab width must be positive');
+ assert.ok(FONT.tabsSize80 > 0, 'shrink tab width must be positive');
+ });
+
+ test('maximum value (32) produces reasonable tab dimensions', () => {
+ updateTabsSize(32);
+ assert.ok(FONT.tabsSize35 > 80, 'normal tab height should scale up');
+ assert.ok(FONT.tabsSize35 < 100, 'normal tab height should be bounded');
+ });
+
+ test('reset to default restores all values', () => {
+ updateTabsSize(20);
+ updateTabsSize(13);
+ assert.strictEqual(FONT.tabsSize, 13);
+ assert.strictEqual(FONT.tabsSize22, 22);
+ assert.strictEqual(FONT.tabsSize35, 35);
+ assert.strictEqual(FONT.tabsSize38, 38);
+ assert.strictEqual(FONT.tabsSize80, 80);
+ assert.strictEqual(FONT.tabsSize120, 120);
+ });
+});
+
+suite('FONT - Cross-area independence', () => {
+
+ ensureNoDisposablesAreLeakedInTestSuite();
+
+ teardown(() => {
+ updateSidebarSize(13);
+ updateStatusBarSize(12);
+ updatePanelSize(13);
+ updateActivityBarSize(16);
+ updateTabsSize(13);
+ });
+
+ test('updating sidebar does not affect any other area (full derived field check)', () => {
+ const before = snapshotFont();
+ updateSidebarSize(20);
+
+ // All non-sidebar fields must be unchanged
+ assertFieldsUnchanged(before, 'statusBar', 'after sidebar update');
+ assertFieldsUnchanged(before, 'panel', 'after sidebar update');
+ assertFieldsUnchanged(before, 'activityBar', 'after sidebar update');
+ assertFieldsUnchanged(before, 'tabs', 'after sidebar update');
+ });
+
+ test('updating statusBar does not affect any other area', () => {
+ const before = snapshotFont();
+ updateStatusBarSize(20);
+
+ assertFieldsUnchanged(before, 'sidebar', 'after statusBar update');
+ assertFieldsUnchanged(before, 'panel', 'after statusBar update');
+ assertFieldsUnchanged(before, 'activityBar', 'after statusBar update');
+ assertFieldsUnchanged(before, 'tabs', 'after statusBar update');
+ });
+
+ test('updating panel does not affect any other area', () => {
+ const before = snapshotFont();
+ updatePanelSize(20);
+
+ assertFieldsUnchanged(before, 'sidebar', 'after panel update');
+ assertFieldsUnchanged(before, 'statusBar', 'after panel update');
+ assertFieldsUnchanged(before, 'activityBar', 'after panel update');
+ assertFieldsUnchanged(before, 'tabs', 'after panel update');
+ });
+
+ test('updating activityBar does not affect any other area', () => {
+ const before = snapshotFont();
+ updateActivityBarSize(20);
+
+ assertFieldsUnchanged(before, 'sidebar', 'after activityBar update');
+ assertFieldsUnchanged(before, 'statusBar', 'after activityBar update');
+ assertFieldsUnchanged(before, 'panel', 'after activityBar update');
+ assertFieldsUnchanged(before, 'tabs', 'after activityBar update');
+ });
+
+ test('updating tabs does not affect any other area', () => {
+ const before = snapshotFont();
+ updateTabsSize(20);
+
+ assertFieldsUnchanged(before, 'sidebar', 'after tabs update');
+ assertFieldsUnchanged(before, 'statusBar', 'after tabs update');
+ assertFieldsUnchanged(before, 'panel', 'after tabs update');
+ assertFieldsUnchanged(before, 'activityBar', 'after tabs update');
+ });
+
+ test('all areas set to same value produce different derived values due to different coefficients', () => {
+ const commonSize = 18;
+ updateSidebarSize(commonSize);
+ updateStatusBarSize(commonSize);
+ updatePanelSize(commonSize);
+ updateActivityBarSize(commonSize);
+ updateTabsSize(commonSize);
+
+ assert.strictEqual(FONT.sidebarSize, commonSize);
+ assert.strictEqual(FONT.statusBarSize, commonSize);
+ assert.strictEqual(FONT.panelSize, commonSize);
+ assert.strictEqual(FONT.activityBarSize, commonSize);
+ assert.strictEqual(FONT.tabsSize, commonSize);
+
+ // sidebarSize22 = 18 * (22/13), statusBarSize22 = 18 * (22/12)
+ // Different coefficients → different results
+ assert.notStrictEqual(FONT.sidebarSize22, FONT.statusBarSize22,
+ 'same base size should produce different derived values due to different coefficients');
+ });
+
+ test('sequential updates across all areas and full reset', () => {
+ updateSidebarSize(8);
+ updateStatusBarSize(10);
+ updatePanelSize(15);
+ updateActivityBarSize(20);
+ updateTabsSize(25);
+
+ // Verify all set correctly
+ assert.strictEqual(FONT.sidebarSize, 8);
+ assert.strictEqual(FONT.statusBarSize, 10);
+ assert.strictEqual(FONT.panelSize, 15);
+ assert.strictEqual(FONT.activityBarSize, 20);
+ assert.strictEqual(FONT.tabsSize, 25);
+
+ // Reset all
+ updateSidebarSize(13);
+ updateStatusBarSize(12);
+ updatePanelSize(13);
+ updateActivityBarSize(16);
+ updateTabsSize(13);
+
+ // All defaults restored
+ assert.strictEqual(FONT.sidebarSize, 13);
+ assert.strictEqual(FONT.sidebarSize22, 22);
+ assert.strictEqual(FONT.statusBarSize, 12);
+ assert.strictEqual(FONT.statusBarSize22, 22);
+ assert.strictEqual(FONT.panelSize, 13);
+ assert.strictEqual(FONT.panelSize22, 22);
+ assert.strictEqual(FONT.activityBarSize, 16);
+ assert.strictEqual(FONT.activityBarSize48, 48);
+ assert.strictEqual(FONT.tabsSize, 13);
+ assert.strictEqual(FONT.tabsSize35, 35);
+ });
+});
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
index cbd14af..83153fe 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
@@ -38,11 +38,11 @@ import { IExtensionService } from '../../../services/extensions/common/extension
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { IViewsService } from '../../../services/views/common/viewsService.js';
import { SwitchCompositeViewAction } from '../compositeBarActions.js';
-import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
+import { FONT, inspectFontSize, updateActivityBarSize } from '../../../../base/common/font.js';
export class ActivitybarPart extends Part {
- static readonly ACTION_HEIGHT = 48;
+ static get ACTION_HEIGHT() { return FONT.activityBarSize48; }
static readonly pinnedViewContainersKey = 'workbench.activity.pinnedViewlets2';
static readonly placeholderViewContainersKey = 'workbench.activity.placeholderViewlets';
@@ -74,6 +74,9 @@ export class ActivitybarPart extends Part {
if (e.affectsConfiguration('workbench.activityBar.experimental.fontFamily')) {
this.applyActivityBarFontFamily();
}
+ if (e.affectsConfiguration('workbench.activityBar.experimental.fontSize')) {
+ this.applyActivityBarFontSize();
+ }
}));
}
@@ -111,15 +114,34 @@ export class ActivitybarPart extends Part {
this.element = parent;
this.content = append(this.element, $('.content'));
+ // Apply font settings before show() so composite bar uses correct sizes
+ this.applyActivityBarFontFamily(parent);
+ this.applyActivityBarFontSize(parent);
+
if (this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
this.show();
}
- this.applyActivityBarFontFamily(parent);
-
return this.content;
}
+ private applyActivityBarFontSize(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+
+ const { isUserSet, size } = inspectFontSize(this.configurationService, 'workbench.activityBar.experimental.fontSize', 16);
+
+ if (isUserSet) {
+ updateActivityBarSize(size);
+ target.style.setProperty('--vscode-workbench-activitybar-font-size', `${size}px`);
+ } else {
+ updateActivityBarSize(16);
+ target.style.removeProperty('--vscode-workbench-activitybar-font-size');
+ }
+ }
+
private applyActivityBarFontFamily(container?: HTMLElement): void {
const target = container ?? this.getContainer();
if (!target) {
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
index 7cd25d0..680dc73 100644
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
@@ -7,6 +7,7 @@
width: 48px;
height: 100%;
font-family: var(--vscode-workbench-activitybar-font-family, inherit);
+ font-size: var(--vscode-workbench-activitybar-font-size, var(--vscode-workbench-font-size, 16px));
}
.monaco-workbench .activitybar.bordered::before {
diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css
index 76c7b07..6faf068 100644
--- a/src/vs/workbench/browser/parts/panel/media/panelpart.css
+++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css
@@ -10,6 +10,7 @@
.monaco-workbench .part.panel > .content {
font-family: var(--vscode-workbench-panel-font-family, inherit);
+ font-size: var(--vscode-workbench-panel-font-size, var(--vscode-workbench-font-size, 13px));
}
.monaco-workbench .part.panel.bottom .composite.title {
diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts
index a5d3ff4..b9b240d 100644
--- a/src/vs/workbench/browser/parts/panel/panelPart.ts
+++ b/src/vs/workbench/browser/parts/panel/panelPart.ts
@@ -31,6 +31,7 @@ import { getContextMenuActions } from '../../../../platform/actions/browser/menu
import { IPaneCompositeBarOptions } from '../paneCompositeBar.js';
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
+import { FONT, inspectFontSize, updatePanelSize } from '../../../../base/common/font.js';
export class PanelPart extends AbstractPaneCompositePart {
@@ -113,6 +114,9 @@ export class PanelPart extends AbstractPaneCompositePart {
if (e.affectsConfiguration('workbench.bottomPane.experimental.fontFamily')) {
this.applyPanelFontFamily();
}
+ if (e.affectsConfiguration('workbench.bottomPane.experimental.fontSize')) {
+ this.applyPanelFontSize();
+ }
}));
}
@@ -131,6 +135,24 @@ export class PanelPart extends AbstractPaneCompositePart {
}
this.applyPanelFontFamily(container);
+ this.applyPanelFontSize(container);
+ }
+
+ private applyPanelFontSize(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+
+ const { isUserSet, size } = inspectFontSize(this.configurationService, 'workbench.bottomPane.experimental.fontSize', 13);
+
+ if (isUserSet) {
+ updatePanelSize(size);
+ target.style.setProperty('--vscode-workbench-panel-font-size', `${size}px`);
+ } else {
+ updatePanelSize(13);
+ target.style.removeProperty('--vscode-workbench-panel-font-size');
+ }
}
private applyPanelFontFamily(container?: HTMLElement): void {
diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
index b633ee1..7621d4e 100644
--- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
+++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css
@@ -7,8 +7,8 @@
box-sizing: border-box;
cursor: default;
width: 100%;
- height: 22px;
- font-size: 12px;
+ height: var(--vscode-workbench-statusbar-height, 22px);
+ font-size: var(--vscode-workbench-statusbar-font-size, var(--vscode-workbench-font-size, 12px));
font-family: var(--vscode-workbench-statusbar-font-family, inherit);
display: flex;
overflow: hidden;
@@ -58,7 +58,7 @@
.monaco-workbench .part.statusbar > .items-container > .statusbar-item {
display: inline-block;
- line-height: 22px;
+ line-height: var(--vscode-workbench-statusbar-height, 22px);
height: 100%;
vertical-align: top;
max-width: 40vw;
diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
index 62e78d0..d5dc64a 100644
--- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
+++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
@@ -37,6 +37,7 @@ import { Emitter, Event } from '../../../../base/common/event.js';
import { IView } from '../../../../base/browser/ui/grid/grid.js';
import { isManagedHoverTooltipHTMLElement, isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
+import { FONT, inspectFontSize, updateStatusBarSize } from '../../../../base/common/font.js';
export interface IStatusbarEntryContainer extends IDisposable {
@@ -119,14 +120,14 @@ interface IPendingStatusbarEntry {
class StatusbarPart extends Part implements IStatusbarEntryContainer {
- static readonly HEIGHT = 22;
+ static get HEIGHT() { return FONT.statusBarSize22; }
//#region IView
readonly minimumWidth: number = 0;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
- readonly minimumHeight: number = StatusbarPart.HEIGHT;
- readonly maximumHeight: number = StatusbarPart.HEIGHT;
+ get minimumHeight(): number { return FONT.statusBarSize22; }
+ get maximumHeight(): number { return FONT.statusBarSize22; }
//#endregion
@@ -169,6 +170,9 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer {
if (e.affectsConfiguration('workbench.statusBar.experimental.fontFamily')) {
this.applyStatusBarFontFamily();
}
+ if (e.affectsConfiguration('workbench.statusBar.experimental.fontSize')) {
+ this.applyStatusBarFontSize();
+ }
}));
this.viewModel = this._register(new StatusbarViewModel(storageService));
@@ -436,10 +440,33 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer {
this.createInitialStatusbarEntries();
this.applyStatusBarFontFamily(this.element);
+ this.applyStatusBarFontSize(this.element);
return this.element;
}
+ private applyStatusBarFontSize(container?: HTMLElement): void {
+ const target = container ?? this.getContainer();
+ if (!target) {
+ return;
+ }
+
+ const { isUserSet, size } = inspectFontSize(this.configurationService, 'workbench.statusBar.experimental.fontSize', 12);
+
+ if (isUserSet) {
+ updateStatusBarSize(size);
+ target.style.setProperty('--vscode-workbench-statusbar-font-size', `${size}px`);
+ target.style.setProperty('--vscode-workbench-statusbar-height', `${FONT.statusBarSize22}px`);
+ } else {
+ updateStatusBarSize(12);
+ target.style.removeProperty('--vscode-workbench-statusbar-font-size');
+ target.style.removeProperty('--vscode-workbench-statusbar-height');
+ }
+
+ // Notify grid layout that height constraints changed
+ this._onDidChange.fire(undefined);
+ }
+
private applyStatusBarFontFamily(container?: HTMLElement): void {
const target = container ?? this.getContainer();
if (!target) {
@@ -761,7 +788,7 @@ export class AuxiliaryStatusbarPart extends StatusbarPart implements IAuxiliaryS
private static COUNTER = 1;
- readonly height = StatusbarPart.HEIGHT;
+ get height() { return StatusbarPart.HEIGHT; }
constructor(
readonly container: HTMLElement,
diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts
index 8a9eb0e..5bb0959 100644
--- a/src/vs/workbench/browser/workbench.contribution.ts
+++ b/src/vs/workbench/browser/workbench.contribution.ts
@@ -705,6 +705,38 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
markdownDescription: localize('activityBarFontFamily', "Controls the font family in the activity bar."),
'tags': ['accessibility', 'experimental']
},
+ 'workbench.statusBar.experimental.fontSize': {
+ type: 'number',
+ default: 12,
+ minimum: 6,
+ maximum: 32,
+ markdownDescription: localize('statusBarFontSize', "Controls the font size in pixels for the status bar."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.bottomPane.experimental.fontSize': {
+ type: 'number',
+ default: 13,
+ minimum: 6,
+ maximum: 32,
+ markdownDescription: localize('bottomPaneFontSize', "Controls the font size in pixels for the bottom panel."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.activityBar.experimental.fontSize': {
+ type: 'number',
+ default: 16,
+ minimum: 6,
+ maximum: 32,
+ markdownDescription: localize('activityBarFontSize', "Controls the font size in pixels for the activity bar."),
+ 'tags': ['accessibility', 'experimental']
+ },
+ 'workbench.tabs.experimental.fontSize': {
+ type: 'number',
+ default: 13,
+ minimum: 6,
+ maximum: 32,
+ markdownDescription: localize('tabsFontSize', "Controls the font size in pixels for editor tabs."),
+ 'tags': ['accessibility', 'experimental']
+ },
'workbench.settings.editor': {
'type': 'string',
'enum': ['ui', 'json'],
diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts
index ee4cbf8..4d7a787 100644
--- a/src/vs/workbench/browser/workbench.ts
+++ b/src/vs/workbench/browser/workbench.ts
@@ -7,6 +7,7 @@ import './style.js';
import { runWhenWindowIdle } from '../../base/browser/dom.js';
import { Event, Emitter, setGlobalLeakWarningThreshold } from '../../base/common/event.js';
import { RunOnceScheduler, timeout } from '../../base/common/async.js';
+import { inspectFontSize } from '../../base/common/font.js';
import { isFirefox, isSafari, isChrome } from '../../base/browser/browser.js';
import { mark } from '../../base/common/performance.js';
import { onUnexpectedError, setUnexpectedErrorHandler } from '../../base/common/errors.js';
@@ -303,15 +304,7 @@ export class Workbench extends Layout {
private fontSize: number | undefined;
private fontSizeUserSet: boolean | undefined;
private updateFontSize(configurationService: IConfigurationService) {
- const raw = configurationService.getValue<number>('workbench.experimental.fontSize');
- const size = Math.max(6, Math.min(32, typeof raw === 'number' ? raw : 13));
-
- const inspected = configurationService.inspect<number>('workbench.experimental.fontSize');
- const isUserSet = inspected.userValue !== undefined
- || inspected.userLocalValue !== undefined
- || inspected.userRemoteValue !== undefined
- || inspected.workspaceValue !== undefined
- || inspected.workspaceFolderValue !== undefined;
+ const { isUserSet, size } = inspectFontSize(configurationService, 'workbench.experimental.fontSize', 13);
if (this.fontSize === size && this.fontSizeUserSet === isUserSet) {
return;

View File

@@ -1,223 +0,0 @@
diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
index a946cb7..4db07a2 100644
--- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
+++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts
@@ -47,6 +47,7 @@ import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionba
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
+import { FONT, inspectFontSize, updateTabsSize } from '../../../../base/common/font.js';
import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js';
export class EditorCommandsContextActionRunner extends ActionRunner {
@@ -100,10 +101,12 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
protected readonly treeItemsTransfer = LocalSelectionTransfer.getInstance<DraggedTreeItemsIdentifier>();
- private static readonly EDITOR_TAB_HEIGHT = {
- normal: 35 as const,
- compact: 22 as const
- };
+ private static get EDITOR_TAB_HEIGHT() {
+ return {
+ normal: FONT.tabsSize35,
+ compact: FONT.tabsSize22
+ };
+ }
protected editorActionsToolbarContainer: HTMLElement | undefined;
private editorActionsToolbar: WorkbenchToolBar | undefined;
@@ -153,6 +156,11 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
if (e.affectsConfiguration('workbench.tabs.experimental.fontFamily')) {
this.applyTabsFontFamily();
}
+ if (e.affectsConfiguration('workbench.tabs.experimental.fontSize')) {
+ this.applyTabsFontSize();
+ this.updateTabHeight();
+ this.groupView.relayout();
+ }
}));
// Context Keys
@@ -176,11 +184,37 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
}
protected create(parent: HTMLElement): HTMLElement {
+ this.applyTabsFontSize(parent);
this.updateTabHeight();
this.applyTabsFontFamily(parent);
return parent;
}
+ private applyTabsFontSize(container?: HTMLElement): void {
+ const target = container ?? this.parent;
+ if (!target) {
+ return;
+ }
+
+ const { isUserSet, size } = inspectFontSize(this.configurationService, 'workbench.tabs.experimental.fontSize', 13);
+
+ if (isUserSet) {
+ updateTabsSize(size);
+ target.style.setProperty('--vscode-workbench-tabs-font-size', `${size}px`);
+ target.style.setProperty('--vscode-workbench-tabs-compact-height', `${FONT.tabsSize22}px`);
+ target.style.setProperty('--vscode-workbench-tabs-sticky-compact-width', `${FONT.tabsSize38}px`);
+ target.style.setProperty('--vscode-workbench-tabs-sticky-shrink-width', `${FONT.tabsSize80}px`);
+ target.style.setProperty('--vscode-workbench-tabs-fit-width', `${FONT.tabsSize120}px`);
+ } else {
+ updateTabsSize(13);
+ target.style.removeProperty('--vscode-workbench-tabs-font-size');
+ target.style.removeProperty('--vscode-workbench-tabs-compact-height');
+ target.style.removeProperty('--vscode-workbench-tabs-sticky-compact-width');
+ target.style.removeProperty('--vscode-workbench-tabs-sticky-shrink-width');
+ target.style.removeProperty('--vscode-workbench-tabs-fit-width');
+ }
+ }
+
private applyTabsFontFamily(container?: HTMLElement): void {
const target = container ?? this.parent;
if (!target) {
diff --git a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
index e9e0b2b..fc1bebe 100644
--- a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css
@@ -23,7 +23,7 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a {
- font-size: 13px;
+ font-size: var(--vscode-workbench-tabs-font-size, var(--vscode-workbench-font-size, 13px));
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before,
diff --git a/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css
index a24f761..8c72d17 100644
--- a/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css
@@ -7,23 +7,23 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control {
flex: 1 100%;
- height: 22px;
+ height: var(--vscode-workbench-tabs-compact-height, 22px);
cursor: default;
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control .monaco-icon-label {
- height: 22px;
- line-height: 22px;
+ height: var(--vscode-workbench-tabs-compact-height, 22px);
+ line-height: var(--vscode-workbench-tabs-compact-height, 22px);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control .monaco-icon-label::before {
- height: 22px; /* tweak the icon size of the editor labels when icons are enabled */
+ height: var(--vscode-workbench-tabs-compact-height, 22px); /* tweak the icon size of the editor labels when icons are enabled */
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control .outline-element-icon {
padding-right: 3px;
- height: 22px; /* tweak the icon size of the editor labels when icons are enabled */
- line-height: 22px;
+ height: var(--vscode-workbench-tabs-compact-height, 22px); /* tweak the icon size of the editor labels when icons are enabled */
+ line-height: var(--vscode-workbench-tabs-compact-height, 22px);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control .monaco-breadcrumb-item {
@@ -32,7 +32,7 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .breadcrumbs-below-tabs .breadcrumbs-control .monaco-breadcrumb-item::before {
width: 16px;
- height: 22px;
+ height: var(--vscode-workbench-tabs-compact-height, 22px);
display: flex;
align-items: center;
justify-content: center;
diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css
index 924d9b3..58efc4d 100644
--- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css
@@ -160,7 +160,7 @@
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit {
- width: 120px;
+ width: var(--vscode-workbench-tabs-fit-width, 120px);
min-width: fit-content;
flex-shrink: 0;
}
@@ -183,7 +183,7 @@
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink {
- min-width: 80px;
+ min-width: var(--vscode-workbench-tabs-sticky-shrink-width, 80px);
flex-basis: 0; /* all tabs are even */
flex-grow: 1; /* all tabs grow even */
max-width: fit-content;
@@ -210,9 +210,9 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact {
/** Sticky compact tabs have a fixed width of 38px */
- width: 38px;
- min-width: 38px;
- max-width: 38px;
+ width: var(--vscode-workbench-tabs-sticky-compact-width, 38px);
+ min-width: var(--vscode-workbench-tabs-sticky-compact-width, 38px);
+ max-width: var(--vscode-workbench-tabs-sticky-compact-width, 38px);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink,
@@ -220,9 +220,9 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink {
/** Sticky shrink tabs have a fixed width of 80px */
- width: 80px;
- min-width: 80px;
- max-width: 80px;
+ width: var(--vscode-workbench-tabs-sticky-shrink-width, 80px);
+ min-width: var(--vscode-workbench-tabs-sticky-shrink-width, 80px);
+ max-width: var(--vscode-workbench-tabs-sticky-shrink-width, 80px);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-compact,
@@ -257,7 +257,7 @@
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left {
- min-width: 80px; /* make more room for close button when it shows to the left */
+ min-width: var(--vscode-workbench-tabs-sticky-shrink-width, 80px); /* make more room for close button when it shows to the left */
padding-right: 5px; /* we need less room when sizing is shrink/fixed */
}
diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts
index b0befd9..ef06bcc 100644
--- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts
+++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import './media/multieditortabscontrol.css';
+import { FONT } from '../../../../base/common/font.js';
import { isLinux, isMacintosh, isWindows } from '../../../../base/common/platform.js';
import { shorten } from '../../../../base/common/labels.js';
import { EditorResourceAccessor, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, IUntypedEditorInput, preventEditorClose, EditorCloseMethod, EditorsOrder, IToolbarActions } from '../../../common/editor.js';
@@ -94,11 +95,13 @@ export class MultiEditorTabsControl extends EditorTabsControl {
large: 10 as const
};
- private static readonly TAB_WIDTH = {
- compact: 38 as const,
- shrink: 80 as const,
- fit: 120 as const
- };
+ private static get TAB_WIDTH() {
+ return {
+ compact: FONT.tabsSize38,
+ shrink: FONT.tabsSize80,
+ fit: FONT.tabsSize120
+ };
+ }
private static readonly DRAG_OVER_OPEN_TAB_THRESHOLD = 1500;