From 1158134f73707f4b4a356aefdd846bf7a50c3c5d Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Mon, 16 Feb 2026 10:26:04 +0100 Subject: [PATCH 1/2] feat: rework local builds --- src/less/components/commands.less | 4 +- src/less/components/settings-electron.less | 42 ++++++ .../components/dialog-add-version.tsx | 78 ++++++----- src/renderer/components/settings-electron.tsx | 78 +++++++++-- src/renderer/components/version-select.tsx | 132 +++++++++++++++--- src/renderer/runner.ts | 2 + src/renderer/state.ts | 15 +- src/renderer/utils/sort-versions.ts | 14 +- 8 files changed, 288 insertions(+), 77 deletions(-) diff --git a/src/less/components/commands.less b/src/less/components/commands.less index 9f2b576fa5..68f220a072 100644 --- a/src/less/components/commands.less +++ b/src/less/components/commands.less @@ -8,12 +8,12 @@ header { background-color: @background-3; -webkit-app-region: drag; - #version-chooser .bp3-button-text::before { + #version-chooser:not([data-local]) .bp3-button-text::before { content: 'Electron v'; } @media (max-width: 980px) { - #version-chooser .bp3-button-text::before { + #version-chooser:not([data-local]) .bp3-button-text::before { content: 'v'; } } diff --git a/src/less/components/settings-electron.less b/src/less/components/settings-electron.less index 46fcc6dcf4..d2c11d1798 100644 --- a/src/less/components/settings-electron.less +++ b/src/less/components/settings-electron.less @@ -11,6 +11,33 @@ overflow: hidden; background-color: @background-1; + .electron-versions-section-header { + padding: 8px 15px; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.5px; + color: @text-color-2; + background-color: @background-2; + border-bottom: 1px solid @border-color-1; + border-top: 1px solid @border-color-1; + display: flex; + align-items: center; + gap: 6px; + + &:first-child { + border-top: none; + } + + &.local { + border-left: 3px solid @green4; + } + + &.remote { + border-left: 3px solid @blue4; + } + } + .electron-versions-header { display: flex; font-weight: 600; @@ -62,6 +89,14 @@ background-color: rgba(92, 112, 128, 0.15); } + &.local { + border-left: 3px solid @green4; + } + + &.remote { + border-left: 3px solid @blue4; + } + .version-col { flex: 1; padding: 0 15px; @@ -103,6 +138,13 @@ color: @foreground-1; } + .electron-versions-section-header { + background-color: rgba(255, 255, 255, 0.06); + border-bottom-color: @dark-gray4; + border-top-color: @dark-gray4; + color: rgba(255, 255, 255, 0.7); + } + .electron-versions-header { background-color: @dark-gray5; border-bottom-color: @dark-gray4; diff --git a/src/renderer/components/dialog-add-version.tsx b/src/renderer/components/dialog-add-version.tsx index 120b9d7be1..e8ed8aad72 100644 --- a/src/renderer/components/dialog-add-version.tsx +++ b/src/renderer/components/dialog-add-version.tsx @@ -9,7 +9,6 @@ import { Intent, } from '@blueprintjs/core'; import { observer } from 'mobx-react'; -import * as semver from 'semver'; import { Version } from '../../interfaces'; import { AppState } from '../state'; @@ -22,11 +21,20 @@ interface AddVersionDialogProps { interface AddVersionDialogState { isValidElectron: boolean; - isValidVersion: boolean; + isValidName: boolean; existingLocalVersion?: Version; folderPath?: string; localName?: string; - version: string; + name: string; +} + +/** + * Generate a unique version key for a local build. + * Uses a format that is valid semver but can never conflict + * with a real Electron release. + */ +function generateLocalVersionKey(): string { + return `0.0.0-local.${Date.now()}`; } /** @@ -41,14 +49,14 @@ export const AddVersionDialog = observer( super(props); this.state = { - isValidVersion: false, + isValidName: false, isValidElectron: false, - version: '', + name: '', }; this.onSubmit = this.onSubmit.bind(this); this.onClose = this.onClose.bind(this); - this.onChangeVersion = this.onChangeVersion.bind(this); + this.onChangeName = this.onChangeName.bind(this); } /** @@ -65,20 +73,23 @@ export const AddVersionDialog = observer( folderPath, isValidElectron, localName, + // Pre-fill name from detected binary name if available + name: localName || '', + isValidName: !!localName, }); } } /** - * Handles a change of the file input + * Handles a change of the name input */ - public onChangeVersion(event: React.ChangeEvent) { - const version = event.target.value || ''; - const isValidVersion = !!semver.valid(version); + public onChangeName(event: React.ChangeEvent) { + const name = event.target.value || ''; + const isValidName = name.trim().length > 0; this.setState({ - version, - isValidVersion, + name, + isValidName, }); } @@ -86,27 +97,21 @@ export const AddVersionDialog = observer( * Handles the submission of the dialog */ public async onSubmit(): Promise { - const { - folderPath, - version, - isValidElectron, - existingLocalVersion, - localName, - } = this.state; + const { folderPath, name, isValidElectron, existingLocalVersion } = + this.state; if (!folderPath) return; - const toAdd: Version = { - localPath: folderPath, - version, - name: localName, - }; - // swap to old local electron version if the user adds a new one with the same path if (isValidElectron && existingLocalVersion?.localPath) { // set previous version as active version this.props.appState.setVersion(existingLocalVersion.version); } else { + const toAdd: Version = { + localPath: folderPath, + version: generateLocalVersionKey(), + name: name.trim(), + }; this.props.appState.addLocalVersion(toAdd); } this.onClose(); @@ -121,9 +126,8 @@ export const AddVersionDialog = observer( } get buttons() { - const { isValidElectron, isValidVersion, existingLocalVersion } = - this.state; - const canAdd = isValidElectron && isValidVersion && !existingLocalVersion; + const { isValidElectron, isValidName, existingLocalVersion } = this.state; + const canAdd = isValidElectron && isValidName && !existingLocalVersion; const canSwitch = isValidElectron && existingLocalVersion; return [ @@ -209,20 +213,20 @@ export const AddVersionDialog = observer( } private renderVersionInput(): JSX.Element | null { - const { isValidElectron, isValidVersion, version } = this.state; + const { isValidElectron, isValidName, name } = this.state; if (!isValidElectron) return null; return ( <>

- Please specify a version, used for typings and the name. Must be{' '} - semver compliant. + Give this local build a name so you can identify it in the version + list.

); @@ -234,8 +238,8 @@ export const AddVersionDialog = observer( private reset(): void { this.setState({ isValidElectron: false, - isValidVersion: false, - version: '', + isValidName: false, + name: '', folderPath: undefined, localName: undefined, }); diff --git a/src/renderer/components/settings-electron.tsx b/src/renderer/components/settings-electron.tsx index 87fa84f1c6..782eb25f2c 100644 --- a/src/renderer/components/settings-electron.tsx +++ b/src/renderer/components/settings-electron.tsx @@ -119,7 +119,7 @@ const ElectronVersionRow = observer(({ index, style, data }: RowProps) => { /> ); - } else if (disableDownload(version)) { + } else if (!isLocal && disableDownload(version)) { return ( { return