update animation and background color

This commit is contained in:
BeniBenj
2026-02-25 23:49:48 +01:00
parent 0f4543145b
commit 561772d217
4 changed files with 158 additions and 2 deletions

View File

@@ -645,6 +645,8 @@
"--vscode-searchEditor-findMatchBorder",
"--vscode-searchEditor-textInputBorder",
"--vscode-selection-background",
"--vscode-sessionsUpdateButton-downloadedBackground",
"--vscode-sessionsUpdateButton-downloadingBackground",
"--vscode-settings-checkboxBackground",
"--vscode-settings-checkboxBorder",
"--vscode-settings-checkboxForeground",

View File

@@ -7,6 +7,7 @@ import { localize } from '../../nls.js';
import { registerColor, transparent } from '../../platform/theme/common/colorUtils.js';
import { contrastBorder, iconForeground } from '../../platform/theme/common/colorRegistry.js';
import { Color } from '../../base/common/color.js';
import { buttonBackground } from '../../platform/theme/common/colors/inputColors.js';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from '../../workbench/common/theme.js';
// Sessions sidebar background color
@@ -55,3 +56,16 @@ export const agentFeedbackInputWidgetBorder = registerColor(
{ dark: transparent(iconForeground, 0.8), light: transparent(iconForeground, 0.8), hcDark: contrastBorder, hcLight: contrastBorder },
localize('agentFeedbackInputWidget.border', 'Border color of the agent feedback input widget shown in the editor.')
);
// Sessions update button colors
export const sessionsUpdateButtonDownloadingBackground = registerColor(
'sessionsUpdateButton.downloadingBackground',
transparent(buttonBackground, 0.4),
localize('sessionsUpdateButton.downloadingBackground', 'Background color of the update button to show download progress in the agent sessions window.')
);
export const sessionsUpdateButtonDownloadedBackground = registerColor(
'sessionsUpdateButton.downloadedBackground',
transparent(buttonBackground, 0.7),
localize('sessionsUpdateButton.downloadedBackground', 'Background color of the update button when download is complete in the agent sessions window.')
);

View File

@@ -23,7 +23,9 @@ import { IAction } from '../../../../base/common/actions.js';
import { Button } from '../../../../base/browser/ui/button/button.js';
import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { IUpdateService, StateType } from '../../../../platform/update/common/update.js';
import { Downloading, IUpdateService, State, StateType } from '../../../../platform/update/common/update.js';
import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js';
import { sessionsUpdateButtonDownloadingBackground, sessionsUpdateButtonDownloadedBackground } from '../../../common/theme.js';
// --- Account Menu Items --- //
const AccountMenu = new MenuId('SessionsAccountMenu');
@@ -160,7 +162,7 @@ class AccountWidget extends ActionViewItem {
}
}
class UpdateWidget extends ActionViewItem {
export class UpdateWidget extends ActionViewItem {
private updateButton: Button | undefined;
private readonly viewItemDisposables = this._register(new DisposableStore());
@@ -222,9 +224,44 @@ class UpdateWidget extends ActionViewItem {
if (this.isUpdatePending() && !this.isUpdateReady()) {
this.updateButton.enabled = false;
this.updateButton.label = `$(${Codicon.loading.id}~spin) ${this.getUpdateProgressMessage(state.type)}`;
this.updateDownloadProgress(state);
} else {
this.updateButton.enabled = true;
this.updateButton.label = `$(${Codicon.debugRestart.id}) ${localize('update', "Update")}`;
const color = asCssVariable(sessionsUpdateButtonDownloadedBackground);
this.updateButton.element.style.backgroundImage = `linear-gradient(to right, ${color} 100%, transparent 100%)`;
}
}
private updateDownloadProgress(state: State): void {
if (!this.updateButton) {
return;
}
const el = this.updateButton.element;
if (state.type === StateType.Downloading) {
const { downloadedBytes, totalBytes } = state as Downloading;
if (downloadedBytes !== undefined && totalBytes && totalBytes > 0) {
const percent = Math.min(100, Math.round((downloadedBytes / totalBytes) * 100));
const color = asCssVariable(sessionsUpdateButtonDownloadingBackground);
el.style.backgroundImage = `linear-gradient(to right, ${color} ${percent}%, transparent ${percent}%)`;
} else {
// Indeterminate: show a subtle pulsing background
const color = asCssVariable(sessionsUpdateButtonDownloadingBackground);
el.style.backgroundImage = `linear-gradient(to right, ${color} 0%, transparent 100%)`;
}
} else if (state.type === StateType.Downloaded) {
const color = asCssVariable(sessionsUpdateButtonDownloadedBackground);
el.style.backgroundImage = `linear-gradient(to right, ${color} 100%, transparent 100%)`;
} else {
this.clearDownloadProgress();
}
}
private clearDownloadProgress(): void {
if (this.updateButton) {
this.updateButton.element.style.backgroundImage = '';
}
}

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from '../../../../../base/common/actions.js';
import { Emitter } from '../../../../../base/common/event.js';
import { IUpdateService, State } from '../../../../../platform/update/common/update.js';
import { ComponentFixtureContext, createEditorServices, defineComponentFixture, defineThemedFixtureGroup } from '../../../../../workbench/test/browser/componentFixtures/fixtureUtils.js';
import { UpdateWidget } from '../../browser/account.contribution.js';
// Ensure color registrations are loaded
import '../../../../common/theme.js';
import '../../../../../platform/theme/common/colors/inputColors.js';
// Import the CSS
import '../../../../browser/media/sidebarActionButton.css';
import '../../browser/media/accountWidget.css';
const mockUpdate = { version: '1.0.0' };
function createMockUpdateService(state: State): IUpdateService {
const onStateChange = new Emitter<State>();
const service: IUpdateService = {
_serviceBrand: undefined,
state,
onStateChange: onStateChange.event,
checkForUpdates: async () => { },
downloadUpdate: async () => { },
applyUpdate: async () => { },
quitAndInstall: async () => { },
isLatestVersion: async () => true,
_applySpecificUpdate: async () => { },
setInternalOrg: async () => { },
};
return service;
}
function renderUpdateWidget(ctx: ComponentFixtureContext, state: State): void {
ctx.container.style.padding = '16px';
ctx.container.style.width = '300px';
ctx.container.style.backgroundColor = 'var(--vscode-sideBar-background)';
const mockService = createMockUpdateService(state);
const instantiationService = createEditorServices(ctx.disposableStore, {
colorTheme: ctx.theme,
additionalServices: (reg) => {
reg.defineInstance(IUpdateService, mockService);
},
});
const action = ctx.disposableStore.add(new Action('sessions.action.updateWidget', 'Sessions Update'));
const widget = instantiationService.createInstance(UpdateWidget, action, {});
ctx.disposableStore.add(widget);
widget.render(ctx.container);
}
export default defineThemedFixtureGroup({
Ready: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Ready(mockUpdate, true, false)),
}),
CheckingForUpdates: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.CheckingForUpdates(true)),
}),
AvailableForDownload: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.AvailableForDownload(mockUpdate)),
}),
Downloading0Percent: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloading(mockUpdate, true, false, 0, 100_000_000)),
}),
Downloading30Percent: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloading(mockUpdate, true, false, 30_000_000, 100_000_000)),
}),
Downloading65Percent: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloading(mockUpdate, true, false, 65_000_000, 100_000_000)),
}),
Downloading100Percent: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloading(mockUpdate, true, false, 100_000_000, 100_000_000)),
}),
DownloadingIndeterminate: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloading(mockUpdate, true, false)),
}),
Downloaded: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Downloaded(mockUpdate, true, false)),
}),
Updating: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Updating(mockUpdate)),
}),
Overwriting: defineComponentFixture({
render: (ctx) => renderUpdateWidget(ctx, State.Overwriting(mockUpdate, true)),
}),
});