Merge branch 'feature/avalonia' into main

This commit is contained in:
2022-02-01 13:41:09 +01:00
2469 changed files with 8011 additions and 293 deletions

View File

@@ -1,35 +1,36 @@
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
namespace FileTime.App.Core.Clipboard namespace FileTime.App.Core.Clipboard
{ {
public class Clipboard : IClipboard public class Clipboard : IClipboard
{ {
private readonly List<ClipboardItem> _content; private readonly List<AbsolutePath> _content;
public IReadOnlyList<ClipboardItem> Content { get; } public IReadOnlyList<AbsolutePath> Content { get; }
public Type? CommandType { get; private set; } public Type? CommandType { get; private set; }
public Clipboard() public Clipboard()
{ {
_content = new List<ClipboardItem>(); _content = new List<AbsolutePath>();
Content = _content.AsReadOnly(); Content = _content.AsReadOnly();
} }
public void AddContent(IContentProvider contentProvider, string path) public void AddContent(AbsolutePath absolutePath)
{ {
foreach (var content in _content) foreach (var content in _content)
{ {
if (content.ContentProvider == contentProvider && content.Path == path) return; if (content.IsEqual(absolutePath)) return;
} }
_content.Add(new ClipboardItem(contentProvider, path)); _content.Add(new AbsolutePath(absolutePath));
} }
public void RemoveContent(IContentProvider contentProvider, string path) public void RemoveContent(AbsolutePath absolutePath)
{ {
for (var i = 0; i < _content.Count; i++) for (var i = 0; i < _content.Count; i++)
{ {
if (_content[i].ContentProvider == contentProvider && _content[i].Path == path) if (_content[i].IsEqual(absolutePath))
{ {
_content.RemoveAt(i--); _content.RemoveAt(i--);
} }

View File

@@ -1,17 +0,0 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.App.Core.Clipboard
{
public class ClipboardItem : IAbsolutePath
{
public IContentProvider ContentProvider { get; }
public string Path { get; }
public ClipboardItem(IContentProvider contentProvider, string path)
{
ContentProvider = contentProvider;
Path = path;
}
}
}

View File

@@ -1,16 +1,16 @@
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Providers; using FileTime.Core.Models;
namespace FileTime.App.Core.Clipboard namespace FileTime.App.Core.Clipboard
{ {
public interface IClipboard public interface IClipboard
{ {
IReadOnlyList<ClipboardItem> Content { get; } IReadOnlyList<AbsolutePath> Content { get; }
Type? CommandType { get; } Type? CommandType { get; }
void AddContent(IContentProvider contentProvider, string path); void AddContent(AbsolutePath absolutePath);
void Clear(); void Clear();
void RemoveContent(IContentProvider contentProvider, string path); void RemoveContent(AbsolutePath absolutePath);
void SetCommand<T>() where T : ITransportationCommand; void SetCommand<T>() where T : ITransportationCommand;
} }
} }

View File

@@ -4,26 +4,32 @@ namespace FileTime.App.Core.Command
{ {
CloseTab, CloseTab,
Copy, Copy,
Cut,
GoUp,
MoveCursorDown,
MoveCursorUp,
Open,
Paste,
Select,
ToggleHidden,
CreateContainer, CreateContainer,
CreateElement, CreateElement,
MoveCursorUpPage, Cut,
EnterRapidTravel,
GoToHome,
GoToProvider,
GoToRoot,
GoUp,
Delete,
MoveCursorDown,
MoveCursorDownPage, MoveCursorDownPage,
MoveToTop, MoveCursorUp,
MoveCursorUpPage,
MoveToBottom, MoveToBottom,
MoveToFirst, MoveToFirst,
MoveToLast, MoveToLast,
GoToRoot, MoveToTop,
GoToProvider, Open,
GoToHome,
EnterRapidTravel,
OpenOrRun, OpenOrRun,
PasteMerge,
PasteOverwrite,
PasteSkip,
Select,
ToggleHidden,
Rename,
Dummy,
Refresh,
} }
} }

View File

@@ -1,17 +0,0 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.App.Core.Tab
{
public class TabItem : IAbsolutePath
{
public IContentProvider ContentProvider { get; }
public string Path { get; }
public TabItem(IContentProvider contentProvider, string path)
{
ContentProvider = contentProvider;
Path = path;
}
}
}

View File

@@ -1,77 +1,108 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.App.Core.Tab namespace FileTime.App.Core.Tab
{ {
public class TabState public class TabState
{ {
private readonly Dictionary<IContainer, List<TabItem>> _selectedItems; private readonly Dictionary<IContainer, List<AbsolutePath>> _markedItems;
private readonly Dictionary<IContainer, IReadOnlyList<TabItem>> _selectedItemsReadOnly; private readonly Dictionary<IContainer, IReadOnlyList<AbsolutePath>> _markedItemsReadOnly;
public IReadOnlyDictionary<IContainer, IReadOnlyList<TabItem>> SelectedItems { get; } public IReadOnlyDictionary<IContainer, IReadOnlyList<AbsolutePath>> MarkedItems { get; }
public FileTime.Core.Components.Tab Tab { get; } public FileTime.Core.Components.Tab Tab { get; }
public AsyncEventHandler<TabState, AbsolutePath> ItemMarked { get; } = new();
public AsyncEventHandler<TabState, AbsolutePath> ItemUnmarked { get; } = new();
public TabState(FileTime.Core.Components.Tab pane) public TabState(FileTime.Core.Components.Tab pane)
{ {
Tab = pane; Tab = pane;
_selectedItems = new Dictionary<IContainer, List<TabItem>>(); _markedItems = new Dictionary<IContainer, List<AbsolutePath>>();
_selectedItemsReadOnly = new Dictionary<IContainer, IReadOnlyList<TabItem>>(); _markedItemsReadOnly = new Dictionary<IContainer, IReadOnlyList<AbsolutePath>>();
SelectedItems = new ReadOnlyDictionary<IContainer, IReadOnlyList<TabItem>>(_selectedItemsReadOnly); MarkedItems = new ReadOnlyDictionary<IContainer, IReadOnlyList<AbsolutePath>>(_markedItemsReadOnly);
} }
public void AddSelectedItem(IContentProvider contentProvider, IContainer container, string path) public async Task AddMarkedItem(IContainer container, AbsolutePath path)
{ {
if (!_selectedItems.ContainsKey(container)) if (!_markedItems.ContainsKey(container))
{ {
var val = new List<TabItem>(); var val = new List<AbsolutePath>();
_selectedItems.Add(container, val); _markedItems.Add(container, val);
_selectedItemsReadOnly.Add(container, val.AsReadOnly()); _markedItemsReadOnly.Add(container, val.AsReadOnly());
} }
foreach (var content in _selectedItems[container]) foreach (var content in _markedItems[container])
{ {
if (content.ContentProvider == contentProvider && content.Path == path) return; if (content.IsEqual(path)) return;
} }
_selectedItems[container].Add(new TabItem(contentProvider, path)); var tabItem = new AbsolutePath(path);
_markedItems[container].Add(tabItem);
await ItemMarked.InvokeAsync(this, tabItem);
} }
public void RemoveSelectedItem(IContentProvider contentProvider, IContainer container, string path) public async Task RemoveMarkedItem(IContainer container, AbsolutePath path)
{ {
if (_selectedItems.ContainsKey(container)) if (_markedItems.ContainsKey(container))
{ {
var selectedItems = _selectedItems[container]; var markedItems = _markedItems[container];
for (var i = 0; i < selectedItems.Count; i++) for (var i = 0; i < markedItems.Count; i++)
{ {
if (selectedItems[i].ContentProvider == contentProvider && selectedItems[i].Path == path) if (markedItems[i].IsEqual(path))
{ {
selectedItems.RemoveAt(i--); await ItemUnmarked.InvokeAsync(this, markedItems[i]);
markedItems.RemoveAt(i--);
} }
} }
} }
} }
public bool ContainsSelectedItem(IContentProvider contentProvider, IContainer container, string path) public bool ContainsMarkedItem(IContainer container, AbsolutePath path)
{ {
if (!_selectedItems.ContainsKey(container)) return false; if (!_markedItems.ContainsKey(container)) return false;
foreach (var content in _selectedItems[container]) foreach (var content in _markedItems[container])
{ {
if (content.ContentProvider == contentProvider && content.Path == path) return true; if (content.Equals(path)) return true;
} }
return false; return false;
} }
public async Task<IReadOnlyList<TabItem>> GetCurrentSelectedItems() public async Task<IReadOnlyList<AbsolutePath>> GetCurrentMarkedItems()
{ {
var currentLocation = await Tab.GetCurrentLocation(); return GetCurrentMarkedItems(await Tab.GetCurrentLocation());
}
return SelectedItems.ContainsKey(currentLocation) public IReadOnlyList<AbsolutePath> GetCurrentMarkedItems(IContainer container)
? SelectedItems[currentLocation] {
: new List<TabItem>().AsReadOnly(); return MarkedItems.ContainsKey(container)
? MarkedItems[container]
: new List<AbsolutePath>().AsReadOnly();
}
public async Task MakrCurrentItem()
{
var currentLocation = await Tab!.GetCurrentLocation();
if (currentLocation != null)
{
var currentSelectedItem = await Tab.GetCurrentSelectedItem()!;
if (currentSelectedItem != null)
{
if (ContainsMarkedItem(currentLocation, new AbsolutePath(currentSelectedItem)))
{
await RemoveMarkedItem(currentLocation, new AbsolutePath(currentSelectedItem));
}
else
{
await AddMarkedItem(currentLocation, new AbsolutePath(currentSelectedItem));
}
}
await Tab.SelectNextItem();
}
} }
} }
} }

View File

@@ -2,6 +2,7 @@ using FileTime.App.Core.Clipboard;
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Providers; using FileTime.Core.Providers;
using FileTime.Core.StateManagement; using FileTime.Core.StateManagement;
using FileTime.Core.Timeline;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Providers.Smb; using FileTime.Providers.Smb;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -21,7 +22,8 @@ namespace FileTime.App.Core
.AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found")) .AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"))
.AddSingleton<IContentProvider, SmbContentProvider>() .AddSingleton<IContentProvider, SmbContentProvider>()
.AddSingleton<ElementCreationStates>() .AddSingleton<ElementCreationStates>()
.AddSingleton<CommandExecutor>(); .AddSingleton<CommandExecutor>()
.AddSingleton<TimeRunner>();
} }
} }
} }

View File

@@ -1,4 +1,3 @@
using FileTime.ConsoleUI.App.UI.Color;
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Extensions; using FileTime.Core.Extensions;
using FileTime.Core.Models; using FileTime.Core.Models;
@@ -28,8 +27,8 @@ namespace FileTime.ConsoleUI.App
private async Task GoUp() => await _selectedTab!.GoUp(); private async Task GoUp() => await _selectedTab!.GoUp();
private async Task Open() => await _selectedTab!.Open(); private async Task Open() => await _selectedTab!.Open();
private async Task MoveCursorUpPage() => await _selectedTab!.SelectPreviousItem(_renderers[_selectedTab].PageSize); private async Task MoveCursorUpPage() => await _selectedTab!.SelectPreviousItem(UI.Render.PageSize);
private async Task MoveCursorDownPage() => await _selectedTab!.SelectNextItem(_renderers[_selectedTab].PageSize); private async Task MoveCursorDownPage() => await _selectedTab!.SelectNextItem(UI.Render.PageSize);
private async Task MoveCursorToTop() => await _selectedTab!.SelectFirstItem(); private async Task MoveCursorToTop() => await _selectedTab!.SelectFirstItem();
private async Task MoveCursorToBottom() => await _selectedTab!.SelectLastItem(); private async Task MoveCursorToBottom() => await _selectedTab!.SelectLastItem();
@@ -38,22 +37,7 @@ namespace FileTime.ConsoleUI.App
const string hiddenFilterName = "filter_showhiddenelements"; const string hiddenFilterName = "filter_showhiddenelements";
var currentLocation = await _selectedTab!.GetCurrentLocation(); var currentLocation = await _selectedTab!.GetCurrentLocation();
/*IContainer containerToOpen = currentLocation;
if (currentLocation is VirtualContainer oldVirtualContainer)
{
containerToOpen = oldVirtualContainer.HasWithName(hiddenFilterName)
? oldVirtualContainer.ExceptWithName(hiddenFilterName)
: GenerateHiddenFilterVirtualContainer(currentLocation);
}
else
{
containerToOpen = GenerateHiddenFilterVirtualContainer(currentLocation);
} */
var containerToOpen = await currentLocation.ToggleVirtualContainerInChain(hiddenFilterName, GenerateHiddenFilterVirtualContainer); var containerToOpen = await currentLocation.ToggleVirtualContainerInChain(hiddenFilterName, GenerateHiddenFilterVirtualContainer);
await _selectedTab.OpenContainer(containerToOpen); await _selectedTab.OpenContainer(containerToOpen);
static async Task<VirtualContainer> GenerateHiddenFilterVirtualContainer(IContainer container) static async Task<VirtualContainer> GenerateHiddenFilterVirtualContainer(IContainer container)
@@ -81,20 +65,9 @@ namespace FileTime.ConsoleUI.App
public async Task Select() public async Task Select()
{ {
var currentLocation = await _selectedTab!.GetCurrentLocation(); if (_selectedTab != null)
if (currentLocation != null)
{ {
var currentSelectedItem = await _selectedTab.GetCurrentSelectedItem()!; await _tabStates[_selectedTab].MakrCurrentItem();
if (_paneStates[_selectedTab].ContainsSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!))
{
_paneStates[_selectedTab].RemoveSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!);
}
else
{
_paneStates[_selectedTab].AddSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!);
}
await _selectedTab.SelectNextItem();
} }
} }
@@ -103,18 +76,18 @@ namespace FileTime.ConsoleUI.App
_clipboard.Clear(); _clipboard.Clear();
_clipboard.SetCommand<CopyCommand>(); _clipboard.SetCommand<CopyCommand>();
var currentSelectedItems = await _paneStates[_selectedTab!].GetCurrentSelectedItems(); var currentSelectedItems = await _tabStates[_selectedTab!].GetCurrentMarkedItems();
if (currentSelectedItems.Count > 0) if (currentSelectedItems.Count > 0)
{ {
foreach (var selectedItem in currentSelectedItems) foreach (var selectedItem in currentSelectedItems)
{ {
_clipboard.AddContent(selectedItem.ContentProvider, selectedItem.Path); _clipboard.AddContent(new AbsolutePath(selectedItem));
} }
} }
else else
{ {
var currentSelectedItem = (await _selectedTab!.GetCurrentSelectedItem())!; var currentSelectedItem = (await _selectedTab!.GetCurrentSelectedItem())!;
_clipboard.AddContent(currentSelectedItem.Provider, currentSelectedItem.FullName!); _clipboard.AddContent(new AbsolutePath(currentSelectedItem));
} }
} }
@@ -157,7 +130,7 @@ namespace FileTime.ConsoleUI.App
? virtualContainer.BaseContainer ? virtualContainer.BaseContainer
: currentLocation; : currentLocation;
_commandExecutor.ExecuteCommand(command); await _timeRunner.AddCommand(command);
_clipboard.Clear(); _clipboard.Clear();
} }
@@ -194,9 +167,9 @@ namespace FileTime.ConsoleUI.App
private async Task HardDelete() private async Task HardDelete()
{ {
IList<IAbsolutePath>? itemsToDelete = null; IList<AbsolutePath>? itemsToDelete = null;
var currentSelectedItems = await _paneStates[_selectedTab!].GetCurrentSelectedItems(); var currentSelectedItems = (await _tabStates[_selectedTab!].GetCurrentMarkedItems()).Select(p => p.Resolve()).ToList();
var currentSelectedItem = await _selectedTab?.GetCurrentSelectedItem(); var currentSelectedItem = await _selectedTab?.GetCurrentSelectedItem();
if (currentSelectedItems.Count > 0) if (currentSelectedItems.Count > 0)
{ {
@@ -212,7 +185,7 @@ namespace FileTime.ConsoleUI.App
if (delete) if (delete)
{ {
itemsToDelete = currentSelectedItems.Cast<IAbsolutePath>().ToList(); itemsToDelete = currentSelectedItems.Cast<AbsolutePath>().ToList();
} }
} }
else if (currentSelectedItem != null) else if (currentSelectedItem != null)
@@ -225,9 +198,9 @@ namespace FileTime.ConsoleUI.App
if (delete) if (delete)
{ {
itemsToDelete = new List<IAbsolutePath>() itemsToDelete = new List<AbsolutePath>()
{ {
new AbsolutePath(currentSelectedItem.Provider, currentSelectedItem.FullName!) new AbsolutePath(currentSelectedItem)
}; };
} }
} }
@@ -241,7 +214,7 @@ namespace FileTime.ConsoleUI.App
deleteCommand.ItemsToDelete.Add(itemToDelete); deleteCommand.ItemsToDelete.Add(itemToDelete);
} }
_commandExecutor.ExecuteCommand(deleteCommand); await _timeRunner.AddCommand(deleteCommand);
_clipboard.Clear(); _clipboard.Clear();
} }

View File

@@ -10,6 +10,7 @@ using FileTime.App.Core.Tab;
using FileTime.ConsoleUI.App.UI.Color; using FileTime.ConsoleUI.App.UI.Color;
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.App.Core.Command; using FileTime.App.Core.Command;
using FileTime.Core.Timeline;
namespace FileTime.ConsoleUI.App namespace FileTime.ConsoleUI.App
{ {
@@ -17,14 +18,14 @@ namespace FileTime.ConsoleUI.App
{ {
private readonly List<Tab> _tabs = new(); private readonly List<Tab> _tabs = new();
private readonly Dictionary<Tab, Render> _renderers = new(); private readonly Dictionary<Tab, Render> _renderers = new();
private readonly Dictionary<Tab, TabState> _paneStates = new(); private readonly Dictionary<Tab, TabState> _tabStates = new();
private Tab? _selectedTab; private Tab? _selectedTab;
private readonly List<CommandBinding> _commandBindings = new(); private readonly List<CommandBinding> _commandBindings = new();
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IClipboard _clipboard; private readonly IClipboard _clipboard;
private readonly IColoredConsoleRenderer _coloredConsoleRenderer; private readonly IColoredConsoleRenderer _coloredConsoleRenderer;
private readonly CommandExecutor _commandExecutor; private readonly TimeRunner _timeRunner;
private readonly ConsoleReader _consoleReader; private readonly ConsoleReader _consoleReader;
private readonly IStyles _styles; private readonly IStyles _styles;
private readonly List<ConsoleKeyInfo> _previousKeys = new(); private readonly List<ConsoleKeyInfo> _previousKeys = new();
@@ -35,14 +36,14 @@ namespace FileTime.ConsoleUI.App
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IClipboard clipboard, IClipboard clipboard,
IColoredConsoleRenderer coloredConsoleRenderer, IColoredConsoleRenderer coloredConsoleRenderer,
CommandExecutor commandExecutor, TimeRunner timeRunner,
ConsoleReader consoleReader, ConsoleReader consoleReader,
IStyles styles) IStyles styles)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_clipboard = clipboard; _clipboard = clipboard;
_coloredConsoleRenderer = coloredConsoleRenderer; _coloredConsoleRenderer = coloredConsoleRenderer;
_commandExecutor = commandExecutor; _timeRunner = timeRunner;
_consoleReader = consoleReader; _consoleReader = consoleReader;
_styles = styles; _styles = styles;
InitCommandBindings(); InitCommandBindings();
@@ -60,7 +61,7 @@ namespace FileTime.ConsoleUI.App
_tabs.Add(tab); _tabs.Add(tab);
var paneState = new TabState(tab); var paneState = new TabState(tab);
_paneStates.Add(tab, paneState); _tabStates.Add(tab, paneState);
var renderer = _serviceProvider.GetService<Render>()!; var renderer = _serviceProvider.GetService<Render>()!;
renderer.Init(tab, paneState); renderer.Init(tab, paneState);
@@ -73,7 +74,7 @@ namespace FileTime.ConsoleUI.App
{ {
_tabs.Remove(pane); _tabs.Remove(pane);
_renderers.Remove(pane); _renderers.Remove(pane);
_paneStates.Remove(pane); _tabStates.Remove(pane);
} }
private void InitCommandBindings() private void InitCommandBindings()
@@ -134,7 +135,7 @@ namespace FileTime.ConsoleUI.App
Cut), Cut),
new CommandBinding( new CommandBinding(
"paste (merge)", "paste (merge)",
Commands.Paste, Commands.PasteMerge,
new[] new[]
{ {
new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false), new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false),
@@ -143,7 +144,7 @@ namespace FileTime.ConsoleUI.App
PasteMerge), PasteMerge),
new CommandBinding( new CommandBinding(
"paste (overwrite)", "paste (overwrite)",
Commands.Paste, Commands.PasteOverwrite,
new[] new[]
{ {
new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false), new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false),
@@ -152,7 +153,7 @@ namespace FileTime.ConsoleUI.App
PasteOverwrite), PasteOverwrite),
new CommandBinding( new CommandBinding(
"paste (skip)", "paste (skip)",
Commands.Paste, Commands.PasteSkip,
new[] new[]
{ {
new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false), new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false),

View File

@@ -26,7 +26,7 @@ namespace FileTime.ConsoleUI.App.UI
public Tab Tab { get; private set; } public Tab Tab { get; private set; }
public TabState TabState { get; private set; } public TabState TabState { get; private set; }
public int PageSize => Console.WindowHeight - _contentPaddingTop - _contentPaddingBottom; public static int PageSize => Console.WindowHeight - _contentPaddingTop - _contentPaddingBottom;
public Render(IColoredConsoleRenderer coloredRenderer, IStyles appStyle) public Render(IColoredConsoleRenderer coloredRenderer, IStyles appStyle)
{ {
_coloredRenderer = coloredRenderer; _coloredRenderer = coloredRenderer;
@@ -219,7 +219,7 @@ namespace FileTime.ConsoleUI.App.UI
} }
} }
var isSelected = TabState.ContainsSelectedItem(item.Provider, currentContainer, item.FullName!); var isSelected = TabState.ContainsMarkedItem(currentContainer, new AbsolutePath(item));
if (isSelected) if (isSelected)
{ {
backgroundColor = _appStyle.SelectedItemBackground; backgroundColor = _appStyle.SelectedItemBackground;

View File

@@ -1,6 +1,6 @@
namespace AsyncEvent namespace AsyncEvent
{ {
public class AsyncEventHandler<TSender, TArg> where TArg : AsyncEventArgs public class AsyncEventHandler<TSender, TArg>
{ {
private readonly List<Func<TSender, TArg, Task>> _handlers; private readonly List<Func<TSender, TArg, Task>> _handlers;
private readonly Action<Func<TSender, TArg, Task>> _add; private readonly Action<Func<TSender, TArg, Task>> _add;
@@ -56,7 +56,7 @@
return obj; return obj;
} }
} }
public class AsyncEventHandler<TArg> : AsyncEventHandler<object?, AsyncEventArgs> where TArg : AsyncEventArgs public class AsyncEventHandler<TArg> : AsyncEventHandler<object?, TArg>
{ {
public AsyncEventHandler(Action<Func<object?, TArg, Task>>? add = null, Action<Func<object?, TArg, Task>>? remove = null) : base(add, remove) { } public AsyncEventHandler(Action<Func<object?, TArg, Task>>? add = null, Action<Func<object?, TArg, Task>>? remove = null) : base(add, remove) { }
} }

View File

@@ -0,0 +1,9 @@
namespace FileTime.Core.Command
{
public enum CanCommandRun
{
True,
False,
Forceable
}
}

View File

@@ -1,3 +1,5 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public class CommandExecutor public class CommandExecutor
@@ -9,15 +11,15 @@ namespace FileTime.Core.Command
_commandHandlers = commandHandlers.ToList(); _commandHandlers = commandHandlers.ToList();
} }
public void ExecuteCommand(ICommand command) public async Task ExecuteCommandAsync(ICommand command, TimeRunner timeRunner)
{ {
if (command is IExecutableCommand executableCommand) if (command is IExecutableCommand executableCommand)
{ {
executableCommand.Execute(); await executableCommand.Execute(timeRunner);
} }
else else
{ {
_commandHandlers.Find(c => c.CanHandle(command))?.Execute(command); await _commandHandlers.Find(c => c.CanHandle(command))?.ExecuteAsync(command, timeRunner);
} }
} }
} }

View File

@@ -5,59 +5,115 @@ namespace FileTime.Core.Command
{ {
public class CopyCommand : ITransportationCommand public class CopyCommand : ITransportationCommand
{ {
public IList<IAbsolutePath> Sources { get; } = new List<IAbsolutePath>(); private Action<AbsolutePath, AbsolutePath>? _copyOperation;
private Func<IContainer, string, Task<IContainer>>? _createContainer;
private TimeRunner? _timeRunner;
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public IContainer? Target { get; set; } public IContainer? Target { get; set; }
public TransportMode TransportMode { get; set; } = TransportMode.Merge; public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public PointInTime SimulateCommand(PointInTime delta) public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {
throw new NotImplementedException(); if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
var newDiffs = new List<Difference>();
_copyOperation = (_, to) =>
{
var target = to.GetParentAsAbsolutePath().Resolve();
newDiffs.Add(new Difference(
target is IElement
? DifferenceItemType.Element
: DifferenceItemType.Container,
DifferenceActionType.Create,
to
));
};
_createContainer = async (IContainer target, string name) =>
{
var newContainerDiff = new Difference(
DifferenceItemType.Container,
DifferenceActionType.Create,
AbsolutePath.FromParentAndChildName(target, name)
);
newDiffs.Add(newContainerDiff);
return (IContainer)(await newContainerDiff.AbsolutePath.Resolve())!;
};
await DoCopy(Sources, Target, TransportMode.Value);
return startPoint.WithDifferences(newDiffs);
} }
public async Task Execute(Action<IAbsolutePath, IAbsolutePath> copy) public async Task Execute(Action<AbsolutePath, AbsolutePath> copy, TimeRunner timeRunner)
{ {
await DoCopy(Sources, Target, TransportMode, copy); if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
_copyOperation = copy;
_createContainer = async (IContainer target, string name) => await target.CreateContainer(name);
_timeRunner = timeRunner;
await DoCopy(Sources, Target, TransportMode.Value);
} }
private async Task DoCopy(IEnumerable<IAbsolutePath> sources, IContainer target, TransportMode transportMode, Action<IAbsolutePath, IAbsolutePath> copy) private async Task DoCopy(
IEnumerable<AbsolutePath> sources,
IContainer target,
TransportMode transportMode)
{ {
if (_copyOperation == null) throw new ArgumentException("No copy operation were given.");
if (_createContainer == null) throw new ArgumentException("No container creation function were given.");
foreach (var source in sources) foreach (var source in sources)
{ {
var item = await source.ContentProvider.GetByPath(source.Path); var item = await source.Resolve();
if (item is IContainer container) if (item is IContainer container)
{ {
var targetContainer = (await target.GetContainers())?.FirstOrDefault(d => d.Name == container.Name) ?? (await target.CreateContainer(container.Name)!); var targetContainer = (await target.GetContainers())?.FirstOrDefault(d => d.Name == container.Name) ?? (await _createContainer?.Invoke(target, container.Name)!);
var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(item.Provider, d.FullName!)); var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(d));
var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(item.Provider, f.FullName!)); var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(f));
await DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode, copy); await DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode);
_timeRunner?.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
} }
else if (item is IElement element) else if (item is IElement element)
{ {
var targetName = element.Name; var targetName = element.Name;
var targetNameExists = await target.IsExists(targetName); var targetNameExists = await target.IsExists(targetName);
if (transportMode == TransportMode.Merge) if (transportMode == Command.TransportMode.Merge)
{ {
for (var i = 0; targetNameExists; i++) for (var i = 0; targetNameExists; i++)
{ {
targetName = element.Name + (i == 0 ? "_" : $"_{i}"); targetName = element.Name + (i == 0 ? "_" : $"_{i}");
} }
} }
else if (transportMode == TransportMode.Skip && targetNameExists) else if (transportMode == Command.TransportMode.Skip && targetNameExists)
{ {
continue; continue;
} }
var targetPath = target.FullName + Constants.SeparatorChar + targetName; _copyOperation?.Invoke(new AbsolutePath(element), AbsolutePath.FromParentAndChildName(target, targetName));
copy(new AbsolutePath(source.ContentProvider, element.FullName!), new AbsolutePath(target.Provider, targetPath));
} }
} }
} }
public Task<CanCommandRun> CanRun(PointInTime startPoint)
{
//TODO: implement
return Task.FromResult(CanCommandRun.True);
}
} }
} }

View File

@@ -1,12 +1,51 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public class CreateContainerCommand : ICommand public class CreateContainerCommand : IExecutableCommand
{ {
public PointInTime SimulateCommand(PointInTime delta) public AbsolutePath Container { get; }
public string NewContainerName { get; }
public CreateContainerCommand(AbsolutePath container, string newContainerName)
{ {
throw new NotImplementedException(); Container = container;
NewContainerName = newContainerName;
}
public async Task Execute(TimeRunner timeRunner)
{
var possibleContainer = await Container.Resolve();
if (possibleContainer is IContainer container)
{
await container.CreateContainer(NewContainerName);
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
}
//TODO: else
}
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
{
var newDifferences = new List<Difference>()
{
new Difference(DifferenceItemType.Container, DifferenceActionType.Create, new AbsolutePath(Container.ContentProvider, Container.Path + Constants.SeparatorChar + NewContainerName, Container.VirtualContentProvider))
};
return Task.FromResult(startPoint.WithDifferences(newDifferences));
}
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
{
var resolvedContainer = await Container.Resolve();
if (resolvedContainer == null) return CanCommandRun.Forceable;
if (resolvedContainer is not IContainer container
|| await container.IsExists(NewContainerName))
{
return CanCommandRun.False;
}
return CanCommandRun.True;
} }
} }
} }

View File

@@ -1,12 +1,50 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public class CreateElementCommand : ICommand public class CreateElementCommand : IExecutableCommand
{ {
public PointInTime SimulateCommand(PointInTime delta) public AbsolutePath Container { get; }
public string NewElementName { get; }
public CreateElementCommand(AbsolutePath container, string newElementName)
{ {
throw new NotImplementedException(); Container = container;
NewElementName = newElementName;
}
public async Task Execute(TimeRunner timeRunner)
{
var possibleContainer = await Container.Resolve();
if (possibleContainer is IContainer container)
{
await container.CreateElement(NewElementName);
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
}
}
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
{
var newDifferences = new List<Difference>()
{
new Difference(DifferenceItemType.Element, DifferenceActionType.Create, new AbsolutePath(Container.ContentProvider, Container.Path + Constants.SeparatorChar + NewElementName, Container.VirtualContentProvider))
};
return Task.FromResult(startPoint.WithDifferences(newDifferences));
}
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
{
var resolvedContainer = Container.Resolve();
if (resolvedContainer == null) return CanCommandRun.Forceable;
if (resolvedContainer is not IContainer container
|| await container.IsExists(NewElementName))
{
return CanCommandRun.False;
}
return CanCommandRun.True;
} }
} }
} }

View File

@@ -1,3 +1,4 @@
using FileTime.Core.Extensions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -5,37 +6,64 @@ namespace FileTime.Core.Command
{ {
public class DeleteCommand : IExecutableCommand public class DeleteCommand : IExecutableCommand
{ {
public IList<IAbsolutePath> ItemsToDelete { get; } = new List<IAbsolutePath>(); public IList<AbsolutePath> ItemsToDelete { get; } = new List<AbsolutePath>();
public PointInTime SimulateCommand(PointInTime delta) public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {
throw new NotImplementedException(); var newDifferences = new List<Difference>();
foreach (var itemToDelete in ItemsToDelete)
{
var item = await itemToDelete.Resolve();
newDifferences.Add(new Difference(
item.ToDifferenceItemType(),
DifferenceActionType.Delete,
itemToDelete
));
}
return startPoint.WithDifferences(newDifferences);
} }
public async Task Execute() public async Task Execute(TimeRunner timeRunner)
{ {
foreach (var item in ItemsToDelete) foreach (var item in ItemsToDelete)
{ {
await DoDelete(await item.ContentProvider.GetByPath(item.Path)!); await DoDelete((await item.Resolve())!, timeRunner);
} }
} }
private async Task DoDelete(IItem item) private async Task DoDelete(IItem item, TimeRunner timeRunner)
{ {
if (item is IContainer container) if (item is IContainer container)
{ {
foreach (var child in await container.GetItems()) foreach (var child in (await container.GetItems())!)
{ {
await DoDelete(child); await DoDelete(child, timeRunner);
await child.Delete(); await child.Delete();
} }
await item.Delete(); await item.Delete();
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
} }
else if(item is IElement element) else if (item is IElement element)
{ {
await element.Delete(); await element.Delete();
} }
} }
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
{
var result = CanCommandRun.True;
foreach (var itemPath in ItemsToDelete)
{
var resolvedItem = await itemPath.Resolve();
if (!(resolvedItem?.CanDelete ?? true))
{
result = CanCommandRun.Forceable;
}
}
return result;
}
} }
} }

View File

@@ -4,6 +4,7 @@ namespace FileTime.Core.Command
{ {
public interface ICommand public interface ICommand
{ {
PointInTime SimulateCommand(PointInTime moment); Task<CanCommandRun> CanRun(PointInTime startPoint);
Task<PointInTime> SimulateCommand(PointInTime startPoint);
} }
} }

View File

@@ -1,8 +1,10 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public interface ICommandHandler public interface ICommandHandler
{ {
bool CanHandle(object command); bool CanHandle(object command);
void Execute(object command); Task ExecuteAsync(object command, TimeRunner timeRunner);
} }
} }

View File

@@ -1,7 +1,9 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public interface IExecutableCommand : ICommand public interface IExecutableCommand : ICommand
{ {
Task Execute(); Task Execute(TimeRunner timeRunner);
} }
} }

View File

@@ -4,8 +4,8 @@ namespace FileTime.Core.Command
{ {
public interface ITransportationCommand : ICommand public interface ITransportationCommand : ICommand
{ {
IList<IAbsolutePath> Sources { get; } IList<AbsolutePath> Sources { get; }
IContainer Target { get; set;} IContainer? Target { get; set;}
TransportMode TransportMode { get; set; } TransportMode? TransportMode { get; set; }
} }
} }

View File

@@ -5,12 +5,17 @@ namespace FileTime.Core.Command
{ {
public class MoveCommand : ITransportationCommand public class MoveCommand : ITransportationCommand
{ {
public IList<IAbsolutePath> Sources { get; } = new List<IAbsolutePath>(); public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public IContainer? Target { get; set; } public IContainer? Target { get; set; }
public TransportMode TransportMode { get; set; } = TransportMode.Merge; public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public PointInTime SimulateCommand(PointInTime delta) public Task<CanCommandRun> CanRun(PointInTime startPoint)
{
throw new NotImplementedException();
}
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -0,0 +1,37 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class RenameCommand : IExecutableCommand
{
public AbsolutePath Source { get; }
public string Target { get; }
public RenameCommand(AbsolutePath source, string target)
{
Source = source;
Target = target;
}
public async Task Execute(TimeRunner timeRunner)
{
var itemToRename = await Source.Resolve();
if (itemToRename != null)
{
await itemToRename.Rename(Target);
timeRunner.RefreshContainer?.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!));
}
}
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
{
throw new NotImplementedException();
}
public Task<CanCommandRun> CanRun(PointInTime startPoint)
{
throw new NotImplementedException();
}
}
}

View File

@@ -6,7 +6,10 @@ namespace FileTime.Core.Components
public class Tab public class Tab
{ {
private IItem? _currentSelectedItem; private IItem? _currentSelectedItem;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private IContainer _currentLocation; private IContainer _currentLocation;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private string? _lastPath;
public int CurrentSelectedIndex { get; private set; } public int CurrentSelectedIndex { get; private set; }
@@ -33,10 +36,10 @@ namespace FileTime.Core.Components
} }
_currentLocation = value; _currentLocation = value;
await CurrentLocationChanged?.InvokeAsync(this, AsyncEventArgs.Empty); await CurrentLocationChanged.InvokeAsync(this, AsyncEventArgs.Empty);
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!; var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
await SetCurrentSelectedItem(currentLocationItems.Count > 0 ? currentLocationItems[0] : null); await SetCurrentSelectedItem(await GetItemByLastPath() ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
_currentLocation.Refreshed.Add(HandleCurrentLocationRefresh); _currentLocation.Refreshed.Add(HandleCurrentLocationRefresh);
} }
} }
@@ -50,18 +53,82 @@ namespace FileTime.Core.Components
{ {
if (_currentSelectedItem != value) if (_currentSelectedItem != value)
{ {
var contains = (await _currentLocation.GetItems())?.Contains(value) ?? false; IItem? itemToSelect = null;
if(value != null && !contains) throw new IndexOutOfRangeException("Provided item does not exists in the current container."); if (value != null)
{
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i =>
i.FullName == null && value?.FullName == null
? i.Name == value?.Name
: i.FullName == value?.FullName);
if (itemToSelect == null) throw new IndexOutOfRangeException("Provided item does not exists in the current container.");
}
_currentSelectedItem = value; _currentSelectedItem = itemToSelect;
CurrentSelectedIndex = await GetItemIndex(value); _lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName);
await CurrentSelectedItemChanged?.InvokeAsync(this, AsyncEventArgs.Empty); CurrentSelectedIndex = await GetItemIndex(itemToSelect);
await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty);
} }
} }
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
{
container ??= _currentLocation;
var containerFullName = container.FullName;
if (_lastPath == null
|| !container.IsLoaded
|| (containerFullName != null && !_lastPath.StartsWith(containerFullName))
)
{
return null;
}
var itemNameToSelect = _lastPath
.Split(Constants.SeparatorChar)
.Skip(
containerFullName == null
? 0
: containerFullName
.Split(Constants.SeparatorChar)
.Count())
.FirstOrDefault();
return (await container.GetItems())?.FirstOrDefault(i => i.Name == itemNameToSelect);
}
private string GetCommonPath(string? oldPath, string? newPath)
{
var oldPathParts = oldPath?.Split(Constants.SeparatorChar) ?? new string[0];
var newPathParts = newPath?.Split(Constants.SeparatorChar) ?? new string[0];
var commonPathParts = new List<string>();
var max = oldPathParts.Length > newPathParts.Length ? oldPathParts.Length : newPathParts.Length;
for (var i = 0; i < max; i++)
{
if (newPathParts.Length <= i)
{
commonPathParts.AddRange(oldPathParts.Skip(i));
break;
}
else if (oldPathParts.Length <= i || oldPathParts[i] != newPathParts[i])
{
commonPathParts.AddRange(newPathParts.Skip(i));
break;
}
else if (oldPathParts[i] == newPathParts[i])
{
commonPathParts.Add(oldPathParts[i]);
}
}
return string.Join(Constants.SeparatorChar, commonPathParts);
}
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e) private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e)
{ {
var currentSelectedName = (await GetCurrentSelectedItem())?.FullName; var currentSelectedName = (await GetCurrentSelectedItem())?.FullName ?? (await GetItemByLastPath())?.FullName;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!; var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (currentSelectedName != null) if (currentSelectedName != null)
{ {

View File

@@ -0,0 +1,15 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Extensions
{
public static class TimelineExtensions
{
public static DifferenceItemType ToDifferenceItemType(this IItem? item)
{
if (item is IContainer) return DifferenceItemType.Container;
else if (item is IElement) return DifferenceItemType.Element;
else return DifferenceItemType.Unknown;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace FileTime.Core.Interactions
{
public class BasicInputHandler : IInputInterface
{
public Func<IEnumerable<InputElement>, Task<string?[]>>? InputHandler { get; set; }
public async Task<string?[]> ReadInputs(IEnumerable<InputElement> fields) =>
InputHandler != null ? await InputHandler.Invoke(fields) : throw new NotImplementedException(nameof(InputHandler) + " is not set");
}
}

View File

@@ -4,11 +4,13 @@ namespace FileTime.Core.Interactions
{ {
public string Text { get; } public string Text { get; }
public InputType InputType { get; } public InputType InputType { get; }
public string? DefaultValue { get; }
public InputElement(string text, InputType inputType) public InputElement(string text, InputType inputType, string? defaultValue = null)
{ {
Text = text; Text = text;
InputType = inputType; InputType = inputType;
DefaultValue = defaultValue;
} }
} }
} }

View File

@@ -3,6 +3,7 @@ namespace FileTime.Core.Interactions
public enum InputType public enum InputType
{ {
Text, Text,
Password Password,
Bool
} }
} }

View File

@@ -1,17 +1,97 @@
using FileTime.Core.Providers; using FileTime.Core.Providers;
using FileTime.Core.Timeline;
namespace FileTime.Core.Models namespace FileTime.Core.Models
{ {
public class AbsolutePath : IAbsolutePath public sealed class AbsolutePath
{ {
public IContentProvider ContentProvider { get; } public IContentProvider ContentProvider { get; }
public IContentProvider? VirtualContentProvider { get; }
public string Path { get; } public string Path { get; }
public AbsolutePath(IContentProvider contentProvider, string path) public AbsolutePath(AbsolutePath from)
{
ContentProvider = from.ContentProvider;
Path = from.Path;
VirtualContentProvider = from.VirtualContentProvider;
}
public AbsolutePath(IContentProvider contentProvider, string path, IContentProvider? virtualContentProvider)
{ {
ContentProvider = contentProvider; ContentProvider = contentProvider;
Path = path; Path = path;
VirtualContentProvider = virtualContentProvider;
} }
public AbsolutePath(IItem item)
{
if (item is TimeContainer timeContainer)
{
ContentProvider = timeContainer.Provider;
VirtualContentProvider = timeContainer.VirtualProvider;
Path = timeContainer.FullName!;
}
else if (item is TimeElement timeElement)
{
ContentProvider = timeElement.Provider;
VirtualContentProvider = timeElement.VirtualProvider;
Path = timeElement.FullName!;
}
else
{
ContentProvider = item.Provider;
Path = item.FullName!;
}
}
public static AbsolutePath FromParentAndChildName(IContainer parent, string childName)
{
IContentProvider? contentProvider;
IContentProvider? virtualContentProvider;
string? path;
if (parent is TimeContainer timeContainer)
{
contentProvider = timeContainer.Provider;
virtualContentProvider = timeContainer.VirtualProvider;
path = timeContainer.FullName! + Constants.SeparatorChar + childName;
}
else
{
contentProvider = parent.Provider;
path = parent.FullName! + Constants.SeparatorChar + childName;
virtualContentProvider = null;
}
return new AbsolutePath(contentProvider, path, virtualContentProvider);
}
public bool IsEqual(AbsolutePath path)
{
//TODO: sure??
return path.ContentProvider == ContentProvider && path.Path == Path;
}
public async Task<IItem?> Resolve()
{
var result = VirtualContentProvider != null && (await VirtualContentProvider.IsExists(Path))
? await VirtualContentProvider.GetByPath(Path)
: null;
result ??= await ContentProvider.GetByPath(Path);
return result;
}
public string GetParent()
{
var pathParts = Path.Split(Constants.SeparatorChar);
return string.Join(Constants.SeparatorChar, pathParts);
}
public AbsolutePath GetParentAsAbsolutePath() => new(ContentProvider, GetParent(), VirtualContentProvider);
public string GetName() => Path.Split(Constants.SeparatorChar).Last();
} }
} }

View File

@@ -1,10 +0,0 @@
using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public interface IAbsolutePath
{
IContentProvider ContentProvider { get; }
string Path { get; }
}
}

View File

@@ -9,7 +9,6 @@ namespace FileTime.Core.Models
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default); Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
Task Refresh(); Task Refresh();
IContainer? GetParent();
Task<IItem?> GetByPath(string path); Task<IItem?> GetByPath(string path);
Task<IContainer> CreateContainer(string name); Task<IContainer> CreateContainer(string name);
Task<IElement> CreateElement(string name); Task<IElement> CreateElement(string name);
@@ -18,6 +17,8 @@ namespace FileTime.Core.Models
Task<IContainer> Clone(); Task<IContainer> Clone();
bool IsLoaded { get; }
AsyncEventHandler Refreshed { get; } AsyncEventHandler Refreshed { get; }
} }
} }

View File

@@ -7,7 +7,11 @@ namespace FileTime.Core.Models
string Name { get; } string Name { get; }
string? FullName { get; } string? FullName { get; }
bool IsHidden { get; } bool IsHidden { get; }
bool CanDelete { get; }
bool CanRename { get; }
IContentProvider Provider { get; } IContentProvider Provider { get; }
Task Delete(); Task Delete();
Task Rename(string newName);
IContainer? GetParent();
} }
} }

View File

@@ -24,6 +24,9 @@ namespace FileTime.Core.Models
public string? FullName => BaseContainer.FullName; public string? FullName => BaseContainer.FullName;
public bool IsHidden => BaseContainer.IsHidden; public bool IsHidden => BaseContainer.IsHidden;
public bool IsLoaded => BaseContainer.IsLoaded;
public bool CanDelete => BaseContainer.CanDelete;
public bool CanRename => BaseContainer.CanRename;
public IContentProvider Provider => BaseContainer.Provider; public IContentProvider Provider => BaseContainer.Provider;
@@ -159,5 +162,7 @@ namespace FileTime.Core.Models
VirtualContainerName VirtualContainerName
); );
} }
public async Task Rename(string newName) => await BaseContainer.Rename(newName);
} }
} }

View File

@@ -1,4 +1,5 @@
using System;
using AsyncEvent; using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
@@ -11,13 +12,20 @@ namespace FileTime.Core.Providers
private readonly IReadOnlyList<IItem>? _items; private readonly IReadOnlyList<IItem>? _items;
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly(); private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
#pragma warning disable CS8603 // Possible null reference return.
public string Name => null; public string Name => null;
#pragma warning restore CS8603 // Possible null reference return.
public string? FullName => null; public string? FullName => null;
public bool IsHidden => false; public bool IsHidden => false;
public bool IsLoaded => true;
#pragma warning disable CS8603 // Possible null reference return.
public IContentProvider Provider => null; public IContentProvider Provider => null;
#pragma warning restore CS8603 // Possible null reference return.
public bool CanDelete => false;
public bool CanRename => false;
public AsyncEventHandler Refreshed { get; } = new(); public AsyncEventHandler Refreshed { get; } = new();
@@ -52,5 +60,7 @@ namespace FileTime.Core.Providers
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements); public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
public Task<IContainer> Clone() => Task.FromResult((IContainer)this); public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task Rename(string newName) => throw new NotSupportedException();
} }
} }

View File

@@ -0,0 +1,27 @@
using FileTime.Core.Command;
namespace FileTime.Core.Timeline
{
public class CommandTimeState
{
public ICommand Command { get; }
public CanCommandRun CanRun { get; private set; } = CanCommandRun.False;
public bool ForceRun { get; set; }
public TimeProvider? TimeProvider { get; private set; }
public CommandTimeState(ICommand command, PointInTime? startTime)
{
Command = command;
UpdateState(startTime).Wait();
}
public async Task UpdateState(PointInTime? startPoint)
{
CanRun = startPoint == null ? CanCommandRun.False : await Command.CanRun(startPoint);
if (startPoint != null)
{
TimeProvider = startPoint.Provider as TimeProvider;
}
}
}
}

View File

@@ -1,7 +0,0 @@
namespace FileTime.Core.Timeline
{
public class ContainerSnapshot
{
}
}

View File

@@ -0,0 +1,31 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.Core.Timeline
{
public class Difference
{
public DifferenceItemType Type { get; }
public string Name { get; }
public AbsolutePath AbsolutePath { get; }
public DifferenceActionType Action { get; }
public Difference(DifferenceItemType type, DifferenceActionType action, AbsolutePath absolutePath)
{
Type = type;
AbsolutePath = absolutePath;
Action = action;
Name = absolutePath.GetName();
}
public Difference WithVirtualContentProvider(IContentProvider? virtualContentProvider)
{
return new Difference(
Type,
Action,
new AbsolutePath(AbsolutePath.ContentProvider, AbsolutePath.Path, virtualContentProvider)
);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace FileTime.Core.Timeline
{
public enum DifferenceActionType
{
Create,
Delete
}
}

View File

@@ -0,0 +1,9 @@
namespace FileTime.Core.Timeline
{
public enum DifferenceItemType
{
Container,
Element,
Unknown
}
}

View File

@@ -0,0 +1,89 @@
using FileTime.Core.Command;
namespace FileTime.Core.Timeline
{
public class ParallelCommands
{
private static ushort _idCounter;
public List<CommandTimeState> _commands;
public ushort Id { get; }
public IReadOnlyList<CommandTimeState> Commands { get; }
public PointInTime? Result { get; private set; }
public ParallelCommands(PointInTime? result)
: this(new List<CommandTimeState>(), result) { }
private ParallelCommands(List<CommandTimeState> commands, PointInTime? result)
{
Id = _idCounter++;
_commands = commands;
Commands = _commands.AsReadOnly();
Result = result;
}
public static async Task<ParallelCommands> Create(PointInTime? startTime, IEnumerable<ICommand> commands)
{
var commandStates = new List<CommandTimeState>();
var currentTime = startTime;
foreach (var command in commands)
{
CommandTimeState commandTimeState = new(command, currentTime);
if (currentTime != null)
{
var canRun = await command.CanRun(currentTime);
if (canRun == CanCommandRun.True)
{
currentTime = await command.SimulateCommand(currentTime);
}
else
{
currentTime = null;
}
}
commandStates.Add(commandTimeState);
}
return new ParallelCommands(commandStates, currentTime);
}
public async Task AddCommand(ICommand command)
{
_commands.Add(new CommandTimeState(command, Result));
if (Result != null)
{
Result = await command.SimulateCommand(Result);
}
}
public async Task<PointInTime?> RefreshResult(PointInTime? startPoint)
{
var result = startPoint;
foreach (var command in _commands)
{
await command.UpdateState(result);
if (result != null)
{
var canRun = await command.Command.CanRun(result);
if (canRun == CanCommandRun.True || (canRun == CanCommandRun.Forceable && command.ForceRun))
{
result = await command.Command.SimulateCommand(result);
}
else
{
result = null;
}
}
}
Result = result;
return Result;
}
public void RemoveAt(int number) => _commands.RemoveAt(number);
internal void Remove(CommandTimeState command) => _commands.Remove(command);
}
}

View File

@@ -1,12 +1,40 @@
using System.Collections.ObjectModel;
using FileTime.Core.Providers; using FileTime.Core.Providers;
namespace FileTime.Core.Timeline namespace FileTime.Core.Timeline
{ {
public class PointInTime public sealed class PointInTime
{ {
private readonly Dictionary<IContentProvider, RootSnapshot> snapshots = new(); private readonly List<Difference> _differences;
public IReadOnlyDictionary<IContentProvider, RootSnapshot> Snapshots => new Lazy<IReadOnlyDictionary<IContentProvider, RootSnapshot>>(() => new ReadOnlyDictionary<IContentProvider, RootSnapshot>(snapshots)).Value; public IReadOnlyList<Difference> Differences { get; }
public IContentProvider? Provider { get; }
private PointInTime() : this(new List<Difference>(), null) { }
private PointInTime(IEnumerable<Difference> differences, IContentProvider? provider)
{
_differences = new List<Difference>(differences);
Differences = _differences.AsReadOnly();
Provider = provider;
}
private PointInTime(PointInTime previous, IEnumerable<Difference> differences, IContentProvider provider)
: this(MergeDifferences(previous.Differences, differences, provider), provider) { }
public PointInTime WithDifferences(IEnumerable<Difference> differences) => new(this, differences, new TimeProvider(this));
private static List<Difference> MergeDifferences(IEnumerable<Difference> previouses, IEnumerable<Difference> differences, IContentProvider virtualProvider)
{
var merged = new List<Difference>();
merged.AddRange(previouses.Select(p => p.WithVirtualContentProvider(virtualProvider)));
merged.AddRange(differences.Select(d => d.WithVirtualContentProvider(virtualProvider)));
return merged;
}
public static PointInTime CreateEmpty(IContentProvider? parentProvder = null) =>
parentProvder == null ? new PointInTime() : new PointInTime(new List<Difference>(), parentProvder);
} }
} }

View File

@@ -0,0 +1,18 @@
using FileTime.Core.Command;
namespace FileTime.Core.Timeline
{
public class ReadOnlyCommandTimeState
{
public CanCommandRun CanRun { get; }
public bool ForceRun { get; }
public ICommand Command { get; }
public ReadOnlyCommandTimeState(CommandTimeState commandTimeState)
{
CanRun = commandTimeState.CanRun;
ForceRun = commandTimeState.ForceRun;
Command = commandTimeState.Command;
}
}
}

View File

@@ -0,0 +1,11 @@
namespace FileTime.Core.Timeline
{
public class ReadOnlyParallelCommands
{
public IReadOnlyList<ReadOnlyCommandTimeState> Commands { get; }
public ReadOnlyParallelCommands(ParallelCommands parallelCommands)
{
Commands = parallelCommands.Commands.Select(c => new ReadOnlyCommandTimeState(c)).ToList().AsReadOnly();
}
}
}

View File

@@ -1,7 +0,0 @@
namespace FileTime.Core.Timeline
{
public class RootSnapshot
{
}
}

View File

@@ -0,0 +1,121 @@
using AsyncEvent;
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.Core.Timeline
{
public class TimeContainer : IContainer
{
private readonly IContainer? _parent;
private readonly PointInTime _pointInTime;
public bool IsLoaded => true;
public AsyncEventHandler Refreshed { get; } = new AsyncEventHandler();
public string Name { get; }
public string? FullName { get; }
public bool IsHidden => false;
public bool CanDelete => true;
public bool CanRename => true;
public IContentProvider Provider { get; }
public IContentProvider VirtualProvider { get; }
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
{
_parent = parent;
_pointInTime = pointInTime;
Name = name;
Provider = contentProvider;
VirtualProvider = virtualContentProvider;
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
}
public async Task<IContainer> Clone() => new TimeContainer(Name, await _parent!.Clone(), Provider, VirtualProvider, _pointInTime);
public Task<IContainer> CreateContainer(string name) => Task.FromResult((IContainer)new TimeContainer(name, this, Provider, VirtualProvider, _pointInTime));
public Task<IElement> CreateElement(string name) => Task.FromResult((IElement)new TimeElement(name, this, Provider, VirtualProvider));
public Task Delete() => Task.CompletedTask;
public async Task<IItem?> GetByPath(string path)
{
var paths = path.Split(Constants.SeparatorChar);
var item = (await GetItems())!.FirstOrDefault(i => i.Name == paths[0]);
if (paths.Length == 1)
{
return item;
}
if (item is IContainer container)
{
return await container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1)));
}
return null;
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) =>
Task.FromResult(
(IReadOnlyList<IContainer>?)_pointInTime
.Differences
.Where(d =>
d.Type == DifferenceItemType.Container
&& GetParentPath(d.AbsolutePath.Path) == FullName)
.Select(MapContainer)
.ToList()
.AsReadOnly()
);
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) =>
Task.FromResult(
(IReadOnlyList<IElement>?)_pointInTime
.Differences
.Where(d =>
d.Type == DifferenceItemType.Element
&& GetParentPath(d.AbsolutePath.Path) == FullName)
.Select(MapElement)
.ToList()
.AsReadOnly()
);
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
var containers = (await GetContainers(token))!;
var elements = (await GetElements(token))!;
return containers.Cast<IItem>().Concat(elements).ToList().AsReadOnly();
}
public IContainer? GetParent() => _parent;
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
public async Task Refresh() => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
public Task Rename(string newName) => Task.CompletedTask;
private static string GetParentPath(string path) => string.Join(Constants.SeparatorChar, path.Split(Constants.SeparatorChar).Take(-1));
private IContainer MapContainer(Difference containerDiff)
{
if (containerDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{nameof(containerDiff)}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Container}.");
return new TimeContainer(containerDiff.Name, this, Provider, containerDiff.AbsolutePath.VirtualContentProvider ?? containerDiff.AbsolutePath.ContentProvider, _pointInTime);
}
private IElement MapElement(Difference elementDiff)
{
if (elementDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{elementDiff}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Element}.");
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
}
}
}

View File

@@ -0,0 +1,42 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.Core.Timeline
{
public class TimeElement : IElement
{
private readonly IContainer _parent;
public TimeElement(string name, TimeContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider)
{
_parent = parent;
Name = name;
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
Provider = contentProvider;
VirtualProvider = virtualContentProvider;
}
public bool IsSpecial => false;
public string Name { get; }
public string? FullName { get; }
public bool IsHidden => false;
public bool CanDelete => true;
public bool CanRename => true;
public IContentProvider Provider { get; }
public IContentProvider VirtualProvider { get; }
public Task Delete() => Task.CompletedTask;
public IContainer? GetParent() => _parent;
public string GetPrimaryAttributeText() => "";
public Task Rename(string newName) => Task.CompletedTask;
}
}

View File

@@ -0,0 +1,89 @@
using AsyncEvent;
using FileTime.Core.Models;
using FileTime.Core.Providers;
namespace FileTime.Core.Timeline
{
public class TimeProvider : IContentProvider
{
public bool IsLoaded => true;
public AsyncEventHandler Refreshed { get; } = new();
public string Name => "time";
public string? FullName => null;
public bool IsHidden => false;
public bool CanDelete => false;
public bool CanRename => false;
public IContentProvider Provider => this;
private readonly PointInTime _pointInTime;
public TimeProvider(PointInTime pointInTime)
{
_pointInTime = pointInTime;
}
public bool CanHandlePath(string path)
{
throw new NotImplementedException();
}
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task<IContainer> CreateContainer(string name)
{
throw new NotImplementedException();
}
public Task<IElement> CreateElement(string name)
{
throw new NotImplementedException();
}
public Task Delete() => throw new NotSupportedException();
public Task<IItem?> GetByPath(string path)
{
throw new NotImplementedException();
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
throw new NotImplementedException();
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
throw new NotImplementedException();
}
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
throw new NotImplementedException();
}
public IContainer? GetParent() => null;
public Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default)
{
throw new NotImplementedException();
}
public Task<bool> IsExists(string name)
{
throw new NotImplementedException();
}
public Task Refresh() => Task.CompletedTask;
public Task Rename(string newName) => throw new NotSupportedException();
public void SetParent(IContainer container) { }
}
}

View File

@@ -0,0 +1,217 @@
using AsyncEvent;
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.Core.Timeline
{
public class TimeRunner
{
private readonly CommandExecutor _commandExecutor;
private readonly List<ParallelCommands> _commandsToRun = new();
private readonly object _guard = new();
private bool _resourceIsInUse;
private readonly List<Thread> _commandRunners = new();
private bool _enableRunning = true;
public bool EnableRunning
{
get
{
bool result = true;
RunWithLock(() => result = _enableRunning);
return result;
}
set
{
RunWithLock(() => _enableRunning = value);
}
}
public IReadOnlyList<ReadOnlyParallelCommands> ParallelCommands { get; private set; } = new List<ReadOnlyParallelCommands>().AsReadOnly();
public AsyncEventHandler<AbsolutePath> RefreshContainer { get; } = new AsyncEventHandler<AbsolutePath>();
public event EventHandler? CommandsChanged;
public TimeRunner(CommandExecutor commandExecutor)
{
_commandExecutor = commandExecutor;
}
public async Task AddCommand(ICommand command, ParallelCommands? batch = null, bool toNewBatch = false)
{
await RunWithLockAsync(async () =>
{
ParallelCommands batchToAdd;
if (_commandsToRun.Count == 0)
{
batchToAdd = new ParallelCommands(PointInTime.CreateEmpty());
_commandsToRun.Add(batchToAdd);
}
else if (toNewBatch)
{
batchToAdd = new ParallelCommands(_commandsToRun.Last().Result);
_commandsToRun.Add(batchToAdd);
}
else if (batch != null && _commandsToRun.Contains(batch))
{
batchToAdd = batch;
}
else
{
batchToAdd = _commandsToRun[0];
}
await batchToAdd.AddCommand(command);
await RefreshCommands();
if (_commandRunners.Count == 0)
{
StartCommandRunner();
}
});
await UpdateReadOnlyCommands();
}
public async Task TryStartCommandRunner()
{
await RunWithLockAsync(() =>
{
if (_commandRunners.Count == 0 && _commandsToRun.Count > 0)
{
StartCommandRunner();
}
});
}
private void StartCommandRunner()
{
if (_enableRunning)
{
RunCommands();
}
}
private void RunCommands()
{
if (_commandsToRun.Count > 0)
{
foreach (var command in _commandsToRun[0].Commands)
{
if (command.CanRun == CanCommandRun.True || (command.CanRun == CanCommandRun.Forceable && command.ForceRun))
{
var thread = new Thread(new ParameterizedThreadStart(RunCommand));
thread.Start(command);
}
else
{
break;
}
}
}
}
private void RunCommand(object? arg)
{
CommandTimeState? commandToRun = null;
try
{
if (arg is CommandTimeState commandToRun2)
{
commandToRun = commandToRun2;
_commandExecutor.ExecuteCommandAsync(commandToRun.Command, this).Wait();
}
}
finally
{
DisposeCommandThread(Thread.CurrentThread, commandToRun).Wait();
}
}
private async Task DisposeCommandThread(Thread thread, CommandTimeState? command)
{
await RunWithLockAsync(() =>
{
if (command != null)
{
_commandsToRun[0].Remove(command);
}
_commandRunners.Remove(thread);
});
await UpdateReadOnlyCommands();
await TryStartCommandRunner();
}
public async Task Refresh()
{
await RunWithLockAsync(async () =>
{
await RefreshCommands(PointInTime.CreateEmpty());
});
await UpdateReadOnlyCommands();
}
private async Task RefreshCommands(PointInTime? fullStartTime = null)
{
var curretnTime = fullStartTime ?? _commandsToRun[0].Result;
var startIndex = fullStartTime == null ? 1 : 0;
for (var i = startIndex; i < _commandsToRun.Count; i++)
{
curretnTime = await _commandsToRun[i].RefreshResult(curretnTime);
}
}
private async Task UpdateReadOnlyCommands()
{
await RunWithLockAsync(() =>
{
ParallelCommands = _commandsToRun.ConvertAll(c => new ReadOnlyParallelCommands(c)).AsReadOnly();
});
CommandsChanged?.Invoke(this, EventArgs.Empty);
}
private async Task RunWithLockAsync(Action action)
{
await RunWithLockAsync(() => { action(); return Task.CompletedTask; });
}
private async Task RunWithLockAsync(Func<Task> func)
{
while (true)
{
lock (_guard)
{
if (!_resourceIsInUse)
{
_resourceIsInUse = true;
break;
}
}
await Task.Delay(1);
}
try
{
await func();
}
finally
{
lock (_guard)
{
_resourceIsInUse = false;
}
}
}
private void RunWithLock(Action action) => RunWithLockAsync(action).Wait();
}
}

View File

@@ -47,6 +47,8 @@ Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "FileTime.Uno.Windows.Packag
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEvent", "Core\AsyncEvent\AsyncEvent.csproj", "{9BDAC126-200F-4056-8D35-36EC059B40F3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEvent", "Core\AsyncEvent\AsyncEvent.csproj", "{9BDAC126-200F-4056-8D35-36EC059B40F3}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Avalonia", "GuiApp\FileTime.Avalonia\FileTime.Avalonia.csproj", "{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
GuiApp\FileTime.Uno\FileTime.Uno.Shared\FileTime.Uno.Shared.projitems*{02acef48-3be8-43e5-9358-a914bcbea7ca}*SharedItemsImports = 5 GuiApp\FileTime.Uno\FileTime.Uno.Shared\FileTime.Uno.Shared.projitems*{02acef48-3be8-43e5-9358-a914bcbea7ca}*SharedItemsImports = 5
@@ -378,6 +380,26 @@ Global
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x64.Build.0 = Release|Any CPU {9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x64.Build.0 = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.ActiveCfg = Release|Any CPU {9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.Build.0 = Release|Any CPU {9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.Build.0 = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|ARM.ActiveCfg = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|ARM.Build.0 = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|ARM64.Build.0 = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|x64.ActiveCfg = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|x64.Build.0 = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|x86.ActiveCfg = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Debug|x86.Build.0 = Debug|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|Any CPU.Build.0 = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|ARM.ActiveCfg = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|ARM.Build.0 = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|ARM64.ActiveCfg = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|ARM64.Build.0 = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|x64.ActiveCfg = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|x64.Build.0 = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|x86.ActiveCfg = Release|Any CPU
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -400,6 +422,7 @@ Global
{02ACEF48-3BE8-43E5-9358-A914BCBEA7CA} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497} {02ACEF48-3BE8-43E5-9358-A914BCBEA7CA} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497}
{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497} {6BB770CB-B0C1-4A82-86B1-3C56F4393BE1} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497}
{9BDAC126-200F-4056-8D35-36EC059B40F3} = {38B1B927-4201-4B7A-87EE-737B8C6D4090} {9BDAC126-200F-4056-8D35-36EC059B40F3} = {38B1B927-4201-4B7A-87EE-737B8C6D4090}
{22B33BC6-3987-4BE6-8C54-BFC75C78CCE7} = {890275FF-943A-4D07-83BA-14E5C52D7846}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D679DCE-AC84-4A91-BFED-8F8D8E1D8183} SolutionGuid = {8D679DCE-AC84-4A91-BFED-8F8D8E1D8183}

454
src/GuiApp/FileTime.Avalonia/.gitignore vendored Normal file
View File

@@ -0,0 +1,454 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

View File

@@ -0,0 +1,149 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FileTime.Avalonia"
xmlns:converters="using:FileTime.Avalonia.Converters"
xmlns:views="using:FileTime.Avalonia.Views"
x:Class="FileTime.Avalonia.App">
<Application.DataTemplates>
</Application.DataTemplates>
<Application.Resources>
<ResourceDictionary>
<Color x:Key="AppBackgroundColor">#073642</Color>
<Color x:Key="ContainerBackgroundColor">#083e4c</Color>
<Color x:Key="ItemBackgroundColor">#00000000</Color>
<Color x:Key="AlternativeItemBackgroundColor">#10000000</Color>
<Color x:Key="SelectedItemBackgroundColor">#93a1a1</Color>
<Color x:Key="MarkedItemBackgroundColor">#00000000</Color>
<Color x:Key="MarkedAlternativeItemBackgroundColor">#10000000</Color>
<Color x:Key="MarkedSelectedItemBackgroundColor">#b58900</Color>
<Color x:Key="ForegroundColor">#93a1a1</Color>
<Color x:Key="AccentForegroundColor">#268bd2</Color>
<Color x:Key="LightForegroundColor">#7793a1a1</Color>
<Color x:Key="AlternativeItemForegroundColor">#93a1a1</Color>
<Color x:Key="SelectedItemForegroundColor">#073642</Color>
<Color x:Key="MarkedItemForegroundColor">#b58900</Color>
<Color x:Key="MarkedAlternativeItemForegroundColor">#b58900</Color>
<Color x:Key="MarkedSelectedItemForegroundColor">#002b36</Color>
<Color x:Key="ErrorColor">#dc322f</Color>
<LinearGradientBrush x:Key="ContentSeparatorBrush">
<GradientStop Offset="0" Color="#0093a1a1" />
<GradientStop Offset="0.5" Color="#93a1a1" />
<GradientStop Offset="1" Color="#0093a1a1" />
</LinearGradientBrush>
<SolidColorBrush
x:Key="AppBackgroundBrush"
Color="{DynamicResource AppBackgroundColor}" />
<SolidColorBrush
x:Key="ContainerBackgroundBrush"
Color="{DynamicResource ContainerBackgroundColor}" />
<SolidColorBrush
x:Key="ItemBackgroundBrush"
Color="{DynamicResource ItemBackgroundColor}" />
<SolidColorBrush
x:Key="AlternativeItemBackgroundBrush"
Color="{DynamicResource AlternativeItemBackgroundColor}" />
<SolidColorBrush
x:Key="SelectedItemBackgroundBrush"
Color="{DynamicResource SelectedItemBackgroundColor}" />
<SolidColorBrush
x:Key="MarkedItemBackgroundBrush"
Color="{DynamicResource MarkedItemBackgroundColor}" />
<SolidColorBrush
x:Key="MarkedSelectedItemBackgroundBrush"
Color="{DynamicResource MarkedSelectedItemBackgroundColor}" />
<SolidColorBrush
x:Key="MarkedAlternativeItemBackgroundBrush"
Color="{DynamicResource MarkedAlternativeItemBackgroundColor}" />
<SolidColorBrush
x:Key="ForegroundBrush"
Color="{DynamicResource ForegroundColor}" />
<SolidColorBrush
x:Key="AccentForegroundBrush"
Color="{DynamicResource AccentForegroundColor}" />
<SolidColorBrush
x:Key="LightForegroundBrush"
Color="{DynamicResource LightForegroundColor}" />
<SolidColorBrush
x:Key="AlternativeItemForegroundBrush"
Color="{DynamicResource AlternativeItemForegroundColor}" />
<SolidColorBrush
x:Key="SelectedItemForegroundBrush"
Color="{DynamicResource SelectedItemForegroundColor}" />
<SolidColorBrush
x:Key="MarkedItemForegroundBrush"
Color="{DynamicResource MarkedItemForegroundColor}" />
<SolidColorBrush
x:Key="MarkedAlternativeItemForegroundBrush"
Color="{DynamicResource MarkedAlternativeItemForegroundColor}" />
<SolidColorBrush
x:Key="MarkedSelectedItemForegroundBrush"
Color="{DynamicResource MarkedSelectedItemForegroundColor}" />
<SolidColorBrush
x:Key="ErrorBrush"
Color="{DynamicResource ErrorColor}" />
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{DynamicResource SelectedItemBackgroundColor}" />
<converters:FormatSizeConverter x:Key="FormatSizeConverter"/>
<converters:CompareConverter x:Key="EqualityConverter"/>
<converters:CompareConverter x:Key="NotEqualsConverter" ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}"/>
<converters:SplitStringConverter x:Key="SplitStringConverter" />
<converters:ItemViewModeToBrushConverter
x:Key="ItemViewModeToForegroundConverter"
DefaultBrush="{StaticResource ForegroundBrush}"
AlternativeBrush="{StaticResource AlternativeItemForegroundBrush}"
SelectedBrush="{StaticResource SelectedItemForegroundBrush}"
MarkedBrush="{StaticResource MarkedItemForegroundBrush}"
MarkedAlternativeBrush="{StaticResource MarkedAlternativeItemForegroundBrush}"
MarkedSelectedBrush="{StaticResource MarkedSelectedItemForegroundBrush}"/>
<converters:ItemViewModeToBrushConverter
x:Key="ItemViewModeToBackgroundConverter"
DefaultBrush="{StaticResource ItemBackgroundBrush}"
AlternativeBrush="{StaticResource AlternativeItemBackgroundBrush}"
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"
MarkedBrush="{StaticResource MarkedItemBackgroundBrush}"
MarkedAlternativeBrush="{StaticResource MarkedAlternativeItemBackgroundBrush}"
MarkedSelectedBrush="{StaticResource MarkedSelectedItemBackgroundBrush}"/>
<converters:ContextMenuGenerator x:Key="ContextMenuGenerator"/>
<converters:ItemToImageConverter x:Key="ItemToImageConverter"/>
<converters:IsNullConverter x:Key="IsNullConverter"/>
<converters:IsNullConverter x:Key="IsNotNullConverter" Inverse="true"/>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<FluentTheme Mode="Light"/>
<Style Selector="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
<Setter Property="FontSize" Value="16"/>
</Style>
<Style Selector="TextBlock.SmallText">
<Setter Property="FontSize" Value="16"/>
</Style>
<Style Selector="ListBox.ContentListView">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="ListBox.ContentListView > ListBoxItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ContextMenu">
<ContextMenu Items="{Binding Converter={StaticResource ContextMenuGenerator}}"/>
</Setter>
</Style>
</Application.Styles>
</Application>

View File

@@ -0,0 +1,44 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using FileTime.App.Core;
using FileTime.Avalonia.ViewModels;
using FileTime.Avalonia.Views;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace FileTime.Avalonia
{
public class App : global::Avalonia.Application
{
public static IServiceProvider ServiceProvider { get; private set; }
static App()
{
ServiceProvider ??= DependencyInjection
.RegisterDefaultServices()
.AddViewModels()
.AddServices()
.RegisterCommandHandlers()
.BuildServiceProvider();
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
ViewModel = ServiceProvider.GetService<MainPageViewModel>(),
};
}
base.OnFrameworkInitializationCompleted();
}
}
}

View File

@@ -0,0 +1,91 @@
using MvvmGen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using FileTime.App.Core.Tab;
using System.Threading.Tasks;
using FileTime.Core.Models;
namespace FileTime.Avalonia.Application
{
[ViewModel]
public partial class AppState
{
[Property]
private ObservableCollection<TabContainer> _tabs = new();
[Property]
private TabContainer _selectedTab;
[Property]
private ViewMode _viewMode;
[Property]
private string _rapidTravelText = "";
partial void OnInitialize()
{
_tabs.CollectionChanged += TabsChanged;
}
private void TabsChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
SelectedTab ??= Tabs.Count > 0 ? Tabs[0] : null;
List<TabContainer> itemsAdded = new();
List<TabContainer> itemsRemoved = new();
if (e.NewItems != null && e.OldItems != null)
{
itemsAdded.AddRange(e.NewItems.Cast<TabContainer>().Except(e.OldItems.Cast<TabContainer>()));
itemsRemoved.AddRange(e.OldItems.Cast<TabContainer>().Except(e.NewItems.Cast<TabContainer>()));
}
else if (e.NewItems != null)
{
itemsAdded.AddRange(e.NewItems.Cast<TabContainer>());
}
else if (e.OldItems != null)
{
itemsRemoved.AddRange(e.OldItems.Cast<TabContainer>());
}
foreach (var item in itemsAdded)
{
item.TabState.ItemMarked.Add(TabItemMarked);
item.TabState.ItemUnmarked.Add(TabItemUnmarked);
}
foreach (var item in itemsRemoved)
{
item.TabState.ItemMarked.Remove(TabItemMarked);
item.TabState.ItemUnmarked.Remove(TabItemUnmarked);
}
}
private async Task TabItemMarked(TabState tabState, AbsolutePath item)
{
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null)
{
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path);
if (item2 != null)
{
item2.IsMarked = true;
}
}
}
private async Task TabItemUnmarked(TabState tabState, AbsolutePath item)
{
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null)
{
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path);
if (item2 != null)
{
item2.IsMarked = false;
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using FileTime.Avalonia.ViewModels;
namespace FileTime.Avalonia.Application
{
public interface INewItemProcessor
{
Task UpdateMarkedItems(ContainerViewModel containerViewModel);
}
}

View File

@@ -0,0 +1,316 @@
using AsyncEvent;
using FileTime.Core.Components;
using FileTime.Core.Models;
using FileTime.Providers.Local;
using FileTime.Avalonia.Services;
using FileTime.Avalonia.ViewModels;
using MvvmGen;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FileTime.App.Core.Tab;
using System.Collections.Generic;
namespace FileTime.Avalonia.Application
{
[ViewModel]
[Inject(typeof(ItemNameConverterService))]
[Inject(typeof(LocalContentProvider))]
[Inject(typeof(Tab))]
public partial class TabContainer : INewItemProcessor
{
[Property]
private TabState _tabState;
[Property]
private ContainerViewModel _parent;
[Property]
private ContainerViewModel _currentLocation;
[Property]
private ContainerViewModel _childContainer;
[Property]
private int _tabNumber;
[Property]
private bool _isSelected;
private IItemViewModel? _selectedItem;
public IItemViewModel? SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem != value)// && value != null
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
SelectedItemChanged();
}
}
}
partial void OnInitialize()
{
_tabState = new TabState(Tab);
}
public async Task Init(int tabNumber)
{
TabNumber = tabNumber;
Tab.CurrentLocationChanged.Add(Tab_CurrentLocationChanged);
Tab.CurrentSelectedItemChanged.Add(Tab_CurrentSelectedItemChanged);
var currentLocation = await Tab.GetCurrentLocation();
var parent = GenerateParent(currentLocation);
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
await CurrentLocation.Init();
if (parent != null)
{
parent.ChildrenToAdopt.Add(CurrentLocation);
Parent = parent;
await Parent.Init();
}
else
{
Parent = null;
}
await UpdateCurrentSelectedItem();
}
private ContainerViewModel? GenerateParent(IContainer? container, bool recursive = false)
{
var parentContainer = container?.GetParent();
if (parentContainer == null) return null;
var parentParent = recursive ? GenerateParent(parentContainer.GetParent(), recursive) : null;
var parent = new ContainerViewModel(this, parentParent, parentContainer, ItemNameConverterService);
parentParent?.ChildrenToAdopt.Add(parent);
return parent;
}
private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e)
{
var currentLocation = await Tab.GetCurrentLocation();
var parent = GenerateParent(currentLocation);
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
await CurrentLocation.Init();
if (parent != null)
{
parent.ChildrenToAdopt.Add(CurrentLocation);
Parent = parent;
await Parent.Init();
}
else
{
Parent = null;
}
}
private async Task Tab_CurrentSelectedItemChanged(object? sender, AsyncEventArgs e)
{
await UpdateCurrentSelectedItem();
}
public async Task UpdateCurrentSelectedItem()
{
try
{
var tabCurrentSelectenItem = await Tab.GetCurrentSelectedItem();
IItemViewModel? currentSelectenItem = null;
if (tabCurrentSelectenItem == null)
{
SelectedItem = null;
ChildContainer = null;
}
else
{
currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
if (currentSelectenItem is ContainerViewModel currentSelectedContainer)
{
SelectedItem = currentSelectedContainer;
ChildContainer = currentSelectedContainer;
}
else if (currentSelectenItem is ElementViewModel element)
{
SelectedItem = element;
ChildContainer = null;
}
else
{
SelectedItem = null;
ChildContainer = null;
}
}
var items = await _currentLocation.GetItems();
if (items != null && items.Count > 0)
{
foreach (var item in items)
{
var isSelected = item == currentSelectenItem;
item.IsSelected = isSelected;
if (isSelected)
{
var parent = item.Parent;
while (parent != null)
{
parent.IsSelected = true;
parent = parent.Parent;
}
try
{
var child = item;
while (child is ContainerViewModel containerViewModel && containerViewModel.Container.IsLoaded)
{
var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container);
child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
if (child != null)
{
child.IsSelected = true;
}
}
}
catch
{
//INFO collection modified exception on: child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
//TODO: handle or error message
}
}
}
}
else
{
var parent = _currentLocation;
while (parent != null)
{
parent.IsSelected = true;
parent = parent.Parent;
}
}
}
catch
{
//INFO collection modified exception on: currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
//TODO: handle or error message
}
}
private async void SelectedItemChanged()
{
try
{
await Tab.SetCurrentSelectedItem(SelectedItem?.Item);
}
catch { }
}
public async Task Open()
{
if (ChildContainer != null)
{
await Tab.Open();
await UpdateCurrentSelectedItem();
}
}
public async Task GoUp()
{
await Tab.GoUp();
await UpdateCurrentSelectedItem();
}
public async Task MoveCursorDown()
{
await Tab.SelectNextItem();
}
public async Task MoveCursorDownPage()
{
await Tab.SelectNextItem(10);
}
public async Task MoveCursorUp()
{
await Tab.SelectPreviousItem();
}
public async Task MoveCursorUpPage()
{
await Tab.SelectPreviousItem(10);
}
public async Task MoveCursorToFirst()
{
await Tab.SelectFirstItem();
}
public async Task MoveCursorToLast()
{
await Tab.SelectLastItem();
}
public async Task GotToProvider()
{
await Tab.GoToProvider();
}
public async Task GotToRoot()
{
await Tab.GoToRoot();
}
public async Task GotToHome()
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile).Replace(Path.DirectorySeparatorChar, Constants.SeparatorChar);
var resolvedPath = await LocalContentProvider.GetByPath(path);
if (resolvedPath is IContainer homeFolder)
{
await Tab.OpenContainer(homeFolder);
}
}
public async Task CreateContainer(string name)
{
(await Tab.GetCurrentLocation())?.CreateContainer(name);
}
public async Task CreateElement(string name)
{
(await Tab.GetCurrentLocation())?.CreateElement(name);
}
public async Task OpenContainer(IContainer container)
{
await Tab.OpenContainer(container);
}
public async Task MarkCurrentItem()
{
await _tabState.MakrCurrentItem();
}
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel)
{
if (containerViewModel == CurrentLocation && containerViewModel.Container.IsLoaded)
{
var selectedItems = TabState.GetCurrentMarkedItems(containerViewModel.Container);
foreach (var item in await containerViewModel.GetItems())
{
item.IsMarked = selectedItems.Any(c => c.Path == item.Item.FullName);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FileTime.Avalonia.Application
{
public enum ViewMode
{
Default,
RapidTravel
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M103.3 344.3c-6.5-14.2-6.9-18.3 7.4-23.1 25.6-8 8 9.2 43.2 49.2h.3v-93.9c1.2-50.2 44-92.2 97.7-92.2 53.9 0 97.7 43.5 97.7 96.8 0 63.4-60.8 113.2-128.5 93.3-10.5-4.2-2.1-31.7 8.5-28.6 53 0 89.4-10.1 89.4-64.4 0-61-77.1-89.6-116.9-44.6-23.5 26.4-17.6 42.1-17.6 157.6 50.7 31 118.3 22 160.4-20.1 24.8-24.8 38.5-58 38.5-93 0-35.2-13.8-68.2-38.8-93.3-24.8-24.8-57.8-38.5-93.3-38.5s-68.8 13.8-93.5 38.5c-.3.3-16 16.5-21.2 23.9l-.5.6c-3.3 4.7-6.3 9.1-20.1 6.1-6.9-1.7-14.3-5.8-14.3-11.8V20c0-5 3.9-10.5 10.5-10.5h241.3c8.3 0 8.3 11.6 8.3 15.1 0 3.9 0 15.1-8.3 15.1H130.3v132.9h.3c104.2-109.8 282.8-36 282.8 108.9 0 178.1-244.8 220.3-310.1 62.8zm63.3-260.8c-.5 4.2 4.6 24.5 14.6 20.6C306 56.6 384 144.5 390.6 144.5c4.8 0 22.8-15.3 14.3-22.8-93.2-89-234.5-57-238.3-38.2zM393 414.7C283 524.6 94 475.5 61 310.5c0-12.2-30.4-7.4-28.9 3.3 24 173.4 246 256.9 381.6 121.3 6.9-7.8-12.6-28.4-20.7-20.4zM213.6 306.6c0 4 4.3 7.3 5.5 8.5 3 3 6.1 4.4 8.5 4.4 3.8 0 2.6.2 22.3-19.5 19.6 19.3 19.1 19.5 22.3 19.5 5.4 0 18.5-10.4 10.7-18.2L265.6 284l18.2-18.2c6.3-6.8-10.1-21.8-16.2-15.7L249.7 268c-18.6-18.8-18.4-19.5-21.5-19.5-5 0-18 11.7-12.4 17.3L234 284c-18.1 17.9-20.4 19.2-20.4 22.6z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M423.9 255.8L411 413.1c-3.3 40.7-63.9 35.1-60.6-4.9l10-122.5-41.1 2.3c10.1 20.7 15.8 43.9 15.8 68.5 0 41.2-16.1 78.7-42.3 106.5l-39.3-39.3c57.9-63.7 13.1-167.2-74-167.2-25.9 0-49.5 9.9-67.2 26L73 243.2c22-20.7 50.1-35.1 81.4-40.2l75.3-85.7-42.6-24.8-51.6 46c-30 26.8-70.6-18.5-40.5-45.4l68-60.7c9.8-8.8 24.1-10.2 35.5-3.6 0 0 139.3 80.9 139.5 81.1 16.2 10.1 20.7 36 6.1 52.6L285.7 229l106.1-5.9c18.5-1.1 33.6 14.4 32.1 32.7zm-64.9-154c28.1 0 50.9-22.8 50.9-50.9C409.9 22.8 387.1 0 359 0c-28.1 0-50.9 22.8-50.9 50.9 0 28.1 22.8 50.9 50.9 50.9zM179.6 456.5c-80.6 0-127.4-90.6-82.7-156.1l-39.7-39.7C36.4 287 24 320.3 24 356.4c0 130.7 150.7 201.4 251.4 122.5l-39.7-39.7c-16 10.9-35.3 17.3-56.1 17.3z"/></svg>

After

Width:  |  Height:  |  Size: 955 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M322.1 252v-1l-51.2-65.8s-12 1.6-25 15.1c-9 9.3-242.1 239.1-243.4 240.9-7 10 1.6 6.8 15.7 1.7.8 0 114.5-36.6 114.5-36.6.5-.6-.1-.1.6-.6-.4-5.1-.8-26.2-1-27.7-.6-5.2 2.2-6.9 7-8.9l92.6-33.8c.6-.8 88.5-81.7 90.2-83.3zm160.1 120.1c13.3 16.1 20.7 13.3 30.8 9.3 3.2-1.2 115.4-47.6 117.8-48.9 8-4.3-1.7-16.7-7.2-23.4-2.1-2.5-205.1-245.6-207.2-248.3-9.7-12.2-14.3-12.9-38.4-12.8-10.2 0-106.8.5-116.5.6-19.2.1-32.9-.3-19.2 16.9C250 75 476.5 365.2 482.2 372.1zm152.7 1.6c-2.3-.3-24.6-4.7-38-7.2 0 0-115 50.4-117.5 51.6-16 7.3-26.9-3.2-36.7-14.6l-57.1-74c-5.4-.9-60.4-9.6-65.3-9.3-3.1.2-9.6.8-14.4 2.9-4.9 2.1-145.2 52.8-150.2 54.7-5.1 2-11.4 3.6-11.1 7.6.2 2.5 2 2.6 4.6 3.5 2.7.8 300.9 67.6 308 69.1 15.6 3.3 38.5 10.5 53.6 1.7 2.1-1.2 123.8-76.4 125.8-77.8 5.4-4 4.3-6.8-1.7-8.2z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M357.45 468.2c-1.2-7.7-1.3-7.6-9.6-7.6-99.8.2-111.8-2.4-112.7-2.6-12.3-1.7-20.6-10.5-21-23.1-.1-1.6-.2-71.6-1-129.1-.1-4.7 1.6-6.4 5.9-7.5 12.5-3 24.9-6.1 37.3-9.7 4.3-1.3 6.8-.2 8.4 3.5 4.5 10.3 8.8 20.6 13.2 30.9 1.6 3.7.1 4.4-3.4 4.4-10-.2-20-.1-30.4-.1v27h116c-1.4-9.5-2.7-18.1-4-27.5-7 0-13.8.4-20.4-.1-22.6-1.6-18.3-4.4-84-158.6-8.8-20.1-27.9-62.1-36.5-89.2-4.4-14 5.5-25.4 18.9-26.6 18.6-1.7 37.5-1.6 56.2-2 20.6-.4 41.2-.4 61.8-.5 3.1 0 4-1.4 4.3-4.3 1.2-9.8 2.7-19.5 4-29.2.8-5.3 1.6-10.7 2.4-16.1L23.75 0c-3.6 0-5.3 1.1-4.6 5.3 2.2 13.2-.8.8 6.4 45.3 63.4 0 71.8.9 101.8.5 12.3-.2 37 3.5 37.7 22.1.4 11.4-1.1 11.3-32.6 87.4-53.8 129.8-50.7 120.3-67.3 161-1.7 4.1-3.6 5.2-7.6 5.2-8.5-.2-17-.3-25.4.1-1.9.1-5.2 1.8-5.5 3.2-1.5 8.1-2.2 16.3-3.2 24.9h114.3v-27.6c-6.9 0-33.5.4-35.3-2.9 5.3-12.3 10.4-24.4 15.7-36.7 16.3 4 31.9 7.8 47.6 11.7 3.4.9 4.6 3 4.6 6.8-.1 42.9.1 85.9.2 128.8 0 10.2-5.5 19.1-14.9 23.1-6.5 2.7-3.3 3.4-121.4 2.4-5.3 0-7.1 2-7.6 6.8-1.5 12.9-2.9 25.9-5 38.8-.8 5 1.3 5.7 5.3 5.7 183.2.6-30.7 0 337.1 0-2.5-15-4.4-29.4-6.6-43.7zm-174.9-205.7c-13.3-4.2-26.6-8.2-39.9-12.5a44.53 44.53 0 0 1-5.8-2.9c17.2-44.3 34.2-88.1 51.3-132.1 7.5 2.4 7.9-.8 9.4 0 9.3 22.5 18.1 60.1 27 82.8 6.6 16.7 13 33.5 19.7 50.9a35.78 35.78 0 0 1-3.9 2.1c-13.1 3.9-26.4 7.5-39.4 11.7a27.66 27.66 0 0 1-18.4 0z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M248 167.5l64.9 98.8H183.1l64.9-98.8zM496 256c0 136.9-111.1 248-248 248S0 392.9 0 256 111.1 8 248 8s248 111.1 248 248zm-99.8 82.7L248 115.5 99.8 338.7h30.4l33.6-51.7h168.6l33.6 51.7h30.2z"/></svg>

After

Width:  |  Height:  |  Size: 447 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M482.1 32H28.7C5.8 32 0 37.9 0 60.9v390.2C0 474.4 5.8 480 28.7 480h453.4c24.4 0 29.9-5.2 29.9-29.7V62.2c0-24.6-5.4-30.2-29.9-30.2zM178.4 220.3c-27.5-20.2-72.1-8.7-84.2 23.4-4.3 11.1-9.3 9.5-17.5 8.3-9.7-1.5-17.2-3.2-22.5-5.5-28.8-11.4 8.6-55.3 24.9-64.3 41.1-21.4 83.4-22.2 125.3-4.8 40.9 16.8 34.5 59.2 34.5 128.5 2.7 25.8-4.3 58.3 9.3 88.8 1.9 4.4.4 7.9-2.7 10.7-8.4 6.7-39.3 2.2-46.6-7.4-1.9-2.2-1.8-3.6-3.9-6.2-3.6-3.9-7.3-2.2-11.9 1-57.4 36.4-140.3 21.4-147-43.3-3.1-29.3 12.4-57.1 39.6-71 38.2-19.5 112.2-11.8 114-30.9 1.1-10.2-1.9-20.1-11.3-27.3zm286.7 222c0 15.1-11.1 9.9-17.8 9.9H52.4c-7.4 0-18.2 4.8-17.8-10.7.4-13.9 10.5-9.1 17.1-9.1 132.3-.4 264.5-.4 396.8 0 6.8 0 16.6-4.4 16.6 9.9zm3.8-340.5v291c0 5.7-.7 13.9-8.1 13.9-12.4-.4-27.5 7.1-36.1-5.6-5.8-8.7-7.8-4-12.4-1.2-53.4 29.7-128.1 7.1-144.4-85.2-6.1-33.4-.7-67.1 15.7-100 11.8-23.9 56.9-76.1 136.1-30.5v-71c0-26.2-.1-26.2 26-26.2 3.1 0 6.6.4 9.7 0 10.1-.8 13.6 4.4 13.6 14.3-.1.2-.1.3-.1.5zm-51.5 232.3c-19.5 47.6-72.9 43.3-90 5.2-15.1-33.3-15.5-68.2.4-101.5 16.3-34.1 59.7-35.7 81.5-4.8 20.6 28.8 14.9 84.6 8.1 101.1zm-294.8 35.3c-7.5-1.3-33-3.3-33.7-27.8-.4-13.9 7.8-23 19.8-25.8 24.4-5.9 49.3-9.9 73.7-14.7 8.9-2 7.4 4.4 7.8 9.5 1.4 33-26.1 59.2-67.6 58.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M159.7 237.4C108.4 308.3 43.1 348.2 14 326.6-15.2 304.9 2.8 230 54.2 159.1c51.3-70.9 116.6-110.8 145.7-89.2 29.1 21.6 11.1 96.6-40.2 167.5zm351.2-57.3C437.1 303.5 319 367.8 246.4 323.7c-25-15.2-41.3-41.2-49-73.8-33.6 64.8-92.8 113.8-164.1 133.2 49.8 59.3 124.1 96.9 207 96.9 150 0 271.6-123.1 271.6-274.9.1-8.5-.3-16.8-1-25z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M224 373.12c-25.24-31.67-40.08-59.43-45-83.18-22.55-88 112.61-88 90.06 0-5.45 24.25-20.29 52-45 83.18zm138.15 73.23c-42.06 18.31-83.67-10.88-119.3-50.47 103.9-130.07 46.11-200-18.85-200-54.92 0-85.16 46.51-73.28 100.5 6.93 29.19 25.23 62.39 54.43 99.5-32.53 36.05-60.55 52.69-85.15 54.92-50 7.43-89.11-41.06-71.3-91.09 15.1-39.16 111.72-231.18 115.87-241.56 15.75-30.07 25.56-57.4 59.38-57.4 32.34 0 43.4 25.94 60.37 59.87 36 70.62 89.35 177.48 114.84 239.09 13.17 33.07-1.37 71.29-37.01 86.64zm47-136.12C280.27 35.93 273.13 32 224 32c-45.52 0-64.87 31.67-84.66 72.79C33.18 317.1 22.89 347.19 22 349.81-3.22 419.14 48.74 480 111.63 480c21.71 0 60.61-6.06 112.37-62.4 58.68 63.78 101.26 62.4 112.37 62.4 62.89.05 114.85-60.86 89.61-130.19.02-3.89-16.82-38.9-16.82-39.58z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M229.3 182.6c-49.3 0-89.2 39.9-89.2 89.2 0 49.3 39.9 89.2 89.2 89.2s89.2-39.9 89.2-89.2c0-49.3-40-89.2-89.2-89.2zm62.7 56.6l-58.9 30.6c-1.8.9-3.8-.4-3.8-2.3V201c0-1.5 1.3-2.7 2.7-2.6 26.2 1 48.9 15.7 61.1 37.1.7 1.3.2 3-1.1 3.7zM389.1 32H58.9C26.4 32 0 58.4 0 90.9V421c0 32.6 26.4 59 58.9 59H389c32.6 0 58.9-26.4 58.9-58.9V90.9C448 58.4 421.6 32 389.1 32zm-202.6 84.7c0-10.8 8.7-19.5 19.5-19.5h45.3c10.8 0 19.5 8.7 19.5 19.5v15.4c0 1.8-1.7 3-3.3 2.5-12.3-3.4-25.1-5.1-38.1-5.1-13.5 0-26.7 1.8-39.4 5.5-1.7.5-3.4-.8-3.4-2.5v-15.8zm-84.4 37l9.2-9.2c7.6-7.6 19.9-7.6 27.5 0l7.7 7.7c1.1 1.1 1 3-.3 4-6.2 4.5-12.1 9.4-17.6 14.9-5.4 5.4-10.4 11.3-14.8 17.4-1 1.3-2.9 1.5-4 .3l-7.7-7.7c-7.6-7.5-7.6-19.8 0-27.4zm127.2 244.8c-70 0-126.6-56.7-126.6-126.6s56.7-126.6 126.6-126.6c70 0 126.6 56.6 126.6 126.6 0 69.8-56.7 126.6-126.6 126.6z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M377.74 32H70.26C31.41 32 0 63.41 0 102.26v307.48C0 448.59 31.41 480 70.26 480h307.48c38.52 0 69.76-31.08 70.26-69.6-45.96-25.62-110.59-60.34-171.6-88.44-32.07 43.97-84.14 81-148.62 81-70.59 0-93.73-45.3-97.04-76.37-3.97-39.01 14.88-81.5 99.52-81.5 35.38 0 79.35 10.25 127.13 24.96 16.53-30.09 26.45-60.34 26.45-60.34h-178.2v-16.7h92.08v-31.24H88.28v-19.01h109.44V92.34h50.92v50.42h109.44v19.01H248.63v31.24h88.77s-15.21 46.62-38.35 90.92c48.93 16.7 100.01 36.04 148.62 52.74V102.26C447.83 63.57 416.43 32 377.74 32zM47.28 322.95c.99 20.17 10.25 53.73 69.93 53.73 52.07 0 92.58-39.68 117.87-72.9-44.63-18.68-84.48-31.41-109.44-31.41-67.45 0-79.35 33.06-78.36 50.58z"/></svg>

After

Width:  |  Height:  |  Size: 925 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M14 325.3c2.3-4.2 5.2-4.9 9.7-2.5 10.4 5.6 20.6 11.4 31.2 16.7a595.88 595.88 0 0 0 127.4 46.3 616.61 616.61 0 0 0 63.2 11.8 603.33 603.33 0 0 0 95 5.2c17.4-.4 34.8-1.8 52.1-3.8a603.66 603.66 0 0 0 163.3-42.8c2.9-1.2 5.9-2 9.1-1.2 6.7 1.8 9 9 4.1 13.9a70 70 0 0 1-9.6 7.4c-30.7 21.1-64.2 36.4-99.6 47.9a473.31 473.31 0 0 1-75.1 17.6 431 431 0 0 1-53.2 4.8 21.3 21.3 0 0 0-2.5.3H308a21.3 21.3 0 0 0-2.5-.3c-3.6-.2-7.2-.3-10.7-.4a426.3 426.3 0 0 1-50.4-5.3A448.4 448.4 0 0 1 164 420a443.33 443.33 0 0 1-145.6-87c-1.8-1.6-3-3.8-4.4-5.7zM172 65.1l-4.3.6a80.92 80.92 0 0 0-38 15.1c-2.4 1.7-4.6 3.5-7.1 5.4a4.29 4.29 0 0 1-.4-1.4c-.4-2.7-.8-5.5-1.3-8.2-.7-4.6-3-6.6-7.6-6.6h-11.5c-6.9 0-8.2 1.3-8.2 8.2v209.3c0 1 0 2 .1 3 .2 3 2 4.9 4.9 5 7 .1 14.1.1 21.1 0 2.9 0 4.7-2 5-5 .1-1 .1-2 .1-3v-72.4c1.1.9 1.7 1.4 2.2 1.9 17.9 14.9 38.5 19.8 61 15.4 20.4-4 34.6-16.5 43.8-34.9 7-13.9 9.9-28.7 10.3-44.1.5-17.1-1.2-33.9-8.1-49.8-8.5-19.6-22.6-32.5-43.9-36.9-3.2-.7-6.5-1-9.8-1.5-2.8-.1-5.5-.1-8.3-.1zM124.6 107a3.48 3.48 0 0 1 1.7-3.3c13.7-9.5 28.8-14.5 45.6-13.2 14.9 1.1 27.1 8.4 33.5 25.9 3.9 10.7 4.9 21.8 4.9 33 0 10.4-.8 20.6-4 30.6-6.8 21.3-22.4 29.4-42.6 28.5-14-.6-26.2-6-37.4-13.9a3.57 3.57 0 0 1-1.7-3.3c.1-14.1 0-28.1 0-42.2s.1-28 0-42.1zm205.7-41.9c-1 .1-2 .3-2.9.4a148 148 0 0 0-28.9 4.1c-6.1 1.6-12 3.8-17.9 5.8-3.6 1.2-5.4 3.8-5.3 7.7.1 3.3-.1 6.6 0 9.9.1 4.8 2.1 6.1 6.8 4.9 7.8-2 15.6-4.2 23.5-5.7 12.3-2.3 24.7-3.3 37.2-1.4 6.5 1 12.6 2.9 16.8 8.4 3.7 4.8 5.1 10.5 5.3 16.4.3 8.3.2 16.6.3 24.9a7.84 7.84 0 0 1-.2 1.4c-.5-.1-.9 0-1.3-.1a180.56 180.56 0 0 0-32-4.9c-11.3-.6-22.5.1-33.3 3.9-12.9 4.5-23.3 12.3-29.4 24.9-4.7 9.8-5.4 20.2-3.9 30.7 2 14 9 24.8 21.4 31.7 11.9 6.6 24.8 7.4 37.9 5.4 15.1-2.3 28.5-8.7 40.3-18.4a7.36 7.36 0 0 1 1.6-1.1c.6 3.8 1.1 7.4 1.8 11 .6 3.1 2.5 5.1 5.4 5.2 5.4.1 10.9.1 16.3 0a4.84 4.84 0 0 0 4.8-4.7 26.2 26.2 0 0 0 .1-2.8v-106a80 80 0 0 0-.9-12.9c-1.9-12.9-7.4-23.5-19-30.4-6.7-4-14.1-6-21.8-7.1-3.6-.5-7.2-.8-10.8-1.3-3.9.1-7.9.1-11.9.1zm35 127.7a3.33 3.33 0 0 1-1.5 3c-11.2 8.1-23.5 13.5-37.4 14.9-5.7.6-11.4.4-16.8-1.8a20.08 20.08 0 0 1-12.4-13.3 32.9 32.9 0 0 1-.1-19.4c2.5-8.3 8.4-13 16.4-15.6a61.33 61.33 0 0 1 24.8-2.2c8.4.7 16.6 2.3 25 3.4 1.6.2 2.1 1 2.1 2.6-.1 4.8 0 9.5 0 14.3s-.2 9.4-.1 14.1zm259.9 129.4c-1-5-4.8-6.9-9.1-8.3a88.42 88.42 0 0 0-21-3.9 147.32 147.32 0 0 0-39.2 1.9c-14.3 2.7-27.9 7.3-40 15.6a13.75 13.75 0 0 0-3.7 3.5 5.11 5.11 0 0 0-.5 4c.4 1.5 2.1 1.9 3.6 1.8a16.2 16.2 0 0 0 2.2-.1c7.8-.8 15.5-1.7 23.3-2.5 11.4-1.1 22.9-1.8 34.3-.9a71.64 71.64 0 0 1 14.4 2.7c5.1 1.4 7.4 5.2 7.6 10.4.4 8-1.4 15.7-3.5 23.3-4.1 15.4-10 30.3-15.8 45.1a17.6 17.6 0 0 0-1 3c-.5 2.9 1.2 4.8 4.1 4.1a10.56 10.56 0 0 0 4.8-2.5 145.91 145.91 0 0 0 12.7-13.4c12.8-16.4 20.3-35.3 24.7-55.6.8-3.6 1.4-7.3 2.1-10.9v-17.3zM493.1 199q-19.35-53.55-38.7-107.2c-2-5.7-4.2-11.3-6.3-16.9-1.1-2.9-3.2-4.8-6.4-4.8-7.6-.1-15.2-.2-22.9-.1-2.5 0-3.7 2-3.2 4.5a43.1 43.1 0 0 0 1.9 6.1q29.4 72.75 59.1 145.5c1.7 4.1 2.1 7.6.2 11.8-3.3 7.3-5.9 15-9.3 22.3-3 6.5-8 11.4-15.2 13.3a42.13 42.13 0 0 1-15.4 1.1c-2.5-.2-5-.8-7.5-1-3.4-.2-5.1 1.3-5.2 4.8q-.15 5 0 9.9c.1 5.5 2 8 7.4 8.9a108.18 108.18 0 0 0 16.9 2c17.1.4 30.7-6.5 39.5-21.4a131.63 131.63 0 0 0 9.2-18.4q35.55-89.7 70.6-179.6a26.62 26.62 0 0 0 1.6-5.5c.4-2.8-.9-4.4-3.7-4.4-6.6-.1-13.3 0-19.9 0a7.54 7.54 0 0 0-7.7 5.2c-.5 1.4-1.1 2.7-1.6 4.1l-34.8 100c-2.5 7.2-5.1 14.5-7.7 22.2-.4-1.1-.6-1.7-.9-2.4z"/></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M257.2 162.7c-48.7 1.8-169.5 15.5-169.5 117.5 0 109.5 138.3 114 183.5 43.2 6.5 10.2 35.4 37.5 45.3 46.8l56.8-56S341 288.9 341 261.4V114.3C341 89 316.5 32 228.7 32 140.7 32 94 87 94 136.3l73.5 6.8c16.3-49.5 54.2-49.5 54.2-49.5 40.7-.1 35.5 29.8 35.5 69.1zm0 86.8c0 80-84.2 68-84.2 17.2 0-47.2 50.5-56.7 84.2-57.8v40.6zm136 163.5c-7.7 10-70 67-174.5 67S34.2 408.5 9.7 379c-6.8-7.7 1-11.3 5.5-8.3C88.5 415.2 203 488.5 387.7 401c7.5-3.7 13.3 2 5.5 12zm39.8 2.2c-6.5 15.8-16 26.8-21.2 31-5.5 4.5-9.5 2.7-6.5-3.8s19.3-46.5 12.7-55c-6.5-8.3-37-4.3-48-3.2-10.8 1-13 2-14-.3-2.3-5.7 21.7-15.5 37.5-17.5 15.7-1.8 41-.8 46 5.7 3.7 5.1 0 27.1-6.5 43.1z"/></svg>

After

Width:  |  Height:  |  Size: 900 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M240.1 32c-61.9 0-131.5 16.9-184.2 55.4-5.1 3.1-9.1 9.2-7.2 19.4 1.1 5.1 5.1 27.4 10.2 39.6 4.1 10.2 14.2 10.2 20.3 6.1 32.5-22.3 96.5-47.7 152.3-47.7 57.9 0 58.9 28.4 58.9 73.1v38.5C203 227.7 78.2 251 46.7 264.2 11.2 280.5 16.3 357.7 16.3 376s15.2 104 124.9 104c47.8 0 113.7-20.7 153.3-42.1v25.4c0 3 2.1 8.2 6.1 9.1 3.1 1 50.7 2 59.9 2s62.5.3 66.5-.7c4.1-1 5.1-6.1 5.1-9.1V168c-.1-80.3-57.9-136-192-136zm50.2 348c-21.4 13.2-48.7 24.4-79.1 24.4-52.8 0-58.9-33.5-59-44.7 0-12.2-3-42.7 18.3-52.9 24.3-13.2 75.1-29.4 119.8-33.5z"/></svg>

After

Width:  |  Height:  |  Size: 785 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M420.55,301.93a24,24,0,1,1,24-24,24,24,0,0,1-24,24m-265.1,0a24,24,0,1,1,24-24,24,24,0,0,1-24,24m273.7-144.48,47.94-83a10,10,0,1,0-17.27-10h0l-48.54,84.07a301.25,301.25,0,0,0-246.56,0L116.18,64.45a10,10,0,1,0-17.27,10h0l47.94,83C64.53,202.22,8.24,285.55,0,384H576c-8.24-98.45-64.54-181.78-146.85-226.55"/></svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M347.1 215.4c11.7-32.6 45.4-126.9 45.4-157.1 0-26.6-15.7-48.9-43.7-48.9-44.6 0-84.6 131.7-97.1 163.1C242 144 196.6 0 156.6 0c-31.1 0-45.7 22.9-45.7 51.7 0 35.3 34.2 126.8 46.6 162-6.3-2.3-13.1-4.3-20-4.3-23.4 0-48.3 29.1-48.3 52.6 0 8.9 4.9 21.4 8 29.7-36.9 10-51.1 34.6-51.1 71.7C46 435.6 114.4 512 210.6 512c118 0 191.4-88.6 191.4-202.9 0-43.1-6.9-82-54.9-93.7zM311.7 108c4-12.3 21.1-64.3 37.1-64.3 8.6 0 10.9 8.9 10.9 16 0 19.1-38.6 124.6-47.1 148l-34-6 33.1-93.7zM142.3 48.3c0-11.9 14.5-45.7 46.3 47.1l34.6 100.3c-15.6-1.3-27.7-3-35.4 1.4-10.9-28.8-45.5-119.7-45.5-148.8zM140 244c29.3 0 67.1 94.6 67.1 107.4 0 5.1-4.9 11.4-10.6 11.4-20.9 0-76.9-76.9-76.9-97.7.1-7.7 12.7-21.1 20.4-21.1zm184.3 186.3c-29.1 32-66.3 48.6-109.7 48.6-59.4 0-106.3-32.6-128.9-88.3-17.1-43.4 3.8-68.3 20.6-68.3 11.4 0 54.3 60.3 54.3 73.1 0 4.9-7.7 8.3-11.7 8.3-16.1 0-22.4-15.5-51.1-51.4-29.7 29.7 20.5 86.9 58.3 86.9 26.1 0 43.1-24.2 38-42 3.7 0 8.3.3 11.7-.6 1.1 27.1 9.1 59.4 41.7 61.7 0-.9 2-7.1 2-7.4 0-17.4-10.6-32.6-10.6-50.3 0-28.3 21.7-55.7 43.7-71.7 8-6 17.7-9.7 27.1-13.1 9.7-3.7 20-8 27.4-15.4-1.1-11.2-5.7-21.1-16.9-21.1-27.7 0-120.6 4-120.6-39.7 0-6.7.1-13.1 17.4-13.1 32.3 0 114.3 8 138.3 29.1 18.1 16.1 24.3 113.2-31 174.7zm-98.6-126c9.7 3.1 19.7 4 29.7 6-7.4 5.4-14 12-20.3 19.1-2.8-8.5-6.2-16.8-9.4-25.1z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M640 238.2l-3.2 28.2-34.5 2.3-2 18.1 34.5-2.3-3.2 28.2-34.4 2.2-2.3 20.1 34.4-2.2-3 26.1-64.7 4.1 12.7-113.2L527 365.2l-31.9 2-23.8-117.8 30.3-2 13.6 79.4 31.7-82.4 93.1-6.2zM426.8 371.5l28.3-1.8L468 249.6l-28.4 1.9-12.8 120zM162 388.1l-19.4-36-3.5 37.4-28.2 1.7 2.7-29.1c-11 18-32 34.3-56.9 35.8C23.9 399.9-3 377 .3 339.7c2.6-29.3 26.7-62.8 67.5-65.4 37.7-2.4 47.6 23.2 51.3 28.8l2.8-30.8 38.9-2.5c20.1-1.3 38.7 3.7 42.5 23.7l2.6-26.6 64.8-4.2-2.7 27.9-36.4 2.4-1.7 17.9 36.4-2.3-2.7 27.9-36.4 2.3-1.9 19.9 36.3-2.3-2.1 20.8 55-117.2 23.8-1.6L370.4 369l8.9-85.6-22.3 1.4 2.9-27.9 75-4.9-3 28-24.3 1.6-9.7 91.9-58 3.7-4.3-15.6-39.4 2.5-8 16.3-126.2 7.7zm-44.3-70.2l-26.4 1.7C84.6 307.2 76.9 303 65 303.8c-19 1.2-33.3 17.5-34.6 33.3-1.4 16 7.3 32.5 28.7 31.2 12.8-.8 21.3-8.6 28.9-18.9l27-1.7 2.7-29.8zm56.1-7.7c1.2-12.9-7.6-13.6-26.1-12.4l-2.7 28.5c14.2-.9 27.5-2.1 28.8-16.1zm21.1 70.8l5.8-60c-5 13.5-14.7 21.1-27.9 26.6l22.1 33.4zm135.4-45l-7.9-37.8-15.8 39.3 23.7-1.5zm-170.1-74.6l-4.3-17.5-39.6 2.6-8.1 18.2-31.9 2.1 57-121.9 23.9-1.6 30.7 102 9.9-104.7 27-1.8 37.8 63.6 6.5-66.6 28.5-1.9-4 41.2c7.4-13.5 22.9-44.7 63.6-47.5 40.5-2.8 52.4 29.3 53.4 30.3l3.3-32 39.3-2.7c12.7-.9 27.8.3 36.3 9.7l-4.4-11.9 32.2-2.2 12.9 43.2 23-45.7 31-2.2-43.6 78.4-4.8 44.3-28.4 1.9 4.8-44.3-15.8-43c1 22.3-9.2 40.1-32 49.6l25.2 38.8-36.4 2.4-19.2-36.8-4 38.3-28.4 1.9 3.3-31.5c-6.7 9.3-19.7 35.4-59.6 38-26.2 1.7-45.6-10.3-55.4-39.2l-4 40.3-25 1.6-37.6-63.3-6.3 66.2-56.8 3.7zm276.6-82.1c10.2-.7 17.5-2.1 21.6-4.3 4.5-2.4 7-6.4 7.6-12.1.6-5.3-.6-8.8-3.4-10.4-3.6-2.1-10.6-2.8-22.9-2l-2.9 28.8zM327.7 214c5.6 5.9 12.7 8.5 21.3 7.9 4.7-.3 9.1-1.8 13.3-4.1 5.5-3 10.6-8 15.1-14.3l-34.2 2.3 2.4-23.9 63.1-4.3 1.2-12-31.2 2.1c-4.1-3.7-7.8-6.6-11.1-8.1-4-1.7-8.1-2.8-12.2-2.5-8 .5-15.3 3.6-22 9.2-7.7 6.4-12 14.5-12.9 24.4-1.1 9.6 1.4 17.3 7.2 23.3zm-201.3 8.2l23.8-1.6-8.3-37.6-15.5 39.2z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M185.7 268.1h76.2l-38.1-91.6-38.1 91.6zM223.8 32L16 106.4l31.8 275.7 176 97.9 176-97.9 31.8-275.7zM354 373.8h-48.6l-26.2-65.4H168.6l-26.2 65.4H93.7L223.8 81.5z"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM127 384.5c-5.5 9.6-17.8 12.8-27.3 7.3-9.6-5.5-12.8-17.8-7.3-27.3l14.3-24.7c16.1-4.9 29.3-1.1 39.6 11.4L127 384.5zm138.9-53.9H84c-11 0-20-9-20-20s9-20 20-20h51l65.4-113.2-20.5-35.4c-5.5-9.6-2.2-21.8 7.3-27.3 9.6-5.5 21.8-2.2 27.3 7.3l8.9 15.4 8.9-15.4c5.5-9.6 17.8-12.8 27.3-7.3 9.6 5.5 12.8 17.8 7.3 27.3l-85.8 148.6h62.1c20.2 0 31.5 23.7 22.7 40zm98.1 0h-29l19.6 33.9c5.5 9.6 2.2 21.8-7.3 27.3-9.6 5.5-21.8 2.2-27.3-7.3-32.9-56.9-57.5-99.7-74-128.1-16.7-29-4.8-58 7.1-67.8 13.1 22.7 32.7 56.7 58.9 102h52c11 0 20 9 20 20 0 11.1-9 20-20 20z"/></svg>

After

Width:  |  Height:  |  Size: 906 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M255.9 120.9l9.1-15.7c5.6-9.8 18.1-13.1 27.9-7.5 9.8 5.6 13.1 18.1 7.5 27.9l-87.5 151.5h63.3c20.5 0 32 24.1 23.1 40.8H113.8c-11.3 0-20.4-9.1-20.4-20.4 0-11.3 9.1-20.4 20.4-20.4h52l66.6-115.4-20.8-36.1c-5.6-9.8-2.3-22.2 7.5-27.9 9.8-5.6 22.2-2.3 27.9 7.5l8.9 15.7zm-78.7 218l-19.6 34c-5.6 9.8-18.1 13.1-27.9 7.5-9.8-5.6-13.1-18.1-7.5-27.9l14.6-25.2c16.4-5.1 29.8-1.2 40.4 11.6zm168.9-61.7h53.1c11.3 0 20.4 9.1 20.4 20.4 0 11.3-9.1 20.4-20.4 20.4h-29.5l19.9 34.5c5.6 9.8 2.3 22.2-7.5 27.9-9.8 5.6-22.2 2.3-27.9-7.5-33.5-58.1-58.7-101.6-75.4-130.6-17.1-29.5-4.9-59.1 7.2-69.1 13.4 23 33.4 57.7 60.1 104zM256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm216 248c0 118.7-96.1 216-216 216-118.7 0-216-96.1-216-216 0-118.7 96.1-216 216-216 118.7 0 216 96.1 216 216z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M42.1 239.1c22.2 0 29 2.8 33.5 14.6h.8v-22.9c0-11.3-4.8-15.4-17.9-15.4-11.3 0-14.4 2.5-15.1 12.8H4.8c.3-13.9 1.5-19.1 5.8-24.4C17.9 195 29.5 192 56.7 192c33 0 47.1 5 53.9 18.9 2 4.3 4 15.6 4 23.7v76.3H76.3l1.3-19.1h-1c-5.3 15.6-13.6 20.4-35.5 20.4-30.3 0-41.1-10.1-41.1-37.3 0-25.2 12.3-35.8 42.1-35.8zm17.1 48.1c13.1 0 16.9-3 16.9-13.4 0-9.1-4.3-11.6-19.6-11.6-13.1 0-17.9 3-17.9 12.1-.1 10.4 3.7 12.9 20.6 12.9zm77.8-94.9h38.3l-1.5 20.6h.8c9.1-17.1 15.9-20.9 37.5-20.9 14.4 0 24.7 3 31.5 9.1 9.8 8.6 12.8 20.4 12.8 48.1 0 30-3 43.1-12.1 52.9-6.8 7.3-16.4 10.1-33.2 10.1-20.4 0-29.2-5.5-33.8-21.2h-.8v70.3H137v-169zm80.9 60.7c0-27.5-3.3-32.5-20.7-32.5-16.9 0-20.7 5-20.7 28.7 0 28 3.5 33.5 21.2 33.5 16.4 0 20.2-5.6 20.2-29.7zm57.9-60.7h38.3l-1.5 20.6h.8c9.1-17.1 15.9-20.9 37.5-20.9 14.4 0 24.7 3 31.5 9.1 9.8 8.6 12.8 20.4 12.8 48.1 0 30-3 43.1-12.1 52.9-6.8 7.3-16.4 10.1-33.3 10.1-20.4 0-29.2-5.5-33.8-21.2h-.8v70.3h-39.5v-169zm80.9 60.7c0-27.5-3.3-32.5-20.7-32.5-16.9 0-20.7 5-20.7 28.7 0 28 3.5 33.5 21.2 33.5 16.4 0 20.2-5.6 20.2-29.7zm53.8-3.8c0-25.4 3.3-37.8 12.3-45.8 8.8-8.1 22.2-11.3 45.1-11.3 42.8 0 55.7 12.8 55.7 55.7v11.1h-75.3c-.3 2-.3 4-.3 4.8 0 16.9 4.5 21.9 20.1 21.9 13.9 0 17.9-3 17.9-13.9h37.5v2.3c0 9.8-2.5 18.9-6.8 24.7-7.3 9.8-19.6 13.6-44.3 13.6-27.5 0-41.6-3.3-50.6-12.3-8.5-8.5-11.3-21.3-11.3-50.8zm76.4-11.6c-.3-1.8-.3-3.3-.3-3.8 0-12.3-3.3-14.6-19.6-14.6-14.4 0-17.1 3-18.1 15.1l-.3 3.3h38.3zm55.6-45.3h38.3l-1.8 19.9h.7c6.8-14.9 14.4-20.2 29.7-20.2 10.8 0 19.1 3.3 23.4 9.3 5.3 7.3 6.8 14.4 6.8 34 0 1.5 0 5 .2 9.3h-35c.3-1.8.3-3.3.3-4 0-15.4-2-19.4-10.3-19.4-6.3 0-10.8 3.3-13.1 9.3-1 3-1 4.3-1 12.3v68h-38.3V192.3z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M116.9 158.5c-7.5 8.9-19.5 15.9-31.5 14.9-1.5-12 4.4-24.8 11.3-32.6 7.5-9.1 20.6-15.6 31.3-16.1 1.2 12.4-3.7 24.7-11.1 33.8m10.9 17.2c-17.4-1-32.3 9.9-40.5 9.9-8.4 0-21-9.4-34.8-9.1-17.9.3-34.5 10.4-43.6 26.5-18.8 32.3-4.9 80 13.3 106.3 8.9 13 19.5 27.3 33.5 26.8 13.3-.5 18.5-8.6 34.5-8.6 16.1 0 20.8 8.6 34.8 8.4 14.5-.3 23.6-13 32.5-26 10.1-14.8 14.3-29.1 14.5-29.9-.3-.3-28-10.9-28.3-42.9-.3-26.8 21.9-39.5 22.9-40.3-12.5-18.6-32-20.6-38.8-21.1m100.4-36.2v194.9h30.3v-66.6h41.9c38.3 0 65.1-26.3 65.1-64.3s-26.4-64-64.1-64h-73.2zm30.3 25.5h34.9c26.3 0 41.3 14 41.3 38.6s-15 38.8-41.4 38.8h-34.8V165zm162.2 170.9c19 0 36.6-9.6 44.6-24.9h.6v23.4h28v-97c0-28.1-22.5-46.3-57.1-46.3-32.1 0-55.9 18.4-56.8 43.6h27.3c2.3-12 13.4-19.9 28.6-19.9 18.5 0 28.9 8.6 28.9 24.5v10.8l-37.8 2.3c-35.1 2.1-54.1 16.5-54.1 41.5.1 25.2 19.7 42 47.8 42zm8.2-23.1c-16.1 0-26.4-7.8-26.4-19.6 0-12.3 9.9-19.4 28.8-20.5l33.6-2.1v11c0 18.2-15.5 31.2-36 31.2zm102.5 74.6c29.5 0 43.4-11.3 55.5-45.4L640 193h-30.8l-35.6 115.1h-.6L537.4 193h-31.6L557 334.9l-2.8 8.6c-4.6 14.6-12.1 20.3-25.5 20.3-2.4 0-7-.3-8.9-.5v23.4c1.8.4 9.3.7 11.6.7z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"/></svg>

After

Width:  |  Height:  |  Size: 695 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M2 377.4l43 74.3A51.35 51.35 0 0 0 90.9 480h285.4l-59.2-102.6zM501.8 350L335.6 59.3A51.38 51.38 0 0 0 290.2 32h-88.4l257.3 447.6 40.7-70.5c1.9-3.2 21-29.7 2-59.1zM275 304.5l-115.5-200L44 304.5z"/></svg>

After

Width:  |  Height:  |  Size: 453 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M517.5 309.2c38.8-40 58.1-80 58.5-116.1.8-65.5-59.4-118.2-169.4-135C277.9 38.4 118.1 73.6 0 140.5 52 114 110.6 92.3 170.7 82.3c74.5-20.5 153-25.4 221.3-14.8C544.5 91.3 588.8 195 490.8 299.2c-10.2 10.8-22 21.1-35 30.6L304.9 103.4 114.7 388.9c-65.6-29.4-76.5-90.2-19.1-151.2 20.8-22.2 48.3-41.9 79.5-58.1 20-12.2 39.7-22.6 62-30.7-65.1 20.3-122.7 52.9-161.6 92.9-27.7 28.6-41.4 57.1-41.7 82.9-.5 35.1 23.4 65.1 68.4 83l-34.5 51.7h101.6l22-34.4c22.2 1 45.3 0 68.6-2.7l-22.8 37.1h135.5L340 406.3c18.6-5.3 36.9-11.5 54.5-18.7l45.9 71.8H542L468.6 349c18.5-12.1 35-25.5 48.9-39.8zm-187.6 80.5l-25-40.6-32.7 53.3c-23.4 3.5-46.7 5.1-69.2 4.4l101.9-159.3 78.7 123c-17.2 7.4-35.3 13.9-53.7 19.2z"/></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M152.2 236.4c-7.7-8.2-19.7-7.7-24.8 2.8L1.6 490.2c-5 10 2.4 21.7 13.4 21.7h175c5.8.1 11-3.2 13.4-8.4 37.9-77.8 15.1-196.3-51.2-267.1zM244.4 8.1c-122.3 193.4-8.5 348.6 65 495.5 2.5 5.1 7.7 8.4 13.4 8.4H497c11.2 0 18.4-11.8 13.4-21.7 0 0-234.5-470.6-240.4-482.3-5.3-10.6-18.8-10.8-25.6.1z"/></svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M640 199.9v54l-320 200L0 254v-54l320 200 320-200.1zm-194.5 72l47.1-29.4c-37.2-55.8-100.7-92.6-172.7-92.6-72 0-135.5 36.7-172.6 92.4h.3c2.5-2.3 5.1-4.5 7.7-6.7 89.7-74.4 219.4-58.1 290.2 36.3zm-220.1 18.8c16.9-11.9 36.5-18.7 57.4-18.7 34.4 0 65.2 18.4 86.4 47.6l45.4-28.4c-20.9-29.9-55.6-49.5-94.8-49.5-38.9 0-73.4 19.4-94.4 49zM103.6 161.1c131.8-104.3 318.2-76.4 417.5 62.1l.7 1 48.8-30.4C517.1 112.1 424.8 58.1 319.9 58.1c-103.5 0-196.6 53.5-250.5 135.6 9.9-10.5 22.7-23.5 34.2-32.6zm467 32.7z"/></svg>

After

Width:  |  Height:  |  Size: 754 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M318.4 16l-161 480h77.5l25.4-81.4h119.5L405 496h77.5L318.4 16zm-40.3 341.9l41.2-130.4h1.5l40.9 130.4h-83.6zM640 405l-10-31.4L462.1 358l19.4 56.5L640 405zm-462.1-47L10 373.7 0 405l158.5 9.4 19.4-56.4z"/></svg>

After

Width:  |  Height:  |  Size: 459 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M453.1 32h-312c-38.9 0-76.2 31.2-83.3 69.7L1.2 410.3C-5.9 448.8 19.9 480 58.9 480h312c38.9 0 76.2-31.2 83.3-69.7l56.7-308.5c7-38.6-18.8-69.8-57.8-69.8zm-58.2 347.3l-32 13.5-115.4-110c-14.7 10-29.2 19.5-41.7 27.1l22.1 64.2-17.9 12.7-40.6-61-52.4-48.1 15.7-15.4 58 31.1c9.3-10.5 20.8-22.6 32.8-34.9L203 228.9l-68.8-99.8 18.8-28.9 8.9-4.8L265 207.8l4.9 4.5c19.4-18.8 33.8-32.4 33.8-32.4 7.7-6.5 21.5-2.9 30.7 7.9 9 10.5 10.6 24.7 2.7 31.3-1.8 1.3-15.5 11.4-35.3 25.6l4.5 7.3 94.9 119.4-6.3 7.9z"/></svg>

After

Width:  |  Height:  |  Size: 751 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M107.2 283.5l-19-41.8H36.1l-19 41.8H0l62.2-131.4 62.2 131.4h-17.2zm-45-98.1l-19.6 42.5h39.2l-19.6-42.5zm112.7 102.4l-62.2-131.4h17.1l45.1 96 45.1-96h17l-62.1 131.4zm80.6-4.3V156.4H271v127.1h-15.5zm209.1-115.6v115.6h-17.3V167.9h-41.2v-11.5h99.6v11.5h-41.1zM640 218.8c0 9.2-1.7 17.8-5.1 25.8-3.4 8-8.2 15.1-14.2 21.1-6 6-13.1 10.8-21.1 14.2-8 3.4-16.6 5.1-25.8 5.1s-17.8-1.7-25.8-5.1c-8-3.4-15.1-8.2-21.1-14.2-6-6-10.8-13-14.2-21.1-3.4-8-5.1-16.6-5.1-25.8s1.7-17.8 5.1-25.8c3.4-8 8.2-15.1 14.2-21.1 6-6 13-8.4 21.1-11.9 8-3.4 16.6-5.1 25.8-5.1s17.8 1.7 25.8 5.1c8 3.4 15.1 5.8 21.1 11.9 6 6 10.7 13.1 14.2 21.1 3.4 8 5.1 16.6 5.1 25.8zm-15.5 0c0-7.3-1.3-14-3.9-20.3-2.6-6.3-6.2-11.7-10.8-16.3-4.6-4.6-10-8.2-16.2-10.9-6.2-2.7-12.8-4-19.8-4s-13.6 1.3-19.8 4c-6.2 2.7-11.6 6.3-16.2 10.9-4.6 4.6-8.2 10-10.8 16.3-2.6 6.3-3.9 13.1-3.9 20.3 0 7.3 1.3 14 3.9 20.3 2.6 6.3 6.2 11.7 10.8 16.3 4.6 4.6 10 8.2 16.2 10.9 6.2 2.7 12.8 4 19.8 4s13.6-1.3 19.8-4c6.2-2.7 11.6-6.3 16.2-10.9 4.6-4.6 8.2-10 10.8-16.3 2.6-6.3 3.9-13.1 3.9-20.3zm-94.8 96.7v-6.3l88.9-10-242.9 13.4c.6-2.2 1.1-4.6 1.4-7.2.3-2 .5-4.2.6-6.5l64.8-8.1-64.9 1.9c0-.4-.1-.7-.1-1.1-2.8-17.2-25.5-23.7-25.5-23.7l-1.1-26.3h23.8l19 41.8h17.1L348.6 152l-62.2 131.4h17.1l19-41.8h23.6L345 268s-22.7 6.5-25.5 23.7c-.1.3-.1.7-.1 1.1l-64.9-1.9 64.8 8.1c.1 2.3.3 4.4.6 6.5.3 2.6.8 5 1.4 7.2L78.4 299.2l88.9 10v6.3c-5.9.9-10.5 6-10.5 12.2 0 6.8 5.6 12.4 12.4 12.4 6.8 0 12.4-5.6 12.4-12.4 0-6.2-4.6-11.3-10.5-12.2v-5.8l80.3 9v5.4c-5.7 1.1-9.9 6.2-9.9 12.1 0 6.8 5.6 10.2 12.4 10.2 6.8 0 12.4-3.4 12.4-10.2 0-6-4.3-11-9.9-12.1v-4.9l28.4 3.2v23.7h-5.9V360h5.9v-6.6h5v6.6h5.9v-13.8h-5.9V323l38.3 4.3c8.1 11.4 19 13.6 19 13.6l-.1 6.7-5.1.2-.1 12.1h4.1l.1-5h5.2l.1 5h4.1l-.1-12.1-5.1-.2-.1-6.7s10.9-2.1 19-13.6l38.3-4.3v23.2h-5.9V360h5.9v-6.6h5v6.6h5.9v-13.8h-5.9v-23.7l28.4-3.2v4.9c-5.7 1.1-9.9 6.2-9.9 12.1 0 6.8 5.6 10.2 12.4 10.2 6.8 0 12.4-3.4 12.4-10.2 0-6-4.3-11-9.9-12.1v-5.4l80.3-9v5.8c-5.9.9-10.5 6-10.5 12.2 0 6.8 5.6 12.4 12.4 12.4 6.8 0 12.4-5.6 12.4-12.4-.2-6.3-4.7-11.4-10.7-12.3zm-200.8-87.6l19.6-42.5 19.6 42.5h-17.9l-1.7-40.3-1.7 40.3h-17.9z"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M180.41 203.01c-.72 22.65 10.6 32.68 10.88 39.05a8.164 8.164 0 0 1-4.1 6.27l-12.8 8.96a10.66 10.66 0 0 1-5.63 1.92c-.43-.02-8.19 1.83-20.48-25.61a78.608 78.608 0 0 1-62.61 29.45c-16.28.89-60.4-9.24-58.13-56.21-1.59-38.28 34.06-62.06 70.93-60.05 7.1.02 21.6.37 46.99 6.27v-15.62c2.69-26.46-14.7-46.99-44.81-43.91-2.4.01-19.4-.5-45.84 10.11-7.36 3.38-8.3 2.82-10.75 2.82-7.41 0-4.36-21.48-2.94-24.2 5.21-6.4 35.86-18.35 65.94-18.18a76.857 76.857 0 0 1 55.69 17.28 70.285 70.285 0 0 1 17.67 52.36l-.01 69.29zM93.99 235.4c32.43-.47 46.16-19.97 49.29-30.47 2.46-10.05 2.05-16.41 2.05-27.4-9.67-2.32-23.59-4.85-39.56-4.87-15.15-1.14-42.82 5.63-41.74 32.26-1.24 16.79 11.12 31.4 29.96 30.48zm170.92 23.05c-7.86.72-11.52-4.86-12.68-10.37l-49.8-164.65c-.97-2.78-1.61-5.65-1.92-8.58a4.61 4.61 0 0 1 3.86-5.25c.24-.04-2.13 0 22.25 0 8.78-.88 11.64 6.03 12.55 10.37l35.72 140.83 33.16-140.83c.53-3.22 2.94-11.07 12.8-10.24h17.16c2.17-.18 11.11-.5 12.68 10.37l33.42 142.63L420.98 80.1c.48-2.18 2.72-11.37 12.68-10.37h19.72c.85-.13 6.15-.81 5.25 8.58-.43 1.85 3.41-10.66-52.75 169.9-1.15 5.51-4.82 11.09-12.68 10.37h-18.69c-10.94 1.15-12.51-9.66-12.68-10.75L328.67 110.7l-32.78 136.99c-.16 1.09-1.73 11.9-12.68 10.75h-18.3zm273.48 5.63c-5.88.01-33.92-.3-57.36-12.29a12.802 12.802 0 0 1-7.81-11.91v-10.75c0-8.45 6.2-6.9 8.83-5.89 10.04 4.06 16.48 7.14 28.81 9.6 36.65 7.53 52.77-2.3 56.72-4.48 13.15-7.81 14.19-25.68 5.25-34.95-10.48-8.79-15.48-9.12-53.13-21-4.64-1.29-43.7-13.61-43.79-52.36-.61-28.24 25.05-56.18 69.52-55.95 12.67-.01 46.43 4.13 55.57 15.62 1.35 2.09 2.02 4.55 1.92 7.04v10.11c0 4.44-1.62 6.66-4.87 6.66-7.71-.86-21.39-11.17-49.16-10.75-6.89-.36-39.89.91-38.41 24.97-.43 18.96 26.61 26.07 29.7 26.89 36.46 10.97 48.65 12.79 63.12 29.58 17.14 22.25 7.9 48.3 4.35 55.44-19.08 37.49-68.42 34.44-69.26 34.42zm40.2 104.86c-70.03 51.72-171.69 79.25-258.49 79.25A469.127 469.127 0 0 1 2.83 327.46c-6.53-5.89-.77-13.96 7.17-9.47a637.37 637.37 0 0 0 316.88 84.12 630.22 630.22 0 0 0 241.59-49.55c11.78-5 21.77 7.8 10.12 16.38zm29.19-33.29c-8.96-11.52-59.28-5.38-81.81-2.69-6.79.77-7.94-5.12-1.79-9.47 40.07-28.17 105.88-20.1 113.44-10.63 7.55 9.47-2.05 75.41-39.56 106.91-5.76 4.87-11.27 2.3-8.71-4.1 8.44-21.25 27.39-68.49 18.43-80.02z"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm48.2,326.1h-181L207.9,178h181Z"/></svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M448.61 225.62c26.87.18 35.57-7.43 38.92-12.37 12.47-16.32-7.06-47.6-52.85-71.33 17.76-33.58 30.11-63.68 36.34-85.3 3.38-11.83 1.09-19 .45-20.25-1.72 10.52-15.85 48.46-48.2 100.05-25-11.22-56.52-20.1-93.77-23.8-8.94-16.94-34.88-63.86-60.48-88.93C252.18 7.14 238.7 1.07 228.18.22h-.05c-13.83-1.55-22.67 5.85-27.4 11-17.2 18.53-24.33 48.87-25 84.07-7.24-12.35-17.17-24.63-28.5-25.93h-.18c-20.66-3.48-38.39 29.22-36 81.29-38.36 1.38-71 5.75-93 11.23-9.9 2.45-16.22 7.27-17.76 9.72 1-.38 22.4-9.22 111.56-9.22 5.22 53 29.75 101.82 26 93.19-9.73 15.4-38.24 62.36-47.31 97.7-5.87 22.88-4.37 37.61.15 47.14 5.57 12.75 16.41 16.72 23.2 18.26 25 5.71 55.38-3.63 86.7-21.14-7.53 12.84-13.9 28.51-9.06 39.34 7.31 19.65 44.49 18.66 88.44-9.45 20.18 32.18 40.07 57.94 55.7 74.12a39.79 39.79 0 0 0 8.75 7.09c5.14 3.21 8.58 3.37 8.58 3.37-8.24-6.75-34-38-62.54-91.78 22.22-16 45.65-38.87 67.47-69.27 122.82 4.6 143.29-24.76 148-31.64 14.67-19.88 3.43-57.44-57.32-93.69zm-77.85 106.22c23.81-37.71 30.34-67.77 29.45-92.33 27.86 17.57 47.18 37.58 49.06 58.83 1.14 12.93-8.1 29.12-78.51 33.5zM216.9 387.69c9.76-6.23 19.53-13.12 29.2-20.49 6.68 13.33 13.6 26.1 20.6 38.19-40.6 21.86-68.84 12.76-49.8-17.7zm215-171.35c-10.29-5.34-21.16-10.34-32.38-15.05a722.459 722.459 0 0 0 22.74-36.9c39.06 24.1 45.9 53.18 9.64 51.95zM279.18 398c-5.51-11.35-11-23.5-16.5-36.44 43.25 1.27 62.42-18.73 63.28-20.41 0 .07-25 15.64-62.53 12.25a718.78 718.78 0 0 0 85.06-84q13.06-15.31 24.93-31.11c-.36-.29-1.54-3-16.51-12-51.7 60.27-102.34 98-132.75 115.92-20.59-11.18-40.84-31.78-55.71-61.49-20-39.92-30-82.39-31.57-116.07 12.3.91 25.27 2.17 38.85 3.88-22.29 36.8-14.39 63-13.47 64.23 0-.07-.95-29.17 20.14-59.57a695.23 695.23 0 0 0 44.67 152.84c.93-.38 1.84.88 18.67-8.25-26.33-74.47-33.76-138.17-34-173.43 20-12.42 48.18-19.8 81.63-17.81 44.57 2.67 86.36 15.25 116.32 30.71q-10.69 15.66-23.33 32.47C365.63 152 339.1 145.84 337.5 146c.11 0 25.9 14.07 41.52 47.22a717.63 717.63 0 0 0-115.34-31.71 646.608 646.608 0 0 0-39.39-6.05c-.07.45-1.81 1.85-2.16 20.33C300 190.28 358.78 215.68 389.36 233c.74 23.55-6.95 51.61-25.41 79.57-24.6 37.31-56.39 67.23-84.77 85.43zm27.4-287c-44.56-1.66-73.58 7.43-94.69 20.67 2-52.3 21.31-76.38 38.21-75.28C267 52.15 305 108.55 306.58 111zm-130.65 3.1c.48 12.11 1.59 24.62 3.21 37.28-14.55-.85-28.74-1.25-42.4-1.26-.08 3.24-.12-51 24.67-49.59h.09c5.76 1.09 10.63 6.88 14.43 13.57zm-28.06 162c20.76 39.7 43.3 60.57 65.25 72.31-46.79 24.76-77.53 20-84.92 4.51-.2-.21-11.13-15.3 19.67-76.81zm210.06 74.8"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M186.5 293c0 19.3-14 25.4-31.2 25.4h-45.1v-52.9h46c18.6.1 30.3 7.8 30.3 27.5zm-7.7-82.3c0-17.7-13.7-21.9-28.9-21.9h-39.6v44.8H153c15.1 0 25.8-6.6 25.8-22.9zm132.3 23.2c-18.3 0-30.5 11.4-31.7 29.7h62.2c-1.7-18.5-11.3-29.7-30.5-29.7zM448 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zM271.7 185h77.8v-18.9h-77.8V185zm-43 110.3c0-24.1-11.4-44.9-35-51.6 17.2-8.2 26.2-17.7 26.2-37 0-38.2-28.5-47.5-61.4-47.5H68v192h93.1c34.9-.2 67.6-16.9 67.6-55.9zM380 280.5c0-41.1-24.1-75.4-67.6-75.4-42.4 0-71.1 31.8-71.1 73.6 0 43.3 27.3 73 71.1 73 33.2 0 54.7-14.9 65.1-46.8h-33.7c-3.7 11.9-18.6 18.1-30.2 18.1-22.4 0-34.1-13.1-34.1-35.3h100.2c.1-2.3.3-4.8.3-7.2z"/></svg>

After

Width:  |  Height:  |  Size: 963 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M232 237.2c31.8-15.2 48.4-38.2 48.4-74 0-70.6-52.6-87.8-113.3-87.8H0v354.4h171.8c64.4 0 124.9-30.9 124.9-102.9 0-44.5-21.1-77.4-64.7-89.7zM77.9 135.9H151c28.1 0 53.4 7.9 53.4 40.5 0 30.1-19.7 42.2-47.5 42.2h-79v-82.7zm83.3 233.7H77.9V272h84.9c34.3 0 56 14.3 56 50.6 0 35.8-25.9 47-57.6 47zm358.5-240.7H376V94h143.7v34.9zM576 305.2c0-75.9-44.4-139.2-124.9-139.2-78.2 0-131.3 58.8-131.3 135.8 0 79.9 50.3 134.7 131.3 134.7 61.3 0 101-27.6 120.1-86.3H509c-6.7 21.9-34.3 33.5-55.7 33.5-41.3 0-63-24.2-63-65.3h185.1c.3-4.2.6-8.7.6-13.2zM390.4 274c2.3-33.7 24.7-54.8 58.5-54.8 35.4 0 53.2 20.8 56.2 54.8H390.4z"/></svg>

After

Width:  |  Height:  |  Size: 864 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M416 32H32C14.4 32 0 46.4 0 64v384c0 17.6 14.4 32 32 32h384c17.6 0 32-14.4 32-32V64c0-17.6-14.4-32-32-32zm-64 257.4c0 49.4-11.4 82.6-103.8 82.6h-16.9c-44.1 0-62.4-14.9-70.4-38.8h-.9V368H96V136h64v74.7h1.1c4.6-30.5 39.7-38.8 69.7-38.8h17.3c92.4 0 103.8 33.1 103.8 82.5v35zm-64-28.9v22.9c0 21.7-3.4 33.8-38.4 33.8h-45.3c-28.9 0-44.1-6.5-44.1-35.7v-19c0-29.3 15.2-35.7 44.1-35.7h45.3c35-.2 38.4 12 38.4 33.7z"/></svg>

After

Width:  |  Height:  |  Size: 665 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M22.2 32A16 16 0 0 0 6 47.8a26.35 26.35 0 0 0 .2 2.8l67.9 412.1a21.77 21.77 0 0 0 21.3 18.2h325.7a16 16 0 0 0 16-13.4L505 50.7a16 16 0 0 0-13.2-18.3 24.58 24.58 0 0 0-2.8-.2L22.2 32zm285.9 297.8h-104l-28.1-147h157.3l-25.2 147z"/></svg>

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zm-141.651-35.33c4.937-32.999-20.191-50.739-54.55-62.573l11.146-44.702-27.213-6.781-10.851 43.524c-7.154-1.783-14.502-3.464-21.803-5.13l10.929-43.81-27.198-6.781-11.153 44.686c-5.922-1.349-11.735-2.682-17.377-4.084l.031-.14-37.53-9.37-7.239 29.062s20.191 4.627 19.765 4.913c11.022 2.751 13.014 10.044 12.68 15.825l-12.696 50.925c.76.194 1.744.473 2.829.907-.907-.225-1.876-.473-2.876-.713l-17.796 71.338c-1.349 3.348-4.767 8.37-12.471 6.464.271.395-19.78-4.937-19.78-4.937l-13.51 31.147 35.414 8.827c6.588 1.651 13.045 3.379 19.4 5.006l-11.262 45.213 27.182 6.781 11.153-44.733a1038.209 1038.209 0 0 0 21.687 5.627l-11.115 44.523 27.213 6.781 11.262-45.128c46.404 8.781 81.299 5.239 95.986-36.727 11.836-33.79-.589-53.281-25.004-65.991 17.78-4.098 31.174-15.792 34.747-39.949zm-62.177 87.179c-8.41 33.79-65.308 15.523-83.755 10.943l14.944-59.899c18.446 4.603 77.6 13.717 68.811 48.956zm8.417-87.667c-7.673 30.736-55.031 15.12-70.393 11.292l13.548-54.327c15.363 3.828 64.836 10.973 56.845 43.035z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M78.4 67.2C173.8-22 324.5-24 421.5 71c14.3 14.1-6.4 37.1-22.4 21.5-84.8-82.4-215.8-80.3-298.9-3.2-16.3 15.1-36.5-8.3-21.8-22.1zm98.9 418.6c19.3 5.7 29.3-23.6 7.9-30C73 421.9 9.4 306.1 37.7 194.8c5-19.6-24.9-28.1-30.2-7.1-32.1 127.4 41.1 259.8 169.8 298.1zm148.1-2c121.9-40.2 192.9-166.9 164.4-291-4.5-19.7-34.9-13.8-30 7.9 24.2 107.7-37.1 217.9-143.2 253.4-21.2 7-10.4 36 8.8 29.7zm-62.9-79l.2-71.8c0-8.2-6.6-14.8-14.8-14.8-8.2 0-14.8 6.7-14.8 14.8l-.2 71.8c0 8.2 6.6 14.8 14.8 14.8s14.8-6.6 14.8-14.8zm71-269c2.1 90.9 4.7 131.9-85.5 132.5-92.5-.7-86.9-44.3-85.5-132.5 0-21.8-32.5-19.6-32.5 0v71.6c0 69.3 60.7 90.9 118 90.1 57.3.8 118-20.8 118-90.1v-71.6c0-19.6-32.5-21.8-32.5 0z"/></svg>

After

Width:  |  Height:  |  Size: 939 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M0 32v448h448V32H0zm316.5 325.2L224 445.9l-92.5-88.7 64.5-184-64.5-86.6h184.9L252 173.2l64.5 184z"/></svg>

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M166 116.9c0 23.4-16.4 49.1-72.5 49.1H23.4l21-88.8h67.8c42.1 0 53.8 23.3 53.8 39.7zm126.2-39.7h-67.8L205.7 166h70.1c53.8 0 70.1-25.7 70.1-49.1.1-16.4-11.6-39.7-53.7-39.7zM88.8 208.1H21L0 296.9h70.1c56.1 0 72.5-23.4 72.5-49.1 0-16.3-11.7-39.7-53.8-39.7zm180.1 0h-67.8l-18.7 88.8h70.1c53.8 0 70.1-23.4 70.1-49.1 0-16.3-11.7-39.7-53.7-39.7zm189.3-53.8h-67.8l-18.7 88.8h70.1c53.8 0 70.1-23.4 70.1-49.1.1-16.3-11.6-39.7-53.7-39.7zm-28 137.9h-67.8L343.7 381h70.1c56.1 0 70.1-23.4 70.1-49.1 0-16.3-11.6-39.7-53.7-39.7zM240.8 346H173l-18.7 88.8h70.1c56.1 0 70.1-25.7 70.1-49.1.1-16.3-11.6-39.7-53.7-39.7z"/></svg>

After

Width:  |  Height:  |  Size: 856 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M446.6 222.7c-1.8-8-6.8-15.4-12.5-18.5-1.8-1-13-2.2-25-2.7-20.1-.9-22.3-1.3-28.7-5-10.1-5.9-12.8-12.3-12.9-29.5-.1-33-13.8-63.7-40.9-91.3-19.3-19.7-40.9-33-65.5-40.5-5.9-1.8-19.1-2.4-63.3-2.9-69.4-.8-84.8.6-108.4 10C45.9 59.5 14.7 96.1 3.3 142.9 1.2 151.7.7 165.8.2 246.8c-.6 101.5.1 116.4 6.4 136.5 15.6 49.6 59.9 86.3 104.4 94.3 14.8 2.7 197.3 3.3 216 .8 32.5-4.4 58-17.5 81.9-41.9 17.3-17.7 28.1-36.8 35.2-62.1 4.9-17.6 4.5-142.8 2.5-151.7zm-322.1-63.6c7.8-7.9 10-8.2 58.8-8.2 43.9 0 45.4.1 51.8 3.4 9.3 4.7 13.4 11.3 13.4 21.9 0 9.5-3.8 16.2-12.3 21.6-4.6 2.9-7.3 3.1-50.3 3.3-26.5.2-47.7-.4-50.8-1.2-16.6-4.7-22.8-28.5-10.6-40.8zm191.8 199.8l-14.9 2.4-77.5.9c-68.1.8-87.3-.4-90.9-2-7.1-3.1-13.8-11.7-14.9-19.4-1.1-7.3 2.6-17.3 8.2-22.4 7.1-6.4 10.2-6.6 97.3-6.7 89.6-.1 89.1-.1 97.6 7.8 12.1 11.3 9.5 31.2-4.9 39.4z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M162.4 196c4.8-4.9 6.2-5.1 36.4-5.1 27.2 0 28.1.1 32.1 2.1 5.8 2.9 8.3 7 8.3 13.6 0 5.9-2.4 10-7.6 13.4-2.8 1.8-4.5 1.9-31.1 2.1-16.4.1-29.5-.2-31.5-.8-10.3-2.9-14.1-17.7-6.6-25.3zm61.4 94.5c-53.9 0-55.8.2-60.2 4.1-3.5 3.1-5.7 9.4-5.1 13.9.7 4.7 4.8 10.1 9.2 12 2.2 1 14.1 1.7 56.3 1.2l47.9-.6 9.2-1.5c9-5.1 10.5-17.4 3.1-24.4-5.3-4.7-5-4.7-60.4-4.7zm223.4 130.1c-3.5 28.4-23 50.4-51.1 57.5-7.2 1.8-9.7 1.9-172.9 1.8-157.8 0-165.9-.1-172-1.8-8.4-2.2-15.6-5.5-22.3-10-5.6-3.8-13.9-11.8-17-16.4-3.8-5.6-8.2-15.3-10-22C.1 423 0 420.3 0 256.3 0 93.2 0 89.7 1.8 82.6 8.1 57.9 27.7 39 53 33.4c7.3-1.6 332.1-1.9 340-.3 21.2 4.3 37.9 17.1 47.6 36.4 7.7 15.3 7-1.5 7.3 180.6.2 115.8 0 164.5-.7 170.5zm-85.4-185.2c-1.1-5-4.2-9.6-7.7-11.5-1.1-.6-8-1.3-15.5-1.7-12.4-.6-13.8-.8-17.8-3.1-6.2-3.6-7.9-7.6-8-18.3 0-20.4-8.5-39.4-25.3-56.5-12-12.2-25.3-20.5-40.6-25.1-3.6-1.1-11.8-1.5-39.2-1.8-42.9-.5-52.5.4-67.1 6.2-27 10.7-46.3 33.4-53.4 62.4-1.3 5.4-1.6 14.2-1.9 64.3-.4 62.8 0 72.1 4 84.5 9.7 30.7 37.1 53.4 64.6 58.4 9.2 1.7 122.2 2.1 133.7.5 20.1-2.7 35.9-10.8 50.7-25.9 10.7-10.9 17.4-22.8 21.8-38.5 3.2-10.9 2.9-88.4 1.7-93.9z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Some files were not shown because too many files have changed in this diff Show More