Repository: felixse/FluentTerminal Branch: master Commit: ba83ec485eea Files: 406 Total size: 2.8 MB Directory structure: gitextract_0v3c3gm7/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── Explorer Context Menu Integration/ │ ├── Install.bat │ ├── Install_with_icon.bat │ ├── README.md │ └── Uninstall.bat ├── FluentTerminal.App/ │ ├── Actions/ │ │ └── FocusAction.cs │ ├── Adapters/ │ │ ├── AppServiceConnectionAdapter.cs │ │ ├── ApplicationViewAdapter.cs │ │ └── MessageDialogAdapter.cs │ ├── App.xaml │ ├── App.xaml.cs │ ├── Behaviors/ │ │ └── MiddleClickBehavior.cs │ ├── CommandLineArguments/ │ │ ├── NewVerb.cs │ │ ├── RunVerb.cs │ │ ├── SettingsVerb.cs │ │ └── Target.cs │ ├── Converters/ │ │ ├── BackgroundToApplicationThemeConverter.cs │ │ ├── BooleanNegationConverter.cs │ │ ├── ColorResourceKeyFallbackConverter.cs │ │ ├── EnumValueToVisibiltyConverter.cs │ │ ├── FalseToVisibleConverter.cs │ │ ├── I18NConverter.cs │ │ ├── IconConverter.cs │ │ ├── IntToExtendedVirtualKeyConverter.cs │ │ ├── IntToVisibilityConverter.cs │ │ ├── MenuItemViewModelBaseToMenuFlayoutItemBaseConverter.cs │ │ ├── MenuViewModelToFlyoutMenuConverter.cs │ │ ├── NegateConverter.cs │ │ ├── NullToCollapsedConverter.cs │ │ ├── StringToColorConverter.cs │ │ ├── TabColorFallbackConverter.cs │ │ ├── TabThemeSelectedConverter.cs │ │ ├── TerminalViewModelToViewConverter.cs │ │ ├── TextMiddleEllipsisConverter.cs │ │ ├── ToolTipValueToPixelConverter.cs │ │ └── TrueToVisibleConverter.cs │ ├── Dialogs/ │ │ ├── AboutDialog.xaml │ │ ├── AboutDialog.xaml.cs │ │ ├── CreateKeyBindingDialog.xaml │ │ ├── CreateKeyBindingDialog.xaml.cs │ │ ├── CustomCommandDialog.xaml │ │ ├── CustomCommandDialog.xaml.cs │ │ ├── InputDialog.xaml │ │ ├── InputDialog.xaml.cs │ │ ├── SshInfoDialog.xaml │ │ └── SshInfoDialog.xaml.cs │ ├── FluentTerminal.App.csproj │ ├── Package.appxmanifest │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── Default.rd.xml │ ├── Services/ │ │ ├── ApplicationLanguageService.cs │ │ ├── ClipboardService.cs │ │ ├── CommandHistoryService.cs │ │ ├── FileSystemService.cs │ │ ├── ImageFileSystemService.cs │ │ ├── StartupTaskService.cs │ │ └── SystemFontService.cs │ ├── Strings/ │ │ ├── ar/ │ │ │ └── Resources.resw │ │ ├── ar-iq/ │ │ │ └── Resources.resw │ │ ├── az-Latn/ │ │ │ └── Resources.resw │ │ ├── bg/ │ │ │ └── Resources.resw │ │ ├── bs/ │ │ │ └── Resources.resw │ │ ├── de/ │ │ │ └── Resources.resw │ │ ├── en-US/ │ │ │ └── Resources.resw │ │ ├── eo/ │ │ │ └── Resources.resw │ │ ├── es/ │ │ │ └── Resources.resw │ │ ├── fa/ │ │ │ └── Resources.resw │ │ ├── fr/ │ │ │ └── Resources.resw │ │ ├── he/ │ │ │ └── Resources.resw │ │ ├── hi/ │ │ │ └── Resources.resw │ │ ├── hr/ │ │ │ └── Resources.resw │ │ ├── hu/ │ │ │ └── Resources.resw │ │ ├── id/ │ │ │ └── Resources.resw │ │ ├── it/ │ │ │ └── Resources.resw │ │ ├── ja/ │ │ │ └── Resources.resw │ │ ├── ko/ │ │ │ └── Resources.resw │ │ ├── nl/ │ │ │ └── Resources.resw │ │ ├── pl/ │ │ │ └── Resources.resw │ │ ├── pt/ │ │ │ └── Resources.resw │ │ ├── pt-BR/ │ │ │ └── Resources.resw │ │ ├── ro/ │ │ │ └── Resources.resw │ │ ├── ru/ │ │ │ └── Resources.resw │ │ ├── sl/ │ │ │ └── Resources.resw │ │ ├── sq/ │ │ │ └── Resources.resw │ │ ├── sr-Latn/ │ │ │ └── Resources.resw │ │ ├── sr-cyrl/ │ │ │ └── Resources.resw │ │ ├── sv/ │ │ │ └── Resources.resw │ │ ├── tr/ │ │ │ └── Resources.resw │ │ ├── ug-Arab/ │ │ │ └── Resources.resw │ │ ├── uk/ │ │ │ └── Resources.resw │ │ ├── uz-Latn/ │ │ │ └── Resources.resw │ │ ├── vi/ │ │ │ └── Resources.resw │ │ ├── zh-Hans/ │ │ │ └── Resources.resw │ │ └── zh-Hant/ │ │ └── Resources.resw │ ├── Styles/ │ │ └── Custom.xaml │ ├── Utilities/ │ │ ├── ColorExtensions.cs │ │ ├── ContrastHelper.cs │ │ ├── DelayedAction.cs │ │ ├── DispatcherExtensions.cs │ │ ├── InteractiveSurface.cs │ │ └── JumpListHelper.cs │ ├── ViewModels/ │ │ ├── CommandItemViewModel.cs │ │ ├── CommandProfileProviderViewModel.cs │ │ ├── DelayedHistorySaver.cs │ │ ├── EnvironmentVariableViewModel.cs │ │ ├── ISessionSuccessTracker.cs │ │ ├── ITerminalView.cs │ │ ├── Infrastructure/ │ │ │ └── IErrorHandler.cs │ │ ├── MainViewModel.cs │ │ ├── Menu/ │ │ │ ├── ExpandableMenuItemViewModel.cs │ │ │ ├── Mdl2Icon.cs │ │ │ ├── MenuItemKeyBindingViewModel.cs │ │ │ ├── MenuItemViewModel.cs │ │ │ ├── MenuItemViewModelBase.cs │ │ │ ├── MenuViewModel.cs │ │ │ ├── RadioMenuItemViewModel.cs │ │ │ ├── SeparatorMenuItemViewModel.cs │ │ │ └── ToggleMenuItemViewModel.cs │ │ ├── OverlayViewModel.cs │ │ ├── ProfileViewModelBase.cs │ │ ├── Profiles/ │ │ │ ├── CommonProfileProviderViewModel.cs │ │ │ ├── ProfileProviderViewModelBase.cs │ │ │ ├── SshConnectViewModel.cs │ │ │ └── SshConnectionInfoValidationResult.cs │ │ ├── Settings/ │ │ │ ├── AboutPageViewModel.cs │ │ │ ├── GeneralPageViewModel.cs │ │ │ ├── KeyBindingViewModel.cs │ │ │ ├── KeyBindingsPageViewModel.cs │ │ │ ├── KeyBindingsViewModel.cs │ │ │ ├── MousePageViewModel.cs │ │ │ ├── ProfilesPageViewModel.cs │ │ │ ├── SshProfilesPageViewModel.cs │ │ │ ├── TerminalPageViewModel.cs │ │ │ └── ThemesPageViewModel.cs │ │ ├── SettingsViewModel.cs │ │ ├── ShellProfileViewModel.cs │ │ ├── SshProfileViewModel.cs │ │ ├── TabThemeViewModel.cs │ │ ├── TerminalViewModel.cs │ │ ├── ThemeViewModel.cs │ │ └── Utilities/ │ │ ├── TaskUtilities.cs │ │ └── WebViewSpecialCharEncoder.cs │ └── Views/ │ ├── BooleanTemplateSelector.cs │ ├── EnvironmentVariablesView.xaml │ ├── EnvironmentVariablesView.xaml.cs │ ├── KeyBindingsView.xaml │ ├── KeyBindingsView.xaml.cs │ ├── MainPage.xaml │ ├── MainPage.xaml.cs │ ├── MenuExtension.cs │ ├── NoValueTemplateSelector.cs │ ├── OverlayView.xaml │ ├── OverlayView.xaml.cs │ ├── SettingsPage.xaml │ ├── SettingsPage.xaml.cs │ ├── SettingsPages/ │ │ ├── GeneralSettings.xaml │ │ ├── GeneralSettings.xaml.cs │ │ ├── KeyBindingSettings.xaml │ │ ├── KeyBindingSettings.xaml.cs │ │ ├── MouseSettings.xaml │ │ ├── MouseSettings.xaml.cs │ │ ├── ShellProfileSettings.xaml │ │ ├── ShellProfileSettings.xaml.cs │ │ ├── SshProfileSettings.xaml │ │ ├── SshProfileSettings.xaml.cs │ │ ├── TerminalSettings.xaml │ │ ├── TerminalSettings.xaml.cs │ │ ├── ThemeSettings.xaml │ │ └── ThemeSettings.xaml.cs │ ├── TabBar.xaml │ ├── TabBar.xaml.cs │ ├── TabBarBackgroundBindingHelper.cs │ ├── TabThemeTemplateSelector.cs │ ├── TemplateSelectors.xaml │ ├── TemplateSelectors.xaml.cs │ ├── TerminalColorPicker.xaml │ ├── TerminalColorPicker.xaml.cs │ ├── TerminalKeybindTemplateSelector.cs │ ├── TerminalThemeTemplateSelector.cs │ ├── TerminalView.xaml │ ├── TerminalView.xaml.cs │ ├── XtermTerminalView.xaml │ └── XtermTerminalView.xaml.cs ├── FluentTerminal.App.Services/ │ ├── Adapters/ │ │ └── ApplicationDataContainerAdapter.cs │ ├── ApplicationDataContainers.cs │ ├── Constants.cs │ ├── Dialogs/ │ │ ├── IAboutDialog.cs │ │ ├── ICreateKeyBindingDialog.cs │ │ ├── ICustomCommandDialog.cs │ │ ├── IInputDialog.cs │ │ ├── IMessageDialog.cs │ │ └── ISshConnectionInfoDialog.cs │ ├── EventArgs/ │ │ ├── CancelableEventArgs.cs │ │ ├── NewTabRequestedEventArgs.cs │ │ └── NewWindowRequestedEventArgs.cs │ ├── Exceptions/ │ │ ├── ParseThemeException.cs │ │ ├── ReadTextFileException.cs │ │ └── SaveTextFileException.cs │ ├── FluentTerminal.App.Services.csproj │ ├── IAppServiceConnection.cs │ ├── IApplicationDataContainer.cs │ ├── IApplicationLanguageService.cs │ ├── IApplicationView.cs │ ├── IClipboardService.cs │ ├── ICommandHistoryService.cs │ ├── IDefaultValueProvider.cs │ ├── IDialogService.cs │ ├── IFileSystemService.cs │ ├── IImageFileSystemService.cs │ ├── IKeyboardCommandService.cs │ ├── INotificationService.cs │ ├── ISettingsService.cs │ ├── IShellProfileMigrationService.cs │ ├── IStartupTaskService.cs │ ├── ISystemFontService.cs │ ├── IThemeParser.cs │ ├── IThemeParserFactory.cs │ ├── ITrayProcessCommunicationService.cs │ ├── IUpdateService.cs │ ├── Implementation/ │ │ ├── DefaultValueProvider.cs │ │ ├── DialogService.cs │ │ ├── FluentTerminalThemeParser.cs │ │ ├── ITermThemeParser.cs │ │ ├── KeyboardCommandService.cs │ │ ├── MoshBackwardCompatibility.cs │ │ ├── NotificationService.cs │ │ ├── SettingsService.cs │ │ ├── ShellProfileMigrationService.cs │ │ ├── ThemeParserFactory.cs │ │ ├── TrayProcessCommunicationService.cs │ │ └── UpdateService.cs │ ├── Logger.cs │ ├── Terminal.cs │ └── Utilities/ │ ├── EnumHelper.cs │ ├── I18N.cs │ ├── PreserveDictionaryKeyCaseContractResolver.cs │ └── TerminalThemeContractResolver.cs ├── FluentTerminal.App.Services.Test/ │ ├── DefaultValueProviderTests.cs │ ├── DialogServiceTests.cs │ ├── FluentTerminal.App.Services.Test.csproj │ ├── FluentTerminalThemeParserTests.cs │ ├── ITermThemeParserTests.cs │ ├── KeyboardCommandServiceTests.cs │ ├── SettingsServiceTests.cs │ ├── TestData/ │ │ └── AdventureTime.itermcolors │ ├── ThemeParserFactoryTests.cs │ └── TrayProcessCommunicationServiceTests.cs ├── FluentTerminal.App.ViewModels/ │ ├── DelayedHistorySaver.cs │ ├── EnvironmentVariableViewModel.cs │ ├── FluentTerminal.App.ViewModels.csproj │ ├── ISessionSuccessTracker.cs │ ├── ITerminalView.cs │ ├── Infrastructure/ │ │ └── IErrorHandler.cs │ ├── MainViewModel.cs │ ├── Menu/ │ │ ├── ExpandableMenuItemViewModel.cs │ │ ├── Mdl2Icon.cs │ │ ├── MenuItemKeyBindingViewModel.cs │ │ ├── MenuItemViewModel.cs │ │ ├── MenuItemViewModelBase.cs │ │ ├── MenuViewModel.cs │ │ ├── RadioMenuItemViewModel.cs │ │ ├── SeparatorMenuItemViewModel.cs │ │ └── ToggleMenuItemViewModel.cs │ ├── OverlayViewModel.cs │ ├── ProfileViewModelBase.cs │ ├── Profiles/ │ │ ├── CommonProfileProviderViewModel.cs │ │ ├── ProfileProviderViewModelBase.cs │ │ ├── SshConnectViewModel.cs │ │ └── SshConnectionInfoValidationResult.cs │ ├── Settings/ │ │ ├── AboutPageViewModel.cs │ │ ├── GeneralPageViewModel.cs │ │ ├── KeyBindingViewModel.cs │ │ ├── KeyBindingsPageViewModel.cs │ │ ├── KeyBindingsViewModel.cs │ │ ├── MousePageViewModel.cs │ │ ├── ProfilesPageViewModel.cs │ │ ├── SshProfilesPageViewModel.cs │ │ ├── TerminalPageViewModel.cs │ │ └── ThemesPageViewModel.cs │ ├── SettingsViewModel.cs │ ├── ShellProfileViewModel.cs │ ├── SshProfileViewModel.cs │ ├── TabThemeViewModel.cs │ ├── TerminalViewModel.cs │ ├── ThemeViewModel.cs │ └── Utilities/ │ ├── TaskUtilities.cs │ └── WebViewSpecialCharEncoder.cs ├── FluentTerminal.Client/ │ ├── .gitignore │ ├── package.json │ ├── src/ │ │ ├── index.html │ │ ├── index.ts │ │ └── style.css │ ├── tsconfig.json │ └── webpack.config.js ├── FluentTerminal.Models/ │ ├── ApplicationSettings.cs │ ├── Enums/ │ │ ├── BellStyle.cs │ │ ├── Command.cs │ │ ├── CursorStyle.cs │ │ ├── ExtendedVirtualKey.cs │ │ ├── InactiveTabColorMode.cs │ │ ├── MouseAction.cs │ │ ├── NewTerminalLocation.cs │ │ ├── ScrollBarStyle.cs │ │ ├── SessionType.cs │ │ ├── StartupTaskStatus.cs │ │ ├── TabThemeKey.cs │ │ └── TabsPosition.cs │ ├── ExecutedCommand.cs │ ├── ExportedTerminalTheme.cs │ ├── File.cs │ ├── FluentTerminal.Models.csproj │ ├── IMessage.cs │ ├── ImageFile.cs │ ├── KeyBinding.cs │ ├── MessageIdentifiers.cs │ ├── MessageKeys.cs │ ├── Messages/ │ │ ├── ApplicationSettingsChangedMessage.cs │ │ ├── CommandHistoryChangedMessage.cs │ │ ├── CurrentThemeChangedMessage.cs │ │ ├── DefaultShellProfileChangedMessage.cs │ │ ├── KeyBindingsChangedMessage.cs │ │ ├── ShellProfileAddedMessage.cs │ │ ├── ShellProfileChangedMessage.cs │ │ ├── ShellProfileDeletedMessage.cs │ │ ├── TerminalOptionsChangedMessage.cs │ │ ├── ThemeAddedMessage.cs │ │ └── ThemeDeletedMessage.cs │ ├── Requests/ │ │ ├── CheckFileExistsRequest.cs │ │ ├── CreateTerminalRequest.cs │ │ ├── GetCommandPathRequest.cs │ │ ├── GetSshConfigFolderRequest.cs │ │ ├── GetUserNameRequest.cs │ │ ├── MuteTerminalRequest.cs │ │ ├── PauseTerminalOutputRequest.cs │ │ ├── QuitApplicationRequest.cs │ │ ├── ReadTextFileRequest.cs │ │ ├── ResizeTerminalRequest.cs │ │ ├── SaveTextFileRequest.cs │ │ ├── SetToggleWindowKeyBindingsRequest.cs │ │ ├── TerminalExitedRequest.cs │ │ └── UpdateSettingsRequest.cs │ ├── Responses/ │ │ ├── CommonResponse.cs │ │ ├── CreateTerminalResponse.cs │ │ ├── GetSshConfigFolderResponse.cs │ │ ├── PauseTerminalOutputResponse.cs │ │ ├── StringValueResponse.cs │ │ └── TerminalResponse.cs │ ├── SearchRequest.cs │ ├── ShellProfile.cs │ ├── SshProfile.cs │ ├── StringExtensions.cs │ ├── TabTheme.cs │ ├── TerminalColors.cs │ ├── TerminalExitStatus.cs │ ├── TerminalOptions.cs │ ├── TerminalOutput.cs │ ├── TerminalSize.cs │ └── TerminalTheme.cs ├── FluentTerminal.Package/ │ ├── FluentTerminal.Package.wapproj │ ├── Package.appxmanifest │ ├── priconfig.default.xml │ └── priconfig.packaging.xml ├── FluentTerminal.RuntimeComponent/ │ ├── Enums/ │ │ └── MouseButton.cs │ ├── FluentTerminal.RuntimeComponent.csproj │ ├── Interfaces/ │ │ └── IxtermEventListener.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ └── WebAllowedObjects/ │ └── TerminalBridge.cs ├── FluentTerminal.SystemTray/ │ ├── App.config │ ├── BufferedReader.cs │ ├── FileFinder.cs │ ├── FluentTerminal.SystemTray.csproj │ ├── Native/ │ │ ├── ProcessApi.cs │ │ └── WindowApi.cs │ ├── ProcessUtils.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Services/ │ │ ├── AppCommunicationService.cs │ │ ├── ConPty/ │ │ │ ├── ConPtySession.cs │ │ │ ├── Native/ │ │ │ │ ├── ConsoleApi.cs │ │ │ │ ├── ProcessApi.cs │ │ │ │ └── PseudoConsoleApi.cs │ │ │ ├── Processes/ │ │ │ │ ├── Process.cs │ │ │ │ └── ProcessFactory.cs │ │ │ ├── PseudoConsole.cs │ │ │ ├── PseudoConsolePipe.cs │ │ │ └── Terminal.cs │ │ ├── ITerminalSession.cs │ │ ├── TerminalsManager.cs │ │ ├── ToggleWindowService.cs │ │ └── WinPty/ │ │ └── WinPtySession.cs │ ├── SystemTrayApplicationContext.cs │ ├── Utilities.cs │ ├── VolumeControl.cs │ └── app.manifest ├── FluentTerminal.sln ├── LICENSE ├── MOSH_README.md ├── README.md ├── UpdateTranslations.ps1 └── nuget.config ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Git will handle the files in whatever way it thinks is best. # Overwrites global core.autocrlf git setting * text=auto ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: [push] jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@master - name: Build client working-directory: ./FluentTerminal.Client run: | yarn yarn build - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.0.2 - name: MSBuild run: msbuild FluentTerminal.sln -t:build -p:Configuration=Debug -p:Platform=x64 -m -restore ================================================ FILE: .gitignore ================================================ # Project specific FluentTerminal.App/Win32 FluentTerminal.App/Client/ FluentTerminal.Client/dist /.sonarqube ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Typescript v1 declaration files typings/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs !FluentTerminal.App/MoshExecutables/* ================================================ FILE: Explorer Context Menu Integration/Install.bat ================================================ reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f ================================================ FILE: Explorer Context Menu Integration/Install_with_icon.bat ================================================ copy /y FluentTerminal.ico "%LOCALAPPDATA%\Microsoft\WindowsApps" reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f ================================================ FILE: Explorer Context Menu Integration/README.md ================================================ If you want a an icon for the context menu entries, download the .ico file and put in the same directory as the Install_with_icon.bat file then run it. The same Uninstall.bat works for both Install.bat files. ================================================ FILE: Explorer Context Menu Integration/Uninstall.bat ================================================ reg delete "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here" /f reg delete "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here" /f reg delete "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here" /f reg delete "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here" /f del %LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico ================================================ FILE: FluentTerminal.App/Actions/FocusAction.cs ================================================ using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace FluentTerminal.App.Actions { public class FocusAction : DependencyObject, IAction { public object Execute(object sender, object parameter) { var control = TargetObject ?? sender as Control; if (control != null) { if (!control.IsLoaded) control.Loaded += Control_Loaded; else control.Focus(FocusState.Programmatic); } return null; } private void Control_Loaded(object sender, RoutedEventArgs e) { Control control = sender as Control; control.Focus(FocusState.Programmatic); control.Loaded -= Control_Loaded; // won't be needed anymore. Remove reference just in case } public Control TargetObject { get { return (Control)GetValue(TargetObjectProperty); } set { SetValue(TargetObjectProperty, value); } } public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register(nameof(TargetObject), typeof(Control), typeof(FocusAction), new PropertyMetadata(null)); } } ================================================ FILE: FluentTerminal.App/Adapters/AppServiceConnectionAdapter.cs ================================================ using FluentTerminal.App.Services; using System; using System.Threading.Tasks; using Windows.ApplicationModel.AppService; using Windows.Foundation.Collections; namespace FluentTerminal.App.Adapters { public class AppServiceConnectionAdapter : IAppServiceConnection { private readonly AppServiceConnection _appServiceConnection; public event EventHandler MessageReceived; public AppServiceConnectionAdapter(AppServiceConnection appServiceConnection) { _appServiceConnection = appServiceConnection; _appServiceConnection.RequestReceived += OnRequestReceived; } private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { MessageReceived?.Invoke(this, args.Request.Message); } public async Task SendMessageAsync(ValueSet message) { var response = await _appServiceConnection.SendMessageAsync(message); if (response.Status == AppServiceResponseStatus.Success) { return response.Message; } return null; } } } ================================================ FILE: FluentTerminal.App/Adapters/ApplicationViewAdapter.cs ================================================ using FluentTerminal.App.Services; using FluentTerminal.App.Services.EventArgs; using System; using System.Threading.Tasks; using Windows.ApplicationModel.Core; using Windows.Foundation.Metadata; using Windows.UI.Core; using Windows.UI.Core.Preview; using Windows.UI.ViewManagement; using FluentTerminal.App.Utilities; namespace FluentTerminal.App.Adapters { public class ApplicationViewAdapter : IApplicationView { private readonly ApplicationView _applicationView; private readonly CoreDispatcher _dispatcher; private bool _closed; public event CloseRequestedHandler CloseRequested; public event EventHandler Closed; public ApplicationViewAdapter() { _applicationView = ApplicationView.GetForCurrentView(); _applicationView.Consolidated += _applicationView_Consolidated; _dispatcher = CoreApplication.GetCurrentView().Dispatcher; SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested; Logger.Instance.Debug("Created ApplicationViewAdapter for ApplicationView with Id: {Id}", _applicationView.Id); } private void _applicationView_Consolidated(ApplicationView sender, ApplicationViewConsolidatedEventArgs args) { _applicationView.Consolidated -= _applicationView_Consolidated; Closed?.Invoke(this, EventArgs.Empty); } public int Id => _applicationView.Id; public string Title { get => _applicationView.Title; set => _applicationView.Title = value ?? string.Empty; } public bool IsApiContractPresent(string api, ushort version) { return ApiInformation.IsApiContractPresent(api, version); } public Task ExecuteOnUiThreadAsync(Action action, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal, bool enforceNewSchedule = false) => _dispatcher.ExecuteAsync(action, priority, enforceNewSchedule); public Task ExecuteOnUiThreadAsync(Func func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal, bool enforceNewSchedule = false) => _dispatcher.ExecuteAsync(func, priority, enforceNewSchedule); public async Task TryCloseAsync() { if (_closed) { Logger.Instance.Debug("ApplicationViewAdapter.TryCloseAsync was called, but was already closed. ApplicationView.Id: {Id}", _applicationView.Id); return true; } Logger.Instance.Debug("TryCloseAsync ApplicationView with Id: {Id}", _applicationView.Id); _closed = await _applicationView.TryConsolidateAsync(); return _closed; } public bool ToggleFullScreen() { if (_applicationView.IsFullScreenMode) { _applicationView.ExitFullScreenMode(); return true; } return _applicationView.TryEnterFullScreenMode(); } private async void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e) { var deferral = e.GetDeferral(); var args = new CancelableEventArgs(); if (CloseRequested is { } closeRequestedHandler) { // ConfigureAwait(true) because SystemNavigationManagerPreview.GetForCurrentView().CloseRequested requires the calling (UI) thread await closeRequestedHandler(this, args).ConfigureAwait(true); } e.Handled = args.Cancelled; if (!e.Handled) { SystemNavigationManagerPreview.GetForCurrentView().CloseRequested -= OnCloseRequested; } deferral.Complete(); } } } ================================================ FILE: FluentTerminal.App/Adapters/MessageDialogAdapter.cs ================================================ using FluentTerminal.App.Services; using FluentTerminal.App.Services.Dialogs; using FluentTerminal.App.Services.Utilities; using System; using System.Threading.Tasks; using Windows.UI.Popups; namespace FluentTerminal.App.Adapters { public class MessageDialogAdapter : IMessageDialog { private readonly MessageDialog _messageDialog; public MessageDialogAdapter() { _messageDialog = new MessageDialog(string.Empty); } public string Title { get => _messageDialog.Title; set => _messageDialog.Title = value; } public string Content { get => _messageDialog.Content; set => _messageDialog.Content = value; } public void AddButton(DialogButton button) { var command = new UICommand { Label = I18N.Translate(button.ToString()), Id = button }; _messageDialog.Commands.Add(command); } public Task ShowAsync() { return _messageDialog.ShowAsync().AsTask().ContinueWith(t => (DialogButton) t.Result.Id, TaskContinuationOptions.OnlyOnRanToCompletion); } } } ================================================ FILE: FluentTerminal.App/App.xaml ================================================  ================================================ FILE: FluentTerminal.App/App.xaml.cs ================================================ using Autofac; using CommandLine; using FluentTerminal.App.Adapters; using FluentTerminal.App.CommandLineArguments; using FluentTerminal.App.Dialogs; using FluentTerminal.App.Services; using FluentTerminal.App.Services.Adapters; using FluentTerminal.App.Services.Dialogs; using FluentTerminal.App.Services.EventArgs; using FluentTerminal.App.Services.Implementation; using FluentTerminal.App.ViewModels; using FluentTerminal.App.Views; using FluentTerminal.Models; using FluentTerminal.Models.Enums; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.Background; using Windows.ApplicationModel.Core; using Windows.Storage; using Windows.UI.Popups; using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using FluentTerminal.App.Utilities; using IContainer = Autofac.IContainer; using FluentTerminal.App.Services.Utilities; using FluentTerminal.App.ViewModels.Profiles; using FluentTerminal.Models.Messages; using Microsoft.Toolkit.Mvvm.Messaging; namespace FluentTerminal.App { // ReSharper disable once RedundantExtendsListEntry public sealed partial class App : Application { private readonly TaskCompletionSource _trayReady = new TaskCompletionSource(); private readonly ISettingsService _settingsService; private readonly ITrayProcessCommunicationService _trayProcessCommunicationService; private readonly IDialogService _dialogService; private readonly INotificationService _notificationService; private bool _alreadyLaunched; private bool _isLaunching; private ApplicationSettings _applicationSettings; private readonly IContainer _container; private readonly List _mainViewModels; private SettingsViewModel _settingsViewModel; private int? _settingsWindowId; private IAppServiceConnection _appServiceConnection; private BackgroundTaskDeferral _appServiceDeferral; private Parser _commandLineParser; private int? _activeWindowId; public App() { _mainViewModels = new List(); InitializeComponent(); UnhandledException += OnUnhandledException; TaskScheduler.UnobservedTaskException += (sender, e) => Logger.Instance.Error(e.Exception, "Unobserved Task Exception"); var applicationDataContainers = new ApplicationDataContainers { LocalSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings), RoamingSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings), KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)), ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(Constants.ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)), Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.ThemesContainerName, ApplicationDataCreateDisposition.Always)), SshProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.SshProfilesContainerName, ApplicationDataCreateDisposition.Always)), HistoryContainer = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.ExecutedCommandsContainerName, ApplicationDataCreateDisposition.Always)) }; var builder = new ContainerBuilder(); builder.RegisterInstance(applicationDataContainers); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().InstancePerDependency(); builder.RegisterType().InstancePerDependency(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); _container = builder.Build(); WeakReferenceMessenger.Default.Register(this, (r, m) => r.OnApplicationSettingsChanged(m)); _settingsService = _container.Resolve(); _notificationService = _container.Resolve(); var shellProfileMigrationService = _container.Resolve(); foreach (var profile in _settingsService.GetShellProfiles()) { shellProfileMigrationService.Migrate(profile); _settingsService.SaveShellProfile(profile); } foreach (var profile in _settingsService.GetSshProfiles()) { shellProfileMigrationService.Migrate(profile); _settingsService.SaveSshProfile(profile); } _trayProcessCommunicationService = _container.Resolve(); _dialogService = _container.Resolve(); _applicationSettings = _settingsService.GetApplicationSettings(); JsonConvert.DefaultSettings = () => { var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), }; settings.Converters.Add(new StringEnumConverter(typeof(CamelCaseNamingStrategy))); return settings; }; _commandLineParser = new Parser(settings => { settings.CaseSensitive = false; settings.CaseInsensitiveEnumValues = true; }); } private void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) { Logger.Instance.Error(e.Exception, "Unhandled Exception"); } private static IEnumerable SplitArguments(string arguments) { var chars = arguments.ToCharArray(); var inQuote = false; for (var i = 0; i < chars.Length; i++) { if (chars[i] == '"') { inQuote = !inQuote; } if (!inQuote && chars[i] == ' ') { chars[i] = '\n'; } } foreach (var value in new string(chars).Split('\n')) { yield return value.Trim('"'); } } protected override async void OnActivated(IActivatedEventArgs args) { await OnLaunchOrActivate(args); base.OnActivated(args); } private async Task ParseCommandLineArgumentsAsync(object verb, CommandLineActivatedEventArgs commandLineActivated) { using var deferral = commandLineActivated.Operation.GetDeferral(); if (verb is SettingsVerb settingsVerb) { if (string.IsNullOrWhiteSpace(settingsVerb.Import) && !settingsVerb.Export) { await ShowSettingsAsync(); } else if (settingsVerb.Export) { var settings = _settingsService.ExportSettings(); var path = Path.Combine(commandLineActivated.Operation.CurrentDirectoryPath, "config.json"); try { await StartSystemTray().ConfigureAwait(false); await _trayProcessCommunicationService.SaveTextFileAsync(path, settings).ConfigureAwait(false); _notificationService.ShowNotification("Export settings", $"Settings were exported to {path}."); } catch (Exception e) { _notificationService.ShowNotification("Export settings", $"Failed with error: {e.Message}"); commandLineActivated.Operation.ExitCode = 1; } if (!_isLaunching && !_alreadyLaunched) { Exit(); } } else if (!string.IsNullOrWhiteSpace(settingsVerb.Import)) { var path = Path.Combine(commandLineActivated.Operation.CurrentDirectoryPath, settingsVerb.Import.Replace(".\\", string.Empty)); try { await StartSystemTray().ConfigureAwait(false); var content = await _trayProcessCommunicationService.ReadTextFileAsync(path).ConfigureAwait(false); _settingsService.ImportSettings(content); _notificationService.ShowNotification("Import settings", $"Successfully imported settings from {path}."); } catch (Exception e) { _notificationService.ShowNotification("Import settings", $"Failed with error: {e.Message}"); commandLineActivated.Operation.ExitCode = 1; } if (!_isLaunching && !_alreadyLaunched) { Exit(); } } } else if (verb is NewVerb newVerb) { var profile = default(ShellProfile); if (!string.IsNullOrWhiteSpace(newVerb.Profile)) { profile = _settingsService.GetShellProfiles().FirstOrDefault(x => x.Name.Equals(newVerb.Profile, StringComparison.CurrentCultureIgnoreCase)); } if (profile == null) { profile = _settingsService.GetDefaultShellProfile(); } if (!string.IsNullOrWhiteSpace(newVerb.Directory) && newVerb.Directory != ".") { profile.WorkingDirectory = newVerb.Directory; } else { profile.WorkingDirectory = commandLineActivated.Operation.CurrentDirectoryPath; } var location = newVerb.Target == Target.Default ? _applicationSettings.NewTerminalLocation : newVerb.Target == Target.Tab ? NewTerminalLocation.Tab : NewTerminalLocation.Window; await CreateTerminalAsync(profile, location); } else if (verb is RunVerb runVerb) { var profile = new ShellProfile { Id = Guid.Empty, Location = null, Arguments = runVerb.Command, WorkingDirectory = runVerb.Directory, UseConPty = _settingsService.GetApplicationSettings().UseConPty }; if (runVerb.SmartBuffer.HasValue) { profile.UseBuffer = runVerb.SmartBuffer.Value; } if (!string.IsNullOrWhiteSpace(runVerb.Theme)) { var theme = _settingsService.GetThemes().FirstOrDefault(x => x.Name.Equals(runVerb.Theme, StringComparison.CurrentCultureIgnoreCase)); if (theme != null) { profile.TerminalThemeId = theme.Id; } } if (string.IsNullOrWhiteSpace(profile.WorkingDirectory)) { profile.WorkingDirectory = commandLineActivated.Operation.CurrentDirectoryPath; } var location = runVerb.Target == Target.Default ? _applicationSettings.NewTerminalLocation : runVerb.Target == Target.Tab ? NewTerminalLocation.Tab : NewTerminalLocation.Window; await CreateTerminalAsync(profile, location); } } private Task ShowOrCreateWindowAsync(ActivationViewSwitcher viewSwitcher) { var viewModel = _mainViewModels.Find(o => o.ApplicationView.Id == _activeWindowId) ?? _mainViewModels.LastOrDefault(); return viewModel == null ? CreateTerminalAsync(_settingsService.GetDefaultShellProfile(), NewTerminalLocation.Tab, viewSwitcher) : ShowAsStandaloneAsync(viewModel, viewSwitcher); } protected override async void OnLaunched(LaunchActivatedEventArgs args) { await OnLaunchOrActivate(args); } public async Task OnLaunchOrActivate(IActivatedEventArgs args) { if (args is LaunchActivatedEventArgs launchActivated) { if (_isLaunching) { return; } _isLaunching = true; if (!_alreadyLaunched) { await InitializeLoggerAsync(); // ReSharper disable once AssignmentIsFullyDiscarded _ = JumpListHelper.UpdateAsync(_settingsService); var viewModel = _container.Resolve(); if (launchActivated.Arguments.StartsWith(JumpListHelper.ShellProfileFlag)) { await viewModel.AddProfileByGuidAsync(Guid.Parse(launchActivated.Arguments.Replace(JumpListHelper.ShellProfileFlag, string.Empty))); } else { await viewModel.AddDefaultProfileAsync(NewTerminalLocation.Tab); } await CreateMainViewAsync(typeof(MainPage), viewModel, true); Window.Current.Activate(); } else if (launchActivated.Arguments.StartsWith(JumpListHelper.ShellProfileFlag)) { var location = _applicationSettings.NewTerminalLocation; var profile = _settingsService.GetShellProfile(Guid.Parse(launchActivated.Arguments.Replace(JumpListHelper.ShellProfileFlag, string.Empty))); await CreateTerminalAsync(profile, location, launchActivated.ViewSwitcher); } else { var viewModel = await CreateNewTerminalWindowAsync(); await viewModel.AddDefaultProfileAsync(NewTerminalLocation.Tab); await ShowAsStandaloneAsync(viewModel, launchActivated.ViewSwitcher); } _isLaunching = false; } else if (args is ProtocolActivatedEventArgs protocolActivated) { if (protocolActivated.Uri == new Uri("ftcmd://fluent.terminal?focus")) { await ShowOrCreateWindowAsync(protocolActivated.ViewSwitcher); return; } MainViewModel mainViewModel = null; // IApplicationView to use for creating view models IApplicationView applicationView; if (_alreadyLaunched) { applicationView = (_mainViewModels.FirstOrDefault(o => o.ApplicationView.Id == _activeWindowId) ?? _mainViewModels.Last()).ApplicationView; } else { // App wasn't launched before double clicking a shortcut, so we have to create a window // in order to be able to communicate with user. mainViewModel = _container.Resolve(); await CreateMainViewAsync(typeof(MainPage), mainViewModel, true); applicationView = mainViewModel.ApplicationView; } bool isSsh; try { isSsh = SshConnectViewModel.CheckScheme(protocolActivated.Uri); } catch (Exception ex) { await new MessageDialog( $"{I18N.TranslateWithFallback("InvalidLink", "Invalid link.")} {ex.Message}", "Invalid Link") .ShowAsync(); mainViewModel?.ApplicationView.TryCloseAsync(); return; } if (isSsh) { SshConnectViewModel vm; try { vm = SshConnectViewModel.ParseUri(protocolActivated.Uri, _settingsService, applicationView, _trayProcessCommunicationService, _container.Resolve(), _container.Resolve().HistoryContainer); } catch (Exception ex) { await new MessageDialog( $"{I18N.TranslateWithFallback("InvalidLink", "Invalid link.")} {ex.Message}", "Invalid Link") .ShowAsync(); mainViewModel?.ApplicationView.TryCloseAsync(); return; } if (_applicationSettings.AutoFallbackToWindowsUsernameInLinks && string.IsNullOrEmpty(vm.Username)) { vm.Username = await _trayProcessCommunicationService.GetUserNameAsync(); } var error = await vm.AcceptChangesAsync(true); var profile = (SshProfile)vm.Model; if (!string.IsNullOrEmpty(error)) { // Link is valid, but incomplete (i.e. username missing), so we need to show dialog. profile = await _dialogService.ShowSshConnectionInfoDialogAsync(profile); if (profile == null) { // User clicked "Cancel" in the dialog. mainViewModel?.ApplicationView.TryCloseAsync(); return; } } if (mainViewModel == null) await CreateTerminalAsync(profile, _applicationSettings.NewTerminalLocation, protocolActivated.ViewSwitcher); else await mainViewModel.AddTabAsync(profile); return; } if (CommandProfileProviderViewModel.CheckScheme(protocolActivated.Uri)) { CommandProfileProviderViewModel vm; try { vm = CommandProfileProviderViewModel.ParseUri(protocolActivated.Uri, _settingsService, applicationView, _trayProcessCommunicationService, _container.Resolve()); } catch (Exception ex) { await new MessageDialog( $"{I18N.TranslateWithFallback("InvalidLink", "Invalid link.")} {ex.Message}", "Invalid Link") .ShowAsync(); mainViewModel?.ApplicationView.TryCloseAsync(); return; } var error = await vm.AcceptChangesAsync(true); var profile = vm.Model; if (!string.IsNullOrEmpty(error)) { // Link is valid, but incomplete, so we need to show dialog. profile = await _dialogService.ShowCustomCommandDialogAsync(profile); if (profile == null) { // User clicked "Cancel" in the dialog. mainViewModel?.ApplicationView.TryCloseAsync(); return; } } if (mainViewModel == null) { await CreateTerminalAsync(profile, _applicationSettings.NewTerminalLocation, protocolActivated.ViewSwitcher); } else { await mainViewModel.AddTabAsync(profile); } return; } await new MessageDialog( $"{I18N.TranslateWithFallback("InvalidLink", "Invalid link.")} {protocolActivated.Uri}", "Invalid Link") .ShowAsync(); // ReSharper disable once AssignmentIsFullyDiscarded _ = mainViewModel?.ApplicationView.TryCloseAsync(); return; } else if (args is CommandLineActivatedEventArgs commandLineActivated) { var arguments = commandLineActivated.Operation.Arguments; if (string.IsNullOrWhiteSpace(arguments)) { arguments = "new"; } _commandLineParser .ParseArguments(SplitArguments(arguments), typeof(NewVerb), typeof(RunVerb), typeof(SettingsVerb)) .WithParsed(async verb => await ParseCommandLineArgumentsAsync(verb, commandLineActivated)); } } private static async Task InitializeLoggerAsync() { var logDirectory = await ApplicationData.Current.LocalCacheFolder.CreateFolderAsync("Logs", CreationCollisionOption.OpenIfExists); var logFile = Path.Combine(logDirectory.Path, "fluentterminal.app.log"); var configFile = await logDirectory.CreateFileAsync("config.json", CreationCollisionOption.OpenIfExists); var configContent = await FileIO.ReadTextAsync(configFile); if (string.IsNullOrWhiteSpace(configContent)) { configContent = JsonConvert.SerializeObject(new Logger.Configuration()); await FileIO.WriteTextAsync(configFile, configContent); } var config = JsonConvert.DeserializeObject(configContent) ?? new Logger.Configuration(); Logger.Instance.Initialize(logFile, config); } protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details) { if (details.CallerPackageFamilyName == Package.Current.Id.FamilyName) { _appServiceDeferral = args.TaskInstance.GetDeferral(); args.TaskInstance.Canceled += OnTaskCanceled; _appServiceConnection = new AppServiceConnectionAdapter(details.AppServiceConnection); _trayReady.TrySetResult(0); } } } private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { _appServiceDeferral?.Complete(); _appServiceDeferral = null; _appServiceConnection = null; // ReSharper disable once ArrangeStaticMemberQualifier Application.Current.Exit(); } private async Task CreateMainViewAsync(Type pageType, INotifyPropertyChanged viewModel, bool extendViewIntoTitleBar) { ApplicationViewSwitcher.DisableSystemViewActivationPolicy(); await StartSystemTray(); if (!(Window.Current.Content is Frame rootFrame)) { rootFrame = new Frame(); Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar; if (viewModel is MainViewModel mainViewModel) { mainViewModel.Closed += OnMainViewModelClosed; mainViewModel.NewWindowRequested += OnNewWindowRequested; mainViewModel.ShowSettingsRequested += OnShowSettingsRequested; mainViewModel.ActivatedMv += OnMainViewActivated; mainViewModel.TabTearedOff += OnTabTearOff; _mainViewModels.Add(mainViewModel); } rootFrame.Navigate(pageType, viewModel); } _alreadyLaunched = true; Window.Current.Activate(); } private async Task CreateNewTerminalWindowAsync() { var viewModel = await CreateSecondaryViewAsync(typeof(MainPage), true); viewModel.Closed += OnMainViewModelClosed; viewModel.NewWindowRequested += OnNewWindowRequested; viewModel.ShowSettingsRequested += OnShowSettingsRequested; viewModel.ActivatedMv += OnMainViewActivated; viewModel.TabTearedOff += OnTabTearOff; _mainViewModels.Add(viewModel); return viewModel; } private async Task CreateSecondaryViewAsync(Type pageType, bool extendViewIntoTitleBar) { var windowId = 0; TViewModel viewModel = default; var newView = CoreApplication.CreateNewView(); // ConfigureAwait(true) because ApplicationViewSwitcher.TryShowAsStandaloneAsync requires the same thread as CoreApplication.CreateNewView await newView.Dispatcher.ExecuteAsync(() => { viewModel = _container.Resolve(); newView.TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar; var frame = new Frame(); frame.Navigate(pageType, viewModel); Window.Current.Content = frame; Window.Current.Activate(); windowId = ApplicationView.GetForCurrentView().Id; }).ConfigureAwait(true); if (viewModel is SettingsViewModel settingsViewModel) { _settingsViewModel = settingsViewModel; _settingsViewModel.Closed += OnSettingsClosed; _settingsWindowId = windowId; } await ApplicationViewSwitcher.TryShowAsStandaloneAsync(windowId); return viewModel; } private void OnApplicationSettingsChanged(ApplicationSettingsChangedMessage message) { _applicationSettings = message.ApplicationSettings; _trayProcessCommunicationService.UpdateSettings(message.ApplicationSettings); } private void OnMainViewModelClosed(object sender, EventArgs e) { if (sender is MainViewModel viewModel) { Logger.Instance.Debug("MainViewModel with ApplicationView Id: {@id} closed.", viewModel.ApplicationView.Id); viewModel.Closed -= OnMainViewModelClosed; viewModel.NewWindowRequested -= OnNewWindowRequested; viewModel.ShowSettingsRequested -= OnShowSettingsRequested; viewModel.ActivatedMv -= OnMainViewActivated; viewModel.TabTearedOff -= OnTabTearOff; if (_activeWindowId == viewModel.ApplicationView.Id) { _activeWindowId = 0; } _mainViewModels.Remove(viewModel); try { // try/catch because the method cannot be called on the last window Window.Current.Close(); } catch { // ignored } } } private void OnMainViewActivated(object sender, EventArgs e) { if (sender is MainViewModel viewModel) { Logger.Instance.Debug("MainViewModel with ApplicationView Id: {@id} activated.", viewModel.ApplicationView.Id); _activeWindowId = viewModel.ApplicationView.Id; } } private async void OnTabTearOff(object sender, TerminalViewModel model) { Logger.Instance.Debug("App.xaml.cs on tab tear off"); var newViewModel = await CreateNewTerminalWindowAsync(); await newViewModel.AddTabAsync(await model.SerializeAsync(), 0); } private async void OnNewWindowRequested(object sender, NewWindowRequestedEventArgs e) { var viewModel = await CreateNewTerminalWindowAsync().ConfigureAwait(false); await viewModel.AddTabAsync(e.Profile).ConfigureAwait(false); } private void OnSettingsClosed(object sender, EventArgs e) { // ReSharper disable once AssignmentIsFullyDiscarded _ = JumpListHelper.UpdateAsync(_settingsService); _settingsViewModel.Closed -= OnSettingsClosed; _settingsViewModel = null; _settingsWindowId = null; } private void OnShowSettingsRequested(object sender, EventArgs e) { // ReSharper disable once AssignmentIsFullyDiscarded _ = ShowSettingsAsync(); } private Task ShowAsStandaloneAsync(MainViewModel viewModel, ActivationViewSwitcher viewSwitcher = null) { var viewId = viewModel.ApplicationView.Id; if (viewSwitcher != null) { return viewModel.ApplicationView.ExecuteOnUiThreadAsync(async () => await viewSwitcher.ShowAsStandaloneAsync(viewId)); } return ApplicationViewSwitcher.TryShowAsStandaloneAsync(viewId).AsTask(); } private async Task CreateTerminalAsync(ShellProfile profile, NewTerminalLocation location, ActivationViewSwitcher viewSwitcher = null) { if (!_alreadyLaunched) { var viewModel = _container.Resolve(); await viewModel.AddTabAsync(profile); await CreateMainViewAsync(typeof(MainPage), viewModel, true); } else if (location == NewTerminalLocation.Tab && _mainViewModels.Count > 0) { var item = _mainViewModels.FirstOrDefault(o => o.ApplicationView.Id == _activeWindowId) ?? _mainViewModels.Last(); await item.AddTabAsync(profile); await ShowAsStandaloneAsync(item, viewSwitcher); } else { var viewModel = await CreateNewTerminalWindowAsync(); await viewModel.AddTabAsync(profile); await ShowAsStandaloneAsync(viewModel, viewSwitcher); } } private Task ShowSettingsAsync() { if (!_alreadyLaunched) { return CreateMainViewAsync(typeof(SettingsPage), _container.Resolve(), true); } if (_settingsViewModel == null) { return CreateSecondaryViewAsync(typeof(SettingsPage), true); } if (_settingsWindowId.HasValue) { return ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value).AsTask(); } return Task.CompletedTask; } private Task StartSystemTray() { if (_trayReady.Task.IsCompleted) { return Task.CompletedTask; } var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("AppLaunchedParameterGroup").AsTask(); return Task.WhenAll(launch, _trayReady.Task).ContinueWith( t => _trayProcessCommunicationService.Initialize(_appServiceConnection), TaskContinuationOptions.OnlyOnRanToCompletion); } } } ================================================ FILE: FluentTerminal.App/Behaviors/MiddleClickBehavior.cs ================================================ using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Input; namespace FluentTerminal.App.Behaviors { public class MiddleClickBehavior : Trigger { private bool _middleButtonPressed; protected override void OnAttached() { AssociatedObject.PointerPressed += OnPointerPressed; AssociatedObject.PointerReleased += OnPointerReleased; } protected override void OnDetaching() { AssociatedObject.PointerPressed -= OnPointerPressed; AssociatedObject.PointerReleased -= OnPointerReleased; } private void OnPointerPressed(object sender, PointerRoutedEventArgs e) { var point = e.GetCurrentPoint(AssociatedObject); _middleButtonPressed = point.Properties.IsMiddleButtonPressed; } private void OnPointerReleased(object sender, PointerRoutedEventArgs e) { var point = e.GetCurrentPoint(AssociatedObject); var middleButtonStillPressed = point.Properties.IsMiddleButtonPressed; if (_middleButtonPressed && !middleButtonStillPressed) { Interaction.ExecuteActions(AssociatedObject, Actions, null); } } } } ================================================ FILE: FluentTerminal.App/CommandLineArguments/NewVerb.cs ================================================ using CommandLine; namespace FluentTerminal.App.CommandLineArguments { [Verb("new")] public class NewVerb { [Value(0)] public string Directory { get; set; } [Option("profile")] public string Profile { get; set; } [Option("target")] public Target Target { get; set; } } } ================================================ FILE: FluentTerminal.App/CommandLineArguments/RunVerb.cs ================================================ using CommandLine; namespace FluentTerminal.App.CommandLineArguments { [Verb("run")] public class RunVerb { [Value(0)] public string Command { get; set; } [Option("directory")] public string Directory { get; set; } [Option("theme")] public string Theme { get; set; } [Option("smart-buffer")] public bool? SmartBuffer { get; set; } [Option("target")] public Target Target { get; set; } } } ================================================ FILE: FluentTerminal.App/CommandLineArguments/SettingsVerb.cs ================================================ using CommandLine; namespace FluentTerminal.App.CommandLineArguments { [Verb("settings")] public class SettingsVerb { [Option('i', "import", HelpText = "Import settings from specified file.")] public string Import { get; set; } [Option('e', "export", HelpText = "Export settings.")] public bool Export { get; set; } } } ================================================ FILE: FluentTerminal.App/CommandLineArguments/Target.cs ================================================ namespace FluentTerminal.App.CommandLineArguments { public enum Target { Default, Tab, Window } } ================================================ FILE: FluentTerminal.App/Converters/BackgroundToApplicationThemeConverter.cs ================================================ using FluentTerminal.App.Utilities; using Microsoft.Toolkit.Uwp.Helpers; using System; using Windows.UI; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class BackgroundToApplicationThemeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { Color color; if (value is string colorString) { color = colorString.ToColor(); } else if (value is Color) { color = (Color)value; } return ContrastHelper.GetIdealThemeForBackgroundColor(color); } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/BooleanNegationConverter.cs ================================================ using System; using Windows.UI.Xaml.Data; // ReSharper disable LocalizableElement namespace FluentTerminal.App.Converters { public class BooleanNegationConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (targetType == typeof(bool)) { if (value is bool isValue) { return !isValue; } throw new ArgumentException($"{nameof(value)} argument has to be of type bool.", nameof(value)); } if (targetType == typeof(bool?)) { if (value == null) { return null; } if (value is bool isValue) { return !isValue; } throw new ArgumentException($"{nameof(value)} argument has to be of type bool or Nullable.", nameof(value)); } throw new ArgumentException($"{nameof(targetType)} argument has to {typeof(bool)}.", nameof(targetType)); } public object ConvertBack(object value, Type targetType, object parameter, string language) => Convert(value, targetType, parameter, language); } } ================================================ FILE: FluentTerminal.App/Converters/ColorResourceKeyFallbackConverter.cs ================================================ using Microsoft.Toolkit.Uwp.Helpers; using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; namespace FluentTerminal.App.Converters { public class ColorResourceKeyFallbackConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value == null) { return Application.Current.Resources[(string)parameter]; } else if (value is string hex) { var color = hex.ToColor(); return new SolidColorBrush(color); } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/EnumValueToVisibiltyConverter.cs ================================================ using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class EnumValueToVisibiltyConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value.GetType().IsEnum && Enum.TryParse(value.GetType(), parameter.ToString(), true, out object result)) { return Equals(value, result) ? Visibility.Visible : Visibility.Collapsed; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/FalseToVisibleConverter.cs ================================================ using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class FalseToVisibleConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is Boolean collapsed) { return collapsed ? Visibility.Collapsed : Visibility.Visible; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { if (value is Visibility visibility) { return visibility == Visibility.Collapsed; } return null; } } } ================================================ FILE: FluentTerminal.App/Converters/I18NConverter.cs ================================================ using FluentTerminal.App.Services.Utilities; using System; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class I18NConverter : IValueConverter { // To avoid empty translations private static string Translate(string resource) { var translation = I18N.Translate(resource); return string.IsNullOrEmpty(translation) ? $"[ {resource} ]" : translation; } public object Convert(object value, Type targetType, object parameter, string language) { if (value is Enum enumValue && parameter is string enumType) { return Translate($"{enumType}.{enumValue}"); } else if (value is string stringValue) { return Translate(stringValue); } else if (parameter is string resource) { return Translate(resource); } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/IconConverter.cs ================================================ using FluentTerminal.App.ViewModels.Menu; using Microsoft.Toolkit.Uwp.Helpers; using System; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; // ReSharper disable LocalizableElement namespace FluentTerminal.App.Converters { public class IconConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is Mdl2Icon mdl2Icon) { var fontIcon = new FontIcon { FontFamily = new FontFamily("Segoe MDL2 Assets"), Glyph = mdl2Icon.Glyph }; if(!string.IsNullOrWhiteSpace(mdl2Icon.Color)) { fontIcon.Foreground = new SolidColorBrush(mdl2Icon.Color.ToColor()); } return fontIcon; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotSupportedException(); } } ================================================ FILE: FluentTerminal.App/Converters/IntToExtendedVirtualKeyConverter.cs ================================================ using FluentTerminal.App.Services.Utilities; using FluentTerminal.Models.Enums; using System; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class IntToExtendedVirtualKeyConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is int intValue) { return EnumHelper.GetEnumDescription((ExtendedVirtualKey)intValue); } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/IntToVisibilityConverter.cs ================================================ using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class IntToVisibilityConverter : DependencyObject, IValueConverter { public static readonly DependencyProperty ElseProperty = DependencyProperty.Register(nameof(Else), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Collapsed)); public static readonly DependencyProperty IfProperty = DependencyProperty.Register(nameof(If), typeof(int), typeof(IntToVisibilityConverter), new PropertyMetadata(0)); public static readonly DependencyProperty ThenProperty = DependencyProperty.Register(nameof(Then), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Visible)); public Visibility Else { get { return (Visibility)GetValue(ElseProperty); } set { SetValue(ElseProperty, value); } } public int If { get { return (int)GetValue(IfProperty); } set { SetValue(IfProperty, value); } } public Visibility Then { get { return (Visibility)GetValue(ThenProperty); } set { SetValue(ThenProperty, value); } } public object Convert(object value, Type targetType, object parameter, string language) { if (value is int count) { return count == If ? Then : Else; } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/MenuItemViewModelBaseToMenuFlayoutItemBaseConverter.cs ================================================ using System; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using FluentTerminal.App.ViewModels.Menu; using FluentTerminal.App.Views; using Microsoft.UI.Xaml.Controls; using Windows.Foundation.Metadata; // ReSharper disable LocalizableElement namespace FluentTerminal.App.Converters { public class MenuItemViewModelBaseToMenuFlayoutItemBaseConverter : IValueConverter { #region Static private static readonly IconConverter IconConverter = new IconConverter(); private static MenuFlyoutItemBase GetItem(MenuItemViewModelBase viewModel) { if (viewModel is ExpandableMenuItemViewModel expandable) { return GetExpandableItem(expandable); } if (viewModel is SeparatorMenuItemViewModel) { return GetSeparatorItem(); } if (viewModel is MenuItemViewModel regular) { return GetRegularItem(regular); } if (viewModel is ToggleMenuItemViewModel toggle) { return GetToggleItem(toggle); } if (viewModel is RadioMenuItemViewModel radio) { return GetRadioItem(radio); } // Won't happen ever, but still... throw new NotImplementedException( $"Unexpected {nameof(MenuItemViewModelBase)} type: {viewModel.GetType()}"); } private static MenuFlyoutSeparator GetSeparatorItem() { return new MenuFlyoutSeparator(); } private static MenuFlyoutItem GetRegularItem(MenuItemViewModel viewModel) { var item = new MenuFlyoutItem(); item.SetBinding(MenuFlyoutItem.TextProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Text)), Mode = BindingMode.OneWay }); item.SetBinding(ToolTipService.ToolTipProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Description)), Mode = BindingMode.OneWay }); item.SetBinding(MenuFlyoutItem.IconProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Icon)), Converter = IconConverter, Mode = BindingMode.OneWay }); item.SetBinding(MenuFlyoutItem.CommandProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModel.Command)), Mode = BindingMode.OneTime }); if (viewModel.KeyBinding != null) { if (viewModel.KeyBinding.IsExtendedVirtualKey) { if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 6)) { item.KeyboardAcceleratorTextOverride = viewModel.KeyBinding.GetOverrideText(); } } else { item.KeyboardAccelerators?.Add(new KeyboardAccelerator { Key = (VirtualKey)viewModel.KeyBinding.Key, Modifiers = (VirtualKeyModifiers)viewModel.KeyBinding.KeyModifiers, IsEnabled = true }); } } return item; } private static MenuFlyoutSubItem GetExpandableItem(ExpandableMenuItemViewModel viewModel) { var item = new MenuFlyoutSubItem(); item.SetBinding(MenuFlyoutSubItem.TextProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Text)), Mode = BindingMode.OneWay }); item.SetBinding(ToolTipService.ToolTipProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Description)), Mode = BindingMode.OneWay }); item.SetBinding(MenuFlyoutSubItem.IconProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Icon)), Converter = IconConverter, Mode = BindingMode.OneWay }); item.SetBinding(MenuExtension.SubItemsProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(ExpandableMenuItemViewModel.SubItems)), Mode = BindingMode.OneWay }); return item; } private static ToggleMenuFlyoutItem GetToggleItem(ToggleMenuItemViewModel viewModel) { var item = new ToggleMenuFlyoutItem(); item.SetBinding(MenuFlyoutItem.TextProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Text)), Mode = BindingMode.OneWay }); item.SetBinding(ToolTipService.ToolTipProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Description)), Mode = BindingMode.OneWay }); item.SetBinding(MenuFlyoutItem.IconProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Icon)), Converter = IconConverter, Mode = BindingMode.OneWay }); item.SetBinding(ToggleMenuFlyoutItem.IsCheckedProperty, new Binding { Source = viewModel.BindingSource, Path = new PropertyPath(viewModel.BindingPath), Mode = BindingMode.TwoWay }); return item; } private static RadioMenuFlyoutItem GetRadioItem(RadioMenuItemViewModel viewModel) { var item = new RadioMenuFlyoutItem(); item.SetBinding(MenuFlyoutItem.TextProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Text)), Mode = BindingMode.OneWay }); item.SetBinding(ToolTipService.ToolTipProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Description)), Mode = BindingMode.OneWay }); item.SetBinding(MenuFlyoutItem.IconProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(MenuItemViewModelBase.Icon)), Converter = IconConverter, Mode = BindingMode.OneWay }); item.SetBinding(RadioMenuFlyoutItem.GroupNameProperty, new Binding { Source = viewModel, Path = new PropertyPath(nameof(RadioMenuItemViewModel.GroupName)), Mode = BindingMode.OneWay }); item.SetBinding(RadioMenuFlyoutItem.IsCheckedProperty, new Binding { Source = viewModel.BindingSource, Path = new PropertyPath(viewModel.BindingPath), Mode = BindingMode.TwoWay }); return item; } #endregion Static #region IValueConverter // ReSharper disable once AssignNullToNotNullAttribute public object Convert(object value, Type targetType = null, object parameter = null, string language = null) { if (value == null) { return null; } return value is MenuItemViewModelBase menuItemViewModel ? GetItem(menuItemViewModel) : throw new ArgumentException( $"Invalid {nameof(value)} argument type: {value.GetType()}. {typeof(MenuItemViewModelBase)} expected.", nameof(value)); } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotSupportedException(); #endregion IValueConverter } } ================================================ FILE: FluentTerminal.App/Converters/MenuViewModelToFlyoutMenuConverter.cs ================================================ using System; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using FluentTerminal.App.ViewModels.Menu; using Windows.UI.Xaml.Controls.Primitives; namespace FluentTerminal.App.Converters { public class MenuViewModelToFlyoutMenuConverter : IValueConverter { private static readonly MenuItemViewModelBaseToMenuFlayoutItemBaseConverter ItemConverter = new MenuItemViewModelBaseToMenuFlayoutItemBaseConverter(); public object Convert(object value, Type targetType, object parameter, string language) { if (value is null) { return null; } if (!(value is MenuViewModel menuViewModel)) { throw new ArgumentException( $"Invalid {nameof(value)} argument type: {value.GetType()}. {typeof(MenuViewModel)} expected."); } var menuFlyout = new MenuFlyout { Placement = FlyoutPlacementMode.Bottom }; foreach (var menuItemViewModelBase in menuViewModel.Items) { menuFlyout.Items?.Add((MenuFlyoutItemBase) ItemConverter.Convert(menuItemViewModelBase)); } return menuFlyout; } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotSupportedException(); } } ================================================ FILE: FluentTerminal.App/Converters/NegateConverter.cs ================================================ using System; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class NegateConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is bool boolean) { return !boolean; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/NullToCollapsedConverter.cs ================================================ using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class NullToCollapsedConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { return value == null ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/StringToColorConverter.cs ================================================ using FluentTerminal.App.Utilities; using System; using Windows.UI; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class StringToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is string colorString) { return colorString.FromString(); } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { if (value is Color color && bool.TryParse((string)parameter, out bool allowTransparency)) { return color.ToColorString(allowTransparency); } return null; } } } ================================================ FILE: FluentTerminal.App/Converters/TabColorFallbackConverter.cs ================================================ using FluentTerminal.App.ViewModels; using FluentTerminal.Models.Enums; using Microsoft.Toolkit.Uwp.Helpers; using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; namespace FluentTerminal.App.Converters { public class TabColorFallbackConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is TabThemeViewModel viewModel && Enum.TryParse((string)parameter, true, out TabThemeKey tabThemeKey)) { var theme = viewModel.Theme; switch (tabThemeKey) { case TabThemeKey.Background: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundOpacity); case TabThemeKey.BackgroundPointerOver: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundPointerOverOpacity); case TabThemeKey.BackgroundPressed: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundPressedOpacity); case TabThemeKey.BackgroundSelected: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedOpacity); case TabThemeKey.BackgroundSelectedPointerOver: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedPointerOverOpacity); case TabThemeKey.BackgroundSelectedPressed: return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedPressedOpacity); } } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } private Brush GetBrush(TabThemeKey tabThemeKey, string color, double opacity) { if (double.IsNaN(opacity)) { return (Brush)Application.Current.Resources[GetFallbackRessourceKey(tabThemeKey)]; } else { return CreateBrush(color, opacity); } } private Brush CreateBrush(string hex, double opacity) { var color = hex.ToColor(); return new SolidColorBrush(color) { Opacity = opacity }; } private string GetFallbackRessourceKey(TabThemeKey tabThemeKey) { switch (tabThemeKey) { case TabThemeKey.Background: return "SystemControlTransparentBrush"; case TabThemeKey.BackgroundPointerOver: return "SystemControlHighlightListLowBrush"; case TabThemeKey.BackgroundPressed: return "SystemControlHighlightListMediumBrush"; case TabThemeKey.BackgroundSelected: return "SystemControlHighlightListAccentLowBrush"; case TabThemeKey.BackgroundSelectedPointerOver: return "SystemControlHighlightListAccentMediumBrush"; case TabThemeKey.BackgroundSelectedPressed: return "SystemControlHighlightListAccentHighBrush"; } return null; } } } ================================================ FILE: FluentTerminal.App/Converters/TabThemeSelectedConverter.cs ================================================ using FluentTerminal.App.ViewModels; using System; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class TabThemeSelectedConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is TerminalViewModel terminal && parameter is string idString && int.TryParse(idString, out var id)) { return terminal.TabTheme.Theme.Id == id; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/TerminalViewModelToViewConverter.cs ================================================ using FluentTerminal.App.ViewModels; using FluentTerminal.App.Views; using System; using System.Collections.Generic; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { internal class TerminalViewModelToViewConverter : IValueConverter { private readonly Dictionary _viewDictionary; public TerminalViewModelToViewConverter() { _viewDictionary = new Dictionary(); } public void RemoveTerminal(TerminalViewModel viewModel) { if (_viewDictionary.TryGetValue(viewModel, out TerminalView terminalView)) { viewModel.DisposalPrepare(); terminalView.DisposalPrepare(); } _viewDictionary.Remove(viewModel); GC.Collect(); } public object Convert(object value, Type targetType, object parameter, string language) { if (value is TerminalViewModel terminal) { if (_viewDictionary.TryGetValue(terminal, out TerminalView view)) { return view; } var newView = new TerminalView(terminal); _viewDictionary.Add(terminal, newView); return newView; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/TextMiddleEllipsisConverter.cs ================================================ using System; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { class TextMiddleEllipsisConverter : IValueConverter { private TextBlock _textBlock = null; private double MeasureWidth(string text) { if (_textBlock == null) { _textBlock = new TextBlock { Text = text, Margin = new Thickness(12, 8, 6, 0), Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"] }; } else { _textBlock.Text = text; } _textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); return _textBlock.DesiredSize.Width; } public object Convert(object value, Type targetType, object parameter, string language) { if (value is String text) { const int MaxWidth = 136; string result = text; var width = MeasureWidth(text); if (width > MaxWidth) { double oversize = width - MaxWidth; double charSize = width / text.Length; int charsToCut = (int)(oversize / charSize) + 3; int cutStart = text.Length / 2 - charsToCut / 2; int cutEnd = text.Length / 2 + (charsToCut - charsToCut / 2); result = text.Substring(0, cutStart) + ".." + text.Substring(cutEnd); width = MeasureWidth(result); while (width > MaxWidth && cutStart > 0 && cutEnd < text.Length) { result = text.Substring(0, --cutStart) + ".." + text.Substring(++cutEnd); width = MeasureWidth(result); } } return result; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/ToolTipValueToPixelConverter.cs ================================================ using System; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class ToolTipValueToPixelConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { return string.Format("{0} px", value); } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: FluentTerminal.App/Converters/TrueToVisibleConverter.cs ================================================ using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace FluentTerminal.App.Converters { public class TrueToVisibleConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is Boolean visible) { return visible ? Visibility.Visible : Visibility.Collapsed; } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { if (value is Visibility visibility) { return visibility == Visibility.Visible; } return null; } } } ================================================ FILE: FluentTerminal.App/Dialogs/AboutDialog.xaml ================================================  - ================================================ FILE: FluentTerminal.App/Dialogs/AboutDialog.xaml.cs ================================================ using FluentTerminal.App.Services; using FluentTerminal.App.Services.Dialogs; using FluentTerminal.App.Utilities; using System; using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Documents; namespace FluentTerminal.App.Dialogs { public sealed partial class AboutDialog : ContentDialog, IAboutDialog { public AboutDialog(ISettingsService settingsService, IUpdateService updateService) { this.InitializeComponent(); var currentTheme = settingsService.GetCurrentTheme(); RequestedTheme = ContrastHelper.GetIdealThemeForBackgroundColor(currentTheme.Colors.Background); CreatedBy.Inlines.Add(new Run { Text = "Created by " }); CreatedBy.Inlines.Add(new Italic { Inlines = { new Run { Text = "felixse " } } }); CreatedBy.Inlines.Add(new Run { Text = "and " }); CreatedBy.Inlines.Add(new Hyperlink { Inlines = { new Run { Text = "contributors" } }, NavigateUri = new Uri("https://github.com/felixse/FluentTerminal/graphs/contributors") }); CurrentVersion.Text = updateService.GetCurrentVersion().ToString(); ReleaseNotesHyperlink.NavigateUri = new Uri("https://github.com/felixse/FluentTerminal/releases/tag/" + CurrentVersion.Text); } public new Task ShowAsync() { return base.ShowAsync().AsTask(); } private void ContentDialog_CloseButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { Hide(); } } } ================================================ FILE: FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml ================================================  ================================================ FILE: FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml.cs ================================================ using FluentTerminal.App.Services.Dialogs; using FluentTerminal.App.Services.Utilities; using FluentTerminal.Models; using Microsoft.Toolkit.Mvvm.Input; using System; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Input; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace FluentTerminal.App.Dialogs { // ReSharper disable once RedundantExtendsListEntry public sealed partial class CreateKeyBindingDialog : ContentDialog, ICreateKeyBindingDialog, INotifyPropertyChanged { private bool _ctrl; private bool _shift; private bool _alt; private bool _meta; private int _key; public bool Ctrl { get => _ctrl; set => SetProperty(ref _ctrl, value); } public bool Shift { get => _shift; set => SetProperty(ref _shift, value); } public bool Alt { get => _alt; set => SetProperty(ref _alt, value); } public bool Meta { get => _meta; set => SetProperty(ref _meta, value); } public int Key { get => _key; set { SetProperty(ref _key, value); } } public CreateKeyBindingDialog() { InitializeComponent(); ResetCommand = new RelayCommand(Reset); PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown; Reset(); this.PrimaryButtonText = I18N.Translate("OK"); this.SecondaryButtonText = I18N.Translate("Cancel"); } public ICommand ResetCommand { get; } public event PropertyChangedEventHandler PropertyChanged; public Task CreateKeyBinding() { return ShowAsync().AsTask() .ContinueWith( t => t.Result == ContentDialogResult.Primary ? new KeyBinding {Alt = Alt, Ctrl = Ctrl, Key = Key, Meta = Meta, Shift = Shift} : null, TaskContinuationOptions.OnlyOnRanToCompletion); } private void OnResetButtonPreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { e.Handled = true; } private void RegisterKeyBindingDialog_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { switch (e.Key) { case VirtualKey.Control: Ctrl = true; break; case VirtualKey.Shift: Shift = true; break; case VirtualKey.Menu: Alt = true; break; case VirtualKey.LeftWindows: case VirtualKey.RightWindows: Meta = true; break; default: Key = (int)e.Key; break; } ResetButton.Visibility = Visibility.Visible; e.Handled = true; } private void Reset() { Alt = false; Ctrl = false; Meta = false; Shift = false; Key = 0; ResetButton.Visibility = Visibility.Collapsed; } private void SetProperty(ref T field, T value, [CallerMemberName]string propertyName = "") { if (field?.Equals(value) ?? value == null) { return; } field = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } } ================================================ FILE: FluentTerminal.App/Dialogs/CustomCommandDialog.xaml ================================================