Compare commits

...
10 Commits
Author SHA1 Message Date
FelixSe 9b61d11f6a Added settings window to configure which shell to use 2018-02-03 21:25:34 +01:00
FelixSe 49ee8489d6 finished resize overlay 2018-01-31 21:18:13 +01:00
FelixSe 7b485ce407 setup dependency injection 2018-01-31 19:37:41 +01:00
FelixSe 66465b525f add MVVM infrastructure 2018-01-31 19:26:34 +01:00
FelixSe 9c5d8c27fd Move logic from web client to app to prepare configuration 2018-01-29 22:26:37 +01:00
FelixSe 4ae3be544b Resize terminal on window resize 2018-01-28 22:06:57 +01:00
FelixSe e32e0398ee Remove unnecessary build step 2018-01-28 22:05:38 +01:00
FelixSe fab22f2479 cleanup app.xaml.cs 2018-01-28 21:50:20 +01:00
Felix b2151c69ab Merge pull request #1 from vitorgrs/master
Remove splashscreen
2018-01-25 22:45:40 +01:00
Vitor Mikaelson f5fba8517a Remove splashscreen 2018-01-25 20:51:02 +00:00
64 changed files with 2078 additions and 328 deletions

No files matched your search

+143 -74
View File
@@ -1,6 +1,9 @@
using FluentTerminal.App.Views;
using Autofac;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.App.ViewModels;
using FluentTerminal.App.Views;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
@@ -11,56 +14,91 @@ using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App
{
sealed partial class App : Application
{
public static BackgroundTaskDeferral AppServiceDeferral = null;
public static AppServiceConnection Connection = null;
public static App Instance;
private bool _alreadyLaunched;
private IContainer _container;
private int _openTerminalWindows;
private int? _settingsWindowId;
public App()
{
InitializeComponent();
Instance = this;
var builder = new ContainerBuilder();
builder.RegisterType<SettingsService>().As<ISettingsService>().InstancePerDependency();
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
builder.RegisterType<TerminalService>().As<ITerminalService>().SingleInstance();
builder.RegisterType<MainViewModel>().InstancePerDependency();
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
_container = builder.Build();
}
public static BackgroundTaskDeferral AppServiceDeferral = null;
public static AppServiceConnection Connection = null;
public static App Instance;
int? _settingsWindowId;
int idCreate = 0;
List<int> idSaved = new List<int>();
protected override async void OnLaunched(LaunchActivatedEventArgs e)
public async Task CreateNewTerminalWindow()
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
var id = await CreateSecondaryView(typeof(MainPage), true);
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
await Task.WhenAll(launch, clearCache);
public void SettingsWindowClosed()
{
_settingsWindowId = null;
}
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
public async Task ShowSettings()
{
if (_settingsWindowId == null)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
_settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), true);
}
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
}
if (rootFrame.Content == null)
public void TerminalWindowClosed()
{
_openTerminalWindows--;
}
protected override async void OnActivated(IActivatedEventArgs args)
{
if (args is CommandLineActivatedEventArgs commandLineActivated)
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
rootFrame.Navigate(typeof(MainPage), e.Arguments);
idSaved.Add(ApplicationView.GetForCurrentView().Id);
var argument = commandLineActivated.Operation.Arguments.Trim();
if (_alreadyLaunched)
{
if (argument == "settings")
{
await ShowSettings();
}
else if (argument == "new")
{
await CreateNewTerminalWindow();
}
}
else
{
if (argument == "settings")
{
CreateMainView(typeof(SettingsPage), true);
}
else if (argument == "new")
{
CreateMainView(typeof(MainPage), true);
}
}
}
else if (false) //todo: add option to create new windows on launch
{
await CreateNewWindow(typeof(MainPage), true);
}
Window.Current.Activate();
}
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
// connection established from the fulltrust process
base.OnBackgroundActivated(args);
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
{
@@ -71,6 +109,81 @@ namespace FluentTerminal.App
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
await Task.WhenAll(launch, clearCache);
if (!_alreadyLaunched)
{
CreateMainView(typeof(MainPage), true);
Window.Current.Activate();
}
else if (_openTerminalWindows == 0)
{
await CreateSecondaryView(typeof(MainPage), true);
}
}
private void CreateMainView(Type pageType, bool extendViewIntoTitleBar)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar;
rootFrame.Navigate(pageType, GetViewModelForPageType(pageType));
if (pageType == typeof(MainPage))
{
_openTerminalWindows++;
}
}
_alreadyLaunched = true;
Window.Current.Activate();
}
private async Task<int> CreateSecondaryView(Type pageType, bool ExtendViewIntoTitleBar)
{
int windowId = 0;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
var frame = new Frame();
frame.Navigate(pageType, GetViewModelForPageType(pageType));
Window.Current.Content = frame;
Window.Current.Activate();
windowId = ApplicationView.GetForCurrentView().Id;
});
if (pageType == typeof(MainPage))
{
_openTerminalWindows++;
}
return windowId;
}
private object GetViewModelForPageType(Type pageType)
{
if (pageType == typeof(MainPage))
{
return _container.Resolve<MainViewModel>();
}
else if (pageType == typeof(SettingsPage))
{
return _container.Resolve<SettingsViewModel>();
}
throw new Exception("No ViewModel defined for Page: " + pageType.Name);
}
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
if (args.Request.Message.ContainsKey("exit"))
@@ -86,49 +199,5 @@ namespace FluentTerminal.App
AppServiceDeferral.Complete();
}
}
public async Task<int> CreateNewWindow(Type pageType, bool ExtendViewIntoTitleBar)
{
var create = CoreApplication.CreateNewView();
await create.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
var frame = new Frame();
frame.Navigate(pageType, null);
Window.Current.Content = frame;
Window.Current.Activate();
idCreate = ApplicationView.GetForCurrentView().Id;
});
for (int i = idSaved.Count - 1; i >= 0; i--)
if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
idCreate, ViewSizePreference.Default,
idSaved[i], ViewSizePreference.Default)
) break;
idSaved.Add(idCreate);
return idCreate;
}
public async Task ShowNew()
{
var id = await CreateNewWindow(typeof(MainPage), true);
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
public async Task ShowSettings()
{
if (_settingsWindowId == null)
{
_settingsWindowId = await CreateNewWindow(typeof(Views.SettingsPage), false);
}
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,27 @@
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;
}
}
}
@@ -0,0 +1,39 @@
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 Dictionary<int, TerminalView> _viewDictionary;
public TerminalViewModelToViewConverter()
{
_viewDictionary = new Dictionary<int, TerminalView>();
}
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TerminalViewModel terminal)
{
if (_viewDictionary.TryGetValue(terminal.Id, out TerminalView view))
{
return view;
}
var newView = new TerminalView(terminal);
_viewDictionary.Add(terminal.Id, newView);
return newView;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,27 @@
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;
}
}
}
+174 -14
View File
@@ -19,7 +19,7 @@
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<PackageCertificateKeyFile>FluentTerminal.App_StoreKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8FA8EF727E767FAE5409DDD1694C1CEE054A2962</PackageCertificateThumbprint>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
</PropertyGroup>
@@ -99,6 +99,22 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\FalseToVisibleConverter.cs" />
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
<Compile Include="Converters\TrueToVisibleConverter.cs" />
<Compile Include="Utilities\ApplicationDataContainerExtensions.cs" />
<Compile Include="Services\IDefaultValueProvider.cs" />
<Compile Include="Services\Implementation\DefaultValueProvider.cs" />
<Compile Include="Services\Implementation\SettingsService.cs" />
<Compile Include="Services\Implementation\TerminalService.cs" />
<Compile Include="Services\ISettingsService.cs" />
<Compile Include="Services\ITerminalService.cs" />
<Compile Include="Utilities\HttpJsonContent.cs" />
<Compile Include="Utilities\IHttpContentExtensions.cs" />
<Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModel.cs" />
<Compile Include="ViewModels\TerminalViewModel.cs" />
<Compile Include="Views\ITerminalView.cs" />
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
@@ -106,8 +122,23 @@
<Compile Include="Views\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TerminalPage.xaml.cs">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<Compile Include="Views\SettingsPages\ColorSettings.xaml.cs">
<DependentUpon>ColorSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\FontSettings.xaml.cs">
<DependentUpon>FontSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\GeneralSettings.xaml.cs">
<DependentUpon>GeneralSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\KeyBindingSettings.xaml.cs">
<DependentUpon>KeyBindingSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\ShellSettings.xaml.cs">
<DependentUpon>ShellSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TerminalView.xaml.cs">
<DependentUpon>TerminalView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
@@ -116,6 +147,8 @@
</AppxManifest>
<None Include="FluentTerminal.App_StoreKey.pfx" />
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
<Content Include="Win32\FluentTerminal.Models.dll" />
<Content Include="Win32\FluentTerminal.Models.pdb" />
<Content Include="Win32\FluentTerminal.SystemTray.exe.config" />
</ItemGroup>
<ItemGroup>
@@ -166,13 +199,6 @@
<Content Include="Assets\AppIcons\Wide310x150Logo.scale-400.png" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
<Content Include="Win32\Client\bundle.js" />
<Content Include="Win32\Client\index.html" />
<Content Include="Win32\Client\style.css" />
@@ -187,11 +213,107 @@
<Content Include="Win32\Microsoft.Owin.Host.HttpListener.dll" />
<Content Include="Win32\Microsoft.Owin.Hosting.dll" />
<Content Include="Win32\Microsoft.Owin.StaticFiles.dll" />
<Content Include="Win32\Microsoft.Win32.Primitives.dll" />
<Content Include="Win32\netstandard.dll" />
<Content Include="Win32\Newtonsoft.Json.dll" />
<Content Include="Win32\Owin.dll" />
<Content Include="Win32\System.AppContext.dll" />
<Content Include="Win32\System.Collections.Concurrent.dll" />
<Content Include="Win32\System.Collections.dll" />
<Content Include="Win32\System.Collections.NonGeneric.dll" />
<Content Include="Win32\System.Collections.Specialized.dll" />
<Content Include="Win32\System.ComponentModel.dll" />
<Content Include="Win32\System.ComponentModel.EventBasedAsync.dll" />
<Content Include="Win32\System.ComponentModel.Primitives.dll" />
<Content Include="Win32\System.ComponentModel.TypeConverter.dll" />
<Content Include="Win32\System.Console.dll" />
<Content Include="Win32\System.Data.Common.dll" />
<Content Include="Win32\System.Diagnostics.Contracts.dll" />
<Content Include="Win32\System.Diagnostics.Debug.dll" />
<Content Include="Win32\System.Diagnostics.FileVersionInfo.dll" />
<Content Include="Win32\System.Diagnostics.Process.dll" />
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
<Content Include="Win32\System.Diagnostics.TextWriterTraceListener.dll" />
<Content Include="Win32\System.Diagnostics.Tools.dll" />
<Content Include="Win32\System.Diagnostics.TraceSource.dll" />
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
<Content Include="Win32\System.Drawing.Primitives.dll" />
<Content Include="Win32\System.Dynamic.Runtime.dll" />
<Content Include="Win32\System.Globalization.Calendars.dll" />
<Content Include="Win32\System.Globalization.dll" />
<Content Include="Win32\System.Globalization.Extensions.dll" />
<Content Include="Win32\System.IO.Compression.dll" />
<Content Include="Win32\System.IO.Compression.ZipFile.dll" />
<Content Include="Win32\System.IO.dll" />
<Content Include="Win32\System.IO.FileSystem.dll" />
<Content Include="Win32\System.IO.FileSystem.DriveInfo.dll" />
<Content Include="Win32\System.IO.FileSystem.Primitives.dll" />
<Content Include="Win32\System.IO.FileSystem.Watcher.dll" />
<Content Include="Win32\System.IO.IsolatedStorage.dll" />
<Content Include="Win32\System.IO.MemoryMappedFiles.dll" />
<Content Include="Win32\System.IO.Pipes.dll" />
<Content Include="Win32\System.IO.UnmanagedMemoryStream.dll" />
<Content Include="Win32\System.Linq.dll" />
<Content Include="Win32\System.Linq.Expressions.dll" />
<Content Include="Win32\System.Linq.Parallel.dll" />
<Content Include="Win32\System.Linq.Queryable.dll" />
<Content Include="Win32\System.Net.Http.dll" />
<Content Include="Win32\System.Net.Http.Formatting.dll" />
<Content Include="Win32\System.Net.NameResolution.dll" />
<Content Include="Win32\System.Net.NetworkInformation.dll" />
<Content Include="Win32\System.Net.Ping.dll" />
<Content Include="Win32\System.Net.Primitives.dll" />
<Content Include="Win32\System.Net.Requests.dll" />
<Content Include="Win32\System.Net.Security.dll" />
<Content Include="Win32\System.Net.Sockets.dll" />
<Content Include="Win32\System.Net.WebHeaderCollection.dll" />
<Content Include="Win32\System.Net.WebSockets.Client.dll" />
<Content Include="Win32\System.Net.WebSockets.dll" />
<Content Include="Win32\System.ObjectModel.dll" />
<Content Include="Win32\System.Reflection.dll" />
<Content Include="Win32\System.Reflection.Extensions.dll" />
<Content Include="Win32\System.Reflection.Primitives.dll" />
<Content Include="Win32\System.Resources.Reader.dll" />
<Content Include="Win32\System.Resources.ResourceManager.dll" />
<Content Include="Win32\System.Resources.Writer.dll" />
<Content Include="Win32\System.Runtime.CompilerServices.VisualC.dll" />
<Content Include="Win32\System.Runtime.dll" />
<Content Include="Win32\System.Runtime.Extensions.dll" />
<Content Include="Win32\System.Runtime.Handles.dll" />
<Content Include="Win32\System.Runtime.InteropServices.dll" />
<Content Include="Win32\System.Runtime.InteropServices.RuntimeInformation.dll" />
<Content Include="Win32\System.Runtime.Numerics.dll" />
<Content Include="Win32\System.Runtime.Serialization.Formatters.dll" />
<Content Include="Win32\System.Runtime.Serialization.Json.dll" />
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
<Content Include="Win32\System.Runtime.Serialization.Xml.dll" />
<Content Include="Win32\System.Security.Claims.dll" />
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
<Content Include="Win32\System.Security.Cryptography.Csp.dll" />
<Content Include="Win32\System.Security.Cryptography.Encoding.dll" />
<Content Include="Win32\System.Security.Cryptography.Primitives.dll" />
<Content Include="Win32\System.Security.Cryptography.X509Certificates.dll" />
<Content Include="Win32\System.Security.Principal.dll" />
<Content Include="Win32\System.Security.SecureString.dll" />
<Content Include="Win32\System.Text.Encoding.dll" />
<Content Include="Win32\System.Text.Encoding.Extensions.dll" />
<Content Include="Win32\System.Text.RegularExpressions.dll" />
<Content Include="Win32\System.Threading.dll" />
<Content Include="Win32\System.Threading.Overlapped.dll" />
<Content Include="Win32\System.Threading.Tasks.dll" />
<Content Include="Win32\System.Threading.Tasks.Parallel.dll" />
<Content Include="Win32\System.Threading.Thread.dll" />
<Content Include="Win32\System.Threading.ThreadPool.dll" />
<Content Include="Win32\System.Threading.Timer.dll" />
<Content Include="Win32\System.ValueTuple.dll" />
<Content Include="Win32\System.Web.Http.dll" />
<Content Include="Win32\System.Web.Http.Owin.dll" />
<Content Include="Win32\System.Xml.ReaderWriter.dll" />
<Content Include="Win32\System.Xml.XDocument.dll" />
<Content Include="Win32\System.Xml.XmlDocument.dll" />
<Content Include="Win32\System.Xml.XmlSerializer.dll" />
<Content Include="Win32\System.Xml.XPath.dll" />
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
<Content Include="Win32\Unity.Abstractions.dll" />
<Content Include="Win32\Unity.Abstractions.pdb" />
<Content Include="Win32\Unity.Configuration.dll" />
@@ -230,29 +352,67 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\TerminalPage.xaml">
<Page Include="Views\SettingsPages\ColorSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\FontSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\GeneralSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\KeyBindingSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\ShellSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\TerminalView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac">
<Version>4.6.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.0.6</Version>
</PackageReference>
<PackageReference Include="MvvmLightLibs">
<Version>5.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>10.0.3</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<SDKReference Include="WindowsDesktop, Version=10.0.16299.0">
<Name>Windows Desktop Extensions for the UWP</Name>
</SDKReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.RuntimeComponent\FluentTerminal.RuntimeComponent.csproj">
<Project>{BAAE6239-343F-454C-977F-F81F91715961}</Project>
<Name>FluentTerminal.RuntimeComponent</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>mkdir "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32" 2&gt; NULL
xcopy /y /s "$(SolutionDir)FluentTerminal.SystemTray\bin\$(PlatformName)\$(ConfigurationName)" "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32"
</PostBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
+8 -3
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.3.0" />
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.4.0" />
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Fluent Terminal</DisplayName>
@@ -23,13 +23,18 @@
<uap:ShowOn Tile="square310x310Logo" />
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" />
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.appService">
<uap:AppService Name="FluentTerminalAppService" />
</uap:Extension>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
<uap5:Extension Category="windows.appExecutionAlias" Executable="FluentTerminal.App.exe" EntryPoint="FluentTerminal.App.App">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="flute.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
</Extensions>
</Application>
</Applications>
@@ -0,0 +1,9 @@
using FluentTerminal.Models;
namespace FluentTerminal.App.Services
{
public interface IDefaultValueProvider
{
ShellConfiguration GetDefaultShellConfiguration();
}
}
@@ -0,0 +1,10 @@
using FluentTerminal.Models;
namespace FluentTerminal.App.Services
{
public interface ISettingsService
{
ShellConfiguration GetShellConfiguration();
void SaveShellConfiguration(ShellConfiguration spawnConfiguration);
}
}
@@ -0,0 +1,11 @@
using FluentTerminal.Models;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface ITerminalService
{
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration);
Task ResizeTerminal(int id, TerminalSize size);
}
}
@@ -0,0 +1,19 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
namespace FluentTerminal.App.Services.Implementation
{
internal class DefaultValueProvider : IDefaultValueProvider
{
public ShellConfiguration GetDefaultShellConfiguration()
{
return new ShellConfiguration
{
Shell = ShellType.PowerShell,
CustomShellLocation = string.Empty,
WorkingDirectory = string.Empty,
Arguments = string.Empty
};
}
}
}
@@ -0,0 +1,38 @@
using FluentTerminal.App.Utilities;
using FluentTerminal.Models;
using Windows.Storage;
namespace FluentTerminal.App.Services.Implementation
{
internal class SettingsService : ISettingsService
{
private readonly IDefaultValueProvider _defaultValueProvider;
private ApplicationDataContainer _localSettings;
private ApplicationDataContainer _roamingSettings;
public SettingsService(IDefaultValueProvider defaultValueProvider)
{
_defaultValueProvider = defaultValueProvider;
_localSettings = ApplicationData.Current.LocalSettings;
_roamingSettings = ApplicationData.Current.RoamingSettings;
ApplicationData.Current.DataChanged += OnDataChanged;
}
private void OnDataChanged(ApplicationData sender, object args)
{
//todo handle data changed for roaming settings
}
public ShellConfiguration GetShellConfiguration()
{
return _localSettings.ReadValueFromJson(nameof(ShellConfiguration), _defaultValueProvider.GetDefaultShellConfiguration());
}
public void SaveShellConfiguration(ShellConfiguration spawnConfiguration)
{
_localSettings.WriteValueAsJson(nameof(ShellConfiguration), spawnConfiguration);
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Threading.Tasks;
using FluentTerminal.App.Utilities;
using FluentTerminal.Models;
using Windows.Web.Http;
namespace FluentTerminal.App.Services.Implementation
{
internal class TerminalService : ITerminalService
{
private readonly HttpClient _httpClient;
public TerminalService()
{
_httpClient = new HttpClient();
}
public async Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration)
{
var request = new CreateTerminalRequest
{
Size = size,
Configuration = shellConfiguration
};
var response = await _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals"), HttpJsonContent.From(request));
var createTerminalResponse = await response.Content.ReadAs<CreateTerminalResponse>();
return createTerminalResponse;
}
public Task ResizeTerminal(int id, TerminalSize size)
{
return _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals/{id}/resize"), HttpJsonContent.From(size)).AsTask();
}
}
}
@@ -0,0 +1,23 @@
using Newtonsoft.Json;
using Windows.Storage;
namespace FluentTerminal.App.Utilities
{
public static class ApplicationDataContainerExtensions
{
public static T ReadValueFromJson<T>(this ApplicationDataContainer container, string name, T fallbackValue)
{
if (container.Values.TryGetValue(name, out object value))
{
JsonConvert.PopulateObject((string)value, fallbackValue);
}
return fallbackValue;
}
public static void WriteValueAsJson<T>(this ApplicationDataContainer container, string name, T value)
{
var serialized = JsonConvert.SerializeObject(value);
container.Values[name] = serialized;
}
}
}
@@ -0,0 +1,14 @@
using Newtonsoft.Json;
using Windows.Web.Http;
namespace FluentTerminal.App.Utilities
{
public static class HttpJsonContent
{
public static HttpStringContent From(object content)
{
var serialized = JsonConvert.SerializeObject(content);
return new HttpStringContent(serialized, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json");
}
}
}
@@ -0,0 +1,16 @@
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
using Windows.Web.Http;
namespace FluentTerminal.App.Utilities
{
public static class IHttpContentExtensions
{
public static async Task<T> ReadAs<T>(this IHttpContent content)
{
var contentBody = await content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(contentBody);
}
}
}
@@ -0,0 +1,65 @@
using FluentTerminal.App.Services;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels
{
public class MainViewModel : ViewModelBase
{
private readonly ISettingsService _settingsService;
private readonly ITerminalService _terminalService;
private TerminalViewModel _selectedTerminal;
private string _title;
public MainViewModel(ISettingsService settingsService, ITerminalService terminalService)
{
_settingsService = settingsService;
_terminalService = terminalService;
Title = "Fluent Terminal";
AddTerminalCommand = new RelayCommand(AddTerminal);
ShowSettingsCommand = new RelayCommand(async () => await ShowSettings());
AddTerminal();
}
public RelayCommand AddTerminalCommand { get; }
public RelayCommand ShowSettingsCommand { get; }
public TerminalViewModel SelectedTerminal
{
get => _selectedTerminal;
set => Set(ref _selectedTerminal, value);
}
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
public string Title
{
get => _title;
set => Set(ref _title, value);
}
private void AddTerminal()
{
var terminal = new TerminalViewModel(_settingsService, _terminalService);
terminal.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TerminalViewModel.Title) && Terminals.Count == 1)
{
Title = terminal.Title;
}
};
Terminals.Add(terminal);
SelectedTerminal = terminal;
}
private Task ShowSettings()
{
return App.Instance.ShowSettings();
}
}
}
@@ -0,0 +1,157 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Threading.Tasks;
using Windows.Storage.Pickers;
using Windows.UI.Popups;
namespace FluentTerminal.App.ViewModels
{
public class SettingsViewModel : ViewModelBase
{
private readonly ISettingsService _settingsService;
private readonly IDefaultValueProvider _defaultValueProvider;
private ShellConfiguration _shellConfiguration;
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider)
{
_settingsService = settingsService;
_defaultValueProvider = defaultValueProvider;
BrowseForCustomShellCommand = new RelayCommand(async () => await BrowseForCustomShell());
BrowseForWorkingDirectoryCommand = new RelayCommand(async () => await BrowseForWorkingDirectory());
RestoreDefaultsCommand = new RelayCommand<string>(async (area) => await RestoreDefaults(area));
_shellConfiguration = _settingsService.GetShellConfiguration();
ShellType = _shellConfiguration.Shell;
}
public RelayCommand BrowseForCustomShellCommand { get; }
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
public RelayCommand<string> RestoreDefaultsCommand { get; }
public bool CMDIsSelected
{
get => ShellType == ShellType.CMD;
set => ShellType = ShellType.CMD;
}
public bool CustomShellIsSelected
{
get => ShellType == ShellType.Custom;
set => ShellType = ShellType.Custom;
}
public string CustomShellLocation
{
get => _shellConfiguration.CustomShellLocation;
set
{
if (_shellConfiguration.CustomShellLocation != value)
{
_shellConfiguration.CustomShellLocation = value;
_settingsService.SaveShellConfiguration(_shellConfiguration);
RaisePropertyChanged();
}
}
}
public string WorkingDirectory
{
get => _shellConfiguration.WorkingDirectory;
set
{
if (_shellConfiguration.WorkingDirectory != value)
{
_shellConfiguration.WorkingDirectory = value;
_settingsService.SaveShellConfiguration(_shellConfiguration);
RaisePropertyChanged();
}
}
}
public string Arguments
{
get => _shellConfiguration.Arguments;
set
{
if (_shellConfiguration.Arguments != value)
{
_shellConfiguration.Arguments = value;
_settingsService.SaveShellConfiguration(_shellConfiguration);
RaisePropertyChanged();
}
}
}
public bool PoweShellIsSelected
{
get => ShellType == ShellType.PowerShell;
set => ShellType = ShellType.PowerShell;
}
public ShellType ShellType
{
get => _shellConfiguration.Shell;
set
{
if (_shellConfiguration.Shell != value)
{
_shellConfiguration.Shell = value;
_settingsService.SaveShellConfiguration(_shellConfiguration);
RaisePropertyChanged();
RaisePropertyChanged(nameof(CustomShellIsSelected));
RaisePropertyChanged(nameof(PoweShellIsSelected));
RaisePropertyChanged(nameof(CMDIsSelected));
}
}
}
private async Task BrowseForCustomShell()
{
var picker = new FileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
picker.FileTypeFilter.Add(".exe");
var file = await picker.PickSingleFileAsync().AsTask();
if (file != null)
{
CustomShellLocation = file.Path;
}
}
private async Task BrowseForWorkingDirectory()
{
var picker = new FolderPicker();
picker.FileTypeFilter.Add(".whatever"); // else a ComException is thrown
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
var folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
WorkingDirectory = folder.Path;
}
}
private async Task RestoreDefaults(string area)
{
var dialog = new MessageDialog("Are you sure you want to restore default values for this page?", "Please confirm");
dialog.Commands.Add(new UICommand("OK"));
dialog.Commands.Add(new UICommand("Cancel"));
var result = await dialog.ShowAsync();
if (result.Label == "OK" && area == "shell")
{
var defaults = _defaultValueProvider.GetDefaultShellConfiguration();
ShellType = defaults.Shell;
CustomShellLocation = defaults.CustomShellLocation;
WorkingDirectory = defaults.WorkingDirectory;
Arguments = defaults.Arguments;
}
}
}
}
@@ -0,0 +1,112 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Views;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using System;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml;
namespace FluentTerminal.App.ViewModels
{
public class TerminalViewModel : ViewModelBase
{
private ITerminalView _terminalView;
private string _title;
private string _resizeOverlayContent;
private bool _showResizeOverlay;
private DispatcherTimer _resizeOverlayTimer;
private readonly ISettingsService _settingsService;
private readonly ITerminalService _terminalService;
private int _terminalId;
public int Id { get; private set; }
public bool Initialized { get; private set; }
public string Title
{
get => _title;
set => Set(ref _title, value);
}
public bool ShowResizeOverlay
{
get => _showResizeOverlay;
set
{
Set(ref _showResizeOverlay, value);
if (value)
{
if (_resizeOverlayTimer.IsEnabled)
{
_resizeOverlayTimer.Stop();
}
_resizeOverlayTimer.Start();
}
}
}
public string ResizeOverlayContent
{
get => _resizeOverlayContent;
set => Set(ref _resizeOverlayContent, value);
}
public TerminalViewModel(ISettingsService settingsService, ITerminalService terminalService)
{
_settingsService = settingsService;
_terminalService = terminalService;
_resizeOverlayTimer = new DispatcherTimer();
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
}
private void OnResizeOverlayTimerFinished(object sender, object e)
{
_resizeOverlayTimer.Stop();
ShowResizeOverlay = false;
}
public async Task OnViewIsReady(ITerminalView terminalView)
{
_terminalView = terminalView;
var size = await _terminalView.CreateTerminal();
var configuration = _settingsService.GetShellConfiguration();
var response = await _terminalService.CreateTerminal(size, configuration);
if (response.Success)
{
_terminalId = response.Id;
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
await _terminalView.ConnectToSocket(response.WebSocketUrl);
Initialized = true;
}
else
{
var dialog = new MessageDialog(response.Error, "Error");
await dialog.ShowAsync();
}
}
private void OnTerminalTitleChanged(object sender, string e)
{
Title = e;
}
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
{
if (!Initialized)
{
return;
}
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
ShowResizeOverlay = true;
await _terminalService.ResizeTerminal(_terminalId, e);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using FluentTerminal.Models;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.Views
{
public interface ITerminalView
{
event EventHandler<TerminalSize> TerminalSizeChanged;
event EventHandler<string> TerminalTitleChanged;
Task<TerminalSize> CreateTerminal();
Task ConnectToSocket(string url);
}
}
+15 -5
View File
@@ -2,11 +2,15 @@
x:Class="FluentTerminal.App.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:FluentTerminal.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource SystemControlAcrylicWindowBrush}"
RequestedTheme="Dark"
mc:Ignorable="d">
<Page.Resources>
<converters:TerminalViewModelToViewConverter x:Key="TerminalViewModelToViewConverter" />
</Page.Resources>
<Grid x:Name="Root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -27,19 +31,25 @@
<TextBlock
Grid.Column="0"
Canvas.ZIndex="9"
IsHitTestVisible="False"
Style="{StaticResource WindowTitleTextBlockStyle}"
Text="Fluent Terminal" />
<!--<Button
Text="{x:Bind ViewModel.Title, Mode=OneWay}" />
<Button
Grid.Column="2"
Click="OnSettingsButtonClick"
Command="{x:Bind ViewModel.ShowSettingsCommand}"
Style="{StaticResource TitleBarButtonStyle}">
<Viewbox Width="16" Height="16">
<SymbolIcon Symbol="Setting" />
</Viewbox>
</Button>-->
</Button>
</Grid>
</Grid>
</Grid>
<Frame x:Name="RootFrame" Grid.Row="1" />
<ContentControl
x:Name="TerminalContainer"
Grid.Row="1"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Content="{x:Bind ViewModel.SelectedTerminal, Mode=OneWay, Converter={StaticResource TerminalViewModelToViewConverter}}" />
</Grid>
</Page>
+21 -8
View File
@@ -1,10 +1,13 @@
using System.ComponentModel;
using FluentTerminal.App.ViewModels;
using System.ComponentModel;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Core.Preview;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views
{
@@ -31,6 +34,8 @@ namespace FluentTerminal.App.Views
}
}
public MainViewModel ViewModel { get; private set; }
public MainPage()
{
InitializeComponent();
@@ -40,11 +45,25 @@ namespace FluentTerminal.App.Views
Loaded += OnLoaded;
Unloaded += OnUnloaded;
Window.Current.Activated += OnWindowActivated;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is MainViewModel viewModel)
{
ViewModel = viewModel;
}
}
private void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
App.Instance.TerminalWindowClosed();
}
private async void OnWindowActivated(object sender, WindowActivatedEventArgs e)
{
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && RootFrame.Content is TerminalPage terminal)
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && TerminalContainer.Content is TerminalView terminal)
{
await terminal.FocusWebView();
}
@@ -59,7 +78,6 @@ namespace FluentTerminal.App.Views
{
coreTitleBar.LayoutMetricsChanged += OnLayoutMetricsChanged;
UpdateLayoutMetrics();
RootFrame.Navigate(typeof(TerminalPage));
}
private void OnLayoutMetricsChanged(CoreApplicationViewTitleBar sender, object e)
@@ -88,10 +106,5 @@ namespace FluentTerminal.App.Views
PropertyChanged(this, new PropertyChangedEventArgs(nameof(CoreTitleBarPadding)));
}
}
private async void OnSettingsButtonClick(object sender, RoutedEventArgs e)
{
await App.Instance.ShowSettings();
}
}
}
+88 -5
View File
@@ -2,12 +2,95 @@
x:Class="FluentTerminal.App.Views.SettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:FluentTerminal.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:FluentTerminal.App.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Settings" />
<Page.Resources>
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
<converters:FalseToVisibleConverter x:Key="FalseToVisibleConverter" />
</Page.Resources>
<Grid x:Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock
x:Name="AppTitle"
Canvas.ZIndex="1"
IsHitTestVisible="False"
Style="{StaticResource CaptionTextBlockStyle}"
Text="Settings"
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource TrueToVisibleConverter}}" />
<NavigationView
x:Name="NavigationView"
Canvas.ZIndex="0"
CompactModeThresholdWidth="560"
ExpandedModeThresholdWidth="560"
IsSettingsVisible="False"
Loaded="NavigationView_Loaded"
OpenPaneLength="280"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItems>
<NavigationViewItem
Content="General"
Icon="Setting"
Tag="general" />
<NavigationViewItem
Content="Key Bindings"
Icon="Setting"
Tag="keyBindings" />
<NavigationViewItem
Content="Shell"
Icon="Setting"
Tag="shell" />
<NavigationViewItem
Content="Font"
Icon="Setting"
Tag="font" />
<NavigationViewItem
Content="Colors"
Icon="Setting"
Tag="colors" />
</NavigationView.MenuItems>
<NavigationView.HeaderTemplate>
<DataTemplate>
<Grid Margin="24,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding CoreTitleBarHeight}" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Margin="0,-2,0,0"
IsHitTestVisible="False"
Style="{StaticResource CaptionTextBlockStyle}"
Text="Settings"
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource FalseToVisibleConverter}}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="{Binding SelectedItem.Content, ElementName=NavigationView}" />
<CommandBar
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
DefaultLabelPosition="Right">
<AppBarButton
Command="{Binding ViewModel.RestoreDefaultsCommand}"
CommandParameter="{Binding SelectedItem.Tag, ElementName=NavigationView}"
Icon="Undo"
Label="Restore Defaults" />
</CommandBar>
</Grid>
</DataTemplate>
</NavigationView.HeaderTemplate>
<Frame x:Name="ContentFrame" Margin="24" />
</NavigationView>
</Grid>
</Page>
</Page>
+77 -2
View File
@@ -1,12 +1,87 @@
using Windows.UI.Xaml.Controls;
using FluentTerminal.App.ViewModels;
using FluentTerminal.App.Views.SettingsPages;
using System.Linq;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.UI.Core.Preview;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views
{
public sealed partial class SettingsPage : Page
{
public double CoreTitleBarHeight { get; }
public SettingsViewModel ViewModel { get; private set; }
public SettingsPage()
{
this.InitializeComponent();
InitializeComponent();
Root.DataContext = this;
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonForegroundColor = (Color)this.Resources["SystemBaseHighColor"];
ApplicationView.GetForCurrentView().Title = "Settings";
CoreApplicationViewTitleBar coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.LayoutMetricsChanged += TitleBar_LayoutMetricsChanged;
CoreTitleBarHeight = coreTitleBar.Height;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += SettingsPage_CloseRequested;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is SettingsViewModel viewModel)
{
ViewModel = viewModel;
}
}
private void SettingsPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
App.Instance.SettingsWindowClosed();
}
private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
AppTitle.Margin = new Thickness(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset + 12, 8, 0, 0);
}
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
{
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "shell");
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is NavigationViewItem item)
{
switch (item.Tag)
{
case "general":
ContentFrame.Navigate(typeof(GeneralSettings), ViewModel);
break;
case "shell":
ContentFrame.Navigate(typeof(ShellSettings), ViewModel);
break;
case "colors":
ContentFrame.Navigate(typeof(ColorSettings), ViewModel);
break;
case "font":
ContentFrame.Navigate(typeof(FontSettings), ViewModel);
break;
case "keyBindings":
ContentFrame.Navigate(typeof(KeyBindingSettings), ViewModel);
break;
}
}
}
}
}
@@ -0,0 +1,11 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.ColorSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
</Page>
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
namespace FluentTerminal.App.Views.SettingsPages
{
/// <summary>
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
/// </summary>
public sealed partial class ColorSettings : Page
{
public ColorSettings()
{
this.InitializeComponent();
}
}
}
@@ -0,0 +1,11 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.FontSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
</Page>
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
namespace FluentTerminal.App.Views.SettingsPages
{
/// <summary>
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
/// </summary>
public sealed partial class FontSettings : Page
{
public FontSettings()
{
this.InitializeComponent();
}
}
}
@@ -1,9 +1,10 @@
<Page
x:Class="FluentTerminal.App.Views.TerminalPage"
x:Class="FluentTerminal.App.Views.SettingsPages.GeneralSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
NavigationCacheMode="Required"
mc:Ignorable="d">
<Grid x:Name="WebViewContainer" Margin="12" />
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
</Page>
@@ -0,0 +1,12 @@
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class GeneralSettings : Page
{
public GeneralSettings()
{
this.InitializeComponent();
}
}
}
@@ -0,0 +1,11 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.KeyBindingSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
</Page>
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
namespace FluentTerminal.App.Views.SettingsPages
{
/// <summary>
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
/// </summary>
public sealed partial class KeyBindingSettings : Page
{
public KeyBindingSettings()
{
this.InitializeComponent();
}
}
}
@@ -0,0 +1,52 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.ShellSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
NavigationCacheMode="Required"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Margin="0,0,0,8" Text="Shell Type" />
<RadioButton Content="PowerShell" IsChecked="{Binding ViewModel.PoweShellIsSelected, Mode=TwoWay}" />
<RadioButton Content="CMD" IsChecked="{Binding ViewModel.CMDIsSelected, Mode=TwoWay}" />
<RadioButton Content="Custom" IsChecked="{Binding ViewModel.CustomShellIsSelected, Mode=TwoWay}" />
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
<TextBox
Width="400"
Header="Custom Shell Location"
IsEnabled="{x:Bind ViewModel.CustomShellIsSelected, Mode=OneWay}"
Text="{x:Bind ViewModel.CustomShellLocation, Mode=TwoWay}" />
<Button
Margin="6,0,0,0"
VerticalAlignment="Bottom"
Command="{x:Bind ViewModel.BrowseForCustomShellCommand}"
Content="..."
IsEnabled="{x:Bind ViewModel.CustomShellIsSelected, Mode=OneWay}"
Style="{StaticResource ButtonRevealStyle}" />
</StackPanel>
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
<TextBox
Width="400"
Header="Working Directory"
Text="{x:Bind ViewModel.WorkingDirectory, Mode=TwoWay}" />
<Button
Margin="6,0,0,0"
VerticalAlignment="Bottom"
Command="{x:Bind ViewModel.BrowseForWorkingDirectoryCommand}"
Content="..."
Style="{StaticResource ButtonRevealStyle}" />
</StackPanel>
<TextBox
Width="400"
Margin="0,24,0,0"
HorizontalAlignment="Left"
Header="Arguments"
Text="{x:Bind ViewModel.Arguments, Mode=TwoWay}" />
</StackPanel>
</Page>
@@ -0,0 +1,24 @@
using FluentTerminal.App.ViewModels;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class ShellSettings : Page
{
public SettingsViewModel ViewModel { get; private set; }
public ShellSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is SettingsViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -1,65 +0,0 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Core.Preview;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class TerminalPage : Page
{
private WebView _webView;
public TerminalPage()
{
InitializeComponent();
Loaded += OnLoaded;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested;
}
public async Task FocusWebView()
{
if (_webView != null)
{
_webView.Focus(FocusState.Programmatic);
await ExecuteScriptAsync("document.focus();");
}
}
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
_webView.Navigate(new Uri("about:blank"));
_webView = null;
}
public Task<string> ExecuteScriptAsync(string script)
{
try
{
return _webView.InvokeScriptAsync("eval", new[]
{
"try {" + script + "} catch(error) {}"
}).AsTask();
}
catch (Exception e)
{
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
}
return Task.FromResult(string.Empty);
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
_webView = new WebView(WebViewExecutionMode.SeparateThread)
{
DefaultBackgroundColor = Colors.Transparent
};
WebViewContainer.Children.Add(_webView);
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
_webView.Focus(FocusState.Programmatic);
}
}
}
@@ -0,0 +1,27 @@
<UserControl
x:Class="FluentTerminal.App.Views.TerminalView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:FluentTerminal.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:FluentTerminal.App.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources>
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
</UserControl.Resources>
<Grid>
<Grid x:Name="WebViewContainer" Margin="12" />
<Grid
Margin="12"
Padding="6"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Background="{ThemeResource SystemControlAccentAcrylicWindowAccentMediumHighBrush}"
Visibility="{x:Bind ViewModel.ShowResizeOverlay, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<TextBlock Foreground="White" Text="{x:Bind ViewModel.ResizeOverlayContent, Mode=OneWay}" />
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,143 @@
using FluentTerminal.App.ViewModels;
using FluentTerminal.Models;
using FluentTerminal.RuntimeComponent.Interfaces;
using FluentTerminal.RuntimeComponent.WebAllowedObjects;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Core.Preview;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class TerminalView : UserControl, ITerminalView, ITerminalEventListener
{
private WebView _webView;
private SemaphoreSlim _loaded;
private BlockingCollection<Action> _dispatcherJobs;
public event EventHandler<TerminalSize> TerminalSizeChanged;
public event EventHandler<string> TerminalTitleChanged;
public TerminalViewModel ViewModel { get; }
public TerminalView(TerminalViewModel viewModel)
{
ViewModel = viewModel;
InitializeComponent();
Loaded += OnLoaded;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested; //todo move to mainpage
_loaded = new SemaphoreSlim(0, 1);
StartMediatorTask();
}
public async Task FocusWebView()
{
if (_webView != null)
{
_webView.Focus(FocusState.Programmatic);
await ExecuteScriptAsync("document.focus();");
}
}
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
_webView.Navigate(new Uri("about:blank"));
_webView = null;
}
public Task<string> ExecuteScriptAsync(string script)
{
try
{
return _webView.InvokeScriptAsync("eval", new[] { script }).AsTask();
}
catch (Exception e)
{
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
}
return Task.FromResult(string.Empty);
}
private void StartMediatorTask()
{
_dispatcherJobs = new BlockingCollection<Action>();
var dispatcher = CoreApplication.GetCurrentView().CoreWindow.Dispatcher;
Task.Run(async () =>
{
foreach (var job in _dispatcherJobs.GetConsumingEnumerable())
{
try
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => job.Invoke());
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
});
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
_webView = new WebView(WebViewExecutionMode.SeparateThread)
{
DefaultBackgroundColor = Colors.Transparent
};
WebViewContainer.Children.Add(_webView);
_webView.NavigationCompleted += _webView_NavigationCompleted;
_webView.NavigationStarting += _webView_NavigationStarting;
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
await _loaded.WaitAsync();
_webView.Focus(FocusState.Programmatic);
await ViewModel.OnViewIsReady(this);
}
private void _webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
_loaded.Release();
}
private void _webView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
var bridge = new TerminalBridge(this);
_webView.AddWebAllowedObject("terminalBridge", bridge);
}
public async Task<TerminalSize> CreateTerminal()
{
var size = await ExecuteScriptAsync($"createTerminal()");
return JsonConvert.DeserializeObject<TerminalSize>(size);
}
public Task ConnectToSocket(string url)
{
return ExecuteScriptAsync($"connectToWebSocket('{url}');");
}
public void OnTerminalResized(int columns, int rows)
{
_dispatcherJobs.Add(() => TerminalSizeChanged?.Invoke(this, new TerminalSize { Columns = columns, Rows = rows }));
}
public void OnTitleChanged(string title)
{
_dispatcherJobs.Add(() => TerminalTitleChanged?.Invoke(this, title));
}
}
}
+34 -22
View File
@@ -86,49 +86,49 @@ __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPO
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_2__xterm_js_build_addons_fit_fit__);
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_3__xterm_js_build_addons_winptyCompat_winptyCompat__);
var term, protocol, socketURL, socket;
var term, socket;
var terminalContainer = document.getElementById('terminal-container');
createTerminal();
function createTerminal() {
function start() {
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
term = new __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__({
cursorBlink: true,
fontFamily: 'consolas',
fontSize: 13,
allowTransparency: true,
theme: {
background: "transparent"
background: "transparent"
}
});
window.term = term; // Expose `term` to window for debugging purposes
window.term = term;
term.on('resize', function (size) {
if (!socketURL) {
return;
}
var cols = size.cols,
rows = size.rows,
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'});
terminalBridge.notifySizeChanged(term.cols, term.rows);
});
term.on('title', function (title) {
terminalBridge.notifyTitleChanged(title);
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
term.open(terminalContainer);
term.winptyCompatInit();
term.fit();
term.focus();
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
console.log("got response");
res.text().then(function (url) {
socketURL = JSON.parse(url);
socket = new WebSocket(socketURL);
socket.onopen = runRealTerminal;
});
});
var resizeTimeout;
window.onresize = function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(term.fit(), 500);
}
return JSON.stringify({
rows: term.rows,
columns: term.cols
});
}
function runRealTerminal() {
@@ -136,6 +136,18 @@ function runRealTerminal() {
term._initialized = true;
}
function createTerminal(configuration) {
return start();
}
function connectToWebSocket(url) {
socket = new WebSocket(url);
socket.onopen = runRealTerminal;
}
window.createTerminal = createTerminal;
window.connectToWebSocket = connectToWebSocket;
/***/ }),
/* 1 */
+34 -22
View File
@@ -7,52 +7,64 @@ Terminal.applyAddon(attach);
Terminal.applyAddon(fit);
Terminal.applyAddon(winptyCompat);
var term, protocol, socketURL, socket;
var term, socket;
var terminalContainer = document.getElementById('terminal-container');
createTerminal();
function createTerminal() {
function start() {
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
term = new Terminal({
cursorBlink: true,
fontFamily: 'consolas',
fontSize: 13,
allowTransparency: true,
theme: {
background: "transparent"
background: "transparent"
}
});
window.term = term; // Expose `term` to window for debugging purposes
window.term = term;
term.on('resize', function (size) {
if (!socketURL) {
return;
}
var cols = size.cols,
rows = size.rows,
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'});
terminalBridge.notifySizeChanged(term.cols, term.rows);
});
term.on('title', function (title) {
terminalBridge.notifyTitleChanged(title);
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
term.open(terminalContainer);
term.winptyCompatInit();
term.fit();
term.focus();
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
console.log("got response");
res.text().then(function (url) {
socketURL = JSON.parse(url);
socket = new WebSocket(socketURL);
socket.onopen = runRealTerminal;
});
});
var resizeTimeout;
window.onresize = function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(term.fit(), 500);
}
return JSON.stringify({
rows: term.rows,
columns: term.cols
});
}
function runRealTerminal() {
term.attach(socket);
term._initialized = true;
}
function createTerminal(configuration) {
return start();
}
function connectToWebSocket(url) {
socket = new WebSocket(url);
socket.onopen = runRealTerminal;
}
window.createTerminal = createTerminal;
window.connectToWebSocket = connectToWebSocket;
@@ -0,0 +1,8 @@
namespace FluentTerminal.Models
{
public class CreateTerminalRequest
{
public TerminalSize Size { get; set; }
public ShellConfiguration Configuration { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace FluentTerminal.Models
{
public class CreateTerminalResponse
{
public int Id { get; set; }
public bool Success { get; set; }
public string WebSocketUrl { get; set; }
public string Error { get; set; }
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace FluentTerminal.Models.Enums
{
public enum ShellType
{
CMD,
PowerShell,
Custom
}
}
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
@@ -0,0 +1,12 @@
using FluentTerminal.Models.Enums;
namespace FluentTerminal.Models
{
public class ShellConfiguration
{
public string Arguments { get; set; }
public ShellType Shell { get; set; }
public string CustomShellLocation { get; set; }
public string WorkingDirectory { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace FluentTerminal.Models
{
public class TerminalSize
{
public int Columns { get; set; }
public int Rows { get; set; }
}
}
@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{BAAE6239-343F-454C-977F-F81F91715961}</ProjectGuid>
<OutputType>winmdobj</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FluentTerminal.RuntimeComponent</RootNamespace>
<AssemblyName>FluentTerminal.RuntimeComponent</AssemblyName>
<DefaultLanguage>de-DE</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.16299.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AllowCrossPlatformRetargeting>false</AllowCrossPlatformRetargeting>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="Interfaces\ITerminalEventListener.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WebAllowedObjects\TerminalBridge.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.0.6</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FluentTerminal.RuntimeComponent.Interfaces
{
public interface ITerminalEventListener
{
void OnTerminalResized(int columns, int rows);
void OnTitleChanged(string title);
}
}
@@ -0,0 +1,29 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FluentTerminal.RuntimeComponent")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FluentTerminal.RuntimeComponent")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
@@ -0,0 +1,26 @@
using FluentTerminal.RuntimeComponent.Interfaces;
using Windows.Foundation.Metadata;
namespace FluentTerminal.RuntimeComponent.WebAllowedObjects
{
[AllowForWeb]
public sealed class TerminalBridge
{
private readonly ITerminalEventListener _terminalEventListener;
public TerminalBridge(ITerminalEventListener terminalEventListener)
{
_terminalEventListener = terminalEventListener;
}
public void NotifySizeChanged(int columns, int rows)
{
_terminalEventListener.OnTerminalResized(columns, rows);
}
public void NotifyTitleChanged(string title)
{
_terminalEventListener.OnTitleChanged(title);
}
}
}
+34 -22
View File
@@ -86,49 +86,49 @@ __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPO
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_2__xterm_js_build_addons_fit_fit__);
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_3__xterm_js_build_addons_winptyCompat_winptyCompat__);
var term, protocol, socketURL, socket;
var term, socket;
var terminalContainer = document.getElementById('terminal-container');
createTerminal();
function createTerminal() {
function start() {
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
term = new __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__({
cursorBlink: true,
fontFamily: 'consolas',
fontSize: 13,
allowTransparency: true,
theme: {
background: "transparent"
background: "transparent"
}
});
window.term = term; // Expose `term` to window for debugging purposes
window.term = term;
term.on('resize', function (size) {
if (!socketURL) {
return;
}
var cols = size.cols,
rows = size.rows,
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'});
terminalBridge.notifySizeChanged(term.cols, term.rows);
});
term.on('title', function (title) {
terminalBridge.notifyTitleChanged(title);
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
term.open(terminalContainer);
term.winptyCompatInit();
term.fit();
term.focus();
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
console.log("got response");
res.text().then(function (url) {
socketURL = JSON.parse(url);
socket = new WebSocket(socketURL);
socket.onopen = runRealTerminal;
});
});
var resizeTimeout;
window.onresize = function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(term.fit(), 500);
}
return JSON.stringify({
rows: term.rows,
columns: term.cols
});
}
function runRealTerminal() {
@@ -136,6 +136,18 @@ function runRealTerminal() {
term._initialized = true;
}
function createTerminal(configuration) {
return start();
}
function connectToWebSocket(url) {
socket = new WebSocket(url);
socket.onopen = runRealTerminal;
}
window.createTerminal = createTerminal;
window.connectToWebSocket = connectToWebSocket;
/***/ }),
/* 1 */
@@ -1,8 +1,6 @@
using FluentTerminal.SystemTray.DataTransferObjects;
using FluentTerminal.Models;
using FluentTerminal.SystemTray.Services;
using System;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
namespace FluentTerminal.SystemTray.Controllers
@@ -17,17 +15,17 @@ namespace FluentTerminal.SystemTray.Controllers
}
[HttpPost]
[Route("terminals")]
public string Post()
[Route("terminals/{id}/resize")]
public void Resize(int id, [FromBody]TerminalSize size)
{
var cols = int.Parse(this.Request.GetQueryNameValuePairs().SingleOrDefault(q => q.Key == "cols").Value);
var rows = int.Parse(this.Request.GetQueryNameValuePairs().SingleOrDefault(q => q.Key == "rows").Value);
var options = new TerminalOptions
{
Columns = cols,
Rows = rows
};
return _terminalsManager.CreateTerminal(options);
_terminalsManager.ResizeTerminal(id, size);
}
[HttpPost]
[Route("terminals")]
public CreateTerminalResponse Create([FromBody]CreateTerminalRequest request)
{
return _terminalsManager.CreateTerminal(request);
}
}
}
@@ -1,10 +0,0 @@
namespace FluentTerminal.SystemTray.DataTransferObjects
{
public class TerminalOptions
{
public int Rows { get; set; }
public int Columns { get; set; }
public string PWD { get; set; }
public string ShellExecutable { get; set; }
}
}
@@ -113,11 +113,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Controllers\TerminalsController.cs" />
<Compile Include="DataTransferObjects\TerminalOptions.cs" />
<Compile Include="LoopbackEnabler.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\Terminal.cs" />
<Compile Include="Services\TerminalSession.cs" />
<Compile Include="Services\TerminalsManager.cs" />
<Compile Include="Startup.cs" />
<Compile Include="SystemTrayApplicationContext.cs" />
@@ -186,7 +185,12 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>xcopy /y "$(ProjectDir)BinaryDependencies\$(PlatformName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)"
@@ -1,5 +1,6 @@
using Fleck;
using FluentTerminal.SystemTray.DataTransferObjects;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.IO;
using System.IO.Pipes;
@@ -10,23 +11,16 @@ using static winpty.WinPty;
namespace FluentTerminal.SystemTray.Services
{
public class Terminal : IDisposable
public class TerminalSession : IDisposable
{
private ManualResetEventSlim _connectedEvent;
private bool _disposedValue;
private IntPtr _handle;
private Stream _stdin;
private Stream _stdout;
private bool _disposedValue;
private ManualResetEventSlim _connectedEvent;
private IWebSocketConnection _webSocket;
public int Id { get; }
public string WebSocketUrl { get; }
public event EventHandler ConnectionClosed;
public Terminal(TerminalOptions options)
public TerminalSession(CreateTerminalRequest request)
{
var configHandle = IntPtr.Zero;
var spawnConfigHandle = IntPtr.Zero;
@@ -35,21 +29,21 @@ namespace FluentTerminal.SystemTray.Services
try
{
configHandle = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out errorHandle);
winpty_config_set_initial_size(configHandle, options.Columns, options.Rows);
winpty_config_set_initial_size(configHandle, request.Size.Columns, request.Size.Rows);
_handle = winpty_open(configHandle, out errorHandle);
if (errorHandle != IntPtr.Zero)
{
throw new Exception(winpty_error_code(errorHandle).ToString());
throw new Exception(winpty_error_msg(errorHandle).ToString());
}
string exe = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe";
string args = "";
string cwd = @"C:\";
string exe = GetShellLocation(request.Configuration);
string args = $"\"{request.Configuration.Arguments}\"";
string cwd = GetWorkingDirectory(request.Configuration);
spawnConfigHandle = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, args, cwd, null, out errorHandle);
if (errorHandle != IntPtr.Zero)
{
throw new Exception(winpty_error_code(errorHandle).ToString());
throw new Exception(winpty_error_msg(errorHandle).ToString());
}
_stdin = CreatePipe(winpty_conin_name(_handle), PipeDirection.Out);
@@ -57,7 +51,7 @@ namespace FluentTerminal.SystemTray.Services
if (!winpty_spawn(_handle, spawnConfigHandle, out IntPtr process, out IntPtr thread, out int procError, out errorHandle))
{
throw new Exception(winpty_error_code(errorHandle).ToString());
throw new Exception(winpty_error_msg(errorHandle).ToString());
}
}
finally
@@ -67,7 +61,6 @@ namespace FluentTerminal.SystemTray.Services
winpty_error_free(errorHandle);
}
var port = Utilities.GetAvailablePort(49151);
if (port == null)
@@ -102,6 +95,79 @@ namespace FluentTerminal.SystemTray.Services
ListenToStdOut();
}
~TerminalSession()
{
Dispose(false);
}
private string GetShellLocation(ShellConfiguration configuration)
{
switch (configuration.Shell)
{
case ShellType.CMD:
return @"C:\Windows\System32\cmd.exe";
case ShellType.PowerShell:
return @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe";
case ShellType.Custom:
return configuration.CustomShellLocation;
default:
return null;
}
}
private string GetWorkingDirectory(ShellConfiguration configuration)
{
if (string.IsNullOrWhiteSpace(configuration.WorkingDirectory) || !Directory.Exists(configuration.WorkingDirectory))
{
return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}
return configuration.WorkingDirectory;
}
public event EventHandler ConnectionClosed;
public int Id { get; }
public string WebSocketUrl { get; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Resize(TerminalSize size)
{
var errorHandle = IntPtr.Zero;
try
{
winpty_set_size(_handle, size.Columns, size.Rows, out errorHandle);
if (errorHandle != IntPtr.Zero)
{
throw new Exception(winpty_error_msg(errorHandle).ToString());
}
}
finally
{
winpty_error_free(errorHandle);
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_stdin?.Dispose();
_stdout?.Dispose();
}
winpty_free(_handle);
_disposedValue = true;
}
}
private Stream CreatePipe(string pipeName, PipeDirection direction)
{
string serverName = ".";
@@ -143,32 +209,5 @@ namespace FluentTerminal.SystemTray.Services
while (!reader.EndOfStream);
});
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_stdin?.Dispose();
_stdout?.Dispose();
}
winpty_free(_handle);
_disposedValue = true;
}
}
~Terminal()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
@@ -1,28 +1,57 @@
using FluentTerminal.SystemTray.DataTransferObjects;
using FluentTerminal.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace FluentTerminal.SystemTray.Services
{
public class TerminalsManager
{
private Dictionary<int, Terminal> _terminals = new Dictionary<int, Terminal>();
private Dictionary<int, TerminalSession> _terminals = new Dictionary<int, TerminalSession>();
public TerminalsManager()
{
}
public string CreateTerminal(TerminalOptions options)
public CreateTerminalResponse CreateTerminal(CreateTerminalRequest request)
{
var terminal = new Terminal(options);
TerminalSession terminal;
try
{
terminal = new TerminalSession(request);
}
catch (Exception e)
{
return new CreateTerminalResponse { Error = e.Message };
}
terminal.ConnectionClosed += OnTerminalConnectionClosed;
_terminals.Add(terminal.Id, terminal);
return terminal.WebSocketUrl;
return new CreateTerminalResponse
{
Success = true,
Id = terminal.Id,
WebSocketUrl = terminal.WebSocketUrl
};
}
public void ResizeTerminal(int id, TerminalSize size)
{
if (_terminals.TryGetValue(id, out TerminalSession terminal))
{
terminal.Resize(size);
}
else
{
Debug.WriteLine($"ResizeTerminal: terminal with id '{id}' was not found");
}
}
private void OnTerminalConnectionClosed(object sender, System.EventArgs e)
{
if (sender is Terminal terminal)
if (sender is TerminalSession terminal)
{
_terminals.Remove(terminal.Id);
terminal.Dispose();
+2 -2
View File
@@ -2,6 +2,7 @@
using Owin;
using System.Web.Http;
using Unity;
using Unity.Lifetime;
namespace FluentTerminal.SystemTray
{
@@ -13,11 +14,10 @@ namespace FluentTerminal.SystemTray
config.MapHttpAttributeRoutes();
var container = new UnityContainer();
container.RegisterType<TerminalsManager>();
container.RegisterType<TerminalsManager>(new ContainerControlledLifetimeManager());
config.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
//appBuilder.UseFileServer()
appBuilder.UseStaticFiles("/Client");
appBuilder.UseWebApi(config);
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -18,7 +19,9 @@ namespace FluentTerminal.SystemTray
public SystemTrayApplicationContext()
{
var openMenuItem = new MenuItem("Open App", new EventHandler(OpenApp));
var openMenuItem = new MenuItem("Open", new EventHandler(OpenApp));
var newWindowItem = new MenuItem("New Window", new EventHandler(NewWindow));
var settingsMenuItem = new MenuItem("Show Settings", new EventHandler(ShowSettings));
var exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
openMenuItem.DefaultItem = true;
@@ -27,7 +30,7 @@ namespace FluentTerminal.SystemTray
notifyIcon.DoubleClick += OpenApp;
notifyIcon.Text = "Fluent Terminal";
notifyIcon.Icon = Properties.Resources.Square44x44Logo_scale_100;
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { openMenuItem, exitMenuItem });
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { openMenuItem, newWindowItem, settingsMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
@@ -37,8 +40,17 @@ namespace FluentTerminal.SystemTray
await appListEntries.First().LaunchAsync();
}
private async void Exit(object sender, EventArgs e)
private void ShowSettings(object sender, EventArgs e)
{
Process.Start("flute.exe", "settings");
}
private void NewWindow(object sender, EventArgs e)
{
Process.Start("flute.exe", "new");
}
private async void Exit(object sender, EventArgs e)
{
var message = new ValueSet
{
+37
View File
@@ -6,10 +6,15 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.App", "FluentTerminal.App\FluentTerminal.App.csproj", "{41020100-3414-4E0A-9A8E-92D3C4179DF8}"
ProjectSection(ProjectDependencies) = postProject
{9EDDE318-0003-4553-B6F9-3FE5473563FD} = {9EDDE318-0003-4553-B6F9-3FE5473563FD}
{BAAE6239-343F-454C-977F-F81F91715961} = {BAAE6239-343F-454C-977F-F81F91715961}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.SystemTray", "FluentTerminal.SystemTray\FluentTerminal.SystemTray.csproj", "{9EDDE318-0003-4553-B6F9-3FE5473563FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.RuntimeComponent", "FluentTerminal.RuntimeComponent\FluentTerminal.RuntimeComponent.csproj", "{BAAE6239-343F-454C-977F-F81F91715961}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.Models", "FluentTerminal.Models\FluentTerminal.Models.csproj", "{5EA40453-A0AA-449E-8406-D6EA51D7E70E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,6 +63,38 @@ Global
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x64.Build.0 = Release|x64
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x86.ActiveCfg = Release|x86
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x86.Build.0 = Release|x86
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|ARM.ActiveCfg = Debug|ARM
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|ARM.Build.0 = Debug|ARM
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x64.ActiveCfg = Debug|x64
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x64.Build.0 = Debug|x64
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x86.ActiveCfg = Debug|x86
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x86.Build.0 = Debug|x86
{BAAE6239-343F-454C-977F-F81F91715961}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAAE6239-343F-454C-977F-F81F91715961}.Release|Any CPU.Build.0 = Release|Any CPU
{BAAE6239-343F-454C-977F-F81F91715961}.Release|ARM.ActiveCfg = Release|ARM
{BAAE6239-343F-454C-977F-F81F91715961}.Release|ARM.Build.0 = Release|ARM
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x64.ActiveCfg = Release|x64
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x64.Build.0 = Release|x64
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x86.ActiveCfg = Release|x86
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x86.Build.0 = Release|x86
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|ARM.Build.0 = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x64.ActiveCfg = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x64.Build.0 = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x86.ActiveCfg = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x86.Build.0 = Debug|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|Any CPU.Build.0 = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|ARM.ActiveCfg = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|ARM.Build.0 = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x64.ActiveCfg = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x64.Build.0 = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x86.ActiveCfg = Release|Any CPU
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE