Replaced the splash screen with a preload.

Added a bare minimum example.

Closes #193
Closes #324
Closes #327
Closes #717
Closes #851

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2022-08-03 20:20:04 +02:00 committed by Akos Kitta
parent aebec0f942
commit 92bc5ecf7b
15 changed files with 156 additions and 433 deletions

1
.vscode/launch.json vendored
View File

@ -20,7 +20,6 @@
"--no-app-auto-install",
"--plugins=local-dir:../plugins",
"--hosted-plugin-inspect=9339",
"--nosplash",
"--content-trace",
"--open-devtools"
],

View File

@ -6,10 +6,6 @@ import {
ElectronMainWindowServiceExt,
electronMainWindowServiceExtPath,
} from '../../../electron-common/electron-main-window-service-ext';
import {
SplashService,
splashServicePath,
} from '../../../electron-common/splash-service';
import { ElectronWindowService } from './electron-window-service';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
@ -24,9 +20,4 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
)
)
.inSingletonScope();
bind(SplashService)
.toDynamicValue(({ container }) =>
ElectronIpcConnectionProvider.createProxy(container, splashServicePath)
)
.inSingletonScope();
});

View File

@ -1,17 +1,15 @@
import * as remote from '@theia/core/electron-shared/@electron/remote';
import {
ConnectionStatus,
ConnectionStatusService,
} from '@theia/core/lib/browser/connection-status-service';
import { nls } from '@theia/core/lib/common';
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
ConnectionStatus,
ConnectionStatusService,
} from '@theia/core/lib/browser/connection-status-service';
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
import { SplashService } from '../../../electron-common/splash-service';
import { nls } from '@theia/core/lib/common';
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
import { ElectronMainWindowServiceExt } from '../../../electron-common/electron-main-window-service-ext';
@ -23,20 +21,14 @@ export class ElectronWindowService
@inject(ConnectionStatusService)
private readonly connectionStatusService: ConnectionStatusService;
@inject(SplashService)
private readonly splashService: SplashService;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
@inject(ElectronMainWindowServiceExt)
private readonly mainWindowServiceExt: ElectronMainWindowServiceExt;
@postConstruct()
protected override init(): void {
this.appStateService
.reachedAnyState('initialized_layout')
.then(() => this.splashService.requestClose());
// NOOP
// Does not listen on Theia's `window.zoomLevel` changes.
// TODO: IDE2 must switch to the Theia preferences and drop the custom one.
}
protected shouldUnload(): boolean {

View File

@ -1,5 +0,0 @@
export const splashServicePath = '/services/splash-service';
export const SplashService = Symbol('SplashService');
export interface SplashService {
requestClose(): Promise<void>;
}

View File

@ -16,13 +16,8 @@ import {
ElectronMainWindowServiceExt,
electronMainWindowServiceExtPath,
} from '../electron-common/electron-main-window-service-ext';
import {
SplashService,
splashServicePath,
} from '../electron-common/splash-service';
import { ElectronMainWindowServiceExtImpl } from './electron-main-window-service-ext-impl';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
import { SplashServiceImpl } from './splash/splash-service-impl';
import { ElectronMainApplication } from './theia/electron-main-application';
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
import { TheiaElectronWindow } from './theia/theia-electron-window';
@ -34,17 +29,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainWindowServiceImpl).toSelf().inSingletonScope();
rebind(ElectronMainWindowService).toService(ElectronMainWindowServiceImpl);
bind(SplashServiceImpl).toSelf().inSingletonScope();
bind(SplashService).toService(SplashServiceImpl);
bind(ElectronConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler(splashServicePath, () =>
context.container.get(SplashService)
)
)
.inSingletonScope();
// IDE updater bindings
bind(IDEUpdaterImpl).toSelf().inSingletonScope();
bind(IDEUpdater).toService(IDEUpdaterImpl);

View File

@ -1,158 +0,0 @@
/*
MIT License
Copyright (c) 2017 Troy McKinnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Copied from https://raw.githubusercontent.com/trodi/electron-splashscreen/2f5052a133be021cbf9a438d0ef4719cd1796b75/index.ts
/**
* Module handles configurable splashscreen to show while app is loading.
*/
import { Event } from '@theia/core/lib/common/event';
import { BrowserWindow } from 'electron';
/**
* When splashscreen was shown.
* @ignore
*/
let splashScreenTimestamp = 0;
/**
* Splashscreen is loaded and ready to show.
* @ignore
*/
let splashScreenReady = false;
/**
* Main window has been loading for a min amount of time.
* @ignore
*/
let slowStartup = false;
/**
* True when expected work is complete and we've closed splashscreen, else user prematurely closed splashscreen.
* @ignore
*/
let done = false;
/**
* Show splashscreen if criteria are met.
* @ignore
*/
const showSplash = () => {
if (splashScreen && splashScreenReady && slowStartup) {
splashScreen.show();
splashScreenTimestamp = Date.now();
}
};
/**
* Close splashscreen / show main screen. Ensure screen is visible for a min amount of time.
* @ignore
*/
const closeSplashScreen = (main: Electron.BrowserWindow, min: number): void => {
if (splashScreen) {
const timeout = min - (Date.now() - splashScreenTimestamp);
setTimeout(() => {
done = true;
if (splashScreen) {
splashScreen.isDestroyed() || splashScreen.close(); // Avoid `Error: Object has been destroyed` (#19)
splashScreen = null;
}
if (!main.isDestroyed()) {
main.show();
}
}, timeout);
}
};
/** `electron-splashscreen` config object. */
export interface Config {
/** Options for the window that is loading and having a splashscreen tied to. */
windowOpts: Electron.BrowserWindowConstructorOptions;
/**
* URL to the splashscreen template. This is the path to an `HTML` or `SVG` file.
* If you want to simply show a `PNG`, wrap it in an `HTML` file.
*/
templateUrl: string;
/**
* Full set of browser window options for the splashscreen. We override key attributes to
* make it look & feel like a splashscreen; the rest is up to you!
*/
splashScreenOpts: Electron.BrowserWindowConstructorOptions;
/** Number of ms the window will load before splashscreen appears (default: 500ms). */
delay?: number;
/** Minimum ms the splashscreen will be visible (default: 500ms). */
minVisible?: number;
/** Close window that is loading if splashscreen is closed by user (default: true). */
closeWindow?: boolean;
}
/**
* The actual splashscreen browser window.
* @ignore
*/
let splashScreen: Electron.BrowserWindow | null;
/**
* Initializes a splashscreen that will show/hide smartly (and handle show/hiding of main window).
* @param config - Configures splashscreen
* @returns {BrowserWindow} the main browser window ready for loading
*/
export const initSplashScreen = async (
config: Config,
windowFactory: (
options: Electron.BrowserViewConstructorOptions
) => Promise<BrowserWindow>,
onCloseRequested?: Event<void>
): Promise<BrowserWindow> => {
const xConfig: Required<Config> = {
windowOpts: config.windowOpts,
templateUrl: config.templateUrl,
splashScreenOpts: config.splashScreenOpts,
delay: config.delay ?? 500,
minVisible: config.minVisible ?? 500,
closeWindow: config.closeWindow ?? true,
};
xConfig.splashScreenOpts.center = true;
xConfig.splashScreenOpts.frame = false;
xConfig.windowOpts.show = false;
const window = await windowFactory(xConfig.windowOpts);
splashScreen = new BrowserWindow(xConfig.splashScreenOpts);
splashScreen.loadURL(`file://${xConfig.templateUrl}`);
xConfig.closeWindow &&
splashScreen.on('close', () => {
done || window.close();
});
// Splashscreen is fully loaded and ready to view.
splashScreen.webContents.on('did-finish-load', () => {
splashScreenReady = true;
showSplash();
});
// Startup is taking enough time to show a splashscreen.
setTimeout(() => {
slowStartup = true;
showSplash();
}, xConfig.delay);
if (onCloseRequested) {
onCloseRequested(() => closeSplashScreen(window, xConfig.minVisible));
} else {
window.webContents.on('did-finish-load', () => {
closeSplashScreen(window, xConfig.minVisible);
});
}
window.on('closed', () => closeSplashScreen(window, 0)); // XXX: close splash when main window is closed
return window;
};

View File

@ -1,20 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { SplashService } from '../../electron-common/splash-service';
@injectable()
export class SplashServiceImpl implements SplashService {
protected requested = false;
protected readonly onCloseRequestedEmitter = new Emitter<void>();
get onCloseRequested(): Event<void> {
return this.onCloseRequestedEmitter.event;
}
async requestClose(): Promise<void> {
if (!this.requested) {
this.requested = true;
this.onCloseRequestedEmitter.fire();
}
}
}

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: auto;
text-align: center;
padding: 0px;
}
img {
max-width: 95%;
height: auto;
}
</style>
</head>
<body>
<div class="container">
<p><img src="splash.png"></p>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View File

@ -1,18 +1,15 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { injectable } from '@theia/core/shared/inversify';
import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
contentTracing,
ipcMain,
screen,
Event as ElectronEvent,
} from '@theia/core/electron-shared/electron';
import { fork } from 'child_process';
import { AddressInfo } from 'net';
import { join, dirname } from 'path';
import * as fs from 'fs-extra';
import { initSplashScreen } from '../splash/splash-screen';
import { MaybePromise } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
@ -20,7 +17,6 @@ import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainExecutionParams,
} from '@theia/core/lib/electron-main/electron-main-application';
import { SplashServiceImpl } from '../splash/splash-service-impl';
import { URI } from '@theia/core/shared/vscode-uri';
import { Deferred } from '@theia/core/lib/common/promise-util';
import * as os from '@theia/core/lib/common/os';
@ -42,14 +38,6 @@ interface WorkspaceOptions {
const WORKSPACES = 'workspaces';
/**
* Purely a dev thing. If you start the app with the `--nosplash` argument,
* then you won't have the splash screen (which is always on top :confused:) and can debug the app at startup.
* Note: if you start the app from VS Code with the `App (Electron)` config, the splash screen will be disabled.
*/
const APP_STARTED_WITH_NOSPLASH =
typeof process !== 'undefined' && process.argv.indexOf('--nosplash') !== -1;
/**
* If the app is started with `--open-devtools` argument, the `Dev Tools` will be opened.
*/
@ -70,9 +58,6 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
private _firstWindowId: number | undefined;
private openFilePromise = new Deferred();
@inject(SplashServiceImpl)
private readonly splashService: SplashServiceImpl;
override async start(config: FrontendApplicationConfig): Promise<void> {
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
// See: https://github.com/electron-userland/electron-builder/issues/2468
@ -289,69 +274,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
super.onSecondInstance(event, argv, cwd);
}
/**
* Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it.
*
* @param options
*/
override async createWindow(
asyncOptions: MaybePromise<TheiaBrowserWindowOptions> = this.getDefaultTheiaWindowOptions()
): Promise<BrowserWindow> {
let options = await asyncOptions;
options = this.avoidOverlap(options);
let electronWindow: BrowserWindow | undefined;
if (this.windows.size || APP_STARTED_WITH_NOSPLASH) {
electronWindow = await this.doCreateWindow(options);
} else {
const { bounds } = screen.getDisplayNearestPoint(
screen.getCursorScreenPoint()
);
const splashHeight = 450;
const splashWidth = 600;
const splashY = Math.floor(bounds.y + (bounds.height - splashHeight) / 2);
const splashX = Math.floor(bounds.x + (bounds.width - splashWidth) / 2);
const splashScreenOpts: BrowserWindowConstructorOptions = {
height: splashHeight,
width: splashWidth,
x: splashX,
y: splashY,
transparent: true,
alwaysOnTop: true,
focusable: false,
minimizable: false,
maximizable: false,
hasShadow: false,
resizable: false,
};
electronWindow = await initSplashScreen(
{
windowOpts: options,
templateUrl: join(
__dirname,
'..',
'..',
'..',
'src',
'electron-main',
'splash',
'static',
'splash.html'
),
delay: 0,
minVisible: 2000,
splashScreenOpts,
},
(windowOptions) => this.doCreateWindow(windowOptions),
this.splashService.onCloseRequested
);
}
return electronWindow;
}
private async doCreateWindow(
options: TheiaBrowserWindowOptions
): Promise<BrowserWindow> {
const electronWindow = await super.createWindow(options);
const electronWindow = await super.createWindow(asyncOptions);
if (APP_STARTED_WITH_DEV_TOOLS) {
electronWindow.webContents.openDevTools();
}

View File

@ -61,7 +61,7 @@
},
"generator": {
"config": {
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
"preloadTemplate": "./resources/preload.html"
}
}
}

View File

@ -0,0 +1,114 @@
<head>
<style>
/* The colors are hard-coded and based on the `editor.background` and `editor.foreground` values from `./arduino-ide-extension/src/browser/data/default.color-theme.json` */
@media (prefers-color-scheme: light) {
html {
background: #ffffff;
}
}
/* The colors are hard-coded and based on the `editor.background` and `editor.foreground` values from `./arduino-ide-extension/src/browser/data/dark.color-theme.json` */
@media (prefers-color-scheme: dark) {
html {
background: #1f272a;
}
}
.theia-preload {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* Above styles copied from https://github.com/eclipse-theia/theia/blob/5aeef6c0c683b4e91713ab736957e6655b486adc/packages/core/src/browser/style/index.css#L147-L161 */
/* Otherwise, there is a flickering when Theia's CSS loads. */
background-image: none;
}
.theia-preload::after {
/* remove default loading animation */
content: none;
}
.spinner-container {
display: flex;
flex-direction: center;
align-self: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.custom-spinner {
align-self: center;
}
.custom-spinner svg {
width: 16vw;
height: 16vh;
animation-delay: 0;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-name: preload-spinner;
animation-timing-function: ease;
}
@keyframes preload-spinner {
0% {
transform: scale(1.0);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1.0);
}
}
</style>
</head>
<body>
<div class='spinner-container'>
<div class='custom-spinner'>
<svg id="spinner" xmlns="http://www.w3.org/2000/svg" width="2499" height="2500"
viewBox="0 0 1372.201 1372.684">
<path fill="#00979D" stroke="#81C9CB" stroke-width=".932" stroke-miterlimit="10"
d="M1371.701 686.024c0 378.658-306.972 685.605-685.549 685.605C307.451 1371.629.5 1064.682.5 686.024.5 307.455 307.451.483 686.152.483c378.594.001 685.549 306.972 685.549 685.541z" />
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-16.3" y1="16.071" x2="1354.901" y2="16.071"
gradientTransform="matrix(1 0 0 -1 16.8 702.696)">
<stop offset=".117" stop-color="#fff" stop-opacity="0" />
<stop offset=".252" stop-color="#c0d1d3" stop-opacity=".153" />
<stop offset=".387" stop-color="#91b3b7" stop-opacity=".306" />
<stop offset=".52" stop-color="#6d9fa3" stop-opacity=".457" />
<stop offset=".65" stop-color="#4d9195" stop-opacity=".604" />
<stop offset=".776" stop-color="#30888b" stop-opacity=".746" />
<stop offset=".895" stop-color="#148386" stop-opacity=".881" />
<stop offset="1" stop-color="#008184" />
</linearGradient>
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-16.8" y1="16.071" x2="1355.401" y2="16.071"
gradientTransform="matrix(1 0 0 -1 16.8 702.696)">
<stop offset="0" stop-color="#fff" stop-opacity="0" />
<stop offset=".153" stop-color="#c0d1d3" stop-opacity=".153" />
<stop offset=".306" stop-color="#91b3b7" stop-opacity=".306" />
<stop offset=".457" stop-color="#6d9fa3" stop-opacity=".457" />
<stop offset=".604" stop-color="#4d9195" stop-opacity=".604" />
<stop offset=".746" stop-color="#30888b" stop-opacity=".746" />
<stop offset=".881" stop-color="#148386" stop-opacity=".881" />
<stop offset="1" stop-color="#008184" />
</linearGradient>
<path opacity=".5" fill="url(#a)" stroke="url(#b)" stroke-miterlimit="10"
d="M1371.701 686.595c0 378.65-306.972 685.606-685.549 685.606C307.451 1372.201.5 1065.23.5 686.595.5 308.019 307.451 1.048 686.152 1.048c378.594.016 685.549 306.97 685.549 685.547z" />
<g fill="#FFF">
<path
d="M947.959 931.196c-12.909 0-26.127-.929-39.127-2.864-108.978-15.554-181.848-93.822-222.665-153.989-40.946 60.166-113.811 138.512-222.74 154.045a275.864 275.864 0 0 1-39.133 2.785c-67.753 0-131.358-25.217-179.201-71.003-48.299-46.165-74.951-108.114-74.951-174.171 0-66.14 26.651-128.004 75.021-174.253 47.797-45.793 111.449-70.936 179.231-70.936 12.918 0 26.067.928 39.023 2.783 108.932 15.535 181.794 93.813 222.743 153.99 40.825-60.177 113.689-138.432 222.658-153.99 13-1.863 26.148-2.783 39.066-2.783 67.753 0 131.401 25.208 179.197 70.936 48.345 46.249 74.937 108.113 74.937 174.253 0 66.057-26.524 128.006-74.868 174.171-47.881 45.785-111.434 71.026-179.191 71.026M734.42 686.024c21.283 40.534 84.067 141.676 186.692 156.375 8.984 1.236 18.028 1.923 26.839 1.923 92.185 0 167.225-71.002 167.225-158.322s-75.023-158.321-167.291-158.321c-8.812 0-17.853.629-26.753 1.921-102.644 14.664-165.428 115.806-186.712 156.424M424.393 527.702c-92.308 0-167.36 70.998-167.36 158.321 0 87.305 75.021 158.322 167.245 158.322 8.852 0 17.897-.688 26.879-1.922 102.629-14.697 165.394-115.783 186.689-156.375-21.237-40.535-84.061-141.761-186.689-156.376-8.877-1.341-17.945-1.97-26.764-1.97" />
<path
d="M354.37 662.051h152.625v49.181H354.37zM1016.484 662.051h-51.671v-51.747h-49.348v51.747h-51.648v49.181h51.648v51.737h49.348v-51.737h51.671z" />
</g>
</svg>
</div>
</div>
</body>

6
electron/.gitignore vendored
View File

@ -15,4 +15,8 @@ dist/
electron-builder.env
# The generated `package.json` under the `build` folder.
build/package.json
build/package.json
# Resources the packager copies from dev to prod
build/resources/preload.html
build/patch/frontend/index.js

View File

@ -1,100 +0,0 @@
// Patch for the startup theme. Customizes the `ThemeService.get().defaultTheme();` to dispatch the default IDE2 theme based on the OS' theme.
// For all subsequent starts of the IDE the theme applied will be the last one set by the user.
// With the current version of Theia adopted (1.25) it is not possible to extend the `ThemeService`, it will be possible starting from Theia 1.27.
// Once the version of Theia is updated, this patch will be removed and this functionality will be implemented via dependency injection.
// Ideally, we should open a PR in Theia and add support for `light` and `dark` default themes in the app config.
const {
ThemeService,
ThemeServiceSymbol,
BuiltinThemeProvider,
} = require('@theia/core/lib/browser/theming');
const {
ApplicationProps,
} = require('@theia/application-package/lib/application-props');
const {
FrontendApplicationConfigProvider,
} = require('@theia/core/lib/browser/frontend-application-config-provider');
// It is a mighty hack to support theme updates in the bundled IDE2.
// If the custom theme registration happens before the restoration of the existing monaco themes, then any custom theme changes will be ignored.
// This patch introduces a static deferred promise in the monaco-theming service that will be resolved when the restoration is ready.
// IDE2 cannot require the monaco theme service on the outer module level, as it requires the application config provider to be initialized,
// but the initialization happens only in the generated `index.js`.
// This patch customizes the monaco theme service behavior before loading the DI containers via the preload.
// The preload is called only once before the app loads. The Theia extensions are not loaded at that point, but the app config provider is ready.
const preloader = require('@theia/core/lib/browser/preloader');
const originalPreload = preloader.preload;
preloader.preload = async function () {
const { MonacoThemingService } = require('@theia/monaco/lib/browser/monaco-theming-service');
const { MonacoThemeServiceIsReady } = require('arduino-ide-extension/lib/browser/utils/window');
const { Deferred } = require('@theia/core/lib/common/promise-util');
const ready = new Deferred();
if (!window[MonacoThemeServiceIsReady]) {
window[MonacoThemeServiceIsReady] = ready;
console.log('Registered a custom monaco-theme service initialization signal on the window object.');
}
// Here, it is safe to patch the theme service, app config provider is ready.
MonacoThemingService.init = async function () {
this.updateBodyUiTheme();
ThemeService.get().onDidColorThemeChange(() => this.updateBodyUiTheme());
await this.restore();
ready.resolve();
}.bind(MonacoThemingService);
return originalPreload();
}.bind(preloader);
const lightTheme = 'arduino-theme';
const darkTheme = 'arduino-theme-dark';
const defaultTheme =
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? darkTheme
: lightTheme;
const originalGet = FrontendApplicationConfigProvider.get;
FrontendApplicationConfigProvider.get = function () {
const originalProps = originalGet.bind(FrontendApplicationConfigProvider)();
return { ...originalProps, defaultTheme };
}.bind(FrontendApplicationConfigProvider);
const arduinoDarkTheme = {
id: 'arduino-theme-dark',
type: 'dark',
label: 'Dark (Arduino)',
editorTheme: 'arduino-theme-dark',
activate() {},
deactivate() {},
};
const arduinoLightTheme = {
id: 'arduino-theme',
type: 'light',
label: 'Light (Arduino)',
editorTheme: 'arduino-theme',
activate() {},
deactivate() {},
};
if (!window[ThemeServiceSymbol]) {
const themeService = new ThemeService();
Object.defineProperty(themeService, 'defaultTheme', {
get: function () {
return (
this.themes[defaultTheme] ||
this.themes[ApplicationProps.DEFAULT.frontend.config.defaultTheme]
);
},
});
themeService.register(
...BuiltinThemeProvider.themes,
arduinoDarkTheme,
arduinoLightTheme
);
themeService.startupTheme();
themeService.setCurrentTheme(defaultTheme);
window[ThemeServiceSymbol] = themeService;
}
// Require the original, generated `index.js` for `webpack` as the next entry for the `bundle.js`.
require('../../src-gen/frontend/index');

View File

@ -7,6 +7,8 @@
const glob = require('glob');
const isCI = require('is-ci');
shell.env.THEIA_ELECTRON_SKIP_REPLACE_FFMPEG = '1'; // Do not run the ffmpeg validation for the packager.
// Note, this will crash on PI if the available memory is less than desired heap size.
// https://github.com/shelljs/shelljs/issues/1024#issuecomment-1001552543
shell.env.NODE_OPTIONS = '--max_old_space_size=4096'; // Increase heap size for the CI
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(
@ -14,7 +16,7 @@
);
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish, getChannelFile } = utils;
const { isRelease, getChannelFile } = utils;
const { version } = template;
const { productName } = template.build;
@ -58,6 +60,11 @@
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
// Clean up the `./electron/build/patch` and `./electron/build/resources` folder with Git.
// To avoid file duplication between bundled app and dev mode, some files are copied from `./electron-app` to `./electron/build` folder.
const foldersToSyncFromDev = ['patch', 'resources'];
foldersToSyncFromDev.forEach(filename => shell.exec(`git -C ${path('..', 'build', filename)} clean -ffxdq`, { async: false }));
const extensions = require('./extensions.json');
echo(
`Building the application with the following extensions:\n${extensions
@ -70,20 +77,28 @@
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const name of [
for (const filename of [
...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
'i18n'
]) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
cp('-rf', path(rootPath, filename), path('..', workingCopy));
}
//---------------------------------------------------------------------------------------------+
// Copy the patched `index.js` for the frontend, the Theia preload, etc. from `./electron-app` |
//---------------------------------------------------------------------------------------------+
for (const filename of foldersToSyncFromDev) {
cp('-rf', path('..', workingCopy, 'electron-app', filename), path('..', 'build'));
}
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
@ -438,6 +453,12 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
}
}
/**
* @param {import('fs').PathLike} file
* @param {string|undefined} [algorithm="sha512"]
* @param {BufferEncoding|undefined} [encoding="base64"]
* @param {object|undefined} [options]
*/
async function hashFile(
file,
algorithm = 'sha512',