Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe60f37213 | ||
|
|
0dc1a1cd5d | ||
|
|
e1fc980779 | ||
|
|
ccd8c1eaa4 | ||
|
|
ad1a933207 | ||
|
|
8d7c57d6fe | ||
|
|
8d039f59ef | ||
|
|
cb8d3cabc7 | ||
|
|
579def4f63 | ||
|
|
5d54d448b0 |
No files matched your search
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services.Exceptions
|
||||
{
|
||||
public class ReadTextFileException : Exception
|
||||
{
|
||||
public ReadTextFileException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public ReadTextFileException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ReadTextFileException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ReadTextFileException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
// This validator is needed because accelerator key has to be member of VirtualKey enum.
|
||||
public interface IAcceleratorKeyValidator
|
||||
{
|
||||
bool Valid(int key);
|
||||
|
||||
bool Valid(ExtendedVirtualKey key);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ namespace FluentTerminal.App.Services
|
||||
|
||||
Task SaveTextFileAsync(string path, string content);
|
||||
|
||||
Task<string> ReadTextFileAsync(string path);
|
||||
|
||||
Task<string> GetSshConfigDirAsync();
|
||||
|
||||
Task<string[]> GetFilesFromSshConfigDirAsync();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -231,8 +230,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
Command = nameof(Command.ShowSettings),
|
||||
Ctrl = true,
|
||||
Shift = true,
|
||||
// I've had to replace ExtendedVirtualKey.Comma because it isn't a member of VirtualKey enum, thus cannot be shown in menu.
|
||||
Key = (int)ExtendedVirtualKey.S
|
||||
Key = (int)ExtendedVirtualKey.Comma
|
||||
}
|
||||
};
|
||||
|
||||
@@ -342,7 +340,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
new TabTheme
|
||||
{
|
||||
Id = 0,
|
||||
Name = I18N.Translate("TabTheme.None"),
|
||||
Name = "TabTheme.None",
|
||||
BackgroundOpacity = double.NaN,
|
||||
BackgroundPointerOverOpacity = double.NaN,
|
||||
BackgroundPressedOpacity = double.NaN,
|
||||
@@ -353,37 +351,37 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
new TabTheme
|
||||
{
|
||||
Id = 1,
|
||||
Name = I18N.Translate("TabTheme.Red"),
|
||||
Name = "TabTheme.Red",
|
||||
Color = "#E81123"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 2,
|
||||
Name = I18N.Translate("TabTheme.Green"),
|
||||
Name = "TabTheme.Green",
|
||||
Color = "#10893E"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 3,
|
||||
Name = I18N.Translate("TabTheme.Blue"),
|
||||
Name = "TabTheme.Blue",
|
||||
Color = "#0078D7"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 4,
|
||||
Name = I18N.Translate("TabTheme.Purple"),
|
||||
Name = "TabTheme.Purple",
|
||||
Color = "#881798"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 5,
|
||||
Name = I18N.Translate("TabTheme.Orange"),
|
||||
Name = "TabTheme.Orange",
|
||||
Color = "#FF8C00"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 6,
|
||||
Name = I18N.Translate("TabTheme.Teal"),
|
||||
Name = "TabTheme.Teal",
|
||||
Color = "#00B7C3"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
config.Themes.Add(theme);
|
||||
}
|
||||
|
||||
foreach (var profile in GetShellProfiles().Where(x => !x.PreInstalled))
|
||||
foreach (var profile in GetShellProfiles())
|
||||
{
|
||||
config.Profiles.Add(profile);
|
||||
}
|
||||
@@ -117,21 +117,18 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
SaveTheme(theme, existingTheme != null);
|
||||
}
|
||||
|
||||
// Can't create pre-installed profiles
|
||||
foreach (var profile in config.Profiles.Where(x => !x.PreInstalled))
|
||||
foreach (var profile in config.Profiles)
|
||||
{
|
||||
var existingProfile = GetShellProfile(profile.Id);
|
||||
var isNew = existingProfile.EqualTo(default);
|
||||
var isNew = existingProfile == default;
|
||||
|
||||
// You can only edit certain parts of preinstalled profiles
|
||||
if (!isNew && existingProfile.PreInstalled)
|
||||
{
|
||||
existingProfile.WorkingDirectory = profile.WorkingDirectory;
|
||||
existingProfile.Arguments = profile.Arguments;
|
||||
existingProfile.TabThemeId = profile.TabThemeId;
|
||||
existingProfile.TerminalThemeId = profile.TerminalThemeId;
|
||||
existingProfile.KeyBindings = profile.KeyBindings;
|
||||
SaveShellProfile(profile, isNew);
|
||||
// those cannot be edited
|
||||
profile.Name = existingProfile.Name;
|
||||
profile.Location = existingProfile.Location;
|
||||
|
||||
SaveShellProfile(profile, false);
|
||||
continue;
|
||||
}
|
||||
SaveShellProfile(profile, isNew);
|
||||
@@ -140,7 +137,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
foreach (var profile in config.SshProfiles)
|
||||
{
|
||||
var existingProfile = GetSshProfile(profile.Id);
|
||||
var isNew = existingProfile.EqualTo(default(SshProfile));
|
||||
var isNew = existingProfile == default;
|
||||
|
||||
SaveSshProfile(profile, isNew);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,18 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> ReadTextFileAsync(string path)
|
||||
{
|
||||
var response = await GetResponseAsync<StringValueResponse>(new ReadTextFileRequest { Path = path }).ConfigureAwait(false);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
throw new ReadTextFileException(string.IsNullOrWhiteSpace(response.Error) ? "Failed to read the file." : response.Error);
|
||||
}
|
||||
|
||||
return response.Value;
|
||||
}
|
||||
|
||||
public async Task<string> GetSshConfigDirAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_sshConfigDir))
|
||||
|
||||
@@ -77,6 +77,8 @@ namespace FluentTerminal.App.Services
|
||||
|
||||
public byte Id { get; }
|
||||
|
||||
public ShellProfile Profile { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// To be called by either view or viewmodel
|
||||
/// </summary>
|
||||
@@ -143,6 +145,8 @@ namespace FluentTerminal.App.Services
|
||||
/// </summary>
|
||||
public async Task<TerminalResponse> StartShellProcessAsync(ShellProfile shellProfile, TerminalSize size, SessionType sessionType, string termState)
|
||||
{
|
||||
Profile = shellProfile;
|
||||
|
||||
if (!_requireShellProcessStart && !string.IsNullOrEmpty(termState))
|
||||
{
|
||||
OutputReceived?.Invoke(this, Encoding.UTF8.GetBytes(termState));
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private readonly ICommandHistoryService _commandHistoryService;
|
||||
private readonly IAcceleratorKeyValidator _acceleratorKeyValidator;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private bool _useAcrylicBackground;
|
||||
private double _backgroundOpacity;
|
||||
@@ -38,7 +37,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
private IDictionary<string, ICollection<KeyBinding>> _keyBindings;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService,
|
||||
IApplicationView applicationView, IClipboardService clipboardService, ICommandHistoryService commandHistoryService, IAcceleratorKeyValidator acceleratorKeyValidator)
|
||||
IApplicationView applicationView, IClipboardService clipboardService, ICommandHistoryService commandHistoryService)
|
||||
{
|
||||
MessengerInstance.Register<ApplicationSettingsChangedMessage>(this, OnApplicationSettingsChanged);
|
||||
MessengerInstance.Register<ShellProfileAddedMessage>(this, OnShellProfileAdded);
|
||||
@@ -57,7 +56,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
_clipboardService = clipboardService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_commandHistoryService = commandHistoryService;
|
||||
_acceleratorKeyValidator = acceleratorKeyValidator;
|
||||
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewTab), async () => await AddDefaultProfileAsync(NewTerminalLocation.Tab));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewWindow), async () => await AddDefaultProfileAsync(NewTerminalLocation.Window));
|
||||
@@ -677,8 +675,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
icon: Mdl2Icon.Settings());
|
||||
|
||||
if (_keyBindings.TryGetValue(nameof(Command.ShowSettings), out var keyBindings) &&
|
||||
keyBindings.FirstOrDefault() is KeyBinding settingsKeyBinding &&
|
||||
_acceleratorKeyValidator.Valid(settingsKeyBinding.Key))
|
||||
keyBindings.FirstOrDefault() is KeyBinding settingsKeyBinding)
|
||||
{
|
||||
LoadKeyBindingsFromModel(settingsItem, settingsKeyBinding);
|
||||
}
|
||||
@@ -720,8 +717,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
var defaultProfileCommand = tab ? nameof(Command.NewTab) : nameof(Command.NewWindow);
|
||||
|
||||
if (_keyBindings.TryGetValue(defaultProfileCommand, out var kbs) &&
|
||||
kbs.FirstOrDefault() is KeyBinding tabKeyBindings &&
|
||||
_acceleratorKeyValidator.Valid(tabKeyBindings.Key))
|
||||
kbs.FirstOrDefault() is KeyBinding tabKeyBindings)
|
||||
{
|
||||
LoadKeyBindingsFromModel(defaultProfileItem, tabKeyBindings);
|
||||
}
|
||||
@@ -743,8 +739,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
var command = tab ? nameof(Command.NewSshTab) : nameof(Command.NewSshWindow);
|
||||
|
||||
if (_keyBindings.TryGetValue(command, out var keyBindings) &&
|
||||
keyBindings.FirstOrDefault() is KeyBinding remoteTabKeyBinding &&
|
||||
_acceleratorKeyValidator.Valid(remoteTabKeyBinding.Key))
|
||||
keyBindings.FirstOrDefault() is KeyBinding remoteTabKeyBinding)
|
||||
{
|
||||
LoadKeyBindingsFromModel(remoteConnectItem, remoteTabKeyBinding);
|
||||
}
|
||||
@@ -765,8 +760,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
command = tab ? nameof(Command.NewCustomCommandTab) : nameof(Command.NewCustomCommandWindow);
|
||||
|
||||
if (_keyBindings.TryGetValue(command, out keyBindings) &&
|
||||
keyBindings.FirstOrDefault() is KeyBinding quickTabKeyBinding &&
|
||||
_acceleratorKeyValidator.Valid(quickTabKeyBinding.Key))
|
||||
keyBindings.FirstOrDefault() is KeyBinding quickTabKeyBinding)
|
||||
{
|
||||
LoadKeyBindingsFromModel(quickLaunchItem, quickTabKeyBinding);
|
||||
}
|
||||
@@ -795,13 +789,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
private void LoadKeyBindingsFromModel(MenuItemViewModel menuItemViewModel, KeyBinding keyBinding)
|
||||
{
|
||||
if (!_acceleratorKeyValidator.Valid(keyBinding.Key))
|
||||
{
|
||||
menuItemViewModel.KeyBinding = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (menuItemViewModel.KeyBinding == null)
|
||||
{
|
||||
menuItemViewModel.KeyBinding = new MenuItemKeyBindingViewModel(keyBinding);
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.System;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Menu
|
||||
{
|
||||
public class MenuItemKeyBindingViewModel : ViewModelBase
|
||||
{
|
||||
#region Properties
|
||||
|
||||
private int _key;
|
||||
|
||||
public int Key
|
||||
@@ -80,9 +83,21 @@ namespace FluentTerminal.App.ViewModels.Menu
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
public bool IsExtendedVirtualKey => !Enum.IsDefined(typeof(VirtualKey), Key);
|
||||
|
||||
#region Constructors
|
||||
public string GetOverrideText()
|
||||
{
|
||||
var segments = new List<string>();
|
||||
|
||||
if (Ctrl) segments.Add("Ctrl");
|
||||
if (Alt) segments.Add("Alt");
|
||||
if (Windows) segments.Add("Windows");
|
||||
if (Shift) segments.Add("Shift");
|
||||
|
||||
segments.Add(EnumHelper.GetEnumDescription((ExtendedVirtualKey)Key));
|
||||
|
||||
return string.Join("+", segments);
|
||||
}
|
||||
|
||||
public MenuItemKeyBindingViewModel(int key = 0, bool ctrl = false, bool alt = false, bool shift = false,
|
||||
bool windows = false)
|
||||
@@ -101,9 +116,7 @@ namespace FluentTerminal.App.ViewModels.Menu
|
||||
{
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Methods
|
||||
|
||||
private void SetKeyModifiers()
|
||||
{
|
||||
@@ -142,6 +155,5 @@ namespace FluentTerminal.App.ViewModels.Menu
|
||||
return _key == other._key && _keyModifiers == other._keyModifiers;
|
||||
}
|
||||
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
private string _hoveredUri;
|
||||
private MenuViewModel _contextMenu;
|
||||
private MenuViewModel _tabContextMenu;
|
||||
private readonly TabThemeViewModel _transparentTabThemeViewModel;
|
||||
|
||||
public TerminalViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService,
|
||||
IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, ShellProfile shellProfile,
|
||||
@@ -167,6 +168,8 @@ namespace FluentTerminal.App.ViewModels
|
||||
defaultTabTheme.IsSelected = true;
|
||||
}
|
||||
|
||||
_transparentTabThemeViewModel = TabThemes.FirstOrDefault(t => t.Theme.Id == 0);
|
||||
|
||||
Terminal = new Terminal(TrayProcessCommunicationService, _terminalId);
|
||||
Terminal.KeyboardCommandReceived += Terminal_KeyboardCommandReceived;
|
||||
Terminal.OutputReceived += Terminal_OutputReceived;
|
||||
@@ -218,13 +221,13 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
public IApplicationView ApplicationView { get; }
|
||||
|
||||
public TabTheme BackgroundTabTheme
|
||||
public TabThemeViewModel BackgroundTabTheme
|
||||
{
|
||||
// The effective background theme depends on whether it is selected (use the theme), or if it is inactive
|
||||
// (if we're set to underline inactive tabs, use the null theme).
|
||||
get => IsSelected || (!IsSelected && ApplicationSettings.InactiveTabColorMode == InactiveTabColorMode.Background) ?
|
||||
_tabTheme?.Theme :
|
||||
SettingsService.GetTabThemes().FirstOrDefault(t => t.Color == null);
|
||||
TabTheme :
|
||||
_transparentTabThemeViewModel;
|
||||
}
|
||||
|
||||
public IClipboardService ClipboardService { get; }
|
||||
@@ -689,6 +692,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
private void Terminal_TitleChanged(object sender, string e)
|
||||
{
|
||||
if (e == Terminal.Profile.Location) return;
|
||||
ApplicationView.ExecuteOnUiThreadAsync(() => ShellTitle = e);
|
||||
}
|
||||
|
||||
@@ -777,7 +781,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
//todo localise name
|
||||
var icon = tabTheme.Theme.Id == 0 ? Mdl2Icon.PaginationDotOutline10() : Mdl2Icon.PaginationDotSolid10(tabTheme.Theme.Color);
|
||||
return new RadioMenuItemViewModel(I18N.Translate($"TabTheme.{tabTheme.Theme.Name}"), $"{Terminal.Id}_tabTheme", tabTheme, bindingPath: nameof(TabThemeViewModel.IsSelected) , icon: icon);
|
||||
return new RadioMenuItemViewModel(I18N.Translate($"{tabTheme.Theme.Name}"), $"{Terminal.Id}_tabTheme", tabTheme, bindingPath: nameof(TabThemeViewModel.IsSelected) , icon: icon);
|
||||
}
|
||||
|
||||
var tabColor = new ExpandableMenuItemViewModel(I18N.Translate("Color.Text"));
|
||||
|
||||
+193
-149
@@ -47,6 +47,7 @@ namespace FluentTerminal.App
|
||||
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;
|
||||
@@ -110,13 +111,13 @@ namespace FluentTerminal.App
|
||||
builder.RegisterType<StartupTaskService>().As<IStartupTaskService>().SingleInstance();
|
||||
builder.RegisterType<ApplicationLanguageService>().As<IApplicationLanguageService>().SingleInstance();
|
||||
builder.RegisterType<ShellProfileMigrationService>().As<IShellProfileMigrationService>().SingleInstance();
|
||||
builder.RegisterType<AcceleratorKeyValidator>().As<IAcceleratorKeyValidator>().SingleInstance();
|
||||
|
||||
_container = builder.Build();
|
||||
|
||||
Messenger.Default.Register<ApplicationSettingsChangedMessage>(this, OnApplicationSettingsChanged);
|
||||
|
||||
_settingsService = _container.Resolve<ISettingsService>();
|
||||
_notificationService = _container.Resolve<INotificationService>();
|
||||
|
||||
var shellProfileMigrationService = _container.Resolve<IShellProfileMigrationService>();
|
||||
foreach (var profile in _settingsService.GetShellProfiles())
|
||||
@@ -186,7 +187,189 @@ namespace FluentTerminal.App
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
if (args is ProtocolActivatedEventArgs protocolActivated)
|
||||
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
|
||||
};
|
||||
|
||||
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<MainViewModel>();
|
||||
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"))
|
||||
{
|
||||
@@ -262,7 +445,7 @@ namespace FluentTerminal.App
|
||||
|
||||
var error = await vm.AcceptChangesAsync(true);
|
||||
|
||||
var profile = (SshProfile) vm.Model;
|
||||
var profile = (SshProfile)vm.Model;
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
@@ -347,8 +530,7 @@ namespace FluentTerminal.App
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args is CommandLineActivatedEventArgs commandLineActivated)
|
||||
else if (args is CommandLineActivatedEventArgs commandLineActivated)
|
||||
{
|
||||
var arguments = commandLineActivated.Operation.Arguments;
|
||||
if (string.IsNullOrWhiteSpace(arguments))
|
||||
@@ -358,153 +540,10 @@ namespace FluentTerminal.App
|
||||
|
||||
_commandLineParser
|
||||
.ParseArguments(SplitArguments(arguments), typeof(NewVerb), typeof(RunVerb), typeof(SettingsVerb))
|
||||
.WithParsed(async verb => await CliRunAsync(verb, commandLineActivated));
|
||||
.WithParsed(async verb => await ParseCommandLineArgumentsAsync(verb, commandLineActivated));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CliRunAsync(object verb, CommandLineActivatedEventArgs commandLineActivated)
|
||||
{
|
||||
if (verb is SettingsVerb settingsVerb)
|
||||
{
|
||||
if (!settingsVerb.Import && !settingsVerb.Export)
|
||||
{
|
||||
await ShowSettingsAsync();
|
||||
}
|
||||
else if (settingsVerb.Export)
|
||||
{
|
||||
var exportFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("config.json", CreationCollisionOption.OpenIfExists);
|
||||
|
||||
var settings = _settingsService.ExportSettings();
|
||||
await FileIO.WriteTextAsync(exportFile, settings);
|
||||
await new MessageDialog($"{I18N.Translate("SettingsExported")} {exportFile.Path}").ShowAsync();
|
||||
}
|
||||
else if (settingsVerb.Import)
|
||||
{
|
||||
var file = await ApplicationData.Current.LocalFolder.GetFileAsync("config.json");
|
||||
var content = await FileIO.ReadTextAsync(file);
|
||||
_settingsService.ImportSettings(content);
|
||||
await new MessageDialog($"{I18N.Translate("SettingsImported")} {file.Path}").ShowAsync();
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(runVerb.Buffer) && bool.TryParse(runVerb.Buffer, out var useBuffer))
|
||||
{
|
||||
profile.UseBuffer = useBuffer;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (_isLaunching)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isLaunching = true;
|
||||
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
await InitializeLoggerAsync();
|
||||
|
||||
// ReSharper disable once AssignmentIsFullyDiscarded
|
||||
_ = JumpListHelper.UpdateAsync(_settingsService);
|
||||
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
if (args.Arguments.StartsWith(JumpListHelper.ShellProfileFlag))
|
||||
{
|
||||
await viewModel.AddProfileByGuidAsync(Guid.Parse(args.Arguments.Replace(JumpListHelper.ShellProfileFlag, string.Empty)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await viewModel.AddDefaultProfileAsync(NewTerminalLocation.Tab);
|
||||
}
|
||||
await CreateMainViewAsync(typeof(MainPage), viewModel, true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
else if (args.Arguments.StartsWith(JumpListHelper.ShellProfileFlag))
|
||||
{
|
||||
var location = _applicationSettings.NewTerminalLocation;
|
||||
var profile = _settingsService.GetShellProfile(Guid.Parse(args.Arguments.Replace(JumpListHelper.ShellProfileFlag, string.Empty)));
|
||||
await CreateTerminalAsync(profile, location, args.ViewSwitcher);
|
||||
}
|
||||
else
|
||||
{
|
||||
var viewModel = await CreateNewTerminalWindowAsync();
|
||||
await viewModel.AddDefaultProfileAsync(NewTerminalLocation.Tab);
|
||||
await ShowAsStandaloneAsync(viewModel, args.ViewSwitcher);
|
||||
}
|
||||
|
||||
_isLaunching = false;
|
||||
}
|
||||
|
||||
private static async Task InitializeLoggerAsync()
|
||||
{
|
||||
var logDirectory = await ApplicationData.Current.LocalCacheFolder.CreateFolderAsync("Logs", CreationCollisionOption.OpenIfExists);
|
||||
@@ -759,6 +798,11 @@ namespace FluentTerminal.App
|
||||
|
||||
private Task StartSystemTray()
|
||||
{
|
||||
if (_trayReady.Task.IsCompleted)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("AppLaunchedParameterGroup").AsTask();
|
||||
|
||||
return Task.WhenAll(launch, _trayReady.Task).ContinueWith(
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace FluentTerminal.App.CommandLineArguments
|
||||
[Option("theme")]
|
||||
public string Theme { get; set; }
|
||||
|
||||
[Option("buffer")]
|
||||
public string Buffer { get; set; }
|
||||
[Option("smart-buffer")]
|
||||
public bool? SmartBuffer { get; set; }
|
||||
|
||||
[Option("target")]
|
||||
public Target Target { get; set; }
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace FluentTerminal.App.CommandLineArguments
|
||||
public class SettingsVerb
|
||||
{
|
||||
[Option('i', "import",
|
||||
HelpText = "Import settings.")]
|
||||
public bool Import { get; set; }
|
||||
HelpText = "Import settings from specified file.")]
|
||||
public string Import { get; set; }
|
||||
|
||||
[Option('e', "export",
|
||||
HelpText = "Export settings.")]
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace FluentTerminal.App.Converters
|
||||
{
|
||||
return Translate($"{enumType}.{enumValue}");
|
||||
}
|
||||
else if (value is string stringValue)
|
||||
{
|
||||
return Translate(stringValue);
|
||||
}
|
||||
else if (parameter is string resource)
|
||||
{
|
||||
return Translate(resource);
|
||||
|
||||
+16
-5
@@ -7,6 +7,7 @@ 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
|
||||
|
||||
@@ -90,12 +91,22 @@ namespace FluentTerminal.App.Converters
|
||||
|
||||
if (viewModel.KeyBinding != null)
|
||||
{
|
||||
item.KeyboardAccelerators?.Add(new KeyboardAccelerator
|
||||
if (viewModel.KeyBinding.IsExtendedVirtualKey)
|
||||
{
|
||||
Key = (VirtualKey) viewModel.KeyBinding.Key,
|
||||
Modifiers = (VirtualKeyModifiers) viewModel.KeyBinding.KeyModifiers,
|
||||
IsEnabled = true
|
||||
});
|
||||
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;
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace FluentTerminal.App.Dialogs
|
||||
// ReSharper disable once RedundantExtendsListEntry
|
||||
public sealed partial class CreateKeyBindingDialog : ContentDialog, ICreateKeyBindingDialog, INotifyPropertyChanged
|
||||
{
|
||||
private readonly IAcceleratorKeyValidator _acceleratorKeyValidator;
|
||||
|
||||
private bool _ctrl;
|
||||
private bool _shift;
|
||||
private bool _alt;
|
||||
@@ -53,17 +51,12 @@ namespace FluentTerminal.App.Dialogs
|
||||
get => _key;
|
||||
set
|
||||
{
|
||||
// We're ignoring invalid keys.
|
||||
if (_acceleratorKeyValidator.Valid(value))
|
||||
{
|
||||
Set(ref _key, value);
|
||||
}
|
||||
Set(ref _key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public CreateKeyBindingDialog(IAcceleratorKeyValidator acceleratorKeyValidator)
|
||||
public CreateKeyBindingDialog()
|
||||
{
|
||||
_acceleratorKeyValidator = acceleratorKeyValidator;
|
||||
InitializeComponent();
|
||||
ResetCommand = new RelayCommand(Reset);
|
||||
PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown;
|
||||
|
||||
@@ -133,7 +133,6 @@
|
||||
<Compile Include="Dialogs\AboutDialog.xaml.cs">
|
||||
<DependentUpon>AboutDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Services\AcceleratorKeyValidator.cs" />
|
||||
<Compile Include="Services\CommandHistoryService.cs" />
|
||||
<Compile Include="Utilities\DelayedAction.cs" />
|
||||
<Compile Include="Utilities\DispatcherExtensions.cs" />
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using Windows.System;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class AcceleratorKeyValidator : IAcceleratorKeyValidator
|
||||
{
|
||||
public bool Valid(int key) => Enum.IsDefined(typeof(VirtualKey), key);
|
||||
|
||||
public bool Valid(ExtendedVirtualKey key) => Valid((int) key);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
<ResourceDictionary
|
||||
x:Class="FluentTerminal.App.Views.TemplateSelectors"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:views="using:FluentTerminal.App.Views"
|
||||
xmlns:models="using:FluentTerminal.Models"
|
||||
xmlns:settings="using:FluentTerminal.App.ViewModels.Settings">
|
||||
xmlns:settings="using:FluentTerminal.App.ViewModels.Settings"
|
||||
xmlns:views="using:FluentTerminal.App.Views">
|
||||
<views:TabThemeTemplateSelector x:Key="TabThemeTemplateSelector">
|
||||
<views:TabThemeTemplateSelector.DefaultTemplate>
|
||||
<DataTemplate x:DataType="models:TabTheme">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay, Converter={StaticResource I18NConverter}}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</views:TabThemeTemplateSelector.DefaultTemplate>
|
||||
@@ -21,11 +21,11 @@
|
||||
<DataTemplate x:DataType="models:TabTheme">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Foreground="{x:Bind Color}"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Foreground="{x:Bind Color}"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay, Converter={StaticResource I18NConverter}}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</views:TabThemeTemplateSelector.ColoredTemplate>
|
||||
@@ -51,9 +51,9 @@
|
||||
<views:TerminalKeybindTemplateSelector.KeyBindingsTemplate>
|
||||
<DataTemplate x:DataType="settings:KeyBindingsViewModel">
|
||||
<views:KeyBindingsView
|
||||
Margin="0,6,0,0"
|
||||
ShowCommandName="False"
|
||||
ViewModel="{x:Bind}" />
|
||||
Margin="0,6,0,0"
|
||||
ShowCommandName="False"
|
||||
ViewModel="{x:Bind}" />
|
||||
</DataTemplate>
|
||||
</views:TerminalKeybindTemplateSelector.KeyBindingsTemplate>
|
||||
</views:TerminalKeybindTemplateSelector>
|
||||
|
||||
@@ -29,6 +29,7 @@ let searchAddon: SearchAddon;
|
||||
let serializeAddon: SerializeAddon;
|
||||
let webLinksAddon: WebLinksAddon;
|
||||
let unicode11Addon: Unicode11Addon;
|
||||
let altGrPressed = false;
|
||||
|
||||
const terminalContainer = document.getElementById('terminal-container');
|
||||
|
||||
@@ -165,9 +166,16 @@ window.createTerminal = (options, theme, keyBindings) => {
|
||||
});
|
||||
|
||||
term.attachCustomKeyEventHandler(function (e) {
|
||||
if (e.type != "keydown") {
|
||||
if (e.altKey && e.type === "keydown" && e.location === 2) {
|
||||
altGrPressed = true;
|
||||
} else if (e.altKey && e.type === "keyup" && e.key === "Control") {
|
||||
altGrPressed = false
|
||||
}
|
||||
|
||||
if (e.type != "keydown" || altGrPressed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < window.keyBindings.length; i++) {
|
||||
var keyBinding = window.keyBindings[i];
|
||||
if (keyBinding.ctrl == e.ctrlKey
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
GetCommandPathRequest = 15,
|
||||
PauseTerminalOutputRequest = 16,
|
||||
PauseTerminalOutputResponse = 17,
|
||||
QuitApplicationRequest = 18
|
||||
QuitApplicationRequest = 18,
|
||||
ReadTextFileRequest = 19
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FluentTerminal.Models.Requests
|
||||
{
|
||||
public class ReadTextFileRequest : IMessage
|
||||
{
|
||||
public byte Identifier => (byte)MessageIdentifiers.ReadTextFileRequest;
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
namespace FluentTerminal.Models.Responses
|
||||
{
|
||||
public class CommonResponse : IMessage
|
||||
public class CommonResponse : TerminalResponse
|
||||
{
|
||||
public virtual byte Identifier => (byte) MessageIdentifiers.CommonResponse;
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
public override byte Identifier => (byte) MessageIdentifiers.CommonResponse;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace FluentTerminal.Models.Responses
|
||||
{
|
||||
public class CreateTerminalResponse : TerminalResponse, IMessage
|
||||
public class CreateTerminalResponse : TerminalResponse
|
||||
{
|
||||
public byte Identifier => (byte) MessageIdentifiers.CreateTerminalResponse;
|
||||
public override byte Identifier => (byte) MessageIdentifiers.CreateTerminalResponse;
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace FluentTerminal.Models.Responses
|
||||
{
|
||||
public class PauseTerminalOutputResponse : TerminalResponse, IMessage
|
||||
public class PauseTerminalOutputResponse : TerminalResponse
|
||||
{
|
||||
public byte Identifier => (byte) MessageIdentifiers.PauseTerminalOutputResponse;
|
||||
public override byte Identifier => (byte) MessageIdentifiers.PauseTerminalOutputResponse;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FluentTerminal.Models.Responses
|
||||
namespace FluentTerminal.Models.Responses
|
||||
{
|
||||
public class TerminalResponse
|
||||
public abstract class TerminalResponse : IMessage
|
||||
{
|
||||
public abstract byte Identifier { get; }
|
||||
public bool Success { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace FluentTerminal.SystemTray
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Allow CPU to jump between TerminalSessions' ReadingLoop Tasks
|
||||
await Task.Delay(1).ConfigureAwait(false);
|
||||
|
||||
bool paused;
|
||||
|
||||
lock (_lock)
|
||||
|
||||
@@ -134,6 +134,9 @@ namespace FluentTerminal.SystemTray.Services
|
||||
case MessageIdentifiers.QuitApplicationRequest:
|
||||
HandleQuitApplicationRequest();
|
||||
return;
|
||||
case MessageIdentifiers.ReadTextFileRequest:
|
||||
await HandleReadTextFileRequestAsync(args).ConfigureAwait(false);
|
||||
return;
|
||||
default:
|
||||
Logger.Instance.Error("Received unknown message type: {messageType}", messageType);
|
||||
return;
|
||||
@@ -214,6 +217,31 @@ namespace FluentTerminal.SystemTray.Services
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
private async Task HandleReadTextFileRequestAsync(AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
var deferreal = args.GetDeferral();
|
||||
var deferral = args.GetDeferral();
|
||||
var messageContent = (string)args.Request.Message[MessageKeys.Content];
|
||||
var request = JsonConvert.DeserializeObject<ReadTextFileRequest>(messageContent);
|
||||
var response = new StringValueResponse();
|
||||
|
||||
try
|
||||
{
|
||||
var content = Utilities.ReadFile(request.Path);
|
||||
response.Success = true;
|
||||
response.Value = content;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Error = e.Message;
|
||||
}
|
||||
|
||||
await args.Request.SendResponseAsync(CreateMessage(response));
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
private async Task HandleGetSshConfigFolderRequestAsync(AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
var deferral = args.GetDeferral();
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace FluentTerminal.SystemTray.Services.ConPty
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_terminal?.ConsoleOutStream?.Close();
|
||||
_reader?.Dispose();
|
||||
|
||||
ConnectionClosed?.Invoke(this, _terminal.ExitCode);
|
||||
@@ -91,6 +92,11 @@ namespace FluentTerminal.SystemTray.Services.ConPty
|
||||
_reader?.SetPaused(value);
|
||||
}
|
||||
|
||||
~ConPtySession()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
private bool _disposed;
|
||||
@@ -113,6 +119,7 @@ namespace FluentTerminal.SystemTray.Services.ConPty
|
||||
public void Dispose()
|
||||
{
|
||||
_terminal.Exited -= _terminal_Exited;
|
||||
_terminal.OutputReady -= _terminal_OutputReady;
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ namespace FluentTerminal.SystemTray.Services.ConPty
|
||||
|
||||
if (disposeManaged)
|
||||
{
|
||||
ConsoleOutStream.Dispose();
|
||||
_consoleInputWriter?.Dispose();
|
||||
_process?.Dispose();
|
||||
_pseudoConsole?.Dispose();
|
||||
|
||||
@@ -536,6 +536,11 @@ namespace FluentTerminal.SystemTray
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ReadFile(string path)
|
||||
{
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
private static bool? MuteProcess(int id, bool mute, bool force = false)
|
||||
{
|
||||
bool? isAudioSessionMuted = VolumeControl.GetAudioSessionMute(id);
|
||||
|
||||
@@ -68,8 +68,6 @@ Sign up to [POEditor](https://poeditor.com/join/project/TfKNIt826z) to help with
|
||||
- From an elevated/admin shell, execute `choco install fluent-terminal`
|
||||
|
||||
### Scoop package manager installation
|
||||
- [Enable sideloading apps](https://www.windowscentral.com/how-enable-windows-10-sideload-apps-outside-store)
|
||||
- Alternatively, [enable developer mode](https://docs.microsoft.com/en-US/windows/uwp/get-started/enable-your-device-for-development) if you plan to do UWP app development. **For most users that do not intend to do UWP app development, developer mode will not be necessary.**
|
||||
- Install Scoop
|
||||
- Install 7zip and git (to add buckets): `scoop install 7zip git`
|
||||
- From an elevated/admin shell, execute
|
||||
|
||||
Reference in new issue
Block a user