import * as path from 'vs/base/common/path'; import { Options } from 'vs/ipc'; import { localize } from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryChannelClient } from 'vs/server/common/telemetry'; import { getOptions } from 'vs/server/common/util'; import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; import 'vs/workbench/services/localizations/browser/localizationsService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; /** * All client-side customization to VS Code should live in this file when * possible. */ const options = getOptions<Options>(); class TelemetryService extends TelemetryChannelClient { public constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, ) { super(remoteAgentService.getConnection()!.getChannel('telemetry')); } } const TELEMETRY_SECTION_ID = 'telemetry'; Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({ 'id': TELEMETRY_SECTION_ID, 'order': 110, 'type': 'object', 'title': localize('telemetryConfigurationTitle', 'Telemetry'), 'properties': { 'telemetry.enableTelemetry': { 'type': 'boolean', 'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'), 'default': !options.disableTelemetry, 'tags': ['usesOnlineServices'] } } }); registerSingleton(ITelemetryService, TelemetryService); /** * This is called by vs/workbench/browser/web.main.ts after the workbench has * been initialized so we can initialize our own client-side code. */ export const initialize = async (services: ServiceCollection): Promise<void> => { const event = new CustomEvent('ide-ready'); window.dispatchEvent(event); if (parent) { // Tell the parent loading has completed. parent.postMessage({ event: 'loaded' }, '*'); // Proxy or stop proxing events as requested by the parent. const listeners = new Map<string, (event: Event) => void>(); window.addEventListener('message', (parentEvent) => { const eventName = parentEvent.data.bind || parentEvent.data.unbind; if (eventName) { const oldListener = listeners.get(eventName); if (oldListener) { document.removeEventListener(eventName, oldListener); } } if (parentEvent.data.bind && parentEvent.data.prop) { const listener = (event: Event) => { parent.postMessage({ event: parentEvent.data.event, [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event] }, window.location.origin); }; listeners.set(parentEvent.data.bind, listener); document.addEventListener(parentEvent.data.bind, listener); } }); } if (!window.isSecureContext) { (services.get(INotificationService) as INotificationService).notify({ severity: Severity.Warning, message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.', actions: { primary: [{ id: 'understand', label: 'I understand', tooltip: '', class: undefined, enabled: true, checked: true, dispose: () => undefined, run: () => { return Promise.resolve(); } }], } }); } const logService = (services.get(ILogService) as ILogService); const storageService = (services.get(IStorageService) as IStorageService); const updateCheckEndpoint = path.join(options.base, '/update/check'); const getUpdate = async (): Promise<void> => { logService.debug('Checking for update...'); const response = await fetch(updateCheckEndpoint, { headers: { 'Accept': 'application/json' }, }); if (!response.ok) { throw new Error(response.statusText); } const json = await response.json(); if (json.error) { throw new Error(json.error); } if (json.isLatest) { return; } const lastNoti = storageService.getNumber('csLastUpdateNotification', StorageScope.GLOBAL); if (lastNoti) { // Only remind them again after 1 week. const timeout = 1000*60*60*24*7; const threshold = lastNoti + timeout; if (Date.now() < threshold) { return; } } storageService.store('csLastUpdateNotification', Date.now(), StorageScope.GLOBAL, StorageTarget.MACHINE); (services.get(INotificationService) as INotificationService).notify({ severity: Severity.Info, message: `[code-server v${json.latest}](https://github.com/cdr/code-server/releases/tag/v${json.latest}) has been released!`, }); }; const updateLoop = (): void => { getUpdate().catch((error) => { logService.debug(`failed to check for update: ${error}`); }).finally(() => { // Check again every 6 hours. setTimeout(updateLoop, 1000*60*60*6); }); }; if (!options.disableUpdateCheck) { updateLoop(); } // This will be used to set the background color while VS Code loads. const theme = storageService.get('colorThemeData', StorageScope.GLOBAL); if (theme) { localStorage.setItem('colorThemeData', theme); } // Use to show or hide logout commands and menu options. const contextKeyService = (services.get(IContextKeyService) as IContextKeyService); contextKeyService.createKey('code-server.authed', options.authed); // Add a logout command. const logoutEndpoint = path.join(options.base, '/logout') + `?base=${options.base}`; const LOGOUT_COMMAND_ID = 'code-server.logout'; CommandsRegistry.registerCommand( LOGOUT_COMMAND_ID, () => { window.location.href = logoutEndpoint; }, ); // Add logout to command palette. MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: LOGOUT_COMMAND_ID, title: localize('logout', "Log out") }, when: ContextKeyExpr.has('code-server.authed') }); // Add logout to the (web-only) home menu. MenuRegistry.appendMenuItem(MenuId.MenubarHomeMenu, { command: { id: LOGOUT_COMMAND_ID, title: localize('logout', "Log out") }, when: ContextKeyExpr.has('code-server.authed') }); };