diff --git a/app/ide-desktop/eslint.config.js b/app/ide-desktop/eslint.config.js index 9c820d608023..f3453bc427a3 100644 --- a/app/ide-desktop/eslint.config.js +++ b/app/ide-desktop/eslint.config.js @@ -198,10 +198,6 @@ const RESTRICTED_SYNTAXES = [ 'TSAsExpression:has(TSUnknownKeyword, TSNeverKeyword, TSAnyKeyword) > TSAsExpression', message: 'Use type assertions to specific types instead of `unknown`, `any` or `never`', }, - { - selector: 'IfStatement > ExpressionStatement', - message: 'Wrap `if` branches in `{}`', - }, ] /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/app/ide-desktop/lib/content/esbuild-config.ts b/app/ide-desktop/lib/content/esbuild-config.ts index 3db5b078676b..f7a620ebfd68 100644 --- a/app/ide-desktop/lib/content/esbuild-config.ts +++ b/app/ide-desktop/lib/content/esbuild-config.ts @@ -96,6 +96,7 @@ export function bundlerOptions(args: Arguments) { entryPoints: [ pathModule.resolve(THIS_PATH, 'src', 'index.ts'), pathModule.resolve(THIS_PATH, 'src', 'index.html'), + pathModule.resolve(THIS_PATH, 'src', 'run.js'), pathModule.resolve(THIS_PATH, 'src', 'style.css'), pathModule.resolve(THIS_PATH, 'src', 'docsStyle.css'), ...wasmArtifacts.split(pathModule.delimiter), @@ -107,9 +108,13 @@ export function bundlerOptions(args: Arguments) { outbase: 'src', plugins: [ { + // This is a workaround that is needed + // because esbuild panics when using `loader: { '.js': 'copy' }`. + // See https://github.com/evanw/esbuild/issues/3041. + // Setting `loader: 'copy'` prevents this file from being converted to ESM + // because of the `"type": "module"` in the `package.json`. // This file MUST be in CommonJS format because it is loaded using `Function()` - // in `ensogl/pack/js/src/runner/index.ts`. - // All other files are ESM because of `"type": "module"` in `package.json`. + // in `ensogl/pack/js/src/runner/index.ts` name: 'pkg-js-is-cjs', setup: build => { build.onLoad({ filter: /[/\\]pkg.js$/ }, async ({ path }) => ({ diff --git a/app/ide-desktop/lib/content/src/index.html b/app/ide-desktop/lib/content/src/index.html index d4e33dfb5555..65625b65e350 100644 --- a/app/ide-desktop/lib/content/src/index.html +++ b/app/ide-desktop/lib/content/src/index.html @@ -37,6 +37,7 @@ +
diff --git a/app/ide-desktop/lib/content/src/index.ts b/app/ide-desktop/lib/content/src/index.ts index 3b5da038dcf0..e1d0145b2787 100644 --- a/app/ide-desktop/lib/content/src/index.ts +++ b/app/ide-desktop/lib/content/src/index.ts @@ -8,6 +8,8 @@ import * as authentication from 'enso-authentication' import * as contentConfig from 'enso-content-config' import * as app from '../../../../../target/ensogl-pack/linked-dist/index' +import * as projectManager from './project_manager' +import GLOBAL_CONFIG from '../../../../gui/config.yaml' assert { type: 'yaml' } const logger = app.log.logger @@ -23,8 +25,6 @@ const ESBUILD_EVENT_NAME = 'change' const SECOND = 1000 /** Time in seconds after which a `fetchTimeout` ends. */ const FETCH_TIMEOUT = 300 -/** The `id` attribute of the element that the IDE will be rendered into. */ -const IDE_ELEMENT_ID = 'root' // =================== // === Live reload === @@ -119,132 +119,104 @@ function displayDeprecatedVersionDialog() { } // ======================== -// === Main entry point === +// === Main Entry Point === // ======================== interface StringConfig { [key: string]: StringConfig | string } -// Hack to mutate `configOptions.OPTIONS` -let currentAppInstance: app.App | null = new app.App({ - config: { - loader: { - wasmUrl: 'pkg-opt.wasm', - jsUrl: 'pkg.js', - assetsUrl: 'dynamic-assets', - }, - }, - configOptions: contentConfig.OPTIONS, - packageInfo: { - version: BUILD_INFO.version, - engineVersion: BUILD_INFO.engineVersion, - }, -}) - -function tryStopProject() { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - currentAppInstance?.wasm?.drop?.() -} - -async function runProject(inputConfig?: StringConfig) { - tryStopProject() - const rootElement = document.getElementById(IDE_ELEMENT_ID) - if (!rootElement) { - logger.error(`The root element (the element with ID '${IDE_ELEMENT_ID}') was not found.`) - } else { - while (rootElement.firstChild) { - rootElement.removeChild(rootElement.firstChild) - } - } - - const config = Object.assign( - { - loader: { - wasmUrl: 'pkg-opt.wasm', - jsUrl: 'pkg.js', - assetsUrl: 'dynamic-assets', +class Main { + async main(inputConfig: StringConfig) { + const config = Object.assign( + { + loader: { + wasmUrl: 'pkg-opt.wasm', + jsUrl: 'pkg.js', + assetsUrl: 'dynamic-assets', + }, }, - }, - inputConfig - ) - - currentAppInstance = new app.App({ - config, - configOptions: contentConfig.OPTIONS, - packageInfo: { - version: BUILD_INFO.version, - engineVersion: BUILD_INFO.engineVersion, - }, - }) - console.log('bruh', currentAppInstance) + inputConfig + ) + + const appInstance = new app.App({ + config, + configOptions: contentConfig.OPTIONS, + packageInfo: { + version: BUILD_INFO.version, + engineVersion: BUILD_INFO.engineVersion, + }, + }) - if (!currentAppInstance.initialized) { - console.error('Failed to initialize the application.') - } else { - if (contentConfig.OPTIONS.options.dataCollection.value) { - // TODO: Add remote-logging here. - } - if (!(await checkMinSupportedVersion(contentConfig.OPTIONS))) { - displayDeprecatedVersionDialog() - } else { - const email = contentConfig.OPTIONS.groups.authentication.options.email.value - // The default value is `""`, so a truthiness check is most appropriate here. - if (email) { - logger.log(`User identified as '${email}'.`) + if (appInstance.initialized) { + if (contentConfig.OPTIONS.options.dataCollection.value) { + // TODO: Add remote-logging here. } - void currentAppInstance.run() - } - } -} - -if ( - (contentConfig.OPTIONS.options.authentication.value || - contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value) && - contentConfig.OPTIONS.groups.startup.options.entry.value === - contentConfig.OPTIONS.groups.startup.options.entry.default -) { - window.tryStopProject = tryStopProject - window.runProject = runProject - const hideAuth = () => { - const auth = document.getElementById('dashboard') - const ide = document.getElementById('root') - if (auth) { - auth.style.display = 'none' - } - if (ide) { - ide.hidden = false - } - } - /** This package is an Electron desktop app (i.e., not in the Cloud), so - * we're running on the desktop. */ - /** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/345 - * `content` and `dashboard` packages **MUST BE MERGED INTO ONE**. The IDE - * should only have one entry point. Right now, we have two. One for the cloud - * and one for the desktop. Once these are merged, we can't hardcode the - * platform here, and need to detect it from the environment. */ - const platform = authentication.Platform.desktop - /** FIXME [PB]: https://github.com/enso-org/cloud-v2/issues/366 - * React hooks rerender themselves multiple times. It is resulting in multiple - * Enso main scene being initialized. As a temporary workaround we check whether - * appInstance was already ran. Target solution should move running appInstance - * where it will be called only once. */ - let appInstanceRan = false - const onAuthenticated = () => { - if (!contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value) { - hideAuth() - if (!appInstanceRan) { - appInstanceRan = true - void runProject() + if (!(await checkMinSupportedVersion(contentConfig.OPTIONS))) { + displayDeprecatedVersionDialog() + } else { + if ( + (contentConfig.OPTIONS.options.authentication.value || + contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value) && + contentConfig.OPTIONS.groups.startup.options.entry.value === + contentConfig.OPTIONS.groups.startup.options.entry.default + ) { + const hideAuth = () => { + const auth = document.getElementById('dashboard') + const ide = document.getElementById('root') + if (auth) auth.style.display = 'none' + if (ide) ide.style.display = '' + } + /** This package is an Electron desktop app (i.e., not in the Cloud), so + * we're running on the desktop. */ + /** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/345 + * `content` and `dashboard` packages **MUST BE MERGED INTO ONE**. The IDE + * should only have one entry point. Right now, we have two. One for the cloud + * and one for the desktop. Once these are merged, we can't hardcode the + * platform here, and need to detect it from the environment. */ + const platform = authentication.Platform.desktop + /** FIXME [PB]: https://github.com/enso-org/cloud-v2/issues/366 + * React hooks rerender themselves multiple times. It is resulting in multiple + * Enso main scene being initialized. As a temporary workaround we check whether + * appInstance was already ran. Target solution should move running appInstance + * where it will be called only once. */ + let appInstanceRan = false + const onAuthenticated = () => { + if ( + !contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value + ) { + hideAuth() + if (!appInstanceRan) { + appInstanceRan = true + void appInstance.run() + } + } + } + authentication.run({ + logger, + platform, + projectManager: projectManager.ProjectManager.default(), + showDashboard: + contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value, + onAuthenticated, + }) + } else { + void appInstance.run() + } + const email = contentConfig.OPTIONS.groups.authentication.options.email.value + // The default value is `""`, so a truthiness check is most appropriate here. + if (email) { + logger.log(`User identified as '${email}'.`) + } } + } else { + console.error('Failed to initialize the application.') } } - authentication.run({ - logger, - platform, - showDashboard: contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value, - onAuthenticated, - }) -} else { - void runProject() } + +const API = new Main() + +// @ts-expect-error `globalConfig.windowAppScopeName` is not known at typecheck time. +// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access +window[GLOBAL_CONFIG.windowAppScopeName] = API diff --git a/app/ide-desktop/lib/content/src/newtype.ts b/app/ide-desktop/lib/content/src/newtype.ts new file mode 100644 index 000000000000..b3a85ec61518 --- /dev/null +++ b/app/ide-desktop/lib/content/src/newtype.ts @@ -0,0 +1,39 @@ +/** @file TypeScript's closest equivalent of `newtype`s. */ + +interface NewtypeVariant