mirror of
https://github.com/voideditor/void
synced 2026-05-23 09:28:23 +00:00
commit
362d481ec4
10 changed files with 213 additions and 60 deletions
|
|
@ -124,7 +124,8 @@ import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationS
|
|||
import { LLMMessageChannel } from '../../platform/void/electron-main/llmMessageChannel.js';
|
||||
import { IMetricsService } from '../../platform/void/common/metricsService.js';
|
||||
import { MetricsMainService } from '../../platform/void/electron-main/metricsMainService.js';
|
||||
|
||||
import { VoidMainUpdateService } from '../../platform/void/electron-main/voidUpdateMainService.js';
|
||||
import { IVoidUpdateService } from '../../platform/void/common/voidUpdateService.js';
|
||||
/**
|
||||
* The main VS Code application. There will only ever be one instance,
|
||||
* even if the user starts many instances (e.g. from the command line).
|
||||
|
|
@ -1107,6 +1108,7 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
// Void main process services (required for services with a channel for comm between browser and electron-main (node))
|
||||
services.set(IMetricsService, new SyncDescriptor(MetricsMainService, undefined, false));
|
||||
services.set(IVoidUpdateService, new SyncDescriptor(VoidMainUpdateService, undefined, false));
|
||||
|
||||
// Default Extensions Profile Init
|
||||
services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService, undefined, true));
|
||||
|
|
@ -1245,6 +1247,10 @@ export class CodeApplication extends Disposable {
|
|||
// Void - use loggerChannel as reference
|
||||
const metricsChannel = ProxyChannel.fromService(accessor.get(IMetricsService), disposables);
|
||||
mainProcessElectronServer.registerChannel('void-channel-metrics', metricsChannel);
|
||||
|
||||
const voidUpdatesChannel = ProxyChannel.fromService(accessor.get(IVoidUpdateService), disposables);
|
||||
mainProcessElectronServer.registerChannel('void-channel-update', voidUpdatesChannel);
|
||||
|
||||
const llmMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
|
||||
mainProcessElectronServer.registerChannel('void-channel-llmMessageService', llmMessageChannel);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { timeout } from '../../../base/common/async.js';
|
||||
// import { timeout } from '../../../base/common/async.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { IConfigurationService } from '../../configuration/common/configuration.js';
|
||||
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
|
||||
// import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
|
||||
import { ILifecycleMainService, LifecycleMainPhase } from '../../lifecycle/electron-main/lifecycleMainService.js';
|
||||
import { ILogService } from '../../log/common/log.js';
|
||||
import { IProductService } from '../../product/common/productService.js';
|
||||
|
|
@ -60,7 +61,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
|||
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
|
||||
@IRequestService protected requestService: IRequestService,
|
||||
@ILogService protected logService: ILogService,
|
||||
@IProductService protected readonly productService: IProductService
|
||||
@IProductService protected readonly productService: IProductService,
|
||||
) {
|
||||
lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen)
|
||||
.finally(() => this.initialize());
|
||||
|
|
@ -79,72 +80,29 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
|||
}
|
||||
console.log('is built, continuing with update service')
|
||||
|
||||
// Void commented this
|
||||
// if (this.environmentMainService.disableUpdates) {
|
||||
// this.setState(State.Disabled(DisablementReason.DisabledByEnvironment));
|
||||
// this.logService.info('update#ctor - updates are disabled by the environment');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (!this.productService.updateUrl || !this.productService.commit) {
|
||||
// this.setState(State.Disabled(DisablementReason.MissingConfiguration));
|
||||
// this.logService.info('update#ctor - updates are disabled as there is no update URL');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Void - for now, always update
|
||||
|
||||
const updateMode = 'default' //this.configurationService.getValue<'none' | 'manual' | 'start' | 'default'>('update.mode');
|
||||
|
||||
const quality = this.getProductQuality(updateMode);
|
||||
if (!quality) {
|
||||
this.setState(State.Disabled(DisablementReason.ManuallyDisabled));
|
||||
this.logService.info('update#ctor - updates are disabled by user preference');
|
||||
return;
|
||||
}
|
||||
|
||||
// const quality = 'stable'
|
||||
this.url = this.doBuildUpdateFeedUrl(quality);
|
||||
this.url = this.doBuildUpdateFeedUrl('stable');
|
||||
if (!this.url) {
|
||||
this.setState(State.Disabled(DisablementReason.InvalidConfiguration));
|
||||
this.logService.info('update#ctor - updates are disabled as the update URL is badly formed');
|
||||
return;
|
||||
}
|
||||
|
||||
// hidden setting
|
||||
if (this.configurationService.getValue<boolean>('_update.prss')) {
|
||||
const url = new URL(this.url);
|
||||
url.searchParams.set('prss', 'true');
|
||||
this.url = url.toString();
|
||||
}
|
||||
this.setState(State.Disabled(DisablementReason.ManuallyDisabled));
|
||||
|
||||
this.setState(State.Idle(this.getUpdateType()));
|
||||
|
||||
// if (updateMode === 'manual') {
|
||||
// this.logService.info('update#ctor - manual checks only; automatic updates are disabled by user preference');
|
||||
// return;
|
||||
// }
|
||||
// Void - temporarily disabled while we figure out how to do this the right way
|
||||
|
||||
// if (updateMode === 'start') {
|
||||
// this.logService.info('update#ctor - startup checks only; automatic updates are disabled by user preference');
|
||||
// this.setState(State.Idle(this.getUpdateType()));
|
||||
|
||||
// // Check for updates only once after 30 seconds
|
||||
// setTimeout(() => this.checkForUpdates(false), 30 * 1000);
|
||||
// } else {
|
||||
// Start checking for updates after 30 seconds
|
||||
this.scheduleCheckForUpdates(30 * 1000).then(undefined, err => this.logService.error(err));
|
||||
// }
|
||||
// start checking for updates after 10 seconds
|
||||
// this.scheduleCheckForUpdates(10 * 1000).then(undefined, err => this.logService.error(err));
|
||||
}
|
||||
|
||||
private getProductQuality(updateMode: string): string | undefined {
|
||||
return updateMode === 'none' ? undefined : this.productService.quality;
|
||||
}
|
||||
|
||||
private async scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise<void> {
|
||||
await timeout(delay);
|
||||
await this.checkForUpdates(false);
|
||||
return await this.scheduleCheckForUpdates(60 * 60 * 1000);
|
||||
}
|
||||
// private async scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise<void> {
|
||||
// await timeout(delay);
|
||||
// await this.checkForUpdates(false);
|
||||
// return await this.scheduleCheckForUpdates(60 * 60 * 1000);
|
||||
// }
|
||||
|
||||
async checkForUpdates(explicit: boolean): Promise<void> {
|
||||
this.logService.trace('update#checkForUpdates, state = ', this.state.type);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
|
|||
@IEnvironmentMainService environmentMainService: IEnvironmentMainService,
|
||||
@IRequestService requestService: IRequestService,
|
||||
@ILogService logService: ILogService,
|
||||
@IProductService productService: IProductService
|
||||
@IProductService productService: IProductService,
|
||||
) {
|
||||
super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
|||
@ILogService logService: ILogService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService,
|
||||
@IProductService productService: IProductService
|
||||
@IProductService productService: IProductService,
|
||||
) {
|
||||
super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,3 +16,6 @@ import '../common/refreshModelService.js'
|
|||
|
||||
// metrics
|
||||
import '../common/metricsService.js'
|
||||
|
||||
// updates
|
||||
import '../common/voidUpdateService.js'
|
||||
|
|
|
|||
46
src/vs/platform/void/common/voidUpdateService.ts
Normal file
46
src/vs/platform/void/common/voidUpdateService.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
|
||||
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
||||
|
||||
|
||||
|
||||
export interface IVoidUpdateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
check: () => Promise<{ hasUpdate: true, message: string } | { hasUpdate: false } | null>;
|
||||
}
|
||||
|
||||
|
||||
export const IVoidUpdateService = createDecorator<IVoidUpdateService>('VoidUpdateService');
|
||||
|
||||
|
||||
// implemented by calling channel
|
||||
export class VoidUpdateService implements IVoidUpdateService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly voidUpdateService: IVoidUpdateService;
|
||||
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService, // (only usable on client side)
|
||||
) {
|
||||
// creates an IPC proxy to use metricsMainService.ts
|
||||
this.voidUpdateService = ProxyChannel.toService<IVoidUpdateService>(mainProcessService.getChannel('void-channel-update'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// anything transmitted over a channel must be async even if it looks like it doesn't have to be
|
||||
check: IVoidUpdateService['check'] = async () => {
|
||||
const res = await this.voidUpdateService.check()
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IVoidUpdateService, VoidUpdateService, InstantiationType.Eager);
|
||||
|
||||
|
||||
50
src/vs/platform/void/electron-main/voidUpdateMainService.ts
Normal file
50
src/vs/platform/void/electron-main/voidUpdateMainService.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
|
||||
|
||||
import { IProductService } from '../../product/common/productService.js';
|
||||
|
||||
import { IVoidUpdateService } from '../common/voidUpdateService.js';
|
||||
|
||||
|
||||
|
||||
export class VoidMainUpdateService extends Disposable implements IVoidUpdateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async check() {
|
||||
const isDevMode = !this._envMainService.isBuilt // found in abstractUpdateService.ts
|
||||
|
||||
if (isDevMode) {
|
||||
return { hasUpdate: false } as const
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://updates.voideditor.dev/api/v0/${this._productService.commit}`)
|
||||
const resJSON = await res.json()
|
||||
|
||||
if (!resJSON) return null
|
||||
|
||||
const { hasUpdate, downloadMessage } = resJSON ?? {}
|
||||
if (hasUpdate === undefined)
|
||||
return null
|
||||
|
||||
const after = (downloadMessage || '') + ''
|
||||
return { hasUpdate: !!hasUpdate, message: after }
|
||||
}
|
||||
catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ export class ConsistentItemService extends Disposable {
|
|||
}
|
||||
|
||||
const initializeEditor = (editor: ICodeEditor) => {
|
||||
if (editor.getModel()?.uri.scheme !== 'file') return
|
||||
// if (editor.getModel()?.uri.scheme !== 'file') return // THIS BREAKS THINGS
|
||||
addTabSwitchListeners(editor)
|
||||
addDisposeListener(editor)
|
||||
putItemsOnEditor(editor, editor.getModel()?.uri ?? null)
|
||||
|
|
|
|||
|
|
@ -26,3 +26,6 @@ import './voidSettingsPane.js'
|
|||
|
||||
// register css
|
||||
import './media/void.css'
|
||||
|
||||
// update (frontend part, also see platform/)
|
||||
import './voidUpdateActions.js'
|
||||
|
|
|
|||
87
src/vs/workbench/contrib/void/browser/voidUpdateActions.ts
Normal file
87
src/vs/workbench/contrib/void/browser/voidUpdateActions.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import Severity from '../../../../base/common/severity.js';
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
import { localize2 } from '../../../../nls.js';
|
||||
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
import { IMetricsService } from '../../../../platform/void/common/metricsService.js';
|
||||
import { IVoidUpdateService } from '../../../../platform/void/common/voidUpdateService.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
|
||||
|
||||
|
||||
|
||||
const notifyYesUpdate = (notifService: INotificationService, msg?: string) => {
|
||||
const message = msg || 'This is a very old version of void, please download the latest version! [Void Editor](https://voideditor.com/download-beta)!'
|
||||
notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: message,
|
||||
})
|
||||
}
|
||||
const notifyNoUpdate = (notifService: INotificationService) => {
|
||||
notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: 'Void is up-to-date!',
|
||||
})
|
||||
}
|
||||
const notifyErrChecking = (notifService: INotificationService) => {
|
||||
const message = `Void Error: There was an error checking for updates. If this persists, please get in touch or reinstall Void [here](https://voideditor.com/download-beta)!`
|
||||
notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: message,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Action
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
f1: true,
|
||||
id: 'void.voidCheckUpdate',
|
||||
title: localize2('voidCheckUpdate', 'Void: Check for Updates'),
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const voidUpdateService = accessor.get(IVoidUpdateService)
|
||||
const notifService = accessor.get(INotificationService)
|
||||
const metricsService = accessor.get(IMetricsService)
|
||||
|
||||
const res = await voidUpdateService.check()
|
||||
if (!res) { notifyErrChecking(notifService); metricsService.capture('Void Update: Error', {}) }
|
||||
else if (res.hasUpdate) { notifyYesUpdate(notifService, res.message); metricsService.capture('Void Update: Yes', {}) }
|
||||
else if (!res.hasUpdate) { notifyNoUpdate(notifService); metricsService.capture('Void Update: No', {}) }
|
||||
}
|
||||
})
|
||||
|
||||
// on mount
|
||||
class VoidUpdateWorkbenchContribution extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.void.voidUpdate'
|
||||
constructor(
|
||||
@IVoidUpdateService private readonly voidUpdateService: IVoidUpdateService,
|
||||
@INotificationService private readonly notifService: INotificationService,
|
||||
@IMetricsService private readonly metricsService: IMetricsService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// on mount
|
||||
setTimeout(async () => {
|
||||
const res = await this.voidUpdateService.check()
|
||||
|
||||
const notifService = this.notifService
|
||||
const metricsService = this.metricsService
|
||||
|
||||
if (!res) { notifyErrChecking(notifService); metricsService.capture('Void Update Startup: Error', {}) }
|
||||
else if (res.hasUpdate) { notifyYesUpdate(this.notifService, res.message); metricsService.capture('Void Update Startup: Yes', {}) }
|
||||
else if (!res.hasUpdate) { metricsService.capture('Void Update Startup: No', {}) } // display nothing if up to date
|
||||
|
||||
}, 5 * 1000)
|
||||
}
|
||||
}
|
||||
registerWorkbenchContribution2(VoidUpdateWorkbenchContribution.ID, VoidUpdateWorkbenchContribution, WorkbenchPhase.BlockRestore);
|
||||
Loading…
Reference in a new issue