diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8c8771aff..bf910c649b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - node: [16.13.0] + node: [16.17.1] os: [macos-11, windows-2019] arch: [x64, arm64] include: @@ -57,6 +57,8 @@ jobs: env: npm_config_arch: ${{ matrix.arch }} TARGET_ARCH: ${{ matrix.arch }} + - name: Validate Electron version + run: yarn run validate-electron-version - name: Lint run: yarn lint - name: Validate changelog diff --git a/.node-version b/.node-version index 58a4133d91..c85fa1bbef 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -16.13.0 +16.17.1 diff --git a/.nvmrc b/.nvmrc index ff650592a1..e0325e5adb 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.13.0 +v16.17.1 diff --git a/.tool-versions b/.tool-versions index 866f9e8254..2daf433083 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ python 3.9.5 -nodejs 16.13.0 +nodejs 16.17.1 diff --git a/app/.npmrc b/app/.npmrc index efea0b211b..e2daa8fcd9 100644 --- a/app/.npmrc +++ b/app/.npmrc @@ -1,3 +1,3 @@ runtime = electron disturl = https://electronjs.org/headers -target = 19.0.0 +target = 22.0.0 diff --git a/app/src/lib/app-state.ts b/app/src/lib/app-state.ts index 0e9525456d..5e1ebe43c4 100644 --- a/app/src/lib/app-state.ts +++ b/app/src/lib/app-state.ts @@ -327,6 +327,7 @@ export enum FoldoutType { Branch, AppMenu, AddMenu, + PushPull, } export type AppMenuFoldout = { @@ -358,6 +359,7 @@ export type Foldout = | { type: FoldoutType.AddMenu } | BranchFoldout | AppMenuFoldout + | { type: FoldoutType.PushPull } export enum RepositorySectionTab { Changes, diff --git a/app/src/lib/editors/darwin.ts b/app/src/lib/editors/darwin.ts index 3086ebacff..7634e7482e 100644 --- a/app/src/lib/editors/darwin.ts +++ b/app/src/lib/editors/darwin.ts @@ -91,6 +91,10 @@ const editors: IDarwinExternalEditor[] = [ name: 'WebStorm', bundleIdentifiers: ['com.jetbrains.WebStorm'], }, + { + name: 'CLion', + bundleIdentifiers: ['com.jetbrains.CLion'], + }, { name: 'Typora', bundleIdentifiers: ['abnerworks.Typora'], diff --git a/app/src/lib/editors/linux.ts b/app/src/lib/editors/linux.ts index 0670548b44..8136ac932c 100644 --- a/app/src/lib/editors/linux.ts +++ b/app/src/lib/editors/linux.ts @@ -23,9 +23,26 @@ const editors: ILinuxExternalEditor[] = [ name: 'Neovim', paths: ['/usr/bin/nvim'], }, + { + name: 'Neovim-Qt', + paths: ['/usr/bin/nvim-qt'], + }, + { + name: 'Neovide', + paths: ['/usr/bin/neovide'], + }, + { + name: 'gVim', + paths: ['/usr/bin/gvim'], + }, { name: 'Visual Studio Code', - paths: ['/usr/share/code/bin/code', '/snap/bin/code', '/usr/bin/code'], + paths: [ + '/usr/share/code/bin/code', + '/snap/bin/code', + '/usr/bin/code', + '/mnt/c/Program Files/Microsoft VS Code/bin/code', + ], }, { name: 'Visual Studio Code (Insiders)', @@ -33,7 +50,11 @@ const editors: ILinuxExternalEditor[] = [ }, { name: 'VSCodium', - paths: ['/usr/bin/codium', '/var/lib/flatpak/app/com.vscodium.codium'], + paths: [ + '/usr/bin/codium', + '/var/lib/flatpak/app/com.vscodium.codium', + '/usr/share/vscodium-bin/bin/codium', + ], }, { name: 'Sublime Text', @@ -74,6 +95,34 @@ const editors: ILinuxExternalEditor[] = [ name: 'Emacs', paths: ['/snap/bin/emacs', '/usr/local/bin/emacs', '/usr/bin/emacs'], }, + { + name: 'Kate', + paths: ['/usr/bin/kate'], + }, + { + name: 'GEdit', + paths: ['/usr/bin/gedit'], + }, + { + name: 'GNOME Text Editor', + paths: ['/usr/bin/gnome-text-editor'], + }, + { + name: 'GNOME Builder', + paths: ['/usr/bin/gnome-builder'], + }, + { + name: 'Notepadqq', + paths: ['/usr/bin/notepadqq'], + }, + { + name: 'Geany', + paths: ['/usr/bin/geany'], + }, + { + name: 'Mousepad', + paths: ['/usr/bin/mousepad'], + }, ] async function getAvailablePath(paths: string[]): Promise { diff --git a/app/src/lib/editors/win32.ts b/app/src/lib/editors/win32.ts index bf786e8f3a..afdc6c09ba 100644 --- a/app/src/lib/editors/win32.ts +++ b/app/src/lib/editors/win32.ts @@ -59,7 +59,7 @@ type WindowsExternalEditor = { readonly displayNamePrefix: string /** Value of the Publisher registry key that belongs to this editor. */ - readonly publisher: string + readonly publishers: string[] /** * Default shell script name for JetBrains Product @@ -150,21 +150,21 @@ const editors: WindowsExternalEditor[] = [ registryKeys: [CurrentUserUninstallKey('atom')], executableShimPaths: [['bin', 'atom.cmd']], displayNamePrefix: 'Atom', - publisher: 'GitHub Inc.', + publishers: ['GitHub Inc.'], }, { name: 'Atom Beta', registryKeys: [CurrentUserUninstallKey('atom-beta')], executableShimPaths: [['bin', 'atom-beta.cmd']], displayNamePrefix: 'Atom Beta', - publisher: 'GitHub Inc.', + publishers: ['GitHub Inc.'], }, { name: 'Atom Nightly', registryKeys: [CurrentUserUninstallKey('atom-nightly')], executableShimPaths: [['bin', 'atom-nightly.cmd']], displayNamePrefix: 'Atom Nightly', - publisher: 'GitHub Inc.', + publishers: ['GitHub Inc.'], }, { name: 'Visual Studio Code', @@ -186,7 +186,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['bin', 'code.cmd']], displayNamePrefix: 'Microsoft Visual Studio Code', - publisher: 'Microsoft Corporation', + publishers: ['Microsoft Corporation'], }, { name: 'Visual Studio Code (Insiders)', @@ -208,29 +208,63 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['bin', 'code-insiders.cmd']], displayNamePrefix: 'Microsoft Visual Studio Code Insiders', - publisher: 'Microsoft Corporation', + publishers: ['Microsoft Corporation'], }, { name: 'Visual Studio Codium', registryKeys: [ // 64-bit version of VSCodium (user) CurrentUserUninstallKey('{2E1F05D1-C245-4562-81EE-28188DB6FD17}_is1'), - // 32-bit version of VSCodium (user) + // 32-bit version of VSCodium (user) - new key + CurrentUserUninstallKey('{0FD05EB4-651E-4E78-A062-515204B47A3A}_is1'), + // ARM64 version of VSCodium (user) - new key + CurrentUserUninstallKey('{57FD70A5-1B8D-4875-9F40-C5553F094828}_is1'), + // 64-bit version of VSCodium (system) - new key + LocalMachineUninstallKey('{88DA3577-054F-4CA1-8122-7D820494CFFB}_is1'), + // 32-bit version of VSCodium (system) - new key + Wow64LocalMachineUninstallKey( + '{763CBF88-25C6-4B10-952F-326AE657F16B}_is1' + ), + // ARM64 version of VSCodium (system) - new key + LocalMachineUninstallKey('{67DEE444-3D04-4258-B92A-BC1F0FF2CAE4}_is1'), + // 32-bit version of VSCodium (user) - old key CurrentUserUninstallKey('{C6065F05-9603-4FC4-8101-B9781A25D88E}}_is1'), - // ARM64 version of VSCodium (user) + // ARM64 version of VSCodium (user) - old key CurrentUserUninstallKey('{3AEBF0C8-F733-4AD4-BADE-FDB816D53D7B}_is1'), - // 64-bit version of VSCodium (system) + // 64-bit version of VSCodium (system) - old key LocalMachineUninstallKey('{D77B7E06-80BA-4137-BCF4-654B95CCEBC5}_is1'), - // 32-bit version of VSCodium (system) + // 32-bit version of VSCodium (system) - old key Wow64LocalMachineUninstallKey( '{E34003BB-9E10-4501-8C11-BE3FAA83F23F}_is1' ), - // ARM64 version of VSCodium (system) + // ARM64 version of VSCodium (system) - old key LocalMachineUninstallKey('{D1ACE434-89C5-48D1-88D3-E2991DF85475}_is1'), ], executableShimPaths: [['bin', 'codium.cmd']], displayNamePrefix: 'VSCodium', - publisher: 'Microsoft Corporation', + publishers: ['VSCodium', 'Microsoft Corporation'], + }, + { + name: 'Visual Studio Codium (Insiders)', + registryKeys: [ + // 64-bit version of VSCodium - Insiders (user) + CurrentUserUninstallKey('{20F79D0D-A9AC-4220-9A81-CE675FFB6B41}_is1'), + // 32-bit version of VSCodium - Insiders (user) + CurrentUserUninstallKey('{ED2E5618-3E7E-4888-BF3C-A6CCC84F586F}_is1'), + // ARM64 version of VSCodium - Insiders (user) + CurrentUserUninstallKey('{2E362F92-14EA-455A-9ABD-3E656BBBFE71}_is1'), + // 64-bit version of VSCodium - Insiders (system) + LocalMachineUninstallKey('{B2E0DDB2-120E-4D34-9F7E-8C688FF839A2}_is1'), + // 32-bit version of VSCodium - Insiders (system) + Wow64LocalMachineUninstallKey( + '{EF35BB36-FA7E-4BB9-B7DA-D1E09F2DA9C9}_is1' + ), + // ARM64 version of VSCodium - Insiders (system) + LocalMachineUninstallKey('{44721278-64C6-4513-BC45-D48E07830599}_is1'), + ], + executableShimPaths: [['bin', 'codium-insiders.cmd']], + displayNamePrefix: 'VSCodium (Insiders)', + publishers: ['VSCodium'], }, { name: 'Sublime Text', @@ -242,7 +276,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['subl.exe']], displayNamePrefix: 'Sublime Text', - publisher: 'Sublime HQ Pty Ltd', + publishers: ['Sublime HQ Pty Ltd'], }, { name: 'Brackets', @@ -251,7 +285,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['Brackets.exe']], displayNamePrefix: 'Brackets', - publisher: 'brackets.io', + publishers: ['brackets.io'], }, { name: 'ColdFusion Builder', @@ -263,7 +297,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['CFBuilder.exe']], displayNamePrefix: 'Adobe ColdFusion Builder', - publisher: 'Adobe Systems Incorporated', + publishers: ['Adobe Systems Incorporated'], }, { name: 'Typora', @@ -277,7 +311,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['typora.exe']], displayNamePrefix: 'Typora', - publisher: 'typora.io', + publishers: ['typora.io'], }, { name: 'SlickEdit', @@ -307,7 +341,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['win', 'vs.exe']], displayNamePrefix: 'SlickEdit', - publisher: 'SlickEdit Inc.', + publishers: ['SlickEdit Inc.'], }, { name: 'Aptana Studio 3', @@ -316,7 +350,7 @@ const editors: WindowsExternalEditor[] = [ ], executableShimPaths: [['AptanaStudio3.exe']], displayNamePrefix: 'Aptana Studio', - publisher: 'Appcelerator', + publishers: ['Appcelerator'], }, { name: 'JetBrains Webstorm', @@ -324,7 +358,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('webstorm'), jetBrainsToolboxScriptName: 'webstorm', displayNamePrefix: 'WebStorm', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains Phpstorm', @@ -332,7 +366,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('phpstorm'), jetBrainsToolboxScriptName: 'phpstorm', displayNamePrefix: 'PhpStorm', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'Android Studio', @@ -344,7 +378,7 @@ const editors: WindowsExternalEditor[] = [ ['..', 'bin', `studio.exe`], ], displayNamePrefix: 'Android Studio', - publisher: 'Google LLC', + publishers: ['Google LLC'], }, { name: 'Notepad++', @@ -356,7 +390,7 @@ const editors: WindowsExternalEditor[] = [ ], installLocationRegistryKey: 'DisplayIcon', displayNamePrefix: 'Notepad++', - publisher: 'Notepad++ Team', + publishers: ['Notepad++ Team'], }, { name: 'JetBrains Rider', @@ -364,14 +398,14 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('rider'), jetBrainsToolboxScriptName: 'rider', displayNamePrefix: 'JetBrains Rider', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'RStudio', registryKeys: [Wow64LocalMachineUninstallKey('RStudio')], installLocationRegistryKey: 'DisplayIcon', displayNamePrefix: 'RStudio', - publisher: 'RStudio', + publishers: ['RStudio', 'Posit Software'], }, { name: 'JetBrains IntelliJ Idea', @@ -379,7 +413,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('idea'), jetBrainsToolboxScriptName: 'idea', displayNamePrefix: 'IntelliJ IDEA ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains IntelliJ Idea Community Edition', @@ -388,7 +422,7 @@ const editors: WindowsExternalEditor[] = [ ), executableShimPaths: executableShimPathsForJetBrainsIDE('idea'), displayNamePrefix: 'IntelliJ IDEA Community Edition ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains PyCharm', @@ -396,14 +430,14 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('pycharm'), jetBrainsToolboxScriptName: 'pycharm', displayNamePrefix: 'PyCharm ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains PyCharm Community Edition', registryKeys: registryKeysForJetBrainsIDE('PyCharm Community Edition'), executableShimPaths: executableShimPathsForJetBrainsIDE('pycharm'), displayNamePrefix: 'PyCharm Community Edition', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains CLion', @@ -411,7 +445,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('clion'), jetBrainsToolboxScriptName: 'clion', displayNamePrefix: 'CLion ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains RubyMine', @@ -419,7 +453,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('rubymine'), jetBrainsToolboxScriptName: 'rubymine', displayNamePrefix: 'RubyMine ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains GoLand', @@ -427,7 +461,7 @@ const editors: WindowsExternalEditor[] = [ executableShimPaths: executableShimPathsForJetBrainsIDE('goland'), jetBrainsToolboxScriptName: 'goland', displayNamePrefix: 'GoLand ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, { name: 'JetBrains Fleet', @@ -435,7 +469,7 @@ const editors: WindowsExternalEditor[] = [ jetBrainsToolboxScriptName: 'fleet', installLocationRegistryKey: 'DisplayIcon', displayNamePrefix: 'Fleet ', - publisher: 'JetBrains s.r.o.', + publishers: ['JetBrains s.r.o.'], }, ] @@ -471,7 +505,7 @@ async function findApplication(editor: WindowsExternalEditor) { if ( !displayName.startsWith(editor.displayNamePrefix) || - publisher !== editor.publisher + !editor.publishers.includes(publisher) ) { log.debug(`Unexpected registry entries for ${editor.name}`) continue diff --git a/app/src/lib/feature-flag.ts b/app/src/lib/feature-flag.ts index 6bfc307c19..6d3a63d1b9 100644 --- a/app/src/lib/feature-flag.ts +++ b/app/src/lib/feature-flag.ts @@ -122,3 +122,8 @@ export function enableStackedPopups(): boolean { export function enablePreventClosingWhileUpdating(): boolean { return true } + +/** Should we enable the new push-pull-fetch dropdown? */ +export function enablePushPullFetchDropdown(): boolean { + return enableBetaFeatures() +} diff --git a/app/src/main-process/main.ts b/app/src/main-process/main.ts index 640c3a4414..bef7e254ea 100644 --- a/app/src/main-process/main.ts +++ b/app/src/main-process/main.ts @@ -689,11 +689,11 @@ app.on('activate', () => { }) app.on('web-contents-created', (event, contents) => { - contents.on('new-window', (event, url) => { - // Prevent links or window.open from opening new windows - event.preventDefault() + contents.setWindowOpenHandler(({ url }) => { log.warn(`Prevented new window to: ${url}`) + return { action: 'deny' } }) + // prevent link navigation within our windows // see https://www.electronjs.org/docs/tutorial/security#12-disable-or-limit-navigation contents.on('will-navigate', (event, url) => { @@ -729,7 +729,13 @@ function createWindow() { electron: '>=1.2.1', } - const extensions = [REACT_DEVELOPER_TOOLS, ChromeLens] + const axeDevTools = { + id: 'lhdoppojpmngadmnindnejefpokejbdd', + electron: '>=1.2.1', + Permissions: ['tabs', 'debugger'], + } + + const extensions = [REACT_DEVELOPER_TOOLS, ChromeLens, axeDevTools] for (const extension of extensions) { try { diff --git a/app/src/main-process/ordered-webrequest.ts b/app/src/main-process/ordered-webrequest.ts index b200cdcb58..bcb4494264 100644 --- a/app/src/main-process/ordered-webrequest.ts +++ b/app/src/main-process/ordered-webrequest.ts @@ -1,7 +1,7 @@ import { WebRequest, OnBeforeRequestListenerDetails, - Response, + CallbackResponse, OnBeforeSendHeadersListenerDetails, BeforeSendResponse, OnCompletedListenerDetails, @@ -112,7 +112,7 @@ export class OrderedWebRequest { public readonly onBeforeRequest: AsyncListenerSet< OnBeforeRequestListenerDetails, - Response + CallbackResponse > public readonly onBeforeSendHeaders: AsyncListenerSet< @@ -140,7 +140,7 @@ export class OrderedWebRequest { this.onBeforeRequest = new AsyncListenerSet( webRequest.onBeforeRequest.bind(webRequest), async (listeners, details) => { - let response: Response = {} + let response: CallbackResponse = {} for (const listener of listeners) { response = await listener(details) diff --git a/app/src/ui/app.tsx b/app/src/ui/app.tsx index 04935d73be..bbe2be7374 100644 --- a/app/src/ui/app.tsx +++ b/app/src/ui/app.tsx @@ -93,10 +93,7 @@ import { RepositoryStateCache } from '../lib/stores/repository-state-cache' import { PopupType, Popup } from '../models/popup' import { OversizedFiles } from './changes/oversized-files-warning' import { PushNeedsPullWarning } from './push-needs-pull' -import { - ForcePushBranchState, - getCurrentBranchForcePushState, -} from '../lib/rebase' +import { getCurrentBranchForcePushState } from '../lib/rebase' import { Banner, BannerType } from '../models/banner' import { StashAndSwitchBranch } from './stash-changes/stash-and-switch-branch-dialog' import { OverwriteStash } from './stash-changes/overwrite-stashed-changes-dialog' @@ -2800,9 +2797,15 @@ export class App extends React.Component { remoteName = tip.branch.upstreamRemoteName } - const isForcePush = - getCurrentBranchForcePushState(branchesState, aheadBehind) === - ForcePushBranchState.Recommended + const currentFoldout = this.state.currentFoldout + + const isDropdownOpen = + currentFoldout !== null && currentFoldout.type === FoldoutType.PushPull + + const forcePushBranchState = getCurrentBranchForcePushState( + branchesState, + aheadBehind + ) return ( { tipState={tip.kind} pullWithRebase={pullWithRebase} rebaseInProgress={rebaseInProgress} - isForcePush={isForcePush} + forcePushBranchState={forcePushBranchState} shouldNudge={ this.state.currentOnboardingTutorialStep === TutorialStep.PushBranch } + isDropdownOpen={isDropdownOpen} + askForConfirmationOnForcePush={this.state.askForConfirmationOnForcePush} + onDropdownStateChanged={this.onPushPullDropdownStateChanged} /> ) } @@ -2884,6 +2890,14 @@ export class App extends React.Component { this.props.dispatcher.openCreatePullRequestInBrowser(repository, branch) } + private onPushPullDropdownStateChanged = (newState: DropdownState) => { + if (newState === 'open') { + this.props.dispatcher.showFoldout({ type: FoldoutType.PushPull }) + } else { + this.props.dispatcher.closeFoldout(FoldoutType.PushPull) + } + } + private onBranchDropdownStateChanged = (newState: DropdownState) => { if (newState === 'open') { this.props.dispatcher.showFoldout({ type: FoldoutType.Branch }) diff --git a/app/src/ui/history/commit-summary.tsx b/app/src/ui/history/commit-summary.tsx index 904f195617..e342225d71 100644 --- a/app/src/ui/history/commit-summary.tsx +++ b/app/src/ui/history/commit-summary.tsx @@ -536,6 +536,7 @@ export class CommitSummary extends React.Component< let filesAdded = 0 let filesModified = 0 let filesRemoved = 0 + let filesRenamed = 0 for (const file of this.props.changesetData.files) { switch (file.status.kind) { case AppFileStatusKind.New: @@ -547,9 +548,14 @@ export class CommitSummary extends React.Component< case AppFileStatusKind.Deleted: filesRemoved += 1 break + case AppFileStatusKind.Renamed: + filesRenamed += 1 } } + const hasFileDescription = + filesAdded + filesModified + filesRemoved + filesRenamed > 0 + const filesLongDescription = ( <> {filesAdded > 0 ? ( @@ -579,6 +585,15 @@ export class CommitSummary extends React.Component< {filesRemoved} deleted ) : null} + {filesRenamed > 0 ? ( + + + {filesRenamed} renamed + + ) : null} ) @@ -586,7 +601,9 @@ export class CommitSummary extends React.Component< 0 ? filesLongDescription : undefined} + tooltip={ + fileCount > 0 && hasFileDescription ? filesLongDescription : undefined + } > {filesShortDescription} diff --git a/app/src/ui/lib/app-proxy.ts b/app/src/ui/lib/app-proxy.ts index 960b6d228d..78db36c4fe 100644 --- a/app/src/ui/lib/app-proxy.ts +++ b/app/src/ui/lib/app-proxy.ts @@ -8,7 +8,6 @@ export type PathType = | 'home' | 'appData' | 'userData' - | 'cache' | 'temp' | 'exe' | 'module' @@ -21,6 +20,7 @@ export type PathType = | 'recent' | 'logs' | 'crashDumps' + | 'sessionData' /** * Get the version of the app. diff --git a/app/src/ui/repository-settings/repository-settings.tsx b/app/src/ui/repository-settings/repository-settings.tsx index 57fa3b6069..f381127a5d 100644 --- a/app/src/ui/repository-settings/repository-settings.tsx +++ b/app/src/ui/repository-settings/repository-settings.tsx @@ -288,12 +288,14 @@ export class RepositorySettings extends React.Component< const errors = new Array() if (this.state.remote && this.props.remote) { - if (this.state.remote.url !== this.props.remote.url) { + const trimmedUrl = this.state.remote.url.trim() + + if (trimmedUrl !== this.props.remote.url) { try { await this.props.dispatcher.setRemoteURL( this.props.repository, this.props.remote.name, - this.state.remote.url + trimmedUrl ) } catch (e) { log.error( diff --git a/app/src/ui/toolbar/branch-dropdown.tsx b/app/src/ui/toolbar/branch-dropdown.tsx index 0004087378..4996d243ed 100644 --- a/app/src/ui/toolbar/branch-dropdown.tsx +++ b/app/src/ui/toolbar/branch-dropdown.tsx @@ -183,7 +183,7 @@ export class BranchDropdown extends React.Component< const isOpen = this.props.isOpen const currentState: DropdownState = isOpen && canOpen ? 'open' : 'closed' - const buttonClassName = classNames('nudge-arrow', { + const buttonClassName = classNames('branch-toolbar-button', 'nudge-arrow', { 'nudge-arrow-up': this.props.shouldNudge, }) diff --git a/app/src/ui/toolbar/dropdown.tsx b/app/src/ui/toolbar/dropdown.tsx index f49b03c640..c59328854c 100644 --- a/app/src/ui/toolbar/dropdown.tsx +++ b/app/src/ui/toolbar/dropdown.tsx @@ -13,7 +13,26 @@ import { TooltipTarget } from '../lib/tooltip' export type DropdownState = 'open' | 'closed' +/** Represents the style of the dropdown */ +export enum ToolbarDropdownStyle { + /** + * The dropdown is rendered as a single button and, when expanded, takes the + * full height of the window. + */ + Foldout, + + /** + * The dropdown is rendered as two buttons: one is the toolbar button itself, + * and the other one is the expand/collapse button. + * When expanded, it only takes the height of the content. + */ + MultiOption, +} + export interface IToolbarDropdownProps { + /** The style of the dropdown. Default: Foldout */ + readonly dropdownStyle?: ToolbarDropdownStyle + /** The primary button text, describing its function */ readonly title?: string @@ -74,6 +93,8 @@ export interface IToolbarDropdownProps { */ readonly onContextMenu?: (event: React.MouseEvent) => void + readonly onClick?: (event: React.MouseEvent) => void + /** * A function that's called whenever something is dragged over the * dropdown. @@ -185,7 +206,8 @@ export class ToolbarDropdown extends React.Component< IToolbarDropdownProps, IToolbarDropdownState > { - private innerButton: ToolbarButton | null = null + private innerButton = React.createRef() + private rootDiv = React.createRef() private focusTrapOptions: FocusTrapOptions public constructor(props: IToolbarDropdownProps) { @@ -224,13 +246,25 @@ export class ToolbarDropdown extends React.Component< } const state = this.props.dropdownState - - return ( + const dropdownIcon = ( ) + + return this.props.dropdownStyle === ToolbarDropdownStyle.MultiOption ? ( + + {dropdownIcon} + + ) : ( + dropdownIcon + ) } - private onClick = (event: React.MouseEvent) => { + private onToggleDropdownClick = ( + event: React.MouseEvent + ) => { const newState: DropdownState = this.props.dropdownState === 'open' ? 'closed' : 'open' @@ -247,13 +281,22 @@ export class ToolbarDropdown extends React.Component< this.props.onDropdownStateChanged(newState, source) } + private onMainButtonClick = (event: React.MouseEvent) => { + if (this.props.dropdownStyle === ToolbarDropdownStyle.MultiOption) { + this.props.onClick?.(event) + return + } + + this.onToggleDropdownClick(event) + } + private onContextMenu = (event: React.MouseEvent) => { this.props.onContextMenu?.(event) } private updateClientRectIfNecessary() { - if (this.props.dropdownState === 'open' && this.innerButton) { - const newRect = this.innerButton.getButtonBoundingClientRect() + if (this.props.dropdownState === 'open' && this.rootDiv.current) { + const newRect = this.rootDiv.current.getBoundingClientRect() if (newRect) { const currentRect = this.state.clientRect @@ -268,10 +311,6 @@ export class ToolbarDropdown extends React.Component< this.updateClientRectIfNecessary() } - public componentWillUnmount() { - this.innerButton = null - } - public componentDidUpdate() { this.updateClientRectIfNecessary() } @@ -305,12 +344,16 @@ export class ToolbarDropdown extends React.Component< return undefined } + const heightStyle: React.CSSProperties = + this.props.dropdownStyle === ToolbarDropdownStyle.MultiOption + ? { maxHeight: '100%', width: rect.width } + : { height: '100%', minWidth: rect.width } + return { position: 'absolute', marginLeft: rect.left, - minWidth: rect.width, - height: '100%', top: 0, + ...heightStyle, } } @@ -354,22 +397,21 @@ export class ToolbarDropdown extends React.Component< ) } - private onRef = (ref: ToolbarButton | null) => { - this.innerButton = ref - } - /** * Programmatically move keyboard focus to the button element. */ public focusButton = () => { - if (this.innerButton) { - this.innerButton.focusButton() + if (this.innerButton.current) { + this.innerButton.current.focusButton() } } public render() { const className = classNames( 'toolbar-dropdown', + this.props.dropdownStyle === ToolbarDropdownStyle.MultiOption + ? 'multi-option-style' + : 'foldout-style', this.props.dropdownState, this.props.className ) @@ -383,16 +425,17 @@ export class ToolbarDropdown extends React.Component< role={this.props.role} aria-expanded={ariaExpanded} onDragOver={this.props.onDragOver} + ref={this.rootDiv} > {this.renderDropdownContents()} {this.props.children} - {this.renderDropdownArrow()} + {this.props.dropdownStyle !== ToolbarDropdownStyle.MultiOption && + this.renderDropdownArrow()} + {this.props.dropdownStyle === ToolbarDropdownStyle.MultiOption && + this.renderDropdownArrow()} ) } diff --git a/app/src/ui/toolbar/push-pull-button-dropdown.tsx b/app/src/ui/toolbar/push-pull-button-dropdown.tsx new file mode 100644 index 0000000000..49ca29ca40 --- /dev/null +++ b/app/src/ui/toolbar/push-pull-button-dropdown.tsx @@ -0,0 +1,133 @@ +import React from 'react' +import { Button } from '../lib/button' +import { Octicon, syncClockwise } from '../octicons' +import { + DropdownItem, + DropdownItemClassName, + DropdownItemType, + forcePushIcon, +} from './push-pull-button' + +interface IPushPullButtonDropDownProps { + readonly itemTypes: ReadonlyArray + /** The name of the remote. */ + readonly remoteName: string | null + + /** Will the app prompt the user to confirm a force push? */ + readonly askForConfirmationOnForcePush: boolean + + readonly fetch: () => void + readonly forcePushWithLease: () => void +} + +export class PushPullButtonDropDown extends React.Component { + private buttonsContainerRef: HTMLDivElement | null = null + + public componentDidMount() { + window.addEventListener('keydown', this.onDropdownKeyDown) + } + + public componentWillUnmount() { + window.removeEventListener('keydown', this.onDropdownKeyDown) + } + + private onButtonsContainerRef = (ref: HTMLDivElement | null) => { + this.buttonsContainerRef = ref + } + + private onDropdownKeyDown = (event: KeyboardEvent) => { + // Allow using Up and Down arrow keys to navigate the dropdown items + // (equivalent to Tab and Shift+Tab) + if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') { + return + } + + event.preventDefault() + const items = this.buttonsContainerRef?.querySelectorAll( + `.${DropdownItemClassName}` + ) + + if (items === undefined) { + return + } + + const focusedItem = + this.buttonsContainerRef?.querySelector(':focus') + if (!focusedItem) { + return + } + + const focusedIndex = Array.from(items).indexOf(focusedItem) + const nextIndex = + event.key === 'ArrowDown' ? focusedIndex + 1 : focusedIndex - 1 + // http://javascript.about.com/od/problemsolving/a/modulobug.htm + const nextItem = items[(nextIndex + items.length) % items.length] + nextItem?.focus() + } + + private getDropdownItemWithType(type: DropdownItemType): DropdownItem { + const { remoteName } = this.props + + switch (type) { + case DropdownItemType.Fetch: + return { + title: `Fetch ${remoteName}`, + description: `Fetch the latest changes from ${remoteName}`, + action: this.props.fetch, + icon: syncClockwise, + } + case DropdownItemType.ForcePush: { + const forcePushWarning = this.props + .askForConfirmationOnForcePush ? null : ( + <> +
+
+
+ Warning: A force push will + rewrite history on the remote. Any collaborators working on this + branch will need to reset their own local branch to match the + history of the remote. +
+ + ) + return { + title: `Force push ${remoteName}`, + description: ( + <> + Overwrite any changes on {remoteName} with your local changes + {forcePushWarning} + + ), + action: this.props.forcePushWithLease, + icon: forcePushIcon, + } + } + } + } + + public renderDropdownItem = (type: DropdownItemType) => { + const item = this.getDropdownItemWithType(type) + return ( + + ) + } + + public render() { + const { itemTypes } = this.props + return ( +
+ {itemTypes.map(this.renderDropdownItem)} +
+ ) + } +} diff --git a/app/src/ui/toolbar/push-pull-button.tsx b/app/src/ui/toolbar/push-pull-button.tsx index 8cbce44c3d..29bdc93394 100644 --- a/app/src/ui/toolbar/push-pull-button.tsx +++ b/app/src/ui/toolbar/push-pull-button.tsx @@ -13,6 +13,18 @@ import { RelativeTime } from '../relative-time' import { ToolbarButton, ToolbarButtonStyle } from './button' import classNames from 'classnames' +import { + DropdownState, + IToolbarDropdownProps, + ToolbarDropdown, + ToolbarDropdownStyle, +} from './dropdown' +import { FoldoutType } from '../../lib/app-state' +import { ForcePushBranchState } from '../../lib/rebase' +import { PushPullButtonDropDown } from './push-pull-button-dropdown' +import { enablePushPullFetchDropdown } from '../../lib/feature-flag' + +export const DropdownItemClassName = 'push-pull-dropdown-item' interface IPushPullButtonProps { /** @@ -52,8 +64,8 @@ interface IPushPullButtonProps { /** Is the detached HEAD state related to a rebase or not? */ readonly rebaseInProgress: boolean - /** If the current branch has been rebased, the user is permitted to force-push */ - readonly isForcePush: boolean + /** Force push state of the current branch */ + readonly forcePushBranchState: ForcePushBranchState /** Whether this component should show its onboarding tutorial nudge arrow */ readonly shouldNudge: boolean @@ -62,6 +74,32 @@ interface IPushPullButtonProps { * The number of tags that would get pushed if the user performed a push. */ readonly numTagsToPush: number + + /** Whether or not the push-pull dropdown is currently open */ + readonly isDropdownOpen: boolean + + /** Will the app prompt the user to confirm a force push? */ + readonly askForConfirmationOnForcePush: boolean + + /** + * An event handler for when the drop down is opened, or closed, by a pointer + * event or by pressing the space or enter key while focused. + * + * @param state - The new state of the drop down + */ + readonly onDropdownStateChanged: (state: DropdownState) => void +} + +export enum DropdownItemType { + Fetch = 'fetch', + ForcePush = 'force-push', +} + +export type DropdownItem = { + readonly title: string + readonly description: string | JSX.Element + readonly action: () => void + readonly icon: OcticonSymbol.OcticonSymbolType } function renderAheadBehind(aheadBehind: IAheadBehind, numTagsToPush: number) { @@ -104,165 +142,11 @@ function renderLastFetched(lastFetched: Date | null): JSX.Element | string { } } -/** The common props for all button states */ -const defaultProps = { - className: 'push-pull-button', - style: ToolbarButtonStyle.Subtitle, -} - -function progressButton(progress: Progress, networkActionInProgress: boolean) { - return ( - - ) -} - -function publishRepositoryButton(onClick: () => void) { - return ( - - ) -} - -function unbornRepositoryButton() { - return ( - - ) -} - -function detachedHeadButton(rebaseInProgress: boolean) { - const description = rebaseInProgress - ? 'Rebase in progress' - : 'Cannot publish detached HEAD' - - return ( - - ) -} - -function publishBranchButton( - isGitHub: boolean, - onClick: () => void, - shouldNudge: boolean -) { - const description = isGitHub - ? 'Publish this branch to GitHub' - : 'Publish this branch to the remote' - - const className = classNames(defaultProps.className, 'nudge-arrow', { - 'nudge-arrow-up': shouldNudge, - }) - - return ( - - ) -} - -function fetchButton( - remoteName: string, - aheadBehind: IAheadBehind, - numTagsToPush: number, - lastFetched: Date | null, - onClick: () => void -) { - const title = `Fetch ${remoteName}` - return ( - - {renderAheadBehind(aheadBehind, numTagsToPush)} - - ) -} - -function pullButton( - remoteName: string, - aheadBehind: IAheadBehind, - numTagsToPush: number, - lastFetched: Date | null, - pullWithRebase: boolean, - onClick: () => void -) { - const title = pullWithRebase - ? `Pull ${remoteName} with rebase` - : `Pull ${remoteName}` - - return ( - - {renderAheadBehind(aheadBehind, numTagsToPush)} - - ) -} - -function pushButton( - remoteName: string, - aheadBehind: IAheadBehind, - numTagsToPush: number, - lastFetched: Date | null, - onClick: () => void -) { - return ( - - {renderAheadBehind(aheadBehind, numTagsToPush)} - - ) -} - /** * This represents the "double arrow" icon used to show a force-push, and is a * less complicated icon than the generated Octicon from the `octicons` package. */ -const forcePushIcon: OcticonSymbol.OcticonSymbolType = { +export const forcePushIcon: OcticonSymbol.OcticonSymbolType = { w: 10, h: 16, d: @@ -273,51 +157,83 @@ const forcePushIcon: OcticonSymbol.OcticonSymbolType = { fr: 'evenodd', } -function forcePushButton( - remoteName: string, - aheadBehind: IAheadBehind, - numTagsToPush: number, - lastFetched: Date | null, - onClick: () => void -) { - return ( - - {renderAheadBehind(aheadBehind, numTagsToPush)} - - ) -} - /** * A button which pushes, pulls, or updates depending on the state of the * repository. */ -export class PushPullButton extends React.Component { +export class PushPullButton extends React.Component { + /** The common props for all button states */ + private defaultButtonProps() { + return { + className: 'push-pull-button', + style: ToolbarButtonStyle.Subtitle, + } + } + + /** The common props for all dropdown states */ + private defaultDropdownProps(): Omit< + IToolbarDropdownProps, + 'dropdownContentRenderer' + > { + return { + buttonClassName: 'push-pull-button', + style: ToolbarButtonStyle.Subtitle, + dropdownStyle: ToolbarDropdownStyle.MultiOption, + dropdownState: this.props.isDropdownOpen ? 'open' : 'closed', + onDropdownStateChanged: this.props.onDropdownStateChanged, + } + } + + private closeDropdown() { + this.props.dispatcher.closeFoldout(FoldoutType.PushPull) + } + private push = () => { + this.closeDropdown() this.props.dispatcher.push(this.props.repository) } private forcePushWithLease = () => { + this.closeDropdown() this.props.dispatcher.confirmOrForcePush(this.props.repository) } private pull = () => { + this.closeDropdown() this.props.dispatcher.pull(this.props.repository) } private fetch = () => { + this.closeDropdown() this.props.dispatcher.fetch( this.props.repository, FetchType.UserInitiatedTask ) } + private getDropdownContentRenderer( + itemTypes: ReadonlyArray + ) { + return () => { + return ( + + ) + } + } + public render() { + return this.renderButton() + } + + private renderButton() { const { progress, networkActionInProgress, @@ -329,28 +245,28 @@ export class PushPullButton extends React.Component { rebaseInProgress, lastFetched, pullWithRebase, - isForcePush, + forcePushBranchState, } = this.props if (progress !== null) { - return progressButton(progress, networkActionInProgress) + return this.progressButton(progress, networkActionInProgress) } if (remoteName === null) { - return publishRepositoryButton(this.push) + return this.publishRepositoryButton(this.push) } if (tipState === TipState.Unborn) { - return unbornRepositoryButton() + return this.unbornRepositoryButton() } if (tipState === TipState.Detached) { - return detachedHeadButton(rebaseInProgress) + return this.detachedHeadButton(rebaseInProgress) } if (aheadBehind === null) { const isGitHubRepository = repository.gitHubRepository !== null - return publishBranchButton( + return this.publishBranchButton( isGitHubRepository, this.push, this.props.shouldNudge @@ -360,17 +276,11 @@ export class PushPullButton extends React.Component { const { ahead, behind } = aheadBehind if (ahead === 0 && behind === 0 && numTagsToPush === 0) { - return fetchButton( - remoteName, - aheadBehind, - numTagsToPush, - lastFetched, - this.fetch - ) + return this.fetchButton(remoteName, lastFetched, this.fetch) } - if (isForcePush) { - return forcePushButton( + if (forcePushBranchState === ForcePushBranchState.Recommended) { + return this.forcePushButton( remoteName, aheadBehind, numTagsToPush, @@ -380,17 +290,18 @@ export class PushPullButton extends React.Component { } if (behind > 0) { - return pullButton( + return this.pullButton( remoteName, aheadBehind, numTagsToPush, lastFetched, pullWithRebase || false, + forcePushBranchState, this.pull ) } - return pushButton( + return this.pushButton( remoteName, aheadBehind, numTagsToPush, @@ -398,4 +309,254 @@ export class PushPullButton extends React.Component { this.push ) } + + private progressButton(progress: Progress, networkActionInProgress: boolean) { + return ( + + ) + } + + private publishRepositoryButton(onClick: () => void) { + return ( + + ) + } + + private unbornRepositoryButton() { + return ( + + ) + } + + private detachedHeadButton(rebaseInProgress: boolean) { + const description = rebaseInProgress + ? 'Rebase in progress' + : 'Cannot publish detached HEAD' + + return ( + + ) + } + + private publishBranchButton( + isGitHub: boolean, + onClick: () => void, + shouldNudge: boolean + ) { + const description = isGitHub + ? 'Publish this branch to GitHub' + : 'Publish this branch to the remote' + + if (!enablePushPullFetchDropdown()) { + const className = classNames( + this.defaultButtonProps().className, + 'nudge-arrow', + { + 'nudge-arrow-up': shouldNudge, + } + ) + + return ( + + ) + } + + const className = classNames( + this.defaultDropdownProps().className, + 'nudge-arrow', + { + 'nudge-arrow-up': shouldNudge, + } + ) + + return ( + + ) + } + + private fetchButton( + remoteName: string, + lastFetched: Date | null, + onClick: () => void + ) { + const title = `Fetch ${remoteName}` + return ( + + ) + } + + private pullButton( + remoteName: string, + aheadBehind: IAheadBehind, + numTagsToPush: number, + lastFetched: Date | null, + pullWithRebase: boolean, + forcePushBranchState: ForcePushBranchState, + onClick: () => void + ) { + const title = pullWithRebase + ? `Pull ${remoteName} with rebase` + : `Pull ${remoteName}` + + const dropdownItemTypes = [DropdownItemType.Fetch] + + if (forcePushBranchState !== ForcePushBranchState.NotAvailable) { + dropdownItemTypes.push(DropdownItemType.ForcePush) + } + + if (!enablePushPullFetchDropdown()) { + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } + + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } + + private pushButton( + remoteName: string, + aheadBehind: IAheadBehind, + numTagsToPush: number, + lastFetched: Date | null, + onClick: () => void + ) { + if (!enablePushPullFetchDropdown()) { + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } + + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } + + private forcePushButton( + remoteName: string, + aheadBehind: IAheadBehind, + numTagsToPush: number, + lastFetched: Date | null, + onClick: () => void + ) { + if (!enablePushPullFetchDropdown()) { + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } + + return ( + + {renderAheadBehind(aheadBehind, numTagsToPush)} + + ) + } } diff --git a/app/styles/_ui.scss b/app/styles/_ui.scss index 1122e15c7d..f43653c03a 100644 --- a/app/styles/_ui.scss +++ b/app/styles/_ui.scss @@ -21,6 +21,7 @@ @import 'ui/toolbar/toolbar'; @import 'ui/toolbar/button'; @import 'ui/toolbar/dropdown'; +@import 'ui/toolbar/push-pull-button'; @import 'ui/tab-bar'; @import 'ui/panel'; @import 'ui/popup'; diff --git a/app/styles/_variables.scss b/app/styles/_variables.scss index 3d5c99bca3..d423367cc7 100644 --- a/app/styles/_variables.scss +++ b/app/styles/_variables.scss @@ -248,6 +248,8 @@ $overlay-background-color: rgba(0, 0, 0, 0.4); --toolbar-button-focus-progress-color: #{$gray-700}; --toolbar-button-hover-progress-color: #{$gray-700}; --toolbar-dropdown-open-progress-color: #{$gray-200}; + --toolbar-dropdown-text-warning-color: #{$yellow-800}; + --toolbar-dropdown-text-hover-color: var(--box-hover-text-color); /** * App menu bar colors (Windows/Linux only) diff --git a/app/styles/themes/_dark.scss b/app/styles/themes/_dark.scss index ead9bf1cc0..5725534e34 100644 --- a/app/styles/themes/_dark.scss +++ b/app/styles/themes/_dark.scss @@ -174,6 +174,8 @@ body.theme-dark { --toolbar-button-focus-progress-color: #{$gray-700}; --toolbar-button-hover-progress-color: #{$gray-700}; --toolbar-dropdown-open-progress-color: #{$gray-200}; + --toolbar-dropdown-text-warning-color: #{$yellow-700}; + --toolbar-dropdown-text-hover-color: #{$white}; /** * App menu bar colors (Windows/Linux only) diff --git a/app/styles/ui/toolbar/_button.scss b/app/styles/ui/toolbar/_button.scss index 5d981d5d29..9d3bbc1df9 100644 --- a/app/styles/ui/toolbar/_button.scss +++ b/app/styles/ui/toolbar/_button.scss @@ -1,4 +1,8 @@ .toolbar-button { + display: flex; + flex-direction: row; + align-items: stretch; + // Make sure the contents shrink beyond their intrinsic width // See https://css-tricks.com/flexbox-truncated-text/ min-width: 0; @@ -11,6 +15,11 @@ // above all the other content. position: relative; + .toolbar-dropdown-button { + width: 40px; + height: 49px; + } + // General button behavior, mostly resets. // For the button content styling see second button style. Note that we // explicitly use > here to only target the direct descendant button since diff --git a/app/styles/ui/toolbar/_dropdown.scss b/app/styles/ui/toolbar/_dropdown.scss index 85dcbceb31..ba9ab5c614 100644 --- a/app/styles/ui/toolbar/_dropdown.scss +++ b/app/styles/ui/toolbar/_dropdown.scss @@ -3,13 +3,21 @@ // See https://css-tricks.com/flexbox-truncated-text/ min-width: 0; + display: flex; + flex-direction: row; + & > .toolbar-button { width: 100%; height: 100%; } + & .toolbar-dropdown-arrow-button { + width: 39px; + } + &.open { - & > .toolbar-button > button { + &.foldout-style > .toolbar-button > button, + &.multi-option-style > .toolbar-dropdown-arrow-button > button { color: var(--toolbar-button-active-color); background-color: var(--toolbar-button-active-background-color); diff --git a/app/styles/ui/toolbar/_push-pull-button.scss b/app/styles/ui/toolbar/_push-pull-button.scss new file mode 100644 index 0000000000..64d9f6ccee --- /dev/null +++ b/app/styles/ui/toolbar/_push-pull-button.scss @@ -0,0 +1,66 @@ +.push-pull-dropdown { + display: flex; + flex-direction: column; + margin-top: 1px; + z-index: 0; + max-width: 100%; + + .push-pull-dropdown-item { + display: flex; + flex-direction: row; + height: fit-content; + padding: 10px; + gap: 10px; + color: var(--text-color); + background-color: var(--box-background-color); + white-space: normal; + + // Unset styles from Button component + text-align: unset; + border: unset; + border-radius: unset; + box-shadow: unset !important; + + .octicon { + height: 16px; + width: 16px; + } + + // Override background on focus to keep the default color + &:focus { + background-color: var(--box-background-color); + } + + &:hover { + background-color: var(--box-hover-background-color) !important; + color: var(--toolbar-dropdown-text-hover-color) !important; + } + + &:not(:last-child) { + // Enforce this bottom border style even in focused state + border-bottom: 1px solid var(--box-border-color) !important; + } + + .text-container { + display: flex; + flex-direction: column; + row-gap: 3px; + + .title { + font-weight: 600; + } + + .detail { + color: var(--text-secondary-color); + + .warning { + color: var(--toolbar-dropdown-text-warning-color); + + .warning-title { + font-weight: 600; + } + } + } + } + } +} diff --git a/app/styles/ui/toolbar/_toolbar.scss b/app/styles/ui/toolbar/_toolbar.scss index 454df483a0..c4ef988a71 100644 --- a/app/styles/ui/toolbar/_toolbar.scss +++ b/app/styles/ui/toolbar/_toolbar.scss @@ -37,16 +37,16 @@ } } - .toolbar-dropdown { - &.branch-button { + .toolbar-button { + &.branch-toolbar-button { width: 230px; } - } - - .toolbar-button { &.revert-progress { width: 230px; } + &.toolbar-dropdown-arrow-button { + width: 39px; + } } } diff --git a/app/styles/ui/window/_tooltips.scss b/app/styles/ui/window/_tooltips.scss index 27f84b7439..ca924e6d91 100644 --- a/app/styles/ui/window/_tooltips.scss +++ b/app/styles/ui/window/_tooltips.scss @@ -193,6 +193,10 @@ body > .tooltip, color: var(--color-deleted); } + .files-renamed-icon { + color: var(--color-renamed); + } + .octicon { margin-right: var(--spacing-third); vertical-align: bottom; // For some reason, `bottom` places the text in the middle diff --git a/changelog.json b/changelog.json index 7bc754c45c..b8b2b8aec2 100644 --- a/changelog.json +++ b/changelog.json @@ -3,6 +3,18 @@ "3.1.6": [ "[Improved] Upgrade embedded Git to 2.39.1 and Git LFS to 3.3.0 - #15915" ], + "3.1.6-beta2": [ + "[Fixed] Fix crash launching the app on Apple silicon devices - #16011", + "[Fixed] Trim leading and trailing whitespaces in URLs of repository remotes - #15821. Thanks @Shivareddy-Aluri!", + "[Fixed] Fix support for the latest versions of RStudio on Windows - #15810" + ], + "3.1.6-beta1": [ + "[Added] Add fetch and force-push actions in a dropdown as an alternative to the main Pull/Push/Publish action button - #15907", + "[Added] Add JetBrains CLion support on macOS - #15881. Thanks @tsvetilian-ty!", + "[Fixed] Fix support for latest versions of VSCodium on Windows - #15585. Thanks @voidei!", + "[Improved] Upgrade to Electron v22.0.3 - #15831", + "[Improved] Upgrade embedded Git to 2.39.1 and Git LFS to 3.3.0 - #15915" + ], "3.1.5": [ "[Added] Enable menu option to Force-push branches that have diverged - #15211", "[Added] Add menu option to Fetch the current repository at any time - #7805", @@ -28,6 +40,7 @@ "[Improved] Close repository list after creating or adding repositories - #15508. Thanks @angusdev!", "[Improved] Always show an error message when an update fails - #15530" ], + "3.1.4": ["[Improved] Upgrade embedded Git to 2.35.6"], "3.1.4-beta1": [ "[Added] Add support for JetBrains Toolbox and JetBrains Fleet editor for Windows - #12912. Thanks @tsvetilian-ty!", "[Added] Add support for Emacs editor for Linux - #15857. Thanks @zipperer!", diff --git a/docs/technical/editor-integration.md b/docs/technical/editor-integration.md index 5b520648ba..6cdb0386d4 100644 --- a/docs/technical/editor-integration.md +++ b/docs/technical/editor-integration.md @@ -252,6 +252,7 @@ These editors are currently supported: - [JetBrains PhpStorm](https://www.jetbrains.com/phpstorm/) - [JetBrains PyCharm](https://www.jetbrains.com/pycharm/) - [JetBrains RubyMine](https://www.jetbrains.com/rubymine/) + - [JetBrains CLion](https://www.jetbrains.com/clion/) - [RStudio](https://rstudio.com/) - [TextMate](https://macromates.com) - [Brackets](http://brackets.io/) diff --git a/package.json b/package.json index 7c345c3e80..e5ac739268 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "eslint": "eslint --cache --rulesdir ./eslint-rules \"./eslint-rules/**/*.js\" \"./script/**/*.ts{,x}\" \"./app/{src,typings,test}/**/*.{j,t}s{,x}\" \"./changelog.json\"", "eslint-check": "eslint --print-config .eslintrc.* | eslint-config-prettier-check", "publish": "ts-node -P script/tsconfig.json script/publish.ts", + "validate-electron-version": "ts-node -P script/tsconfig.json script/validate-electron-version.ts", "clean-slate": "rimraf out node_modules app/node_modules && yarn", "rebuild-hard:dev": "yarn clean-slate && yarn build:dev", "rebuild-hard:prod": "yarn clean-slate && yarn build:prod", @@ -84,7 +85,7 @@ "jest-diff": "^25.0.0", "jest-extended": "^0.11.2", "jest-localstorage-mock": "^2.3.0", - "jszip": "^3.7.1", + "jszip": "^3.8.0", "klaw-sync": "^3.0.0", "legal-eagle": "0.16.0", "mini-css-extract-plugin": "^2.5.3", @@ -155,7 +156,7 @@ "@types/webpack-hot-middleware": "^2.25.6", "@types/webpack-merge": "^5.0.0", "@types/xml2js": "^0.4.0", - "electron": "19.0.0", + "electron": "22.0.3", "electron-builder": "^22.7.0", "electron-packager": "^15.1.0", "electron-winstaller": "^5.0.0", diff --git a/script/build.ts b/script/build.ts index 34ae530b78..075cb99354 100755 --- a/script/build.ts +++ b/script/build.ts @@ -199,7 +199,7 @@ function packageApp() { new RegExp('/\\.git($|/)'), new RegExp('/node_modules/\\.bin($|/)'), ], - appCopyright: 'Copyright © 2017 GitHub, Inc.', + appCopyright: 'Copyright © 2023 GitHub, Inc.', // macOS appBundleId: getBundleID(), diff --git a/script/entitlements-dev.plist b/script/entitlements-dev.plist index 374fef149d..985c9eba95 100644 --- a/script/entitlements-dev.plist +++ b/script/entitlements-dev.plist @@ -8,5 +8,7 @@ com.apple.security.cs.disable-library-validation + com.apple.security.cs.allow-jit + diff --git a/script/entitlements.plist b/script/entitlements.plist index 80e73892e6..7d3263efd9 100644 --- a/script/entitlements.plist +++ b/script/entitlements.plist @@ -6,5 +6,7 @@ com.apple.security.automation.apple-events + com.apple.security.cs.allow-jit + diff --git a/script/validate-electron-version.ts b/script/validate-electron-version.ts new file mode 100644 index 0000000000..18494e0c8f --- /dev/null +++ b/script/validate-electron-version.ts @@ -0,0 +1,53 @@ +/* eslint-disable no-sync */ +/// + +import * as distInfo from './dist-info' + +type ChannelToValidate = 'production' | 'beta' + +/** + * This object states the valid/expected Electron versions for each publishable + * channel of GitHub Desktop. + * + * The purpose of this is to ensure that we don't accidentally publish a + * production/beta/test build with the wrong version of Electron, which could + * cause really bad regressions to our users, and also the inability to go back + * to a previous version of GitHub Desktop without losing all settings. + */ +const ValidElectronVersions: Record = { + production: '19.0.0', + beta: '22.0.3', +} + +const channel = getChannelToValidate() + +if (channel === null) { + console.log( + `No need to validate the Electron version of a ${distInfo.getChannel()} build.` + ) + process.exit(0) +} + +const expectedVersion = ValidElectronVersions[channel] +const pkg: Package = require('../package.json') +const actualVersion = pkg.devDependencies?.electron + +if (actualVersion !== expectedVersion) { + console.error( + `The Electron version for the ${channel} channel is incorrect. Expected ${expectedVersion} but found ${actualVersion}.` + ) + process.exit(1) +} + +console.log( + `The Electron version for the ${channel} channel is correct: ${actualVersion}.` +) + +function getChannelToValidate(): ChannelToValidate | null { + const channel = distInfo.getChannel() + return isChannelToValidate(channel) ? channel : null +} + +function isChannelToValidate(channel: string): channel is ChannelToValidate { + return Object.keys(ValidElectronVersions).includes(channel) +} diff --git a/yarn.lock b/yarn.lock index fa2dcf36f3..2e46820054 100644 --- a/yarn.lock +++ b/yarn.lock @@ -521,22 +521,6 @@ ajv "^6.12.0" ajv-keywords "^3.4.1" -"@electron/get@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40" - integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - global-tunnel-ng "^2.7.1" - "@electron/get@^1.6.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.1.tgz#41aa60871b9d7e225bbe68135012f88a9ef87012" @@ -552,6 +536,21 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@electron/get@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.2.tgz#ae2a967b22075e9c25aaf00d5941cd79c21efd7e" + integrity sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g== + dependencies: + debug "^4.1.1" + env-paths "^2.2.0" + fs-extra "^8.1.0" + got "^11.8.5" + progress "^2.0.3" + semver "^6.2.0" + sumchecker "^3.0.1" + optionalDependencies: + global-agent "^3.0.0" + "@es-joy/jsdoccomment@~0.18.0": version "0.18.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.18.0.tgz#2532b2ecb8576d694011b157c447ed6b12534c70" @@ -879,6 +878,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" @@ -900,6 +904,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -953,6 +964,16 @@ dependencies: "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + "@types/classnames@^2.2.2": version "2.2.3" resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.3.tgz#3f0ff6873da793870e20a260cada55982f38a9e5" @@ -1108,6 +1129,11 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -1173,6 +1199,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + "@types/klaw-sync@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@types/klaw-sync/-/klaw-sync-6.0.0.tgz#ff0b36601efaaa109d513c4ced109311fd06ba36" @@ -1339,6 +1372,13 @@ resolved "https://registry.yarnpkg.com/@types/reserved-words/-/reserved-words-0.1.0.tgz#a9d5318bb4ac4466862b93fc702542b75d2cd3ac" integrity sha512-ls6lSkkhEFm8XSVQjHj47pJoCL9sVK91mwIONw0Iwjqkmy98ForMFYa5+/vb6sytTaK0HSwkzKKYzREPTUhhhg== +"@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" @@ -2663,6 +2703,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -2676,6 +2721,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -2979,16 +3037,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - config-chain@^1.1.11: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -3246,6 +3294,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -3271,6 +3326,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -3603,14 +3663,14 @@ electron-winstaller@*, electron-winstaller@^5.0.0: lodash.template "^4.2.2" temp "^0.9.0" -electron@19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-19.0.0.tgz#f6b742b708ec118676ba3b38d0f3712d8f0311cf" - integrity sha512-VXwqLQxuIUr0SI8vOYDj5OLPwtKa/trn5DVKd/BFGT/U/IerfVoSZuydGLOjSL5yJlckfmKQpiq+8PW4gI8hXA== +electron@22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-22.0.3.tgz#44806cd053ea2ed35bffefd92143d3fc69d7337d" + integrity sha512-eETrJTINTzlXgQrnJSrKiF2Xdt5EHpxZ6Kk+WUjFCE0zUztdVm+hrngUecqhj8TPFlYScTANzPwRwUIjOChl+g== dependencies: - "@electron/get" "^1.14.1" + "@electron/get" "^2.0.0" "@types/node" "^16.11.26" - extract-zip "^1.0.3" + extract-zip "^2.0.1" element-closest@^2.0.2: version "2.0.2" @@ -4371,17 +4431,7 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -extract-zip@^2.0.0: +extract-zip@^2.0.0, extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -4923,6 +4973,23 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -5178,9 +5245,9 @@ htmlparser2@^6.1.0: entities "^2.0.0" http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@1.8.1: version "1.8.1" @@ -5211,6 +5278,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -6353,6 +6428,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-edm-parser@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/json-edm-parser/-/json-edm-parser-0.1.2.tgz#1e60b0fef1bc0af67bc0d146dfdde5486cd615b4" @@ -6478,10 +6558,10 @@ jsx-ast-utils@^3.3.2: array-includes "^3.1.5" object.assign "^4.1.3" -jszip@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9" - integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== +jszip@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.8.0.tgz#a2ac3c33fe96a76489765168213655850254d51b" + integrity sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -6500,6 +6580,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== + dependencies: + json-buffer "3.0.1" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -7024,6 +7111,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + mini-css-extract-plugin@^2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz#c5c79f9b22ce9b4f164e9492267358dbe35376d9" @@ -7082,13 +7174,6 @@ mkdirp@^0.5.1: dependencies: minimist "0.0.8" -mkdirp@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - mrmime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" @@ -7238,6 +7323,11 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-conf@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" @@ -7483,6 +7573,11 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" @@ -7980,6 +8075,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" @@ -8112,19 +8212,6 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.2.2, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readable-stream@^3.0.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -8146,6 +8233,19 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" +readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" @@ -8323,6 +8423,11 @@ reserved-words@^0.1.2: resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -8391,6 +8496,13 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -9621,11 +9733,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - typescript@^4.6.4: version "4.6.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"